3
0

WhiteBoxToolApi.cpp 150 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "Util/WhiteBoxMathUtil.h"
  9. #include "Util/WhiteBoxTextureUtil.h"
  10. #include <AzCore/Console/Console.h>
  11. #include <AzCore/Debug/Profiler.h>
  12. #include <AzCore/IO/FileIO.h>
  13. #include <AzCore/Math/MathUtils.h>
  14. #include <AzCore/Math/Matrix3x4.h>
  15. #include <AzCore/Math/MathStringConversions.h>
  16. #include <AzCore/Math/Transform.h>
  17. #include <AzCore/Math/Vector2.h>
  18. #include <AzCore/Math/Vector3.h>
  19. #include <AzCore/std/containers/map.h>
  20. #include <AzCore/std/containers/vector.h>
  21. #include <AzCore/std/functional.h>
  22. #include <AzCore/std/hash.h>
  23. #include <AzCore/std/numeric.h>
  24. #include <AzCore/std/parallel/lock.h>
  25. #include <AzCore/std/sort.h>
  26. #include <AzCore/std/string/conversions.h>
  27. #include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
  28. #include <WhiteBox/WhiteBoxToolApi.h>
  29. namespace OpenMesh
  30. {
  31. // Overload methods need to be declared before including OpenMesh so their definitions are found
  32. inline AZ::Vector3 normalize(const AZ::Vector3& v)
  33. {
  34. AZ::Vector3 vret = v;
  35. vret.Normalize();
  36. return vret;
  37. }
  38. inline float dot(const AZ::Vector3& v1, const AZ::Vector3& v2)
  39. {
  40. return v1.Dot(v2);
  41. }
  42. inline float norm(const AZ::Vector3& v)
  43. {
  44. return v.GetLength();
  45. }
  46. inline AZ::Vector3 cross(const AZ::Vector3& v1, const AZ::Vector3& v2)
  47. {
  48. return v1.Cross(v2);
  49. }
  50. inline AZ::Vector3 vectorize(AZ::Vector3& v, float s)
  51. {
  52. v = AZ::Vector3(s);
  53. return v;
  54. }
  55. inline void newell_norm(AZ::Vector3& n, const AZ::Vector3& a, const AZ::Vector3& b)
  56. {
  57. n.SetX(n.GetX() + (a.GetY() * b.GetZ()));
  58. n.SetY(n.GetY() + (a.GetZ() * b.GetX()));
  59. n.SetZ(n.GetZ() + (a.GetX() * b.GetY()));
  60. }
  61. }
  62. // OpenMesh includes
  63. AZ_PUSH_DISABLE_WARNING(4702, "-Wunknown-warning-option") // OpenMesh\Core\Utils\Property.hh has unreachable code
  64. #include <OpenMesh/Core/IO/MeshIO.hh>
  65. #include <OpenMesh/Core/IO/SR_binary.hh>
  66. #include <OpenMesh/Core/IO/importer/ImporterT.hh>
  67. #include <OpenMesh/Core/Mesh/Traits.hh>
  68. #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
  69. #include <OpenMesh/Core/Utils/GenProg.hh>
  70. #include <OpenMesh/Core/Utils/vector_traits.hh>
  71. AZ_POP_DISABLE_WARNING
  72. AZ_DECLARE_BUDGET(AzToolsFramework);
  73. namespace OpenMesh
  74. {
  75. template<>
  76. struct vector_traits<AZ::Vector3>
  77. {
  78. //! Type of the vector class
  79. using vector_type = AZ::Vector3;
  80. //! Type of the scalar value
  81. using value_type = float;
  82. //! size/dimension of the vector
  83. static const size_t size_ = 3;
  84. //! size/dimension of the vector
  85. static size_t size()
  86. {
  87. return size_;
  88. }
  89. };
  90. template<>
  91. struct vector_traits<AZ::Vector2>
  92. {
  93. //! Type of the vector class
  94. using vector_type = AZ::Vector2;
  95. //! Type of the scalar value
  96. using value_type = float;
  97. //! size/dimension of the vector
  98. static const size_t size_ = 2;
  99. //! size/dimension of the vector
  100. static size_t size()
  101. {
  102. return size_;
  103. }
  104. };
  105. template<>
  106. inline void vector_cast(const AZ::Vector3& src, OpenMesh::Vec3f& dst, GenProg::Int2Type<3> /*unused*/)
  107. {
  108. dst[0] = static_cast<vector_traits<Vec3f>::value_type>(src.GetX());
  109. dst[1] = static_cast<vector_traits<Vec3f>::value_type>(src.GetY());
  110. dst[2] = static_cast<vector_traits<Vec3f>::value_type>(src.GetZ());
  111. }
  112. template<>
  113. inline void vector_cast(const AZ::Vector2& src, OpenMesh::Vec2f& dst, GenProg::Int2Type<2> /*unused*/)
  114. {
  115. dst[0] = static_cast<vector_traits<Vec2f>::value_type>(src.GetX());
  116. dst[1] = static_cast<vector_traits<Vec2f>::value_type>(src.GetY());
  117. }
  118. template<>
  119. inline void vector_cast(const OpenMesh::Vec3f& src, AZ::Vector3& dst, GenProg::Int2Type<3> /*unused*/)
  120. {
  121. dst.SetX(static_cast<vector_traits<Vec3f>::value_type>(src[0]));
  122. dst.SetY(static_cast<vector_traits<Vec3f>::value_type>(src[1]));
  123. dst.SetZ(static_cast<vector_traits<Vec3f>::value_type>(src[2]));
  124. }
  125. template<>
  126. inline void vector_cast(const OpenMesh::Vec2f& src, AZ::Vector2& dst, GenProg::Int2Type<2> /*unused*/)
  127. {
  128. dst.SetX(static_cast<vector_traits<Vec2f>::value_type>(src[0]));
  129. dst.SetY(static_cast<vector_traits<Vec2f>::value_type>(src[1]));
  130. }
  131. template<>
  132. inline void vector_cast(const AZ::Vector3& src, OpenMesh::Vec3d& dst, GenProg::Int2Type<3> /*unused*/)
  133. {
  134. dst[0] = static_cast<vector_traits<Vec3d>::value_type>(src.GetX());
  135. dst[1] = static_cast<vector_traits<Vec3d>::value_type>(src.GetY());
  136. dst[2] = static_cast<vector_traits<Vec3d>::value_type>(src.GetZ());
  137. }
  138. template<>
  139. inline void vector_cast(const AZ::Vector2& src, OpenMesh::Vec2d& dst, GenProg::Int2Type<2> /*unused*/)
  140. {
  141. dst[0] = static_cast<vector_traits<Vec2d>::value_type>(src.GetX());
  142. dst[1] = static_cast<vector_traits<Vec2d>::value_type>(src.GetY());
  143. }
  144. template<>
  145. inline void vector_cast(const OpenMesh::Vec3d& src, AZ::Vector3& dst, GenProg::Int2Type<3> /*unused*/)
  146. {
  147. dst.SetX(static_cast<float>(src[0]));
  148. dst.SetY(static_cast<float>(src[1]));
  149. dst.SetZ(static_cast<float>(src[2]));
  150. }
  151. template<>
  152. inline void vector_cast(const OpenMesh::Vec2d& src, AZ::Vector2& dst, GenProg::Int2Type<2> /*unused*/)
  153. {
  154. dst.SetX(static_cast<float>(src[0]));
  155. dst.SetY(static_cast<float>(src[1]));
  156. }
  157. } // namespace OpenMesh
  158. #ifdef AZ_ENABLE_TRACING
  159. #define WHITEBOX_LOG(str, ...) \
  160. do \
  161. { \
  162. if (cl_whiteBoxLogMessages) \
  163. { \
  164. AZ_Printf(str, __VA_ARGS__) \
  165. } \
  166. } while (0);
  167. #else
  168. #define WHITEBOX_LOG(str, ...)
  169. #endif
  170. namespace WhiteBox
  171. {
  172. // cvar for logging debug messages
  173. AZ_CVAR(bool, cl_whiteBoxLogMessages, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Log debug messages.");
  174. struct WhiteBoxTraits : public OpenMesh::DefaultTraits
  175. {
  176. using Point = AZ::Vector3;
  177. using Normal = AZ::Vector3;
  178. using TexCoord2D = AZ::Vector2;
  179. using TexCoord3D = AZ::Vector3;
  180. };
  181. using Mesh = OpenMesh::TriMesh_ArrayKernelT<WhiteBoxTraits>;
  182. } // namespace WhiteBox
  183. namespace AZStd
  184. {
  185. template<>
  186. struct hash<WhiteBox::Mesh::FaceHandle>
  187. {
  188. public:
  189. size_t operator()(const WhiteBox::Mesh::FaceHandle& faceHandle) const
  190. {
  191. size_t h{0};
  192. AZStd::hash_combine(h, faceHandle.idx());
  193. return h;
  194. }
  195. };
  196. } // namespace AZStd
  197. namespace WhiteBox
  198. {
  199. // alias for vector of OpenMesh FaceHandles
  200. using FaceHandlesInternal = AZStd::vector<Mesh::FaceHandle>;
  201. // a property to map from a FaceHandle to the Polygon it corresponds to
  202. // note: PolygonHandle will include the FaceHandle used to do the lookup
  203. using FaceHandlePolygonMapping = AZStd::unordered_map<Mesh::FaceHandle, FaceHandlesInternal>;
  204. using PolygonPropertyHandle = OpenMesh::MPropHandleT<FaceHandlePolygonMapping>;
  205. // unique string to lookup the polygon custom property via get_property_handle
  206. static const char* const PolygonProps = "polygon-props";
  207. // a property to track the hidden state of a vertex
  208. using VertexBoolPropertyHandle = OpenMesh::VPropHandleT<bool>;
  209. // unique string to lookup the vertex custom property via get_property_handle
  210. static const char* const VertexHiddenProp = "vertex-hidden-props";
  211. } // namespace WhiteBox
  212. namespace OpenMesh::IO
  213. {
  214. template<>
  215. struct binary<WhiteBox::FaceHandlesInternal>
  216. {
  217. using value_type = WhiteBox::FaceHandlesInternal;
  218. static const bool is_streamable = true;
  219. // return generic binary size of self, if known
  220. static size_t size_of()
  221. {
  222. return UnknownSize;
  223. }
  224. // return binary size of the value
  225. static size_t size_of(const value_type& _v)
  226. {
  227. AZ_PROFILE_FUNCTION(AzToolsFramework);
  228. if (_v.empty())
  229. {
  230. return sizeof(uint32_t);
  231. }
  232. value_type::const_iterator it = _v.begin();
  233. const auto count = static_cast<uint32_t>(_v.size());
  234. size_t bytes = IO::size_of(count);
  235. for (; it != _v.end(); ++it)
  236. {
  237. bytes += IO::size_of(it->idx());
  238. }
  239. return bytes;
  240. }
  241. static size_t store(std::ostream& _os, const value_type& _v, bool _swap = false)
  242. {
  243. AZ_PROFILE_FUNCTION(AzToolsFramework);
  244. size_t bytes = 0;
  245. const auto count = static_cast<uint32_t>(_v.size());
  246. value_type::const_iterator it = _v.begin();
  247. bytes += IO::store(_os, count, _swap);
  248. for (; it != _v.end() && _os.good(); ++it)
  249. {
  250. bytes += IO::store(_os, (*it).idx(), _swap);
  251. }
  252. return _os.good() ? bytes : 0;
  253. }
  254. static size_t restore(std::istream& _is, value_type& _v, bool _swap = false)
  255. {
  256. AZ_PROFILE_FUNCTION(AzToolsFramework);
  257. size_t bytes = 0;
  258. uint32_t count = 0;
  259. _v.clear();
  260. bytes += IO::restore(_is, count, _swap);
  261. _v.reserve(count);
  262. for (size_t i = 0; i < count && _is.good(); ++i)
  263. {
  264. int elem; // value_type::value_type -> Mesh::FaceHandle (underlying type int)
  265. bytes += IO::restore(_is, elem, _swap);
  266. _v.push_back(value_type::value_type(elem));
  267. }
  268. return _is.good() ? bytes : 0;
  269. }
  270. };
  271. template<>
  272. struct binary<WhiteBox::FaceHandlePolygonMapping>
  273. {
  274. using value_type = WhiteBox::FaceHandlePolygonMapping;
  275. static const bool is_streamable = true;
  276. // return generic binary size of self, if known
  277. static size_t size_of()
  278. {
  279. return UnknownSize;
  280. }
  281. // return binary size of the value
  282. static size_t size_of(const value_type& _v)
  283. {
  284. AZ_PROFILE_FUNCTION(AzToolsFramework);
  285. if (_v.empty())
  286. {
  287. return sizeof(uint32_t);
  288. }
  289. value_type::const_iterator it = _v.begin();
  290. const auto count = static_cast<uint32_t>(_v.size());
  291. size_t bytes = IO::size_of(count);
  292. for (; it != _v.end(); ++it)
  293. {
  294. bytes += IO::size_of(it->first.idx());
  295. bytes += IO::size_of(it->second);
  296. }
  297. return bytes;
  298. }
  299. static size_t store(std::ostream& _os, const value_type& _v, bool _swap = false)
  300. {
  301. AZ_PROFILE_FUNCTION(AzToolsFramework);
  302. size_t bytes = 0;
  303. const auto count = static_cast<uint32_t>(_v.size());
  304. value_type::const_iterator it = _v.begin();
  305. bytes += IO::store(_os, count, _swap);
  306. for (; it != _v.end() && _os.good(); ++it)
  307. {
  308. bytes += IO::store(_os, it->first.idx(), _swap);
  309. bytes += IO::store(_os, it->second, _swap);
  310. }
  311. return _os.good() ? bytes : 0;
  312. }
  313. static size_t restore(std::istream& _is, value_type& _v, bool _swap = false)
  314. {
  315. AZ_PROFILE_FUNCTION(AzToolsFramework);
  316. size_t bytes = 0;
  317. uint32_t count = 0;
  318. _v.clear();
  319. bytes += IO::restore(_is, count, _swap);
  320. value_type::mapped_type val;
  321. for (size_t i = 0; i < count && _is.good(); ++i)
  322. {
  323. int key; // value_type::key_type -> Mesh::FaceHandle (underlying type int)
  324. bytes += IO::restore(_is, key, _swap);
  325. bytes += IO::restore(_is, val, _swap);
  326. _v[value_type::key_type(key)] = val;
  327. }
  328. return _is.good() ? bytes : 0;
  329. }
  330. };
  331. } // namespace OpenMesh::IO
  332. namespace WhiteBox
  333. {
  334. static const float NormalTolerance = 0.99f;
  335. static const float AdjacentPolygonNormalTolerance = 0.0001f;
  336. // A wrapper for the OpenMesh source data.
  337. struct WhiteBoxMesh
  338. {
  339. AZ_CLASS_ALLOCATOR(WhiteBoxMesh, AZ::SystemAllocator);
  340. WhiteBoxMesh() = default;
  341. WhiteBoxMesh(WhiteBoxMesh&&) = default;
  342. WhiteBoxMesh& operator=(WhiteBoxMesh&&) = default;
  343. Mesh mesh; //!< The OpenMesh triangle mesh kernel (with customized AZ traits).
  344. };
  345. // 0,0 is tl - 1,1 is br
  346. // 3 is 0,0
  347. // 2 is 1,0
  348. // 1 is 1,1
  349. // 0 is 0,1
  350. const Mesh::TexCoord2D g_quadUVs[] = {
  351. Mesh::TexCoord2D(0.0f, 1.0f),
  352. Mesh::TexCoord2D(1.0f, 1.0f),
  353. Mesh::TexCoord2D(1.0f, 0.0f),
  354. Mesh::TexCoord2D(0.0f, 0.0f),
  355. };
  356. // conversion functions between OpenMesh and AZ types
  357. // convert WhiteBox face handle to OpenMesh face handle
  358. static Mesh::FaceHandle om_fh(const Api::FaceHandle fh)
  359. {
  360. return Mesh::FaceHandle{fh.Index()};
  361. }
  362. // convert WhiteBox vertex handle to OpenMesh vertex handle
  363. static Mesh::VertexHandle om_vh(const Api::VertexHandle vh)
  364. {
  365. return Mesh::VertexHandle{vh.Index()};
  366. }
  367. // convert WhiteBox edge handle to OpenMesh edge handle
  368. static Mesh::EdgeHandle om_eh(const Api::EdgeHandle eh)
  369. {
  370. return Mesh::EdgeHandle{eh.Index()};
  371. }
  372. // convert WhiteBox halfedge handle to OpenMesh halfedge handle
  373. static Mesh::HalfedgeHandle om_heh(const Api::HalfedgeHandle heh)
  374. {
  375. return Mesh::HalfedgeHandle{heh.Index()};
  376. }
  377. // convert OpenMesh face handle to WhiteBox face handle
  378. static Api::FaceHandle wb_fh(const Mesh::FaceHandle fh)
  379. {
  380. return Api::FaceHandle{fh.idx()};
  381. }
  382. // convert OpenMesh vertex handle to WhiteBox vertex handle
  383. static Api::VertexHandle wb_vh(const Mesh::VertexHandle vh)
  384. {
  385. return Api::VertexHandle{vh.idx()};
  386. }
  387. // convert OpenMesh halfedge handle to WhiteBox halfedge handle
  388. static Api::HalfedgeHandle wb_heh(const Mesh::HalfedgeHandle heh)
  389. {
  390. return Api::HalfedgeHandle{heh.idx()};
  391. }
  392. // convert OpenMesh edge handle to WhiteBox edge handle
  393. static Api::EdgeHandle wb_eh(const Mesh::EdgeHandle eh)
  394. {
  395. return Api::EdgeHandle{eh.idx()};
  396. }
  397. // map from internal handles to external handles
  398. Api::PolygonHandle PolygonHandleFromInternal(const FaceHandlesInternal& faceHandlesInternal)
  399. {
  400. Api::PolygonHandle polygonHandle;
  401. polygonHandle.m_faceHandles.reserve(faceHandlesInternal.size());
  402. AZStd::transform(
  403. faceHandlesInternal.begin(), faceHandlesInternal.end(), AZStd::back_inserter(polygonHandle.m_faceHandles),
  404. &wb_fh);
  405. return polygonHandle;
  406. }
  407. FaceHandlesInternal InternalFaceHandlesFromPolygon(const Api::PolygonHandle& polygonHandle)
  408. {
  409. AZ_PROFILE_FUNCTION(AzToolsFramework);
  410. FaceHandlesInternal faceHandlesInternal;
  411. faceHandlesInternal.reserve(polygonHandle.m_faceHandles.size());
  412. AZStd::transform(
  413. polygonHandle.m_faceHandles.begin(), polygonHandle.m_faceHandles.end(),
  414. AZStd::back_inserter(faceHandlesInternal), &om_fh);
  415. return faceHandlesInternal;
  416. }
  417. namespace Api
  418. {
  419. AZStd::mutex g_omSerializationLock; // serialization lock required when using Open Mesh IOManager
  420. namespace Internal
  421. {
  422. // when performing an append (extrusion or impression) new vertices will
  423. // be added to the mesh - this struct maps from the existing vertex and the
  424. // newly added one at the same location.
  425. // note: it is possible that as part of an impression, m_existing and m_added
  426. // both refer to the same vertex handle as the same vertex will be reused
  427. struct VertexHandlePair
  428. {
  429. VertexHandle m_existing;
  430. VertexHandle m_added;
  431. VertexHandlePair() = default;
  432. VertexHandlePair(const VertexHandle existing, const VertexHandle added)
  433. : m_existing(existing)
  434. , m_added(added)
  435. {
  436. }
  437. };
  438. // a collection of VertexHandlePairs
  439. // generated as part of an append (extrusion or impression)
  440. struct AppendedVerts
  441. {
  442. AZStd::vector<VertexHandlePair> m_vertexHandlePairs;
  443. };
  444. // intermediate data to use when appending an edge (performing an 'edge extrusion')
  445. struct EdgeAppendVertexHandles
  446. {
  447. PolygonHandle m_existingPolygonHandle; // the polygon to be replaced by the new edge extrusion
  448. // the vertices to use when 'appending' new geometry to the mesh while performing an edge extrusion.
  449. VertexHandle m_toVertexHandle;
  450. VertexHandle m_fromVertexHandle;
  451. VertexHandle m_addedFromVertexHandle;
  452. VertexHandle m_addedToVertexHandle;
  453. VertexHandle m_afterToVertexHandle;
  454. VertexHandle m_beforeFromVertexHandle;
  455. };
  456. // intermediate data to use when appending an edge (performing an 'edge extrusion')
  457. struct EdgeAppendPolygonHandles
  458. {
  459. PolygonHandle m_nearPolygonHandle;
  460. PolygonHandle m_farPolygonHandle;
  461. PolygonHandle m_topPolygonHandle;
  462. PolygonHandle m_bottomPolygonHandle;
  463. };
  464. } // namespace Internal
  465. // forward declarations
  466. AZStd::vector<FaceVertHandles> BuildNewVertexFaceHandles(
  467. WhiteBoxMesh& whiteBox, const Internal::AppendedVerts& appendedVerts, const FaceHandles& existingFaces);
  468. void RemoveFaces(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles);
  469. void CalculatePlanarUVs(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles);
  470. // restores WhiteBoxMesh properties, use when properties have not been initialized or have been cleared
  471. static void InitializeWhiteBoxMesh(WhiteBoxMesh& whiteBox)
  472. {
  473. // add default properties for all white box meshes
  474. PolygonPropertyHandle polygonPropsHandle;
  475. whiteBox.mesh.add_property(polygonPropsHandle, PolygonProps);
  476. whiteBox.mesh.mproperty(polygonPropsHandle).set_persistent(true);
  477. VertexBoolPropertyHandle vertexPropsHiddenHandle;
  478. whiteBox.mesh.add_property(vertexPropsHiddenHandle, VertexHiddenProp);
  479. whiteBox.mesh.property(vertexPropsHiddenHandle).set_persistent(true);
  480. // request default properties required for all white box meshes
  481. whiteBox.mesh.request_face_normals();
  482. whiteBox.mesh.request_halfedge_texcoords2D();
  483. }
  484. WhiteBoxMeshPtr CreateWhiteBoxMesh()
  485. {
  486. auto whiteBox = WhiteBoxMeshPtr(aznew WhiteBoxMesh());
  487. InitializeWhiteBoxMesh(*whiteBox);
  488. return whiteBox;
  489. }
  490. void WhiteBoxMeshDeleter::DestroyWhiteBoxMesh(WhiteBoxMesh* whiteBox)
  491. {
  492. delete whiteBox;
  493. }
  494. VertexHandles MeshVertexHandles(const WhiteBoxMesh& whiteBox)
  495. {
  496. AZ_PROFILE_FUNCTION(AzToolsFramework);
  497. VertexHandles vertexHandles;
  498. vertexHandles.reserve(whiteBox.mesh.n_vertices());
  499. for (const auto& vertexHandle : whiteBox.mesh.vertices())
  500. {
  501. vertexHandles.push_back(wb_vh(vertexHandle));
  502. }
  503. return vertexHandles;
  504. }
  505. FaceHandles MeshFaceHandles(const WhiteBoxMesh& whiteBox)
  506. {
  507. AZ_PROFILE_FUNCTION(AzToolsFramework);
  508. FaceHandles faceHandles;
  509. faceHandles.reserve(whiteBox.mesh.n_faces());
  510. for (const auto& faceHandle : whiteBox.mesh.faces())
  511. {
  512. faceHandles.push_back(wb_fh(faceHandle));
  513. }
  514. return faceHandles;
  515. }
  516. PolygonHandles MeshPolygonHandles(const WhiteBoxMesh& whiteBox)
  517. {
  518. AZ_PROFILE_FUNCTION(AzToolsFramework);
  519. PolygonPropertyHandle polygonPropsHandle;
  520. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  521. const auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  522. AZStd::vector<PolygonHandle> polygonHandles;
  523. for (const auto& polygonProp : polygonProps)
  524. {
  525. // don't add duplicate polygons
  526. PolygonHandle polygonHandle = PolygonHandleFromInternal(polygonProp.second);
  527. if (AZStd::find(polygonHandles.begin(), polygonHandles.end(), polygonHandle) == polygonHandles.end())
  528. {
  529. polygonHandles.push_back(AZStd::move(polygonHandle));
  530. }
  531. }
  532. return polygonHandles;
  533. }
  534. EdgeHandlesCollection PolygonBorderEdgeHandles(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  535. {
  536. AZ_PROFILE_FUNCTION(AzToolsFramework);
  537. const HalfedgeHandlesCollection halfedgeHandlesCollection =
  538. PolygonBorderHalfedgeHandles(whiteBox, polygonHandle);
  539. EdgeHandlesCollection orderedEdgeHandlesCollection;
  540. orderedEdgeHandlesCollection.reserve(halfedgeHandlesCollection.size());
  541. for (const auto& halfedgeHandles : halfedgeHandlesCollection)
  542. {
  543. EdgeHandles orderedEdgeHandles;
  544. orderedEdgeHandles.reserve(halfedgeHandles.size());
  545. for (const auto& halfedgeHandle : halfedgeHandles)
  546. {
  547. orderedEdgeHandles.push_back(HalfedgeEdgeHandle(whiteBox, halfedgeHandle));
  548. }
  549. orderedEdgeHandlesCollection.push_back(orderedEdgeHandles);
  550. }
  551. return orderedEdgeHandlesCollection;
  552. }
  553. EdgeHandles PolygonBorderEdgeHandlesFlattened(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  554. {
  555. AZ_PROFILE_FUNCTION(AzToolsFramework);
  556. const EdgeHandlesCollection borderEdgeHandlesCollection = PolygonBorderEdgeHandles(whiteBox, polygonHandle);
  557. EdgeHandles polygonBorderEdgeHandles;
  558. for (const auto& borderEdgeHandles : borderEdgeHandlesCollection)
  559. {
  560. polygonBorderEdgeHandles.insert(
  561. polygonBorderEdgeHandles.end(), borderEdgeHandles.cbegin(), borderEdgeHandles.cend());
  562. }
  563. return polygonBorderEdgeHandles;
  564. }
  565. EdgeHandles MeshPolygonEdgeHandles(const WhiteBoxMesh& whiteBox)
  566. {
  567. AZ_PROFILE_FUNCTION(AzToolsFramework);
  568. auto polygonHandles = MeshPolygonHandles(whiteBox);
  569. EdgeHandles allEdgeHandles;
  570. for (const auto& polygonHandle : polygonHandles)
  571. {
  572. auto polygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, polygonHandle);
  573. allEdgeHandles.insert(allEdgeHandles.end(), polygonEdgeHandles.begin(), polygonEdgeHandles.end());
  574. }
  575. // remove duplicates
  576. AZStd::sort(allEdgeHandles.begin(), allEdgeHandles.end());
  577. allEdgeHandles.erase(AZStd::unique(allEdgeHandles.begin(), allEdgeHandles.end()), allEdgeHandles.end());
  578. return allEdgeHandles;
  579. }
  580. EdgeHandles MeshEdgeHandles(const WhiteBoxMesh& whiteBox)
  581. {
  582. AZ_PROFILE_FUNCTION(AzToolsFramework);
  583. EdgeHandles edgeHandles;
  584. edgeHandles.reserve(whiteBox.mesh.n_edges());
  585. for (const auto& edgeHandle : whiteBox.mesh.edges())
  586. {
  587. edgeHandles.push_back(wb_eh(edgeHandle));
  588. }
  589. return edgeHandles;
  590. }
  591. EdgeTypes MeshUserEdgeHandles(const WhiteBoxMesh& whiteBox)
  592. {
  593. AZ_PROFILE_FUNCTION(AzToolsFramework);
  594. EdgeHandles userEdgeHandles = MeshPolygonEdgeHandles(whiteBox);
  595. AZStd::sort(userEdgeHandles.begin(), userEdgeHandles.end());
  596. EdgeHandles allEdgeHandles = MeshEdgeHandles(whiteBox);
  597. AZStd::sort(allEdgeHandles.begin(), allEdgeHandles.end());
  598. EdgeHandles meshEdgeHandles;
  599. meshEdgeHandles.reserve(allEdgeHandles.size()); // over reserve vector
  600. AZStd::set_difference(
  601. allEdgeHandles.begin(), allEdgeHandles.end(), userEdgeHandles.begin(), userEdgeHandles.end(),
  602. AZStd::back_inserter(meshEdgeHandles));
  603. return {AZStd::move(userEdgeHandles), AZStd::move(meshEdgeHandles)};
  604. }
  605. AZStd::vector<AZ::Vector3> MeshVertexPositions(const WhiteBoxMesh& whiteBox)
  606. {
  607. AZ_PROFILE_FUNCTION(AzToolsFramework);
  608. return VertexPositions(whiteBox, MeshVertexHandles(whiteBox));
  609. }
  610. HalfedgeHandles FaceHalfedgeHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  611. {
  612. HalfedgeHandles halfedgeHandles;
  613. halfedgeHandles.reserve(3);
  614. for (Mesh::ConstFaceHalfedgeCCWIter faceHalfedgeIt = whiteBox.mesh.cfh_ccwiter(om_fh(faceHandle));
  615. faceHalfedgeIt.is_valid(); ++faceHalfedgeIt)
  616. {
  617. halfedgeHandles.push_back(wb_heh(*faceHalfedgeIt));
  618. }
  619. return halfedgeHandles;
  620. }
  621. EdgeHandles FaceEdgeHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  622. {
  623. if (!faceHandle.IsValid())
  624. {
  625. return {};
  626. }
  627. EdgeHandles edgeHandles;
  628. edgeHandles.reserve(3);
  629. for (const auto& halfedgeHandle : FaceHalfedgeHandles(whiteBox, faceHandle))
  630. {
  631. edgeHandles.push_back(HalfedgeEdgeHandle(whiteBox, halfedgeHandle));
  632. }
  633. return edgeHandles;
  634. }
  635. VertexHandles FaceVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  636. {
  637. if (!faceHandle.IsValid())
  638. {
  639. return {};
  640. }
  641. VertexHandles vertexHandles;
  642. vertexHandles.reserve(3);
  643. for (const auto& halfedgeHandle : FaceHalfedgeHandles(whiteBox, faceHandle))
  644. {
  645. vertexHandles.emplace_back(HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
  646. }
  647. return vertexHandles;
  648. }
  649. AZStd::vector<AZ::Vector3> FaceVertexPositions(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  650. {
  651. return VertexPositions(whiteBox, FaceVertexHandles(whiteBox, faceHandle));
  652. }
  653. AZStd::vector<AZ::Vector3> FacesPositions(const WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
  654. {
  655. AZ_PROFILE_FUNCTION(AzToolsFramework);
  656. AZStd::vector<AZ::Vector3> triangles;
  657. triangles.reserve(faceHandles.size() * 3);
  658. for (const auto& faceHandle : faceHandles)
  659. {
  660. const auto corners = FaceVertexPositions(whiteBox, faceHandle);
  661. triangles.insert(triangles.end(), corners.begin(), corners.end());
  662. }
  663. return triangles;
  664. }
  665. FaceHandle HalfedgeFaceHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  666. {
  667. if (halfedgeHandle.IsValid())
  668. {
  669. return wb_fh(whiteBox.mesh.face_handle(om_heh(halfedgeHandle)));
  670. }
  671. return {};
  672. }
  673. HalfedgeHandle HalfedgeOppositeHalfedgeHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  674. {
  675. if (halfedgeHandle.IsValid())
  676. {
  677. return wb_heh(whiteBox.mesh.opposite_halfedge_handle(om_heh(halfedgeHandle)));
  678. }
  679. return {};
  680. }
  681. FaceHandle HalfedgeOppositeFaceHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  682. {
  683. if (halfedgeHandle.IsValid())
  684. {
  685. return wb_fh(whiteBox.mesh.opposite_face_handle(om_heh(halfedgeHandle)));
  686. }
  687. return {};
  688. }
  689. HalfedgeHandles VertexOutgoingHalfedgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  690. {
  691. HalfedgeHandles outgoingHalfEdgeHandles;
  692. for (auto oheh = whiteBox.mesh.cvoh_ccwbegin(om_vh(vertexHandle));
  693. oheh != whiteBox.mesh.cvoh_ccwend(om_vh(vertexHandle)); ++oheh)
  694. {
  695. outgoingHalfEdgeHandles.push_back(wb_heh(*oheh));
  696. }
  697. return outgoingHalfEdgeHandles;
  698. }
  699. HalfedgeHandles VertexIncomingHalfedgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  700. {
  701. HalfedgeHandles incomingHalfEdgeHandles;
  702. for (auto iheh = whiteBox.mesh.cvih_ccwbegin(om_vh(vertexHandle));
  703. iheh != whiteBox.mesh.cvih_ccwend(om_vh(vertexHandle)); ++iheh)
  704. {
  705. incomingHalfEdgeHandles.push_back(wb_heh(*iheh));
  706. }
  707. return incomingHalfEdgeHandles;
  708. }
  709. HalfedgeHandles VertexHalfedgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  710. {
  711. AZ_PROFILE_FUNCTION(AzToolsFramework);
  712. HalfedgeHandles outgoingHandles = VertexOutgoingHalfedgeHandles(whiteBox, vertexHandle);
  713. HalfedgeHandles incomingHandles = VertexIncomingHalfedgeHandles(whiteBox, vertexHandle);
  714. auto allHandles = AZStd::move(outgoingHandles);
  715. allHandles.insert(
  716. allHandles.end(), AZStd::make_move_iterator(incomingHandles.begin()),
  717. AZStd::make_move_iterator(incomingHandles.end()));
  718. return allHandles;
  719. }
  720. EdgeHandles VertexEdgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  721. {
  722. AZ_PROFILE_FUNCTION(AzToolsFramework);
  723. const auto omVertexHandle = om_vh(vertexHandle);
  724. return AZStd::accumulate(
  725. whiteBox.mesh.cve_ccwbegin(omVertexHandle), whiteBox.mesh.cve_ccwend(omVertexHandle), EdgeHandles{},
  726. [](EdgeHandles edgeHandles, const auto edgeHandle)
  727. {
  728. edgeHandles.push_back(wb_eh(edgeHandle));
  729. return edgeHandles;
  730. });
  731. }
  732. static bool BuildFaceHandles(
  733. const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle, FaceHandles& faceHandles,
  734. const AZ::Vector3& normal)
  735. {
  736. AZ_PROFILE_FUNCTION(AzToolsFramework);
  737. const auto* const found_fh = AZStd::find(faceHandles.cbegin(), faceHandles.cend(), faceHandle);
  738. if (found_fh == faceHandles.cend())
  739. {
  740. const AZ::Vector3 nextNormal = FaceNormal(whiteBox, faceHandle).GetNormalized();
  741. if (OpenMesh::dot(nextNormal, normal) > NormalTolerance)
  742. {
  743. faceHandles.push_back(faceHandle);
  744. return true;
  745. }
  746. }
  747. return false;
  748. }
  749. static FaceHandle OppositeFaceHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  750. {
  751. AZ_PROFILE_FUNCTION(AzToolsFramework);
  752. const HalfedgeHandle oppositeHalfedgeHandle = HalfedgeOppositeHalfedgeHandle(whiteBox, halfedgeHandle);
  753. if (HalfedgeIsBoundary(whiteBox, oppositeHalfedgeHandle))
  754. {
  755. return {};
  756. }
  757. // note: oppositeFaceHandle will be invalid if oppositeHalfedgeHandle is a boundary
  758. const FaceHandle oppositeFaceHandle = HalfedgeFaceHandle(whiteBox, oppositeHalfedgeHandle);
  759. return oppositeFaceHandle;
  760. }
  761. static void SideFaceHandlesInternal(
  762. const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle, FaceHandles& faceHandles,
  763. const AZ::Vector3& normal)
  764. {
  765. AZ_PROFILE_FUNCTION(AzToolsFramework);
  766. if (BuildFaceHandles(whiteBox, faceHandle, faceHandles, normal))
  767. {
  768. // all halfedges for a given face
  769. const auto halfedges = FaceHalfedgeHandles(whiteBox, faceHandle);
  770. for (const HalfedgeHandle& halfedgeHandle : halfedges)
  771. {
  772. const FaceHandle oppositeFaceHandle = OppositeFaceHandle(whiteBox, halfedgeHandle);
  773. if (oppositeFaceHandle.IsValid())
  774. {
  775. SideFaceHandlesInternal(whiteBox, oppositeFaceHandle, faceHandles, normal);
  776. }
  777. }
  778. }
  779. }
  780. FaceHandles SideFaceHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  781. {
  782. AZ_PROFILE_FUNCTION(AzToolsFramework);
  783. FaceHandles faceHandles;
  784. SideFaceHandlesInternal(
  785. whiteBox, faceHandle, faceHandles, FaceNormal(whiteBox, faceHandle).GetNormalized());
  786. return faceHandles;
  787. }
  788. static HalfedgeHandlesCollection BorderHalfedgeHandles(
  789. const WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
  790. {
  791. AZ_PROFILE_FUNCTION(AzToolsFramework);
  792. // build all possible halfedge handles
  793. HalfedgeHandles halfedgeHandles;
  794. for (const auto& faceHandle : faceHandles)
  795. {
  796. // find all vertices for a given face
  797. const auto vertexHandles = FaceVertexHandles(whiteBox, faceHandle);
  798. for (const auto& vertexHandle : vertexHandles)
  799. {
  800. // find all outgoing halfedges from vertex
  801. const auto outgoingHalfedgeHandles = VertexOutgoingHalfedgeHandles(whiteBox, vertexHandle);
  802. for (const auto& halfedgeHandle : outgoingHalfedgeHandles)
  803. {
  804. // find what face corresponds to this halfedge
  805. const FaceHandle halfedgeFaceHandle = HalfedgeFaceHandle(whiteBox, halfedgeHandle);
  806. // if the halfedge corresponds to a face on this side
  807. if (AZStd::find(faceHandles.cbegin(), faceHandles.cend(), halfedgeFaceHandle) !=
  808. faceHandles.end())
  809. {
  810. // check the opposite face handle
  811. const FaceHandle oppositeFaceHandle = HalfedgeOppositeFaceHandle(whiteBox, halfedgeHandle);
  812. // if the opposite face handle isn't on this side, we know it is a 'boundary' halfedge
  813. if (AZStd::find(faceHandles.cbegin(), faceHandles.cend(), oppositeFaceHandle) ==
  814. faceHandles.end())
  815. {
  816. // check we haven't already stored this halfedge
  817. if (AZStd::find(halfedgeHandles.cbegin(), halfedgeHandles.cend(), halfedgeHandle) ==
  818. halfedgeHandles.cend())
  819. {
  820. // add to border halfedges
  821. halfedgeHandles.push_back(halfedgeHandle);
  822. }
  823. }
  824. }
  825. }
  826. }
  827. }
  828. // handle potentially pathological case where all edges have
  829. // been hidden and no halfedge loop can be found
  830. if (halfedgeHandles.empty())
  831. {
  832. return {};
  833. }
  834. HalfedgeHandlesCollection orderHalfedgeHandlesCollection;
  835. // can sort based on tip/tail
  836. HalfedgeHandles orderedHalfedgeHandles;
  837. orderedHalfedgeHandles.push_back(halfedgeHandles.back());
  838. halfedgeHandles.pop_back();
  839. // empty our list of unordered border side halfedge handles
  840. while (!halfedgeHandles.empty())
  841. {
  842. // use next vertex to get halfedges in order
  843. const VertexHandle nextVertex = HalfedgeVertexHandleAtTip(whiteBox, orderedHalfedgeHandles.back());
  844. // find next ordered halfedge
  845. const auto* const nextHalfedge = AZStd::find_if(
  846. halfedgeHandles.cbegin(), halfedgeHandles.cend(),
  847. [nextVertex, &whiteBox](auto halfedgeHandle)
  848. {
  849. return nextVertex == HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle);
  850. });
  851. // if we found it
  852. if (nextHalfedge != halfedgeHandles.end())
  853. {
  854. // add it to the ordered list and remove it from the unordered list
  855. orderedHalfedgeHandles.push_back(*nextHalfedge);
  856. halfedgeHandles[nextHalfedge - halfedgeHandles.begin()] = halfedgeHandles.back();
  857. halfedgeHandles.pop_back();
  858. }
  859. else
  860. {
  861. // cycle detected, start a new list
  862. orderHalfedgeHandlesCollection.push_back(orderedHalfedgeHandles);
  863. orderedHalfedgeHandles.clear();
  864. orderedHalfedgeHandles.push_back(halfedgeHandles.back());
  865. halfedgeHandles.pop_back();
  866. }
  867. }
  868. if (halfedgeHandles.empty())
  869. {
  870. AZ_Assert(!orderedHalfedgeHandles.empty(), "No ordered halfedges generated");
  871. orderHalfedgeHandlesCollection.push_back(orderedHalfedgeHandles);
  872. }
  873. // finally return the ordered list
  874. return orderHalfedgeHandlesCollection;
  875. }
  876. HalfedgeHandlesCollection SideBorderHalfedgeHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  877. {
  878. AZ_PROFILE_FUNCTION(AzToolsFramework);
  879. // find all face handles for a side
  880. return BorderHalfedgeHandles(whiteBox, SideFaceHandles(whiteBox, faceHandle));
  881. }
  882. static VertexHandlesCollection BorderVertexHandles(
  883. const WhiteBoxMesh& whiteBox, const HalfedgeHandlesCollection& halfedgeHandlesCollection)
  884. {
  885. AZ_PROFILE_FUNCTION(AzToolsFramework);
  886. VertexHandlesCollection orderedVertexHandlesCollection;
  887. orderedVertexHandlesCollection.reserve(halfedgeHandlesCollection.size());
  888. for (const auto& halfedgeHandles : halfedgeHandlesCollection)
  889. {
  890. VertexHandles orderedVertexHandles;
  891. orderedVertexHandles.reserve(halfedgeHandles.size());
  892. for (const auto& halfedgeHandle : halfedgeHandles)
  893. {
  894. orderedVertexHandles.push_back(HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
  895. }
  896. orderedVertexHandlesCollection.push_back(orderedVertexHandles);
  897. }
  898. return orderedVertexHandlesCollection;
  899. }
  900. VertexHandlesCollection SideBorderVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  901. {
  902. AZ_PROFILE_FUNCTION(AzToolsFramework);
  903. return BorderVertexHandles(whiteBox, SideBorderHalfedgeHandles(whiteBox, faceHandle));
  904. }
  905. static VertexHandles FacesVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
  906. {
  907. AZ_PROFILE_FUNCTION(AzToolsFramework);
  908. VertexHandles vertexHandles;
  909. for (const FaceHandle& faceHandle : faceHandles)
  910. {
  911. const auto faceVertexHandles = FaceVertexHandles(whiteBox, faceHandle);
  912. for (const VertexHandle& faceVertexHandle : faceVertexHandles)
  913. {
  914. const auto* const vertexIt =
  915. AZStd::find(vertexHandles.cbegin(), vertexHandles.cend(), faceVertexHandle);
  916. // ensure we do not add duplicate vertices
  917. if (vertexIt == vertexHandles.end())
  918. {
  919. vertexHandles.push_back(faceVertexHandle);
  920. }
  921. }
  922. }
  923. return vertexHandles;
  924. }
  925. VertexHandles SideVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  926. {
  927. AZ_PROFILE_FUNCTION(AzToolsFramework);
  928. return FacesVertexHandles(whiteBox, SideFaceHandles(whiteBox, faceHandle));
  929. }
  930. VertexHandle HalfedgeVertexHandleAtTip(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  931. {
  932. return wb_vh(whiteBox.mesh.to_vertex_handle(om_heh(halfedgeHandle)));
  933. }
  934. VertexHandle HalfedgeVertexHandleAtTail(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  935. {
  936. return wb_vh(whiteBox.mesh.from_vertex_handle(om_heh(halfedgeHandle)));
  937. }
  938. AZ::Vector3 HalfedgeVertexPositionAtTip(const WhiteBoxMesh& whiteBox, HalfedgeHandle halfedgeHandle)
  939. {
  940. return VertexPosition(whiteBox, HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
  941. }
  942. AZ::Vector3 HalfedgeVertexPositionAtTail(const WhiteBoxMesh& whiteBox, HalfedgeHandle halfedgeHandle)
  943. {
  944. return VertexPosition(whiteBox, HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle));
  945. }
  946. EdgeHandle HalfedgeEdgeHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  947. {
  948. return wb_eh(whiteBox.mesh.edge_handle(om_heh(halfedgeHandle)));
  949. }
  950. bool HalfedgeIsBoundary(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  951. {
  952. return whiteBox.mesh.is_boundary(om_heh(halfedgeHandle));
  953. }
  954. HalfedgeHandle HalfedgeHandleNext(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  955. {
  956. return wb_heh(whiteBox.mesh.next_halfedge_handle(om_heh(halfedgeHandle)));
  957. }
  958. HalfedgeHandle HalfedgeHandlePrevious(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  959. {
  960. return wb_heh(whiteBox.mesh.prev_halfedge_handle(om_heh(halfedgeHandle)));
  961. }
  962. static AZStd::array<AZ::Vector3, 2> EdgeVertexPositions(
  963. const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const VertexHandle vertexHandle)
  964. {
  965. if (const auto vertexHandles = EdgeVertexHandles(whiteBox, edgeHandle); vertexHandle.IsValid())
  966. {
  967. const auto otherVertexHandle = vertexHandles[0] == vertexHandle ? vertexHandles[1] : vertexHandles[0];
  968. return {VertexPosition(whiteBox, vertexHandle), VertexPosition(whiteBox, otherVertexHandle)};
  969. }
  970. else
  971. {
  972. return {VertexPosition(whiteBox, vertexHandles[0]), VertexPosition(whiteBox, vertexHandles[1])};
  973. }
  974. }
  975. AZStd::array<AZ::Vector3, 2> EdgeVertexPositions(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  976. {
  977. return EdgeVertexPositions(whiteBox, edgeHandle, VertexHandle{});
  978. }
  979. AZStd::array<VertexHandle, 2> EdgeVertexHandles(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  980. {
  981. // note: first halfedge handle should always exist
  982. if (const auto halfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
  983. halfedgeHandle.IsValid())
  984. {
  985. return {
  986. HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle),
  987. HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle)};
  988. }
  989. AZ_Assert(false, "Could not find Vertex Handles for Edge Handle %d", edgeHandle.Index());
  990. return {VertexHandle{}, VertexHandle{}};
  991. }
  992. // provide the ability to pass a vertex handle to explicitly determine the direction of the axis
  993. static AZ::Vector3 EdgeVectorWithStartingVertexHandle(
  994. const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const VertexHandle vertexHandle)
  995. {
  996. const auto edgeVertexPositions = EdgeVertexPositions(whiteBox, edgeHandle, vertexHandle);
  997. return (edgeVertexPositions[1] - edgeVertexPositions[0]);
  998. }
  999. AZ::Vector3 EdgeVector(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1000. {
  1001. return EdgeVectorWithStartingVertexHandle(whiteBox, edgeHandle, VertexHandle{});
  1002. }
  1003. // provide the ability to pass a vertex handle to explicitly determine the direction of the axis
  1004. static AZ::Vector3 EdgeAxisWithStartingVertexHandle(
  1005. const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const VertexHandle vertexHandle)
  1006. {
  1007. if (const AZ::Vector3 edgeVector = EdgeVectorWithStartingVertexHandle(whiteBox, edgeHandle, vertexHandle);
  1008. edgeVector.GetLength() > 0.0f)
  1009. {
  1010. return edgeVector / edgeVector.GetLength();
  1011. }
  1012. return AZ::Vector3::CreateZero();
  1013. }
  1014. AZ::Vector3 EdgeAxis(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1015. {
  1016. return EdgeAxisWithStartingVertexHandle(whiteBox, edgeHandle, VertexHandle{});
  1017. }
  1018. bool EdgeIsBoundary(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1019. {
  1020. return whiteBox.mesh.is_boundary(om_eh(edgeHandle));
  1021. }
  1022. // note: halfedge handle must be from the edge handle passed in
  1023. static bool EdgeIsUser(
  1024. const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle, const EdgeHandle edgeHandle)
  1025. {
  1026. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1027. const auto polygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(
  1028. whiteBox, FacePolygonHandle(whiteBox, HalfedgeFaceHandle(whiteBox, halfedgeHandle)));
  1029. return AZStd::find(polygonEdgeHandles.cbegin(), polygonEdgeHandles.cend(), edgeHandle) !=
  1030. polygonEdgeHandles.cend();
  1031. }
  1032. // note: overload of EdgeIsUser that does not require halfedgeHande to be
  1033. // passed in but does slightly more work
  1034. static bool EdgeIsUser(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1035. {
  1036. const auto halfedgeHandle = EdgeHalfedgeHandles(whiteBox, edgeHandle);
  1037. return AZStd::any_of(
  1038. AZStd::cbegin(halfedgeHandle), AZStd::cend(halfedgeHandle),
  1039. [&whiteBox, edgeHandle](const HalfedgeHandle halfedgeHandle)
  1040. {
  1041. return EdgeIsUser(whiteBox, halfedgeHandle, edgeHandle);
  1042. });
  1043. }
  1044. EdgeHandles EdgeGrouping(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1045. {
  1046. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1047. // a non-user ('mesh') edge is never part of a grouping so if one is passed
  1048. // in ensure we return an empty group
  1049. if (!EdgeIsUser(whiteBox, edgeHandle))
  1050. {
  1051. return EdgeHandles{};
  1052. }
  1053. // the edge group to begin building
  1054. EdgeHandles edgeGrouping = {edgeHandle};
  1055. // get vertex handles from the hovered/selected edge
  1056. const auto initialVertexHandles = Api::EdgeVertexHandles(whiteBox, edgeHandle);
  1057. auto vertexHandles = VertexHandles{initialVertexHandles.cbegin(), initialVertexHandles.cend()};
  1058. // track all vertices we've already seen
  1059. auto visitedVertexHandles = VertexHandles{};
  1060. while (!vertexHandles.empty())
  1061. {
  1062. const auto vertexHandle = vertexHandles.back();
  1063. vertexHandles.pop_back();
  1064. // if the vertex is not hidden this is where the search ends
  1065. if (!VertexIsHidden(whiteBox, vertexHandle))
  1066. {
  1067. continue;
  1068. }
  1069. visitedVertexHandles.push_back(vertexHandle);
  1070. // for all connected vertex handles to this edge
  1071. for (const auto& vertexEdgeHandle : VertexEdgeHandles(whiteBox, vertexHandle))
  1072. {
  1073. // check all halfedges in the edge
  1074. for (const auto& halfedgeHandle : EdgeHalfedgeHandles(whiteBox, vertexEdgeHandle))
  1075. {
  1076. // only track the edge if it's a 'user' edge (selectable - not a 'mesh' edge)
  1077. if (!EdgeIsUser(whiteBox, halfedgeHandle, vertexEdgeHandle))
  1078. {
  1079. continue;
  1080. }
  1081. // check if have we already added the edge to the grouping
  1082. if (AZStd::find(edgeGrouping.cbegin(), edgeGrouping.cend(), vertexEdgeHandle) !=
  1083. edgeGrouping.cend())
  1084. {
  1085. continue;
  1086. }
  1087. // store the edge to the grouping
  1088. edgeGrouping.push_back(vertexEdgeHandle);
  1089. for (const auto& nextVertexHandle : Api::EdgeVertexHandles(whiteBox, vertexEdgeHandle))
  1090. {
  1091. // if we haven't seen this vertex yet, add it to
  1092. // the vertex handles to explore
  1093. if (AZStd::find(
  1094. visitedVertexHandles.cbegin(), visitedVertexHandles.cend(), nextVertexHandle) ==
  1095. visitedVertexHandles.cend())
  1096. {
  1097. vertexHandles.push_back(nextVertexHandle);
  1098. }
  1099. }
  1100. }
  1101. }
  1102. }
  1103. return edgeGrouping;
  1104. }
  1105. bool EdgeIsHidden(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1106. {
  1107. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1108. const EdgeHandles userEdgeHandles = MeshPolygonEdgeHandles(whiteBox);
  1109. return AZStd::find(userEdgeHandles.cbegin(), userEdgeHandles.cend(), edgeHandle) == userEdgeHandles.cend();
  1110. }
  1111. AZStd::vector<FaceHandle> EdgeFaceHandles(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1112. {
  1113. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1114. const auto openMeshEdgeHandle = om_eh(edgeHandle);
  1115. const auto firstHalfedgeHandle = whiteBox.mesh.halfedge_handle(openMeshEdgeHandle, 0);
  1116. const auto secondHalfedgeHandle = whiteBox.mesh.halfedge_handle(openMeshEdgeHandle, 1);
  1117. AZ_Assert(
  1118. firstHalfedgeHandle.is_valid() || secondHalfedgeHandle.is_valid(),
  1119. "There should be at least one valid half edge handle for any given edge");
  1120. AZStd::vector<FaceHandle> validFaceHandles;
  1121. // only one face handle is valid at mesh boundaries
  1122. if (const auto firstFaceHandle = whiteBox.mesh.face_handle(firstHalfedgeHandle); firstFaceHandle.is_valid())
  1123. {
  1124. validFaceHandles.push_back(wb_fh(firstFaceHandle));
  1125. }
  1126. if (const auto secondFaceHandle = whiteBox.mesh.face_handle(secondHalfedgeHandle);
  1127. secondFaceHandle.is_valid())
  1128. {
  1129. validFaceHandles.push_back(wb_fh(secondFaceHandle));
  1130. }
  1131. return validFaceHandles;
  1132. }
  1133. static size_t EdgeHalfedgeMapping(const EdgeHalfedge edgeHalfedge)
  1134. {
  1135. switch (edgeHalfedge)
  1136. {
  1137. case EdgeHalfedge::First:
  1138. return 0;
  1139. case EdgeHalfedge::Second:
  1140. return 1;
  1141. default:
  1142. AZ_Assert(false, "Invalid EdgeHalfedge type passed");
  1143. return 2;
  1144. }
  1145. }
  1146. HalfedgeHandle EdgeHalfedgeHandle(
  1147. const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const EdgeHalfedge edgeHalfedge)
  1148. {
  1149. return wb_heh(whiteBox.mesh.halfedge_handle(om_eh(edgeHandle), static_cast<unsigned int>(EdgeHalfedgeMapping(edgeHalfedge))));
  1150. }
  1151. HalfedgeHandles EdgeHalfedgeHandles(const WhiteBoxMesh& whiteBox, EdgeHandle edgeHandle)
  1152. {
  1153. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1154. const AZStd::array<HalfedgeHandle, 2> halfedgeHandles = {
  1155. EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First),
  1156. EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second)};
  1157. return AZStd::accumulate(
  1158. halfedgeHandles.cbegin(), halfedgeHandles.cend(), HalfedgeHandles{},
  1159. [&whiteBox](HalfedgeHandles halfedgeHandles, const HalfedgeHandle halfedgeHandle)
  1160. {
  1161. if (!HalfedgeIsBoundary(whiteBox, halfedgeHandle))
  1162. {
  1163. halfedgeHandles.push_back(halfedgeHandle);
  1164. }
  1165. return halfedgeHandles;
  1166. });
  1167. }
  1168. void TranslateEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
  1169. {
  1170. WHITEBOX_LOG(
  1171. "White Box", "TranslateEdge eh(%s) %s", ToString(edgeHandle).c_str(), AZStd::to_string(displacement).c_str());
  1172. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1173. const auto vertexHandles = EdgeVertexHandles(whiteBox, edgeHandle);
  1174. for (const auto& vertexHandle : vertexHandles)
  1175. {
  1176. auto position = VertexPosition(whiteBox, vertexHandle);
  1177. position += displacement;
  1178. SetVertexPosition(whiteBox, vertexHandle, position);
  1179. }
  1180. CalculateNormals(whiteBox);
  1181. CalculatePlanarUVs(whiteBox);
  1182. }
  1183. // Given a displacement in local space applied to an edge, find the halfedge handle that the
  1184. // edge is most likely moving towards. We're attempting to infer the user's intention which is
  1185. // never perfect so there's a chance we may not return the edge the user expects. On the
  1186. // whole the heuristic used (delta distance moved towards a connected face midpoint) is pretty stable.
  1187. static HalfedgeHandle FindBestFitHalfedge(
  1188. WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
  1189. {
  1190. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1191. // get both halfedge handles for the edge (0 and 1 just correspond to each halfedge)
  1192. const HalfedgeHandle firstHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
  1193. const HalfedgeHandle secondHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second);
  1194. // get all vertices for each face (triangle) that each halfedge corresponds to
  1195. const auto firstFaceVertexHandles =
  1196. FaceVertexHandles(whiteBox, HalfedgeFaceHandle(whiteBox, firstHalfedgeHandle));
  1197. const auto secondFaceVertexHandles =
  1198. FaceVertexHandles(whiteBox, HalfedgeFaceHandle(whiteBox, secondHalfedgeHandle));
  1199. // calculate the midpoint of each face
  1200. const auto firstFaceMidpoint = VerticesMidpoint(whiteBox, firstFaceVertexHandles);
  1201. const auto secondFaceMidpoint = VerticesMidpoint(whiteBox, secondFaceVertexHandles);
  1202. // calculate the midpoint of the edge we wish to append to and where it will be after the displacement
  1203. const auto edgeMidpoint = EdgeMidpoint(whiteBox, edgeHandle);
  1204. const auto nextEdgePosition = edgeMidpoint + displacement;
  1205. // calculate how far the center of each face is from the edge midpoint
  1206. const auto distanceFromFirstFace = (firstFaceMidpoint - edgeMidpoint).GetLength();
  1207. const auto distanceFromSecondFace = (secondFaceMidpoint - edgeMidpoint).GetLength();
  1208. // then calculate how far the center of each face is from the edge midpoint plus the displacement
  1209. const auto nextDistanceFromFirstFace = (nextEdgePosition - firstFaceMidpoint).GetLength();
  1210. const auto nextDistanceFromSecondFace = (nextEdgePosition - secondFaceMidpoint).GetLength();
  1211. // next see what the delta is from next and current positions
  1212. // this is to determine did the displacement move us towards the first or second face
  1213. // i.e. infer which way the user dragged
  1214. const auto nextDeltaFromFirstFace = nextDistanceFromFirstFace - distanceFromFirstFace;
  1215. const auto nextDeltaFromSecondFace = nextDistanceFromSecondFace - distanceFromSecondFace;
  1216. // pick the best halfedge we inferred
  1217. const EdgeHalfedge halfedge =
  1218. nextDeltaFromFirstFace < nextDeltaFromSecondFace ? EdgeHalfedge::First : EdgeHalfedge::Second;
  1219. return EdgeHalfedgeHandle(whiteBox, edgeHandle, halfedge);
  1220. }
  1221. // Determine the vertices required to append the new edge geometry being created
  1222. static Internal::EdgeAppendVertexHandles CalculateEdgeAppendVertexHandles(
  1223. WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
  1224. {
  1225. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1226. // based on the displacement find which halfedge is a better fit (which direction did we move in)
  1227. const HalfedgeHandle halfedgeHandle = FindBestFitHalfedge(whiteBox, edgeHandle, displacement);
  1228. const FaceHandle faceHandle = HalfedgeFaceHandle(whiteBox, halfedgeHandle);
  1229. // find the polygon this face handle corresponds to
  1230. const PolygonHandle polygonHandle = FacePolygonHandle(whiteBox, faceHandle);
  1231. // find all border vertex handles for this polygon
  1232. const VertexHandlesCollection polygonBorderVertexHandlesCollection =
  1233. PolygonBorderVertexHandles(whiteBox, polygonHandle);
  1234. // following the direction of the halfedge, what vertex is it pointing to
  1235. const VertexHandle toVertexHandle = HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle);
  1236. // following the direction of the halfedge, what vertex is coming from
  1237. const VertexHandle fromVertexHandle = HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle);
  1238. // find which vertex loop the vertex is in based on the halfedge we've selected
  1239. const VertexHandles borderVertexHandles = AZStd::accumulate(
  1240. AZStd::begin(polygonBorderVertexHandlesCollection), AZStd::end(polygonBorderVertexHandlesCollection),
  1241. VertexHandles{},
  1242. [toVertexHandle](VertexHandles borderVertexHandles, const VertexHandles& vertexHandles)
  1243. {
  1244. // check if the vertex is in this loop of the collection
  1245. // (there may be 1 - * loops in the collection)
  1246. if (AZStd::find(AZStd::begin(vertexHandles), AZStd::end(vertexHandles), toVertexHandle) !=
  1247. AZStd::end(vertexHandles))
  1248. {
  1249. // if so add the vertex handles for this loop to be returned
  1250. borderVertexHandles.insert(
  1251. borderVertexHandles.end(), vertexHandles.begin(), vertexHandles.end());
  1252. }
  1253. return borderVertexHandles;
  1254. });
  1255. // find the index of the vertex handle in the polygon handle collection
  1256. const auto toVertexHandlePolygonIndex =
  1257. AZStd::find(borderVertexHandles.begin(), borderVertexHandles.end(), toVertexHandle) -
  1258. borderVertexHandles.begin();
  1259. const auto fromVertexHandlePolygonIndex =
  1260. AZStd::find(borderVertexHandles.begin(), borderVertexHandles.end(), fromVertexHandle) -
  1261. borderVertexHandles.begin();
  1262. // we then want to find the vertex after the 'to' vertex, and the vertex before the 'from' vertex
  1263. const VertexHandle afterToVertexHandle = borderVertexHandles
  1264. [((toVertexHandlePolygonIndex + borderVertexHandles.size()) + 1) % borderVertexHandles.size()];
  1265. const VertexHandle beforeFromVertexHandle = borderVertexHandles
  1266. [((fromVertexHandlePolygonIndex + borderVertexHandles.size()) - 1) % borderVertexHandles.size()];
  1267. // find the position of the 'to' and 'from' vertex handle
  1268. const Mesh::Point toVertexPosition = VertexPosition(whiteBox, toVertexHandle);
  1269. const Mesh::Point fromVertexPosition = VertexPosition(whiteBox, fromVertexHandle);
  1270. // find the next position by moving the previous positions by the displacement
  1271. const Mesh::Point nextToVertexPosition = toVertexPosition + displacement;
  1272. const Mesh::Point nextFromVertexPosition = fromVertexPosition + displacement;
  1273. // add two new vertices in the new positions
  1274. const VertexHandle addedToVertexHandle = AddVertex(whiteBox, nextToVertexPosition);
  1275. const VertexHandle addedFromVertexHandle = AddVertex(whiteBox, nextFromVertexPosition);
  1276. // populate data for the next stage
  1277. Internal::EdgeAppendVertexHandles edgeAppendHandles;
  1278. edgeAppendHandles.m_existingPolygonHandle = polygonHandle;
  1279. edgeAppendHandles.m_toVertexHandle = toVertexHandle;
  1280. edgeAppendHandles.m_fromVertexHandle = fromVertexHandle;
  1281. edgeAppendHandles.m_addedFromVertexHandle = addedFromVertexHandle;
  1282. edgeAppendHandles.m_addedToVertexHandle = addedToVertexHandle;
  1283. edgeAppendHandles.m_afterToVertexHandle = afterToVertexHandle;
  1284. edgeAppendHandles.m_beforeFromVertexHandle = beforeFromVertexHandle;
  1285. return edgeAppendHandles;
  1286. }
  1287. // After determining the vertex handles required, build the polygons for the new appended edge
  1288. static Internal::EdgeAppendPolygonHandles AddNewPolygonsForEdgeAppend(
  1289. WhiteBoxMesh& whiteBox, const Internal::EdgeAppendVertexHandles& edgeAppendVertexHandles)
  1290. {
  1291. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1292. Internal::EdgeAppendPolygonHandles edgeAppendPolygonHandles;
  1293. // build two faces to make up the polygon on the 'near' side of the edge
  1294. AZStd::vector<FaceVertHandles> nearFaceHandles = {
  1295. FaceVertHandles{
  1296. edgeAppendVertexHandles.m_fromVertexHandle, edgeAppendVertexHandles.m_toVertexHandle,
  1297. edgeAppendVertexHandles.m_addedToVertexHandle},
  1298. FaceVertHandles{
  1299. edgeAppendVertexHandles.m_fromVertexHandle, edgeAppendVertexHandles.m_addedToVertexHandle,
  1300. edgeAppendVertexHandles.m_addedFromVertexHandle}};
  1301. edgeAppendPolygonHandles.m_nearPolygonHandle = AddPolygon(whiteBox, nearFaceHandles);
  1302. AZStd::vector<FaceVertHandles> farFaceHandles;
  1303. // note: need to check the number of faces for the polygon we'll be replacing with the edge append
  1304. if (edgeAppendVertexHandles.m_existingPolygonHandle.m_faceHandles.size() > 1)
  1305. {
  1306. // build two faces to make up the polygon on the 'far' side of the edge
  1307. farFaceHandles = {
  1308. FaceVertHandles{
  1309. edgeAppendVertexHandles.m_addedFromVertexHandle, edgeAppendVertexHandles.m_addedToVertexHandle,
  1310. edgeAppendVertexHandles.m_afterToVertexHandle},
  1311. FaceVertHandles{
  1312. edgeAppendVertexHandles.m_addedFromVertexHandle, edgeAppendVertexHandles.m_afterToVertexHandle,
  1313. edgeAppendVertexHandles.m_beforeFromVertexHandle}};
  1314. }
  1315. else
  1316. {
  1317. // build one face to make up the polygon on the 'far' side of the edge
  1318. // if we're extruding an edge on a triangle not a quad
  1319. farFaceHandles = {FaceVertHandles{
  1320. edgeAppendVertexHandles.m_addedFromVertexHandle, edgeAppendVertexHandles.m_addedToVertexHandle,
  1321. edgeAppendVertexHandles.m_afterToVertexHandle}};
  1322. }
  1323. edgeAppendPolygonHandles.m_farPolygonHandle = AddPolygon(whiteBox, farFaceHandles);
  1324. // add the top triangle for the edge extrusion
  1325. const AZStd::vector<FaceVertHandles> topFaceHandles = {FaceVertHandles{
  1326. edgeAppendVertexHandles.m_fromVertexHandle, edgeAppendVertexHandles.m_addedFromVertexHandle,
  1327. edgeAppendVertexHandles.m_beforeFromVertexHandle}};
  1328. edgeAppendPolygonHandles.m_topPolygonHandle = AddPolygon(whiteBox, topFaceHandles);
  1329. // add the bottom triangle for the edge extrusion
  1330. const AZStd::vector<FaceVertHandles> bottomFaceHandles = {FaceVertHandles{
  1331. edgeAppendVertexHandles.m_toVertexHandle, edgeAppendVertexHandles.m_afterToVertexHandle,
  1332. edgeAppendVertexHandles.m_addedToVertexHandle}};
  1333. edgeAppendPolygonHandles.m_bottomPolygonHandle = AddPolygon(whiteBox, bottomFaceHandles);
  1334. return edgeAppendPolygonHandles;
  1335. }
  1336. // given two polygon handles, return the (first) edge that is shared between the two polygons
  1337. // note: this may not always give expected results for polygons with greater than two faces
  1338. static EdgeHandle FindSelectedEdgeHandle(
  1339. const WhiteBoxMesh& whiteBox, const PolygonHandle& nearPolygonHandle, const PolygonHandle& farPolygonHandle)
  1340. {
  1341. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1342. // actually find the new edge we created
  1343. const EdgeHandles nearEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, nearPolygonHandle);
  1344. const EdgeHandles farEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, farPolygonHandle);
  1345. // add all edges and find the one duplicate (this will be the new edge we want to return to the caller)
  1346. EdgeHandles allEdgeHandles;
  1347. allEdgeHandles.reserve(nearEdgeHandles.size() + farEdgeHandles.size());
  1348. allEdgeHandles.insert(allEdgeHandles.end(), nearEdgeHandles.begin(), nearEdgeHandles.end());
  1349. allEdgeHandles.insert(allEdgeHandles.end(), farEdgeHandles.begin(), farEdgeHandles.end());
  1350. AZStd::sort(allEdgeHandles.begin(), allEdgeHandles.end());
  1351. auto* edgeIt = AZStd::adjacent_find(allEdgeHandles.begin(), allEdgeHandles.end());
  1352. if (edgeIt != allEdgeHandles.end())
  1353. {
  1354. return *edgeIt;
  1355. }
  1356. return EdgeHandle{};
  1357. }
  1358. static bool EdgeExtrusionAllowed(const PolygonHandle& polygonHandle)
  1359. {
  1360. // currently only allow edge extrusion for quad polygons
  1361. return polygonHandle.m_faceHandles.size() <= 2;
  1362. }
  1363. EdgeHandle TranslateEdgeAppend(
  1364. WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
  1365. {
  1366. WHITEBOX_LOG(
  1367. "White Box", "TranslateEdgeAppend eh(%s) %s", ToString(edgeHandle).c_str(), AZStd::to_string(displacement).c_str());
  1368. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1369. // the new and existing handles required for an edge append
  1370. const Internal::EdgeAppendVertexHandles edgeAppendVertexHandles =
  1371. CalculateEdgeAppendVertexHandles(whiteBox, edgeHandle, displacement);
  1372. // if edge extrusion is not allowed simply return the previous edge handle
  1373. if (!EdgeExtrusionAllowed(edgeAppendVertexHandles.m_existingPolygonHandle))
  1374. {
  1375. return edgeHandle;
  1376. }
  1377. // remove the current polygon (two new polygons will later be inserted in its place)
  1378. RemoveFaces(whiteBox, edgeAppendVertexHandles.m_existingPolygonHandle.m_faceHandles);
  1379. const Internal::EdgeAppendPolygonHandles edgeAppendPolygonHandles =
  1380. AddNewPolygonsForEdgeAppend(whiteBox, edgeAppendVertexHandles);
  1381. // update internal state
  1382. CalculateNormals(whiteBox);
  1383. CalculatePlanarUVs(whiteBox);
  1384. return FindSelectedEdgeHandle(
  1385. whiteBox, edgeAppendPolygonHandles.m_nearPolygonHandle, edgeAppendPolygonHandles.m_farPolygonHandle);
  1386. }
  1387. AZ::Vector3 PolygonNormal(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1388. {
  1389. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1390. return AZStd::accumulate(
  1391. polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend(),
  1392. AZ::Vector3::CreateZero(),
  1393. [&whiteBox](const AZ::Vector3& normal, const FaceHandle faceHandle)
  1394. {
  1395. return normal + FaceNormal(whiteBox, faceHandle);
  1396. })
  1397. .GetNormalizedSafe();
  1398. }
  1399. PolygonHandle FacePolygonHandle(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  1400. {
  1401. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1402. PolygonPropertyHandle polygonPropsHandle;
  1403. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  1404. const auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  1405. const auto polygonIt = polygonProps.find(om_fh(faceHandle));
  1406. if (polygonIt != polygonProps.end())
  1407. {
  1408. return PolygonHandleFromInternal(polygonIt->second);
  1409. }
  1410. return {};
  1411. }
  1412. VertexHandles PolygonVertexHandles(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1413. {
  1414. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1415. return FacesVertexHandles(whiteBox, polygonHandle.m_faceHandles);
  1416. }
  1417. VertexHandlesCollection PolygonBorderVertexHandles(
  1418. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1419. {
  1420. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1421. return BorderVertexHandles(whiteBox, PolygonBorderHalfedgeHandles(whiteBox, polygonHandle));
  1422. }
  1423. VertexHandles PolygonBorderVertexHandlesFlattened(
  1424. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1425. {
  1426. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1427. const VertexHandlesCollection borderVertexHandlesCollection =
  1428. BorderVertexHandles(whiteBox, PolygonBorderHalfedgeHandles(whiteBox, polygonHandle));
  1429. VertexHandles polygonBorderVertexHandles;
  1430. for (const auto& borderVertexHandles : borderVertexHandlesCollection)
  1431. {
  1432. polygonBorderVertexHandles.insert(
  1433. polygonBorderVertexHandles.end(), borderVertexHandles.cbegin(), borderVertexHandles.cend());
  1434. }
  1435. return polygonBorderVertexHandles;
  1436. }
  1437. HalfedgeHandles PolygonBorderHalfedgeHandlesFlattened(
  1438. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1439. {
  1440. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1441. const HalfedgeHandlesCollection borderHalfedgeHandlesCollection =
  1442. PolygonBorderHalfedgeHandles(whiteBox, polygonHandle);
  1443. HalfedgeHandles polygonBorderHalfedgeHandles;
  1444. for (const auto& borderHalfedgeHandles : borderHalfedgeHandlesCollection)
  1445. {
  1446. polygonBorderHalfedgeHandles.insert(
  1447. polygonBorderHalfedgeHandles.end(), borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend());
  1448. }
  1449. return polygonBorderHalfedgeHandles;
  1450. }
  1451. HalfedgeHandles PolygonHalfedgeHandles(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1452. {
  1453. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1454. return AZStd::accumulate(
  1455. polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend(), HalfedgeHandles{},
  1456. [&whiteBox](HalfedgeHandles halfedges, const FaceHandle faceHandle)
  1457. {
  1458. const HalfedgeHandles nextHalfedgeHandles = FaceHalfedgeHandles(whiteBox, faceHandle);
  1459. halfedges.insert(halfedges.end(), nextHalfedgeHandles.cbegin(), nextHalfedgeHandles.cend());
  1460. return halfedges;
  1461. });
  1462. }
  1463. HalfedgeHandlesCollection PolygonBorderHalfedgeHandles(
  1464. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1465. {
  1466. return BorderHalfedgeHandles(whiteBox, polygonHandle.m_faceHandles);
  1467. }
  1468. AZStd::vector<AZ::Vector3> PolygonVertexPositions(
  1469. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1470. {
  1471. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1472. return VertexPositions(whiteBox, PolygonVertexHandles(whiteBox, polygonHandle));
  1473. }
  1474. VertexPositionsCollection PolygonBorderVertexPositions(
  1475. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1476. {
  1477. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1478. const auto polygonBorderVertexHandlesCollection = PolygonBorderVertexHandles(whiteBox, polygonHandle);
  1479. VertexPositionsCollection polygonBorderVertexPositionsCollection;
  1480. polygonBorderVertexPositionsCollection.reserve(polygonBorderVertexHandlesCollection.size());
  1481. for (const auto& polygonBorderVertexHandles : polygonBorderVertexHandlesCollection)
  1482. {
  1483. polygonBorderVertexPositionsCollection.push_back(VertexPositions(whiteBox, polygonBorderVertexHandles));
  1484. }
  1485. return polygonBorderVertexPositionsCollection;
  1486. }
  1487. AZStd::vector<AZ::Vector3> PolygonFacesPositions(
  1488. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  1489. {
  1490. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1491. return FacesPositions(whiteBox, polygonHandle.m_faceHandles);
  1492. }
  1493. AZ::Vector3 VertexPosition(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1494. {
  1495. return whiteBox.mesh.point(om_vh(vertexHandle));
  1496. }
  1497. AZStd::vector<AZ::Vector3> VertexPositions(const WhiteBoxMesh& whiteBox, const VertexHandles& vertexHandles)
  1498. {
  1499. AZStd::vector<AZ::Vector3> positions;
  1500. positions.reserve(vertexHandles.size());
  1501. AZStd::transform(
  1502. vertexHandles.cbegin(), vertexHandles.cend(), AZStd::back_inserter(positions),
  1503. [&whiteBox](const auto vertexHandle)
  1504. {
  1505. return VertexPosition(whiteBox, vertexHandle);
  1506. });
  1507. return positions;
  1508. }
  1509. EdgeHandles VertexUserEdgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1510. {
  1511. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1512. auto vertexEdgeHandles = VertexEdgeHandles(whiteBox, vertexHandle);
  1513. vertexEdgeHandles.erase(
  1514. AZStd::remove_if(
  1515. AZStd::begin(vertexEdgeHandles), AZStd::end(vertexEdgeHandles),
  1516. [&whiteBox](const EdgeHandle edgeHandle)
  1517. {
  1518. return !EdgeIsUser(whiteBox, edgeHandle);
  1519. }),
  1520. AZStd::end(vertexEdgeHandles));
  1521. return vertexEdgeHandles;
  1522. }
  1523. template<typename EdgeFn>
  1524. static AZStd::vector<AZ::Vector3> VertexUserEdges(
  1525. const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle, EdgeFn&& edgeFn)
  1526. {
  1527. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1528. const auto vertexEdgeHandles = VertexUserEdgeHandles(whiteBox, vertexHandle);
  1529. AZStd::vector<AZ::Vector3> edgeVectors;
  1530. edgeVectors.reserve(vertexEdgeHandles.size());
  1531. AZStd::transform(
  1532. AZStd::cbegin(vertexEdgeHandles), AZStd::cend(vertexEdgeHandles), AZStd::back_inserter(edgeVectors),
  1533. [&whiteBox, edgeFn, vertexHandle](const EdgeHandle edgeHandle)
  1534. {
  1535. return edgeFn(whiteBox, edgeHandle, vertexHandle);
  1536. });
  1537. // filter out any invalid edges
  1538. edgeVectors.erase(
  1539. AZStd::remove_if(
  1540. AZStd::begin(edgeVectors), AZStd::end(edgeVectors),
  1541. [](const AZ::Vector3& edge)
  1542. {
  1543. return AZ::IsCloseMag<float>(edge.GetLengthSq(), 0.0f);
  1544. }),
  1545. AZStd::end(edgeVectors));
  1546. return edgeVectors;
  1547. }
  1548. AZStd::vector<AZ::Vector3> VertexUserEdgeVectors(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1549. {
  1550. return VertexUserEdges(whiteBox, vertexHandle, &EdgeVectorWithStartingVertexHandle);
  1551. }
  1552. AZStd::vector<AZ::Vector3> VertexUserEdgeAxes(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1553. {
  1554. return VertexUserEdges(whiteBox, vertexHandle, &EdgeAxisWithStartingVertexHandle);
  1555. }
  1556. bool VertexIsHidden(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1557. {
  1558. VertexBoolPropertyHandle vertexPropsHiddenHandle;
  1559. whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
  1560. return whiteBox.mesh.property(vertexPropsHiddenHandle, om_vh(vertexHandle));
  1561. }
  1562. bool VertexIsIsolated(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1563. {
  1564. const auto connectedEdgeHandles = VertexEdgeHandles(whiteBox, vertexHandle);
  1565. return AZStd::all_of(
  1566. AZStd::cbegin(connectedEdgeHandles), AZStd::cend(connectedEdgeHandles),
  1567. [&whiteBox](const EdgeHandle edgeHandle)
  1568. {
  1569. return !EdgeIsUser(whiteBox, edgeHandle);
  1570. });
  1571. }
  1572. AZ::Vector3 FaceNormal(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  1573. {
  1574. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1575. return whiteBox.mesh.normal(om_fh(faceHandle));
  1576. }
  1577. AZ::Vector2 HalfedgeUV(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
  1578. {
  1579. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1580. return whiteBox.mesh.texcoord2D(om_heh(halfedgeHandle));
  1581. }
  1582. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1583. // note: MeshXXXCount functions are only valid if garbage_collection is called after
  1584. // each face/vertex removal. If garbage_collection is deferred, the faces()/vertices()/halfedges()
  1585. // range must be used to count iterations via skipping iterator to ignore deleted faces
  1586. AZ::u64 MeshFaceCount(const WhiteBoxMesh& whiteBox)
  1587. {
  1588. return static_cast<AZ::u64>(whiteBox.mesh.n_faces());
  1589. }
  1590. AZ::u64 MeshHalfedgeCount(const WhiteBoxMesh& whiteBox)
  1591. {
  1592. return static_cast<AZ::u64>(whiteBox.mesh.n_halfedges());
  1593. }
  1594. AZ::u64 MeshVertexCount(const WhiteBoxMesh& whiteBox)
  1595. {
  1596. return static_cast<AZ::u64>(whiteBox.mesh.n_vertices());
  1597. }
  1598. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1599. Faces MeshFaces(const WhiteBoxMesh& whiteBox)
  1600. {
  1601. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1602. Faces faces;
  1603. faces.reserve(MeshFaceCount(whiteBox));
  1604. for (const auto& faceHandle : MeshFaceHandles(whiteBox))
  1605. {
  1606. const auto halfEdgeHandles = FaceHalfedgeHandles(whiteBox, faceHandle);
  1607. Face face;
  1608. AZStd::transform(
  1609. halfEdgeHandles.begin(), halfEdgeHandles.end(), face.begin(),
  1610. [&whiteBox](const auto halfedgeHandle)
  1611. {
  1612. // calculate the position of each vertex at the tip of each vertex handle
  1613. return VertexPosition(whiteBox, HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
  1614. });
  1615. faces.push_back(face);
  1616. }
  1617. return faces;
  1618. }
  1619. void CalculatePlanarUVs(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
  1620. {
  1621. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1622. auto& mesh = whiteBox.mesh;
  1623. for (const auto& faceHandle : faceHandles)
  1624. {
  1625. for (Mesh::ConstFaceHalfedgeCCWIter faceHalfedgeIt = mesh.fh_ccwiter(om_fh(faceHandle));
  1626. faceHalfedgeIt.is_valid(); ++faceHalfedgeIt)
  1627. {
  1628. const Mesh::HalfedgeHandle heh = *faceHalfedgeIt;
  1629. const Mesh::VertexHandle vh = mesh.to_vertex_handle(heh);
  1630. const AZ::Vector3 position = mesh.point(vh);
  1631. const AZ::Vector3 normal = FaceNormal(whiteBox, faceHandle);
  1632. const Mesh::TexCoord2D uv = CreatePlanarUVFromVertex(normal, position);
  1633. mesh.set_texcoord2D(heh, uv);
  1634. }
  1635. }
  1636. }
  1637. void CalculatePlanarUVs(WhiteBoxMesh& whiteBox)
  1638. {
  1639. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1640. CalculatePlanarUVs(whiteBox, MeshFaceHandles(whiteBox));
  1641. }
  1642. static PolygonHandle MergeFaces(
  1643. const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle,
  1644. const HalfedgeHandle oppositeHalfedgeHandle, const HalfedgeHandles& borderHalfedgeHandles,
  1645. const EdgeHandles& buildingEdgeHandles)
  1646. {
  1647. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1648. // the polygon handle to build
  1649. PolygonHandle polygonHandle;
  1650. // begin populating halfedges to visit to build a polygon
  1651. HalfedgeHandles halfedgesToVisit{halfedgeHandle};
  1652. // store already visited halfedges
  1653. HalfedgeHandles visitedHalfedges;
  1654. while (!halfedgesToVisit.empty())
  1655. {
  1656. const HalfedgeHandle halfedgeToVisit = halfedgesToVisit.back();
  1657. halfedgesToVisit.pop_back();
  1658. visitedHalfedges.push_back(halfedgeToVisit);
  1659. const FaceHandle faceHandleToVisit = HalfedgeFaceHandle(whiteBox, halfedgeToVisit);
  1660. const HalfedgeHandles faceHalfedges = FaceHalfedgeHandles(whiteBox, faceHandleToVisit);
  1661. // check we have not already visited this face handle
  1662. if (AZStd::find(
  1663. polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend(), faceHandleToVisit) !=
  1664. polygonHandle.m_faceHandles.cend())
  1665. {
  1666. continue;
  1667. }
  1668. // store the face handle in this polygon
  1669. polygonHandle.m_faceHandles.push_back(faceHandleToVisit);
  1670. // for all halfedges
  1671. for (const auto& faceHalfedgeHandle : faceHalfedges)
  1672. {
  1673. const EdgeHandle edgeHandle = HalfedgeEdgeHandle(whiteBox, faceHalfedgeHandle);
  1674. // if we haven't seen this halfedge before and we want to track it,
  1675. // store it in visited halfedges
  1676. if (faceHalfedgeHandle != oppositeHalfedgeHandle
  1677. // ignore border halfedges (not inside the polygon)
  1678. && AZStd::find(borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend(), faceHalfedgeHandle) ==
  1679. borderHalfedgeHandles.cend()
  1680. // ensure we do not visit the same halfedge again
  1681. && AZStd::find(visitedHalfedges.cbegin(), visitedHalfedges.cend(), faceHalfedgeHandle) ==
  1682. visitedHalfedges.cend()
  1683. // ignore the halfedge if we've already tracked it in our 'building' list
  1684. && AZStd::find(buildingEdgeHandles.cbegin(), buildingEdgeHandles.cend(), edgeHandle) ==
  1685. buildingEdgeHandles.cend())
  1686. {
  1687. halfedgesToVisit.push_back(HalfedgeOppositeHalfedgeHandle(whiteBox, faceHalfedgeHandle));
  1688. }
  1689. }
  1690. }
  1691. // return the polygon we've built by traversing all connected face handles
  1692. // (by following the connected halfedges)
  1693. return polygonHandle;
  1694. }
  1695. static void PopulatePolygonProps(FaceHandlePolygonMapping& polygonProps, const FaceHandles& faceHandles)
  1696. {
  1697. for (const auto& faceHandle : faceHandles)
  1698. {
  1699. auto polygonIt = polygonProps.find(om_fh(faceHandle));
  1700. for (const auto& innerFaceHandle : faceHandles)
  1701. {
  1702. polygonIt->second.push_back(om_fh(innerFaceHandle));
  1703. }
  1704. }
  1705. }
  1706. static void ClearPolygonProps(FaceHandlePolygonMapping& polygonProps, const FaceHandles& faceHandles)
  1707. {
  1708. for (const auto& faceHandle : faceHandles)
  1709. {
  1710. if (auto polygonIt = polygonProps.find(om_fh(faceHandle)); polygonIt != polygonProps.end())
  1711. {
  1712. polygonIt->second.clear();
  1713. }
  1714. }
  1715. }
  1716. // restore all vertices along the restored edges (after creating a new polygon)
  1717. static void RestoreVertexHandlesForEdges(WhiteBoxMesh& whiteBox, const EdgeHandles& restoredEdgeHandles)
  1718. {
  1719. for (const auto& edgeHandle : restoredEdgeHandles)
  1720. {
  1721. for (const auto& vertexHandle : EdgeVertexHandles(whiteBox, edgeHandle))
  1722. {
  1723. RestoreVertex(whiteBox, vertexHandle);
  1724. }
  1725. }
  1726. }
  1727. AZStd::optional<AZStd::array<PolygonHandle, 2>> RestoreEdge(
  1728. WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, EdgeHandles& restoringEdgeHandles)
  1729. {
  1730. WHITEBOX_LOG("White Box", "RestoreEdge eh(%s)", ToString(edgeHandle).c_str());
  1731. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1732. // check we're not selecting an existing user edge
  1733. if (!EdgeIsHidden(whiteBox, edgeHandle))
  1734. {
  1735. // do nothing
  1736. return {};
  1737. }
  1738. // attempt to make a new polygon if possible
  1739. const HalfedgeHandle firstHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
  1740. const HalfedgeHandle secondHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second);
  1741. // the existing polygon, containing the edge we've selected
  1742. const PolygonHandle polygonHandle =
  1743. FacePolygonHandle(whiteBox, HalfedgeFaceHandle(whiteBox, firstHalfedgeHandle));
  1744. // all border halfedges (not necessarily contiguous)
  1745. const HalfedgeHandles polygonBorderHalfedgeHandles =
  1746. PolygonBorderHalfedgeHandlesFlattened(whiteBox, polygonHandle);
  1747. const auto firstPolygon = MergeFaces(
  1748. whiteBox, firstHalfedgeHandle, secondHalfedgeHandle, polygonBorderHalfedgeHandles,
  1749. restoringEdgeHandles);
  1750. const auto secondPolygon = MergeFaces(
  1751. whiteBox, secondHalfedgeHandle, firstHalfedgeHandle, polygonBorderHalfedgeHandles,
  1752. restoringEdgeHandles);
  1753. // check if the first and second polygons are identical,
  1754. // this can happen if the vertex list forms a loop
  1755. const bool identical = [first = firstPolygon, second = secondPolygon]() mutable
  1756. {
  1757. AZStd::sort(first.m_faceHandles.begin(), first.m_faceHandles.end());
  1758. AZStd::sort(second.m_faceHandles.begin(), second.m_faceHandles.end());
  1759. return first == second;
  1760. }();
  1761. // if the number of face handles in at least one of the new polygons is the
  1762. // same as the existing polygon, we know a new polygon has not been formed
  1763. // (the restored edge has not connected to another edge and created a new polygon)
  1764. if (firstPolygon.m_faceHandles.size() == polygonHandle.m_faceHandles.size() || identical)
  1765. {
  1766. restoringEdgeHandles.push_back(edgeHandle);
  1767. return {};
  1768. }
  1769. // get polygon property handle from mesh
  1770. PolygonPropertyHandle polygonPropsHandle;
  1771. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  1772. auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  1773. // update all face handles to refer to the new face handles in the group
  1774. // clear existing face handles
  1775. ClearPolygonProps(polygonProps, polygonHandle.m_faceHandles);
  1776. // populate face handles for first polygon
  1777. PopulatePolygonProps(polygonProps, firstPolygon.m_faceHandles);
  1778. // populate face handles for second polygon
  1779. PopulatePolygonProps(polygonProps, secondPolygon.m_faceHandles);
  1780. // get all edges
  1781. const auto firstPolygonEdges = Api::PolygonBorderEdgeHandlesFlattened(whiteBox, firstPolygon);
  1782. const auto secondPolygonEdges = Api::PolygonBorderEdgeHandlesFlattened(whiteBox, secondPolygon);
  1783. Api::EdgeHandles allPolygonEdges;
  1784. allPolygonEdges.reserve(firstPolygonEdges.size() + secondPolygonEdges.size());
  1785. allPolygonEdges.insert(allPolygonEdges.end(), firstPolygonEdges.begin(), firstPolygonEdges.end());
  1786. allPolygonEdges.insert(allPolygonEdges.end(), secondPolygonEdges.begin(), secondPolygonEdges.end());
  1787. AZStd::sort(allPolygonEdges.begin(), allPolygonEdges.end());
  1788. allPolygonEdges.erase(AZStd::unique(allPolygonEdges.begin(), allPolygonEdges.end()), allPolygonEdges.end());
  1789. RestoreVertexHandlesForEdges(whiteBox, restoringEdgeHandles);
  1790. // remove all edges that make up the new polygons from the ones currently being restored
  1791. restoringEdgeHandles.erase(
  1792. AZStd::remove_if(
  1793. restoringEdgeHandles.begin(), restoringEdgeHandles.end(),
  1794. [&allPolygonEdges](const Api::EdgeHandle edgeHandle)
  1795. {
  1796. return AZStd::find(allPolygonEdges.begin(), allPolygonEdges.end(), edgeHandle) !=
  1797. allPolygonEdges.end();
  1798. }),
  1799. restoringEdgeHandles.end());
  1800. return AZStd::make_optional<AZStd::array<PolygonHandle, 2>>(
  1801. AZStd::array<PolygonHandle, 2>{firstPolygon, secondPolygon});
  1802. }
  1803. void RestoreVertex(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1804. {
  1805. WHITEBOX_LOG("White Box", "RestoreVertex vh(%s)", ToString(vertexHandle).c_str());
  1806. VertexBoolPropertyHandle vertexPropsHiddenHandle;
  1807. whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
  1808. whiteBox.mesh.property(vertexPropsHiddenHandle, om_vh(vertexHandle)) = false;
  1809. }
  1810. bool TryRestoreVertex(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1811. {
  1812. WHITEBOX_LOG("White Box", "TryRestoreVertex vh(%s)", ToString(vertexHandle).c_str());
  1813. // if none of the connected edge handles are user edges then the vertex should not be restored
  1814. if (!VertexIsIsolated(whiteBox, vertexHandle))
  1815. {
  1816. RestoreVertex(whiteBox, vertexHandle);
  1817. return true;
  1818. }
  1819. return false;
  1820. }
  1821. PolygonHandle HideEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1822. {
  1823. WHITEBOX_LOG("White Box", "HideEdge eh(%s)", ToString(edgeHandle).c_str());
  1824. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1825. if (MeshHalfedgeCount(whiteBox) == 0)
  1826. {
  1827. return {};
  1828. }
  1829. // get halfedge handles
  1830. const HalfedgeHandle firstHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
  1831. const HalfedgeHandle secondHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second);
  1832. // get face handles from each halfedge
  1833. const FaceHandle firstFaceHandle = HalfedgeFaceHandle(whiteBox, firstHalfedgeHandle);
  1834. const FaceHandle secondFaceHandle = HalfedgeFaceHandle(whiteBox, secondHalfedgeHandle);
  1835. // get polygon handle from each face handle
  1836. const PolygonHandle firstPolygonHandle = FacePolygonHandle(whiteBox, firstFaceHandle);
  1837. const PolygonHandle secondPolygonHandle = FacePolygonHandle(whiteBox, secondFaceHandle);
  1838. // get all vertex handles associated with the first polygon
  1839. const VertexHandles firstPolygonVertexHandles = PolygonVertexHandles(whiteBox, firstPolygonHandle);
  1840. // union of all face handles
  1841. FaceHandles combinedFaceHandles;
  1842. combinedFaceHandles.reserve(
  1843. firstPolygonHandle.m_faceHandles.size() + secondPolygonHandle.m_faceHandles.size());
  1844. combinedFaceHandles.insert(
  1845. combinedFaceHandles.end(), firstPolygonHandle.m_faceHandles.begin(),
  1846. firstPolygonHandle.m_faceHandles.end());
  1847. combinedFaceHandles.insert(
  1848. combinedFaceHandles.end(), secondPolygonHandle.m_faceHandles.begin(),
  1849. secondPolygonHandle.m_faceHandles.end());
  1850. // get polygon property handle from mesh
  1851. PolygonPropertyHandle polygonPropsHandle;
  1852. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  1853. auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  1854. // update all face handles to refer to the new face handles in the group
  1855. for (const auto& faceHandle : combinedFaceHandles)
  1856. {
  1857. auto polygonIt = polygonProps.find(om_fh(faceHandle));
  1858. polygonIt->second.clear();
  1859. for (const auto& innerFaceHandle : combinedFaceHandles)
  1860. {
  1861. polygonIt->second.push_back(om_fh(innerFaceHandle));
  1862. }
  1863. }
  1864. // hide any vertices that are not connected to a 'user' edge
  1865. for (const auto& vertexHandle : firstPolygonVertexHandles)
  1866. {
  1867. if (VertexIsIsolated(whiteBox, vertexHandle))
  1868. {
  1869. HideVertex(whiteBox, vertexHandle);
  1870. }
  1871. }
  1872. return PolygonHandle{combinedFaceHandles};
  1873. }
  1874. VertexHandle SplitFace(WhiteBoxMesh& whiteBox, const FaceHandle faceHandle, const AZ::Vector3& position)
  1875. {
  1876. WHITEBOX_LOG("White Box", "SplitFace fh(%s)", ToString(faceHandle).c_str());
  1877. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1878. const auto omFaceHandle = om_fh(faceHandle);
  1879. const auto omVertexHandle = whiteBox.mesh.split_copy(omFaceHandle, position);
  1880. const VertexHandle splitVertexHandle = wb_vh(omVertexHandle);
  1881. // as all new edges will be by default hidden, ensure
  1882. // the newly added vertex is also hidden
  1883. HideVertex(whiteBox, splitVertexHandle);
  1884. // build collection of current face handles for newly inserted vertex
  1885. auto omFaceHandles = AZStd::accumulate(
  1886. whiteBox.mesh.vf_ccwbegin(omVertexHandle), whiteBox.mesh.vf_ccwend(omVertexHandle),
  1887. AZStd::vector<Mesh::FaceHandle>{},
  1888. [](AZStd::vector<Mesh::FaceHandle> faceHandles, const Mesh::FaceHandle faceHandle)
  1889. {
  1890. faceHandles.push_back(faceHandle);
  1891. return faceHandles;
  1892. });
  1893. // get polygon property handle from mesh
  1894. PolygonPropertyHandle polygonPropsHandle;
  1895. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  1896. auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  1897. // get all faces associated with the split face handle and added the newly split faces
  1898. // ensuring we do not have any duplicates
  1899. const auto polygonIt = polygonProps.find(om_fh(faceHandle));
  1900. omFaceHandles.insert(omFaceHandles.end(), polygonIt->second.begin(), polygonIt->second.end());
  1901. AZStd::sort(omFaceHandles.begin(), omFaceHandles.end());
  1902. omFaceHandles.erase(AZStd::unique(omFaceHandles.begin(), omFaceHandles.end()), omFaceHandles.end());
  1903. // update all face handles to point to the new polygon grouping
  1904. for (const auto& omFaceHandle2 : omFaceHandles)
  1905. {
  1906. polygonProps[omFaceHandle2] = omFaceHandles;
  1907. }
  1908. return splitVertexHandle;
  1909. }
  1910. VertexHandle SplitEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& position)
  1911. {
  1912. WHITEBOX_LOG("White Box", "SplitEdge eh(%s)", ToString(edgeHandle).c_str());
  1913. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1914. const HalfedgeHandle halfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
  1915. const VertexHandle tailVertexHandle = HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle);
  1916. const VertexHandle tipVertexHandle = HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle);
  1917. const VertexHandle existingConnectedVerts[] = {tailVertexHandle, tipVertexHandle};
  1918. // determine if the edge is a user edge or not before the split
  1919. const bool userEdge = EdgeIsUser(whiteBox, halfedgeHandle, edgeHandle);
  1920. const auto omEdgeHandle = om_eh(edgeHandle);
  1921. const auto omVertexHandle = whiteBox.mesh.add_vertex(position);
  1922. whiteBox.mesh.split_copy(omEdgeHandle, omVertexHandle);
  1923. const VertexHandle splitVertexHandle = wb_vh(omVertexHandle);
  1924. // if the edge that was split was not a 'user' edge we should ensure the
  1925. // newly added vertex is also hidden
  1926. if (!userEdge)
  1927. {
  1928. HideVertex(whiteBox, splitVertexHandle);
  1929. }
  1930. // get all outing edge handles from the new inserted vertex
  1931. const EdgeHandles splitEdgeHandles = VertexEdgeHandles(whiteBox, splitVertexHandle);
  1932. AZStd::for_each(
  1933. splitEdgeHandles.cbegin(), splitEdgeHandles.cend(),
  1934. [&whiteBox, &existingConnectedVerts](const EdgeHandle edgeHandle)
  1935. {
  1936. const auto vertexHandles = EdgeVertexHandles(whiteBox, edgeHandle);
  1937. const bool alreadyConnectedVertex = AZStd::any_of(
  1938. AZStd::cbegin(existingConnectedVerts), AZStd::cend(existingConnectedVerts),
  1939. [&vertexHandles](const VertexHandle vertexHandle)
  1940. {
  1941. return AZStd::find(
  1942. AZStd::cbegin(vertexHandles), AZStd::cend(vertexHandles), vertexHandle) !=
  1943. AZStd::cend(vertexHandles);
  1944. });
  1945. // find if the edge was added or is part of the existing edge which was split
  1946. if (!alreadyConnectedVertex)
  1947. {
  1948. const FaceHandles faceHandles = EdgeFaceHandles(whiteBox, edgeHandle);
  1949. const PolygonHandle polygonHandle = FacePolygonHandle(whiteBox, faceHandles[0]);
  1950. // if the edge was not already connected to on of the existing verts,
  1951. // find the associated polygon handle update them with the newly split faces
  1952. const PolygonHandle existingPolygonHandle = polygonHandle.m_faceHandles.empty()
  1953. ? FacePolygonHandle(whiteBox, faceHandles[1])
  1954. : polygonHandle;
  1955. const FaceHandle newFaceHandle =
  1956. polygonHandle.m_faceHandles.empty() ? faceHandles[0] : faceHandles[1];
  1957. PolygonPropertyHandle polygonPropsHandle;
  1958. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  1959. auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  1960. auto omExistingPolygonHandle = InternalFaceHandlesFromPolygon(existingPolygonHandle);
  1961. omExistingPolygonHandle.push_back(om_fh(newFaceHandle));
  1962. // update all face handles to point to the new polygon grouping
  1963. for (const Mesh::FaceHandle& faceHandle : omExistingPolygonHandle)
  1964. {
  1965. polygonProps[faceHandle] = omExistingPolygonHandle;
  1966. }
  1967. }
  1968. });
  1969. return splitVertexHandle;
  1970. }
  1971. bool FlipEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  1972. {
  1973. WHITEBOX_LOG("White Box", "FlipEdge eh(%s)", ToString(edgeHandle).c_str());
  1974. const auto omEdgeHandle = om_eh(edgeHandle);
  1975. // check if edge can be flipped
  1976. const bool canFlip = whiteBox.mesh.is_flip_ok(omEdgeHandle) && EdgeIsHidden(whiteBox, edgeHandle);
  1977. if (canFlip)
  1978. {
  1979. whiteBox.mesh.flip(omEdgeHandle);
  1980. }
  1981. return canFlip;
  1982. }
  1983. void HideVertex(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
  1984. {
  1985. WHITEBOX_LOG("White Box", "HideVertex vh(%s)", ToString(vertexHandle).c_str());
  1986. VertexBoolPropertyHandle vertexPropsHiddenHandle;
  1987. whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
  1988. whiteBox.mesh.property(vertexPropsHiddenHandle, om_vh(vertexHandle)) = true;
  1989. }
  1990. void Clear(WhiteBoxMesh& whiteBox)
  1991. {
  1992. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1993. PolygonPropertyHandle polygonPropsHandle;
  1994. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  1995. whiteBox.mesh.remove_property(polygonPropsHandle);
  1996. VertexBoolPropertyHandle vertexPropsHiddenHandle;
  1997. whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
  1998. whiteBox.mesh.remove_property(vertexPropsHiddenHandle);
  1999. whiteBox.mesh.clear();
  2000. InitializeWhiteBoxMesh(whiteBox);
  2001. }
  2002. PolygonHandle AddTriPolygon(
  2003. WhiteBoxMesh& whiteBox, const VertexHandle vh0, const VertexHandle vh1, const VertexHandle vh2)
  2004. {
  2005. WHITEBOX_LOG(
  2006. "White Box", "AddTriPolygon vh(%s), vh(%s), vh(%s)", ToString(vh0).c_str(), ToString(vh1).c_str(),
  2007. ToString(vh2).c_str());
  2008. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2009. return AddPolygon(whiteBox, AZStd::vector<FaceVertHandles>{{vh0, vh1, vh2}});
  2010. }
  2011. PolygonHandle AddQuadPolygon(
  2012. WhiteBoxMesh& whiteBox, const VertexHandle vh0, const VertexHandle vh1, const VertexHandle vh2,
  2013. const VertexHandle vh3)
  2014. {
  2015. WHITEBOX_LOG(
  2016. "White Box", "AddQuadPolygon vh(%s), vh(%s), vh(%s), vh(%s)", ToString(vh0).c_str(),
  2017. ToString(vh1).c_str(), ToString(vh2).c_str(), ToString(vh3).c_str());
  2018. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2019. return AddPolygon(whiteBox, AZStd::vector<FaceVertHandles>{{vh0, vh1, vh2}, {vh0, vh2, vh3}});
  2020. }
  2021. PolygonHandle AddPolygon(WhiteBoxMesh& whiteBox, const FaceVertHandlesList& faceVertHandles)
  2022. {
  2023. WHITEBOX_LOG("White Box", "AddPolygon [%s]", ToString(faceVertHandles).c_str());
  2024. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2025. PolygonPropertyHandle polygonPropsHandle;
  2026. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  2027. auto polygon = FaceHandlesInternal{};
  2028. polygon.reserve(faceVertHandles.size());
  2029. for (const auto& face : faceVertHandles)
  2030. {
  2031. polygon.push_back(whiteBox.mesh.add_face(
  2032. om_vh(face.m_vertexHandles[0]), om_vh(face.m_vertexHandles[1]), om_vh(face.m_vertexHandles[2])));
  2033. }
  2034. PolygonHandle polygonHandle = PolygonHandleFromInternal(polygon);
  2035. auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  2036. // multiple face handles map to a polygon handle
  2037. for (const auto& faceHandle : polygon)
  2038. {
  2039. polygonProps[faceHandle] = polygon;
  2040. }
  2041. return polygonHandle;
  2042. }
  2043. PolygonHandles InitializeAsUnitCube(WhiteBoxMesh& whiteBox)
  2044. {
  2045. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2046. // generate vertices
  2047. VertexHandle vertexHandles[8];
  2048. // top verts
  2049. vertexHandles[0] = AddVertex(whiteBox, AZ::Vector3(-0.5f, -0.5f, 0.5f));
  2050. vertexHandles[1] = AddVertex(whiteBox, AZ::Vector3(0.5f, -0.5f, 0.5f));
  2051. vertexHandles[2] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.5f, 0.5f));
  2052. vertexHandles[3] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.5f, 0.5f));
  2053. // bottom verts
  2054. vertexHandles[4] = AddVertex(whiteBox, AZ::Vector3(-0.5f, -0.5f, -0.5f));
  2055. vertexHandles[5] = AddVertex(whiteBox, AZ::Vector3(0.5f, -0.5f, -0.5f));
  2056. vertexHandles[6] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.5f, -0.5f));
  2057. vertexHandles[7] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.5f, -0.5f));
  2058. // generate faces
  2059. PolygonHandles polygonHandles{
  2060. // top
  2061. AddQuadPolygon(whiteBox, vertexHandles[0], vertexHandles[1], vertexHandles[2], vertexHandles[3]),
  2062. // bottom
  2063. AddQuadPolygon(whiteBox, vertexHandles[7], vertexHandles[6], vertexHandles[5], vertexHandles[4]),
  2064. // front
  2065. AddQuadPolygon(whiteBox, vertexHandles[4], vertexHandles[5], vertexHandles[1], vertexHandles[0]),
  2066. // right
  2067. AddQuadPolygon(whiteBox, vertexHandles[5], vertexHandles[6], vertexHandles[2], vertexHandles[1]),
  2068. // back
  2069. AddQuadPolygon(whiteBox, vertexHandles[6], vertexHandles[7], vertexHandles[3], vertexHandles[2]),
  2070. // left
  2071. AddQuadPolygon(whiteBox, vertexHandles[7], vertexHandles[4], vertexHandles[0], vertexHandles[3])};
  2072. CalculateNormals(whiteBox);
  2073. CalculatePlanarUVs(whiteBox);
  2074. return polygonHandles;
  2075. }
  2076. PolygonHandle InitializeAsUnitQuad(WhiteBoxMesh& whiteBox)
  2077. {
  2078. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2079. // generate vertices
  2080. VertexHandle vertexHandles[4];
  2081. // front face
  2082. vertexHandles[0] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.0f, -0.5f)); // bottom left
  2083. vertexHandles[1] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.0f, -0.5f)); // bottom right
  2084. vertexHandles[2] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.0f, 0.5f)); // top right
  2085. vertexHandles[3] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.0f, 0.5f)); // top left
  2086. // generate faces - front
  2087. auto polygonHandle =
  2088. AddQuadPolygon(whiteBox, vertexHandles[0], vertexHandles[1], vertexHandles[2], vertexHandles[3]);
  2089. CalculateNormals(whiteBox);
  2090. CalculatePlanarUVs(whiteBox);
  2091. return polygonHandle;
  2092. }
  2093. PolygonHandle InitializeAsUnitTriangle(WhiteBoxMesh& whiteBox)
  2094. {
  2095. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2096. // generate vertices
  2097. VertexHandle vertexHandles[3];
  2098. const auto pointOnCircle = [](const float angle)
  2099. {
  2100. return AZ::Vector3{cosf(angle), sinf(angle), 0.0f};
  2101. };
  2102. // front face
  2103. vertexHandles[0] = AddVertex(whiteBox, pointOnCircle(AZ::DegToRad(90.0f))); // top
  2104. vertexHandles[1] = AddVertex(whiteBox, pointOnCircle(AZ::DegToRad(-150.0f))); // bottom left
  2105. vertexHandles[2] = AddVertex(whiteBox, pointOnCircle(AZ::DegToRad(-30.0f))); // bottom right
  2106. // generate faces - front
  2107. auto polygonHandle = AddTriPolygon(whiteBox, vertexHandles[0], vertexHandles[1], vertexHandles[2]);
  2108. CalculateNormals(whiteBox);
  2109. CalculatePlanarUVs(whiteBox);
  2110. return polygonHandle;
  2111. }
  2112. void SetVertexPosition(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle, const AZ::Vector3& position)
  2113. {
  2114. WHITEBOX_LOG(
  2115. "White Box", "SetVertexPosition vh(%s) %s", ToString(vertexHandle).c_str(), AZStd::to_string(position).c_str());
  2116. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2117. whiteBox.mesh.set_point(om_vh(vertexHandle), position);
  2118. }
  2119. void SetVertexPositionAndUpdateUVs(
  2120. WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle, const AZ::Vector3& position)
  2121. {
  2122. WHITEBOX_LOG(
  2123. "White Box", "SetVertexPositionAndUpdateUVs vh(%s) %s", ToString(vertexHandle).c_str(), AZStd::to_string(position).c_str());
  2124. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2125. SetVertexPosition(whiteBox, vertexHandle, position);
  2126. CalculatePlanarUVs(whiteBox);
  2127. }
  2128. VertexHandle AddVertex(WhiteBoxMesh& whiteBox, const AZ::Vector3& vertex)
  2129. {
  2130. WHITEBOX_LOG("White Box", "AddVertex %s", AZStd::to_string(vertex).c_str());
  2131. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2132. return wb_vh(whiteBox.mesh.add_vertex(vertex));
  2133. }
  2134. FaceHandle AddFace(WhiteBoxMesh& whiteBox, const VertexHandle v0, const VertexHandle v1, const VertexHandle v2)
  2135. {
  2136. WHITEBOX_LOG(
  2137. "White Box", "AddFace vh(%s), vh(%s), vh(%s)", ToString(v0).c_str(), ToString(v1).c_str(),
  2138. ToString(v2).c_str());
  2139. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2140. return wb_fh(whiteBox.mesh.add_face(om_vh(v0), om_vh(v1), om_vh(v2)));
  2141. }
  2142. void CalculateNormals(WhiteBoxMesh& whiteBox)
  2143. {
  2144. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2145. whiteBox.mesh.update_normals();
  2146. }
  2147. void ZeroUVs(WhiteBoxMesh& whiteBox)
  2148. {
  2149. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2150. for (const Mesh::FaceHandle& faceHandle : whiteBox.mesh.faces())
  2151. {
  2152. for (Mesh::FaceHalfedgeCCWIter faceHalfedgeIt = whiteBox.mesh.fh_ccwiter(faceHandle);
  2153. faceHalfedgeIt.is_valid(); ++faceHalfedgeIt)
  2154. {
  2155. whiteBox.mesh.set_texcoord2D(*faceHalfedgeIt, AZ::Vector2::CreateZero());
  2156. }
  2157. }
  2158. }
  2159. AZ::Vector3 EdgeMidpoint(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
  2160. {
  2161. const auto vertexPositions = EdgeVertexPositions(whiteBox, edgeHandle);
  2162. return (vertexPositions[0] + vertexPositions[1]) * 0.5f;
  2163. }
  2164. AZ::Vector3 FaceMidpoint(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
  2165. {
  2166. const auto vertexPositions = FaceVertexPositions(whiteBox, faceHandle);
  2167. return AZStd::accumulate(
  2168. AZStd::begin(vertexPositions), AZStd::end(vertexPositions), AZ::Vector3::CreateZero(),
  2169. [](const AZ::Vector3& acc, const AZ::Vector3& position)
  2170. {
  2171. return acc + position;
  2172. }) /
  2173. 3;
  2174. }
  2175. AZ::Vector3 PolygonMidpoint(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
  2176. {
  2177. // first attempt using border vertex handles (this is usually what we want, but it might
  2178. // fail if all edges of a polygon have been hidden)
  2179. if (const auto polygonBorderVertexHandles = PolygonBorderVertexHandlesFlattened(whiteBox, polygonHandle);
  2180. !polygonBorderVertexHandles.empty())
  2181. {
  2182. return VerticesMidpoint(whiteBox, polygonBorderVertexHandles);
  2183. }
  2184. // if that fails, fall back to all vertex handles in the polygon to calculate the midpoint
  2185. return VerticesMidpoint(whiteBox, PolygonVertexHandles(whiteBox, polygonHandle));
  2186. }
  2187. AZ::Vector3 VerticesMidpoint(const WhiteBoxMesh& whiteBox, const VertexHandles& vertexHandles)
  2188. {
  2189. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2190. AzToolsFramework::MidpointCalculator midpointCalculator;
  2191. for (const auto& vertexHandle : vertexHandles)
  2192. {
  2193. midpointCalculator.AddPosition(VertexPosition(whiteBox, vertexHandle));
  2194. }
  2195. return midpointCalculator.CalculateMidpoint();
  2196. }
  2197. static HalfedgeHandle FindHalfedgeInAdjacentPolygon(
  2198. const WhiteBoxMesh& whiteBox, const Internal::VertexHandlePair vertexHandlePair,
  2199. const PolygonHandle& selectedPolygonHandle, const PolygonHandle& adjacentPolygonHandle)
  2200. {
  2201. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2202. const auto selectedPolygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, selectedPolygonHandle);
  2203. const auto adjacentPolygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, adjacentPolygonHandle);
  2204. // iterate over all halfedges in the adjacent polygon
  2205. for (const auto& edgeHandle : adjacentPolygonEdgeHandles)
  2206. {
  2207. const auto* const foundEdgeHandleInSelectedPolygon =
  2208. AZStd::find(selectedPolygonEdgeHandles.cbegin(), selectedPolygonEdgeHandles.cend(), edgeHandle);
  2209. // did not find edge handle in selected polygon
  2210. if (foundEdgeHandleInSelectedPolygon == selectedPolygonEdgeHandles.cend())
  2211. {
  2212. // find outgoing edge handles
  2213. for (const auto& halfedgeHandle :
  2214. VertexOutgoingHalfedgeHandles(whiteBox, vertexHandlePair.m_existing))
  2215. {
  2216. // attempt to find one of the outgoing halfedge handles in the adjacent polygon
  2217. if (HalfedgeEdgeHandle(whiteBox, halfedgeHandle) == edgeHandle)
  2218. {
  2219. return halfedgeHandle;
  2220. }
  2221. }
  2222. }
  2223. }
  2224. return HalfedgeHandle{};
  2225. }
  2226. // add 'linking/connecting' faces for when an 'impression' happens
  2227. // note: temporary measure before triangulation support is added to the white box tool
  2228. static void AddLinkingFace(
  2229. const WhiteBoxMesh& whiteBox, const Internal::VertexHandlePair vertexHandlePair,
  2230. const PolygonHandle& selectedPolygonHandle, const PolygonHandle& adjacentPolygonHandle,
  2231. FaceVertHandlesCollection& vertsForLinkingAdjacentPolygons)
  2232. {
  2233. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2234. // if we found a valid halfedge
  2235. if (const HalfedgeHandle foundHalfedgeHandle = FindHalfedgeInAdjacentPolygon(
  2236. whiteBox, vertexHandlePair, selectedPolygonHandle, adjacentPolygonHandle);
  2237. foundHalfedgeHandle.IsValid())
  2238. {
  2239. // find the 'to' vertex
  2240. const auto toVertexHandle = HalfedgeVertexHandleAtTip(whiteBox, foundHalfedgeHandle);
  2241. // find if the face handle of the halfedge is 'in' the adjacent polygon
  2242. const auto* const faceHandleInAdjacentPolygon = AZStd::find(
  2243. adjacentPolygonHandle.m_faceHandles.cbegin(), adjacentPolygonHandle.m_faceHandles.cend(),
  2244. HalfedgeFaceHandle(whiteBox, foundHalfedgeHandle));
  2245. // adjust winding order if the outgoing halfedge is in the adjacent polygon or not
  2246. FaceVertHandles linkingPolygonVertexHandles =
  2247. faceHandleInAdjacentPolygon != adjacentPolygonHandle.m_faceHandles.cend()
  2248. ? FaceVertHandles{vertexHandlePair.m_existing, toVertexHandle, vertexHandlePair.m_added}
  2249. : FaceVertHandles{vertexHandlePair.m_existing, vertexHandlePair.m_added, toVertexHandle};
  2250. // store verts for new polygon
  2251. vertsForLinkingAdjacentPolygons.push_back(
  2252. AZStd::vector<FaceVertHandles>{AZStd::move(linkingPolygonVertexHandles)});
  2253. }
  2254. }
  2255. // return true if existing verts were reused and linking faces were added
  2256. // return false if a new adjacent polygon must be created (new verts have
  2257. // been added and will be used)
  2258. static bool TryAddLinkingFaces(
  2259. WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const Internal::AppendedVerts& appendedVerts,
  2260. const PolygonHandle& selectedPolygonHandle, const Internal::VertexHandlePair& currentVertexHandlePair,
  2261. const Internal::VertexHandlePair& nextVertexHandlePair,
  2262. AZStd::vector<PolygonHandle>& polygonHandlesToRemove,
  2263. FaceVertHandlesCollection& vertsForExistingAdjacentPolygons,
  2264. FaceVertHandlesCollection& vertsForLinkingAdjacentPolygons)
  2265. {
  2266. // find all faces connected to this edge
  2267. for (const auto& faceHandle : EdgeFaceHandles(whiteBox, edgeHandle))
  2268. {
  2269. // find a face that is _not_ part of the polygon being appended/selected
  2270. if (AZStd::find(
  2271. selectedPolygonHandle.m_faceHandles.cbegin(), selectedPolygonHandle.m_faceHandles.cend(),
  2272. faceHandle) == selectedPolygonHandle.m_faceHandles.cend())
  2273. {
  2274. // the polygon handle of the face connected to one of the top edges
  2275. const auto adjacentPolygonHandle = FacePolygonHandle(whiteBox, faceHandle);
  2276. const auto selectedPolygonNormal = PolygonNormal(whiteBox, selectedPolygonHandle);
  2277. // the normal of the adjacent (connected) polygon
  2278. const auto adjacentPolygonNormal = PolygonNormal(whiteBox, adjacentPolygonHandle);
  2279. const float angleCosine = adjacentPolygonNormal.Dot(selectedPolygonNormal);
  2280. // check if the adjacent polygon is completely orthogonal to the
  2281. // selected polygon - if so reuse the existing verts and do not
  2282. // create a new adjacent polygon as part of the operation
  2283. if (AZ::IsClose(angleCosine, 0.0f, AdjacentPolygonNormalTolerance))
  2284. {
  2285. // if the current or next vertex on the border have had a new vertex added
  2286. if (currentVertexHandlePair.m_added != currentVertexHandlePair.m_existing ||
  2287. nextVertexHandlePair.m_added != nextVertexHandlePair.m_existing)
  2288. {
  2289. // remove the existing adjacent polygon (a new one will be added
  2290. // that is connected to the newly added verts)
  2291. polygonHandlesToRemove.push_back(adjacentPolygonHandle);
  2292. // calculate new verts for faces to be created
  2293. const auto adjacentPolygonVerts =
  2294. BuildNewVertexFaceHandles(whiteBox, appendedVerts, adjacentPolygonHandle.m_faceHandles);
  2295. // store the face verts to be added later after existing faces have been removed
  2296. vertsForExistingAdjacentPolygons.push_back(adjacentPolygonVerts);
  2297. }
  2298. // first linking face
  2299. if (currentVertexHandlePair.m_added != currentVertexHandlePair.m_existing)
  2300. {
  2301. AddLinkingFace(
  2302. whiteBox, currentVertexHandlePair, selectedPolygonHandle, adjacentPolygonHandle,
  2303. vertsForLinkingAdjacentPolygons);
  2304. }
  2305. // second linking face
  2306. if (nextVertexHandlePair.m_added != nextVertexHandlePair.m_existing)
  2307. {
  2308. AddLinkingFace(
  2309. whiteBox, nextVertexHandlePair, selectedPolygonHandle, adjacentPolygonHandle,
  2310. vertsForLinkingAdjacentPolygons);
  2311. }
  2312. return true;
  2313. }
  2314. }
  2315. }
  2316. return false;
  2317. }
  2318. // build the adjacent walls of an extrusion
  2319. // note: borderVertexHandles must be ordered correctly (CCW)
  2320. static void AddAdjacentFaces(
  2321. WhiteBoxMesh& whiteBox, const Internal::AppendedVerts& appendedVerts, const bool appendAll,
  2322. const PolygonHandle& selectedPolygonHandle, const VertexHandles& borderVertexHandles,
  2323. const EdgeHandles& borderEdgeHandles, AZStd::vector<PolygonHandle>& polygonHandlesToRemove,
  2324. FaceVertHandlesCollection& vertsForNewAdjacentPolygons,
  2325. FaceVertHandlesCollection& vertsForExistingAdjacentPolygons,
  2326. FaceVertHandlesCollection& vertsForLinkingAdjacentPolygons)
  2327. {
  2328. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2329. // adjacent faces
  2330. for (size_t index = 0; index < borderVertexHandles.size(); ++index)
  2331. {
  2332. const size_t nextIndexWrapped = (index + 1) % borderVertexHandles.size();
  2333. const VertexHandle existingBorderVertexHandle = borderVertexHandles[index];
  2334. const VertexHandle nextExistingBorderVertexHandle = borderVertexHandles[nextIndexWrapped];
  2335. const auto* const currentVertexHandlePairIt = AZStd::find_if(
  2336. appendedVerts.m_vertexHandlePairs.cbegin(), appendedVerts.m_vertexHandlePairs.cend(),
  2337. [existingBorderVertexHandle](const Internal::VertexHandlePair vertexHandlePair)
  2338. {
  2339. return vertexHandlePair.m_existing == existingBorderVertexHandle;
  2340. });
  2341. const auto* const nextVertexHandlePairIt = AZStd::find_if(
  2342. appendedVerts.m_vertexHandlePairs.cbegin(), appendedVerts.m_vertexHandlePairs.cend(),
  2343. [nextExistingBorderVertexHandle](const Internal::VertexHandlePair vertexHandlePair)
  2344. {
  2345. return vertexHandlePair.m_existing == nextExistingBorderVertexHandle;
  2346. });
  2347. // find the edge on the border of the polygon we're appending
  2348. const auto* const edgeHandleIt = AZStd::find_if(
  2349. borderEdgeHandles.cbegin(), borderEdgeHandles.cend(),
  2350. [&whiteBox, existingBorderVertexHandle, nextExistingBorderVertexHandle](const EdgeHandle edgeHandle)
  2351. {
  2352. const auto edgeVertexHandles = EdgeVertexHandles(whiteBox, edgeHandle);
  2353. return (existingBorderVertexHandle == edgeVertexHandles[0] &&
  2354. nextExistingBorderVertexHandle == edgeVertexHandles[1]) ||
  2355. (existingBorderVertexHandle == edgeVertexHandles[1] &&
  2356. nextExistingBorderVertexHandle == edgeVertexHandles[0]);
  2357. });
  2358. // does a new side need to be created (new verts added) or should we reuse existing verts
  2359. const bool createNewAdjacentPolygon = appendAll
  2360. // short circuit if appendAll is true (no linking faces are required)
  2361. || !TryAddLinkingFaces(whiteBox, *edgeHandleIt, appendedVerts, selectedPolygonHandle,
  2362. *currentVertexHandlePairIt, *nextVertexHandlePairIt, polygonHandlesToRemove,
  2363. vertsForExistingAdjacentPolygons, vertsForLinkingAdjacentPolygons);
  2364. // a new full side must be created (we're not reusing existing verts for the new polygon)
  2365. if (createNewAdjacentPolygon)
  2366. {
  2367. vertsForNewAdjacentPolygons.push_back(AZStd::vector<FaceVertHandles>{
  2368. {existingBorderVertexHandle, nextExistingBorderVertexHandle, nextVertexHandlePairIt->m_added},
  2369. {existingBorderVertexHandle, nextVertexHandlePairIt->m_added,
  2370. currentVertexHandlePairIt->m_added}});
  2371. }
  2372. }
  2373. }
  2374. // note: it is important to collect all face handles to remove and call RemoveFaces
  2375. // once for a given operation (for example to do not call RemoveFaces in a loop,
  2376. // instead build the collection of face handles in a loop and then call RemoveFaces)
  2377. // this is to ensure the face handles remain stable as they may be invalidated/changed
  2378. // during garbage_collect
  2379. void RemoveFaces(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
  2380. {
  2381. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2382. whiteBox.mesh.request_face_status();
  2383. whiteBox.mesh.request_edge_status();
  2384. whiteBox.mesh.request_vertex_status();
  2385. PolygonPropertyHandle polygonPropsHandle;
  2386. whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
  2387. auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
  2388. // erase face handles from the polygon map and
  2389. // delete the faces from OpenMesh
  2390. for (const auto& faceHandle : faceHandles)
  2391. {
  2392. polygonProps.erase(om_fh(faceHandle));
  2393. whiteBox.mesh.delete_face(om_fh(faceHandle), false);
  2394. }
  2395. using VertexHandlePtrs = AZStd::vector<Mesh::VertexHandle*>;
  2396. using FaceHandlePtrs = AZStd::vector<Mesh::FaceHandle*>;
  2397. using HalfedgeHandlePtrs = AZStd::vector<Mesh::HalfedgeHandle*>;
  2398. // store pointers to all face handles stored within the map (the values - e.g. it.second)
  2399. FaceHandlePtrs faceHandlePtrs;
  2400. for (auto& polygonProp : polygonProps)
  2401. {
  2402. faceHandlePtrs = AZStd::accumulate(
  2403. polygonProp.second.begin(), polygonProp.second.end(), faceHandlePtrs,
  2404. [](FaceHandlePtrs ptrs, Mesh::FaceHandle& faceHandle)
  2405. {
  2406. ptrs.push_back(&faceHandle);
  2407. return ptrs;
  2408. });
  2409. }
  2410. // make a copy of the face handles to compare against after the garbage_collect
  2411. FaceHandlesInternal faceHandlesCopy;
  2412. faceHandlesCopy.reserve(faceHandlePtrs.size());
  2413. AZStd::transform(
  2414. faceHandlePtrs.begin(), faceHandlePtrs.end(), AZStd::back_inserter(faceHandlesCopy),
  2415. [](const Mesh::FaceHandle* faceHandle)
  2416. {
  2417. return *faceHandle;
  2418. });
  2419. // actually delete faces from mesh
  2420. auto vhs = VertexHandlePtrs{};
  2421. auto hehs = HalfedgeHandlePtrs{};
  2422. whiteBox.mesh.garbage_collection(vhs, hehs, faceHandlePtrs);
  2423. using ModifiedFaceHandle = AZStd::pair<Mesh::FaceHandle, Mesh::FaceHandle>;
  2424. using ModifiedFaceHandles = AZStd::vector<ModifiedFaceHandle>;
  2425. const ModifiedFaceHandles modifiedFaceHandles = AZStd::inner_product(
  2426. faceHandlesCopy.begin(), faceHandlesCopy.end(), faceHandlePtrs.begin(), ModifiedFaceHandles{},
  2427. //reduce
  2428. [](ModifiedFaceHandles modifiedFaceHandles, const ModifiedFaceHandle& fh)
  2429. {
  2430. if (fh.first.is_valid())
  2431. {
  2432. modifiedFaceHandles.push_back(fh);
  2433. }
  2434. return modifiedFaceHandles;
  2435. },
  2436. //transform
  2437. [](const Mesh::FaceHandle lhs, const Mesh::FaceHandle* rhs)
  2438. {
  2439. // if any of the faceHandlePtrs differ, we know the handles
  2440. // have been invalidated during the garbage collect
  2441. if (lhs != *rhs)
  2442. {
  2443. return ModifiedFaceHandle{lhs, *rhs};
  2444. }
  2445. return ModifiedFaceHandle{};
  2446. });
  2447. // iterate over all modified face handles
  2448. for (const ModifiedFaceHandle& modifiedFaceHandle : modifiedFaceHandles)
  2449. {
  2450. // find the value in the map using the old key
  2451. // e.g. faceHandle 10 -> polygon was 10, 11 -> now 4, 5
  2452. auto found = polygonProps.find(modifiedFaceHandle.first);
  2453. if (found != polygonProps.end())
  2454. {
  2455. // copy the updated faceHandle value (e.g was 10, now 4)
  2456. auto faceHandle = modifiedFaceHandle.second;
  2457. // pull the values out of the map (e.g. 4, 5) - make a copy
  2458. auto val = found->second;
  2459. // erase the old key/value -> key 10, value 4, 5
  2460. polygonProps.erase(found);
  2461. // reinsert the values back into the map with the right key (key 4, value 4, 5)
  2462. polygonProps.insert({faceHandle, val});
  2463. }
  2464. }
  2465. }
  2466. AZStd::vector<FaceVertHandles> BuildNewVertexFaceHandles(
  2467. WhiteBoxMesh& whiteBox, const Internal::AppendedVerts& appendedVerts, const FaceHandles& existingFaces)
  2468. {
  2469. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2470. AZStd::vector<FaceVertHandles> faces;
  2471. faces.reserve(existingFaces.size());
  2472. // for each face
  2473. for (const FaceHandle& faceHandle : existingFaces)
  2474. {
  2475. VertexHandles vertexHandlesForFace;
  2476. vertexHandlesForFace.reserve(3);
  2477. const auto vertexHandles = FaceVertexHandles(whiteBox, faceHandle);
  2478. // for each vertex handle
  2479. for (const VertexHandle& vertexHandle : vertexHandles)
  2480. {
  2481. // find vertex handle in vertices list
  2482. const auto* const vertexHandlePairIt = AZStd::find_if(
  2483. appendedVerts.m_vertexHandlePairs.cbegin(), appendedVerts.m_vertexHandlePairs.cend(),
  2484. [vertexHandle](const Internal::VertexHandlePair vertexHandlePair)
  2485. {
  2486. return vertexHandle == vertexHandlePair.m_existing;
  2487. });
  2488. // record corresponding (added) vertex
  2489. if (vertexHandlePairIt != appendedVerts.m_vertexHandlePairs.cend())
  2490. {
  2491. // store vertex
  2492. vertexHandlesForFace.push_back(vertexHandlePairIt->m_added);
  2493. }
  2494. // or existing vertex if one was not added
  2495. else
  2496. {
  2497. vertexHandlesForFace.push_back(vertexHandle);
  2498. }
  2499. }
  2500. // add face using vertex stored vertex
  2501. FaceVertHandles face;
  2502. face.m_vertexHandles[0] = vertexHandlesForFace[0];
  2503. face.m_vertexHandles[1] = vertexHandlesForFace[1];
  2504. face.m_vertexHandles[2] = vertexHandlesForFace[2];
  2505. faces.push_back(face);
  2506. }
  2507. return faces;
  2508. }
  2509. // determine whether new or existing verts be returned based on the type of
  2510. // append (extrude -> out, impression -> in)
  2511. template<typename AppendVertFn>
  2512. static AZStd::tuple<Internal::AppendedVerts, bool> AddVertsForAppend(
  2513. WhiteBoxMesh& whiteBox, const VertexHandles& existingVertexHandles, const PolygonHandle& polygonHandle,
  2514. AppendVertFn&& appendFn)
  2515. {
  2516. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2517. const AZ::Vector3 polygonNormal = PolygonNormal(whiteBox, polygonHandle);
  2518. const auto polygonHalfedgeHandles = PolygonHalfedgeHandles(whiteBox, polygonHandle);
  2519. const AZ::Vector3 extrudeDirection = appendFn(AZ::Vector3::CreateZero());
  2520. const float angleCosine = extrudeDirection.Dot(polygonNormal);
  2521. // detect if the user is extruding the polygon (pulling out) - if so we
  2522. // always want to append new vertices for every existing vertex
  2523. const bool appendAll = angleCosine >= 0.0f;
  2524. Internal::AppendedVerts appendedVerts;
  2525. appendedVerts.m_vertexHandlePairs.reserve(existingVertexHandles.size());
  2526. for (const VertexHandle& existingVertexHandle : existingVertexHandles)
  2527. {
  2528. bool vertexHandleAdded = false;
  2529. // visit all connected halfedge handles
  2530. for (const auto& halfedgeHandle : VertexHalfedgeHandles(whiteBox, existingVertexHandle))
  2531. {
  2532. const auto edgeHandle = HalfedgeEdgeHandle(whiteBox, halfedgeHandle);
  2533. const bool boundaryEdge = EdgeIsBoundary(whiteBox, edgeHandle);
  2534. // is the edge not contained in selected polygon (we want to only check adjacent polygons)
  2535. // or is the edge on a boundary (no adjacent face)
  2536. if (boundaryEdge ||
  2537. AZStd::find(polygonHalfedgeHandles.cbegin(), polygonHalfedgeHandles.cend(), halfedgeHandle) ==
  2538. polygonHalfedgeHandles.cend())
  2539. {
  2540. const auto nextHalfedgeHandle = HalfedgeHandleNext(whiteBox, halfedgeHandle);
  2541. const auto nextEdgeHandle = HalfedgeEdgeHandle(whiteBox, nextHalfedgeHandle);
  2542. const AZ::Vector3 edgeAxis = EdgeAxis(whiteBox, edgeHandle);
  2543. const AZ::Vector3 nextEdgeAxis = EdgeAxis(whiteBox, nextEdgeHandle);
  2544. // calculate face normal from two edges
  2545. const AZ::Vector3 faceNormal = edgeAxis.Cross(nextEdgeAxis).GetNormalizedSafe();
  2546. const bool adjacentFaceAndPolygonNormalOrthogonal = AZ::IsClose(
  2547. AZ::GetAbs(faceNormal.Dot(polygonNormal)), 0.0f, AdjacentPolygonNormalTolerance);
  2548. // if the polygon normal and edge direction are not parallel, we should
  2549. // add a new vertex for the polygon to be later created
  2550. if (appendAll || boundaryEdge || !adjacentFaceAndPolygonNormalOrthogonal)
  2551. {
  2552. vertexHandleAdded = true;
  2553. const AZ::Vector3 localPoint = VertexPosition(whiteBox, existingVertexHandle);
  2554. const AZ::Vector3 extrudedPoint = appendFn(localPoint);
  2555. const VertexHandle addedVertex = AddVertex(whiteBox, extrudedPoint);
  2556. // vertex pairs differ
  2557. appendedVerts.m_vertexHandlePairs.emplace_back(existingVertexHandle, addedVertex);
  2558. break;
  2559. }
  2560. }
  2561. }
  2562. if (!vertexHandleAdded)
  2563. {
  2564. const AZ::Vector3 localPoint = VertexPosition(whiteBox, existingVertexHandle);
  2565. const AZ::Vector3 extrudedPoint = appendFn(localPoint);
  2566. SetVertexPosition(whiteBox, existingVertexHandle, extrudedPoint);
  2567. // vertex pairs match
  2568. appendedVerts.m_vertexHandlePairs.emplace_back(existingVertexHandle, existingVertexHandle);
  2569. }
  2570. }
  2571. return {appendedVerts, appendAll};
  2572. }
  2573. //! @AppendVertexFn The way vertices should be translated as an append happens
  2574. //! note: This is a customization point for scale and translation types of append
  2575. template<typename AppendVertexFn>
  2576. static AppendedPolygonHandles Extrude(
  2577. WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, AppendVertexFn&& appendFn)
  2578. {
  2579. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2580. // find border vertex handles for polygon
  2581. const auto polygonBorderVertexHandlesCollection = PolygonBorderVertexHandles(whiteBox, polygonHandle);
  2582. // handle potentially pathological case where all edges have
  2583. // been hidden and no border vertex loop can be found
  2584. if (polygonBorderVertexHandlesCollection.empty())
  2585. {
  2586. AppendedPolygonHandles appendedPolygonHandles;
  2587. appendedPolygonHandles.m_appendedPolygonHandle = polygonHandle;
  2588. return appendedPolygonHandles;
  2589. }
  2590. // find all vertex handles for polygon
  2591. const auto polygonVertexHandles = PolygonVertexHandles(whiteBox, polygonHandle);
  2592. const auto borderEdgeHandlesCollection = PolygonBorderEdgeHandles(whiteBox, polygonHandle);
  2593. // the vertices to use for the new append (vertex handle pairs may both be existing, or new and existing)
  2594. const auto [appendedVerts, appendAll] =
  2595. AddVertsForAppend(whiteBox, polygonVertexHandles, polygonHandle, appendFn);
  2596. // the face vertex combinations to use for the new polygon begin appended
  2597. const AZStd::vector<FaceVertHandles> topFacesToAdd =
  2598. BuildNewVertexFaceHandles(whiteBox, appendedVerts, polygonHandle.m_faceHandles);
  2599. // polygons that will be removed as part of this operation
  2600. AZStd::vector<PolygonHandle> polygonHandlesToRemove;
  2601. // all new faces to be added
  2602. FaceVertHandlesCollection vertsForNewAdjacentPolygons;
  2603. FaceVertHandlesCollection vertsForExistingAdjacentPolygons;
  2604. FaceVertHandlesCollection vertsForLinkingAdjacentPolygons;
  2605. for (size_t index = 0; index < polygonBorderVertexHandlesCollection.size(); ++index)
  2606. {
  2607. // note: quads/walls of extrusion
  2608. AddAdjacentFaces(
  2609. whiteBox, appendedVerts, appendAll, polygonHandle, polygonBorderVertexHandlesCollection[index],
  2610. borderEdgeHandlesCollection[index], polygonHandlesToRemove, vertsForNewAdjacentPolygons,
  2611. vertsForExistingAdjacentPolygons, vertsForLinkingAdjacentPolygons);
  2612. }
  2613. // <missing> - add bottom faces if mesh was 2d previously (reverse winding order)
  2614. FaceHandles allFacesToRemove = polygonHandle.m_faceHandles;
  2615. for (const auto& polygonHandleToRemove : polygonHandlesToRemove)
  2616. {
  2617. allFacesToRemove.insert(
  2618. allFacesToRemove.end(), polygonHandleToRemove.m_faceHandles.cbegin(), polygonHandleToRemove.m_faceHandles.cend());
  2619. }
  2620. // remove all faces that were already there
  2621. // note: it is very important to not use the existing polygon handle after remove
  2622. // has been called as this will invalidate all existing face handles
  2623. RemoveFaces(whiteBox, allFacesToRemove);
  2624. PolygonHandles polygonHandlesToRestore;
  2625. // re-add existing adjacent polygons
  2626. for (const auto& verts : vertsForExistingAdjacentPolygons)
  2627. {
  2628. polygonHandlesToRestore.push_back(AddPolygon(whiteBox, verts));
  2629. }
  2630. AZ_Assert(
  2631. polygonHandlesToRestore.size() == polygonHandlesToRemove.size(),
  2632. "PolygonHandles to restore and PolygonHandles to remove have different sizes");
  2633. AppendedPolygonHandles appendedPolygonHandles;
  2634. appendedPolygonHandles.m_restoredPolygonHandles.reserve(polygonHandlesToRestore.size());
  2635. for (size_t index = 0; index < polygonHandlesToRestore.size(); ++index)
  2636. {
  2637. RestoredPolygonHandlePair restoredPair;
  2638. restoredPair.m_before = polygonHandlesToRemove[index];
  2639. restoredPair.m_after = polygonHandlesToRestore[index];
  2640. appendedPolygonHandles.m_restoredPolygonHandles.push_back(AZStd::move(restoredPair));
  2641. }
  2642. // add linking polygons
  2643. for (const auto& verts : vertsForLinkingAdjacentPolygons)
  2644. {
  2645. AddPolygon(whiteBox, verts);
  2646. }
  2647. // add top polygon
  2648. PolygonHandle newPolygonHandle = AddPolygon(whiteBox, topFacesToAdd);
  2649. // add new adjacent polygons
  2650. for (const auto& verts : vertsForNewAdjacentPolygons)
  2651. {
  2652. AddPolygon(whiteBox, verts);
  2653. }
  2654. whiteBox.mesh.update_normals();
  2655. appendedPolygonHandles.m_appendedPolygonHandle = newPolygonHandle;
  2656. return appendedPolygonHandles;
  2657. }
  2658. using AppendFn = AZStd::function<AZ::Vector3(const AZ::Vector3&)>;
  2659. static AppendFn TranslatePoint(const AZ::Vector3& direction, const float distance)
  2660. {
  2661. return [direction, distance](const AZ::Vector3& point)
  2662. {
  2663. return point + (direction * distance);
  2664. };
  2665. }
  2666. PolygonHandle TranslatePolygonAppend(
  2667. WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float distance)
  2668. {
  2669. WHITEBOX_LOG("White Box", "TranslatePolygonAppend ph(%s) %f", ToString(polygonHandle).c_str(), distance)
  2670. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2671. return TranslatePolygonAppendAdvanced(whiteBox, polygonHandle, distance).m_appendedPolygonHandle;
  2672. }
  2673. AppendedPolygonHandles TranslatePolygonAppendAdvanced(
  2674. WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float distance)
  2675. {
  2676. WHITEBOX_LOG(
  2677. "White Box", "TranslatePolygonAppendAdvanced ph(%s) %f", ToString(polygonHandle).c_str(), distance)
  2678. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2679. // check mesh has faces
  2680. if (whiteBox.mesh.n_faces() == 0)
  2681. {
  2682. return {};
  2683. }
  2684. auto appendedPolygonHandles =
  2685. Extrude(whiteBox, polygonHandle, TranslatePoint(PolygonNormal(whiteBox, polygonHandle), distance));
  2686. return appendedPolygonHandles;
  2687. }
  2688. void TranslatePolygon(WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float distance)
  2689. {
  2690. WHITEBOX_LOG("White Box", "TranslatePolygon ph(%s) %d", ToString(polygonHandle).c_str(), distance)
  2691. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2692. const auto vertexHandles = PolygonVertexHandles(whiteBox, polygonHandle);
  2693. const auto vertexPositions = VertexPositions(whiteBox, vertexHandles);
  2694. const auto normal = PolygonNormal(whiteBox, polygonHandle);
  2695. for (size_t index = 0; index < vertexHandles.size(); ++index)
  2696. {
  2697. SetVertexPosition(whiteBox, vertexHandles[index], vertexPositions[index] + normal * distance);
  2698. }
  2699. CalculatePlanarUVs(whiteBox);
  2700. }
  2701. PolygonHandle ScalePolygonAppendRelative(
  2702. WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float scale)
  2703. {
  2704. WHITEBOX_LOG("White Box", "ScalePolygonAppendRelative ph(%s) %f", ToString(polygonHandle).c_str(), scale);
  2705. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2706. // check mesh has faces
  2707. if (whiteBox.mesh.n_faces() == 0)
  2708. {
  2709. return {};
  2710. }
  2711. const AZ::Transform polygonSpace =
  2712. PolygonSpace(whiteBox, polygonHandle, PolygonMidpoint(whiteBox, polygonHandle));
  2713. const auto scalePolygonFn = [polygonSpace, scale](const AZ::Vector3& localPosition)
  2714. {
  2715. return ScalePosition(1.0f + scale, localPosition, polygonSpace);
  2716. };
  2717. auto appendedPolygonHandles = Extrude(whiteBox, polygonHandle, scalePolygonFn);
  2718. return appendedPolygonHandles.m_appendedPolygonHandle;
  2719. }
  2720. static AZ::Transform BuildSpace(const AZ::Vector3& normal, const AZ::Vector3& pivot)
  2721. {
  2722. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2723. AZ::Vector3 axis1;
  2724. AZ::Vector3 axis2;
  2725. CalculateOrthonormalBasis(normal, axis1, axis2);
  2726. AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumns(axis1, axis2, normal, pivot);
  2727. return AZ::Transform::CreateFromMatrix3x4(matrix);
  2728. }
  2729. AZ::Transform PolygonSpace(
  2730. const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const AZ::Vector3& pivot)
  2731. {
  2732. return BuildSpace(PolygonNormal(whiteBox, polygonHandle), pivot);
  2733. }
  2734. AZ::Transform EdgeSpace(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& pivot)
  2735. {
  2736. const auto vertexPositions = EdgeVertexPositions(whiteBox, edgeHandle);
  2737. return BuildSpace((vertexPositions[1] - vertexPositions[0]).GetNormalizedSafe(), pivot);
  2738. }
  2739. void ScalePolygonRelative(
  2740. WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const AZ::Vector3& pivot,
  2741. const float scaleDelta)
  2742. {
  2743. WHITEBOX_LOG(
  2744. "White Box", "ScalePolygonRelative ph(%s) pivot %s scale: %f", ToString(polygonHandle).c_str(),
  2745. AZStd::to_string(pivot).c_str(), scaleDelta);
  2746. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2747. const AZ::Transform polygonSpace = PolygonSpace(whiteBox, polygonHandle, pivot);
  2748. for (const auto& vertexHandle : PolygonVertexHandles(whiteBox, polygonHandle))
  2749. {
  2750. SetVertexPosition(
  2751. whiteBox, vertexHandle,
  2752. ScalePosition(1.0f + scaleDelta, VertexPosition(whiteBox, vertexHandle), polygonSpace));
  2753. }
  2754. CalculateNormals(whiteBox);
  2755. CalculatePlanarUVs(whiteBox);
  2756. }
  2757. bool WriteMesh(const WhiteBoxMesh& whiteBox, WhiteBoxMeshStream& output)
  2758. {
  2759. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2760. AZStd::lock_guard lg(g_omSerializationLock);
  2761. std::stringstream whiteBoxStream;
  2762. if (OpenMesh::IO::write_mesh(
  2763. whiteBox.mesh, whiteBoxStream, ".om",
  2764. OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::FaceTexCoord |
  2765. OpenMesh::IO::Options::FaceNormal))
  2766. {
  2767. const std::string outputStr = whiteBoxStream.str();
  2768. output.clear();
  2769. output.reserve(outputStr.size());
  2770. AZStd::copy(outputStr.cbegin(), outputStr.cend(), AZStd::back_inserter(output));
  2771. return true;
  2772. }
  2773. // handle error
  2774. return false;
  2775. }
  2776. ReadResult ReadMesh(WhiteBoxMesh& whiteBox, const WhiteBoxMeshStream& input)
  2777. {
  2778. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2779. if (input.empty())
  2780. {
  2781. return ReadResult::Empty;
  2782. }
  2783. std::string inputStr;
  2784. inputStr.reserve(input.size());
  2785. AZStd::copy(input.cbegin(), input.cend(), AZStd::back_inserter(inputStr));
  2786. std::stringstream whiteBoxStream;
  2787. whiteBoxStream.str(inputStr);
  2788. whiteBoxStream >> std::noskipws;
  2789. return ReadMesh(whiteBox, whiteBoxStream);
  2790. }
  2791. ReadResult ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input)
  2792. {
  2793. const auto skipws = input.flags() & std::ios_base::skipws;
  2794. AZ_Assert(skipws == 0, "Input stream must not skip white space characters");
  2795. if (skipws != 0)
  2796. {
  2797. return ReadResult::Error;
  2798. }
  2799. AZStd::lock_guard lg(g_omSerializationLock);
  2800. OpenMesh::IO::Options options{OpenMesh::IO::Options::FaceTexCoord | OpenMesh::IO::Options::FaceNormal};
  2801. return OpenMesh::IO::read_mesh(whiteBox.mesh, input, ".om", options) ? ReadResult::Full : ReadResult::Error;
  2802. }
  2803. WhiteBoxMeshPtr CloneMesh(const WhiteBoxMesh& whiteBox)
  2804. {
  2805. AZ_PROFILE_FUNCTION(AzToolsFramework);
  2806. WhiteBoxMeshStream clonedData;
  2807. if (!WriteMesh(whiteBox, clonedData))
  2808. {
  2809. return nullptr;
  2810. }
  2811. WhiteBoxMeshPtr newMesh = CreateWhiteBoxMesh();
  2812. if (ReadMesh(*newMesh, clonedData) != ReadResult::Full)
  2813. {
  2814. return nullptr;
  2815. }
  2816. return newMesh;
  2817. }
  2818. bool SaveToObj(const WhiteBoxMesh& whiteBox, const AZStd::string& filePath)
  2819. {
  2820. OpenMesh::IO::Options options{OpenMesh::IO::Options::FaceTexCoord};
  2821. OpenMesh::IO::ExporterT<WhiteBox::Mesh> exporter{whiteBox.mesh};
  2822. return OpenMesh::IO::OBJWriter().write(filePath.c_str(), exporter, options);
  2823. }
  2824. bool SaveToWbm(const WhiteBoxMesh& whiteBox, AZ::IO::GenericStream& stream)
  2825. {
  2826. WhiteBoxMeshStream buffer;
  2827. const bool success = WhiteBox::Api::WriteMesh(whiteBox, buffer);
  2828. const auto bytesWritten = stream.Write(buffer.size(), buffer.data());
  2829. return success && bytesWritten == buffer.size();
  2830. }
  2831. bool SaveToWbm(const WhiteBoxMesh& whiteBox, const AZStd::string& filePath)
  2832. {
  2833. AZ::IO::FileIOStream fileStream(filePath.c_str(), AZ::IO::OpenMode::ModeWrite);
  2834. if (!fileStream.IsOpen())
  2835. {
  2836. return false;
  2837. }
  2838. return SaveToWbm(whiteBox, fileStream);
  2839. }
  2840. static AZStd::string TrimLastChar(const AZStd::string& str)
  2841. {
  2842. if (str.empty())
  2843. {
  2844. return str;
  2845. }
  2846. return str.substr(0, str.length() - 1);
  2847. }
  2848. AZStd::string ToString(const PolygonHandle& polygonHandle)
  2849. {
  2850. AZStd::string str = "";
  2851. for (auto faceHandle : polygonHandle.m_faceHandles)
  2852. {
  2853. str.append(ToString(faceHandle)).append(",");
  2854. }
  2855. return TrimLastChar(str);
  2856. }
  2857. AZStd::string ToString(const FaceVertHandles& faceVertHandles)
  2858. {
  2859. AZStd::string str = "";
  2860. for (auto vertexHandle : faceVertHandles.m_vertexHandles)
  2861. {
  2862. str.append(ToString(vertexHandle)).append(",");
  2863. }
  2864. return TrimLastChar(str);
  2865. }
  2866. AZStd::string ToString(const FaceVertHandlesList& faceVertHandlesList)
  2867. {
  2868. AZStd::string str = "";
  2869. for (auto faceVertHandles : faceVertHandlesList)
  2870. {
  2871. str.append("fvh(").append(ToString(faceVertHandles)).append("),");
  2872. }
  2873. return TrimLastChar(str);
  2874. }
  2875. AZStd::string ToString(const VertexHandle vertexHandle)
  2876. {
  2877. return AZStd::to_string(vertexHandle.Index());
  2878. }
  2879. AZStd::string ToString(const FaceHandle faceHandle)
  2880. {
  2881. return AZStd::to_string(faceHandle.Index());
  2882. }
  2883. AZStd::string ToString(const EdgeHandle edgeHandle)
  2884. {
  2885. return AZStd::to_string(edgeHandle.Index());
  2886. }
  2887. AZStd::string ToString(const HalfedgeHandle halfedgeHandle)
  2888. {
  2889. return AZStd::to_string(halfedgeHandle.Index());
  2890. }
  2891. } // namespace Api
  2892. } // namespace WhiteBox