12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
- // Copyright (C) 2015 Faust Logic, Inc.
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- #include "platform/platform.h"
- #include "environment/meshRoad.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "util/catmullRom.h"
- #include "math/util/quadTransforms.h"
- #include "scene/simPath.h"
- #include "scene/sceneRenderState.h"
- #include "scene/sceneManager.h"
- #include "scene/sgUtil.h"
- #include "renderInstance/renderPassManager.h"
- #include "T3D/gameBase/gameConnection.h"
- #include "core/stream/bitStream.h"
- #include "gfx/gfxDrawUtil.h"
- #include "gfx/gfxTransformSaver.h"
- #include "gfx/primBuilder.h"
- #include "gfx/gfxDebugEvent.h"
- #include "materials/materialManager.h"
- #include "math/mathIO.h"
- #include "math/mathUtils.h"
- #include "math/util/frustum.h"
- #include "gui/3d/guiTSControl.h"
- #include "materials/shaderData.h"
- #include "gfx/sim/gfxStateBlockData.h"
- #include "gfx/sim/debugDraw.h"
- #include "collision/concretePolyList.h"
- #include "T3D/physics/physicsPlugin.h"
- #include "T3D/physics/physicsBody.h"
- #include "T3D/physics/physicsCollision.h"
- #include "environment/nodeListManager.h"
- #ifdef TORQUE_AFX_ENABLED
- #include "afx/ce/afxZodiacMgr.h"
- #endif
- #define MIN_METERS_PER_SEGMENT 1.0f
- #define MIN_NODE_DEPTH 0.25f
- #define MAX_NODE_DEPTH 50.0f
- #define MIN_NODE_WIDTH 0.25f
- #define MAX_NODE_WIDTH 50.0f
- static U32 gIdxArray[6][2][3] = {
- { { 0, 4, 5 }, { 0, 5, 1 }, }, // Top Face
- { { 2, 6, 4 }, { 2, 4, 0 }, }, // Left Face
- { { 1, 5, 7 }, { 1, 7, 3 }, }, // Right Face
- { { 2, 3, 7 }, { 2, 7, 6 }, }, // Bottom Face
- { { 0, 1, 3 }, { 0, 3, 2 }, }, // Front Face
- { { 4, 6, 7 }, { 4, 7, 5 }, }, // Back Face
- };
- static S32 QSORT_CALLBACK compareHitSegments(const void* a,const void* b)
- {
- const MeshRoadHitSegment *fa = (MeshRoadHitSegment*)a;
- const MeshRoadHitSegment *fb = (MeshRoadHitSegment*)b;
- F32 diff = fb->t - fa->t;
- return (diff > 0) ? 1 : (diff < 0) ? -1 : 0;
- }
- //-----------------------------------------------------------------------------
- // MeshRoadNodeList Struct
- //-----------------------------------------------------------------------------
- struct MeshRoadNodeList : public NodeListManager::NodeList
- {
- Vector<Point3F> mPositions;
- Vector<F32> mWidths;
- Vector<F32> mDepths;
- Vector<VectorF> mNormals;
- MeshRoadNodeList() { }
- virtual ~MeshRoadNodeList() { }
- };
- //-----------------------------------------------------------------------------
- // MeshRoadNodeEvent Class
- //-----------------------------------------------------------------------------
- class MeshRoadNodeEvent : public NodeListEvent
- {
- typedef NodeListEvent Parent;
- public:
- Vector<Point3F> mPositions;
- Vector<F32> mWidths;
- Vector<F32> mDepths;
- Vector<VectorF> mNormals;
- public:
- MeshRoadNodeEvent() { mNodeList = NULL; }
- virtual ~MeshRoadNodeEvent() { }
- virtual void pack(NetConnection*, BitStream*);
- virtual void unpack(NetConnection*, BitStream*);
- virtual void copyIntoList(NodeListManager::NodeList* copyInto);
- virtual void padListToSize();
- DECLARE_CONOBJECT(MeshRoadNodeEvent);
- };
- void MeshRoadNodeEvent::pack(NetConnection* conn, BitStream* stream)
- {
- Parent::pack( conn, stream );
- stream->writeInt( mPositions.size(), 16 );
- for (U32 i=0; i<mPositions.size(); ++i)
- {
- mathWrite( *stream, mPositions[i] );
- stream->write( mWidths[i] );
- stream->write( mDepths[i] );
- mathWrite( *stream, mNormals[i] );
- }
- }
- void MeshRoadNodeEvent::unpack(NetConnection* conn, BitStream* stream)
- {
- mNodeList = new MeshRoadNodeList();
- Parent::unpack( conn, stream );
- U32 count = stream->readInt( 16 );
- Point3F pos;
- F32 width, depth;
- VectorF normal;
- MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
- for (U32 i=0; i<count; ++i)
- {
- mathRead( *stream, &pos );
- stream->read( &width );
- stream->read( &depth );
- mathRead( *stream, &normal );
- list->mPositions.push_back( pos );
- list->mWidths.push_back( width );
- list->mDepths.push_back( depth );
- list->mNormals.push_back( normal );
- }
- list->mTotalValidNodes = count;
- // Do we have a complete list?
- if (list->mPositions.size() >= mTotalNodes)
- list->mListComplete = true;
- }
- void MeshRoadNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto)
- {
- MeshRoadNodeList* prevList = dynamic_cast<MeshRoadNodeList*>(copyInto);
- MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
- // Merge our list with the old list.
- for (U32 i=mLocalListStart, index=0; i<mLocalListStart+list->mPositions.size(); ++i, ++index)
- {
- prevList->mPositions[i] = list->mPositions[index];
- prevList->mWidths[i] = list->mWidths[index];
- prevList->mDepths[i] = list->mDepths[index];
- prevList->mNormals[i] = list->mNormals[index];
- }
- }
- void MeshRoadNodeEvent::padListToSize()
- {
- MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
- U32 totalValidNodes = list->mTotalValidNodes;
- // Pad our list front?
- if (mLocalListStart)
- {
- MeshRoadNodeList* newlist = new MeshRoadNodeList();
- newlist->mPositions.increment(mLocalListStart);
- newlist->mWidths.increment(mLocalListStart);
- newlist->mDepths.increment(mLocalListStart);
- newlist->mNormals.increment(mLocalListStart);
- newlist->mPositions.merge(list->mPositions);
- newlist->mWidths.merge(list->mWidths);
- newlist->mDepths.merge(list->mDepths);
- newlist->mNormals.merge(list->mNormals);
- delete list;
- mNodeList = list = newlist;
- }
- // Pad our list end?
- if (list->mPositions.size() < mTotalNodes)
- {
- U32 delta = mTotalNodes - list->mPositions.size();
- list->mPositions.increment(delta);
- list->mWidths.increment(delta);
- list->mDepths.increment(delta);
- list->mNormals.increment(delta);
- }
- list->mTotalValidNodes = totalValidNodes;
- }
- IMPLEMENT_CO_NETEVENT_V1(MeshRoadNodeEvent);
- ConsoleDocClass( MeshRoadNodeEvent,
- "@brief Sends messages to the Mesh Road Editor\n\n"
- "Editor use only.\n\n"
- "@internal"
- );
- //-----------------------------------------------------------------------------
- // MeshRoadNodeListNotify Class
- //-----------------------------------------------------------------------------
- class MeshRoadNodeListNotify : public NodeListNotify
- {
- typedef NodeListNotify Parent;
- protected:
- SimObjectPtr<MeshRoad> mRoad;
- public:
- MeshRoadNodeListNotify( MeshRoad* road, U32 listId ) { mRoad = road; mListId = listId; }
- virtual ~MeshRoadNodeListNotify() { mRoad = NULL; }
- virtual void sendNotification( NodeListManager::NodeList* list );
- };
- void MeshRoadNodeListNotify::sendNotification( NodeListManager::NodeList* list )
- {
- if (mRoad.isValid())
- {
- // Build the road's nodes
- MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list );
- if (roadList)
- mRoad->buildNodesFromList( roadList );
- }
- }
- //-------------------------------------------------------------------------
- // MeshRoadProfile Class
- //-------------------------------------------------------------------------
- MeshRoadProfile::MeshRoadProfile()
- {
- mRoad = NULL;
- // Set transformation matrix to identity
- mObjToSlice.identity();
- mSliceToObj.identity();
- }
- S32 MeshRoadProfile::clickOnLine(Point3F &p)
- {
- Point3F newProfilePt;
- Point3F ptOnSegment;
- F32 dist = 0.0f;
- F32 minDist = 99999.0f;
- U32 idx = 0;
- for(U32 i=0; i < mNodes.size()-1; i++)
- {
- ptOnSegment = MathUtils::mClosestPointOnSegment(mNodes[i].getPosition(), mNodes[i+1].getPosition(), p);
- dist = (p - ptOnSegment).len();
- if(dist < minDist)
- {
- minDist = dist;
- newProfilePt = ptOnSegment;
- idx = i+1;
- }
- }
- if(minDist <= 0.1f)
- {
- p.set(newProfilePt.x, newProfilePt.y, newProfilePt.z);
- return idx;
- }
- return -1;
- }
- void MeshRoadProfile::addPoint(U32 nodeId, Point3F &p)
- {
- if(nodeId < mNodes.size() && nodeId != 0)
- {
- p.z = 0.0f;
- mNodes.insert(nodeId, p);
- mSegMtrls.insert(nodeId-1, mSegMtrls[nodeId-1]);
- mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
- generateNormals();
- }
- }
- void MeshRoadProfile::removePoint(U32 nodeId)
- {
- if(nodeId > 0 && nodeId < mNodes.size()-1)
- {
- mNodes.erase(nodeId);
- mSegMtrls.remove(nodeId-1);
- mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
- generateNormals();
- }
- }
- void MeshRoadProfile::setNodePosition(U32 nodeId, Point3F pos)
- {
- if(nodeId < mNodes.size())
- {
- mNodes[nodeId].setPosition(pos.x, pos.y);
- mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
- generateNormals();
- }
- }
- void MeshRoadProfile::toggleSmoothing(U32 nodeId)
- {
- if(nodeId > 0 && nodeId < mNodes.size()-1)
- {
- mNodes[nodeId].setSmoothing(!mNodes[nodeId].isSmooth());
- mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
- generateNormals();
- }
- }
- void MeshRoadProfile::toggleSegMtrl(U32 seg)
- {
- if(seg < mSegMtrls.size())
- {
- switch(mSegMtrls[seg])
- {
- case MeshRoad::Side: mSegMtrls[seg] = MeshRoad::Top; break;
- case MeshRoad::Top: mSegMtrls[seg] = MeshRoad::Bottom; break;
- case MeshRoad::Bottom: mSegMtrls[seg] = MeshRoad::Side; break;
- }
- mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
- }
- }
- void MeshRoadProfile::generateNormals()
- {
- VectorF t, b, n, t2, n2;
- Point3F averagePt;
- mNodeNormals.clear();
- // Loop through all profile line segments
- for(U32 i=0; i < mNodes.size()-1; i++)
- {
- // Calculate normal for each node in line segment
- for(U32 j=0; j<2; j++)
- {
- // Smoothed Node: Average the node with nodes before and after.
- // Direction between the node and the average is the smoothed normal.
- if( mNodes[i+j].isSmooth() )
- {
- b = Point3F(0.0f, 0.0f, 1.0f);
- t = mNodes[i+j-1].getPosition() - mNodes[i+j].getPosition();
- n = mCross(t, b);
- n.normalizeSafe();
- t2 = mNodes[i+j].getPosition() - mNodes[i+j+1].getPosition();
- n2 = mCross(t2, b);
- n2.normalizeSafe();
- n += n2;
- }
- // Non-smoothed Node: Normal is perpendicular to segment.
- else
- {
- b = Point3F(0.0f, 0.0f, 1.0f);
- t = mNodes[i].getPosition() - mNodes[i+1].getPosition();
- n = mCross(t, b);
- }
- n.normalizeSafe();
- mNodeNormals.push_back(n);
- }
- }
- }
- void MeshRoadProfile::generateEndCap(F32 width)
- {
- Point3F pt;
- mCap.newPoly();
- for ( U32 i = 0; i < mNodes.size(); i++ )
- {
- pt = mNodes[i].getPosition();
- mCap.addVert(pt);
- }
- for ( S32 i = mNodes.size()-1; i >= 0; i-- )
- {
- pt = mNodes[i].getPosition();
- pt.x = -pt.x - width;
- mCap.addVert(pt);
- }
- mCap.decompose();
- }
- void MeshRoadProfile::setProfileDepth(F32 depth)
- {
- Point3F curPos = mNodes[mNodes.size()-1].getPosition();
- mNodes[mNodes.size()-1].setPosition(curPos.x, -depth);
- }
- void MeshRoadProfile::setTransform(const MatrixF &mat, const Point3F &p)
- {
- mObjToSlice.identity();
- mSliceToObj.identity();
- mObjToSlice *= mat;
- mSliceToObj *= mObjToSlice.inverse();
- mSliceToObj.transpose();
- mStartPos = p;
- }
- void MeshRoadProfile::getNodeWorldPos(U32 nodeId, Point3F &p)
- {
- if(nodeId < mNodes.size())
- {
- p = mNodes[nodeId].getPosition();
- mObjToSlice.mulP(p);
- p += mStartPos;
- }
- }
- void MeshRoadProfile::getNormToSlice(U32 normId, VectorF &n)
- {
- if(normId < mNodeNormals.size())
- {
- n = mNodeNormals[normId];
- mObjToSlice.mulP(n);
- }
- }
- void MeshRoadProfile::getNormWorldPos(U32 normId, Point3F &p)
- {
- if(normId < mNodeNormals.size())
- {
- U32 nodeId = normId/2 + (U32)(mFmod(normId,2.0f));
- p = mNodes[nodeId].getPosition();
- p += 0.5f * mNodeNormals[normId]; // Length = 0.5 units
- mObjToSlice.mulP(p);
- p += mStartPos;
- }
- }
- void MeshRoadProfile::worldToObj(Point3F &p)
- {
- p -= mStartPos;
- mSliceToObj.mulP(p);
- p.z = 0.0f;
- }
- void MeshRoadProfile::objToWorld(Point3F &p)
- {
- mObjToSlice.mulP(p);
- p += mStartPos;
- }
- F32 MeshRoadProfile::getProfileLen()
- {
- F32 sum = 0.0f;
- Point3F segmentVec;
- for(U32 i=0; i < mNodes.size()-1; i++)
- {
- segmentVec = mNodes[i+1].getPosition() - mNodes[i].getPosition();
- sum += segmentVec.len();
- }
- return sum;
- }
- F32 MeshRoadProfile::getNodePosPercent(U32 nodeId)
- {
- nodeId = mFmod(nodeId, mNodes.size());
- if(nodeId == 0)
- return 0.0f;
- else if(nodeId == mNodes.size()-1)
- return 1.0f;
- F32 totLen = getProfileLen();
- F32 sum = 0.0f;
- Point3F segmentVec;
- for(U32 i=0; i < nodeId; i++)
- {
- segmentVec = mNodes[i+1].getPosition() - mNodes[i].getPosition();
- sum += segmentVec.len();
- }
- return sum/totLen;
- }
- void MeshRoadProfile::resetProfile(F32 defaultDepth)
- {
- Point3F pos(0.0f, 0.0f, 0.0f);
- mNodes.clear();
- mNodes.push_back(pos);
- pos.y = -defaultDepth;
- mNodes.push_back(pos);
- mSegMtrls.clear();
- mSegMtrls.push_back(MeshRoad::Side);
- mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
- generateNormals();
- }
- //------------------------------------------------------------------------------
- // MeshRoadConvex Class
- //------------------------------------------------------------------------------
- const MatrixF& MeshRoadConvex::getTransform() const
- {
- return MatrixF::Identity; //mObject->getTransform();
- }
- Box3F MeshRoadConvex::getBoundingBox() const
- {
- return box;
- }
- Box3F MeshRoadConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
- {
- Box3F newBox = box;
- newBox.minExtents.convolve(scale);
- newBox.maxExtents.convolve(scale);
- mat.mul(newBox);
- return newBox;
- }
- Point3F MeshRoadConvex::support(const VectorF& vec) const
- {
- F32 bestDot = mDot( verts[0], vec );
- const Point3F *bestP = &verts[0];
- for(S32 i=1; i<4; i++)
- {
- F32 newD = mDot(verts[i], vec);
- if(newD > bestDot)
- {
- bestDot = newD;
- bestP = &verts[i];
- }
- }
- return *bestP;
- }
- void MeshRoadConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf)
- {
- cf->material = 0;
- cf->mObject = mObject;
- // For a tetrahedron this is pretty easy... first
- // convert everything into world space.
- Point3F tverts[4];
- mat.mulP(verts[0], &tverts[0]);
- mat.mulP(verts[1], &tverts[1]);
- mat.mulP(verts[2], &tverts[2]);
- mat.mulP(verts[3], &tverts[3]);
- // Points...
- S32 firstVert = cf->mVertexList.size();
- cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0];
- cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1];
- cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2];
- cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3];
- // Edges...
- cf->mEdgeList.increment();
- cf->mEdgeList.last().vertex[0] = firstVert+0;
- cf->mEdgeList.last().vertex[1] = firstVert+1;
- cf->mEdgeList.increment();
- cf->mEdgeList.last().vertex[0] = firstVert+1;
- cf->mEdgeList.last().vertex[1] = firstVert+2;
- cf->mEdgeList.increment();
- cf->mEdgeList.last().vertex[0] = firstVert+2;
- cf->mEdgeList.last().vertex[1] = firstVert+0;
- cf->mEdgeList.increment();
- cf->mEdgeList.last().vertex[0] = firstVert+3;
- cf->mEdgeList.last().vertex[1] = firstVert+0;
- cf->mEdgeList.increment();
- cf->mEdgeList.last().vertex[0] = firstVert+3;
- cf->mEdgeList.last().vertex[1] = firstVert+1;
- cf->mEdgeList.increment();
- cf->mEdgeList.last().vertex[0] = firstVert+3;
- cf->mEdgeList.last().vertex[1] = firstVert+2;
- // Triangles...
- cf->mFaceList.increment();
- cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]);
- cf->mFaceList.last().vertex[0] = firstVert+2;
- cf->mFaceList.last().vertex[1] = firstVert+1;
- cf->mFaceList.last().vertex[2] = firstVert+0;
- cf->mFaceList.increment();
- cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]);
- cf->mFaceList.last().vertex[0] = firstVert+1;
- cf->mFaceList.last().vertex[1] = firstVert+0;
- cf->mFaceList.last().vertex[2] = firstVert+3;
- cf->mFaceList.increment();
- cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]);
- cf->mFaceList.last().vertex[0] = firstVert+2;
- cf->mFaceList.last().vertex[1] = firstVert+1;
- cf->mFaceList.last().vertex[2] = firstVert+3;
- cf->mFaceList.increment();
- cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]);
- cf->mFaceList.last().vertex[0] = firstVert+0;
- cf->mFaceList.last().vertex[1] = firstVert+2;
- cf->mFaceList.last().vertex[2] = firstVert+3;
- }
- void MeshRoadConvex::getPolyList( AbstractPolyList* list )
- {
- // Transform the list into object space and set the pointer to the object
- //MatrixF i( mObject->getTransform() );
- //Point3F iS( mObject->getScale() );
- //list->setTransform(&i, iS);
- list->setTransform( &MatrixF::Identity, Point3F::One );
- list->setObject(mObject);
- // Points...
- S32 base = list->addPoint(verts[1]);
- list->addPoint(verts[2]);
- list->addPoint(verts[0]);
- list->addPoint(verts[3]);
- // Planes...
- list->begin(0,0);
- list->vertex(base + 2);
- list->vertex(base + 1);
- list->vertex(base + 0);
- list->plane(base + 2, base + 1, base + 0);
- list->end();
- list->begin(0,0);
- list->vertex(base + 2);
- list->vertex(base + 1);
- list->vertex(base + 3);
- list->plane(base + 2, base + 1, base + 3);
- list->end();
- list->begin(0,0);
- list->vertex(base + 3);
- list->vertex(base + 1);
- list->vertex(base + 0);
- list->plane(base + 3, base + 1, base + 0);
- list->end();
- list->begin(0,0);
- list->vertex(base + 2);
- list->vertex(base + 3);
- list->vertex(base + 0);
- list->plane(base + 2, base + 3, base + 0);
- list->end();
- }
- //------------------------------------------------------------------------------
- // MeshRoadSegment Class
- //------------------------------------------------------------------------------
- MeshRoadSegment::MeshRoadSegment()
- {
- mPlaneCount = 0;
- columns = 0;
- rows = 0;
- numVerts = 0;
- numTriangles = 0;
- startVert = 0;
- endVert = 0;
- startIndex = 0;
- endIndex = 0;
- slice0 = NULL;
- slice1 = NULL;
- }
- MeshRoadSegment::MeshRoadSegment( MeshRoadSlice *rs0, MeshRoadSlice *rs1, const MatrixF &roadMat )
- {
- columns = 0;
- rows = 0;
- numVerts = 0;
- numTriangles = 0;
- startVert = 0;
- endVert = 0;
- startIndex = 0;
- endIndex = 0;
- slice0 = rs0;
- slice1 = rs1;
- // Calculate the bounding box(s)
- worldbounds.minExtents = worldbounds.maxExtents = rs0->p0;
- for(U32 i=0; i < rs0->verts.size(); i++)
- worldbounds.extend( rs0->verts[i] );
- for(U32 i=0; i < rs1->verts.size(); i++)
- worldbounds.extend( rs1->verts[i] );
- objectbounds = worldbounds;
- roadMat.mul( objectbounds );
- // Calculate the planes for this segment
- // Will be used for intersection/buoyancy tests
- mPlaneCount = 6;
- mPlanes[0].set( slice0->pb0, slice0->p0, slice1->p0 ); // left
- mPlanes[1].set( slice1->pb2, slice1->p2, slice0->p2 ); // right
- mPlanes[2].set( slice0->pb2, slice0->p2, slice0->p0 ); // near
- mPlanes[3].set( slice1->p0, slice1->p2, slice1->pb2 ); // far
- mPlanes[4].set( slice1->p2, slice1->p0, slice0->p0 ); // top
- mPlanes[5].set( slice0->pb0, slice1->pb0, slice1->pb2 ); // bottom
- }
- void MeshRoadSegment::set( MeshRoadSlice *rs0, MeshRoadSlice *rs1 )
- {
- columns = 0;
- rows = 0;
- numVerts = 0;
- numTriangles = 0;
- startVert = 0;
- endVert = 0;
- startIndex = 0;
- endIndex = 0;
- slice0 = rs0;
- slice1 = rs1;
- }
- bool MeshRoadSegment::intersectBox( const Box3F &bounds ) const
- {
- // This code copied from Frustum class.
- Point3F maxPoint;
- F32 maxDot;
- // Note the planes are ordered left, right, near,
- // far, top, bottom for getting early rejections
- // from the typical horizontal scene.
- for ( S32 i = 0; i < mPlaneCount; i++ )
- {
- // This is pretty much as optimal as you can
- // get for a plane vs AABB test...
- //
- // 4 comparisons
- // 3 multiplies
- // 2 adds
- // 1 negation
- //
- // It will early out as soon as it detects the
- // bounds is outside one of the planes.
- if ( mPlanes[i].x > 0 )
- maxPoint.x = bounds.maxExtents.x;
- else
- maxPoint.x = bounds.minExtents.x;
- if ( mPlanes[i].y > 0 )
- maxPoint.y = bounds.maxExtents.y;
- else
- maxPoint.y = bounds.minExtents.y;
- if ( mPlanes[i].z > 0 )
- maxPoint.z = bounds.maxExtents.z;
- else
- maxPoint.z = bounds.minExtents.z;
- maxDot = mDot( maxPoint, mPlanes[ i ] );
- if ( maxDot <= -mPlanes[ i ].d )
- return false;
- }
- return true;
- }
- bool MeshRoadSegment::containsPoint( const Point3F &pnt ) const
- {
- // This code from Frustum class.
- F32 maxDot;
- // Note the planes are ordered left, right, near,
- // far, top, bottom for getting early rejections
- // from the typical horizontal scene.
- for ( S32 i = 0; i < mPlaneCount; i++ )
- {
- const PlaneF &plane = mPlanes[ i ];
- // This is pretty much as optimal as you can
- // get for a plane vs point test...
- //
- // 1 comparison
- // 2 multiplies
- // 1 adds
- //
- // It will early out as soon as it detects the
- // point is outside one of the planes.
- maxDot = mDot( pnt, plane ) + plane.d;
- if ( maxDot < 0.0f )
- return false;
- }
- return true;
- }
- F32 MeshRoadSegment::distanceToSurface(const Point3F &pnt) const
- {
- return mPlanes[4].distToPlane( pnt );
- }
- //------------------------------------------------------------------------------
- // MeshRoad Class
- //------------------------------------------------------------------------------
- ConsoleDocClass( MeshRoad,
- "@brief A strip of rectangular mesh segments defined by a 3D spline "
- "for prototyping road-shaped objects in your scene.\n\n"
-
- "User may control width and depth per node, overall spline shape in three "
- "dimensions, and seperate Materials for rendering the top, bottom, and side surfaces.\n\n"
-
- "MeshRoad is not capable of handling intersections, branches, curbs, or other "
- "desirable features in a final 'road' asset and is therefore intended for "
- "prototyping and experimentation.\n\n"
- "Materials assigned to MeshRoad should tile vertically.\n\n"
- "@ingroup Terrain"
- );
- bool MeshRoad::smEditorOpen = false;
- bool MeshRoad::smShowBatches = false;
- bool MeshRoad::smShowSpline = true;
- bool MeshRoad::smShowRoad = true;
- bool MeshRoad::smShowRoadProfile = false;
- bool MeshRoad::smWireframe = true;
- SimObjectPtr<SimSet> MeshRoad::smServerMeshRoadSet = NULL;
- GFXStateBlockRef MeshRoad::smWireframeSB;
- IMPLEMENT_CO_NETOBJECT_V1(MeshRoad);
- MeshRoad::MeshRoad()
- : mTextureLength( 5.0f ),
- mBreakAngle( 3.0f ),
- mWidthSubdivisions( 0 ),
- mPhysicsRep( NULL )
- {
- mConvexList = new Convex;
- // Setup NetObject.
- mTypeMask |= StaticObjectType | StaticShapeObjectType;
- mNetFlags.set(Ghostable);
- mMatInst[Top] = NULL;
- mMatInst[Bottom] = NULL;
- mMatInst[Side] = NULL;
- mTypeMask |= TerrainLikeObjectType;
- for (U32 i = 0; i < SurfaceCount; i++)
- {
- mVertCount[i] = 0;
- mTriangleCount[i] = 0;
- }
- INIT_ASSET(TopMaterial);
- INIT_ASSET(BottomMaterial);
- INIT_ASSET(SideMaterial);
- mSideProfile.mRoad = this;
- }
- MeshRoad::~MeshRoad()
- {
- delete mConvexList;
- mConvexList = NULL;
- }
- void MeshRoad::initPersistFields()
- {
- docsURL;
- addGroup( "MeshRoad" );
- INITPERSISTFIELD_MATERIALASSET(TopMaterial, MeshRoad, "Material for the upper surface of the road.");
- INITPERSISTFIELD_MATERIALASSET(BottomMaterial, MeshRoad, "Material for the bottom surface of the road.");
- INITPERSISTFIELD_MATERIALASSET(SideMaterial, MeshRoad, "Material for the side surface of the road.");
- addField( "textureLength", TypeF32, Offset( mTextureLength, MeshRoad ),
- "The length in meters of textures mapped to the MeshRoad." );
- addField( "breakAngle", TypeF32, Offset( mBreakAngle, MeshRoad ),
- "Angle in degrees - MeshRoad will subdivide the spline if its curve is greater than this threshold." );
- addField( "widthSubdivisions", TypeS32, Offset( mWidthSubdivisions, MeshRoad ),
- "Subdivide segments widthwise this many times when generating vertices." );
- endGroup( "MeshRoad" );
- addGroup( "Internal" );
- addProtectedField( "Node", TypeString, 0, &addNodeFromField, &emptyStringProtectedGetFn,
- "Do not modify, for internal use." );
- addProtectedField( "ProfileNode", TypeString, 0, &addProfileNodeFromField, &emptyStringProtectedGetFn,
- "Do not modify, for internal use." );
- endGroup( "Internal" );
- Parent::initPersistFields();
- }
- void MeshRoad::consoleInit()
- {
- Parent::consoleInit();
- Con::addVariable( "$MeshRoad::EditorOpen", TypeBool, &MeshRoad::smEditorOpen, "True if the MeshRoad editor is open, otherwise false.\n"
- "@ingroup Editors\n");
- Con::addVariable( "$MeshRoad::wireframe", TypeBool, &MeshRoad::smWireframe, "If true, will render the wireframe of the road.\n"
- "@ingroup Editors\n");
- Con::addVariable( "$MeshRoad::showBatches", TypeBool, &MeshRoad::smShowBatches, "Determines if the debug rendering of the batches cubes is displayed or not.\n"
- "@ingroup Editors\n");
- Con::addVariable( "$MeshRoad::showSpline", TypeBool, &MeshRoad::smShowSpline, "If true, the spline on which the curvature of this road is based will be rendered.\n"
- "@ingroup Editors\n");
- Con::addVariable( "$MeshRoad::showRoad", TypeBool, &MeshRoad::smShowRoad, "If true, the road will be rendered. When in the editor, roads are always rendered regardless of this flag.\n"
- "@ingroup Editors\n");
- Con::addVariable( "$MeshRoad::showRoadProfile", TypeBool, &MeshRoad::smShowRoadProfile, "If true, the road profile will be shown in the editor.\n"
- "@ingroup Editors\n");
- }
- bool MeshRoad::addNodeFromField( void *object, const char *index, const char *data )
- {
- MeshRoad *pObj = static_cast<MeshRoad*>(object);
- //if ( !pObj->isProperlyAdded() )
- //{
- F32 width, depth;
- Point3F pos, normal;
- U32 result = dSscanf( data, "%g %g %g %g %g %g %g %g", &pos.x, &pos.y, &pos.z, &width, &depth, &normal.x, &normal.y, &normal.z );
- if ( result == 8 )
- pObj->_addNode( pos, width, depth, normal );
- //}
- return false;
- }
- bool MeshRoad::addProfileNodeFromField( void* obj, const char *index, const char* data )
- {
- MeshRoad *pObj = static_cast<MeshRoad*>(obj);
- F32 x, y;
- U32 smooth, mtrl;
- U32 result = dSscanf( data, "%g %g %d %d", &x, &y, &smooth, &mtrl );
- if ( result == 4 )
- {
- if(!pObj->mSideProfile.mNodes.empty())
- pObj->mSideProfile.mSegMtrls.push_back(mtrl);
- MeshRoadProfileNode node;
- node.setPosition(x, y);
- node.setSmoothing(smooth != 0);
- pObj->mSideProfile.mNodes.push_back(node);
- }
- return false;
- }
- bool MeshRoad::onAdd()
- {
- if ( !Parent::onAdd() )
- return false;
- // Reset the World Box.
- //setGlobalBounds();
- resetWorldBox();
- // Set the Render Transform.
- setRenderTransform(mObjToWorld);
- // Add to ServerMeshRoadSet
- if ( isServerObject() )
- {
- getServerSet()->addObject( this );
- }
- if ( isClientObject() )
- _initMaterial();
- // If this road was not created from a file, give profile two default nodes
- if(mSideProfile.mNodes.empty())
- {
- // Initialize with two nodes in vertical line with unit length
- MeshRoadProfileNode node1(Point3F(0.0f, 0.0f, 0.0f));
- MeshRoadProfileNode node2(Point3F(0.0f, -5.0f, 0.0f));
- mSideProfile.mNodes.push_back(node1);
- mSideProfile.mNodes.push_back(node2);
- // Both node normals are straight to the right, perpendicular to the profile line
- VectorF norm(1.0f, 0.0f, 0.0f);
- mSideProfile.mNodeNormals.push_back(norm);
- mSideProfile.mNodeNormals.push_back(norm);
- mSideProfile.mSegMtrls.push_back(MeshRoad::Side);
- }
- else
- mSideProfile.generateNormals();
- // Generate the Vert/Index buffers and everything else.
- _regenerate();
- // Add to Scene.
- addToScene();
- return true;
- }
- void MeshRoad::onRemove()
- {
- SAFE_DELETE( mPhysicsRep );
- mConvexList->nukeList();
- for ( U32 i = 0; i < SurfaceCount; i++ )
- {
- SAFE_DELETE( mMatInst[i] );
- }
- removeFromScene();
- Parent::onRemove();
- }
- void MeshRoad::inspectPostApply()
- {
- // Set Parent.
- Parent::inspectPostApply();
- //if ( mMetersPerSegment < MIN_METERS_PER_SEGMENT )
- // mMetersPerSegment = MIN_METERS_PER_SEGMENT;
- setMaskBits(MeshRoadMask);
- }
- void MeshRoad::onStaticModified( const char* slotName, const char*newValue )
- {
- Parent::onStaticModified( slotName, newValue );
- if ( dStricmp( slotName, "breakAngle" ) == 0 )
- {
- setMaskBits( RegenMask );
- }
- }
- void MeshRoad::writeFields( Stream &stream, U32 tabStop )
- {
- Parent::writeFields( stream, tabStop );
- // Now write all nodes
- stream.write(2, "\r\n");
- for ( U32 i = 0; i < mNodes.size(); i++ )
- {
- const MeshRoadNode &node = mNodes[i];
- stream.writeTabs(tabStop);
- char buffer[1024];
- dMemset( buffer, 0, 1024 );
- dSprintf( buffer, 1024, "Node = \"%g %g %g %g %g %g %g %g\";", node.point.x, node.point.y, node.point.z, node.width, node.depth, node.normal.x, node.normal.y, node.normal.z );
- stream.writeLine( (const U8*)buffer );
- }
- stream.write(2, "\r\n");
- Point3F nodePos;
- U8 smooth, mtrl;
- for ( U32 i = 0; i < mSideProfile.mNodes.size(); i++ )
- {
- nodePos = mSideProfile.mNodes[i].getPosition();
- if(i)
- mtrl = mSideProfile.mSegMtrls[i-1];
- else
- mtrl = 0;
- if(mSideProfile.mNodes[i].isSmooth())
- smooth = 1;
- else
- smooth = 0;
- stream.writeTabs(tabStop);
- char buffer[1024];
- dMemset( buffer, 0, 1024 );
- dSprintf( buffer, 1024, "ProfileNode = \"%.6f %.6f %d %d\";", nodePos.x, nodePos.y, smooth, mtrl);
- stream.writeLine( (const U8*)buffer );
- }
- }
- bool MeshRoad::writeField( StringTableEntry fieldname, const char *value )
- {
- if ( fieldname == StringTable->insert("Node") )
- return false;
- if ( fieldname == StringTable->insert("ProfileNode") )
- return false;
- return Parent::writeField( fieldname, value );
- }
- void MeshRoad::onEditorEnable()
- {
- }
- void MeshRoad::onEditorDisable()
- {
- }
- SimSet* MeshRoad::getServerSet()
- {
- if ( !smServerMeshRoadSet )
- {
- smServerMeshRoadSet = new SimSet();
- smServerMeshRoadSet->registerObject( "ServerMeshRoadSet" );
- Sim::getRootGroup()->addObject( smServerMeshRoadSet );
- }
- return smServerMeshRoadSet;
- }
- void MeshRoad::prepRenderImage( SceneRenderState* state )
- {
- if ( mNodes.size() <= 1 )
- return;
- RenderPassManager *renderPass = state->getRenderPass();
-
- // Normal Road RenderInstance
- // Always rendered when the editor is not open
- // otherwise obey the smShowRoad flag
- if ( smShowRoad || !smEditorOpen )
- {
- #ifdef TORQUE_AFX_ENABLED
- afxZodiacMgr::renderMeshRoadZodiacs(state, this);
- #endif
- MeshRenderInst coreRI;
- coreRI.clear();
- coreRI.objectToWorld = &MatrixF::Identity;
- coreRI.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
- coreRI.projection = renderPass->allocSharedXform(RenderPassManager::Projection);
- coreRI.type = RenderPassManager::RIT_Mesh;
-
- BaseMatInstance *matInst;
- for ( U32 i = 0; i < SurfaceCount; i++ )
- {
- matInst = state->getOverrideMaterial( mMatInst[i] );
- if ( !matInst )
- continue;
- // Get the lights if we haven't already.
- if ( matInst->isForwardLit() && !coreRI.lights[0] )
- {
- LightQuery query;
- query.init( getWorldSphere() );
- query.getLights( coreRI.lights, 8 );
- }
- MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
- *ri = coreRI;
- // Currently rendering whole road, fix to cull and batch
- // per segment.
- // Set the correct material for rendering.
- ri->matInst = matInst;
- ri->vertBuff = &mVB[i];
- ri->primBuff = &mPB[i];
- ri->prim = renderPass->allocPrim();
- ri->prim->type = GFXTriangleList;
- ri->prim->minIndex = 0;
- ri->prim->startIndex = 0;
- ri->prim->numPrimitives = mTriangleCount[i];
- ri->prim->startVertex = 0;
- ri->prim->numVertices = mVertCount[i];
- // We sort by the material then vertex buffer.
- ri->defaultKey = matInst->getStateHint();
- ri->defaultKey2 = (uintptr_t)ri->vertBuff; // Not 64bit safe!
- renderPass->addInst( ri );
- }
- }
- // Debug RenderInstance
- // Only when editor is open.
- if ( smEditorOpen )
- {
- ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
- ri->renderDelegate.bind( this, &MeshRoad::_debugRender );
- ri->type = RenderPassManager::RIT_Editor;
- state->getRenderPass()->addInst( ri );
- }
- }
- void MeshRoad::_initMaterial()
- {
- if (mTopMaterialAsset.notNull())
- {
- if (!mMatInst[Top] || !String(mTopMaterialAsset->getMaterialDefinitionName()).equal(mMatInst[Top]->getMaterial()->getName(), String::NoCase))
- {
- SAFE_DELETE(mMatInst[Top]);
- Material* tMat = nullptr;
- if (!Sim::findObject(mTopMaterialAsset->getMaterialDefinitionName(), tMat))
- Con::errorf("MeshRoad::_initMaterial - Material %s was not found.", mTopMaterialAsset->getMaterialDefinitionName());
- mMaterial[Top] = tMat;
- if (mMaterial[Top])
- mMatInst[Top] = mMaterial[Top]->createMatInstance();
- else
- mMatInst[Top] = MATMGR->createMatInstance("WarningMaterial");
- mMatInst[Top]->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>());
- }
- }
- if (mBottomMaterialAsset.notNull())
- {
- if (!mMatInst[Bottom] || !String(mBottomMaterialAsset->getMaterialDefinitionName()).equal(mMatInst[Bottom]->getMaterial()->getName(), String::NoCase))
- {
- SAFE_DELETE(mMatInst[Bottom]);
- Material* tMat = nullptr;
- if (!Sim::findObject(mBottomMaterialAsset->getMaterialDefinitionName(), tMat))
- Con::errorf("MeshRoad::_initMaterial - Material %s was not found.", mBottomMaterialAsset->getMaterialDefinitionName());
- mMaterial[Bottom] = tMat;
- if (mMaterial[Bottom])
- mMatInst[Bottom] = mMaterial[Bottom]->createMatInstance();
- else
- mMatInst[Bottom] = MATMGR->createMatInstance("WarningMaterial");
- mMatInst[Bottom]->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>());
- }
- }
- if (mSideMaterialAsset.notNull())
- {
- if (!mMatInst[Side] || !String(mSideMaterialAsset->getMaterialDefinitionName()).equal(mMatInst[Side]->getMaterial()->getName(), String::NoCase))
- {
- SAFE_DELETE(mMatInst[Side]);
- Material* tMat = nullptr;
- if (!Sim::findObject(mSideMaterialAsset->getMaterialDefinitionName(), tMat))
- Con::errorf("MeshRoad::_initMaterial - Material %s was not found.", mSideMaterialAsset->getMaterialDefinitionName());
- mMaterial[Side] = tMat;
- if (mMaterial[Side])
- mMatInst[Side] = mMaterial[Side]->createMatInstance();
- else
- mMatInst[Side] = MATMGR->createMatInstance("WarningMaterial");
- mMatInst[Side]->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>());
- }
- }
- }
- void MeshRoad::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* )
- {
- //MeshRoadConvex convex;
- //buildConvex( Box3F(true), convex );
- //convex.render();
- //GFXDrawUtil *drawer = GFX->getDrawUtil();
- //GFX->setStateBlock( smStateBlock );
- return;
- /*
- U32 convexCount = mDebugConvex.size();
- PrimBuild::begin( GFXTriangleList, convexCount * 12 );
- PrimBuild::color4i( 0, 0, 255, 155 );
- for ( U32 i = 0; i < convexCount; i++ )
- {
- MeshRoadConvex *convex = mDebugConvex[i];
- Point3F a = convex->verts[0];
- Point3F b = convex->verts[1];
- Point3F c = convex->verts[2];
- Point3F p = convex->verts[3];
- //mObjToWorld.mulP(a);
- //mObjToWorld.mulP(b);
- //mObjToWorld.mulP(c);
- //mObjToWorld.mulP(p);
-
- PrimBuild::vertex3fv( c );
- PrimBuild::vertex3fv( b );
- PrimBuild::vertex3fv( a );
- PrimBuild::vertex3fv( b );
- PrimBuild::vertex3fv( a );
- PrimBuild::vertex3fv( p );
- PrimBuild::vertex3fv( c );
- PrimBuild::vertex3fv( b );
- PrimBuild::vertex3fv( p );
- PrimBuild::vertex3fv( a );
- PrimBuild::vertex3fv( c );
- PrimBuild::vertex3fv( p );
- }
- PrimBuild::end();
- for ( U32 i = 0; i < mSegments.size(); i++ )
- {
- ///GFX->getDrawUtil()->drawWireBox( mSegments[i].worldbounds, ColorI(255,0,0,255) );
- }
- GFX->enterDebugEvent( ColorI( 255, 0, 0 ), "DecalRoad_debugRender" );
- GFXTransformSaver saver;
- GFX->setStateBlock( smStateBlock );
- Point3F size(1,1,1);
- ColorI color( 255, 0, 0, 255 );
- if ( smShowBatches )
- {
- for ( U32 i = 0; i < mBatches.size(); i++ )
- {
- const Box3F &box = mBatches[i].bounds;
- Point3F center;
- box.getCenter( ¢er );
- GFX->getDrawUtil()->drawWireCube( ( box.maxExtents - box.minExtents ) * 0.5f, center, ColorI(255,100,100,255) );
- }
- }
- GFX->leaveDebugEvent();
- */
- }
- U32 MeshRoad::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
- {
- U32 retMask = Parent::packUpdate(con, mask, stream);
- if ( stream->writeFlag( mask & MeshRoadMask ) )
- {
- // Write Object Transform.
- stream->writeAffineTransform( mObjToWorld );
- // Write Materials
- PACK_ASSET(con, TopMaterial);
- PACK_ASSET(con, BottomMaterial);
- PACK_ASSET(con, SideMaterial);
- stream->write( mTextureLength );
- stream->write( mBreakAngle );
- stream->write( mWidthSubdivisions );
- }
- if ( stream->writeFlag( mask & ProfileMask ) )
- {
- stream->writeInt( mSideProfile.mNodes.size(), 16 );
- for( U32 i = 0; i < mSideProfile.mNodes.size(); i++ )
- {
- mathWrite( *stream, mSideProfile.mNodes[i].getPosition() );
- stream->writeFlag( mSideProfile.mNodes[i].isSmooth() );
- if(i)
- stream->writeInt(mSideProfile.mSegMtrls[i-1], 3);
- else
- stream->writeInt(0, 3);
- }
- }
- if ( stream->writeFlag( mask & NodeMask ) )
- {
- const U32 nodeByteSize = 32; // Based on sending all of a node's parameters
- // Test if we can fit all of our nodes within the current stream.
- // We make sure we leave 100 bytes still free in the stream for whatever
- // may follow us.
- S32 allowedBytes = stream->getWriteByteSize() - 100;
- if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) )
- {
- // All nodes should fit, so send them out now.
- stream->writeInt( mNodes.size(), 16 );
- for ( U32 i = 0; i < mNodes.size(); i++ )
- {
- mathWrite( *stream, mNodes[i].point );
- stream->write( mNodes[i].width );
- stream->write( mNodes[i].depth );
- mathWrite( *stream, mNodes[i].normal );
- }
- }
- else
- {
- // There isn't enough space left in the stream for all of the
- // nodes. Batch them up into NetEvents.
- U32 id = gServerNodeListManager->nextListId();
- U32 count = 0;
- U32 index = 0;
- while (count < mNodes.size())
- {
- count += NodeListManager::smMaximumNodesPerEvent;
- if (count > mNodes.size())
- {
- count = mNodes.size();
- }
- MeshRoadNodeEvent* event = new MeshRoadNodeEvent();
- event->mId = id;
- event->mTotalNodes = mNodes.size();
- event->mLocalListStart = index;
- for (; index<count; ++index)
- {
- event->mPositions.push_back( mNodes[index].point );
- event->mWidths.push_back( mNodes[index].width );
- event->mDepths.push_back( mNodes[index].depth );
- event->mNormals.push_back( mNodes[index].normal );
- }
- con->postNetEvent( event );
- }
- stream->write( id );
- }
- }
- stream->writeFlag( mask & RegenMask );
- // Were done ...
- return retMask;
- }
- void MeshRoad::unpackUpdate(NetConnection * con, BitStream * stream)
- {
- // Unpack Parent.
- Parent::unpackUpdate(con, stream);
- // MeshRoadMask
- if(stream->readFlag())
- {
- MatrixF ObjectMatrix;
- stream->readAffineTransform(&ObjectMatrix);
- Parent::setTransform(ObjectMatrix);
- UNPACK_ASSET(con, TopMaterial);
- UNPACK_ASSET(con, BottomMaterial);
- UNPACK_ASSET(con, SideMaterial);
- if ( isProperlyAdded() )
- _initMaterial();
- stream->read( &mTextureLength );
- stream->read( &mBreakAngle );
- stream->read( &mWidthSubdivisions );
- }
- // ProfileMask
- if(stream->readFlag())
- {
- Point3F pos;
- mSideProfile.mNodes.clear();
- mSideProfile.mSegMtrls.clear();
- U32 count = stream->readInt( 16 );
- for( U32 i = 0; i < count; i++)
- {
- mathRead( *stream, &pos );
- MeshRoadProfileNode node(pos);
- node.setSmoothing( stream->readFlag() );
- mSideProfile.mNodes.push_back(node);
- if(i)
- mSideProfile.mSegMtrls.push_back(stream->readInt(3));
- else
- stream->readInt(3);
- }
- mSideProfile.generateNormals();
- }
- // NodeMask
- if ( stream->readFlag() )
- {
- if (stream->readFlag())
- {
- // Nodes have been passed in this update
- U32 count = stream->readInt( 16 );
- mNodes.clear();
- Point3F pos, normal;
- F32 width, depth;
- for ( U32 i = 0; i < count; i++ )
- {
- mathRead( *stream, &pos );
- stream->read( &width );
- stream->read( &depth );
- mathRead( *stream, &normal );
- _addNode( pos, width, depth, normal );
- }
- }
- else
- {
- // Nodes will arrive as events
- U32 id;
- stream->read( &id );
- // Check if the road's nodes made it here before we did.
- NodeListManager::NodeList* list = NULL;
- if ( gClientNodeListManager->findListById( id, &list, true) )
- {
- // Work with the completed list
- MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list );
- if (roadList)
- buildNodesFromList( roadList );
- delete list;
- }
- else
- {
- // Nodes have not yet arrived, so register our interest in the list
- MeshRoadNodeListNotify* notify = new MeshRoadNodeListNotify( this, id );
- gClientNodeListManager->registerNotification( notify );
- }
- }
- }
- if ( stream->readFlag() && isProperlyAdded() )
- _regenerate();
- }
- void MeshRoad::setTransform( const MatrixF &mat )
- {
- for ( U32 i = 0; i < mNodes.size(); i++ )
- {
- mWorldToObj.mulP( mNodes[i].point );
- mat.mulP( mNodes[i].point );
- }
- Parent::setTransform( mat );
- if ( mPhysicsRep )
- mPhysicsRep->setTransform( mat );
- // Regenerate and update the client
- _regenerate();
- setMaskBits( NodeMask | RegenMask );
- }
- void MeshRoad::setScale( const VectorF &scale )
- {
- // We ignore scale requests from the editor
- // right now.
- //Parent::setScale( scale );
- }
- void MeshRoad::buildConvex(const Box3F& box, Convex* convex)
- {
- if ( mSlices.size() < 2 )
- return;
- mConvexList->collectGarbage();
- mDebugConvex.clear();
- Box3F realBox = box;
- mWorldToObj.mul(realBox);
- realBox.minExtents.convolveInverse(mObjScale);
- realBox.maxExtents.convolveInverse(mObjScale);
- if (realBox.isOverlapped(getObjBox()) == false)
- return;
- U32 segmentCount = mSegments.size();
- U32 numConvexes ;
- U32 halfConvexes;
- U32 nextSegOffset = 2*mSideProfile.mNodes.size();
- U32 leftSideOffset = nextSegOffset/2;
- U32 k2, capIdx1, capIdx2, capIdx3;
- // Create convex(s) for each segment
- for ( U32 i = 0; i < segmentCount; i++ )
- {
- const MeshRoadSegment &segment = mSegments[i];
- // Is this segment overlapped?
- if ( !segment.getWorldBounds().isOverlapped( box ) )
- continue;
- // Each segment has 6 faces
- for ( U32 j = 0; j < 6; j++ )
- {
- // Only first segment has front face
- if ( j == 4 && i != 0 )
- continue;
- // Only last segment has back face
- if ( j == 5 && i != segmentCount-1 )
- continue;
- // The top and bottom sides have 2 convex(s)
- // The left, right, front, and back sides depend on the user-defined profile
- switch(j)
- {
- case 0: numConvexes = 2; break; // Top
- case 1: // Left
- case 2: numConvexes = 2* (mSideProfile.mNodes.size()-1); break; // Right
- case 3: numConvexes = 2; break; // Bottom
- case 4: // Front
- case 5: numConvexes = mSideProfile.mCap.getNumTris(); break; // Back
- default: numConvexes = 0;
- }
- halfConvexes = numConvexes/2;
- for ( U32 k = 0; k < numConvexes; k++ )
- {
- // See if this convex exists in the working set already...
- Convex* cc = 0;
- CollisionWorkingList& wl = convex->getWorkingList();
- for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext )
- {
- if ( itr->mConvex->getType() == MeshRoadConvexType )
- {
- MeshRoadConvex *pConvex = static_cast<MeshRoadConvex*>(itr->mConvex);
- if ( pConvex->pRoad == this &&
- pConvex->segmentId == i &&
- pConvex->faceId == j &&
- pConvex->triangleId == k )
- {
- cc = itr->mConvex;
- break;
- }
- }
- }
- if (cc)
- continue;
- Point3F a, b, c;
- // Top or Bottom
- if(j == 0 || j == 3)
- {
- // Get the triangle...
- U32 idx0 = gIdxArray[j][k][0];
- U32 idx1 = gIdxArray[j][k][1];
- U32 idx2 = gIdxArray[j][k][2];
- a = segment[idx0];
- b = segment[idx1];
- c = segment[idx2];
- }
- // Left Side
- else if(j == 1)
- {
- if(k >= halfConvexes)
- {
- k2 = k + leftSideOffset - halfConvexes;
- a = segment.slice1->verts[k2];
- b = segment.slice0->verts[k2];
- c = segment.slice1->verts[k2 + 1];
- }
- else
- {
- k2 = k + leftSideOffset;
- a = segment.slice0->verts[k2];
- b = segment.slice0->verts[k2 + 1];
- c = segment.slice1->verts[k2 + 1];
- }
- }
- // Right Side
- else if(j == 2)
- {
- // a.set(2*k, 2*k, 0.0f);
- // b.set(2*k, 2*k, 2.0f);
- // c.set(2*(k+1), 2*(k+1), 0.0f);
- if(k >= halfConvexes)
- {
- k2 = k - halfConvexes;
- a = segment.slice1->verts[k2];
- b = segment.slice1->verts[k2 + 1];
- c = segment.slice0->verts[k2];
- }
- else
- {
- a = segment.slice0->verts[k];
- b = segment.slice1->verts[k + 1];
- c = segment.slice0->verts[k + 1];
- }
- }
- // Front
- else if(j == 4)
- {
- k2 = nextSegOffset + leftSideOffset - 1;
- capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
- capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
- capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
- if(capIdx1 >= leftSideOffset)
- capIdx1 = k2 - capIdx1;
- if(capIdx2 >= leftSideOffset)
- capIdx2 = k2 - capIdx2;
- if(capIdx3 >= leftSideOffset)
- capIdx3 = k2 - capIdx3;
- a = segment.slice0->verts[capIdx1];
- b = segment.slice0->verts[capIdx2];
- c = segment.slice0->verts[capIdx3];
- }
- // Back
- else
- {
- k2 = nextSegOffset + leftSideOffset - 1;
- capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
- capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
- capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
- if(capIdx1 >= leftSideOffset)
- capIdx1 = k2 - capIdx1;
- if(capIdx2 >= leftSideOffset)
- capIdx2 = k2 - capIdx2;
- if(capIdx3 >= leftSideOffset)
- capIdx3 = k2 - capIdx3;
- a = segment.slice1->verts[capIdx3];
- b = segment.slice1->verts[capIdx2];
- c = segment.slice1->verts[capIdx1];
- }
-
- // Transform the result into object space!
- //mWorldToObj.mulP( a );
- //mWorldToObj.mulP( b );
- //mWorldToObj.mulP( c );
- PlaneF p( c, b, a );
- Point3F peak = ((a + b + c) / 3.0f) + (p * 0.15f);
- // Set up the convex...
- MeshRoadConvex *cp = new MeshRoadConvex();
- mConvexList->registerObject( cp );
- convex->addToWorkingList( cp );
- cp->mObject = this;
- cp->pRoad = this;
- cp->segmentId = i;
- cp->faceId = j;
- cp->triangleId = k;
- cp->normal = p;
- cp->verts[0] = c;
- cp->verts[1] = b;
- cp->verts[2] = a;
- cp->verts[3] = peak;
- // Update the bounding box.
- Box3F &bounds = cp->box;
- bounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX );
- bounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX );
- bounds.minExtents.setMin( a );
- bounds.minExtents.setMin( b );
- bounds.minExtents.setMin( c );
- bounds.minExtents.setMin( peak );
- bounds.maxExtents.setMax( a );
- bounds.maxExtents.setMax( b );
- bounds.maxExtents.setMax( c );
- bounds.maxExtents.setMax( peak );
- mDebugConvex.push_back(cp);
- }
- }
- }
- }
- bool MeshRoad::buildPolyList( PolyListContext, AbstractPolyList* polyList, const Box3F &box, const SphereF & )
- {
- if ( mSlices.size() < 2 )
- return false;
- polyList->setTransform( &MatrixF::Identity, Point3F::One );
- polyList->setObject(this);
- // JCF: optimize this to not always add everything.
- return buildSegmentPolyList( polyList, 0, mSegments.size() - 1, true, true );
- }
- bool MeshRoad::buildSegmentPolyList( AbstractPolyList* polyList, U32 startSegIdx, U32 endSegIdx, bool capFront, bool capEnd )
- {
- if ( mSlices.size() < 2 )
- return false;
- // Add verts
- for ( U32 i = startSegIdx; i <= endSegIdx; i++ )
- {
- const MeshRoadSegment &seg = mSegments[i];
- if ( i == startSegIdx )
- {
- for(U32 j = 0; j < seg.slice0->verts.size(); j++)
- polyList->addPoint( seg.slice0->verts[j] );
- }
- for(U32 j = 0; j < seg.slice1->verts.size(); j++)
- polyList->addPoint( seg.slice1->verts[j] );
- }
- // Temporaries to hold indices for the corner points of a quad.
- S32 p00, p01, p11, p10;
- S32 pb00, pb01, pb11, pb10;
- U32 offset = 0;
- S32 a, b, c;
- U32 mirror;
- DebugDrawer *ddraw = NULL;//DebugDrawer::get();
- ClippedPolyList *cpolyList = dynamic_cast<ClippedPolyList*>(polyList);
- MatrixF mat;
- Point3F scale;
- if ( cpolyList )
- cpolyList->getTransform( &mat, &scale );
- U32 nextSegOffset = 2*mSideProfile.mNodes.size();
- U32 leftSideOffset = nextSegOffset/2;
- for ( U32 i = startSegIdx; i <= endSegIdx; i++ )
- {
- p00 = offset + leftSideOffset;
- p10 = offset;
- pb00 = offset + nextSegOffset - 1;
- pb10 = offset + leftSideOffset - 1;
- p01 = offset + nextSegOffset + leftSideOffset;
- p11 = offset + nextSegOffset;
- pb01 = offset + 2*nextSegOffset - 1;
- pb11 = offset + nextSegOffset + leftSideOffset - 1;
- // Top Face
- polyList->begin( 0,0 );
- polyList->vertex( p00 );
- polyList->vertex( p01 );
- polyList->vertex( p11 );
- polyList->plane( p00, p01, p11 );
- polyList->end();
- if ( ddraw && cpolyList )
- {
- Point3F v0 = cpolyList->mVertexList[p00].point;
- mat.mulP( v0 );
- Point3F v1 = cpolyList->mVertexList[p01].point;
- mat.mulP( v1 );
- Point3F v2 = cpolyList->mVertexList[p11].point;
- mat.mulP( v2 );
- ddraw->drawTri( v0, v1, v2 );
- ddraw->setLastZTest( false );
- ddraw->setLastTTL( 0 );
- }
- polyList->begin( 0,0 );
- polyList->vertex( p00 );
- polyList->vertex( p11 );
- polyList->vertex( p10 );
- polyList->plane( p00, p11, p10 );
- polyList->end();
- if ( ddraw && cpolyList )
- {
- ddraw->drawTri( cpolyList->mVertexList[p00].point, cpolyList->mVertexList[p11].point, cpolyList->mVertexList[p10].point );
- ddraw->setLastTTL( 0 );
- }
- if (buildPolyList_TopSurfaceOnly)
- {
- offset += 4;
- continue;
- }
- // Left Face
- for(U32 j = leftSideOffset; j < nextSegOffset-1; j++)
- {
- a = offset + j;
- b = a + nextSegOffset + 1;
- c = b - 1;
- polyList->begin( 0,0 );
- polyList->vertex( a );
- polyList->vertex( b );
- polyList->vertex( c);
- polyList->plane( a, b, c );
- polyList->end();
- a = offset + j;
- b = a + 1;
- c = a + nextSegOffset + 1;
- polyList->begin( 0,0 );
- polyList->vertex( a );
- polyList->vertex( b );
- polyList->vertex( c );
- polyList->plane( a, b, c );
- polyList->end();
- }
- // Right Face
- for(U32 j = 0; j < leftSideOffset-1; j++)
- {
- a = offset + j;
- b = a + nextSegOffset;
- c = b + 1;
- polyList->begin( 0,0 );
- polyList->vertex( a );
- polyList->vertex( b );
- polyList->vertex( c);
- polyList->plane( a, b, c );
- polyList->end();
- a = offset + j;
- b = a + nextSegOffset + 1;
- c = a + 1;
- polyList->begin( 0,0 );
- polyList->vertex( a );
- polyList->vertex( b );
- polyList->vertex( c );
- polyList->plane( a, b, c );
- polyList->end();
- }
- // Bottom Face
- polyList->begin( 0,0 );
- polyList->vertex( pb00 );
- polyList->vertex( pb10 );
- polyList->vertex( pb11 );
- polyList->plane( pb00, pb10, pb11 );
- polyList->end();
- polyList->begin( 0,0 );
- polyList->vertex( pb00 );
- polyList->vertex( pb11 );
- polyList->vertex( pb01 );
- polyList->plane( pb00, pb11, pb01 );
- polyList->end();
- // Front Face
- if ( i == startSegIdx && capFront )
- {
- mirror = nextSegOffset + leftSideOffset - 1;
- for(U32 j = 0; j < mSideProfile.mCap.getNumTris(); j++)
- {
- a = mSideProfile.mCap.getTriIdx(j, 0);
- b = mSideProfile.mCap.getTriIdx(j, 1);
- c = mSideProfile.mCap.getTriIdx(j, 2);
- if(a >= leftSideOffset)
- a = mirror - a;
- if(b >= leftSideOffset)
- b = mirror - b;
- if(c >= leftSideOffset)
- c = mirror - c;
- polyList->begin( 0,0 );
- polyList->vertex( a );
- polyList->vertex( b );
- polyList->vertex( c );
- polyList->plane( a, b, c );
- polyList->end();
- }
- }
- // Back Face
- if ( i == endSegIdx && capEnd )
- {
- mirror = nextSegOffset + leftSideOffset - 1;
- for(U32 j = 0; j < mSideProfile.mCap.getNumTris(); j++)
- {
- a = mSideProfile.mCap.getTriIdx(j, 0);
- b = mSideProfile.mCap.getTriIdx(j, 1);
- c = mSideProfile.mCap.getTriIdx(j, 2);
- if(a >= leftSideOffset)
- a = offset + nextSegOffset + mirror - a;
- if(b >= leftSideOffset)
- b = offset + nextSegOffset + mirror - b;
- if(c >= leftSideOffset)
- c = offset + nextSegOffset + mirror - c;
- polyList->begin( 0,0 );
- polyList->vertex( c );
- polyList->vertex( b );
- polyList->vertex( a );
- polyList->plane( c, b, a );
- polyList->end();
- }
- }
- offset += nextSegOffset;
- }
- return true;
- }
- bool MeshRoad::castRay( const Point3F &s, const Point3F &e, RayInfo *info )
- {
- Point3F start = s;
- Point3F end = e;
- mObjToWorld.mulP(start);
- mObjToWorld.mulP(end);
- F32 out = 1.0f; // The output fraction/percentage along the line defined by s and e
- VectorF norm(0.0f, 0.0f, 0.0f); // The normal of the face intersected
-
- Vector<MeshRoadHitSegment> hitSegments;
- for ( U32 i = 0; i < mSegments.size(); i++ )
- {
- const MeshRoadSegment &segment = mSegments[i];
- F32 t;
- VectorF n;
- if ( segment.getWorldBounds().collideLine( start, end, &t, &n ) )
- {
- hitSegments.increment();
- hitSegments.last().t = t;
- hitSegments.last().idx = i;
- }
- }
- dQsort( hitSegments.address(), hitSegments.size(), sizeof(MeshRoadHitSegment), compareHitSegments );
- U32 idx0, idx1, idx2;
- F32 t;
- for ( U32 i = 0; i < hitSegments.size(); i++ )
- {
- U32 segIdx = hitSegments[i].idx;
- const MeshRoadSegment &segment = mSegments[segIdx];
- U32 numConvexes ;
- U32 halfConvexes;
- U32 nextSegOffset = 2*mSideProfile.mNodes.size();
- U32 leftSideOffset = nextSegOffset/2;
- U32 k2, capIdx1, capIdx2, capIdx3;
- // Each segment has 6 faces
- for ( U32 j = 0; j < 6; j++ )
- {
- if ( j == 4 && segIdx != 0 )
- continue;
- if ( j == 5 && segIdx != mSegments.size() - 1 )
- continue;
- // The top and bottom sides have 2 convex(s)
- // The left, right, front, and back sides depend on the user-defined profile
- switch(j)
- {
- case 0: numConvexes = 2; break; // Top
- case 1: // Left
- case 2: numConvexes = 2* (mSideProfile.mNodes.size()-1); break; // Right
- case 3: numConvexes = 2; break; // Bottom
- case 4: // Front
- case 5: numConvexes = mSideProfile.mCap.getNumTris(); break; // Back
- default: numConvexes = 0;
- }
- halfConvexes = numConvexes/2;
- // Each face has 2 triangles
- for ( U32 k = 0; k < numConvexes; k++ )
- {
- const Point3F *a = NULL;
- const Point3F *b = NULL;
- const Point3F *c = NULL;
- // Top or Bottom
- if(j == 0 || j == 3)
- {
- idx0 = gIdxArray[j][k][0];
- idx1 = gIdxArray[j][k][1];
- idx2 = gIdxArray[j][k][2];
- a = &segment[idx0];
- b = &segment[idx1];
- c = &segment[idx2];
- }
- // Left Side
- else if(j == 1)
- {
- if(k >= halfConvexes)
- {
- k2 = k + leftSideOffset - halfConvexes;
- a = &segment.slice1->verts[k2];
- b = &segment.slice0->verts[k2];
- c = &segment.slice1->verts[k2 + 1];
- }
- else
- {
- k2 = k + leftSideOffset;
- a = &segment.slice0->verts[k2];
- b = &segment.slice0->verts[k2 + 1];
- c = &segment.slice1->verts[k2 + 1];
- }
- }
- // Right Side
- else if(j == 2)
- {
- if(k >= halfConvexes)
- {
- k2 = k - halfConvexes;
- a = &segment.slice1->verts[k2];
- b = &segment.slice1->verts[k2 + 1];
- c = &segment.slice0->verts[k2];
- }
- else
- {
- a = &segment.slice0->verts[k];
- b = &segment.slice1->verts[k + 1];
- c = &segment.slice0->verts[k + 1];
- }
- }
- // Front
- else if(j == 4)
- {
- k2 = nextSegOffset + leftSideOffset - 1;
- capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
- capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
- capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
- if(capIdx1 >= leftSideOffset)
- capIdx1 = k2 - capIdx1;
- if(capIdx2 >= leftSideOffset)
- capIdx2 = k2 - capIdx2;
- if(capIdx3 >= leftSideOffset)
- capIdx3 = k2 - capIdx3;
- a = &segment.slice0->verts[capIdx1];
- b = &segment.slice0->verts[capIdx2];
- c = &segment.slice0->verts[capIdx3];
- }
- // Back
- else
- {
- k2 = nextSegOffset + leftSideOffset - 1;
- capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
- capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
- capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
- if(capIdx1 >= leftSideOffset)
- capIdx1 = k2 - capIdx1;
- if(capIdx2 >= leftSideOffset)
- capIdx2 = k2 - capIdx2;
- if(capIdx3 >= leftSideOffset)
- capIdx3 = k2 - capIdx3;
- a = &segment.slice1->verts[capIdx3];
- b = &segment.slice1->verts[capIdx2];
- c = &segment.slice1->verts[capIdx1];
- }
- if ( !MathUtils::mLineTriangleCollide( start, end,
- *c, *b, *a,
- NULL,
- &t ) )
- continue;
-
- if ( t >= 0.0f && t < 1.0f && t < out )
- {
- out = t;
- norm = PlaneF( *a, *b, *c );
- }
- }
- }
- if (out >= 0.0f && out < 1.0f)
- break;
- }
- if (out >= 0.0f && out < 1.0f)
- {
- info->t = out;
- info->normal = norm;
- info->point.interpolate(start, end, out);
- info->face = -1;
- info->object = this;
- info->material = this->mMatInst[0];
- return true;
- }
- return false;
- }
- bool MeshRoad::collideBox(const Point3F &start, const Point3F &end, RayInfo* info)
- {
- Con::warnf( "MeshRoad::collideBox() - not yet implemented!" );
- return Parent::collideBox( start, end, info );
- }
- void MeshRoad::_regenerate()
- {
- if ( mNodes.size() == 0 )
- return;
- if ( mSideProfile.mNodes.size() == 2 && mSideProfile.mNodes[1].getPosition().x == 0.0f)
- mSideProfile.setProfileDepth(mNodes[0].depth);
- const Point3F &nodePt = mNodes.first().point;
- MatrixF mat( true );
- mat.setPosition( nodePt );
- Parent::setTransform( mat );
- _generateSlices();
- // Make sure we are in the correct bins given our world box.
- if( getSceneManager() != NULL )
- getSceneManager()->notifyObjectDirty( this );
- }
- void MeshRoad::_generateSlices()
- {
- if ( mNodes.size() < 2 )
- return;
- // Create the spline, initialized with the MeshRoadNode(s)
- U32 nodeCount = mNodes.size();
- MeshRoadSplineNode *splineNodes = new MeshRoadSplineNode[nodeCount];
- for ( U32 i = 0; i < nodeCount; i++ )
- {
- MeshRoadSplineNode &splineNode = splineNodes[i];
- const MeshRoadNode &node = mNodes[i];
- splineNode.x = node.point.x;
- splineNode.y = node.point.y;
- splineNode.z = node.point.z;
- splineNode.width = node.width;
- splineNode.depth = node.depth;
- splineNode.normal = node.normal;
- }
- CatmullRom<MeshRoadSplineNode> spline;
- spline.initialize( nodeCount, splineNodes );
- delete [] splineNodes;
- mSlices.clear();
-
- VectorF lastBreakVector(0,0,0);
- MeshRoadSlice slice;
- MeshRoadSplineNode lastBreakNode;
- lastBreakNode = spline.evaluate(0.0f);
- for ( U32 i = 1; i < mNodes.size(); i++ )
- {
- F32 t1 = spline.getTime(i);
- F32 t0 = spline.getTime(i-1);
-
- F32 segLength = spline.arcLength( t0, t1 );
- U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT );
- numSegments = getMax( numSegments, (U32)1 );
- F32 tstep = ( t1 - t0 ) / numSegments;
- U32 startIdx = 0;
- U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments;
- for ( U32 j = startIdx; j < endIdx; j++ )
- {
- F32 t = t0 + tstep * j;
- MeshRoadSplineNode splineNode = spline.evaluate(t);
- VectorF toNodeVec = splineNode.getPosition() - lastBreakNode.getPosition();
- toNodeVec.normalizeSafe();
- if ( lastBreakVector.isZero() )
- lastBreakVector = toNodeVec;
- F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) );
- if ( j == startIdx ||
- ( j == endIdx - 1 && i == mNodes.size() - 1 ) ||
- angle > mBreakAngle )
- {
- // Push back a spline node
- slice.p1.set( splineNode.x, splineNode.y, splineNode.z );
- slice.width = splineNode.width;
- slice.depth = splineNode.depth;
- slice.normal = splineNode.normal;
- slice.normal.normalize();
- slice.parentNodeIdx = i-1;
- slice.t = t;
- mSlices.push_back( slice );
- lastBreakVector = splineNode.getPosition() - lastBreakNode.getPosition();
- lastBreakVector.normalizeSafe();
- lastBreakNode = splineNode;
- }
- }
- }
-
- MatrixF mat(true);
- Box3F box;
- U32 lastProfileNode = mSideProfile.mNodes.size() - 1;
- F32 depth = mSideProfile.mNodes[lastProfileNode].getPosition().y;
- F32 bttmOffset = mSideProfile.mNodes[lastProfileNode].getPosition().x;
- for ( U32 i = 0; i < mSlices.size(); i++ )
- {
- // Calculate uvec, fvec, and rvec for all slices
- calcSliceTransform( i, mat );
- MeshRoadSlice *slicePtr = &mSlices[i];
- mat.getColumn( 0, &slicePtr->rvec );
- mat.getColumn( 1, &slicePtr->fvec );
- mat.getColumn( 2, &slicePtr->uvec );
- // Calculate p0/p2/pb0/pb2 for all slices
- slicePtr->p0 = slicePtr->p1 - slicePtr->rvec * slicePtr->width * 0.5f;
- slicePtr->p2 = slicePtr->p1 + slicePtr->rvec * slicePtr->width * 0.5f;
- slicePtr->pb0 = slicePtr->p0 + slicePtr->uvec * depth - slicePtr->rvec * bttmOffset;
- slicePtr->pb2 = slicePtr->p2 + slicePtr->uvec * depth + slicePtr->rvec * bttmOffset;
- // Generate or extend the object/world bounds
- if ( i == 0 )
- {
- box.minExtents = slicePtr->p0;
- box.maxExtents = slicePtr->p2;
- box.extend(slicePtr->pb0 );
- box.extend(slicePtr->pb2 );
- }
- else
- {
- box.extend(slicePtr->p0 );
- box.extend(slicePtr->p2 );
- box.extend(slicePtr->pb0 );
- box.extend(slicePtr->pb2 );
- }
- // Right side
- Point3F pos;
- VectorF norm;
- MatrixF profileMat1(true);
- profileMat1.setRow(0, slicePtr->rvec);
- profileMat1.setRow(1, slicePtr->uvec);
- profileMat1.setRow(2, -slicePtr->fvec);
- // Left side
- MatrixF profileMat2(true);
- profileMat2.setRow(0, -slicePtr->rvec);
- profileMat2.setRow(1, slicePtr->uvec);
- profileMat2.setRow(2, slicePtr->fvec);
- for(U32 i = 0; i < 2; i++)
- {
- if(i)
- mSideProfile.setTransform(profileMat2, slicePtr->p0);
- else
- mSideProfile.setTransform(profileMat1, slicePtr->p2);
- // Retain original per-node depth functionality
- if(mSideProfile.mNodes.size() == 2 && mSideProfile.mNodes[1].getPosition().y == -mSlices[0].depth)
- {
- mSideProfile.getNodeWorldPos(0, pos);
- slicePtr->verts.push_back(pos);
- box.extend( pos );
- pos.z -= slicePtr->depth;
- slicePtr->verts.push_back(pos);
- box.extend( pos );
- if(i)
- slicePtr->pb0 = pos;
- else
- slicePtr->pb2 = pos;
- mSideProfile.getNormToSlice(0, norm);
- slicePtr->norms.push_back(norm);
- mSideProfile.getNormToSlice(1, norm);
- slicePtr->norms.push_back(norm);
- }
- // New profile functionality
- else
- {
- for(U32 j = 0; j < mSideProfile.mNodes.size(); j++)
- {
- mSideProfile.getNodeWorldPos(j, pos);
- slicePtr->verts.push_back(pos);
- box.extend( pos );
- }
- for(U32 j = 0; j < mSideProfile.mNodeNormals.size(); j++)
- {
- mSideProfile.getNormToSlice(j, norm);
- slicePtr->norms.push_back(norm);
- }
- }
- }
- }
- mWorldBox = box;
- resetObjectBox();
- _generateSegments();
- }
- void MeshRoad::_generateSegments()
- {
- SAFE_DELETE( mPhysicsRep );
- mSegments.clear();
- for ( U32 i = 0; i < mSlices.size() - 1; i++ )
- {
- MeshRoadSegment seg( &mSlices[i], &mSlices[i+1], getWorldTransform() );
- mSegments.push_back( seg );
- }
- //mSideProfile.generateEndCap(mSlices[0].width);
- if ( isClientObject() )
- _generateVerts();
- if ( PHYSICSMGR )
- {
- ConcretePolyList polylist;
- if ( buildPolyList( PLC_Collision, &polylist, getWorldBox(), getWorldSphere() ) )
- {
- polylist.triangulate();
- PhysicsCollision *colShape = PHYSICSMGR->createCollision();
- colShape->addTriangleMesh( polylist.mVertexList.address(),
- polylist.mVertexList.size(),
- polylist.mIndexList.address(),
- polylist.mIndexList.size() / 3,
- MatrixF::Identity );
- PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
- mPhysicsRep = PHYSICSMGR->createBody();
- mPhysicsRep->init( colShape, 0, 0, this, world );
- }
- }
- }
- void MeshRoad::_generateVerts()
- {
- const U32 widthDivisions = getMax( 0, mWidthSubdivisions );
- const F32 divisionStep = 1.0f / (F32)( widthDivisions + 1 );
- const U32 sliceCount = mSlices.size();
- const U32 segmentCount = mSegments.size();
- U32 numProfSide, numProfTop, numProfBottom;
- numProfSide = numProfTop = numProfBottom = 0;
- // Find how many profile segments are set to side, top, and bottom materials
- for ( U32 i = 0; i < mSideProfile.mSegMtrls.size(); i++)
- {
- switch(mSideProfile.mSegMtrls[i])
- {
- case Side: numProfSide++; break;
- case Top: numProfTop++; break;
- case Bottom: numProfBottom++; break;
- }
- }
- F32 profLen = mSideProfile.getProfileLen();
- mVertCount[Top] = ( 2 + widthDivisions ) * sliceCount;
- mVertCount[Top] += sliceCount * numProfTop * 4;
- mTriangleCount[Top] = segmentCount * 2 * ( widthDivisions + 1 );
- mTriangleCount[Top] += segmentCount * numProfTop * 4;
- mVertCount[Bottom] = sliceCount * 2;
- mVertCount[Bottom] += sliceCount * numProfBottom * 4;
- mTriangleCount[Bottom] = segmentCount * 2;
- mTriangleCount[Bottom] += segmentCount * numProfBottom * 4;
- mVertCount[Side] = sliceCount * numProfSide * 4; // side verts
- mVertCount[Side] += mSideProfile.mNodes.size() * 4; // end cap verts
- mTriangleCount[Side] = segmentCount * numProfSide * 4; // side tris
- mTriangleCount[Side] += mSideProfile.mCap.getNumTris() * 2; // end cap tris
-
- // Calculate TexCoords for Slices
- F32 texCoordV = 0.0f;
- mSlices[0].texCoordV = 0.0f;
- for ( U32 i = 1; i < sliceCount; i++ )
- {
- MeshRoadSlice &slice = mSlices[i];
- MeshRoadSlice &prevSlice = mSlices[i-1];
-
- // Increment the textCoordV for the next slice.
- F32 len = ( slice.p1 - prevSlice.p1 ).len();
- texCoordV += len / mTextureLength;
- slice.texCoordV = texCoordV;
- }
- // Make Vertex Buffers
- GFXVertexPNTT *pVert = NULL;
- U32 vertCounter = 0;
- // Top Buffers...
- mVB[Top].set( GFX, mVertCount[Top], GFXBufferTypeStatic );
- pVert = mVB[Top].lock();
- vertCounter = 0;
-
- for ( U32 i = 0; i < sliceCount; i++ )
- {
- MeshRoadSlice &slice = mSlices[i];
-
- pVert->point = slice.p0;
- pVert->normal = slice.uvec;
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(1,slice.texCoordV);
- pVert++;
- vertCounter++;
- for ( U32 j = 0; j < widthDivisions; j++ )
- {
- const F32 t = divisionStep * (F32)( j + 1 );
- pVert->point.interpolate( slice.p0, slice.p2, t );
- pVert->normal = slice.uvec;
- pVert->tangent = slice.fvec;
- pVert->texCoord.set( 1.0f - t, slice.texCoordV );
- pVert++;
- vertCounter++;
- }
- pVert->point = slice.p2;
- pVert->normal = slice.uvec;
- pVert->tangent = slice.fvec;
- pVert->texCoord.set( 0, slice.texCoordV );
- pVert++;
- vertCounter++;
- }
- if(numProfTop)
- {
- for ( U32 i = 0; i < sliceCount; i++ )
- {
- MeshRoadSlice &slice = mSlices[i];
- // Right Side
- for ( U32 j = 0; j < mSideProfile.mNodes.size()-1; j++)
- {
- if(mSideProfile.mSegMtrls[j] == Top)
- {
- // Vertex 1
- pVert->point = slice.verts[j];
- pVert->normal = slice.norms[2*j];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- // Vertex 2
- pVert->point = slice.verts[j+1];
- pVert->normal = slice.norms[2*j+1];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- }
- }
- // Left Side
- for( U32 j = mSideProfile.mNodes.size(); j < 2*mSideProfile.mNodes.size()-1; j++)
- {
- if(mSideProfile.mSegMtrls[j-mSideProfile.mNodes.size()] == Top)
- {
- // Vertex 1
- pVert->point = slice.verts[j];
- pVert->normal = slice.norms[2*j-2];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- // Vertex 2
- pVert->point = slice.verts[j+1];
- pVert->normal = slice.norms[2*j-1];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- }
- }
- }
- }
- AssertFatal( vertCounter == mVertCount[Top], "MeshRoad, wrote incorrect number of verts in mVB[Top]!" );
- mVB[Top].unlock();
- // Bottom Buffer...
- mVB[Bottom].set( GFX, mVertCount[Bottom], GFXBufferTypeStatic );
- pVert = mVB[Bottom].lock();
- vertCounter = 0;
- for ( U32 i = 0; i < sliceCount; i++ )
- {
- MeshRoadSlice &slice = mSlices[i];
- pVert->point = slice.pb2;
- pVert->normal = -slice.uvec;
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(0,slice.texCoordV);
- pVert++;
- vertCounter++;
- pVert->point = slice.pb0;
- pVert->normal = -slice.uvec;
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(1,slice.texCoordV);
- pVert++;
- vertCounter++;
- }
- if(numProfBottom)
- {
- for ( U32 i = 0; i < sliceCount; i++ )
- {
- MeshRoadSlice &slice = mSlices[i];
- // Right Side
- for ( U32 j = 0; j < mSideProfile.mNodes.size()-1; j++)
- {
- if(mSideProfile.mSegMtrls[j] == Bottom)
- {
- // Vertex 1
- pVert->point = slice.verts[j];
- pVert->normal = slice.norms[2*j];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- // Vertex 2
- pVert->point = slice.verts[j+1];
- pVert->normal = slice.norms[2*j+1];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- }
- }
- // Left Side
- for( U32 j = mSideProfile.mNodes.size(); j < 2*mSideProfile.mNodes.size()-1; j++)
- {
- if(mSideProfile.mSegMtrls[j-mSideProfile.mNodes.size()] == Bottom)
- {
- // Vertex 1
- pVert->point = slice.verts[j];
- pVert->normal = slice.norms[2*j-2];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- // Vertex 2
- pVert->point = slice.verts[j+1];
- pVert->normal = slice.norms[2*j-1];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- }
- }
- }
- }
- AssertFatal( vertCounter == mVertCount[Bottom], "MeshRoad, wrote incorrect number of verts in mVB[Bottom]!" );
- mVB[Bottom].unlock();
- // Side Buffers...
- mVB[Side].set( GFX, mVertCount[Side], GFXBufferTypeStatic );
- pVert = mVB[Side].lock();
- vertCounter = 0;
- if(numProfSide)
- {
- for ( U32 i = 0; i < sliceCount; i++ )
- {
- MeshRoadSlice &slice = mSlices[i];
- // Right Side
- for( U32 j = 0; j < mSideProfile.mNodes.size()-1; j++)
- {
- if(mSideProfile.mSegMtrls[j] == Side)
- {
- // Segment Vertex 1
- pVert->point = slice.verts[j];
- pVert->normal = slice.norms[2*j];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- // Segment Vertex 2
- pVert->point = slice.verts[j+1];
- pVert->normal = slice.norms[2*j+1];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- }
- }
- // Left Side
- for( U32 j = mSideProfile.mNodes.size(); j < 2*mSideProfile.mNodes.size()-1; j++)
- {
- if(mSideProfile.mSegMtrls[j-mSideProfile.mNodes.size()] == Side)
- {
- // Segment Vertex 1
- pVert->point = slice.verts[j];
- pVert->normal = slice.norms[2*j-2];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- // Segment Vertex 2
- pVert->point = slice.verts[j+1];
- pVert->normal = slice.norms[2*j-1];
- pVert->tangent = slice.fvec;
- pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/mTextureLength,slice.texCoordV);
- pVert++;
- vertCounter++;
- }
- }
- }
- }
- // Cap verts
- Point3F pos;
- VectorF norm;
- VectorF tang;
- for( U32 i = 0; i < mSlices.size(); i += mSlices.size()-1)
- {
- MeshRoadSlice &slice = mSlices[i];
- // Back cap
- if(i)
- {
- norm = slice.fvec;
- tang = -slice.rvec;
- }
- // Front cap
- else
- {
- norm = -slice.fvec;
- tang = slice.rvec;
- }
- // Right side
- for( U32 j = 0; j < mSideProfile.mNodes.size(); j++)
- {
- pVert->point = slice.verts[j];
- pVert->normal = norm;
- pVert->tangent = tang;
- pos = mSideProfile.mNodes[j].getPosition();
- pVert->texCoord.set(pos.x/mTextureLength, pos.y/mTextureLength);
- pVert++;
- vertCounter++;
- }
- // Left side
- for( U32 j = 2*mSideProfile.mNodes.size()-1; j >= mSideProfile.mNodes.size(); j--)
- {
- pVert->point = slice.verts[j];
- pVert->normal = norm;
- pVert->tangent = tang;
- pos = mSideProfile.mNodes[j-mSideProfile.mNodes.size()].getPosition();
- pos.x = -pos.x - slice.width;
- pVert->texCoord.set(pos.x/mTextureLength, pos.y/mTextureLength);
- pVert++;
- vertCounter++;
- }
- }
- AssertFatal( vertCounter == mVertCount[Side], "MeshRoad, wrote incorrect number of verts in mVB[Side]!" );
- mVB[Side].unlock();
- // Make Primitive Buffers
- U32 p00, p01, p11, p10;
- U32 offset = 0;
- U16 *pIdx = NULL;
- U32 curIdx = 0;
- // Top Primitive Buffer
- mPB[Top].set( GFX, mTriangleCount[Top] * 3, mTriangleCount[Top], GFXBufferTypeStatic );
- mPB[Top].lock(&pIdx);
- curIdx = 0;
- offset = 0;
- const U32 rowStride = 2 + widthDivisions;
-
- for ( U32 i = 0; i < mSegments.size(); i++ )
- {
- for ( U32 j = 0; j < widthDivisions + 1; j++ )
- {
- p00 = offset;
- p10 = offset + 1;
- p01 = offset + rowStride;
- p11 = offset + rowStride + 1;
- pIdx[curIdx] = p00;
- curIdx++;
- pIdx[curIdx] = p01;
- curIdx++;
- pIdx[curIdx] = p11;
- curIdx++;
- pIdx[curIdx] = p00;
- curIdx++;
- pIdx[curIdx] = p11;
- curIdx++;
- pIdx[curIdx] = p10;
- curIdx++;
- offset += 1;
- }
- offset += 1;
- }
- offset += 2;
- if(numProfTop)
- {
- U32 nextSegOffset = 4 * numProfTop;
- for ( U32 i = 0; i < segmentCount; i++ )
- {
- // Loop through profile segments on right side
- for( U32 j = 0; j < numProfTop; j++)
- {
- // Profile Segment Face 1
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
- curIdx++;
- // Profile Segment Face 2
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- }
- // Loop through profile segments on left side
- for( U32 j = numProfTop; j < 2*numProfTop; j++)
- {
- // Profile Segment Face 1
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- // Profile Segment Face 2
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
- curIdx++;
- }
- }
- }
- AssertFatal( curIdx == mTriangleCount[Top] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Top]!" );
- mPB[Top].unlock();
- // Bottom Primitive Buffer
- mPB[Bottom].set( GFX, mTriangleCount[Bottom] * 3, mTriangleCount[Bottom], GFXBufferTypeStatic );
- mPB[Bottom].lock(&pIdx);
- curIdx = 0;
- offset = 0;
- for ( U32 i = 0; i < mSegments.size(); i++ )
- {
- p00 = offset;
- p10 = offset + 1;
- p01 = offset + 2;
- p11 = offset + 3;
- pIdx[curIdx] = p00;
- curIdx++;
- pIdx[curIdx] = p01;
- curIdx++;
- pIdx[curIdx] = p11;
- curIdx++;
- pIdx[curIdx] = p00;
- curIdx++;
- pIdx[curIdx] = p11;
- curIdx++;
- pIdx[curIdx] = p10;
- curIdx++;
- offset += 2;
- }
- offset += 2;
- if(numProfBottom)
- {
- U32 nextSegOffset = 4 * numProfBottom;
- for ( U32 i = 0; i < segmentCount; i++ )
- {
- // Loop through profile segments on right side
- for( U32 j = 0; j < numProfBottom; j++)
- {
- // Profile Segment Face 1
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
- curIdx++;
- // Profile Segment Face 2
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- }
- // Loop through profile segments on left side
- for( U32 j = numProfBottom; j < 2*numProfBottom; j++)
- {
- // Profile Segment Face 1
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- // Profile Segment Face 2
- pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
- curIdx++;
- pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
- curIdx++;
- }
- }
- }
- AssertFatal( curIdx == mTriangleCount[Bottom] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Bottom]!" );
- mPB[Bottom].unlock();
- // Side Primitive Buffer
- mPB[Side].set( GFX, mTriangleCount[Side] * 3, mTriangleCount[Side], GFXBufferTypeStatic );
- mPB[Side].lock(&pIdx);
- curIdx = 0;
- offset = 4 * numProfSide;
- if(numProfSide)
- {
- for ( U32 i = 0; i < mSegments.size(); i++ )
- {
- // Loop through profile segments on right side
- for( U32 j = 0; j < numProfSide; j++)
- {
- // Profile Segment Face 1
- pIdx[curIdx] = offset*i + 2*j;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + offset + 1;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + 1;
- curIdx++;
- // Profile Segment Face 2
- pIdx[curIdx] = offset*i + 2*j;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + offset;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + offset + 1;
- curIdx++;
- }
- // Loop through profile segments on left side
- for( U32 j = numProfSide; j < 2*numProfSide; j++)
- {
- // Profile Segment Face 1
- pIdx[curIdx] = offset*i + 2*j;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + 1;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + offset + 1;
- curIdx++;
- // Profile Segment Face 2
- pIdx[curIdx] = offset*i + 2*j;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + offset + 1;
- curIdx++;
- pIdx[curIdx] = offset*i + 2*j + offset;
- curIdx++;
- }
- }
- }
- // Cap the front
- offset = sliceCount * numProfSide * 4;
- for ( U32 i = 0; i < mSideProfile.mCap.getNumTris(); i++ )
- {
- pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 0) + offset;
- curIdx++;
- pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 1) + offset;
- curIdx++;
- pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 2) + offset;
- curIdx++;
- }
- // Cap the back
- offset += mSideProfile.mNodes.size() * 2;
- for ( U32 i = 0; i < mSideProfile.mCap.getNumTris(); i++ )
- {
- pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 2) + offset;
- curIdx++;
- pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 1) + offset;
- curIdx++;
- pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 0) + offset;
- curIdx++;
- }
- AssertFatal( curIdx == mTriangleCount[Side] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Side]!" );
- mPB[Side].unlock();
- }
- const MeshRoadNode& MeshRoad::getNode( U32 idx )
- {
- return mNodes[idx];
- }
- VectorF MeshRoad::getNodeNormal( U32 idx )
- {
- if ( mNodes.size() - 1 < idx )
- return VectorF::Zero;
- return mNodes[idx].normal;
- }
- void MeshRoad::setNodeNormal( U32 idx, const VectorF &normal )
- {
- if ( mNodes.size() - 1 < idx )
- return;
- mNodes[idx].normal = normal;
- regenerate();
- setMaskBits( NodeMask | RegenMask );
- }
- Point3F MeshRoad::getNodePosition( U32 idx )
- {
- if ( mNodes.size() - 1 < idx )
- return Point3F::Zero;
- return mNodes[idx].point;
- }
- void MeshRoad::setNodePosition( U32 idx, const Point3F &pos )
- {
- if ( mNodes.size() - 1 < idx )
- return;
- mNodes[idx].point = pos;
- regenerate();
- setMaskBits( NodeMask | RegenMask );
- }
- U32 MeshRoad::addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal )
- {
- U32 idx = _addNode( pos, width, depth, normal );
- regenerate();
- setMaskBits( NodeMask | RegenMask );
- return idx;
- }
- void MeshRoad::buildNodesFromList( MeshRoadNodeList* list )
- {
- mNodes.clear();
- for (U32 i=0; i<list->mPositions.size(); ++i)
- {
- _addNode( list->mPositions[i], list->mWidths[i], list->mDepths[i], list->mNormals[i] );
- }
- _regenerate();
- }
- U32 MeshRoad::insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
- {
- U32 ret = _insertNode( pos, width, depth, normal, idx );
- regenerate();
- setMaskBits( NodeMask | RegenMask );
- return ret;
- }
- void MeshRoad::setNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
- {
- if ( mNodes.size() - 1 < idx )
- return;
- MeshRoadNode &node = mNodes[idx];
- node.point = pos;
- node.width = width;
- node.depth = depth;
- node.normal = normal;
- regenerate();
- setMaskBits( NodeMask | RegenMask );
- }
- void MeshRoad::setNodeWidth( U32 idx, F32 meters )
- {
- meters = mClampF( meters, MIN_NODE_WIDTH, MAX_NODE_WIDTH );
- if ( mNodes.size() - 1 < idx )
- return;
- mNodes[idx].width = meters;
- _regenerate();
- setMaskBits( RegenMask | NodeMask );
- }
- F32 MeshRoad::getNodeWidth( U32 idx )
- {
- if ( mNodes.size() - 1 < idx )
- return -1.0f;
- return mNodes[idx].width;
- }
- void MeshRoad::setNodeDepth( U32 idx, F32 meters )
- {
- meters = mClampF( meters, MIN_NODE_DEPTH, MAX_NODE_DEPTH );
- if ( mNodes.size() - 1 < idx )
- return;
- mNodes[idx].depth = meters;
- _regenerate();
- setMaskBits( MeshRoadMask | RegenMask | NodeMask );
- }
- F32 MeshRoad::getNodeDepth( U32 idx )
- {
- if ( mNodes.size() - 1 < idx )
- return -1.0f;
- return mNodes[idx].depth;
- }
- MatrixF MeshRoad::getNodeTransform( U32 idx )
- {
- MatrixF mat(true);
- if ( mNodes.size() - 1 < idx )
- return mat;
- bool hasNext = idx + 1 < mNodes.size();
- bool hasPrev = (S32)idx - 1 > 0;
- const MeshRoadNode &node = mNodes[idx];
- VectorF fvec( 0, 1, 0 );
- if ( hasNext )
- {
- fvec = mNodes[idx+1].point - node.point;
- fvec.normalizeSafe();
- }
- else if ( hasPrev )
- {
- fvec = node.point - mNodes[idx-1].point;
- fvec.normalizeSafe();
- }
- else
- fvec = mPerp( node.normal );
- if ( fvec.isZero() )
- fvec = mPerp( node.normal );
- F32 dot = mDot( fvec, node.normal );
- if ( dot < -0.9f || dot > 0.9f )
- fvec = mPerp( node.normal );
- VectorF rvec = mCross( fvec, node.normal );
- if ( rvec.isZero() )
- rvec = mPerp( fvec );
- rvec.normalize();
- fvec = mCross( node.normal, rvec );
- fvec.normalize();
- mat.setColumn( 0, rvec );
- mat.setColumn( 1, fvec );
- mat.setColumn( 2, node.normal );
- mat.setColumn( 3, node.point );
- AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
- return mat;
- }
- void MeshRoad::calcSliceTransform( U32 idx, MatrixF &mat )
- {
- if ( mSlices.size() - 1 < idx )
- return;
- bool hasNext = idx + 1 < mSlices.size();
- bool hasPrev = (S32)idx - 1 >= 0;
- const MeshRoadSlice &slice = mSlices[idx];
- VectorF fvec( 0, 1, 0 );
- if ( hasNext )
- {
- fvec = mSlices[idx+1].p1 - slice.p1;
- fvec.normalizeSafe();
- }
- else if ( hasPrev )
- {
- fvec = slice.p1 - mSlices[idx-1].p1;
- fvec.normalizeSafe();
- }
- else
- fvec = mPerp( slice.normal );
- if ( fvec.isZero() )
- fvec = mPerp( slice.normal );
- F32 dot = mDot( fvec, slice.normal );
- if ( dot < -0.9f || dot > 0.9f )
- fvec = mPerp( slice.normal );
- VectorF rvec = mCross( fvec, slice.normal );
- if ( rvec.isZero() )
- rvec = mPerp( fvec );
- rvec.normalize();
- fvec = mCross( slice.normal, rvec );
- fvec.normalize();
- mat.setColumn( 0, rvec );
- mat.setColumn( 1, fvec );
- mat.setColumn( 2, slice.normal );
- mat.setColumn( 3, slice.p1 );
- AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
- }
- F32 MeshRoad::getRoadLength() const
- {
- F32 length = 0.0f;
- for ( U32 i = 0; i < mSegments.size(); i++ )
- {
- length += mSegments[i].length();
- }
- return length;
- }
- void MeshRoad::deleteNode( U32 idx )
- {
- if ( mNodes.size() - 1 < idx )
- return;
- mNodes.erase(idx);
- _regenerate();
- setMaskBits( RegenMask | NodeMask );
- }
- U32 MeshRoad::_addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal )
- {
- mNodes.increment();
- MeshRoadNode &node = mNodes.last();
- node.point = pos;
- node.width = width;
- node.depth = depth;
- node.normal = normal;
- setMaskBits( NodeMask | RegenMask );
- return mNodes.size() - 1;
- }
- U32 MeshRoad::_insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
- {
- U32 ret;
- MeshRoadNode *node;
- if ( idx == U32_MAX )
- {
- mNodes.increment();
- node = &mNodes.last();
- ret = mNodes.size() - 1;
- }
- else
- {
- mNodes.insert( idx );
- node = &mNodes[idx];
- ret = idx;
- }
- node->point = pos;
- node->depth = depth;
- node->width = width;
- node->normal = normal;
- return ret;
- }
- bool MeshRoad::collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx, Point3F *collisionPnt )
- {
- Point3F p0 = origin;
- Point3F p1 = origin + direction * 2000.0f;
- // If the line segment does not collide with the MeshRoad's world box,
- // it definitely does not collide with any part of the river.
- if ( !getWorldBox().collideLine( p0, p1 ) )
- return false;
- if ( mSlices.size() < 2 )
- return false;
- MathUtils::Quad quad;
- MathUtils::Ray ray;
- F32 t;
- // Check each road segment (formed by a pair of slices) for collision
- // with the line segment.
- for ( U32 i = 0; i < mSlices.size() - 1; i++ )
- {
- const MeshRoadSlice &slice0 = mSlices[i];
- const MeshRoadSlice &slice1 = mSlices[i+1];
- // For simplicities sake we will only test for collision between the
- // line segment and the Top face of the river segment.
- // Clockwise starting with the leftmost/closest point.
- quad.p00 = slice0.p0;
- quad.p01 = slice1.p0;
- quad.p11 = slice1.p2;
- quad.p10 = slice0.p2;
- ray.origin = origin;
- ray.direction = direction;
- if ( MathUtils::mRayQuadCollide( quad, ray, NULL, &t ) )
- {
- if ( nodeIdx )
- *nodeIdx = slice0.parentNodeIdx;
- if ( collisionPnt )
- *collisionPnt = ray.origin + ray.direction * t;
- return true;
- }
- }
- return false;
- }
- void MeshRoad::regenerate()
- {
- _regenerate();
- setMaskBits( RegenMask );
- }
- //-------------------------------------------------------------------------
- // Console Methods
- //-------------------------------------------------------------------------
- DefineEngineMethod( MeshRoad, setNodeDepth, void, ( S32 idx, F32 meters ),,
- "Intended as a helper to developers and editor scripts.\n"
- "Sets the depth in meters of a particular node."
- )
- {
- object->setNodeDepth( idx, meters );
- }
- DefineEngineMethod( MeshRoad, regenerate, void, (),,
- "Intended as a helper to developers and editor scripts.\n"
- "Force MeshRoad to recreate its geometry."
- )
- {
- object->regenerate();
- }
- DefineEngineMethod( MeshRoad, postApply, void, (),,
- "Intended as a helper to developers and editor scripts.\n"
- "Force trigger an inspectPostApply. This will transmit "
- "material and other fields ( not including nodes ) to client objects."
- )
- {
- object->inspectPostApply();
- }
- bool MeshRoad::buildPolyList_TopSurfaceOnly = false;
- bool MeshRoad::buildTopPolyList(PolyListContext plc, AbstractPolyList* polyList)
- {
- static Box3F box_prox; static SphereF ball_prox;
- buildPolyList_TopSurfaceOnly = true;
- bool result = buildPolyList(plc, polyList, box_prox, ball_prox);
- buildPolyList_TopSurfaceOnly = false;
- return result;
- }
|