| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "Util/WhiteBoxMathUtil.h"
- #include "Util/WhiteBoxTextureUtil.h"
- #include <AzCore/Console/Console.h>
- #include <AzCore/Debug/Profiler.h>
- #include <AzCore/IO/FileIO.h>
- #include <AzCore/Math/MathUtils.h>
- #include <AzCore/Math/Matrix3x4.h>
- #include <AzCore/Math/MathStringConversions.h>
- #include <AzCore/Math/Transform.h>
- #include <AzCore/Math/Vector2.h>
- #include <AzCore/Math/Vector3.h>
- #include <AzCore/std/containers/map.h>
- #include <AzCore/std/containers/vector.h>
- #include <AzCore/std/functional.h>
- #include <AzCore/std/hash.h>
- #include <AzCore/std/numeric.h>
- #include <AzCore/std/parallel/lock.h>
- #include <AzCore/std/sort.h>
- #include <AzCore/std/string/conversions.h>
- #include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
- #include <WhiteBox/WhiteBoxToolApi.h>
- namespace OpenMesh
- {
- // Overload methods need to be declared before including OpenMesh so their definitions are found
- inline AZ::Vector3 normalize(const AZ::Vector3& v)
- {
- AZ::Vector3 vret = v;
- vret.Normalize();
- return vret;
- }
- inline float dot(const AZ::Vector3& v1, const AZ::Vector3& v2)
- {
- return v1.Dot(v2);
- }
- inline float norm(const AZ::Vector3& v)
- {
- return v.GetLength();
- }
- inline AZ::Vector3 cross(const AZ::Vector3& v1, const AZ::Vector3& v2)
- {
- return v1.Cross(v2);
- }
- inline AZ::Vector3 vectorize(AZ::Vector3& v, float s)
- {
- v = AZ::Vector3(s);
- return v;
- }
- inline void newell_norm(AZ::Vector3& n, const AZ::Vector3& a, const AZ::Vector3& b)
- {
- n.SetX(n.GetX() + (a.GetY() * b.GetZ()));
- n.SetY(n.GetY() + (a.GetZ() * b.GetX()));
- n.SetZ(n.GetZ() + (a.GetX() * b.GetY()));
- }
- }
- // OpenMesh includes
- AZ_PUSH_DISABLE_WARNING(4702, "-Wunknown-warning-option") // OpenMesh\Core\Utils\Property.hh has unreachable code
- #include <OpenMesh/Core/IO/MeshIO.hh>
- #include <OpenMesh/Core/IO/SR_binary.hh>
- #include <OpenMesh/Core/IO/importer/ImporterT.hh>
- #include <OpenMesh/Core/Mesh/Traits.hh>
- #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
- #include <OpenMesh/Core/Utils/GenProg.hh>
- #include <OpenMesh/Core/Utils/vector_traits.hh>
- AZ_POP_DISABLE_WARNING
- AZ_DECLARE_BUDGET(AzToolsFramework);
- namespace OpenMesh
- {
- template<>
- struct vector_traits<AZ::Vector3>
- {
- //! Type of the vector class
- using vector_type = AZ::Vector3;
- //! Type of the scalar value
- using value_type = float;
- //! size/dimension of the vector
- static const size_t size_ = 3;
- //! size/dimension of the vector
- static size_t size()
- {
- return size_;
- }
- };
- template<>
- struct vector_traits<AZ::Vector2>
- {
- //! Type of the vector class
- using vector_type = AZ::Vector2;
- //! Type of the scalar value
- using value_type = float;
- //! size/dimension of the vector
- static const size_t size_ = 2;
- //! size/dimension of the vector
- static size_t size()
- {
- return size_;
- }
- };
- template<>
- inline void vector_cast(const AZ::Vector3& src, OpenMesh::Vec3f& dst, GenProg::Int2Type<3> /*unused*/)
- {
- dst[0] = static_cast<vector_traits<Vec3f>::value_type>(src.GetX());
- dst[1] = static_cast<vector_traits<Vec3f>::value_type>(src.GetY());
- dst[2] = static_cast<vector_traits<Vec3f>::value_type>(src.GetZ());
- }
- template<>
- inline void vector_cast(const AZ::Vector2& src, OpenMesh::Vec2f& dst, GenProg::Int2Type<2> /*unused*/)
- {
- dst[0] = static_cast<vector_traits<Vec2f>::value_type>(src.GetX());
- dst[1] = static_cast<vector_traits<Vec2f>::value_type>(src.GetY());
- }
- template<>
- inline void vector_cast(const OpenMesh::Vec3f& src, AZ::Vector3& dst, GenProg::Int2Type<3> /*unused*/)
- {
- dst.SetX(static_cast<vector_traits<Vec3f>::value_type>(src[0]));
- dst.SetY(static_cast<vector_traits<Vec3f>::value_type>(src[1]));
- dst.SetZ(static_cast<vector_traits<Vec3f>::value_type>(src[2]));
- }
- template<>
- inline void vector_cast(const OpenMesh::Vec2f& src, AZ::Vector2& dst, GenProg::Int2Type<2> /*unused*/)
- {
- dst.SetX(static_cast<vector_traits<Vec2f>::value_type>(src[0]));
- dst.SetY(static_cast<vector_traits<Vec2f>::value_type>(src[1]));
- }
- template<>
- inline void vector_cast(const AZ::Vector3& src, OpenMesh::Vec3d& dst, GenProg::Int2Type<3> /*unused*/)
- {
- dst[0] = static_cast<vector_traits<Vec3d>::value_type>(src.GetX());
- dst[1] = static_cast<vector_traits<Vec3d>::value_type>(src.GetY());
- dst[2] = static_cast<vector_traits<Vec3d>::value_type>(src.GetZ());
- }
- template<>
- inline void vector_cast(const AZ::Vector2& src, OpenMesh::Vec2d& dst, GenProg::Int2Type<2> /*unused*/)
- {
- dst[0] = static_cast<vector_traits<Vec2d>::value_type>(src.GetX());
- dst[1] = static_cast<vector_traits<Vec2d>::value_type>(src.GetY());
- }
- template<>
- inline void vector_cast(const OpenMesh::Vec3d& src, AZ::Vector3& dst, GenProg::Int2Type<3> /*unused*/)
- {
- dst.SetX(static_cast<float>(src[0]));
- dst.SetY(static_cast<float>(src[1]));
- dst.SetZ(static_cast<float>(src[2]));
- }
- template<>
- inline void vector_cast(const OpenMesh::Vec2d& src, AZ::Vector2& dst, GenProg::Int2Type<2> /*unused*/)
- {
- dst.SetX(static_cast<float>(src[0]));
- dst.SetY(static_cast<float>(src[1]));
- }
- } // namespace OpenMesh
- #ifdef AZ_ENABLE_TRACING
- #define WHITEBOX_LOG(str, ...) \
- do \
- { \
- if (cl_whiteBoxLogMessages) \
- { \
- AZ_Printf(str, __VA_ARGS__) \
- } \
- } while (0);
- #else
- #define WHITEBOX_LOG(str, ...)
- #endif
- namespace WhiteBox
- {
- // cvar for logging debug messages
- AZ_CVAR(bool, cl_whiteBoxLogMessages, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Log debug messages.");
- struct WhiteBoxTraits : public OpenMesh::DefaultTraits
- {
- using Point = AZ::Vector3;
- using Normal = AZ::Vector3;
- using TexCoord2D = AZ::Vector2;
- using TexCoord3D = AZ::Vector3;
- };
- using Mesh = OpenMesh::TriMesh_ArrayKernelT<WhiteBoxTraits>;
- } // namespace WhiteBox
- namespace AZStd
- {
- template<>
- struct hash<WhiteBox::Mesh::FaceHandle>
- {
- public:
- size_t operator()(const WhiteBox::Mesh::FaceHandle& faceHandle) const
- {
- size_t h{0};
- AZStd::hash_combine(h, faceHandle.idx());
- return h;
- }
- };
- } // namespace AZStd
- namespace WhiteBox
- {
- // alias for vector of OpenMesh FaceHandles
- using FaceHandlesInternal = AZStd::vector<Mesh::FaceHandle>;
- // a property to map from a FaceHandle to the Polygon it corresponds to
- // note: PolygonHandle will include the FaceHandle used to do the lookup
- using FaceHandlePolygonMapping = AZStd::unordered_map<Mesh::FaceHandle, FaceHandlesInternal>;
- using PolygonPropertyHandle = OpenMesh::MPropHandleT<FaceHandlePolygonMapping>;
- // unique string to lookup the polygon custom property via get_property_handle
- static const char* const PolygonProps = "polygon-props";
- // a property to track the hidden state of a vertex
- using VertexBoolPropertyHandle = OpenMesh::VPropHandleT<bool>;
- // unique string to lookup the vertex custom property via get_property_handle
- static const char* const VertexHiddenProp = "vertex-hidden-props";
- } // namespace WhiteBox
- namespace OpenMesh::IO
- {
- template<>
- struct binary<WhiteBox::FaceHandlesInternal>
- {
- using value_type = WhiteBox::FaceHandlesInternal;
- static const bool is_streamable = true;
- // return generic binary size of self, if known
- static size_t size_of()
- {
- return UnknownSize;
- }
- // return binary size of the value
- static size_t size_of(const value_type& _v)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- if (_v.empty())
- {
- return sizeof(uint32_t);
- }
- value_type::const_iterator it = _v.begin();
- const auto count = static_cast<uint32_t>(_v.size());
- size_t bytes = IO::size_of(count);
- for (; it != _v.end(); ++it)
- {
- bytes += IO::size_of(it->idx());
- }
- return bytes;
- }
- static size_t store(std::ostream& _os, const value_type& _v, bool _swap = false)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- size_t bytes = 0;
- const auto count = static_cast<uint32_t>(_v.size());
- value_type::const_iterator it = _v.begin();
- bytes += IO::store(_os, count, _swap);
- for (; it != _v.end() && _os.good(); ++it)
- {
- bytes += IO::store(_os, (*it).idx(), _swap);
- }
- return _os.good() ? bytes : 0;
- }
- static size_t restore(std::istream& _is, value_type& _v, bool _swap = false)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- size_t bytes = 0;
- uint32_t count = 0;
- _v.clear();
- bytes += IO::restore(_is, count, _swap);
- _v.reserve(count);
- for (size_t i = 0; i < count && _is.good(); ++i)
- {
- int elem; // value_type::value_type -> Mesh::FaceHandle (underlying type int)
- bytes += IO::restore(_is, elem, _swap);
- _v.push_back(value_type::value_type(elem));
- }
- return _is.good() ? bytes : 0;
- }
- };
- template<>
- struct binary<WhiteBox::FaceHandlePolygonMapping>
- {
- using value_type = WhiteBox::FaceHandlePolygonMapping;
- static const bool is_streamable = true;
- // return generic binary size of self, if known
- static size_t size_of()
- {
- return UnknownSize;
- }
- // return binary size of the value
- static size_t size_of(const value_type& _v)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- if (_v.empty())
- {
- return sizeof(uint32_t);
- }
- value_type::const_iterator it = _v.begin();
- const auto count = static_cast<uint32_t>(_v.size());
- size_t bytes = IO::size_of(count);
- for (; it != _v.end(); ++it)
- {
- bytes += IO::size_of(it->first.idx());
- bytes += IO::size_of(it->second);
- }
- return bytes;
- }
- static size_t store(std::ostream& _os, const value_type& _v, bool _swap = false)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- size_t bytes = 0;
- const auto count = static_cast<uint32_t>(_v.size());
- value_type::const_iterator it = _v.begin();
- bytes += IO::store(_os, count, _swap);
- for (; it != _v.end() && _os.good(); ++it)
- {
- bytes += IO::store(_os, it->first.idx(), _swap);
- bytes += IO::store(_os, it->second, _swap);
- }
- return _os.good() ? bytes : 0;
- }
- static size_t restore(std::istream& _is, value_type& _v, bool _swap = false)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- size_t bytes = 0;
- uint32_t count = 0;
- _v.clear();
- bytes += IO::restore(_is, count, _swap);
- value_type::mapped_type val;
- for (size_t i = 0; i < count && _is.good(); ++i)
- {
- int key; // value_type::key_type -> Mesh::FaceHandle (underlying type int)
- bytes += IO::restore(_is, key, _swap);
- bytes += IO::restore(_is, val, _swap);
- _v[value_type::key_type(key)] = val;
- }
- return _is.good() ? bytes : 0;
- }
- };
- } // namespace OpenMesh::IO
- namespace WhiteBox
- {
- static const float NormalTolerance = 0.99f;
- static const float AdjacentPolygonNormalTolerance = 0.0001f;
- // A wrapper for the OpenMesh source data.
- struct WhiteBoxMesh
- {
- AZ_CLASS_ALLOCATOR(WhiteBoxMesh, AZ::SystemAllocator);
- WhiteBoxMesh() = default;
- WhiteBoxMesh(WhiteBoxMesh&&) = default;
- WhiteBoxMesh& operator=(WhiteBoxMesh&&) = default;
- Mesh mesh; //!< The OpenMesh triangle mesh kernel (with customized AZ traits).
- };
- // 0,0 is tl - 1,1 is br
- // 3 is 0,0
- // 2 is 1,0
- // 1 is 1,1
- // 0 is 0,1
- const Mesh::TexCoord2D g_quadUVs[] = {
- Mesh::TexCoord2D(0.0f, 1.0f),
- Mesh::TexCoord2D(1.0f, 1.0f),
- Mesh::TexCoord2D(1.0f, 0.0f),
- Mesh::TexCoord2D(0.0f, 0.0f),
- };
- // conversion functions between OpenMesh and AZ types
- // convert WhiteBox face handle to OpenMesh face handle
- static Mesh::FaceHandle om_fh(const Api::FaceHandle fh)
- {
- return Mesh::FaceHandle{fh.Index()};
- }
- // convert WhiteBox vertex handle to OpenMesh vertex handle
- static Mesh::VertexHandle om_vh(const Api::VertexHandle vh)
- {
- return Mesh::VertexHandle{vh.Index()};
- }
- // convert WhiteBox edge handle to OpenMesh edge handle
- static Mesh::EdgeHandle om_eh(const Api::EdgeHandle eh)
- {
- return Mesh::EdgeHandle{eh.Index()};
- }
- // convert WhiteBox halfedge handle to OpenMesh halfedge handle
- static Mesh::HalfedgeHandle om_heh(const Api::HalfedgeHandle heh)
- {
- return Mesh::HalfedgeHandle{heh.Index()};
- }
- // convert OpenMesh face handle to WhiteBox face handle
- static Api::FaceHandle wb_fh(const Mesh::FaceHandle fh)
- {
- return Api::FaceHandle{fh.idx()};
- }
- // convert OpenMesh vertex handle to WhiteBox vertex handle
- static Api::VertexHandle wb_vh(const Mesh::VertexHandle vh)
- {
- return Api::VertexHandle{vh.idx()};
- }
- // convert OpenMesh halfedge handle to WhiteBox halfedge handle
- static Api::HalfedgeHandle wb_heh(const Mesh::HalfedgeHandle heh)
- {
- return Api::HalfedgeHandle{heh.idx()};
- }
- // convert OpenMesh edge handle to WhiteBox edge handle
- static Api::EdgeHandle wb_eh(const Mesh::EdgeHandle eh)
- {
- return Api::EdgeHandle{eh.idx()};
- }
- // map from internal handles to external handles
- Api::PolygonHandle PolygonHandleFromInternal(const FaceHandlesInternal& faceHandlesInternal)
- {
- Api::PolygonHandle polygonHandle;
- polygonHandle.m_faceHandles.reserve(faceHandlesInternal.size());
- AZStd::transform(
- faceHandlesInternal.begin(), faceHandlesInternal.end(), AZStd::back_inserter(polygonHandle.m_faceHandles),
- &wb_fh);
- return polygonHandle;
- }
- FaceHandlesInternal InternalFaceHandlesFromPolygon(const Api::PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- FaceHandlesInternal faceHandlesInternal;
- faceHandlesInternal.reserve(polygonHandle.m_faceHandles.size());
- AZStd::transform(
- polygonHandle.m_faceHandles.begin(), polygonHandle.m_faceHandles.end(),
- AZStd::back_inserter(faceHandlesInternal), &om_fh);
- return faceHandlesInternal;
- }
- namespace Api
- {
- AZStd::mutex g_omSerializationLock; // serialization lock required when using Open Mesh IOManager
- namespace Internal
- {
- // when performing an append (extrusion or impression) new vertices will
- // be added to the mesh - this struct maps from the existing vertex and the
- // newly added one at the same location.
- // note: it is possible that as part of an impression, m_existing and m_added
- // both refer to the same vertex handle as the same vertex will be reused
- struct VertexHandlePair
- {
- VertexHandle m_existing;
- VertexHandle m_added;
- VertexHandlePair() = default;
- VertexHandlePair(const VertexHandle existing, const VertexHandle added)
- : m_existing(existing)
- , m_added(added)
- {
- }
- };
- // a collection of VertexHandlePairs
- // generated as part of an append (extrusion or impression)
- struct AppendedVerts
- {
- AZStd::vector<VertexHandlePair> m_vertexHandlePairs;
- };
- // intermediate data to use when appending an edge (performing an 'edge extrusion')
- struct EdgeAppendVertexHandles
- {
- PolygonHandle m_existingPolygonHandle; // the polygon to be replaced by the new edge extrusion
- // the vertices to use when 'appending' new geometry to the mesh while performing an edge extrusion.
- VertexHandle m_toVertexHandle;
- VertexHandle m_fromVertexHandle;
- VertexHandle m_addedFromVertexHandle;
- VertexHandle m_addedToVertexHandle;
- VertexHandle m_afterToVertexHandle;
- VertexHandle m_beforeFromVertexHandle;
- };
- // intermediate data to use when appending an edge (performing an 'edge extrusion')
- struct EdgeAppendPolygonHandles
- {
- PolygonHandle m_nearPolygonHandle;
- PolygonHandle m_farPolygonHandle;
- PolygonHandle m_topPolygonHandle;
- PolygonHandle m_bottomPolygonHandle;
- };
- } // namespace Internal
- // forward declarations
- AZStd::vector<FaceVertHandles> BuildNewVertexFaceHandles(
- WhiteBoxMesh& whiteBox, const Internal::AppendedVerts& appendedVerts, const FaceHandles& existingFaces);
- void RemoveFaces(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles);
- void CalculatePlanarUVs(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles);
- // restores WhiteBoxMesh properties, use when properties have not been initialized or have been cleared
- static void InitializeWhiteBoxMesh(WhiteBoxMesh& whiteBox)
- {
- // add default properties for all white box meshes
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.add_property(polygonPropsHandle, PolygonProps);
- whiteBox.mesh.mproperty(polygonPropsHandle).set_persistent(true);
- VertexBoolPropertyHandle vertexPropsHiddenHandle;
- whiteBox.mesh.add_property(vertexPropsHiddenHandle, VertexHiddenProp);
- whiteBox.mesh.property(vertexPropsHiddenHandle).set_persistent(true);
- // request default properties required for all white box meshes
- whiteBox.mesh.request_face_normals();
- whiteBox.mesh.request_halfedge_texcoords2D();
- }
- WhiteBoxMeshPtr CreateWhiteBoxMesh()
- {
- auto whiteBox = WhiteBoxMeshPtr(aznew WhiteBoxMesh());
- InitializeWhiteBoxMesh(*whiteBox);
- return whiteBox;
- }
- void WhiteBoxMeshDeleter::DestroyWhiteBoxMesh(WhiteBoxMesh* whiteBox)
- {
- delete whiteBox;
- }
- VertexHandles MeshVertexHandles(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- VertexHandles vertexHandles;
- vertexHandles.reserve(whiteBox.mesh.n_vertices());
- for (const auto& vertexHandle : whiteBox.mesh.vertices())
- {
- vertexHandles.push_back(wb_vh(vertexHandle));
- }
- return vertexHandles;
- }
- FaceHandles MeshFaceHandles(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- FaceHandles faceHandles;
- faceHandles.reserve(whiteBox.mesh.n_faces());
- for (const auto& faceHandle : whiteBox.mesh.faces())
- {
- faceHandles.push_back(wb_fh(faceHandle));
- }
- return faceHandles;
- }
- PolygonHandles MeshPolygonHandles(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- const auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- AZStd::vector<PolygonHandle> polygonHandles;
- for (const auto& polygonProp : polygonProps)
- {
- // don't add duplicate polygons
- PolygonHandle polygonHandle = PolygonHandleFromInternal(polygonProp.second);
- if (AZStd::find(polygonHandles.begin(), polygonHandles.end(), polygonHandle) == polygonHandles.end())
- {
- polygonHandles.push_back(AZStd::move(polygonHandle));
- }
- }
- return polygonHandles;
- }
- EdgeHandlesCollection PolygonBorderEdgeHandles(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const HalfedgeHandlesCollection halfedgeHandlesCollection =
- PolygonBorderHalfedgeHandles(whiteBox, polygonHandle);
- EdgeHandlesCollection orderedEdgeHandlesCollection;
- orderedEdgeHandlesCollection.reserve(halfedgeHandlesCollection.size());
- for (const auto& halfedgeHandles : halfedgeHandlesCollection)
- {
- EdgeHandles orderedEdgeHandles;
- orderedEdgeHandles.reserve(halfedgeHandles.size());
- for (const auto& halfedgeHandle : halfedgeHandles)
- {
- orderedEdgeHandles.push_back(HalfedgeEdgeHandle(whiteBox, halfedgeHandle));
- }
- orderedEdgeHandlesCollection.push_back(orderedEdgeHandles);
- }
- return orderedEdgeHandlesCollection;
- }
- EdgeHandles PolygonBorderEdgeHandlesFlattened(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const EdgeHandlesCollection borderEdgeHandlesCollection = PolygonBorderEdgeHandles(whiteBox, polygonHandle);
- EdgeHandles polygonBorderEdgeHandles;
- for (const auto& borderEdgeHandles : borderEdgeHandlesCollection)
- {
- polygonBorderEdgeHandles.insert(
- polygonBorderEdgeHandles.end(), borderEdgeHandles.cbegin(), borderEdgeHandles.cend());
- }
- return polygonBorderEdgeHandles;
- }
- EdgeHandles MeshPolygonEdgeHandles(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- auto polygonHandles = MeshPolygonHandles(whiteBox);
- EdgeHandles allEdgeHandles;
- for (const auto& polygonHandle : polygonHandles)
- {
- auto polygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, polygonHandle);
- allEdgeHandles.insert(allEdgeHandles.end(), polygonEdgeHandles.begin(), polygonEdgeHandles.end());
- }
- // remove duplicates
- AZStd::sort(allEdgeHandles.begin(), allEdgeHandles.end());
- allEdgeHandles.erase(AZStd::unique(allEdgeHandles.begin(), allEdgeHandles.end()), allEdgeHandles.end());
- return allEdgeHandles;
- }
- EdgeHandles MeshEdgeHandles(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- EdgeHandles edgeHandles;
- edgeHandles.reserve(whiteBox.mesh.n_edges());
- for (const auto& edgeHandle : whiteBox.mesh.edges())
- {
- edgeHandles.push_back(wb_eh(edgeHandle));
- }
- return edgeHandles;
- }
- EdgeTypes MeshUserEdgeHandles(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- EdgeHandles userEdgeHandles = MeshPolygonEdgeHandles(whiteBox);
- AZStd::sort(userEdgeHandles.begin(), userEdgeHandles.end());
- EdgeHandles allEdgeHandles = MeshEdgeHandles(whiteBox);
- AZStd::sort(allEdgeHandles.begin(), allEdgeHandles.end());
- EdgeHandles meshEdgeHandles;
- meshEdgeHandles.reserve(allEdgeHandles.size()); // over reserve vector
- AZStd::set_difference(
- allEdgeHandles.begin(), allEdgeHandles.end(), userEdgeHandles.begin(), userEdgeHandles.end(),
- AZStd::back_inserter(meshEdgeHandles));
- return {AZStd::move(userEdgeHandles), AZStd::move(meshEdgeHandles)};
- }
- AZStd::vector<AZ::Vector3> MeshVertexPositions(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return VertexPositions(whiteBox, MeshVertexHandles(whiteBox));
- }
- HalfedgeHandles FaceHalfedgeHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- HalfedgeHandles halfedgeHandles;
- halfedgeHandles.reserve(3);
- for (Mesh::ConstFaceHalfedgeCCWIter faceHalfedgeIt = whiteBox.mesh.cfh_ccwiter(om_fh(faceHandle));
- faceHalfedgeIt.is_valid(); ++faceHalfedgeIt)
- {
- halfedgeHandles.push_back(wb_heh(*faceHalfedgeIt));
- }
- return halfedgeHandles;
- }
- EdgeHandles FaceEdgeHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- if (!faceHandle.IsValid())
- {
- return {};
- }
- EdgeHandles edgeHandles;
- edgeHandles.reserve(3);
- for (const auto& halfedgeHandle : FaceHalfedgeHandles(whiteBox, faceHandle))
- {
- edgeHandles.push_back(HalfedgeEdgeHandle(whiteBox, halfedgeHandle));
- }
- return edgeHandles;
- }
- VertexHandles FaceVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- if (!faceHandle.IsValid())
- {
- return {};
- }
- VertexHandles vertexHandles;
- vertexHandles.reserve(3);
- for (const auto& halfedgeHandle : FaceHalfedgeHandles(whiteBox, faceHandle))
- {
- vertexHandles.emplace_back(HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
- }
- return vertexHandles;
- }
- AZStd::vector<AZ::Vector3> FaceVertexPositions(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- return VertexPositions(whiteBox, FaceVertexHandles(whiteBox, faceHandle));
- }
- AZStd::vector<AZ::Vector3> FacesPositions(const WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- AZStd::vector<AZ::Vector3> triangles;
- triangles.reserve(faceHandles.size() * 3);
- for (const auto& faceHandle : faceHandles)
- {
- const auto corners = FaceVertexPositions(whiteBox, faceHandle);
- triangles.insert(triangles.end(), corners.begin(), corners.end());
- }
- return triangles;
- }
- FaceHandle HalfedgeFaceHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- if (halfedgeHandle.IsValid())
- {
- return wb_fh(whiteBox.mesh.face_handle(om_heh(halfedgeHandle)));
- }
- return {};
- }
- HalfedgeHandle HalfedgeOppositeHalfedgeHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- if (halfedgeHandle.IsValid())
- {
- return wb_heh(whiteBox.mesh.opposite_halfedge_handle(om_heh(halfedgeHandle)));
- }
- return {};
- }
- FaceHandle HalfedgeOppositeFaceHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- if (halfedgeHandle.IsValid())
- {
- return wb_fh(whiteBox.mesh.opposite_face_handle(om_heh(halfedgeHandle)));
- }
- return {};
- }
- HalfedgeHandles VertexOutgoingHalfedgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- HalfedgeHandles outgoingHalfEdgeHandles;
- for (auto oheh = whiteBox.mesh.cvoh_ccwbegin(om_vh(vertexHandle));
- oheh != whiteBox.mesh.cvoh_ccwend(om_vh(vertexHandle)); ++oheh)
- {
- outgoingHalfEdgeHandles.push_back(wb_heh(*oheh));
- }
- return outgoingHalfEdgeHandles;
- }
- HalfedgeHandles VertexIncomingHalfedgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- HalfedgeHandles incomingHalfEdgeHandles;
- for (auto iheh = whiteBox.mesh.cvih_ccwbegin(om_vh(vertexHandle));
- iheh != whiteBox.mesh.cvih_ccwend(om_vh(vertexHandle)); ++iheh)
- {
- incomingHalfEdgeHandles.push_back(wb_heh(*iheh));
- }
- return incomingHalfEdgeHandles;
- }
- HalfedgeHandles VertexHalfedgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- HalfedgeHandles outgoingHandles = VertexOutgoingHalfedgeHandles(whiteBox, vertexHandle);
- HalfedgeHandles incomingHandles = VertexIncomingHalfedgeHandles(whiteBox, vertexHandle);
- auto allHandles = AZStd::move(outgoingHandles);
- allHandles.insert(
- allHandles.end(), AZStd::make_move_iterator(incomingHandles.begin()),
- AZStd::make_move_iterator(incomingHandles.end()));
- return allHandles;
- }
- EdgeHandles VertexEdgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto omVertexHandle = om_vh(vertexHandle);
- return AZStd::accumulate(
- whiteBox.mesh.cve_ccwbegin(omVertexHandle), whiteBox.mesh.cve_ccwend(omVertexHandle), EdgeHandles{},
- [](EdgeHandles edgeHandles, const auto edgeHandle)
- {
- edgeHandles.push_back(wb_eh(edgeHandle));
- return edgeHandles;
- });
- }
- static bool BuildFaceHandles(
- const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle, FaceHandles& faceHandles,
- const AZ::Vector3& normal)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto* const found_fh = AZStd::find(faceHandles.cbegin(), faceHandles.cend(), faceHandle);
- if (found_fh == faceHandles.cend())
- {
- const AZ::Vector3 nextNormal = FaceNormal(whiteBox, faceHandle).GetNormalized();
- if (OpenMesh::dot(nextNormal, normal) > NormalTolerance)
- {
- faceHandles.push_back(faceHandle);
- return true;
- }
- }
- return false;
- }
- static FaceHandle OppositeFaceHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const HalfedgeHandle oppositeHalfedgeHandle = HalfedgeOppositeHalfedgeHandle(whiteBox, halfedgeHandle);
- if (HalfedgeIsBoundary(whiteBox, oppositeHalfedgeHandle))
- {
- return {};
- }
- // note: oppositeFaceHandle will be invalid if oppositeHalfedgeHandle is a boundary
- const FaceHandle oppositeFaceHandle = HalfedgeFaceHandle(whiteBox, oppositeHalfedgeHandle);
- return oppositeFaceHandle;
- }
- static void SideFaceHandlesInternal(
- const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle, FaceHandles& faceHandles,
- const AZ::Vector3& normal)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- if (BuildFaceHandles(whiteBox, faceHandle, faceHandles, normal))
- {
- // all halfedges for a given face
- const auto halfedges = FaceHalfedgeHandles(whiteBox, faceHandle);
- for (const HalfedgeHandle& halfedgeHandle : halfedges)
- {
- const FaceHandle oppositeFaceHandle = OppositeFaceHandle(whiteBox, halfedgeHandle);
- if (oppositeFaceHandle.IsValid())
- {
- SideFaceHandlesInternal(whiteBox, oppositeFaceHandle, faceHandles, normal);
- }
- }
- }
- }
- FaceHandles SideFaceHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- FaceHandles faceHandles;
- SideFaceHandlesInternal(
- whiteBox, faceHandle, faceHandles, FaceNormal(whiteBox, faceHandle).GetNormalized());
- return faceHandles;
- }
- static HalfedgeHandlesCollection BorderHalfedgeHandles(
- const WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // build all possible halfedge handles
- HalfedgeHandles halfedgeHandles;
- for (const auto& faceHandle : faceHandles)
- {
- // find all vertices for a given face
- const auto vertexHandles = FaceVertexHandles(whiteBox, faceHandle);
- for (const auto& vertexHandle : vertexHandles)
- {
- // find all outgoing halfedges from vertex
- const auto outgoingHalfedgeHandles = VertexOutgoingHalfedgeHandles(whiteBox, vertexHandle);
- for (const auto& halfedgeHandle : outgoingHalfedgeHandles)
- {
- // find what face corresponds to this halfedge
- const FaceHandle halfedgeFaceHandle = HalfedgeFaceHandle(whiteBox, halfedgeHandle);
- // if the halfedge corresponds to a face on this side
- if (AZStd::find(faceHandles.cbegin(), faceHandles.cend(), halfedgeFaceHandle) !=
- faceHandles.end())
- {
- // check the opposite face handle
- const FaceHandle oppositeFaceHandle = HalfedgeOppositeFaceHandle(whiteBox, halfedgeHandle);
- // if the opposite face handle isn't on this side, we know it is a 'boundary' halfedge
- if (AZStd::find(faceHandles.cbegin(), faceHandles.cend(), oppositeFaceHandle) ==
- faceHandles.end())
- {
- // check we haven't already stored this halfedge
- if (AZStd::find(halfedgeHandles.cbegin(), halfedgeHandles.cend(), halfedgeHandle) ==
- halfedgeHandles.cend())
- {
- // add to border halfedges
- halfedgeHandles.push_back(halfedgeHandle);
- }
- }
- }
- }
- }
- }
- // handle potentially pathological case where all edges have
- // been hidden and no halfedge loop can be found
- if (halfedgeHandles.empty())
- {
- return {};
- }
- HalfedgeHandlesCollection orderHalfedgeHandlesCollection;
- // can sort based on tip/tail
- HalfedgeHandles orderedHalfedgeHandles;
- orderedHalfedgeHandles.push_back(halfedgeHandles.back());
- halfedgeHandles.pop_back();
- // empty our list of unordered border side halfedge handles
- while (!halfedgeHandles.empty())
- {
- // use next vertex to get halfedges in order
- const VertexHandle nextVertex = HalfedgeVertexHandleAtTip(whiteBox, orderedHalfedgeHandles.back());
- // find next ordered halfedge
- const auto* const nextHalfedge = AZStd::find_if(
- halfedgeHandles.cbegin(), halfedgeHandles.cend(),
- [nextVertex, &whiteBox](auto halfedgeHandle)
- {
- return nextVertex == HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle);
- });
- // if we found it
- if (nextHalfedge != halfedgeHandles.end())
- {
- // add it to the ordered list and remove it from the unordered list
- orderedHalfedgeHandles.push_back(*nextHalfedge);
- halfedgeHandles[nextHalfedge - halfedgeHandles.begin()] = halfedgeHandles.back();
- halfedgeHandles.pop_back();
- }
- else
- {
- // cycle detected, start a new list
- orderHalfedgeHandlesCollection.push_back(orderedHalfedgeHandles);
- orderedHalfedgeHandles.clear();
- orderedHalfedgeHandles.push_back(halfedgeHandles.back());
- halfedgeHandles.pop_back();
- }
- }
- if (halfedgeHandles.empty())
- {
- AZ_Assert(!orderedHalfedgeHandles.empty(), "No ordered halfedges generated");
- orderHalfedgeHandlesCollection.push_back(orderedHalfedgeHandles);
- }
- // finally return the ordered list
- return orderHalfedgeHandlesCollection;
- }
- HalfedgeHandlesCollection SideBorderHalfedgeHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // find all face handles for a side
- return BorderHalfedgeHandles(whiteBox, SideFaceHandles(whiteBox, faceHandle));
- }
- static VertexHandlesCollection BorderVertexHandles(
- const WhiteBoxMesh& whiteBox, const HalfedgeHandlesCollection& halfedgeHandlesCollection)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- VertexHandlesCollection orderedVertexHandlesCollection;
- orderedVertexHandlesCollection.reserve(halfedgeHandlesCollection.size());
- for (const auto& halfedgeHandles : halfedgeHandlesCollection)
- {
- VertexHandles orderedVertexHandles;
- orderedVertexHandles.reserve(halfedgeHandles.size());
- for (const auto& halfedgeHandle : halfedgeHandles)
- {
- orderedVertexHandles.push_back(HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
- }
- orderedVertexHandlesCollection.push_back(orderedVertexHandles);
- }
- return orderedVertexHandlesCollection;
- }
- VertexHandlesCollection SideBorderVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return BorderVertexHandles(whiteBox, SideBorderHalfedgeHandles(whiteBox, faceHandle));
- }
- static VertexHandles FacesVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- VertexHandles vertexHandles;
- for (const FaceHandle& faceHandle : faceHandles)
- {
- const auto faceVertexHandles = FaceVertexHandles(whiteBox, faceHandle);
- for (const VertexHandle& faceVertexHandle : faceVertexHandles)
- {
- const auto* const vertexIt =
- AZStd::find(vertexHandles.cbegin(), vertexHandles.cend(), faceVertexHandle);
- // ensure we do not add duplicate vertices
- if (vertexIt == vertexHandles.end())
- {
- vertexHandles.push_back(faceVertexHandle);
- }
- }
- }
- return vertexHandles;
- }
- VertexHandles SideVertexHandles(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return FacesVertexHandles(whiteBox, SideFaceHandles(whiteBox, faceHandle));
- }
- VertexHandle HalfedgeVertexHandleAtTip(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- return wb_vh(whiteBox.mesh.to_vertex_handle(om_heh(halfedgeHandle)));
- }
- VertexHandle HalfedgeVertexHandleAtTail(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- return wb_vh(whiteBox.mesh.from_vertex_handle(om_heh(halfedgeHandle)));
- }
- AZ::Vector3 HalfedgeVertexPositionAtTip(const WhiteBoxMesh& whiteBox, HalfedgeHandle halfedgeHandle)
- {
- return VertexPosition(whiteBox, HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
- }
- AZ::Vector3 HalfedgeVertexPositionAtTail(const WhiteBoxMesh& whiteBox, HalfedgeHandle halfedgeHandle)
- {
- return VertexPosition(whiteBox, HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle));
- }
- EdgeHandle HalfedgeEdgeHandle(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- return wb_eh(whiteBox.mesh.edge_handle(om_heh(halfedgeHandle)));
- }
- bool HalfedgeIsBoundary(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- return whiteBox.mesh.is_boundary(om_heh(halfedgeHandle));
- }
- HalfedgeHandle HalfedgeHandleNext(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- return wb_heh(whiteBox.mesh.next_halfedge_handle(om_heh(halfedgeHandle)));
- }
- HalfedgeHandle HalfedgeHandlePrevious(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- return wb_heh(whiteBox.mesh.prev_halfedge_handle(om_heh(halfedgeHandle)));
- }
- static AZStd::array<AZ::Vector3, 2> EdgeVertexPositions(
- const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const VertexHandle vertexHandle)
- {
- if (const auto vertexHandles = EdgeVertexHandles(whiteBox, edgeHandle); vertexHandle.IsValid())
- {
- const auto otherVertexHandle = vertexHandles[0] == vertexHandle ? vertexHandles[1] : vertexHandles[0];
- return {VertexPosition(whiteBox, vertexHandle), VertexPosition(whiteBox, otherVertexHandle)};
- }
- else
- {
- return {VertexPosition(whiteBox, vertexHandles[0]), VertexPosition(whiteBox, vertexHandles[1])};
- }
- }
- AZStd::array<AZ::Vector3, 2> EdgeVertexPositions(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- return EdgeVertexPositions(whiteBox, edgeHandle, VertexHandle{});
- }
- AZStd::array<VertexHandle, 2> EdgeVertexHandles(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- // note: first halfedge handle should always exist
- if (const auto halfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
- halfedgeHandle.IsValid())
- {
- return {
- HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle),
- HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle)};
- }
- AZ_Assert(false, "Could not find Vertex Handles for Edge Handle %d", edgeHandle.Index());
- return {VertexHandle{}, VertexHandle{}};
- }
- // provide the ability to pass a vertex handle to explicitly determine the direction of the axis
- static AZ::Vector3 EdgeVectorWithStartingVertexHandle(
- const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const VertexHandle vertexHandle)
- {
- const auto edgeVertexPositions = EdgeVertexPositions(whiteBox, edgeHandle, vertexHandle);
- return (edgeVertexPositions[1] - edgeVertexPositions[0]);
- }
- AZ::Vector3 EdgeVector(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- return EdgeVectorWithStartingVertexHandle(whiteBox, edgeHandle, VertexHandle{});
- }
- // provide the ability to pass a vertex handle to explicitly determine the direction of the axis
- static AZ::Vector3 EdgeAxisWithStartingVertexHandle(
- const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const VertexHandle vertexHandle)
- {
- if (const AZ::Vector3 edgeVector = EdgeVectorWithStartingVertexHandle(whiteBox, edgeHandle, vertexHandle);
- edgeVector.GetLength() > 0.0f)
- {
- return edgeVector / edgeVector.GetLength();
- }
- return AZ::Vector3::CreateZero();
- }
- AZ::Vector3 EdgeAxis(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- return EdgeAxisWithStartingVertexHandle(whiteBox, edgeHandle, VertexHandle{});
- }
- bool EdgeIsBoundary(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- return whiteBox.mesh.is_boundary(om_eh(edgeHandle));
- }
- // note: halfedge handle must be from the edge handle passed in
- static bool EdgeIsUser(
- const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle, const EdgeHandle edgeHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto polygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(
- whiteBox, FacePolygonHandle(whiteBox, HalfedgeFaceHandle(whiteBox, halfedgeHandle)));
- return AZStd::find(polygonEdgeHandles.cbegin(), polygonEdgeHandles.cend(), edgeHandle) !=
- polygonEdgeHandles.cend();
- }
- // note: overload of EdgeIsUser that does not require halfedgeHande to be
- // passed in but does slightly more work
- static bool EdgeIsUser(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- const auto halfedgeHandle = EdgeHalfedgeHandles(whiteBox, edgeHandle);
- return AZStd::any_of(
- AZStd::cbegin(halfedgeHandle), AZStd::cend(halfedgeHandle),
- [&whiteBox, edgeHandle](const HalfedgeHandle halfedgeHandle)
- {
- return EdgeIsUser(whiteBox, halfedgeHandle, edgeHandle);
- });
- }
- EdgeHandles EdgeGrouping(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // a non-user ('mesh') edge is never part of a grouping so if one is passed
- // in ensure we return an empty group
- if (!EdgeIsUser(whiteBox, edgeHandle))
- {
- return EdgeHandles{};
- }
- // the edge group to begin building
- EdgeHandles edgeGrouping = {edgeHandle};
- // get vertex handles from the hovered/selected edge
- const auto initialVertexHandles = Api::EdgeVertexHandles(whiteBox, edgeHandle);
- auto vertexHandles = VertexHandles{initialVertexHandles.cbegin(), initialVertexHandles.cend()};
- // track all vertices we've already seen
- auto visitedVertexHandles = VertexHandles{};
- while (!vertexHandles.empty())
- {
- const auto vertexHandle = vertexHandles.back();
- vertexHandles.pop_back();
- // if the vertex is not hidden this is where the search ends
- if (!VertexIsHidden(whiteBox, vertexHandle))
- {
- continue;
- }
- visitedVertexHandles.push_back(vertexHandle);
- // for all connected vertex handles to this edge
- for (const auto& vertexEdgeHandle : VertexEdgeHandles(whiteBox, vertexHandle))
- {
- // check all halfedges in the edge
- for (const auto& halfedgeHandle : EdgeHalfedgeHandles(whiteBox, vertexEdgeHandle))
- {
- // only track the edge if it's a 'user' edge (selectable - not a 'mesh' edge)
- if (!EdgeIsUser(whiteBox, halfedgeHandle, vertexEdgeHandle))
- {
- continue;
- }
- // check if have we already added the edge to the grouping
- if (AZStd::find(edgeGrouping.cbegin(), edgeGrouping.cend(), vertexEdgeHandle) !=
- edgeGrouping.cend())
- {
- continue;
- }
- // store the edge to the grouping
- edgeGrouping.push_back(vertexEdgeHandle);
- for (const auto& nextVertexHandle : Api::EdgeVertexHandles(whiteBox, vertexEdgeHandle))
- {
- // if we haven't seen this vertex yet, add it to
- // the vertex handles to explore
- if (AZStd::find(
- visitedVertexHandles.cbegin(), visitedVertexHandles.cend(), nextVertexHandle) ==
- visitedVertexHandles.cend())
- {
- vertexHandles.push_back(nextVertexHandle);
- }
- }
- }
- }
- }
- return edgeGrouping;
- }
- bool EdgeIsHidden(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const EdgeHandles userEdgeHandles = MeshPolygonEdgeHandles(whiteBox);
- return AZStd::find(userEdgeHandles.cbegin(), userEdgeHandles.cend(), edgeHandle) == userEdgeHandles.cend();
- }
- AZStd::vector<FaceHandle> EdgeFaceHandles(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto openMeshEdgeHandle = om_eh(edgeHandle);
- const auto firstHalfedgeHandle = whiteBox.mesh.halfedge_handle(openMeshEdgeHandle, 0);
- const auto secondHalfedgeHandle = whiteBox.mesh.halfedge_handle(openMeshEdgeHandle, 1);
- AZ_Assert(
- firstHalfedgeHandle.is_valid() || secondHalfedgeHandle.is_valid(),
- "There should be at least one valid half edge handle for any given edge");
- AZStd::vector<FaceHandle> validFaceHandles;
- // only one face handle is valid at mesh boundaries
- if (const auto firstFaceHandle = whiteBox.mesh.face_handle(firstHalfedgeHandle); firstFaceHandle.is_valid())
- {
- validFaceHandles.push_back(wb_fh(firstFaceHandle));
- }
- if (const auto secondFaceHandle = whiteBox.mesh.face_handle(secondHalfedgeHandle);
- secondFaceHandle.is_valid())
- {
- validFaceHandles.push_back(wb_fh(secondFaceHandle));
- }
- return validFaceHandles;
- }
- static size_t EdgeHalfedgeMapping(const EdgeHalfedge edgeHalfedge)
- {
- switch (edgeHalfedge)
- {
- case EdgeHalfedge::First:
- return 0;
- case EdgeHalfedge::Second:
- return 1;
- default:
- AZ_Assert(false, "Invalid EdgeHalfedge type passed");
- return 2;
- }
- }
- HalfedgeHandle EdgeHalfedgeHandle(
- const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const EdgeHalfedge edgeHalfedge)
- {
- return wb_heh(whiteBox.mesh.halfedge_handle(om_eh(edgeHandle), static_cast<unsigned int>(EdgeHalfedgeMapping(edgeHalfedge))));
- }
- HalfedgeHandles EdgeHalfedgeHandles(const WhiteBoxMesh& whiteBox, EdgeHandle edgeHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const AZStd::array<HalfedgeHandle, 2> halfedgeHandles = {
- EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First),
- EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second)};
- return AZStd::accumulate(
- halfedgeHandles.cbegin(), halfedgeHandles.cend(), HalfedgeHandles{},
- [&whiteBox](HalfedgeHandles halfedgeHandles, const HalfedgeHandle halfedgeHandle)
- {
- if (!HalfedgeIsBoundary(whiteBox, halfedgeHandle))
- {
- halfedgeHandles.push_back(halfedgeHandle);
- }
- return halfedgeHandles;
- });
- }
- void TranslateEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
- {
- WHITEBOX_LOG(
- "White Box", "TranslateEdge eh(%s) %s", ToString(edgeHandle).c_str(), AZStd::to_string(displacement).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto vertexHandles = EdgeVertexHandles(whiteBox, edgeHandle);
- for (const auto& vertexHandle : vertexHandles)
- {
- auto position = VertexPosition(whiteBox, vertexHandle);
- position += displacement;
- SetVertexPosition(whiteBox, vertexHandle, position);
- }
- CalculateNormals(whiteBox);
- CalculatePlanarUVs(whiteBox);
- }
- // Given a displacement in local space applied to an edge, find the halfedge handle that the
- // edge is most likely moving towards. We're attempting to infer the user's intention which is
- // never perfect so there's a chance we may not return the edge the user expects. On the
- // whole the heuristic used (delta distance moved towards a connected face midpoint) is pretty stable.
- static HalfedgeHandle FindBestFitHalfedge(
- WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // get both halfedge handles for the edge (0 and 1 just correspond to each halfedge)
- const HalfedgeHandle firstHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
- const HalfedgeHandle secondHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second);
- // get all vertices for each face (triangle) that each halfedge corresponds to
- const auto firstFaceVertexHandles =
- FaceVertexHandles(whiteBox, HalfedgeFaceHandle(whiteBox, firstHalfedgeHandle));
- const auto secondFaceVertexHandles =
- FaceVertexHandles(whiteBox, HalfedgeFaceHandle(whiteBox, secondHalfedgeHandle));
- // calculate the midpoint of each face
- const auto firstFaceMidpoint = VerticesMidpoint(whiteBox, firstFaceVertexHandles);
- const auto secondFaceMidpoint = VerticesMidpoint(whiteBox, secondFaceVertexHandles);
- // calculate the midpoint of the edge we wish to append to and where it will be after the displacement
- const auto edgeMidpoint = EdgeMidpoint(whiteBox, edgeHandle);
- const auto nextEdgePosition = edgeMidpoint + displacement;
- // calculate how far the center of each face is from the edge midpoint
- const auto distanceFromFirstFace = (firstFaceMidpoint - edgeMidpoint).GetLength();
- const auto distanceFromSecondFace = (secondFaceMidpoint - edgeMidpoint).GetLength();
- // then calculate how far the center of each face is from the edge midpoint plus the displacement
- const auto nextDistanceFromFirstFace = (nextEdgePosition - firstFaceMidpoint).GetLength();
- const auto nextDistanceFromSecondFace = (nextEdgePosition - secondFaceMidpoint).GetLength();
- // next see what the delta is from next and current positions
- // this is to determine did the displacement move us towards the first or second face
- // i.e. infer which way the user dragged
- const auto nextDeltaFromFirstFace = nextDistanceFromFirstFace - distanceFromFirstFace;
- const auto nextDeltaFromSecondFace = nextDistanceFromSecondFace - distanceFromSecondFace;
- // pick the best halfedge we inferred
- const EdgeHalfedge halfedge =
- nextDeltaFromFirstFace < nextDeltaFromSecondFace ? EdgeHalfedge::First : EdgeHalfedge::Second;
- return EdgeHalfedgeHandle(whiteBox, edgeHandle, halfedge);
- }
- // Determine the vertices required to append the new edge geometry being created
- static Internal::EdgeAppendVertexHandles CalculateEdgeAppendVertexHandles(
- WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // based on the displacement find which halfedge is a better fit (which direction did we move in)
- const HalfedgeHandle halfedgeHandle = FindBestFitHalfedge(whiteBox, edgeHandle, displacement);
- const FaceHandle faceHandle = HalfedgeFaceHandle(whiteBox, halfedgeHandle);
- // find the polygon this face handle corresponds to
- const PolygonHandle polygonHandle = FacePolygonHandle(whiteBox, faceHandle);
- // find all border vertex handles for this polygon
- const VertexHandlesCollection polygonBorderVertexHandlesCollection =
- PolygonBorderVertexHandles(whiteBox, polygonHandle);
- // following the direction of the halfedge, what vertex is it pointing to
- const VertexHandle toVertexHandle = HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle);
- // following the direction of the halfedge, what vertex is coming from
- const VertexHandle fromVertexHandle = HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle);
- // find which vertex loop the vertex is in based on the halfedge we've selected
- const VertexHandles borderVertexHandles = AZStd::accumulate(
- AZStd::begin(polygonBorderVertexHandlesCollection), AZStd::end(polygonBorderVertexHandlesCollection),
- VertexHandles{},
- [toVertexHandle](VertexHandles borderVertexHandles, const VertexHandles& vertexHandles)
- {
- // check if the vertex is in this loop of the collection
- // (there may be 1 - * loops in the collection)
- if (AZStd::find(AZStd::begin(vertexHandles), AZStd::end(vertexHandles), toVertexHandle) !=
- AZStd::end(vertexHandles))
- {
- // if so add the vertex handles for this loop to be returned
- borderVertexHandles.insert(
- borderVertexHandles.end(), vertexHandles.begin(), vertexHandles.end());
- }
- return borderVertexHandles;
- });
- // find the index of the vertex handle in the polygon handle collection
- const auto toVertexHandlePolygonIndex =
- AZStd::find(borderVertexHandles.begin(), borderVertexHandles.end(), toVertexHandle) -
- borderVertexHandles.begin();
- const auto fromVertexHandlePolygonIndex =
- AZStd::find(borderVertexHandles.begin(), borderVertexHandles.end(), fromVertexHandle) -
- borderVertexHandles.begin();
- // we then want to find the vertex after the 'to' vertex, and the vertex before the 'from' vertex
- const VertexHandle afterToVertexHandle = borderVertexHandles
- [((toVertexHandlePolygonIndex + borderVertexHandles.size()) + 1) % borderVertexHandles.size()];
- const VertexHandle beforeFromVertexHandle = borderVertexHandles
- [((fromVertexHandlePolygonIndex + borderVertexHandles.size()) - 1) % borderVertexHandles.size()];
- // find the position of the 'to' and 'from' vertex handle
- const Mesh::Point toVertexPosition = VertexPosition(whiteBox, toVertexHandle);
- const Mesh::Point fromVertexPosition = VertexPosition(whiteBox, fromVertexHandle);
- // find the next position by moving the previous positions by the displacement
- const Mesh::Point nextToVertexPosition = toVertexPosition + displacement;
- const Mesh::Point nextFromVertexPosition = fromVertexPosition + displacement;
- // add two new vertices in the new positions
- const VertexHandle addedToVertexHandle = AddVertex(whiteBox, nextToVertexPosition);
- const VertexHandle addedFromVertexHandle = AddVertex(whiteBox, nextFromVertexPosition);
- // populate data for the next stage
- Internal::EdgeAppendVertexHandles edgeAppendHandles;
- edgeAppendHandles.m_existingPolygonHandle = polygonHandle;
- edgeAppendHandles.m_toVertexHandle = toVertexHandle;
- edgeAppendHandles.m_fromVertexHandle = fromVertexHandle;
- edgeAppendHandles.m_addedFromVertexHandle = addedFromVertexHandle;
- edgeAppendHandles.m_addedToVertexHandle = addedToVertexHandle;
- edgeAppendHandles.m_afterToVertexHandle = afterToVertexHandle;
- edgeAppendHandles.m_beforeFromVertexHandle = beforeFromVertexHandle;
- return edgeAppendHandles;
- }
- // After determining the vertex handles required, build the polygons for the new appended edge
- static Internal::EdgeAppendPolygonHandles AddNewPolygonsForEdgeAppend(
- WhiteBoxMesh& whiteBox, const Internal::EdgeAppendVertexHandles& edgeAppendVertexHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- Internal::EdgeAppendPolygonHandles edgeAppendPolygonHandles;
- // build two faces to make up the polygon on the 'near' side of the edge
- AZStd::vector<FaceVertHandles> nearFaceHandles = {
- FaceVertHandles{
- edgeAppendVertexHandles.m_fromVertexHandle, edgeAppendVertexHandles.m_toVertexHandle,
- edgeAppendVertexHandles.m_addedToVertexHandle},
- FaceVertHandles{
- edgeAppendVertexHandles.m_fromVertexHandle, edgeAppendVertexHandles.m_addedToVertexHandle,
- edgeAppendVertexHandles.m_addedFromVertexHandle}};
- edgeAppendPolygonHandles.m_nearPolygonHandle = AddPolygon(whiteBox, nearFaceHandles);
- AZStd::vector<FaceVertHandles> farFaceHandles;
- // note: need to check the number of faces for the polygon we'll be replacing with the edge append
- if (edgeAppendVertexHandles.m_existingPolygonHandle.m_faceHandles.size() > 1)
- {
- // build two faces to make up the polygon on the 'far' side of the edge
- farFaceHandles = {
- FaceVertHandles{
- edgeAppendVertexHandles.m_addedFromVertexHandle, edgeAppendVertexHandles.m_addedToVertexHandle,
- edgeAppendVertexHandles.m_afterToVertexHandle},
- FaceVertHandles{
- edgeAppendVertexHandles.m_addedFromVertexHandle, edgeAppendVertexHandles.m_afterToVertexHandle,
- edgeAppendVertexHandles.m_beforeFromVertexHandle}};
- }
- else
- {
- // build one face to make up the polygon on the 'far' side of the edge
- // if we're extruding an edge on a triangle not a quad
- farFaceHandles = {FaceVertHandles{
- edgeAppendVertexHandles.m_addedFromVertexHandle, edgeAppendVertexHandles.m_addedToVertexHandle,
- edgeAppendVertexHandles.m_afterToVertexHandle}};
- }
- edgeAppendPolygonHandles.m_farPolygonHandle = AddPolygon(whiteBox, farFaceHandles);
- // add the top triangle for the edge extrusion
- const AZStd::vector<FaceVertHandles> topFaceHandles = {FaceVertHandles{
- edgeAppendVertexHandles.m_fromVertexHandle, edgeAppendVertexHandles.m_addedFromVertexHandle,
- edgeAppendVertexHandles.m_beforeFromVertexHandle}};
- edgeAppendPolygonHandles.m_topPolygonHandle = AddPolygon(whiteBox, topFaceHandles);
- // add the bottom triangle for the edge extrusion
- const AZStd::vector<FaceVertHandles> bottomFaceHandles = {FaceVertHandles{
- edgeAppendVertexHandles.m_toVertexHandle, edgeAppendVertexHandles.m_afterToVertexHandle,
- edgeAppendVertexHandles.m_addedToVertexHandle}};
- edgeAppendPolygonHandles.m_bottomPolygonHandle = AddPolygon(whiteBox, bottomFaceHandles);
- return edgeAppendPolygonHandles;
- }
- // given two polygon handles, return the (first) edge that is shared between the two polygons
- // note: this may not always give expected results for polygons with greater than two faces
- static EdgeHandle FindSelectedEdgeHandle(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& nearPolygonHandle, const PolygonHandle& farPolygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // actually find the new edge we created
- const EdgeHandles nearEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, nearPolygonHandle);
- const EdgeHandles farEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, farPolygonHandle);
- // add all edges and find the one duplicate (this will be the new edge we want to return to the caller)
- EdgeHandles allEdgeHandles;
- allEdgeHandles.reserve(nearEdgeHandles.size() + farEdgeHandles.size());
- allEdgeHandles.insert(allEdgeHandles.end(), nearEdgeHandles.begin(), nearEdgeHandles.end());
- allEdgeHandles.insert(allEdgeHandles.end(), farEdgeHandles.begin(), farEdgeHandles.end());
- AZStd::sort(allEdgeHandles.begin(), allEdgeHandles.end());
- auto* edgeIt = AZStd::adjacent_find(allEdgeHandles.begin(), allEdgeHandles.end());
- if (edgeIt != allEdgeHandles.end())
- {
- return *edgeIt;
- }
- return EdgeHandle{};
- }
- static bool EdgeExtrusionAllowed(const PolygonHandle& polygonHandle)
- {
- // currently only allow edge extrusion for quad polygons
- return polygonHandle.m_faceHandles.size() <= 2;
- }
- EdgeHandle TranslateEdgeAppend(
- WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& displacement)
- {
- WHITEBOX_LOG(
- "White Box", "TranslateEdgeAppend eh(%s) %s", ToString(edgeHandle).c_str(), AZStd::to_string(displacement).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // the new and existing handles required for an edge append
- const Internal::EdgeAppendVertexHandles edgeAppendVertexHandles =
- CalculateEdgeAppendVertexHandles(whiteBox, edgeHandle, displacement);
- // if edge extrusion is not allowed simply return the previous edge handle
- if (!EdgeExtrusionAllowed(edgeAppendVertexHandles.m_existingPolygonHandle))
- {
- return edgeHandle;
- }
- // remove the current polygon (two new polygons will later be inserted in its place)
- RemoveFaces(whiteBox, edgeAppendVertexHandles.m_existingPolygonHandle.m_faceHandles);
- const Internal::EdgeAppendPolygonHandles edgeAppendPolygonHandles =
- AddNewPolygonsForEdgeAppend(whiteBox, edgeAppendVertexHandles);
- // update internal state
- CalculateNormals(whiteBox);
- CalculatePlanarUVs(whiteBox);
- return FindSelectedEdgeHandle(
- whiteBox, edgeAppendPolygonHandles.m_nearPolygonHandle, edgeAppendPolygonHandles.m_farPolygonHandle);
- }
- AZ::Vector3 PolygonNormal(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return AZStd::accumulate(
- polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend(),
- AZ::Vector3::CreateZero(),
- [&whiteBox](const AZ::Vector3& normal, const FaceHandle faceHandle)
- {
- return normal + FaceNormal(whiteBox, faceHandle);
- })
- .GetNormalizedSafe();
- }
- PolygonHandle FacePolygonHandle(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- const auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- const auto polygonIt = polygonProps.find(om_fh(faceHandle));
- if (polygonIt != polygonProps.end())
- {
- return PolygonHandleFromInternal(polygonIt->second);
- }
- return {};
- }
- VertexHandles PolygonVertexHandles(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return FacesVertexHandles(whiteBox, polygonHandle.m_faceHandles);
- }
- VertexHandlesCollection PolygonBorderVertexHandles(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return BorderVertexHandles(whiteBox, PolygonBorderHalfedgeHandles(whiteBox, polygonHandle));
- }
- VertexHandles PolygonBorderVertexHandlesFlattened(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const VertexHandlesCollection borderVertexHandlesCollection =
- BorderVertexHandles(whiteBox, PolygonBorderHalfedgeHandles(whiteBox, polygonHandle));
- VertexHandles polygonBorderVertexHandles;
- for (const auto& borderVertexHandles : borderVertexHandlesCollection)
- {
- polygonBorderVertexHandles.insert(
- polygonBorderVertexHandles.end(), borderVertexHandles.cbegin(), borderVertexHandles.cend());
- }
- return polygonBorderVertexHandles;
- }
- HalfedgeHandles PolygonBorderHalfedgeHandlesFlattened(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const HalfedgeHandlesCollection borderHalfedgeHandlesCollection =
- PolygonBorderHalfedgeHandles(whiteBox, polygonHandle);
- HalfedgeHandles polygonBorderHalfedgeHandles;
- for (const auto& borderHalfedgeHandles : borderHalfedgeHandlesCollection)
- {
- polygonBorderHalfedgeHandles.insert(
- polygonBorderHalfedgeHandles.end(), borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend());
- }
- return polygonBorderHalfedgeHandles;
- }
- HalfedgeHandles PolygonHalfedgeHandles(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return AZStd::accumulate(
- polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend(), HalfedgeHandles{},
- [&whiteBox](HalfedgeHandles halfedges, const FaceHandle faceHandle)
- {
- const HalfedgeHandles nextHalfedgeHandles = FaceHalfedgeHandles(whiteBox, faceHandle);
- halfedges.insert(halfedges.end(), nextHalfedgeHandles.cbegin(), nextHalfedgeHandles.cend());
- return halfedges;
- });
- }
- HalfedgeHandlesCollection PolygonBorderHalfedgeHandles(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- return BorderHalfedgeHandles(whiteBox, polygonHandle.m_faceHandles);
- }
- AZStd::vector<AZ::Vector3> PolygonVertexPositions(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return VertexPositions(whiteBox, PolygonVertexHandles(whiteBox, polygonHandle));
- }
- VertexPositionsCollection PolygonBorderVertexPositions(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto polygonBorderVertexHandlesCollection = PolygonBorderVertexHandles(whiteBox, polygonHandle);
- VertexPositionsCollection polygonBorderVertexPositionsCollection;
- polygonBorderVertexPositionsCollection.reserve(polygonBorderVertexHandlesCollection.size());
- for (const auto& polygonBorderVertexHandles : polygonBorderVertexHandlesCollection)
- {
- polygonBorderVertexPositionsCollection.push_back(VertexPositions(whiteBox, polygonBorderVertexHandles));
- }
- return polygonBorderVertexPositionsCollection;
- }
- AZStd::vector<AZ::Vector3> PolygonFacesPositions(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return FacesPositions(whiteBox, polygonHandle.m_faceHandles);
- }
- AZ::Vector3 VertexPosition(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- return whiteBox.mesh.point(om_vh(vertexHandle));
- }
- AZStd::vector<AZ::Vector3> VertexPositions(const WhiteBoxMesh& whiteBox, const VertexHandles& vertexHandles)
- {
- AZStd::vector<AZ::Vector3> positions;
- positions.reserve(vertexHandles.size());
- AZStd::transform(
- vertexHandles.cbegin(), vertexHandles.cend(), AZStd::back_inserter(positions),
- [&whiteBox](const auto vertexHandle)
- {
- return VertexPosition(whiteBox, vertexHandle);
- });
- return positions;
- }
- EdgeHandles VertexUserEdgeHandles(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- auto vertexEdgeHandles = VertexEdgeHandles(whiteBox, vertexHandle);
- vertexEdgeHandles.erase(
- AZStd::remove_if(
- AZStd::begin(vertexEdgeHandles), AZStd::end(vertexEdgeHandles),
- [&whiteBox](const EdgeHandle edgeHandle)
- {
- return !EdgeIsUser(whiteBox, edgeHandle);
- }),
- AZStd::end(vertexEdgeHandles));
- return vertexEdgeHandles;
- }
- template<typename EdgeFn>
- static AZStd::vector<AZ::Vector3> VertexUserEdges(
- const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle, EdgeFn&& edgeFn)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto vertexEdgeHandles = VertexUserEdgeHandles(whiteBox, vertexHandle);
- AZStd::vector<AZ::Vector3> edgeVectors;
- edgeVectors.reserve(vertexEdgeHandles.size());
- AZStd::transform(
- AZStd::cbegin(vertexEdgeHandles), AZStd::cend(vertexEdgeHandles), AZStd::back_inserter(edgeVectors),
- [&whiteBox, edgeFn, vertexHandle](const EdgeHandle edgeHandle)
- {
- return edgeFn(whiteBox, edgeHandle, vertexHandle);
- });
- // filter out any invalid edges
- edgeVectors.erase(
- AZStd::remove_if(
- AZStd::begin(edgeVectors), AZStd::end(edgeVectors),
- [](const AZ::Vector3& edge)
- {
- return AZ::IsCloseMag<float>(edge.GetLengthSq(), 0.0f);
- }),
- AZStd::end(edgeVectors));
- return edgeVectors;
- }
- AZStd::vector<AZ::Vector3> VertexUserEdgeVectors(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- return VertexUserEdges(whiteBox, vertexHandle, &EdgeVectorWithStartingVertexHandle);
- }
- AZStd::vector<AZ::Vector3> VertexUserEdgeAxes(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- return VertexUserEdges(whiteBox, vertexHandle, &EdgeAxisWithStartingVertexHandle);
- }
- bool VertexIsHidden(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- VertexBoolPropertyHandle vertexPropsHiddenHandle;
- whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
- return whiteBox.mesh.property(vertexPropsHiddenHandle, om_vh(vertexHandle));
- }
- bool VertexIsIsolated(const WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- const auto connectedEdgeHandles = VertexEdgeHandles(whiteBox, vertexHandle);
- return AZStd::all_of(
- AZStd::cbegin(connectedEdgeHandles), AZStd::cend(connectedEdgeHandles),
- [&whiteBox](const EdgeHandle edgeHandle)
- {
- return !EdgeIsUser(whiteBox, edgeHandle);
- });
- }
- AZ::Vector3 FaceNormal(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return whiteBox.mesh.normal(om_fh(faceHandle));
- }
- AZ::Vector2 HalfedgeUV(const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return whiteBox.mesh.texcoord2D(om_heh(halfedgeHandle));
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // note: MeshXXXCount functions are only valid if garbage_collection is called after
- // each face/vertex removal. If garbage_collection is deferred, the faces()/vertices()/halfedges()
- // range must be used to count iterations via skipping iterator to ignore deleted faces
- AZ::u64 MeshFaceCount(const WhiteBoxMesh& whiteBox)
- {
- return static_cast<AZ::u64>(whiteBox.mesh.n_faces());
- }
- AZ::u64 MeshHalfedgeCount(const WhiteBoxMesh& whiteBox)
- {
- return static_cast<AZ::u64>(whiteBox.mesh.n_halfedges());
- }
- AZ::u64 MeshVertexCount(const WhiteBoxMesh& whiteBox)
- {
- return static_cast<AZ::u64>(whiteBox.mesh.n_vertices());
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
- Faces MeshFaces(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- Faces faces;
- faces.reserve(MeshFaceCount(whiteBox));
- for (const auto& faceHandle : MeshFaceHandles(whiteBox))
- {
- const auto halfEdgeHandles = FaceHalfedgeHandles(whiteBox, faceHandle);
- Face face;
- AZStd::transform(
- halfEdgeHandles.begin(), halfEdgeHandles.end(), face.begin(),
- [&whiteBox](const auto halfedgeHandle)
- {
- // calculate the position of each vertex at the tip of each vertex handle
- return VertexPosition(whiteBox, HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle));
- });
- faces.push_back(face);
- }
- return faces;
- }
- void CalculatePlanarUVs(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- auto& mesh = whiteBox.mesh;
- for (const auto& faceHandle : faceHandles)
- {
- for (Mesh::ConstFaceHalfedgeCCWIter faceHalfedgeIt = mesh.fh_ccwiter(om_fh(faceHandle));
- faceHalfedgeIt.is_valid(); ++faceHalfedgeIt)
- {
- const Mesh::HalfedgeHandle heh = *faceHalfedgeIt;
- const Mesh::VertexHandle vh = mesh.to_vertex_handle(heh);
- const AZ::Vector3 position = mesh.point(vh);
- const AZ::Vector3 normal = FaceNormal(whiteBox, faceHandle);
- const Mesh::TexCoord2D uv = CreatePlanarUVFromVertex(normal, position);
- mesh.set_texcoord2D(heh, uv);
- }
- }
- }
- void CalculatePlanarUVs(WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- CalculatePlanarUVs(whiteBox, MeshFaceHandles(whiteBox));
- }
- static PolygonHandle MergeFaces(
- const WhiteBoxMesh& whiteBox, const HalfedgeHandle halfedgeHandle,
- const HalfedgeHandle oppositeHalfedgeHandle, const HalfedgeHandles& borderHalfedgeHandles,
- const EdgeHandles& buildingEdgeHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // the polygon handle to build
- PolygonHandle polygonHandle;
- // begin populating halfedges to visit to build a polygon
- HalfedgeHandles halfedgesToVisit{halfedgeHandle};
- // store already visited halfedges
- HalfedgeHandles visitedHalfedges;
- while (!halfedgesToVisit.empty())
- {
- const HalfedgeHandle halfedgeToVisit = halfedgesToVisit.back();
- halfedgesToVisit.pop_back();
- visitedHalfedges.push_back(halfedgeToVisit);
- const FaceHandle faceHandleToVisit = HalfedgeFaceHandle(whiteBox, halfedgeToVisit);
- const HalfedgeHandles faceHalfedges = FaceHalfedgeHandles(whiteBox, faceHandleToVisit);
- // check we have not already visited this face handle
- if (AZStd::find(
- polygonHandle.m_faceHandles.cbegin(), polygonHandle.m_faceHandles.cend(), faceHandleToVisit) !=
- polygonHandle.m_faceHandles.cend())
- {
- continue;
- }
- // store the face handle in this polygon
- polygonHandle.m_faceHandles.push_back(faceHandleToVisit);
- // for all halfedges
- for (const auto& faceHalfedgeHandle : faceHalfedges)
- {
- const EdgeHandle edgeHandle = HalfedgeEdgeHandle(whiteBox, faceHalfedgeHandle);
- // if we haven't seen this halfedge before and we want to track it,
- // store it in visited halfedges
- if (faceHalfedgeHandle != oppositeHalfedgeHandle
- // ignore border halfedges (not inside the polygon)
- && AZStd::find(borderHalfedgeHandles.cbegin(), borderHalfedgeHandles.cend(), faceHalfedgeHandle) ==
- borderHalfedgeHandles.cend()
- // ensure we do not visit the same halfedge again
- && AZStd::find(visitedHalfedges.cbegin(), visitedHalfedges.cend(), faceHalfedgeHandle) ==
- visitedHalfedges.cend()
- // ignore the halfedge if we've already tracked it in our 'building' list
- && AZStd::find(buildingEdgeHandles.cbegin(), buildingEdgeHandles.cend(), edgeHandle) ==
- buildingEdgeHandles.cend())
- {
- halfedgesToVisit.push_back(HalfedgeOppositeHalfedgeHandle(whiteBox, faceHalfedgeHandle));
- }
- }
- }
- // return the polygon we've built by traversing all connected face handles
- // (by following the connected halfedges)
- return polygonHandle;
- }
- static void PopulatePolygonProps(FaceHandlePolygonMapping& polygonProps, const FaceHandles& faceHandles)
- {
- for (const auto& faceHandle : faceHandles)
- {
- auto polygonIt = polygonProps.find(om_fh(faceHandle));
- for (const auto& innerFaceHandle : faceHandles)
- {
- polygonIt->second.push_back(om_fh(innerFaceHandle));
- }
- }
- }
- static void ClearPolygonProps(FaceHandlePolygonMapping& polygonProps, const FaceHandles& faceHandles)
- {
- for (const auto& faceHandle : faceHandles)
- {
- if (auto polygonIt = polygonProps.find(om_fh(faceHandle)); polygonIt != polygonProps.end())
- {
- polygonIt->second.clear();
- }
- }
- }
- // restore all vertices along the restored edges (after creating a new polygon)
- static void RestoreVertexHandlesForEdges(WhiteBoxMesh& whiteBox, const EdgeHandles& restoredEdgeHandles)
- {
- for (const auto& edgeHandle : restoredEdgeHandles)
- {
- for (const auto& vertexHandle : EdgeVertexHandles(whiteBox, edgeHandle))
- {
- RestoreVertex(whiteBox, vertexHandle);
- }
- }
- }
- AZStd::optional<AZStd::array<PolygonHandle, 2>> RestoreEdge(
- WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, EdgeHandles& restoringEdgeHandles)
- {
- WHITEBOX_LOG("White Box", "RestoreEdge eh(%s)", ToString(edgeHandle).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // check we're not selecting an existing user edge
- if (!EdgeIsHidden(whiteBox, edgeHandle))
- {
- // do nothing
- return {};
- }
- // attempt to make a new polygon if possible
- const HalfedgeHandle firstHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
- const HalfedgeHandle secondHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second);
- // the existing polygon, containing the edge we've selected
- const PolygonHandle polygonHandle =
- FacePolygonHandle(whiteBox, HalfedgeFaceHandle(whiteBox, firstHalfedgeHandle));
- // all border halfedges (not necessarily contiguous)
- const HalfedgeHandles polygonBorderHalfedgeHandles =
- PolygonBorderHalfedgeHandlesFlattened(whiteBox, polygonHandle);
- const auto firstPolygon = MergeFaces(
- whiteBox, firstHalfedgeHandle, secondHalfedgeHandle, polygonBorderHalfedgeHandles,
- restoringEdgeHandles);
- const auto secondPolygon = MergeFaces(
- whiteBox, secondHalfedgeHandle, firstHalfedgeHandle, polygonBorderHalfedgeHandles,
- restoringEdgeHandles);
- // check if the first and second polygons are identical,
- // this can happen if the vertex list forms a loop
- const bool identical = [first = firstPolygon, second = secondPolygon]() mutable
- {
- AZStd::sort(first.m_faceHandles.begin(), first.m_faceHandles.end());
- AZStd::sort(second.m_faceHandles.begin(), second.m_faceHandles.end());
- return first == second;
- }();
- // if the number of face handles in at least one of the new polygons is the
- // same as the existing polygon, we know a new polygon has not been formed
- // (the restored edge has not connected to another edge and created a new polygon)
- if (firstPolygon.m_faceHandles.size() == polygonHandle.m_faceHandles.size() || identical)
- {
- restoringEdgeHandles.push_back(edgeHandle);
- return {};
- }
- // get polygon property handle from mesh
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- // update all face handles to refer to the new face handles in the group
- // clear existing face handles
- ClearPolygonProps(polygonProps, polygonHandle.m_faceHandles);
- // populate face handles for first polygon
- PopulatePolygonProps(polygonProps, firstPolygon.m_faceHandles);
- // populate face handles for second polygon
- PopulatePolygonProps(polygonProps, secondPolygon.m_faceHandles);
- // get all edges
- const auto firstPolygonEdges = Api::PolygonBorderEdgeHandlesFlattened(whiteBox, firstPolygon);
- const auto secondPolygonEdges = Api::PolygonBorderEdgeHandlesFlattened(whiteBox, secondPolygon);
- Api::EdgeHandles allPolygonEdges;
- allPolygonEdges.reserve(firstPolygonEdges.size() + secondPolygonEdges.size());
- allPolygonEdges.insert(allPolygonEdges.end(), firstPolygonEdges.begin(), firstPolygonEdges.end());
- allPolygonEdges.insert(allPolygonEdges.end(), secondPolygonEdges.begin(), secondPolygonEdges.end());
- AZStd::sort(allPolygonEdges.begin(), allPolygonEdges.end());
- allPolygonEdges.erase(AZStd::unique(allPolygonEdges.begin(), allPolygonEdges.end()), allPolygonEdges.end());
- RestoreVertexHandlesForEdges(whiteBox, restoringEdgeHandles);
- // remove all edges that make up the new polygons from the ones currently being restored
- restoringEdgeHandles.erase(
- AZStd::remove_if(
- restoringEdgeHandles.begin(), restoringEdgeHandles.end(),
- [&allPolygonEdges](const Api::EdgeHandle edgeHandle)
- {
- return AZStd::find(allPolygonEdges.begin(), allPolygonEdges.end(), edgeHandle) !=
- allPolygonEdges.end();
- }),
- restoringEdgeHandles.end());
- return AZStd::make_optional<AZStd::array<PolygonHandle, 2>>(
- AZStd::array<PolygonHandle, 2>{firstPolygon, secondPolygon});
- }
- void RestoreVertex(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- WHITEBOX_LOG("White Box", "RestoreVertex vh(%s)", ToString(vertexHandle).c_str());
- VertexBoolPropertyHandle vertexPropsHiddenHandle;
- whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
- whiteBox.mesh.property(vertexPropsHiddenHandle, om_vh(vertexHandle)) = false;
- }
- bool TryRestoreVertex(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- WHITEBOX_LOG("White Box", "TryRestoreVertex vh(%s)", ToString(vertexHandle).c_str());
- // if none of the connected edge handles are user edges then the vertex should not be restored
- if (!VertexIsIsolated(whiteBox, vertexHandle))
- {
- RestoreVertex(whiteBox, vertexHandle);
- return true;
- }
- return false;
- }
- PolygonHandle HideEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- WHITEBOX_LOG("White Box", "HideEdge eh(%s)", ToString(edgeHandle).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- if (MeshHalfedgeCount(whiteBox) == 0)
- {
- return {};
- }
- // get halfedge handles
- const HalfedgeHandle firstHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
- const HalfedgeHandle secondHalfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::Second);
- // get face handles from each halfedge
- const FaceHandle firstFaceHandle = HalfedgeFaceHandle(whiteBox, firstHalfedgeHandle);
- const FaceHandle secondFaceHandle = HalfedgeFaceHandle(whiteBox, secondHalfedgeHandle);
- // get polygon handle from each face handle
- const PolygonHandle firstPolygonHandle = FacePolygonHandle(whiteBox, firstFaceHandle);
- const PolygonHandle secondPolygonHandle = FacePolygonHandle(whiteBox, secondFaceHandle);
- // get all vertex handles associated with the first polygon
- const VertexHandles firstPolygonVertexHandles = PolygonVertexHandles(whiteBox, firstPolygonHandle);
- // union of all face handles
- FaceHandles combinedFaceHandles;
- combinedFaceHandles.reserve(
- firstPolygonHandle.m_faceHandles.size() + secondPolygonHandle.m_faceHandles.size());
- combinedFaceHandles.insert(
- combinedFaceHandles.end(), firstPolygonHandle.m_faceHandles.begin(),
- firstPolygonHandle.m_faceHandles.end());
- combinedFaceHandles.insert(
- combinedFaceHandles.end(), secondPolygonHandle.m_faceHandles.begin(),
- secondPolygonHandle.m_faceHandles.end());
- // get polygon property handle from mesh
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- // update all face handles to refer to the new face handles in the group
- for (const auto& faceHandle : combinedFaceHandles)
- {
- auto polygonIt = polygonProps.find(om_fh(faceHandle));
- polygonIt->second.clear();
- for (const auto& innerFaceHandle : combinedFaceHandles)
- {
- polygonIt->second.push_back(om_fh(innerFaceHandle));
- }
- }
- // hide any vertices that are not connected to a 'user' edge
- for (const auto& vertexHandle : firstPolygonVertexHandles)
- {
- if (VertexIsIsolated(whiteBox, vertexHandle))
- {
- HideVertex(whiteBox, vertexHandle);
- }
- }
- return PolygonHandle{combinedFaceHandles};
- }
- VertexHandle SplitFace(WhiteBoxMesh& whiteBox, const FaceHandle faceHandle, const AZ::Vector3& position)
- {
- WHITEBOX_LOG("White Box", "SplitFace fh(%s)", ToString(faceHandle).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto omFaceHandle = om_fh(faceHandle);
- const auto omVertexHandle = whiteBox.mesh.split_copy(omFaceHandle, position);
- const VertexHandle splitVertexHandle = wb_vh(omVertexHandle);
- // as all new edges will be by default hidden, ensure
- // the newly added vertex is also hidden
- HideVertex(whiteBox, splitVertexHandle);
- // build collection of current face handles for newly inserted vertex
- auto omFaceHandles = AZStd::accumulate(
- whiteBox.mesh.vf_ccwbegin(omVertexHandle), whiteBox.mesh.vf_ccwend(omVertexHandle),
- AZStd::vector<Mesh::FaceHandle>{},
- [](AZStd::vector<Mesh::FaceHandle> faceHandles, const Mesh::FaceHandle faceHandle)
- {
- faceHandles.push_back(faceHandle);
- return faceHandles;
- });
- // get polygon property handle from mesh
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- // get all faces associated with the split face handle and added the newly split faces
- // ensuring we do not have any duplicates
- const auto polygonIt = polygonProps.find(om_fh(faceHandle));
- omFaceHandles.insert(omFaceHandles.end(), polygonIt->second.begin(), polygonIt->second.end());
- AZStd::sort(omFaceHandles.begin(), omFaceHandles.end());
- omFaceHandles.erase(AZStd::unique(omFaceHandles.begin(), omFaceHandles.end()), omFaceHandles.end());
- // update all face handles to point to the new polygon grouping
- for (const auto& omFaceHandle2 : omFaceHandles)
- {
- polygonProps[omFaceHandle2] = omFaceHandles;
- }
- return splitVertexHandle;
- }
- VertexHandle SplitEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& position)
- {
- WHITEBOX_LOG("White Box", "SplitEdge eh(%s)", ToString(edgeHandle).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const HalfedgeHandle halfedgeHandle = EdgeHalfedgeHandle(whiteBox, edgeHandle, EdgeHalfedge::First);
- const VertexHandle tailVertexHandle = HalfedgeVertexHandleAtTail(whiteBox, halfedgeHandle);
- const VertexHandle tipVertexHandle = HalfedgeVertexHandleAtTip(whiteBox, halfedgeHandle);
- const VertexHandle existingConnectedVerts[] = {tailVertexHandle, tipVertexHandle};
- // determine if the edge is a user edge or not before the split
- const bool userEdge = EdgeIsUser(whiteBox, halfedgeHandle, edgeHandle);
- const auto omEdgeHandle = om_eh(edgeHandle);
- const auto omVertexHandle = whiteBox.mesh.add_vertex(position);
- whiteBox.mesh.split_copy(omEdgeHandle, omVertexHandle);
- const VertexHandle splitVertexHandle = wb_vh(omVertexHandle);
- // if the edge that was split was not a 'user' edge we should ensure the
- // newly added vertex is also hidden
- if (!userEdge)
- {
- HideVertex(whiteBox, splitVertexHandle);
- }
- // get all outing edge handles from the new inserted vertex
- const EdgeHandles splitEdgeHandles = VertexEdgeHandles(whiteBox, splitVertexHandle);
- AZStd::for_each(
- splitEdgeHandles.cbegin(), splitEdgeHandles.cend(),
- [&whiteBox, &existingConnectedVerts](const EdgeHandle edgeHandle)
- {
- const auto vertexHandles = EdgeVertexHandles(whiteBox, edgeHandle);
- const bool alreadyConnectedVertex = AZStd::any_of(
- AZStd::cbegin(existingConnectedVerts), AZStd::cend(existingConnectedVerts),
- [&vertexHandles](const VertexHandle vertexHandle)
- {
- return AZStd::find(
- AZStd::cbegin(vertexHandles), AZStd::cend(vertexHandles), vertexHandle) !=
- AZStd::cend(vertexHandles);
- });
- // find if the edge was added or is part of the existing edge which was split
- if (!alreadyConnectedVertex)
- {
- const FaceHandles faceHandles = EdgeFaceHandles(whiteBox, edgeHandle);
- const PolygonHandle polygonHandle = FacePolygonHandle(whiteBox, faceHandles[0]);
- // if the edge was not already connected to on of the existing verts,
- // find the associated polygon handle update them with the newly split faces
- const PolygonHandle existingPolygonHandle = polygonHandle.m_faceHandles.empty()
- ? FacePolygonHandle(whiteBox, faceHandles[1])
- : polygonHandle;
- const FaceHandle newFaceHandle =
- polygonHandle.m_faceHandles.empty() ? faceHandles[0] : faceHandles[1];
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- auto omExistingPolygonHandle = InternalFaceHandlesFromPolygon(existingPolygonHandle);
- omExistingPolygonHandle.push_back(om_fh(newFaceHandle));
- // update all face handles to point to the new polygon grouping
- for (const Mesh::FaceHandle& faceHandle : omExistingPolygonHandle)
- {
- polygonProps[faceHandle] = omExistingPolygonHandle;
- }
- }
- });
- return splitVertexHandle;
- }
- bool FlipEdge(WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- WHITEBOX_LOG("White Box", "FlipEdge eh(%s)", ToString(edgeHandle).c_str());
- const auto omEdgeHandle = om_eh(edgeHandle);
- // check if edge can be flipped
- const bool canFlip = whiteBox.mesh.is_flip_ok(omEdgeHandle) && EdgeIsHidden(whiteBox, edgeHandle);
- if (canFlip)
- {
- whiteBox.mesh.flip(omEdgeHandle);
- }
- return canFlip;
- }
- void HideVertex(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle)
- {
- WHITEBOX_LOG("White Box", "HideVertex vh(%s)", ToString(vertexHandle).c_str());
- VertexBoolPropertyHandle vertexPropsHiddenHandle;
- whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
- whiteBox.mesh.property(vertexPropsHiddenHandle, om_vh(vertexHandle)) = true;
- }
- void Clear(WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- whiteBox.mesh.remove_property(polygonPropsHandle);
- VertexBoolPropertyHandle vertexPropsHiddenHandle;
- whiteBox.mesh.get_property_handle(vertexPropsHiddenHandle, VertexHiddenProp);
- whiteBox.mesh.remove_property(vertexPropsHiddenHandle);
- whiteBox.mesh.clear();
- InitializeWhiteBoxMesh(whiteBox);
- }
- PolygonHandle AddTriPolygon(
- WhiteBoxMesh& whiteBox, const VertexHandle vh0, const VertexHandle vh1, const VertexHandle vh2)
- {
- WHITEBOX_LOG(
- "White Box", "AddTriPolygon vh(%s), vh(%s), vh(%s)", ToString(vh0).c_str(), ToString(vh1).c_str(),
- ToString(vh2).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return AddPolygon(whiteBox, AZStd::vector<FaceVertHandles>{{vh0, vh1, vh2}});
- }
- PolygonHandle AddQuadPolygon(
- WhiteBoxMesh& whiteBox, const VertexHandle vh0, const VertexHandle vh1, const VertexHandle vh2,
- const VertexHandle vh3)
- {
- WHITEBOX_LOG(
- "White Box", "AddQuadPolygon vh(%s), vh(%s), vh(%s), vh(%s)", ToString(vh0).c_str(),
- ToString(vh1).c_str(), ToString(vh2).c_str(), ToString(vh3).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return AddPolygon(whiteBox, AZStd::vector<FaceVertHandles>{{vh0, vh1, vh2}, {vh0, vh2, vh3}});
- }
- PolygonHandle AddPolygon(WhiteBoxMesh& whiteBox, const FaceVertHandlesList& faceVertHandles)
- {
- WHITEBOX_LOG("White Box", "AddPolygon [%s]", ToString(faceVertHandles).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- auto polygon = FaceHandlesInternal{};
- polygon.reserve(faceVertHandles.size());
- for (const auto& face : faceVertHandles)
- {
- polygon.push_back(whiteBox.mesh.add_face(
- om_vh(face.m_vertexHandles[0]), om_vh(face.m_vertexHandles[1]), om_vh(face.m_vertexHandles[2])));
- }
- PolygonHandle polygonHandle = PolygonHandleFromInternal(polygon);
- auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- // multiple face handles map to a polygon handle
- for (const auto& faceHandle : polygon)
- {
- polygonProps[faceHandle] = polygon;
- }
- return polygonHandle;
- }
- PolygonHandles InitializeAsUnitCube(WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // generate vertices
- VertexHandle vertexHandles[8];
- // top verts
- vertexHandles[0] = AddVertex(whiteBox, AZ::Vector3(-0.5f, -0.5f, 0.5f));
- vertexHandles[1] = AddVertex(whiteBox, AZ::Vector3(0.5f, -0.5f, 0.5f));
- vertexHandles[2] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.5f, 0.5f));
- vertexHandles[3] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.5f, 0.5f));
- // bottom verts
- vertexHandles[4] = AddVertex(whiteBox, AZ::Vector3(-0.5f, -0.5f, -0.5f));
- vertexHandles[5] = AddVertex(whiteBox, AZ::Vector3(0.5f, -0.5f, -0.5f));
- vertexHandles[6] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.5f, -0.5f));
- vertexHandles[7] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.5f, -0.5f));
- // generate faces
- PolygonHandles polygonHandles{
- // top
- AddQuadPolygon(whiteBox, vertexHandles[0], vertexHandles[1], vertexHandles[2], vertexHandles[3]),
- // bottom
- AddQuadPolygon(whiteBox, vertexHandles[7], vertexHandles[6], vertexHandles[5], vertexHandles[4]),
- // front
- AddQuadPolygon(whiteBox, vertexHandles[4], vertexHandles[5], vertexHandles[1], vertexHandles[0]),
- // right
- AddQuadPolygon(whiteBox, vertexHandles[5], vertexHandles[6], vertexHandles[2], vertexHandles[1]),
- // back
- AddQuadPolygon(whiteBox, vertexHandles[6], vertexHandles[7], vertexHandles[3], vertexHandles[2]),
- // left
- AddQuadPolygon(whiteBox, vertexHandles[7], vertexHandles[4], vertexHandles[0], vertexHandles[3])};
- CalculateNormals(whiteBox);
- CalculatePlanarUVs(whiteBox);
- return polygonHandles;
- }
- PolygonHandle InitializeAsUnitQuad(WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // generate vertices
- VertexHandle vertexHandles[4];
- // front face
- vertexHandles[0] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.0f, -0.5f)); // bottom left
- vertexHandles[1] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.0f, -0.5f)); // bottom right
- vertexHandles[2] = AddVertex(whiteBox, AZ::Vector3(0.5f, 0.0f, 0.5f)); // top right
- vertexHandles[3] = AddVertex(whiteBox, AZ::Vector3(-0.5f, 0.0f, 0.5f)); // top left
- // generate faces - front
- auto polygonHandle =
- AddQuadPolygon(whiteBox, vertexHandles[0], vertexHandles[1], vertexHandles[2], vertexHandles[3]);
- CalculateNormals(whiteBox);
- CalculatePlanarUVs(whiteBox);
- return polygonHandle;
- }
- PolygonHandle InitializeAsUnitTriangle(WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // generate vertices
- VertexHandle vertexHandles[3];
- const auto pointOnCircle = [](const float angle)
- {
- return AZ::Vector3{cosf(angle), sinf(angle), 0.0f};
- };
- // front face
- vertexHandles[0] = AddVertex(whiteBox, pointOnCircle(AZ::DegToRad(90.0f))); // top
- vertexHandles[1] = AddVertex(whiteBox, pointOnCircle(AZ::DegToRad(-150.0f))); // bottom left
- vertexHandles[2] = AddVertex(whiteBox, pointOnCircle(AZ::DegToRad(-30.0f))); // bottom right
- // generate faces - front
- auto polygonHandle = AddTriPolygon(whiteBox, vertexHandles[0], vertexHandles[1], vertexHandles[2]);
- CalculateNormals(whiteBox);
- CalculatePlanarUVs(whiteBox);
- return polygonHandle;
- }
- void SetVertexPosition(WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle, const AZ::Vector3& position)
- {
- WHITEBOX_LOG(
- "White Box", "SetVertexPosition vh(%s) %s", ToString(vertexHandle).c_str(), AZStd::to_string(position).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- whiteBox.mesh.set_point(om_vh(vertexHandle), position);
- }
- void SetVertexPositionAndUpdateUVs(
- WhiteBoxMesh& whiteBox, const VertexHandle vertexHandle, const AZ::Vector3& position)
- {
- WHITEBOX_LOG(
- "White Box", "SetVertexPositionAndUpdateUVs vh(%s) %s", ToString(vertexHandle).c_str(), AZStd::to_string(position).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- SetVertexPosition(whiteBox, vertexHandle, position);
- CalculatePlanarUVs(whiteBox);
- }
- VertexHandle AddVertex(WhiteBoxMesh& whiteBox, const AZ::Vector3& vertex)
- {
- WHITEBOX_LOG("White Box", "AddVertex %s", AZStd::to_string(vertex).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return wb_vh(whiteBox.mesh.add_vertex(vertex));
- }
- FaceHandle AddFace(WhiteBoxMesh& whiteBox, const VertexHandle v0, const VertexHandle v1, const VertexHandle v2)
- {
- WHITEBOX_LOG(
- "White Box", "AddFace vh(%s), vh(%s), vh(%s)", ToString(v0).c_str(), ToString(v1).c_str(),
- ToString(v2).c_str());
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return wb_fh(whiteBox.mesh.add_face(om_vh(v0), om_vh(v1), om_vh(v2)));
- }
- void CalculateNormals(WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- whiteBox.mesh.update_normals();
- }
- void ZeroUVs(WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- for (const Mesh::FaceHandle& faceHandle : whiteBox.mesh.faces())
- {
- for (Mesh::FaceHalfedgeCCWIter faceHalfedgeIt = whiteBox.mesh.fh_ccwiter(faceHandle);
- faceHalfedgeIt.is_valid(); ++faceHalfedgeIt)
- {
- whiteBox.mesh.set_texcoord2D(*faceHalfedgeIt, AZ::Vector2::CreateZero());
- }
- }
- }
- AZ::Vector3 EdgeMidpoint(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle)
- {
- const auto vertexPositions = EdgeVertexPositions(whiteBox, edgeHandle);
- return (vertexPositions[0] + vertexPositions[1]) * 0.5f;
- }
- AZ::Vector3 FaceMidpoint(const WhiteBoxMesh& whiteBox, const FaceHandle faceHandle)
- {
- const auto vertexPositions = FaceVertexPositions(whiteBox, faceHandle);
- return AZStd::accumulate(
- AZStd::begin(vertexPositions), AZStd::end(vertexPositions), AZ::Vector3::CreateZero(),
- [](const AZ::Vector3& acc, const AZ::Vector3& position)
- {
- return acc + position;
- }) /
- 3;
- }
- AZ::Vector3 PolygonMidpoint(const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle)
- {
- // first attempt using border vertex handles (this is usually what we want, but it might
- // fail if all edges of a polygon have been hidden)
- if (const auto polygonBorderVertexHandles = PolygonBorderVertexHandlesFlattened(whiteBox, polygonHandle);
- !polygonBorderVertexHandles.empty())
- {
- return VerticesMidpoint(whiteBox, polygonBorderVertexHandles);
- }
- // if that fails, fall back to all vertex handles in the polygon to calculate the midpoint
- return VerticesMidpoint(whiteBox, PolygonVertexHandles(whiteBox, polygonHandle));
- }
- AZ::Vector3 VerticesMidpoint(const WhiteBoxMesh& whiteBox, const VertexHandles& vertexHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- AzToolsFramework::MidpointCalculator midpointCalculator;
- for (const auto& vertexHandle : vertexHandles)
- {
- midpointCalculator.AddPosition(VertexPosition(whiteBox, vertexHandle));
- }
- return midpointCalculator.CalculateMidpoint();
- }
- static HalfedgeHandle FindHalfedgeInAdjacentPolygon(
- const WhiteBoxMesh& whiteBox, const Internal::VertexHandlePair vertexHandlePair,
- const PolygonHandle& selectedPolygonHandle, const PolygonHandle& adjacentPolygonHandle)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto selectedPolygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, selectedPolygonHandle);
- const auto adjacentPolygonEdgeHandles = PolygonBorderEdgeHandlesFlattened(whiteBox, adjacentPolygonHandle);
- // iterate over all halfedges in the adjacent polygon
- for (const auto& edgeHandle : adjacentPolygonEdgeHandles)
- {
- const auto* const foundEdgeHandleInSelectedPolygon =
- AZStd::find(selectedPolygonEdgeHandles.cbegin(), selectedPolygonEdgeHandles.cend(), edgeHandle);
- // did not find edge handle in selected polygon
- if (foundEdgeHandleInSelectedPolygon == selectedPolygonEdgeHandles.cend())
- {
- // find outgoing edge handles
- for (const auto& halfedgeHandle :
- VertexOutgoingHalfedgeHandles(whiteBox, vertexHandlePair.m_existing))
- {
- // attempt to find one of the outgoing halfedge handles in the adjacent polygon
- if (HalfedgeEdgeHandle(whiteBox, halfedgeHandle) == edgeHandle)
- {
- return halfedgeHandle;
- }
- }
- }
- }
- return HalfedgeHandle{};
- }
- // add 'linking/connecting' faces for when an 'impression' happens
- // note: temporary measure before triangulation support is added to the white box tool
- static void AddLinkingFace(
- const WhiteBoxMesh& whiteBox, const Internal::VertexHandlePair vertexHandlePair,
- const PolygonHandle& selectedPolygonHandle, const PolygonHandle& adjacentPolygonHandle,
- FaceVertHandlesCollection& vertsForLinkingAdjacentPolygons)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // if we found a valid halfedge
- if (const HalfedgeHandle foundHalfedgeHandle = FindHalfedgeInAdjacentPolygon(
- whiteBox, vertexHandlePair, selectedPolygonHandle, adjacentPolygonHandle);
- foundHalfedgeHandle.IsValid())
- {
- // find the 'to' vertex
- const auto toVertexHandle = HalfedgeVertexHandleAtTip(whiteBox, foundHalfedgeHandle);
- // find if the face handle of the halfedge is 'in' the adjacent polygon
- const auto* const faceHandleInAdjacentPolygon = AZStd::find(
- adjacentPolygonHandle.m_faceHandles.cbegin(), adjacentPolygonHandle.m_faceHandles.cend(),
- HalfedgeFaceHandle(whiteBox, foundHalfedgeHandle));
- // adjust winding order if the outgoing halfedge is in the adjacent polygon or not
- FaceVertHandles linkingPolygonVertexHandles =
- faceHandleInAdjacentPolygon != adjacentPolygonHandle.m_faceHandles.cend()
- ? FaceVertHandles{vertexHandlePair.m_existing, toVertexHandle, vertexHandlePair.m_added}
- : FaceVertHandles{vertexHandlePair.m_existing, vertexHandlePair.m_added, toVertexHandle};
- // store verts for new polygon
- vertsForLinkingAdjacentPolygons.push_back(
- AZStd::vector<FaceVertHandles>{AZStd::move(linkingPolygonVertexHandles)});
- }
- }
- // return true if existing verts were reused and linking faces were added
- // return false if a new adjacent polygon must be created (new verts have
- // been added and will be used)
- static bool TryAddLinkingFaces(
- WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const Internal::AppendedVerts& appendedVerts,
- const PolygonHandle& selectedPolygonHandle, const Internal::VertexHandlePair& currentVertexHandlePair,
- const Internal::VertexHandlePair& nextVertexHandlePair,
- AZStd::vector<PolygonHandle>& polygonHandlesToRemove,
- FaceVertHandlesCollection& vertsForExistingAdjacentPolygons,
- FaceVertHandlesCollection& vertsForLinkingAdjacentPolygons)
- {
- // find all faces connected to this edge
- for (const auto& faceHandle : EdgeFaceHandles(whiteBox, edgeHandle))
- {
- // find a face that is _not_ part of the polygon being appended/selected
- if (AZStd::find(
- selectedPolygonHandle.m_faceHandles.cbegin(), selectedPolygonHandle.m_faceHandles.cend(),
- faceHandle) == selectedPolygonHandle.m_faceHandles.cend())
- {
- // the polygon handle of the face connected to one of the top edges
- const auto adjacentPolygonHandle = FacePolygonHandle(whiteBox, faceHandle);
- const auto selectedPolygonNormal = PolygonNormal(whiteBox, selectedPolygonHandle);
- // the normal of the adjacent (connected) polygon
- const auto adjacentPolygonNormal = PolygonNormal(whiteBox, adjacentPolygonHandle);
- const float angleCosine = adjacentPolygonNormal.Dot(selectedPolygonNormal);
- // check if the adjacent polygon is completely orthogonal to the
- // selected polygon - if so reuse the existing verts and do not
- // create a new adjacent polygon as part of the operation
- if (AZ::IsClose(angleCosine, 0.0f, AdjacentPolygonNormalTolerance))
- {
- // if the current or next vertex on the border have had a new vertex added
- if (currentVertexHandlePair.m_added != currentVertexHandlePair.m_existing ||
- nextVertexHandlePair.m_added != nextVertexHandlePair.m_existing)
- {
- // remove the existing adjacent polygon (a new one will be added
- // that is connected to the newly added verts)
- polygonHandlesToRemove.push_back(adjacentPolygonHandle);
- // calculate new verts for faces to be created
- const auto adjacentPolygonVerts =
- BuildNewVertexFaceHandles(whiteBox, appendedVerts, adjacentPolygonHandle.m_faceHandles);
- // store the face verts to be added later after existing faces have been removed
- vertsForExistingAdjacentPolygons.push_back(adjacentPolygonVerts);
- }
- // first linking face
- if (currentVertexHandlePair.m_added != currentVertexHandlePair.m_existing)
- {
- AddLinkingFace(
- whiteBox, currentVertexHandlePair, selectedPolygonHandle, adjacentPolygonHandle,
- vertsForLinkingAdjacentPolygons);
- }
- // second linking face
- if (nextVertexHandlePair.m_added != nextVertexHandlePair.m_existing)
- {
- AddLinkingFace(
- whiteBox, nextVertexHandlePair, selectedPolygonHandle, adjacentPolygonHandle,
- vertsForLinkingAdjacentPolygons);
- }
- return true;
- }
- }
- }
- return false;
- }
- // build the adjacent walls of an extrusion
- // note: borderVertexHandles must be ordered correctly (CCW)
- static void AddAdjacentFaces(
- WhiteBoxMesh& whiteBox, const Internal::AppendedVerts& appendedVerts, const bool appendAll,
- const PolygonHandle& selectedPolygonHandle, const VertexHandles& borderVertexHandles,
- const EdgeHandles& borderEdgeHandles, AZStd::vector<PolygonHandle>& polygonHandlesToRemove,
- FaceVertHandlesCollection& vertsForNewAdjacentPolygons,
- FaceVertHandlesCollection& vertsForExistingAdjacentPolygons,
- FaceVertHandlesCollection& vertsForLinkingAdjacentPolygons)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // adjacent faces
- for (size_t index = 0; index < borderVertexHandles.size(); ++index)
- {
- const size_t nextIndexWrapped = (index + 1) % borderVertexHandles.size();
- const VertexHandle existingBorderVertexHandle = borderVertexHandles[index];
- const VertexHandle nextExistingBorderVertexHandle = borderVertexHandles[nextIndexWrapped];
- const auto* const currentVertexHandlePairIt = AZStd::find_if(
- appendedVerts.m_vertexHandlePairs.cbegin(), appendedVerts.m_vertexHandlePairs.cend(),
- [existingBorderVertexHandle](const Internal::VertexHandlePair vertexHandlePair)
- {
- return vertexHandlePair.m_existing == existingBorderVertexHandle;
- });
- const auto* const nextVertexHandlePairIt = AZStd::find_if(
- appendedVerts.m_vertexHandlePairs.cbegin(), appendedVerts.m_vertexHandlePairs.cend(),
- [nextExistingBorderVertexHandle](const Internal::VertexHandlePair vertexHandlePair)
- {
- return vertexHandlePair.m_existing == nextExistingBorderVertexHandle;
- });
- // find the edge on the border of the polygon we're appending
- const auto* const edgeHandleIt = AZStd::find_if(
- borderEdgeHandles.cbegin(), borderEdgeHandles.cend(),
- [&whiteBox, existingBorderVertexHandle, nextExistingBorderVertexHandle](const EdgeHandle edgeHandle)
- {
- const auto edgeVertexHandles = EdgeVertexHandles(whiteBox, edgeHandle);
- return (existingBorderVertexHandle == edgeVertexHandles[0] &&
- nextExistingBorderVertexHandle == edgeVertexHandles[1]) ||
- (existingBorderVertexHandle == edgeVertexHandles[1] &&
- nextExistingBorderVertexHandle == edgeVertexHandles[0]);
- });
- // does a new side need to be created (new verts added) or should we reuse existing verts
- const bool createNewAdjacentPolygon = appendAll
- // short circuit if appendAll is true (no linking faces are required)
- || !TryAddLinkingFaces(whiteBox, *edgeHandleIt, appendedVerts, selectedPolygonHandle,
- *currentVertexHandlePairIt, *nextVertexHandlePairIt, polygonHandlesToRemove,
- vertsForExistingAdjacentPolygons, vertsForLinkingAdjacentPolygons);
- // a new full side must be created (we're not reusing existing verts for the new polygon)
- if (createNewAdjacentPolygon)
- {
- vertsForNewAdjacentPolygons.push_back(AZStd::vector<FaceVertHandles>{
- {existingBorderVertexHandle, nextExistingBorderVertexHandle, nextVertexHandlePairIt->m_added},
- {existingBorderVertexHandle, nextVertexHandlePairIt->m_added,
- currentVertexHandlePairIt->m_added}});
- }
- }
- }
- // note: it is important to collect all face handles to remove and call RemoveFaces
- // once for a given operation (for example to do not call RemoveFaces in a loop,
- // instead build the collection of face handles in a loop and then call RemoveFaces)
- // this is to ensure the face handles remain stable as they may be invalidated/changed
- // during garbage_collect
- void RemoveFaces(WhiteBoxMesh& whiteBox, const FaceHandles& faceHandles)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- whiteBox.mesh.request_face_status();
- whiteBox.mesh.request_edge_status();
- whiteBox.mesh.request_vertex_status();
- PolygonPropertyHandle polygonPropsHandle;
- whiteBox.mesh.get_property_handle(polygonPropsHandle, PolygonProps);
- auto& polygonProps = whiteBox.mesh.property(polygonPropsHandle);
- // erase face handles from the polygon map and
- // delete the faces from OpenMesh
- for (const auto& faceHandle : faceHandles)
- {
- polygonProps.erase(om_fh(faceHandle));
- whiteBox.mesh.delete_face(om_fh(faceHandle), false);
- }
- using VertexHandlePtrs = AZStd::vector<Mesh::VertexHandle*>;
- using FaceHandlePtrs = AZStd::vector<Mesh::FaceHandle*>;
- using HalfedgeHandlePtrs = AZStd::vector<Mesh::HalfedgeHandle*>;
- // store pointers to all face handles stored within the map (the values - e.g. it.second)
- FaceHandlePtrs faceHandlePtrs;
- for (auto& polygonProp : polygonProps)
- {
- faceHandlePtrs = AZStd::accumulate(
- polygonProp.second.begin(), polygonProp.second.end(), faceHandlePtrs,
- [](FaceHandlePtrs ptrs, Mesh::FaceHandle& faceHandle)
- {
- ptrs.push_back(&faceHandle);
- return ptrs;
- });
- }
- // make a copy of the face handles to compare against after the garbage_collect
- FaceHandlesInternal faceHandlesCopy;
- faceHandlesCopy.reserve(faceHandlePtrs.size());
- AZStd::transform(
- faceHandlePtrs.begin(), faceHandlePtrs.end(), AZStd::back_inserter(faceHandlesCopy),
- [](const Mesh::FaceHandle* faceHandle)
- {
- return *faceHandle;
- });
- // actually delete faces from mesh
- auto vhs = VertexHandlePtrs{};
- auto hehs = HalfedgeHandlePtrs{};
- whiteBox.mesh.garbage_collection(vhs, hehs, faceHandlePtrs);
- using ModifiedFaceHandle = AZStd::pair<Mesh::FaceHandle, Mesh::FaceHandle>;
- using ModifiedFaceHandles = AZStd::vector<ModifiedFaceHandle>;
- const ModifiedFaceHandles modifiedFaceHandles = AZStd::inner_product(
- faceHandlesCopy.begin(), faceHandlesCopy.end(), faceHandlePtrs.begin(), ModifiedFaceHandles{},
- //reduce
- [](ModifiedFaceHandles modifiedFaceHandles, const ModifiedFaceHandle& fh)
- {
- if (fh.first.is_valid())
- {
- modifiedFaceHandles.push_back(fh);
- }
- return modifiedFaceHandles;
- },
- //transform
- [](const Mesh::FaceHandle lhs, const Mesh::FaceHandle* rhs)
- {
- // if any of the faceHandlePtrs differ, we know the handles
- // have been invalidated during the garbage collect
- if (lhs != *rhs)
- {
- return ModifiedFaceHandle{lhs, *rhs};
- }
- return ModifiedFaceHandle{};
- });
- // iterate over all modified face handles
- for (const ModifiedFaceHandle& modifiedFaceHandle : modifiedFaceHandles)
- {
- // find the value in the map using the old key
- // e.g. faceHandle 10 -> polygon was 10, 11 -> now 4, 5
- auto found = polygonProps.find(modifiedFaceHandle.first);
- if (found != polygonProps.end())
- {
- // copy the updated faceHandle value (e.g was 10, now 4)
- auto faceHandle = modifiedFaceHandle.second;
- // pull the values out of the map (e.g. 4, 5) - make a copy
- auto val = found->second;
- // erase the old key/value -> key 10, value 4, 5
- polygonProps.erase(found);
- // reinsert the values back into the map with the right key (key 4, value 4, 5)
- polygonProps.insert({faceHandle, val});
- }
- }
- }
- AZStd::vector<FaceVertHandles> BuildNewVertexFaceHandles(
- WhiteBoxMesh& whiteBox, const Internal::AppendedVerts& appendedVerts, const FaceHandles& existingFaces)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- AZStd::vector<FaceVertHandles> faces;
- faces.reserve(existingFaces.size());
- // for each face
- for (const FaceHandle& faceHandle : existingFaces)
- {
- VertexHandles vertexHandlesForFace;
- vertexHandlesForFace.reserve(3);
- const auto vertexHandles = FaceVertexHandles(whiteBox, faceHandle);
- // for each vertex handle
- for (const VertexHandle& vertexHandle : vertexHandles)
- {
- // find vertex handle in vertices list
- const auto* const vertexHandlePairIt = AZStd::find_if(
- appendedVerts.m_vertexHandlePairs.cbegin(), appendedVerts.m_vertexHandlePairs.cend(),
- [vertexHandle](const Internal::VertexHandlePair vertexHandlePair)
- {
- return vertexHandle == vertexHandlePair.m_existing;
- });
- // record corresponding (added) vertex
- if (vertexHandlePairIt != appendedVerts.m_vertexHandlePairs.cend())
- {
- // store vertex
- vertexHandlesForFace.push_back(vertexHandlePairIt->m_added);
- }
- // or existing vertex if one was not added
- else
- {
- vertexHandlesForFace.push_back(vertexHandle);
- }
- }
- // add face using vertex stored vertex
- FaceVertHandles face;
- face.m_vertexHandles[0] = vertexHandlesForFace[0];
- face.m_vertexHandles[1] = vertexHandlesForFace[1];
- face.m_vertexHandles[2] = vertexHandlesForFace[2];
- faces.push_back(face);
- }
- return faces;
- }
- // determine whether new or existing verts be returned based on the type of
- // append (extrude -> out, impression -> in)
- template<typename AppendVertFn>
- static AZStd::tuple<Internal::AppendedVerts, bool> AddVertsForAppend(
- WhiteBoxMesh& whiteBox, const VertexHandles& existingVertexHandles, const PolygonHandle& polygonHandle,
- AppendVertFn&& appendFn)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const AZ::Vector3 polygonNormal = PolygonNormal(whiteBox, polygonHandle);
- const auto polygonHalfedgeHandles = PolygonHalfedgeHandles(whiteBox, polygonHandle);
- const AZ::Vector3 extrudeDirection = appendFn(AZ::Vector3::CreateZero());
- const float angleCosine = extrudeDirection.Dot(polygonNormal);
- // detect if the user is extruding the polygon (pulling out) - if so we
- // always want to append new vertices for every existing vertex
- const bool appendAll = angleCosine >= 0.0f;
- Internal::AppendedVerts appendedVerts;
- appendedVerts.m_vertexHandlePairs.reserve(existingVertexHandles.size());
- for (const VertexHandle& existingVertexHandle : existingVertexHandles)
- {
- bool vertexHandleAdded = false;
- // visit all connected halfedge handles
- for (const auto& halfedgeHandle : VertexHalfedgeHandles(whiteBox, existingVertexHandle))
- {
- const auto edgeHandle = HalfedgeEdgeHandle(whiteBox, halfedgeHandle);
- const bool boundaryEdge = EdgeIsBoundary(whiteBox, edgeHandle);
- // is the edge not contained in selected polygon (we want to only check adjacent polygons)
- // or is the edge on a boundary (no adjacent face)
- if (boundaryEdge ||
- AZStd::find(polygonHalfedgeHandles.cbegin(), polygonHalfedgeHandles.cend(), halfedgeHandle) ==
- polygonHalfedgeHandles.cend())
- {
- const auto nextHalfedgeHandle = HalfedgeHandleNext(whiteBox, halfedgeHandle);
- const auto nextEdgeHandle = HalfedgeEdgeHandle(whiteBox, nextHalfedgeHandle);
- const AZ::Vector3 edgeAxis = EdgeAxis(whiteBox, edgeHandle);
- const AZ::Vector3 nextEdgeAxis = EdgeAxis(whiteBox, nextEdgeHandle);
- // calculate face normal from two edges
- const AZ::Vector3 faceNormal = edgeAxis.Cross(nextEdgeAxis).GetNormalizedSafe();
- const bool adjacentFaceAndPolygonNormalOrthogonal = AZ::IsClose(
- AZ::GetAbs(faceNormal.Dot(polygonNormal)), 0.0f, AdjacentPolygonNormalTolerance);
- // if the polygon normal and edge direction are not parallel, we should
- // add a new vertex for the polygon to be later created
- if (appendAll || boundaryEdge || !adjacentFaceAndPolygonNormalOrthogonal)
- {
- vertexHandleAdded = true;
- const AZ::Vector3 localPoint = VertexPosition(whiteBox, existingVertexHandle);
- const AZ::Vector3 extrudedPoint = appendFn(localPoint);
- const VertexHandle addedVertex = AddVertex(whiteBox, extrudedPoint);
- // vertex pairs differ
- appendedVerts.m_vertexHandlePairs.emplace_back(existingVertexHandle, addedVertex);
- break;
- }
- }
- }
- if (!vertexHandleAdded)
- {
- const AZ::Vector3 localPoint = VertexPosition(whiteBox, existingVertexHandle);
- const AZ::Vector3 extrudedPoint = appendFn(localPoint);
- SetVertexPosition(whiteBox, existingVertexHandle, extrudedPoint);
- // vertex pairs match
- appendedVerts.m_vertexHandlePairs.emplace_back(existingVertexHandle, existingVertexHandle);
- }
- }
- return {appendedVerts, appendAll};
- }
- //! @AppendVertexFn The way vertices should be translated as an append happens
- //! note: This is a customization point for scale and translation types of append
- template<typename AppendVertexFn>
- static AppendedPolygonHandles Extrude(
- WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, AppendVertexFn&& appendFn)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // find border vertex handles for polygon
- const auto polygonBorderVertexHandlesCollection = PolygonBorderVertexHandles(whiteBox, polygonHandle);
- // handle potentially pathological case where all edges have
- // been hidden and no border vertex loop can be found
- if (polygonBorderVertexHandlesCollection.empty())
- {
- AppendedPolygonHandles appendedPolygonHandles;
- appendedPolygonHandles.m_appendedPolygonHandle = polygonHandle;
- return appendedPolygonHandles;
- }
- // find all vertex handles for polygon
- const auto polygonVertexHandles = PolygonVertexHandles(whiteBox, polygonHandle);
- const auto borderEdgeHandlesCollection = PolygonBorderEdgeHandles(whiteBox, polygonHandle);
- // the vertices to use for the new append (vertex handle pairs may both be existing, or new and existing)
- const auto [appendedVerts, appendAll] =
- AddVertsForAppend(whiteBox, polygonVertexHandles, polygonHandle, appendFn);
- // the face vertex combinations to use for the new polygon begin appended
- const AZStd::vector<FaceVertHandles> topFacesToAdd =
- BuildNewVertexFaceHandles(whiteBox, appendedVerts, polygonHandle.m_faceHandles);
- // polygons that will be removed as part of this operation
- AZStd::vector<PolygonHandle> polygonHandlesToRemove;
- // all new faces to be added
- FaceVertHandlesCollection vertsForNewAdjacentPolygons;
- FaceVertHandlesCollection vertsForExistingAdjacentPolygons;
- FaceVertHandlesCollection vertsForLinkingAdjacentPolygons;
- for (size_t index = 0; index < polygonBorderVertexHandlesCollection.size(); ++index)
- {
- // note: quads/walls of extrusion
- AddAdjacentFaces(
- whiteBox, appendedVerts, appendAll, polygonHandle, polygonBorderVertexHandlesCollection[index],
- borderEdgeHandlesCollection[index], polygonHandlesToRemove, vertsForNewAdjacentPolygons,
- vertsForExistingAdjacentPolygons, vertsForLinkingAdjacentPolygons);
- }
- // <missing> - add bottom faces if mesh was 2d previously (reverse winding order)
- FaceHandles allFacesToRemove = polygonHandle.m_faceHandles;
- for (const auto& polygonHandleToRemove : polygonHandlesToRemove)
- {
- allFacesToRemove.insert(
- allFacesToRemove.end(), polygonHandleToRemove.m_faceHandles.cbegin(), polygonHandleToRemove.m_faceHandles.cend());
- }
- // remove all faces that were already there
- // note: it is very important to not use the existing polygon handle after remove
- // has been called as this will invalidate all existing face handles
- RemoveFaces(whiteBox, allFacesToRemove);
- PolygonHandles polygonHandlesToRestore;
- // re-add existing adjacent polygons
- for (const auto& verts : vertsForExistingAdjacentPolygons)
- {
- polygonHandlesToRestore.push_back(AddPolygon(whiteBox, verts));
- }
- AZ_Assert(
- polygonHandlesToRestore.size() == polygonHandlesToRemove.size(),
- "PolygonHandles to restore and PolygonHandles to remove have different sizes");
- AppendedPolygonHandles appendedPolygonHandles;
- appendedPolygonHandles.m_restoredPolygonHandles.reserve(polygonHandlesToRestore.size());
- for (size_t index = 0; index < polygonHandlesToRestore.size(); ++index)
- {
- RestoredPolygonHandlePair restoredPair;
- restoredPair.m_before = polygonHandlesToRemove[index];
- restoredPair.m_after = polygonHandlesToRestore[index];
- appendedPolygonHandles.m_restoredPolygonHandles.push_back(AZStd::move(restoredPair));
- }
- // add linking polygons
- for (const auto& verts : vertsForLinkingAdjacentPolygons)
- {
- AddPolygon(whiteBox, verts);
- }
- // add top polygon
- PolygonHandle newPolygonHandle = AddPolygon(whiteBox, topFacesToAdd);
- // add new adjacent polygons
- for (const auto& verts : vertsForNewAdjacentPolygons)
- {
- AddPolygon(whiteBox, verts);
- }
- whiteBox.mesh.update_normals();
- appendedPolygonHandles.m_appendedPolygonHandle = newPolygonHandle;
- return appendedPolygonHandles;
- }
- using AppendFn = AZStd::function<AZ::Vector3(const AZ::Vector3&)>;
- static AppendFn TranslatePoint(const AZ::Vector3& direction, const float distance)
- {
- return [direction, distance](const AZ::Vector3& point)
- {
- return point + (direction * distance);
- };
- }
- PolygonHandle TranslatePolygonAppend(
- WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float distance)
- {
- WHITEBOX_LOG("White Box", "TranslatePolygonAppend ph(%s) %f", ToString(polygonHandle).c_str(), distance)
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- return TranslatePolygonAppendAdvanced(whiteBox, polygonHandle, distance).m_appendedPolygonHandle;
- }
- AppendedPolygonHandles TranslatePolygonAppendAdvanced(
- WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float distance)
- {
- WHITEBOX_LOG(
- "White Box", "TranslatePolygonAppendAdvanced ph(%s) %f", ToString(polygonHandle).c_str(), distance)
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // check mesh has faces
- if (whiteBox.mesh.n_faces() == 0)
- {
- return {};
- }
- auto appendedPolygonHandles =
- Extrude(whiteBox, polygonHandle, TranslatePoint(PolygonNormal(whiteBox, polygonHandle), distance));
- return appendedPolygonHandles;
- }
- void TranslatePolygon(WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float distance)
- {
- WHITEBOX_LOG("White Box", "TranslatePolygon ph(%s) %d", ToString(polygonHandle).c_str(), distance)
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const auto vertexHandles = PolygonVertexHandles(whiteBox, polygonHandle);
- const auto vertexPositions = VertexPositions(whiteBox, vertexHandles);
- const auto normal = PolygonNormal(whiteBox, polygonHandle);
- for (size_t index = 0; index < vertexHandles.size(); ++index)
- {
- SetVertexPosition(whiteBox, vertexHandles[index], vertexPositions[index] + normal * distance);
- }
- CalculatePlanarUVs(whiteBox);
- }
- PolygonHandle ScalePolygonAppendRelative(
- WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const float scale)
- {
- WHITEBOX_LOG("White Box", "ScalePolygonAppendRelative ph(%s) %f", ToString(polygonHandle).c_str(), scale);
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- // check mesh has faces
- if (whiteBox.mesh.n_faces() == 0)
- {
- return {};
- }
- const AZ::Transform polygonSpace =
- PolygonSpace(whiteBox, polygonHandle, PolygonMidpoint(whiteBox, polygonHandle));
- const auto scalePolygonFn = [polygonSpace, scale](const AZ::Vector3& localPosition)
- {
- return ScalePosition(1.0f + scale, localPosition, polygonSpace);
- };
- auto appendedPolygonHandles = Extrude(whiteBox, polygonHandle, scalePolygonFn);
- return appendedPolygonHandles.m_appendedPolygonHandle;
- }
- static AZ::Transform BuildSpace(const AZ::Vector3& normal, const AZ::Vector3& pivot)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- AZ::Vector3 axis1;
- AZ::Vector3 axis2;
- CalculateOrthonormalBasis(normal, axis1, axis2);
- AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromColumns(axis1, axis2, normal, pivot);
- return AZ::Transform::CreateFromMatrix3x4(matrix);
- }
- AZ::Transform PolygonSpace(
- const WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const AZ::Vector3& pivot)
- {
- return BuildSpace(PolygonNormal(whiteBox, polygonHandle), pivot);
- }
- AZ::Transform EdgeSpace(const WhiteBoxMesh& whiteBox, const EdgeHandle edgeHandle, const AZ::Vector3& pivot)
- {
- const auto vertexPositions = EdgeVertexPositions(whiteBox, edgeHandle);
- return BuildSpace((vertexPositions[1] - vertexPositions[0]).GetNormalizedSafe(), pivot);
- }
- void ScalePolygonRelative(
- WhiteBoxMesh& whiteBox, const PolygonHandle& polygonHandle, const AZ::Vector3& pivot,
- const float scaleDelta)
- {
- WHITEBOX_LOG(
- "White Box", "ScalePolygonRelative ph(%s) pivot %s scale: %f", ToString(polygonHandle).c_str(),
- AZStd::to_string(pivot).c_str(), scaleDelta);
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- const AZ::Transform polygonSpace = PolygonSpace(whiteBox, polygonHandle, pivot);
- for (const auto& vertexHandle : PolygonVertexHandles(whiteBox, polygonHandle))
- {
- SetVertexPosition(
- whiteBox, vertexHandle,
- ScalePosition(1.0f + scaleDelta, VertexPosition(whiteBox, vertexHandle), polygonSpace));
- }
- CalculateNormals(whiteBox);
- CalculatePlanarUVs(whiteBox);
- }
- bool WriteMesh(const WhiteBoxMesh& whiteBox, WhiteBoxMeshStream& output)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- AZStd::lock_guard lg(g_omSerializationLock);
- std::stringstream whiteBoxStream;
- if (OpenMesh::IO::write_mesh(
- whiteBox.mesh, whiteBoxStream, ".om",
- OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::FaceTexCoord |
- OpenMesh::IO::Options::FaceNormal))
- {
- const std::string outputStr = whiteBoxStream.str();
- output.clear();
- output.reserve(outputStr.size());
- AZStd::copy(outputStr.cbegin(), outputStr.cend(), AZStd::back_inserter(output));
- return true;
- }
- // handle error
- return false;
- }
- ReadResult ReadMesh(WhiteBoxMesh& whiteBox, const WhiteBoxMeshStream& input)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- if (input.empty())
- {
- return ReadResult::Empty;
- }
- std::string inputStr;
- inputStr.reserve(input.size());
- AZStd::copy(input.cbegin(), input.cend(), AZStd::back_inserter(inputStr));
- std::stringstream whiteBoxStream;
- whiteBoxStream.str(inputStr);
- whiteBoxStream >> std::noskipws;
- return ReadMesh(whiteBox, whiteBoxStream);
- }
- ReadResult ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input)
- {
- const auto skipws = input.flags() & std::ios_base::skipws;
- AZ_Assert(skipws == 0, "Input stream must not skip white space characters");
- if (skipws != 0)
- {
- return ReadResult::Error;
- }
- AZStd::lock_guard lg(g_omSerializationLock);
- OpenMesh::IO::Options options{OpenMesh::IO::Options::FaceTexCoord | OpenMesh::IO::Options::FaceNormal};
- return OpenMesh::IO::read_mesh(whiteBox.mesh, input, ".om", options) ? ReadResult::Full : ReadResult::Error;
- }
- WhiteBoxMeshPtr CloneMesh(const WhiteBoxMesh& whiteBox)
- {
- AZ_PROFILE_FUNCTION(AzToolsFramework);
- WhiteBoxMeshStream clonedData;
- if (!WriteMesh(whiteBox, clonedData))
- {
- return nullptr;
- }
- WhiteBoxMeshPtr newMesh = CreateWhiteBoxMesh();
- if (ReadMesh(*newMesh, clonedData) != ReadResult::Full)
- {
- return nullptr;
- }
- return newMesh;
- }
- bool SaveToObj(const WhiteBoxMesh& whiteBox, const AZStd::string& filePath)
- {
- OpenMesh::IO::Options options{OpenMesh::IO::Options::FaceTexCoord};
- OpenMesh::IO::ExporterT<WhiteBox::Mesh> exporter{whiteBox.mesh};
- return OpenMesh::IO::OBJWriter().write(filePath.c_str(), exporter, options);
- }
- bool SaveToWbm(const WhiteBoxMesh& whiteBox, AZ::IO::GenericStream& stream)
- {
- WhiteBoxMeshStream buffer;
- const bool success = WhiteBox::Api::WriteMesh(whiteBox, buffer);
- const auto bytesWritten = stream.Write(buffer.size(), buffer.data());
- return success && bytesWritten == buffer.size();
- }
- bool SaveToWbm(const WhiteBoxMesh& whiteBox, const AZStd::string& filePath)
- {
- AZ::IO::FileIOStream fileStream(filePath.c_str(), AZ::IO::OpenMode::ModeWrite);
- if (!fileStream.IsOpen())
- {
- return false;
- }
- return SaveToWbm(whiteBox, fileStream);
- }
- static AZStd::string TrimLastChar(const AZStd::string& str)
- {
- if (str.empty())
- {
- return str;
- }
- return str.substr(0, str.length() - 1);
- }
- AZStd::string ToString(const PolygonHandle& polygonHandle)
- {
- AZStd::string str = "";
- for (auto faceHandle : polygonHandle.m_faceHandles)
- {
- str.append(ToString(faceHandle)).append(",");
- }
- return TrimLastChar(str);
- }
- AZStd::string ToString(const FaceVertHandles& faceVertHandles)
- {
- AZStd::string str = "";
- for (auto vertexHandle : faceVertHandles.m_vertexHandles)
- {
- str.append(ToString(vertexHandle)).append(",");
- }
- return TrimLastChar(str);
- }
- AZStd::string ToString(const FaceVertHandlesList& faceVertHandlesList)
- {
- AZStd::string str = "";
- for (auto faceVertHandles : faceVertHandlesList)
- {
- str.append("fvh(").append(ToString(faceVertHandles)).append("),");
- }
- return TrimLastChar(str);
- }
- AZStd::string ToString(const VertexHandle vertexHandle)
- {
- return AZStd::to_string(vertexHandle.Index());
- }
- AZStd::string ToString(const FaceHandle faceHandle)
- {
- return AZStd::to_string(faceHandle.Index());
- }
- AZStd::string ToString(const EdgeHandle edgeHandle)
- {
- return AZStd::to_string(edgeHandle.Index());
- }
- AZStd::string ToString(const HalfedgeHandle halfedgeHandle)
- {
- return AZStd::to_string(halfedgeHandle.Index());
- }
- } // namespace Api
- } // namespace WhiteBox
|