1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- /*
- ===============================================================================
- Trace model vs. polygonal model collision detection.
- It is more important to minimize the number of collision polygons
- than it is to minimize the number of edges used for collision
- detection (total edges - internal edges).
- Stitching the world tends to minimize the number of edges used
- for collision detection (more internal edges). However stitching
- also results in more collision polygons which usually makes a
- stitched world slower.
- In an average map over 30% of all edges is internal.
- ===============================================================================
- */
- #pragma hdrstop
- #include "../idlib/precompiled.h"
- #include "CollisionModel_local.h"
- #define CMODEL_BINARYFILE_EXT "bcmodel"
- idCollisionModelManagerLocal collisionModelManagerLocal;
- idCollisionModelManager * collisionModelManager = &collisionModelManagerLocal;
- cm_windingList_t * cm_windingList;
- cm_windingList_t * cm_outList;
- cm_windingList_t * cm_tmpList;
- idHashIndex * cm_vertexHash;
- idHashIndex * cm_edgeHash;
- idBounds cm_modelBounds;
- int cm_vertexShift;
- idCVar preLoad_Collision( "preLoad_Collision", "1", CVAR_SYSTEM | CVAR_BOOL, "preload collision beginlevelload" );
- /*
- ===============================================================================
- Proc BSP tree for data pruning
- ===============================================================================
- */
- /*
- ================
- idCollisionModelManagerLocal::ParseProcNodes
- ================
- */
- void idCollisionModelManagerLocal::ParseProcNodes( idLexer *src ) {
- int i;
- src->ExpectTokenString( "{" );
- numProcNodes = src->ParseInt();
- if ( numProcNodes < 0 ) {
- src->Error( "ParseProcNodes: bad numProcNodes" );
- }
- procNodes = (cm_procNode_t *)Mem_ClearedAlloc( numProcNodes * sizeof( cm_procNode_t ), TAG_COLLISION );
- for ( i = 0; i < numProcNodes; i++ ) {
- cm_procNode_t *node;
- node = &procNodes[i];
- src->Parse1DMatrix( 4, node->plane.ToFloatPtr() );
- node->children[0] = src->ParseInt();
- node->children[1] = src->ParseInt();
- }
- src->ExpectTokenString( "}" );
- }
- /*
- ================
- idCollisionModelManagerLocal::LoadProcBSP
- FIXME: if the nodes would be at the start of the .proc file it would speed things up considerably
- ================
- */
- void idCollisionModelManagerLocal::LoadProcBSP( const char *name ) {
- idStr filename;
- idToken token;
- idLexer *src;
- // load it
- filename = name;
- filename.SetFileExtension( PROC_FILE_EXT );
- src = new (TAG_COLLISION) idLexer( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
- if ( !src->IsLoaded() ) {
- common->Warning( "idCollisionModelManagerLocal::LoadProcBSP: couldn't load %s", filename.c_str() );
- delete src;
- return;
- }
- if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) {
- common->Warning( "idCollisionModelManagerLocal::LoadProcBSP: bad id '%s' instead of '%s'", token.c_str(), PROC_FILE_ID );
- delete src;
- return;
- }
- // parse the file
- while ( 1 ) {
- if ( !src->ReadToken( &token ) ) {
- break;
- }
- if ( token == "model" ) {
- src->SkipBracedSection();
- continue;
- }
- if ( token == "shadowModel" ) {
- src->SkipBracedSection();
- continue;
- }
- if ( token == "interAreaPortals" ) {
- src->SkipBracedSection();
- continue;
- }
- if ( token == "nodes" ) {
- ParseProcNodes( src );
- break;
- }
- src->Error( "idCollisionModelManagerLocal::LoadProcBSP: bad token \"%s\"", token.c_str() );
- }
- delete src;
- }
- /*
- ===============================================================================
- Free map
- ===============================================================================
- */
- /*
- ================
- idCollisionModelManagerLocal::Clear
- ================
- */
- void idCollisionModelManagerLocal::Clear() {
- mapName.Clear();
- mapFileTime = 0;
- loaded = 0;
- checkCount = 0;
- maxModels = 0;
- numModels = 0;
- models = NULL;
- memset( trmPolygons, 0, sizeof( trmPolygons ) );
- trmBrushes[0] = NULL;
- trmMaterial = NULL;
- numProcNodes = 0;
- procNodes = NULL;
- getContacts = false;
- contacts = NULL;
- maxContacts = 0;
- numContacts = 0;
- }
- /*
- ================
- idCollisionModelManagerLocal::RemovePolygonReferences_r
- ================
- */
- void idCollisionModelManagerLocal::RemovePolygonReferences_r( cm_node_t *node, cm_polygon_t *p ) {
- cm_polygonRef_t *pref;
- while( node ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- if ( pref->p == p ) {
- pref->p = NULL;
- // cannot return here because we can have links down the tree due to polygon merging
- //return;
- }
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- if ( p->bounds[0][node->planeType] > node->planeDist ) {
- node = node->children[0];
- }
- else if ( p->bounds[1][node->planeType] < node->planeDist ) {
- node = node->children[1];
- }
- else {
- RemovePolygonReferences_r( node->children[1], p );
- node = node->children[0];
- }
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::RemoveBrushReferences_r
- ================
- */
- void idCollisionModelManagerLocal::RemoveBrushReferences_r( cm_node_t *node, cm_brush_t *b ) {
- cm_brushRef_t *bref;
- while( node ) {
- for ( bref = node->brushes; bref; bref = bref->next ) {
- if ( bref->b == b ) {
- bref->b = NULL;
- return;
- }
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- if ( b->bounds[0][node->planeType] > node->planeDist ) {
- node = node->children[0];
- }
- else if ( b->bounds[1][node->planeType] < node->planeDist ) {
- node = node->children[1];
- }
- else {
- RemoveBrushReferences_r( node->children[1], b );
- node = node->children[0];
- }
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::FreeNode
- ================
- */
- void idCollisionModelManagerLocal::FreeNode( cm_node_t *node ) {
- // don't free the node here
- // the nodes are allocated in blocks which are freed when the model is freed
- }
- /*
- ================
- idCollisionModelManagerLocal::FreePolygonReference
- ================
- */
- void idCollisionModelManagerLocal::FreePolygonReference( cm_polygonRef_t *pref ) {
- // don't free the polygon reference here
- // the polygon references are allocated in blocks which are freed when the model is freed
- }
- /*
- ================
- idCollisionModelManagerLocal::FreeBrushReference
- ================
- */
- void idCollisionModelManagerLocal::FreeBrushReference( cm_brushRef_t *bref ) {
- // don't free the brush reference here
- // the brush references are allocated in blocks which are freed when the model is freed
- }
- /*
- ================
- idCollisionModelManagerLocal::FreePolygon
- ================
- */
- void idCollisionModelManagerLocal::FreePolygon( cm_model_t *model, cm_polygon_t *poly ) {
- model->numPolygons--;
- model->polygonMemory -= sizeof( cm_polygon_t ) + ( poly->numEdges - 1 ) * sizeof( poly->edges[0] );
- if ( model->polygonBlock == NULL ) {
- Mem_Free( poly );
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::FreeBrush
- ================
- */
- void idCollisionModelManagerLocal::FreeBrush( cm_model_t *model, cm_brush_t *brush ) {
- model->numBrushes--;
- model->brushMemory -= sizeof( cm_brush_t ) + ( brush->numPlanes - 1 ) * sizeof( brush->planes[0] );
- if ( model->brushBlock == NULL ) {
- Mem_Free( brush );
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::FreeTree_r
- ================
- */
- void idCollisionModelManagerLocal::FreeTree_r( cm_model_t *model, cm_node_t *headNode, cm_node_t *node ) {
- cm_polygonRef_t *pref;
- cm_polygon_t *p;
- cm_brushRef_t *bref;
- cm_brush_t *b;
- // free all polygons at this node
- for ( pref = node->polygons; pref; pref = node->polygons ) {
- p = pref->p;
- if ( p ) {
- // remove all other references to this polygon
- RemovePolygonReferences_r( headNode, p );
- FreePolygon( model, p );
- }
- node->polygons = pref->next;
- FreePolygonReference( pref );
- }
- // free all brushes at this node
- for ( bref = node->brushes; bref; bref = node->brushes ) {
- b = bref->b;
- if ( b ) {
- // remove all other references to this brush
- RemoveBrushReferences_r( headNode, b );
- FreeBrush( model, b );
- }
- node->brushes = bref->next;
- FreeBrushReference( bref );
- }
- // recurse down the tree
- if ( node->planeType != -1 ) {
- FreeTree_r( model, headNode, node->children[0] );
- node->children[0] = NULL;
- FreeTree_r( model, headNode, node->children[1] );
- node->children[1] = NULL;
- }
- FreeNode( node );
- }
- /*
- ================
- idCollisionModelManagerLocal::FreeModel
- ================
- */
- void idCollisionModelManagerLocal::FreeModel( cm_model_t *model ) {
- cm_polygonRefBlock_t *polygonRefBlock, *nextPolygonRefBlock;
- cm_brushRefBlock_t *brushRefBlock, *nextBrushRefBlock;
- cm_nodeBlock_t *nodeBlock, *nextNodeBlock;
- // free the tree structure
- if ( model->node ) {
- FreeTree_r( model, model->node, model->node );
- }
- // free blocks with polygon references
- for ( polygonRefBlock = model->polygonRefBlocks; polygonRefBlock; polygonRefBlock = nextPolygonRefBlock ) {
- nextPolygonRefBlock = polygonRefBlock->next;
- Mem_Free( polygonRefBlock );
- }
- // free blocks with brush references
- for ( brushRefBlock = model->brushRefBlocks; brushRefBlock; brushRefBlock = nextBrushRefBlock ) {
- nextBrushRefBlock = brushRefBlock->next;
- Mem_Free( brushRefBlock );
- }
- // free blocks with nodes
- for ( nodeBlock = model->nodeBlocks; nodeBlock; nodeBlock = nextNodeBlock ) {
- nextNodeBlock = nodeBlock->next;
- Mem_Free( nodeBlock );
- }
- // free block allocated polygons
- Mem_Free( model->polygonBlock );
- // free block allocated brushes
- Mem_Free( model->brushBlock );
- // free edges
- Mem_Free( model->edges );
- // free vertices
- Mem_Free( model->vertices );
- // free the model
- delete model;
- }
- /*
- ================
- idCollisionModelManagerLocal::FreeMap
- ================
- */
- void idCollisionModelManagerLocal::FreeMap() {
- int i;
- if ( !loaded ) {
- Clear();
- return;
- }
- for ( i = 0; i < maxModels; i++ ) {
- if ( !models[i] ) {
- continue;
- }
- FreeModel( models[i] );
- }
- FreeTrmModelStructure();
- Mem_Free( models );
- Clear();
- ShutdownHash();
- }
- /*
- ================
- idCollisionModelManagerLocal::FreeTrmModelStructure
- ================
- */
- void idCollisionModelManagerLocal::FreeTrmModelStructure() {
- int i;
- assert( models );
- if ( !models[MAX_SUBMODELS] ) {
- return;
- }
- for ( i = 0; i < MAX_TRACEMODEL_POLYS; i++ ) {
- FreePolygon( models[MAX_SUBMODELS], trmPolygons[i]->p );
- }
- FreeBrush( models[MAX_SUBMODELS], trmBrushes[0]->b );
- models[MAX_SUBMODELS]->node->polygons = NULL;
- models[MAX_SUBMODELS]->node->brushes = NULL;
- FreeModel( models[MAX_SUBMODELS] );
- }
- /*
- ===============================================================================
- Edge normals
- ===============================================================================
- */
- /*
- ================
- idCollisionModelManagerLocal::CalculateEdgeNormals
- ================
- */
- #define SHARP_EDGE_DOT -0.7f
- void idCollisionModelManagerLocal::CalculateEdgeNormals( cm_model_t *model, cm_node_t *node ) {
- cm_polygonRef_t *pref;
- cm_polygon_t *p;
- cm_edge_t *edge;
- float dot, s;
- int i, edgeNum;
- idVec3 dir;
- while( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- p = pref->p;
- // if we checked this polygon already
- if ( p->checkcount == checkCount ) {
- continue;
- }
- p->checkcount = checkCount;
- for ( i = 0; i < p->numEdges; i++ ) {
- edgeNum = p->edges[i];
- edge = model->edges + abs( edgeNum );
- if ( edge->normal[0] == 0.0f && edge->normal[1] == 0.0f && edge->normal[2] == 0.0f ) {
- // if the edge is only used by this polygon
- if ( edge->numUsers == 1 ) {
- dir = model->vertices[ edge->vertexNum[edgeNum < 0]].p - model->vertices[ edge->vertexNum[edgeNum > 0]].p;
- edge->normal = p->plane.Normal().Cross( dir );
- edge->normal.Normalize();
- } else {
- // the edge is used by more than one polygon
- edge->normal = p->plane.Normal();
- }
- } else {
- dot = edge->normal * p->plane.Normal();
- // if the two planes make a very sharp edge
- if ( dot < SHARP_EDGE_DOT ) {
- // max length normal pointing outside both polygons
- dir = model->vertices[ edge->vertexNum[edgeNum > 0]].p - model->vertices[ edge->vertexNum[edgeNum < 0]].p;
- edge->normal = edge->normal.Cross( dir ) + p->plane.Normal().Cross( -dir );
- edge->normal *= ( 0.5f / ( 0.5f + 0.5f * SHARP_EDGE_DOT ) ) / edge->normal.Length();
- model->numSharpEdges++;
- } else {
- s = 0.5f / ( 0.5f + 0.5f * dot );
- edge->normal = s * ( edge->normal + p->plane.Normal() );
- }
- }
- }
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- CalculateEdgeNormals( model, node->children[1] );
- node = node->children[0];
- }
- }
- /*
- ===============================================================================
- Trace model to general collision model
- ===============================================================================
- */
- /*
- ================
- idCollisionModelManagerLocal::AllocModel
- ================
- */
- cm_model_t *idCollisionModelManagerLocal::AllocModel() {
- cm_model_t *model;
- model = new (TAG_COLLISION) cm_model_t;
- model->contents = 0;
- model->isConvex = false;
- model->maxVertices = 0;
- model->numVertices = 0;
- model->vertices = NULL;
- model->maxEdges = 0;
- model->numEdges = 0;
- model->edges= NULL;
- model->node = NULL;
- model->nodeBlocks = NULL;
- model->polygonRefBlocks = NULL;
- model->brushRefBlocks = NULL;
- model->polygonBlock = NULL;
- model->brushBlock = NULL;
- model->numPolygons = model->polygonMemory =
- model->numBrushes = model->brushMemory =
- model->numNodes = model->numBrushRefs =
- model->numPolygonRefs = model->numInternalEdges =
- model->numSharpEdges = model->numRemovedPolys =
- model->numMergedPolys = model->usedMemory = 0;
- return model;
- }
- /*
- ================
- idCollisionModelManagerLocal::AllocNode
- ================
- */
- cm_node_t *idCollisionModelManagerLocal::AllocNode( cm_model_t *model, int blockSize ) {
- int i;
- cm_node_t *node;
- cm_nodeBlock_t *nodeBlock;
- if ( !model->nodeBlocks || !model->nodeBlocks->nextNode ) {
- nodeBlock = (cm_nodeBlock_t *) Mem_ClearedAlloc( sizeof( cm_nodeBlock_t ) + blockSize * sizeof(cm_node_t), TAG_COLLISION );
- nodeBlock->nextNode = (cm_node_t *) ( ( (byte *) nodeBlock ) + sizeof( cm_nodeBlock_t ) );
- nodeBlock->next = model->nodeBlocks;
- model->nodeBlocks = nodeBlock;
- node = nodeBlock->nextNode;
- for ( i = 0; i < blockSize - 1; i++ ) {
- node->parent = node + 1;
- node = node->parent;
- }
- node->parent = NULL;
- }
- node = model->nodeBlocks->nextNode;
- model->nodeBlocks->nextNode = node->parent;
- node->parent = NULL;
- return node;
- }
- /*
- ================
- idCollisionModelManagerLocal::AllocPolygonReference
- ================
- */
- cm_polygonRef_t *idCollisionModelManagerLocal::AllocPolygonReference( cm_model_t *model, int blockSize ) {
- int i;
- cm_polygonRef_t *pref;
- cm_polygonRefBlock_t *prefBlock;
- if ( !model->polygonRefBlocks || !model->polygonRefBlocks->nextRef ) {
- prefBlock = (cm_polygonRefBlock_t *) Mem_ClearedAlloc( sizeof( cm_polygonRefBlock_t ) + blockSize * sizeof(cm_polygonRef_t), TAG_COLLISION );
- prefBlock->nextRef = (cm_polygonRef_t *) ( ( (byte *) prefBlock ) + sizeof( cm_polygonRefBlock_t ) );
- prefBlock->next = model->polygonRefBlocks;
- model->polygonRefBlocks = prefBlock;
- pref = prefBlock->nextRef;
- for ( i = 0; i < blockSize - 1; i++ ) {
- pref->next = pref + 1;
- pref = pref->next;
- }
- pref->next = NULL;
- }
- pref = model->polygonRefBlocks->nextRef;
- model->polygonRefBlocks->nextRef = pref->next;
- return pref;
- }
- /*
- ================
- idCollisionModelManagerLocal::AllocBrushReference
- ================
- */
- cm_brushRef_t *idCollisionModelManagerLocal::AllocBrushReference( cm_model_t *model, int blockSize ) {
- int i;
- cm_brushRef_t *bref;
- cm_brushRefBlock_t *brefBlock;
- if ( !model->brushRefBlocks || !model->brushRefBlocks->nextRef ) {
- brefBlock = (cm_brushRefBlock_t *) Mem_ClearedAlloc( sizeof(cm_brushRefBlock_t) + blockSize * sizeof(cm_brushRef_t), TAG_COLLISION );
- brefBlock->nextRef = (cm_brushRef_t *) ( ( (byte *) brefBlock ) + sizeof(cm_brushRefBlock_t) );
- brefBlock->next = model->brushRefBlocks;
- model->brushRefBlocks = brefBlock;
- bref = brefBlock->nextRef;
- for ( i = 0; i < blockSize - 1; i++ ) {
- bref->next = bref + 1;
- bref = bref->next;
- }
- bref->next = NULL;
- }
- bref = model->brushRefBlocks->nextRef;
- model->brushRefBlocks->nextRef = bref->next;
- return bref;
- }
- /*
- ================
- idCollisionModelManagerLocal::AllocPolygon
- ================
- */
- cm_polygon_t *idCollisionModelManagerLocal::AllocPolygon( cm_model_t *model, int numEdges ) {
- cm_polygon_t *poly;
- int size;
- size = sizeof( cm_polygon_t ) + ( numEdges - 1 ) * sizeof( poly->edges[0] );
- model->numPolygons++;
- model->polygonMemory += size;
- if ( model->polygonBlock && model->polygonBlock->bytesRemaining >= size ) {
- poly = (cm_polygon_t *) model->polygonBlock->next;
- model->polygonBlock->next += size;
- model->polygonBlock->bytesRemaining -= size;
- } else {
- poly = (cm_polygon_t *) Mem_ClearedAlloc( size, TAG_COLLISION );
- }
- return poly;
- }
- /*
- ================
- idCollisionModelManagerLocal::AllocBrush
- ================
- */
- cm_brush_t *idCollisionModelManagerLocal::AllocBrush( cm_model_t *model, int numPlanes ) {
- cm_brush_t *brush;
- int size;
- size = sizeof( cm_brush_t ) + ( numPlanes - 1 ) * sizeof( brush->planes[0] );
- model->numBrushes++;
- model->brushMemory += size;
- if ( model->brushBlock && model->brushBlock->bytesRemaining >= size ) {
- brush = (cm_brush_t *) model->brushBlock->next;
- model->brushBlock->next += size;
- model->brushBlock->bytesRemaining -= size;
- } else {
- brush = (cm_brush_t *) Mem_ClearedAlloc( size, TAG_COLLISION );
- }
- return brush;
- }
- /*
- ================
- idCollisionModelManagerLocal::AddPolygonToNode
- ================
- */
- void idCollisionModelManagerLocal::AddPolygonToNode( cm_model_t *model, cm_node_t *node, cm_polygon_t *p ) {
- cm_polygonRef_t *pref;
- pref = AllocPolygonReference( model, model->numPolygonRefs < REFERENCE_BLOCK_SIZE_SMALL ? REFERENCE_BLOCK_SIZE_SMALL : REFERENCE_BLOCK_SIZE_LARGE );
- pref->p = p;
- pref->next = node->polygons;
- node->polygons = pref;
- model->numPolygonRefs++;
- }
- /*
- ================
- idCollisionModelManagerLocal::AddBrushToNode
- ================
- */
- void idCollisionModelManagerLocal::AddBrushToNode( cm_model_t *model, cm_node_t *node, cm_brush_t *b ) {
- cm_brushRef_t *bref;
- bref = AllocBrushReference( model, model->numBrushRefs < REFERENCE_BLOCK_SIZE_SMALL ? REFERENCE_BLOCK_SIZE_SMALL : REFERENCE_BLOCK_SIZE_LARGE );
- bref->b = b;
- bref->next = node->brushes;
- node->brushes = bref;
- model->numBrushRefs++;
- }
- /*
- ================
- idCollisionModelManagerLocal::SetupTrmModelStructure
- ================
- */
- void idCollisionModelManagerLocal::SetupTrmModelStructure() {
- int i;
- cm_node_t *node;
- cm_model_t *model;
- // setup model
- model = AllocModel();
- assert( models );
- models[MAX_SUBMODELS] = model;
- // create node to hold the collision data
- node = (cm_node_t *) AllocNode( model, 1 );
- node->planeType = -1;
- model->node = node;
- // allocate vertex and edge arrays
- model->numVertices = 0;
- model->maxVertices = MAX_TRACEMODEL_VERTS;
- model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t), TAG_COLLISION );
- model->numEdges = 0;
- model->maxEdges = MAX_TRACEMODEL_EDGES+1;
- model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t), TAG_COLLISION );
- // create a material for the trace model polygons
- trmMaterial = declManager->FindMaterial( "_tracemodel", false );
- if ( !trmMaterial ) {
- common->FatalError( "_tracemodel material not found" );
- }
- // allocate polygons
- for ( i = 0; i < MAX_TRACEMODEL_POLYS; i++ ) {
- trmPolygons[i] = AllocPolygonReference( model, MAX_TRACEMODEL_POLYS );
- trmPolygons[i]->p = AllocPolygon( model, MAX_TRACEMODEL_POLYEDGES );
- trmPolygons[i]->p->bounds.Clear();
- trmPolygons[i]->p->plane.Zero();
- trmPolygons[i]->p->checkcount = 0;
- trmPolygons[i]->p->contents = -1; // all contents
- trmPolygons[i]->p->material = trmMaterial;
- trmPolygons[i]->p->numEdges = 0;
- }
- // allocate brush for position test
- trmBrushes[0] = AllocBrushReference( model, 1 );
- trmBrushes[0]->b = AllocBrush( model, MAX_TRACEMODEL_POLYS );
- trmBrushes[0]->b->primitiveNum = 0;
- trmBrushes[0]->b->bounds.Clear();
- trmBrushes[0]->b->checkcount = 0;
- trmBrushes[0]->b->contents = -1; // all contents
- trmBrushes[ 0 ]->b->material = trmMaterial;
- trmBrushes[0]->b->numPlanes = 0;
- }
- /*
- ================
- idCollisionModelManagerLocal::SetupTrmModel
- Trace models (item boxes, etc) are converted to collision models on the fly, using the last model slot
- as a reusable temporary buffer
- ================
- */
- cmHandle_t idCollisionModelManagerLocal::SetupTrmModel( const idTraceModel &trm, const idMaterial *material ) {
- int i, j;
- cm_vertex_t *vertex;
- cm_edge_t *edge;
- cm_polygon_t *poly;
- cm_model_t *model;
- const traceModelVert_t *trmVert;
- const traceModelEdge_t *trmEdge;
- const traceModelPoly_t *trmPoly;
- assert( models );
- if ( material == NULL ) {
- material = trmMaterial;
- }
- model = models[MAX_SUBMODELS];
- model->node->brushes = NULL;
- model->node->polygons = NULL;
- // if not a valid trace model
- if ( trm.type == TRM_INVALID || !trm.numPolys ) {
- return TRACE_MODEL_HANDLE;
- }
- // vertices
- model->numVertices = trm.numVerts;
- vertex = model->vertices;
- trmVert = trm.verts;
- for ( i = 0; i < trm.numVerts; i++, vertex++, trmVert++ ) {
- vertex->p = *trmVert;
- vertex->sideSet = 0;
- }
- // edges
- model->numEdges = trm.numEdges;
- edge = model->edges + 1;
- trmEdge = trm.edges + 1;
- for ( i = 0; i < trm.numEdges; i++, edge++, trmEdge++ ) {
- edge->vertexNum[0] = trmEdge->v[0];
- edge->vertexNum[1] = trmEdge->v[1];
- edge->normal = trmEdge->normal;
- edge->internal = false;
- edge->sideSet = 0;
- }
- // polygons
- model->numPolygons = trm.numPolys;
- trmPoly = trm.polys;
- for ( i = 0; i < trm.numPolys; i++, trmPoly++ ) {
- poly = trmPolygons[i]->p;
- poly->numEdges = trmPoly->numEdges;
- for ( j = 0; j < trmPoly->numEdges; j++ ) {
- poly->edges[j] = trmPoly->edges[j];
- }
- poly->plane.SetNormal( trmPoly->normal );
- poly->plane.SetDist( trmPoly->dist );
- poly->bounds = trmPoly->bounds;
- poly->material = material;
- // link polygon at node
- trmPolygons[i]->next = model->node->polygons;
- model->node->polygons = trmPolygons[i];
- }
- // if the trace model is convex
- if ( trm.isConvex ) {
- // setup brush for position test
- trmBrushes[0]->b->numPlanes = trm.numPolys;
- for ( i = 0; i < trm.numPolys; i++ ) {
- trmBrushes[0]->b->planes[i] = trmPolygons[i]->p->plane;
- }
- trmBrushes[0]->b->bounds = trm.bounds;
- // link brush at node
- trmBrushes[0]->next = model->node->brushes;
- trmBrushes[ 0 ]->b->material = material;
- model->node->brushes = trmBrushes[0];
- }
- // model bounds
- model->bounds = trm.bounds;
- // convex
- model->isConvex = trm.isConvex;
- return TRACE_MODEL_HANDLE;
- }
- /*
- ===============================================================================
- Optimisation, removal of polygons contained within brushes or solid
- ===============================================================================
- */
- /*
- ============
- idCollisionModelManagerLocal::R_ChoppedAwayByProcBSP
- ============
- */
- int idCollisionModelManagerLocal::R_ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius ) {
- int res;
- idFixedWinding back;
- cm_procNode_t *node;
- float dist;
- do {
- node = procNodes + nodeNum;
- dist = node->plane.Normal() * origin + node->plane[3];
- if ( dist > radius ) {
- res = SIDE_FRONT;
- }
- else if ( dist < -radius ) {
- res = SIDE_BACK;
- }
- else {
- res = w->Split( &back, node->plane, CHOP_EPSILON );
- }
- if ( res == SIDE_FRONT ) {
- nodeNum = node->children[0];
- }
- else if ( res == SIDE_BACK ) {
- nodeNum = node->children[1];
- }
- else if ( res == SIDE_ON ) {
- // continue with the side the winding faces
- if ( node->plane.Normal() * normal > 0.0f ) {
- nodeNum = node->children[0];
- }
- else {
- nodeNum = node->children[1];
- }
- }
- else {
- // if either node is not solid
- if ( node->children[0] < 0 || node->children[1] < 0 ) {
- return false;
- }
- // only recurse if the node is not solid
- if ( node->children[1] > 0 ) {
- if ( !R_ChoppedAwayByProcBSP( node->children[1], &back, normal, origin, radius ) ) {
- return false;
- }
- }
- nodeNum = node->children[0];
- }
- } while ( nodeNum > 0 );
- if ( nodeNum < 0 ) {
- return false;
- }
- return true;
- }
- /*
- ============
- idCollisionModelManagerLocal::ChoppedAwayByProcBSP
- ============
- */
- int idCollisionModelManagerLocal::ChoppedAwayByProcBSP( const idFixedWinding &w, const idPlane &plane, int contents ) {
- idFixedWinding neww;
- idBounds bounds;
- float radius;
- idVec3 origin;
- // if the .proc file has no BSP tree
- if ( procNodes == NULL ) {
- return false;
- }
- // don't chop if the polygon is not solid
- if ( !(contents & CONTENTS_SOLID) ) {
- return false;
- }
- // make a local copy of the winding
- neww = w;
- neww.GetBounds( bounds );
- origin = (bounds[1] - bounds[0]) * 0.5f;
- radius = origin.Length() + CHOP_EPSILON;
- origin = bounds[0] + origin;
- //
- return R_ChoppedAwayByProcBSP( 0, &neww, plane.Normal(), origin, radius );
- }
- /*
- =============
- idCollisionModelManagerLocal::ChopWindingWithBrush
- returns the least number of winding fragments outside the brush
- =============
- */
- void idCollisionModelManagerLocal::ChopWindingListWithBrush( cm_windingList_t *list, cm_brush_t *b ) {
- int i, k, res, startPlane, planeNum, bestNumWindings;
- idFixedWinding back, front;
- idPlane plane;
- bool chopped;
- int sidedness[MAX_POINTS_ON_WINDING];
- float dist;
- if ( b->numPlanes > MAX_POINTS_ON_WINDING ) {
- return;
- }
- // get sidedness for the list of windings
- for ( i = 0; i < b->numPlanes; i++ ) {
- plane = -b->planes[i];
- dist = plane.Distance( list->origin );
- if ( dist > list->radius ) {
- sidedness[i] = SIDE_FRONT;
- }
- else if ( dist < -list->radius ) {
- sidedness[i] = SIDE_BACK;
- }
- else {
- sidedness[i] = list->bounds.PlaneSide( plane );
- if ( sidedness[i] == PLANESIDE_FRONT ) {
- sidedness[i] = SIDE_FRONT;
- }
- else if ( sidedness[i] == PLANESIDE_BACK ) {
- sidedness[i] = SIDE_BACK;
- }
- else {
- sidedness[i] = SIDE_CROSS;
- }
- }
- }
- cm_outList->numWindings = 0;
- for ( k = 0; k < list->numWindings; k++ ) {
- //
- startPlane = 0;
- bestNumWindings = 1 + b->numPlanes;
- chopped = false;
- do {
- front = list->w[k];
- cm_tmpList->numWindings = 0;
- for ( planeNum = startPlane, i = 0; i < b->numPlanes; i++, planeNum++ ) {
- if ( planeNum >= b->numPlanes ) {
- planeNum = 0;
- }
- res = sidedness[planeNum];
- if ( res == SIDE_CROSS ) {
- plane = -b->planes[planeNum];
- res = front.Split( &back, plane, CHOP_EPSILON );
- }
- // NOTE: disabling this can create gaps at places where Z-fighting occurs
- // Z-fighting should not occur but what if there is a decal brush side
- // with exactly the same size as another brush side ?
- // only leave windings on a brush if the winding plane and brush side plane face the same direction
- if ( res == SIDE_ON && list->primitiveNum >= 0 && (list->normal * b->planes[planeNum].Normal()) > 0 ) {
- // return because all windings in the list will be on this brush side plane
- return;
- }
- if ( res == SIDE_BACK ) {
- if ( cm_outList->numWindings >= MAX_WINDING_LIST ) {
- common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST );
- return;
- }
- // winding and brush didn't intersect, store the original winding
- cm_outList->w[cm_outList->numWindings] = list->w[k];
- cm_outList->numWindings++;
- chopped = false;
- break;
- }
- if ( res == SIDE_CROSS ) {
- if ( cm_tmpList->numWindings >= MAX_WINDING_LIST ) {
- common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST );
- return;
- }
- // store the front winding in the temporary list
- cm_tmpList->w[cm_tmpList->numWindings] = back;
- cm_tmpList->numWindings++;
- chopped = true;
- }
- // if already found a start plane which generates less fragments
- if ( cm_tmpList->numWindings >= bestNumWindings ) {
- break;
- }
- }
- // find the best start plane to get the least number of fragments outside the brush
- if ( cm_tmpList->numWindings < bestNumWindings ) {
- bestNumWindings = cm_tmpList->numWindings;
- // store windings from temporary list in the out list
- for ( i = 0; i < cm_tmpList->numWindings; i++ ) {
- if ( cm_outList->numWindings + i >= MAX_WINDING_LIST ) {
- common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST );
- return;
- }
- cm_outList->w[cm_outList->numWindings+i] = cm_tmpList->w[i];
- }
- // if only one winding left then we can't do any better
- if ( bestNumWindings == 1 ) {
- break;
- }
- }
- // try the next start plane
- startPlane++;
- } while ( chopped && startPlane < b->numPlanes );
- //
- if ( chopped ) {
- cm_outList->numWindings += bestNumWindings;
- }
- }
- for ( k = 0; k < cm_outList->numWindings; k++ ) {
- list->w[k] = cm_outList->w[k];
- }
- list->numWindings = cm_outList->numWindings;
- }
- /*
- ============
- idCollisionModelManagerLocal::R_ChopWindingListWithTreeBrushes
- ============
- */
- void idCollisionModelManagerLocal::R_ChopWindingListWithTreeBrushes( cm_windingList_t *list, cm_node_t *node ) {
- int i;
- cm_brushRef_t *bref;
- cm_brush_t *b;
- while( 1 ) {
- for ( bref = node->brushes; bref; bref = bref->next ) {
- b = bref->b;
- // if we checked this brush already
- if ( b->checkcount == checkCount ) {
- continue;
- }
- b->checkcount = checkCount;
- // if the windings in the list originate from this brush
- if ( b->primitiveNum == list->primitiveNum ) {
- continue;
- }
- // if brush has a different contents
- if ( b->contents != list->contents ) {
- continue;
- }
- // brush bounds and winding list bounds should overlap
- for ( i = 0; i < 3; i++ ) {
- if ( list->bounds[0][i] > b->bounds[1][i] ) {
- break;
- }
- if ( list->bounds[1][i] < b->bounds[0][i] ) {
- break;
- }
- }
- if ( i < 3 ) {
- continue;
- }
- // chop windings in the list with brush
- ChopWindingListWithBrush( list, b );
- // if all windings are chopped away we're done
- if ( !list->numWindings ) {
- return;
- }
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- if ( list->bounds[0][node->planeType] > node->planeDist ) {
- node = node->children[0];
- }
- else if ( list->bounds[1][node->planeType] < node->planeDist ) {
- node = node->children[1];
- }
- else {
- R_ChopWindingListWithTreeBrushes( list, node->children[1] );
- if ( !list->numWindings ) {
- return;
- }
- node = node->children[0];
- }
- }
- }
- /*
- ============
- idCollisionModelManagerLocal::WindingOutsideBrushes
- Returns one winding which is not fully contained in brushes.
- We always favor less polygons over a stitched world.
- If the winding is partly contained and the contained pieces can be chopped off
- without creating multiple winding fragments then the chopped winding is returned.
- ============
- */
- idFixedWinding *idCollisionModelManagerLocal::WindingOutsideBrushes( idFixedWinding *w, const idPlane &plane, int contents, int primitiveNum, cm_node_t *headNode ) {
- int i, windingLeft;
- cm_windingList->bounds.Clear();
- for ( i = 0; i < w->GetNumPoints(); i++ ) {
- cm_windingList->bounds.AddPoint( (*w)[i].ToVec3() );
- }
- cm_windingList->origin = (cm_windingList->bounds[1] - cm_windingList->bounds[0]) * 0.5;
- cm_windingList->radius = cm_windingList->origin.Length() + CHOP_EPSILON;
- cm_windingList->origin = cm_windingList->bounds[0] + cm_windingList->origin;
- cm_windingList->bounds[0] -= idVec3( CHOP_EPSILON, CHOP_EPSILON, CHOP_EPSILON );
- cm_windingList->bounds[1] += idVec3( CHOP_EPSILON, CHOP_EPSILON, CHOP_EPSILON );
- cm_windingList->w[0] = *w;
- cm_windingList->numWindings = 1;
- cm_windingList->normal = plane.Normal();
- cm_windingList->contents = contents;
- cm_windingList->primitiveNum = primitiveNum;
- //
- checkCount++;
- R_ChopWindingListWithTreeBrushes( cm_windingList, headNode );
- //
- if ( !cm_windingList->numWindings ) {
- return NULL;
- }
- if ( cm_windingList->numWindings == 1 ) {
- return &cm_windingList->w[0];
- }
- // if not the world model
- if ( numModels != 0 ) {
- return w;
- }
- // check if winding fragments would be chopped away by the proc BSP tree
- windingLeft = -1;
- for ( i = 0; i < cm_windingList->numWindings; i++ ) {
- if ( !ChoppedAwayByProcBSP( cm_windingList->w[i], plane, contents ) ) {
- if ( windingLeft >= 0 ) {
- return w;
- }
- windingLeft = i;
- }
- }
- if ( windingLeft >= 0 ) {
- return &cm_windingList->w[windingLeft];
- }
- return NULL;
- }
- /*
- ===============================================================================
- Merging polygons
- ===============================================================================
- */
- /*
- =============
- idCollisionModelManagerLocal::ReplacePolygons
- does not allow for a node to have multiple references to the same polygon
- =============
- */
- void idCollisionModelManagerLocal::ReplacePolygons( cm_model_t *model, cm_node_t *node, cm_polygon_t *p1, cm_polygon_t *p2, cm_polygon_t *newp ) {
- cm_polygonRef_t *pref, *lastpref, *nextpref;
- cm_polygon_t *p;
- bool linked;
- while( 1 ) {
- linked = false;
- lastpref = NULL;
- for ( pref = node->polygons; pref; pref = nextpref ) {
- nextpref = pref->next;
- //
- p = pref->p;
- // if this polygon reference should change
- if ( p == p1 || p == p2 ) {
- // if the new polygon is already linked at this node
- if ( linked ) {
- if ( lastpref ) {
- lastpref->next = nextpref;
- }
- else {
- node->polygons = nextpref;
- }
- FreePolygonReference( pref );
- model->numPolygonRefs--;
- }
- else {
- pref->p = newp;
- linked = true;
- lastpref = pref;
- }
- }
- else {
- lastpref = pref;
- }
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- if ( p1->bounds[0][node->planeType] > node->planeDist && p2->bounds[0][node->planeType] > node->planeDist ) {
- node = node->children[0];
- }
- else if ( p1->bounds[1][node->planeType] < node->planeDist && p2->bounds[1][node->planeType] < node->planeDist ) {
- node = node->children[1];
- }
- else {
- ReplacePolygons( model, node->children[1], p1, p2, newp );
- node = node->children[0];
- }
- }
- }
- /*
- =============
- idCollisionModelManagerLocal::TryMergePolygons
- =============
- */
- #define CONTINUOUS_EPSILON 0.005f
- #define NORMAL_EPSILON 0.01f
- cm_polygon_t *idCollisionModelManagerLocal::TryMergePolygons( cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2 ) {
- int i, j, nexti, prevj;
- int p1BeforeShare, p1AfterShare, p2BeforeShare, p2AfterShare;
- int newEdges[CM_MAX_POLYGON_EDGES], newNumEdges;
- int edgeNum, edgeNum1, edgeNum2, newEdgeNum1, newEdgeNum2;
- cm_edge_t *edge;
- cm_polygon_t *newp;
- idVec3 delta, normal;
- float dot;
- bool keep1, keep2;
- if ( p1->material != p2->material ) {
- return NULL;
- }
- if ( idMath::Fabs( p1->plane.Dist() - p2->plane.Dist() ) > NORMAL_EPSILON ) {
- return NULL;
- }
- for ( i = 0; i < 3; i++ ) {
- if ( idMath::Fabs( p1->plane.Normal()[i] - p2->plane.Normal()[i] ) > NORMAL_EPSILON ) {
- return NULL;
- }
- if ( p1->bounds[0][i] > p2->bounds[1][i] ) {
- return NULL;
- }
- if ( p1->bounds[1][i] < p2->bounds[0][i] ) {
- return NULL;
- }
- }
- // this allows for merging polygons with multiple shared edges
- // polygons with multiple shared edges probably never occur tho ;)
- p1BeforeShare = p1AfterShare = p2BeforeShare = p2AfterShare = -1;
- for ( i = 0; i < p1->numEdges; i++ ) {
- nexti = (i+1)%p1->numEdges;
- for ( j = 0; j < p2->numEdges; j++ ) {
- prevj = (j+p2->numEdges-1)%p2->numEdges;
- //
- if ( abs(p1->edges[i]) != abs(p2->edges[j]) ) {
- // if the next edge of p1 and the previous edge of p2 are the same
- if ( abs(p1->edges[nexti]) == abs(p2->edges[prevj]) ) {
- // if both polygons don't use the edge in the same direction
- if ( p1->edges[nexti] != p2->edges[prevj] ) {
- p1BeforeShare = i;
- p2AfterShare = j;
- }
- break;
- }
- }
- // if both polygons don't use the edge in the same direction
- else if ( p1->edges[i] != p2->edges[j] ) {
- // if the next edge of p1 and the previous edge of p2 are not the same
- if ( abs(p1->edges[nexti]) != abs(p2->edges[prevj]) ) {
- p1AfterShare = nexti;
- p2BeforeShare = prevj;
- break;
- }
- }
- }
- }
- if ( p1BeforeShare < 0 || p1AfterShare < 0 || p2BeforeShare < 0 || p2AfterShare < 0 ) {
- return NULL;
- }
- // check if the new polygon would still be convex
- edgeNum = p1->edges[p1BeforeShare];
- edge = model->edges + abs(edgeNum);
- delta = model->vertices[edge->vertexNum[INT32_SIGNBITNOTSET(edgeNum)]].p -
- model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p;
- normal = p1->plane.Normal().Cross( delta );
- normal.Normalize();
- edgeNum = p2->edges[p2AfterShare];
- edge = model->edges + abs(edgeNum);
- delta = model->vertices[edge->vertexNum[INT32_SIGNBITNOTSET(edgeNum)]].p -
- model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p;
- dot = delta * normal;
- if (dot < -CONTINUOUS_EPSILON)
- return NULL; // not a convex polygon
- keep1 = (bool)(dot > CONTINUOUS_EPSILON);
- edgeNum = p2->edges[p2BeforeShare];
- edge = model->edges + abs(edgeNum);
- delta = model->vertices[edge->vertexNum[INT32_SIGNBITNOTSET(edgeNum)]].p -
- model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p;
- normal = p1->plane.Normal().Cross( delta );
- normal.Normalize();
- edgeNum = p1->edges[p1AfterShare];
- edge = model->edges + abs(edgeNum);
- delta = model->vertices[edge->vertexNum[INT32_SIGNBITNOTSET(edgeNum)]].p -
- model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p;
- dot = delta * normal;
- if (dot < -CONTINUOUS_EPSILON)
- return NULL; // not a convex polygon
- keep2 = (bool)(dot > CONTINUOUS_EPSILON);
- newEdgeNum1 = newEdgeNum2 = 0;
- // get new edges if we need to replace colinear ones
- if ( !keep1 ) {
- edgeNum1 = p1->edges[p1BeforeShare];
- edgeNum2 = p2->edges[p2AfterShare];
- GetEdge( model, model->vertices[model->edges[abs(edgeNum1)].vertexNum[INT32_SIGNBITSET(edgeNum1)]].p,
- model->vertices[model->edges[abs(edgeNum2)].vertexNum[INT32_SIGNBITNOTSET(edgeNum2)]].p,
- &newEdgeNum1, -1 );
- if ( newEdgeNum1 == 0 ) {
- keep1 = true;
- }
- }
- if ( !keep2 ) {
- edgeNum1 = p2->edges[p2BeforeShare];
- edgeNum2 = p1->edges[p1AfterShare];
- GetEdge( model, model->vertices[model->edges[abs(edgeNum1)].vertexNum[INT32_SIGNBITSET(edgeNum1)]].p,
- model->vertices[model->edges[abs(edgeNum2)].vertexNum[INT32_SIGNBITNOTSET(edgeNum2)]].p,
- &newEdgeNum2, -1 );
- if ( newEdgeNum2 == 0 ) {
- keep2 = true;
- }
- }
- // set edges for new polygon
- newNumEdges = 0;
- if ( !keep2 ) {
- newEdges[newNumEdges++] = newEdgeNum2;
- }
- if ( p1AfterShare < p1BeforeShare ) {
- for ( i = p1AfterShare + (!keep2); i <= p1BeforeShare - (!keep1); i++ ) {
- newEdges[newNumEdges++] = p1->edges[i];
- }
- }
- else {
- for ( i = p1AfterShare + (!keep2); i < p1->numEdges; i++ ) {
- newEdges[newNumEdges++] = p1->edges[i];
- }
- for ( i = 0; i <= p1BeforeShare - (!keep1); i++ ) {
- newEdges[newNumEdges++] = p1->edges[i];
- }
- }
- if ( !keep1 ) {
- newEdges[newNumEdges++] = newEdgeNum1;
- }
- if ( p2AfterShare < p2BeforeShare ) {
- for ( i = p2AfterShare + (!keep1); i <= p2BeforeShare - (!keep2); i++ ) {
- newEdges[newNumEdges++] = p2->edges[i];
- }
- }
- else {
- for ( i = p2AfterShare + (!keep1); i < p2->numEdges; i++ ) {
- newEdges[newNumEdges++] = p2->edges[i];
- }
- for ( i = 0; i <= p2BeforeShare - (!keep2); i++ ) {
- newEdges[newNumEdges++] = p2->edges[i];
- }
- }
- newp = AllocPolygon( model, newNumEdges );
- memcpy( newp, p1, sizeof(cm_polygon_t) );
- memcpy( newp->edges, newEdges, newNumEdges * sizeof(int) );
- newp->numEdges = newNumEdges;
- newp->checkcount = 0;
- // increase usage count for the edges of this polygon
- for ( i = 0; i < newp->numEdges; i++ ) {
- if ( !keep1 && newp->edges[i] == newEdgeNum1 ) {
- continue;
- }
- if ( !keep2 && newp->edges[i] == newEdgeNum2 ) {
- continue;
- }
- model->edges[abs(newp->edges[i])].numUsers++;
- }
- // create new bounds from the merged polygons
- newp->bounds = p1->bounds + p2->bounds;
- return newp;
- }
- /*
- =============
- idCollisionModelManagerLocal::MergePolygonWithTreePolygons
- =============
- */
- bool idCollisionModelManagerLocal::MergePolygonWithTreePolygons( cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon ) {
- int i;
- cm_polygonRef_t *pref;
- cm_polygon_t *p, *newp;
- while( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- p = pref->p;
- //
- if ( p == polygon ) {
- continue;
- }
- //
- newp = TryMergePolygons( model, polygon, p );
- // if polygons were merged
- if ( newp ) {
- model->numMergedPolys++;
- // replace links to the merged polygons with links to the new polygon
- ReplacePolygons( model, model->node, polygon, p, newp );
- // decrease usage count for edges of both merged polygons
- for ( i = 0; i < polygon->numEdges; i++ ) {
- model->edges[abs(polygon->edges[i])].numUsers--;
- }
- for ( i = 0; i < p->numEdges; i++ ) {
- model->edges[abs(p->edges[i])].numUsers--;
- }
- // free merged polygons
- FreePolygon( model, polygon );
- FreePolygon( model, p );
- return true;
- }
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- if ( polygon->bounds[0][node->planeType] > node->planeDist ) {
- node = node->children[0];
- }
- else if ( polygon->bounds[1][node->planeType] < node->planeDist ) {
- node = node->children[1];
- }
- else {
- if ( MergePolygonWithTreePolygons( model, node->children[1], polygon ) ) {
- return true;
- }
- node = node->children[0];
- }
- }
- return false;
- }
- /*
- =============
- idCollisionModelManagerLocal::MergeTreePolygons
- try to merge any two polygons with the same surface flags and the same contents
- =============
- */
- void idCollisionModelManagerLocal::MergeTreePolygons( cm_model_t *model, cm_node_t *node ) {
- cm_polygonRef_t *pref;
- cm_polygon_t *p;
- bool merge;
- while( 1 ) {
- do {
- merge = false;
- for ( pref = node->polygons; pref; pref = pref->next ) {
- p = pref->p;
- // if we checked this polygon already
- if ( p->checkcount == checkCount ) {
- continue;
- }
- p->checkcount = checkCount;
- // try to merge this polygon with other polygons in the tree
- if ( MergePolygonWithTreePolygons( model, model->node, p ) ) {
- merge = true;
- break;
- }
- }
- } while (merge);
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- MergeTreePolygons( model, node->children[1] );
- node = node->children[0];
- }
- }
- /*
- ===============================================================================
- Find internal edges
- ===============================================================================
- */
- /*
- if (two polygons have the same contents)
- if (the normals of the two polygon planes face towards each other)
- if (an edge is shared between the polygons)
- if (the edge is not shared in the same direction)
- then this is an internal edge
- else
- if (this edge is on the plane of the other polygon)
- if (this edge if fully inside the winding of the other polygon)
- then this edge is an internal edge
- */
- /*
- =============
- idCollisionModelManagerLocal::PointInsidePolygon
- =============
- */
- bool idCollisionModelManagerLocal::PointInsidePolygon( cm_model_t *model, cm_polygon_t *p, idVec3 &v ) {
- int i, edgeNum;
- idVec3 *v1, *v2, dir1, dir2, vec;
- cm_edge_t *edge;
- for ( i = 0; i < p->numEdges; i++ ) {
- edgeNum = p->edges[i];
- edge = model->edges + abs(edgeNum);
- //
- v1 = &model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p;
- v2 = &model->vertices[edge->vertexNum[INT32_SIGNBITNOTSET(edgeNum)]].p;
- dir1 = (*v2) - (*v1);
- vec = v - (*v1);
- dir2 = dir1.Cross( p->plane.Normal() );
- if ( vec * dir2 > VERTEX_EPSILON ) {
- return false;
- }
- }
- return true;
- }
- /*
- =============
- idCollisionModelManagerLocal::FindInternalEdgesOnPolygon
- =============
- */
- void idCollisionModelManagerLocal::FindInternalEdgesOnPolygon( cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2 ) {
- int i, j, k, edgeNum;
- cm_edge_t *edge;
- idVec3 *v1, *v2, dir1, dir2;
- float d;
- // bounds of polygons should overlap or touch
- for ( i = 0; i < 3; i++ ) {
- if ( p1->bounds[0][i] > p2->bounds[1][i] ) {
- return;
- }
- if ( p1->bounds[1][i] < p2->bounds[0][i] ) {
- return;
- }
- }
- //
- // FIXME: doubled geometry causes problems
- //
- for ( i = 0; i < p1->numEdges; i++ ) {
- edgeNum = p1->edges[i];
- edge = model->edges + abs(edgeNum);
- // if already an internal edge
- if ( edge->internal ) {
- continue;
- }
- //
- v1 = &model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p;
- v2 = &model->vertices[edge->vertexNum[INT32_SIGNBITNOTSET(edgeNum)]].p;
- // if either of the two vertices is outside the bounds of the other polygon
- for ( k = 0; k < 3; k++ ) {
- d = p2->bounds[1][k] + VERTEX_EPSILON;
- if ( (*v1)[k] > d || (*v2)[k] > d ) {
- break;
- }
- d = p2->bounds[0][k] - VERTEX_EPSILON;
- if ( (*v1)[k] < d || (*v2)[k] < d ) {
- break;
- }
- }
- if ( k < 3 ) {
- continue;
- }
- //
- k = abs(edgeNum);
- for ( j = 0; j < p2->numEdges; j++ ) {
- if ( k == abs(p2->edges[j]) ) {
- break;
- }
- }
- // if the edge is shared between the two polygons
- if ( j < p2->numEdges ) {
- // if the edge is used by more than 2 polygons
- if ( edge->numUsers > 2 ) {
- // could still be internal but we'd have to test all polygons using the edge
- continue;
- }
- // if the edge goes in the same direction for both polygons
- if ( edgeNum == p2->edges[j] ) {
- // the polygons can lay ontop of each other or one can obscure the other
- continue;
- }
- }
- // the edge was not shared
- else {
- // both vertices should be on the plane of the other polygon
- d = p2->plane.Distance( *v1 );
- if ( idMath::Fabs(d) > VERTEX_EPSILON ) {
- continue;
- }
- d = p2->plane.Distance( *v2 );
- if ( idMath::Fabs(d) > VERTEX_EPSILON ) {
- continue;
- }
- }
- // the two polygon plane normals should face towards each other
- dir1 = (*v2) - (*v1);
- dir2 = p1->plane.Normal().Cross( dir1 );
- if ( p2->plane.Normal() * dir2 < 0 ) {
- //continue;
- break;
- }
- // if the edge was not shared
- if ( j >= p2->numEdges ) {
- // both vertices of the edge should be inside the winding of the other polygon
- if ( !PointInsidePolygon( model, p2, *v1 ) ) {
- continue;
- }
- if ( !PointInsidePolygon( model, p2, *v2 ) ) {
- continue;
- }
- }
- // we got another internal edge
- edge->internal = true;
- model->numInternalEdges++;
- }
- }
- /*
- =============
- idCollisionModelManagerLocal::FindInternalPolygonEdges
- =============
- */
- void idCollisionModelManagerLocal::FindInternalPolygonEdges( cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon ) {
- cm_polygonRef_t *pref;
- cm_polygon_t *p;
- if ( polygon->material->GetCullType() == CT_TWO_SIDED || polygon->material->ShouldCreateBackSides() ) {
- return;
- }
- while( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- p = pref->p;
- //
- // FIXME: use some sort of additional checkcount because currently
- // polygons can be checked multiple times
- //
- // if the polygons don't have the same contents
- if ( p->contents != polygon->contents ) {
- continue;
- }
- if ( p == polygon ) {
- continue;
- }
- FindInternalEdgesOnPolygon( model, polygon, p );
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- if ( polygon->bounds[0][node->planeType] > node->planeDist ) {
- node = node->children[0];
- }
- else if ( polygon->bounds[1][node->planeType] < node->planeDist ) {
- node = node->children[1];
- }
- else {
- FindInternalPolygonEdges( model, node->children[1], polygon );
- node = node->children[0];
- }
- }
- }
- /*
- =============
- idCollisionModelManagerLocal::FindContainedEdges
- =============
- */
- void idCollisionModelManagerLocal::FindContainedEdges( cm_model_t *model, cm_polygon_t *p ) {
- int i, edgeNum;
- cm_edge_t *edge;
- idFixedWinding w;
- for ( i = 0; i < p->numEdges; i++ ) {
- edgeNum = p->edges[i];
- edge = model->edges + abs(edgeNum);
- if ( edge->internal ) {
- continue;
- }
- w.Clear();
- w += model->vertices[edge->vertexNum[INT32_SIGNBITSET(edgeNum)]].p;
- w += model->vertices[edge->vertexNum[INT32_SIGNBITNOTSET(edgeNum)]].p;
- if ( ChoppedAwayByProcBSP( w, p->plane, p->contents ) ) {
- edge->internal = true;
- }
- }
- }
- /*
- =============
- idCollisionModelManagerLocal::FindInternalEdges
- =============
- */
- void idCollisionModelManagerLocal::FindInternalEdges( cm_model_t *model, cm_node_t *node ) {
- cm_polygonRef_t *pref;
- cm_polygon_t *p;
- while( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- p = pref->p;
- // if we checked this polygon already
- if ( p->checkcount == checkCount ) {
- continue;
- }
- p->checkcount = checkCount;
- FindInternalPolygonEdges( model, model->node, p );
- //FindContainedEdges( model, p );
- }
- // if leaf node
- if ( node->planeType == -1 ) {
- break;
- }
- FindInternalEdges( model, node->children[1] );
- node = node->children[0];
- }
- }
- /*
- ===============================================================================
- Spatial subdivision
- ===============================================================================
- */
- /*
- ================
- CM_FindSplitter
- ================
- */
- static int CM_FindSplitter( const cm_node_t *node, const idBounds &bounds, int *planeType, float *planeDist ) {
- int i, j, type, axis[3], polyCount;
- float dist, t, bestt, size[3];
- cm_brushRef_t *bref;
- cm_polygonRef_t *pref;
- const cm_node_t *n;
- bool forceSplit = false;
- for ( i = 0; i < 3; i++ ) {
- size[i] = bounds[1][i] - bounds[0][i];
- axis[i] = i;
- }
- // sort on largest axis
- for ( i = 0; i < 2; i++ ) {
- if ( size[i] < size[i+1] ) {
- t = size[i];
- size[i] = size[i+1];
- size[i+1] = t;
- j = axis[i];
- axis[i] = axis[i+1];
- axis[i+1] = j;
- i = -1;
- }
- }
- // if the node is too small for further splits
- if ( size[0] < MIN_NODE_SIZE ) {
- polyCount = 0;
- for ( pref = node->polygons; pref; pref = pref->next) {
- polyCount++;
- }
- if ( polyCount > MAX_NODE_POLYGONS ) {
- forceSplit = true;
- }
- }
- // find an axial aligned splitter
- for ( i = 0; i < 3; i++ ) {
- // start with the largest axis first
- type = axis[i];
- bestt = size[i];
- // if the node is small anough in this axis direction
- if ( !forceSplit && bestt < MIN_NODE_SIZE ) {
- break;
- }
- // find an axial splitter from the brush bounding boxes
- // also try brushes from parent nodes
- for ( n = node; n; n = n->parent ) {
- for ( bref = n->brushes; bref; bref = bref->next) {
- for ( j = 0; j < 2; j++ ) {
- dist = bref->b->bounds[j][type];
- // if the splitter is already used or outside node bounds
- if ( dist >= bounds[1][type] || dist <= bounds[0][type] ) {
- continue;
- }
- // find the most centered splitter
- t = abs((bounds[1][type] - dist) - (dist - bounds[0][type]));
- if ( t < bestt ) {
- bestt = t;
- *planeType = type;
- *planeDist = dist;
- }
- }
- }
- }
- // find an axial splitter from the polygon bounding boxes
- // also try brushes from parent nodes
- for ( n = node; n; n = n->parent ) {
- for ( pref = n->polygons; pref; pref = pref->next) {
- for ( j = 0; j < 2; j++ ) {
- dist = pref->p->bounds[j][type];
- // if the splitter is already used or outside node bounds
- if ( dist >= bounds[1][type] || dist <= bounds[0][type] ) {
- continue;
- }
- // find the most centered splitter
- t = abs((bounds[1][type] - dist) - (dist - bounds[0][type]));
- if ( t < bestt ) {
- bestt = t;
- *planeType = type;
- *planeDist = dist;
- }
- }
- }
- }
- // if we found a splitter on the largest axis
- if ( bestt < size[i] ) {
- // if forced split due to lots of polygons
- if ( forceSplit ) {
- return true;
- }
- // don't create splitters real close to the bounds
- if ( bounds[1][type] - *planeDist > (MIN_NODE_SIZE*0.5f) &&
- *planeDist - bounds[0][type] > (MIN_NODE_SIZE*0.5f) ) {
- return true;
- }
- }
- }
- return false;
- }
- /*
- ================
- CM_R_InsideAllChildren
- ================
- */
- static int CM_R_InsideAllChildren( cm_node_t *node, const idBounds &bounds ) {
- assert(node != NULL);
- if ( node->planeType != -1 ) {
- if ( bounds[0][node->planeType] >= node->planeDist ) {
- return false;
- }
- if ( bounds[1][node->planeType] <= node->planeDist ) {
- return false;
- }
- if ( !CM_R_InsideAllChildren( node->children[0], bounds ) ) {
- return false;
- }
- if ( !CM_R_InsideAllChildren( node->children[1], bounds ) ) {
- return false;
- }
- }
- return true;
- }
- /*
- ================
- idCollisionModelManagerLocal::R_FilterPolygonIntoTree
- ================
- */
- void idCollisionModelManagerLocal::R_FilterPolygonIntoTree( cm_model_t *model, cm_node_t *node, cm_polygonRef_t *pref, cm_polygon_t *p ) {
- assert(node != NULL);
- while ( node->planeType != -1 ) {
- if ( CM_R_InsideAllChildren( node, p->bounds ) ) {
- break;
- }
- if ( p->bounds[0][node->planeType] >= node->planeDist ) {
- node = node->children[0];
- }
- else if ( p->bounds[1][node->planeType] <= node->planeDist ) {
- node = node->children[1];
- }
- else {
- R_FilterPolygonIntoTree( model, node->children[1], NULL, p );
- node = node->children[0];
- }
- }
- if ( pref ) {
- pref->next = node->polygons;
- node->polygons = pref;
- }
- else {
- AddPolygonToNode( model, node, p );
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::R_FilterBrushIntoTree
- ================
- */
- void idCollisionModelManagerLocal::R_FilterBrushIntoTree( cm_model_t *model, cm_node_t *node, cm_brushRef_t *pref, cm_brush_t *b ) {
- assert(node != NULL);
- while ( node->planeType != -1 ) {
- if ( CM_R_InsideAllChildren( node, b->bounds ) ) {
- break;
- }
- if ( b->bounds[0][node->planeType] >= node->planeDist ) {
- node = node->children[0];
- }
- else if ( b->bounds[1][node->planeType] <= node->planeDist ) {
- node = node->children[1];
- }
- else {
- R_FilterBrushIntoTree( model, node->children[1], NULL, b );
- node = node->children[0];
- }
- }
- if ( pref ) {
- pref->next = node->brushes;
- node->brushes = pref;
- }
- else {
- AddBrushToNode( model, node, b );
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::R_CreateAxialBSPTree
- a brush or polygon is linked in the node closest to the root where
- the brush or polygon is inside all children
- ================
- */
- cm_node_t *idCollisionModelManagerLocal::R_CreateAxialBSPTree( cm_model_t *model, cm_node_t *node, const idBounds &bounds ) {
- int planeType;
- float planeDist;
- cm_polygonRef_t *pref, *nextpref, *prevpref;
- cm_brushRef_t *bref, *nextbref, *prevbref;
- cm_node_t *frontNode, *backNode, *n;
- idBounds frontBounds, backBounds;
- if ( !CM_FindSplitter( node, bounds, &planeType, &planeDist ) ) {
- node->planeType = -1;
- return node;
- }
- // create two child nodes
- frontNode = AllocNode( model, NODE_BLOCK_SIZE_LARGE );
- memset( frontNode, 0, sizeof(cm_node_t) );
- frontNode->parent = node;
- frontNode->planeType = -1;
- //
- backNode = AllocNode( model, NODE_BLOCK_SIZE_LARGE );
- memset( backNode, 0, sizeof(cm_node_t) );
- backNode->parent = node;
- backNode->planeType = -1;
- //
- model->numNodes += 2;
- // set front node bounds
- frontBounds = bounds;
- frontBounds[0][planeType] = planeDist;
- // set back node bounds
- backBounds = bounds;
- backBounds[1][planeType] = planeDist;
- //
- node->planeType = planeType;
- node->planeDist = planeDist;
- node->children[0] = frontNode;
- node->children[1] = backNode;
- // filter polygons and brushes down the tree if necesary
- for ( n = node; n; n = n->parent ) {
- prevpref = NULL;
- for ( pref = n->polygons; pref; pref = nextpref) {
- nextpref = pref->next;
- // if polygon is not inside all children
- if ( !CM_R_InsideAllChildren( n, pref->p->bounds ) ) {
- // filter polygon down the tree
- R_FilterPolygonIntoTree( model, n, pref, pref->p );
- if ( prevpref ) {
- prevpref->next = nextpref;
- }
- else {
- n->polygons = nextpref;
- }
- }
- else {
- prevpref = pref;
- }
- }
- prevbref = NULL;
- for ( bref = n->brushes; bref; bref = nextbref) {
- nextbref = bref->next;
- // if brush is not inside all children
- if ( !CM_R_InsideAllChildren( n, bref->b->bounds ) ) {
- // filter brush down the tree
- R_FilterBrushIntoTree( model, n, bref, bref->b );
- if ( prevbref ) {
- prevbref->next = nextbref;
- }
- else {
- n->brushes = nextbref;
- }
- }
- else {
- prevbref = bref;
- }
- }
- }
- R_CreateAxialBSPTree( model, frontNode, frontBounds );
- R_CreateAxialBSPTree( model, backNode, backBounds );
- return node;
- }
- /*
- int cm_numSavedPolygonLinks;
- int cm_numSavedBrushLinks;
- int CM_R_CountChildren( cm_node_t *node ) {
- if ( node->planeType == -1 ) {
- return 0;
- }
- return 2 + CM_R_CountChildren(node->children[0]) + CM_R_CountChildren(node->children[1]);
- }
- void CM_R_TestOptimisation( cm_node_t *node ) {
- int polyCount, brushCount, numChildren;
- cm_polygonRef_t *pref;
- cm_brushRef_t *bref;
- if ( node->planeType == -1 ) {
- return;
- }
- polyCount = 0;
- for ( pref = node->polygons; pref; pref = pref->next) {
- polyCount++;
- }
- brushCount = 0;
- for ( bref = node->brushes; bref; bref = bref->next) {
- brushCount++;
- }
- if ( polyCount || brushCount ) {
- numChildren = CM_R_CountChildren( node );
- cm_numSavedPolygonLinks += (numChildren - 1) * polyCount;
- cm_numSavedBrushLinks += (numChildren - 1) * brushCount;
- }
- CM_R_TestOptimisation( node->children[0] );
- CM_R_TestOptimisation( node->children[1] );
- }
- */
- /*
- ================
- idCollisionModelManagerLocal::CreateAxialBSPTree
- ================
- */
- cm_node_t *idCollisionModelManagerLocal::CreateAxialBSPTree( cm_model_t *model, cm_node_t *node ) {
- cm_polygonRef_t *pref;
- cm_brushRef_t *bref;
- idBounds bounds;
- // get head node bounds
- bounds.Clear();
- for ( pref = node->polygons; pref; pref = pref->next) {
- bounds += pref->p->bounds;
- }
- for ( bref = node->brushes; bref; bref = bref->next) {
- bounds += bref->b->bounds;
- }
- // create axial BSP tree from head node
- node = R_CreateAxialBSPTree( model, node, bounds );
- return node;
- }
- /*
- ===============================================================================
- Raw polygon and brush data
- ===============================================================================
- */
- /*
- ================
- idCollisionModelManagerLocal::SetupHash
- ================
- */
- void idCollisionModelManagerLocal::SetupHash() {
- if ( !cm_vertexHash ) {
- cm_vertexHash = new (TAG_COLLISION) idHashIndex( VERTEX_HASH_SIZE, 1024 );
- }
- if ( !cm_edgeHash ) {
- cm_edgeHash = new (TAG_COLLISION) idHashIndex( EDGE_HASH_SIZE, 1024 );
- }
- // init variables used during loading and optimization
- if ( !cm_windingList ) {
- cm_windingList = new (TAG_COLLISION) cm_windingList_t;
- }
- if ( !cm_outList ) {
- cm_outList = new (TAG_COLLISION) cm_windingList_t;
- }
- if ( !cm_tmpList ) {
- cm_tmpList = new (TAG_COLLISION) cm_windingList_t;
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::ShutdownHash
- ================
- */
- void idCollisionModelManagerLocal::ShutdownHash() {
- delete cm_vertexHash;
- cm_vertexHash = NULL;
- delete cm_edgeHash;
- cm_edgeHash = NULL;
- delete cm_tmpList;
- cm_tmpList = NULL;
- delete cm_outList;
- cm_outList = NULL;
- delete cm_windingList;
- cm_windingList = NULL;
- }
- /*
- ================
- idCollisionModelManagerLocal::ClearHash
- ================
- */
- void idCollisionModelManagerLocal::ClearHash( idBounds &bounds ) {
- int i;
- float f, max;
- cm_vertexHash->Clear();
- cm_edgeHash->Clear();
- cm_modelBounds = bounds;
- max = bounds[1].x - bounds[0].x;
- f = bounds[1].y - bounds[0].y;
- if ( f > max ) {
- max = f;
- }
- cm_vertexShift = (float) max / VERTEX_HASH_BOXSIZE;
- for ( i = 0; (1<<i) < cm_vertexShift; i++ ) {
- }
- if ( i == 0 ) {
- cm_vertexShift = 1;
- }
- else {
- cm_vertexShift = i;
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::HashVec
- ================
- */
- ID_INLINE int idCollisionModelManagerLocal::HashVec(const idVec3 &vec) {
- /*
- int x, y;
- x = (((int)(vec[0] - cm_modelBounds[0].x + 0.5 )) >> cm_vertexShift) & (VERTEX_HASH_BOXSIZE-1);
- y = (((int)(vec[1] - cm_modelBounds[0].y + 0.5 )) >> cm_vertexShift) & (VERTEX_HASH_BOXSIZE-1);
- assert (x >= 0 && x < VERTEX_HASH_BOXSIZE && y >= 0 && y < VERTEX_HASH_BOXSIZE);
- return y * VERTEX_HASH_BOXSIZE + x;
- */
- int x, y, z;
- x = (((int) (vec[0] - cm_modelBounds[0].x + 0.5)) + 2) >> 2;
- y = (((int) (vec[1] - cm_modelBounds[0].y + 0.5)) + 2) >> 2;
- z = (((int) (vec[2] - cm_modelBounds[0].z + 0.5)) + 2) >> 2;
- return (x + y * VERTEX_HASH_BOXSIZE + z) & (VERTEX_HASH_SIZE-1);
- }
- /*
- ================
- idCollisionModelManagerLocal::GetVertex
- ================
- */
- int idCollisionModelManagerLocal::GetVertex( cm_model_t *model, const idVec3 &v, int *vertexNum ) {
- int i, hashKey, vn;
- idVec3 vert, *p;
-
- for (i = 0; i < 3; i++) {
- if ( idMath::Fabs(v[i] - idMath::Rint(v[i])) < INTEGRAL_EPSILON )
- vert[i] = idMath::Rint(v[i]);
- else
- vert[i] = v[i];
- }
- hashKey = HashVec( vert );
- for (vn = cm_vertexHash->First( hashKey ); vn >= 0; vn = cm_vertexHash->Next( vn ) ) {
- p = &model->vertices[vn].p;
- // first compare z-axis because hash is based on x-y plane
- if (idMath::Fabs(vert[2] - (*p)[2]) < VERTEX_EPSILON &&
- idMath::Fabs(vert[0] - (*p)[0]) < VERTEX_EPSILON &&
- idMath::Fabs(vert[1] - (*p)[1]) < VERTEX_EPSILON )
- {
- *vertexNum = vn;
- return true;
- }
- }
- if ( model->numVertices >= model->maxVertices ) {
- cm_vertex_t *oldVertices;
- // resize vertex array
- model->maxVertices = (float) model->maxVertices * 1.5f + 1;
- oldVertices = model->vertices;
- model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t), TAG_COLLISION );
- memcpy( model->vertices, oldVertices, model->numVertices * sizeof(cm_vertex_t) );
- Mem_Free( oldVertices );
- cm_vertexHash->ResizeIndex( model->maxVertices );
- }
- model->vertices[model->numVertices].p = vert;
- model->vertices[model->numVertices].checkcount = 0;
- *vertexNum = model->numVertices;
- // add vertice to hash
- cm_vertexHash->Add( hashKey, model->numVertices );
- //
- model->numVertices++;
- return false;
- }
- /*
- ================
- idCollisionModelManagerLocal::GetEdge
- ================
- */
- int idCollisionModelManagerLocal::GetEdge( cm_model_t *model, const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ) {
- int v2num, hashKey, e;
- int found, *vertexNum;
- // the first edge is a dummy
- if ( model->numEdges == 0 ) {
- model->numEdges = 1;
- }
- if ( v1num != -1 ) {
- found = 1;
- }
- else {
- found = GetVertex( model, v1, &v1num );
- }
- found &= GetVertex( model, v2, &v2num );
- // if both vertices are the same or snapped onto each other
- if ( v1num == v2num ) {
- *edgeNum = 0;
- return true;
- }
- hashKey = cm_edgeHash->GenerateKey( v1num, v2num );
- // if both vertices where already stored
- if (found) {
- for (e = cm_edgeHash->First( hashKey ); e >= 0; e = cm_edgeHash->Next( e ) )
- {
- // NOTE: only allow at most two users that use the edge in opposite direction
- if ( model->edges[e].numUsers != 1 ) {
- continue;
- }
- vertexNum = model->edges[e].vertexNum;
- if ( vertexNum[0] == v2num ) {
- if ( vertexNum[1] == v1num ) {
- // negative for a reversed edge
- *edgeNum = -e;
- break;
- }
- }
- /*
- else if ( vertexNum[0] == v1num ) {
- if ( vertexNum[1] == v2num ) {
- *edgeNum = e;
- break;
- }
- }
- */
- }
- // if edge found in hash
- if ( e >= 0 ) {
- model->edges[e].numUsers++;
- return true;
- }
- }
- if ( model->numEdges >= model->maxEdges ) {
- cm_edge_t *oldEdges;
- // resize edge array
- model->maxEdges = (float) model->maxEdges * 1.5f + 1;
- oldEdges = model->edges;
- model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t), TAG_COLLISION );
- memcpy( model->edges, oldEdges, model->numEdges * sizeof(cm_edge_t) );
- Mem_Free( oldEdges );
- cm_edgeHash->ResizeIndex( model->maxEdges );
- }
- // setup edge
- model->edges[model->numEdges].vertexNum[0] = v1num;
- model->edges[model->numEdges].vertexNum[1] = v2num;
- model->edges[model->numEdges].internal = false;
- model->edges[model->numEdges].checkcount = 0;
- model->edges[model->numEdges].numUsers = 1; // used by one polygon atm
- model->edges[model->numEdges].normal.Zero();
- //
- *edgeNum = model->numEdges;
- // add edge to hash
- cm_edgeHash->Add( hashKey, model->numEdges );
- model->numEdges++;
- return false;
- }
- /*
- ================
- idCollisionModelManagerLocal::CreatePolygon
- ================
- */
- void idCollisionModelManagerLocal::CreatePolygon( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ) {
- int i, j, edgeNum, v1num;
- int numPolyEdges, polyEdges[MAX_POINTS_ON_WINDING];
- idBounds bounds;
- cm_polygon_t *p;
- // turn the winding into a sequence of edges
- numPolyEdges = 0;
- v1num = -1; // first vertex unknown
- for ( i = 0, j = 1; i < w->GetNumPoints(); i++, j++ ) {
- if ( j >= w->GetNumPoints() ) {
- j = 0;
- }
- GetEdge( model, (*w)[i].ToVec3(), (*w)[j].ToVec3(), &polyEdges[numPolyEdges], v1num );
- if ( polyEdges[numPolyEdges] ) {
- // last vertex of this edge is the first vertex of the next edge
- v1num = model->edges[ abs(polyEdges[numPolyEdges]) ].vertexNum[ INT32_SIGNBITNOTSET(polyEdges[numPolyEdges]) ];
- // this edge is valid so keep it
- numPolyEdges++;
- }
- }
- // should have at least 3 edges
- if ( numPolyEdges < 3 ) {
- return;
- }
- // the polygon is invalid if some edge is found twice
- for ( i = 0; i < numPolyEdges; i++ ) {
- for ( j = i+1; j < numPolyEdges; j++ ) {
- if ( abs(polyEdges[i]) == abs(polyEdges[j]) ) {
- return;
- }
- }
- }
- // don't overflow max edges
- if ( numPolyEdges > CM_MAX_POLYGON_EDGES ) {
- common->Warning( "idCollisionModelManagerLocal::CreatePolygon: polygon has more than %d edges", numPolyEdges );
- numPolyEdges = CM_MAX_POLYGON_EDGES;
- }
- w->GetBounds( bounds );
- p = AllocPolygon( model, numPolyEdges );
- p->numEdges = numPolyEdges;
- p->contents = material->GetContentFlags();
- p->material = material;
- p->checkcount = 0;
- p->plane = plane;
- p->bounds = bounds;
- for ( i = 0; i < numPolyEdges; i++ ) {
- edgeNum = polyEdges[i];
- p->edges[i] = edgeNum;
- }
- R_FilterPolygonIntoTree( model, model->node, NULL, p );
- }
- /*
- ================
- idCollisionModelManagerLocal::PolygonFromWinding
- NOTE: for patches primitiveNum < 0 and abs(primitiveNum) is the real number
- ================
- */
- void idCollisionModelManagerLocal::PolygonFromWinding( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ) {
- int contents;
- contents = material->GetContentFlags();
- // if this polygon is part of the world model
- if ( numModels == 0 ) {
- // if the polygon is fully chopped away by the proc bsp tree
- if ( ChoppedAwayByProcBSP( *w, plane, contents ) ) {
- model->numRemovedPolys++;
- return;
- }
- }
- // get one winding that is not or only partly contained in brushes
- w = WindingOutsideBrushes( w, plane, contents, primitiveNum, model->node );
- // if the polygon is fully contained within a brush
- if ( !w ) {
- model->numRemovedPolys++;
- return;
- }
- if ( w->IsHuge() ) {
- common->Warning( "idCollisionModelManagerLocal::PolygonFromWinding: model %s primitive %d is degenerate", model->name.c_str(), abs(primitiveNum) );
- return;
- }
- CreatePolygon( model, w, plane, material, primitiveNum );
- if ( material->GetCullType() == CT_TWO_SIDED || material->ShouldCreateBackSides() ) {
- w->ReverseSelf();
- CreatePolygon( model, w, -plane, material, primitiveNum );
- }
- }
- /*
- =================
- idCollisionModelManagerLocal::CreatePatchPolygons
- =================
- */
- void idCollisionModelManagerLocal::CreatePatchPolygons( cm_model_t *model, idSurface_Patch &mesh, const idMaterial *material, int primitiveNum ) {
- int i, j;
- float dot;
- int v1, v2, v3, v4;
- idFixedWinding w;
- idPlane plane;
- idVec3 d1, d2;
- for ( i = 0; i < mesh.GetWidth() - 1; i++ ) {
- for ( j = 0; j < mesh.GetHeight() - 1; j++ ) {
- v1 = j * mesh.GetWidth() + i;
- v2 = v1 + 1;
- v3 = v1 + mesh.GetWidth() + 1;
- v4 = v1 + mesh.GetWidth();
- d1 = mesh[v2].xyz - mesh[v1].xyz;
- d2 = mesh[v3].xyz - mesh[v1].xyz;
- plane.SetNormal( d1.Cross(d2) );
- if ( plane.Normalize() != 0.0f ) {
- plane.FitThroughPoint( mesh[v1].xyz );
- dot = plane.Distance( mesh[v4].xyz );
- // if we can turn it into a quad
- if ( idMath::Fabs(dot) < 0.1f ) {
- w.Clear();
- w += mesh[v1].xyz;
- w += mesh[v2].xyz;
- w += mesh[v3].xyz;
- w += mesh[v4].xyz;
- PolygonFromWinding( model, &w, plane, material, -primitiveNum );
- continue;
- }
- else {
- // create one of the triangles
- w.Clear();
- w += mesh[v1].xyz;
- w += mesh[v2].xyz;
- w += mesh[v3].xyz;
- PolygonFromWinding( model, &w, plane, material, -primitiveNum );
- }
- }
- // create the other triangle
- d1 = mesh[v3].xyz - mesh[v1].xyz;
- d2 = mesh[v4].xyz - mesh[v1].xyz;
- plane.SetNormal( d1.Cross(d2) );
- if ( plane.Normalize() != 0.0f ) {
- plane.FitThroughPoint( mesh[v1].xyz );
- w.Clear();
- w += mesh[v1].xyz;
- w += mesh[v3].xyz;
- w += mesh[v4].xyz;
- PolygonFromWinding( model, &w, plane, material, -primitiveNum );
- }
- }
- }
- }
- /*
- =================
- CM_EstimateVertsAndEdges
- =================
- */
- static void CM_EstimateVertsAndEdges( const idMapEntity *mapEnt, int *numVerts, int *numEdges ) {
- int j, width, height;
- *numVerts = *numEdges = 0;
- for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) {
- const idMapPrimitive *mapPrim;
- mapPrim = mapEnt->GetPrimitive(j);
- if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) {
- // assume maximum tesselation without adding verts
- width = static_cast<const idMapPatch*>(mapPrim)->GetWidth();
- height = static_cast<const idMapPatch*>(mapPrim)->GetHeight();
- *numVerts += width * height;
- *numEdges += (width-1) * height + width * (height-1) + (width-1) * (height-1);
- continue;
- }
- if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
- // assume cylinder with a polygon with (numSides - 2) edges ontop and on the bottom
- *numVerts += (static_cast<const idMapBrush*>(mapPrim)->GetNumSides() - 2) * 2;
- *numEdges += (static_cast<const idMapBrush*>(mapPrim)->GetNumSides() - 2) * 3;
- continue;
- }
- }
- }
- /*
- =================
- idCollisionModelManagerLocal::ConverPatch
- =================
- */
- void idCollisionModelManagerLocal::ConvertPatch( cm_model_t *model, const idMapPatch *patch, int primitiveNum ) {
- const idMaterial *material;
- idSurface_Patch *cp;
- material = declManager->FindMaterial( patch->GetMaterial() );
- if ( !( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
- return;
- }
- // copy the patch
- cp = new (TAG_COLLISION) idSurface_Patch( *patch );
- // if the patch has an explicit number of subdivisions use it to avoid cracks
- if ( patch->GetExplicitlySubdivided() ) {
- cp->SubdivideExplicit( patch->GetHorzSubdivisions(), patch->GetVertSubdivisions(), false, true );
- } else {
- cp->Subdivide( DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_LENGTH_CD, false );
- }
- // create collision polygons for the patch
- CreatePatchPolygons( model, *cp, material, primitiveNum );
- delete cp;
- }
- /*
- ================
- idCollisionModelManagerLocal::ConvertBrushSides
- ================
- */
- void idCollisionModelManagerLocal::ConvertBrushSides( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ) {
- int i, j;
- idMapBrushSide *mapSide;
- idFixedWinding w;
- idPlane *planes;
- const idMaterial *material;
- // fix degenerate planes
- planes = (idPlane *) _alloca16( mapBrush->GetNumSides() * sizeof( planes[0] ) );
- for ( i = 0; i < mapBrush->GetNumSides(); i++ ) {
- planes[i] = mapBrush->GetSide(i)->GetPlane();
- planes[i].FixDegeneracies( DEGENERATE_DIST_EPSILON );
- }
- // create a collision polygon for each brush side
- for ( i = 0; i < mapBrush->GetNumSides(); i++ ) {
- mapSide = mapBrush->GetSide(i);
- material = declManager->FindMaterial( mapSide->GetMaterial() );
- if ( !( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
- continue;
- }
- w.BaseForPlane( -planes[i] );
- for ( j = 0; j < mapBrush->GetNumSides() && w.GetNumPoints(); j++ ) {
- if ( i == j ) {
- continue;
- }
- w.ClipInPlace( -planes[j], 0 );
- }
- if ( w.GetNumPoints() ) {
- PolygonFromWinding( model, &w, planes[i], material, primitiveNum );
- }
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::ConvertBrush
- ================
- */
- void idCollisionModelManagerLocal::ConvertBrush( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ) {
- int i, j, contents;
- idBounds bounds;
- idMapBrushSide *mapSide;
- cm_brush_t *brush;
- idPlane *planes;
- idFixedWinding w;
- const idMaterial *material = NULL;
- contents = 0;
- bounds.Clear();
- // fix degenerate planes
- planes = (idPlane *) _alloca16( mapBrush->GetNumSides() * sizeof( planes[0] ) );
- for ( i = 0; i < mapBrush->GetNumSides(); i++ ) {
- planes[i] = mapBrush->GetSide(i)->GetPlane();
- planes[i].FixDegeneracies( DEGENERATE_DIST_EPSILON );
- }
- // we are only getting the bounds for the brush so there's no need
- // to create a winding for the last brush side
- for ( i = 0; i < mapBrush->GetNumSides() - 1; i++ ) {
- mapSide = mapBrush->GetSide(i);
- material = declManager->FindMaterial( mapSide->GetMaterial() );
- contents |= ( material->GetContentFlags() & CONTENTS_REMOVE_UTIL );
- w.BaseForPlane( -planes[i] );
- for ( j = 0; j < mapBrush->GetNumSides() && w.GetNumPoints(); j++ ) {
- if ( i == j ) {
- continue;
- }
- w.ClipInPlace( -planes[j], 0 );
- }
- for ( j = 0; j < w.GetNumPoints(); j++ ) {
- bounds.AddPoint( w[j].ToVec3() );
- }
- }
- if ( !contents ) {
- return;
- }
- // create brush for position test
- brush = AllocBrush( model, mapBrush->GetNumSides() );
- brush->checkcount = 0;
- brush->contents = contents;
- brush->material = material;
- brush->primitiveNum = primitiveNum;
- brush->bounds = bounds;
- brush->numPlanes = mapBrush->GetNumSides();
- for (i = 0; i < mapBrush->GetNumSides(); i++) {
- brush->planes[i] = planes[i];
- }
- AddBrushToNode( model, model->node, brush );
- }
- /*
- ================
- CM_CountNodeBrushes
- ================
- */
- static int CM_CountNodeBrushes( const cm_node_t *node ) {
- int count;
- cm_brushRef_t *bref;
- count = 0;
- for ( bref = node->brushes; bref; bref = bref->next ) {
- count++;
- }
- return count;
- }
- /*
- ================
- CM_R_GetModelBounds
- ================
- */
- static void CM_R_GetNodeBounds( idBounds *bounds, cm_node_t *node ) {
- cm_polygonRef_t *pref;
- cm_brushRef_t *bref;
- while ( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- bounds->AddPoint( pref->p->bounds[0] );
- bounds->AddPoint( pref->p->bounds[1] );
- }
- for ( bref = node->brushes; bref; bref = bref->next ) {
- bounds->AddPoint( bref->b->bounds[0] );
- bounds->AddPoint( bref->b->bounds[1] );
- }
- if ( node->planeType == -1 ) {
- break;
- }
- CM_R_GetNodeBounds( bounds, node->children[1] );
- node = node->children[0];
- }
- }
- /*
- ================
- CM_GetNodeBounds
- ================
- */
- void CM_GetNodeBounds( idBounds *bounds, cm_node_t *node ) {
- bounds->Clear();
- CM_R_GetNodeBounds( bounds, node );
- if ( bounds->IsCleared() ) {
- bounds->Zero();
- }
- }
- /*
- ================
- CM_GetNodeContents
- ================
- */
- int CM_GetNodeContents( cm_node_t *node ) {
- int contents;
- cm_polygonRef_t *pref;
- cm_brushRef_t *bref;
- contents = 0;
- while ( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- contents |= pref->p->contents;
- }
- for ( bref = node->brushes; bref; bref = bref->next ) {
- contents |= bref->b->contents;
- }
- if ( node->planeType == -1 ) {
- break;
- }
- contents |= CM_GetNodeContents( node->children[1] );
- node = node->children[0];
- }
- return contents;
- }
- /*
- ==================
- idCollisionModelManagerLocal::RemapEdges
- ==================
- */
- void idCollisionModelManagerLocal::RemapEdges( cm_node_t *node, int *edgeRemap ) {
- cm_polygonRef_t *pref;
- cm_polygon_t *p;
- int i;
- while ( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- p = pref->p;
- // if we checked this polygon already
- if ( p->checkcount == checkCount ) {
- continue;
- }
- p->checkcount = checkCount;
- for ( i = 0; i < p->numEdges; i++ ) {
- if ( p->edges[i] < 0 ) {
- p->edges[i] = -edgeRemap[ abs(p->edges[i]) ];
- }
- else {
- p->edges[i] = edgeRemap[ p->edges[i] ];
- }
- }
- }
- if ( node->planeType == -1 ) {
- break;
- }
- RemapEdges( node->children[1], edgeRemap );
- node = node->children[0];
- }
- }
- /*
- ==================
- idCollisionModelManagerLocal::OptimizeArrays
- due to polygon merging and polygon removal the vertex and edge array
- can have a lot of unused entries.
- ==================
- */
- void idCollisionModelManagerLocal::OptimizeArrays( cm_model_t *model ) {
- int i, newNumVertices, newNumEdges, *v;
- int *remap;
- cm_edge_t *oldEdges;
- cm_vertex_t *oldVertices;
- remap = (int *) Mem_ClearedAlloc( Max( model->numVertices, model->numEdges ) * sizeof( int ), TAG_COLLISION );
- // get all used vertices
- for ( i = 0; i < model->numEdges; i++ ) {
- remap[ model->edges[i].vertexNum[0] ] = true;
- remap[ model->edges[i].vertexNum[1] ] = true;
- }
- // create remap index and move vertices
- newNumVertices = 0;
- for ( i = 0; i < model->numVertices; i++ ) {
- if ( remap[ i ] ) {
- remap[ i ] = newNumVertices;
- model->vertices[ newNumVertices ] = model->vertices[ i ];
- newNumVertices++;
- }
- }
- model->numVertices = newNumVertices;
- // change edge vertex indexes
- for ( i = 1; i < model->numEdges; i++ ) {
- v = model->edges[i].vertexNum;
- v[0] = remap[ v[0] ];
- v[1] = remap[ v[1] ];
- }
- // create remap index and move edges
- newNumEdges = 1;
- for ( i = 1; i < model->numEdges; i++ ) {
- // if the edge is used
- if ( model->edges[ i ].numUsers ) {
- remap[ i ] = newNumEdges;
- model->edges[ newNumEdges ] = model->edges[ i ];
- newNumEdges++;
- }
- }
- // change polygon edge indexes
- checkCount++;
- RemapEdges( model->node, remap );
- model->numEdges = newNumEdges;
- Mem_Free( remap );
- // realloc vertices
- oldVertices = model->vertices;
- model->maxVertices = model->numVertices;
- model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->numVertices * sizeof(cm_vertex_t), TAG_COLLISION );
- if ( oldVertices ) {
- memcpy( model->vertices, oldVertices, model->numVertices * sizeof(cm_vertex_t) );
- Mem_Free( oldVertices );
- }
- // realloc edges
- oldEdges = model->edges;
- model->maxEdges = model->numEdges;
- model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->numEdges * sizeof(cm_edge_t), TAG_COLLISION );
- if ( oldEdges ) {
- memcpy( model->edges, oldEdges, model->numEdges * sizeof(cm_edge_t) );
- Mem_Free( oldEdges );
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::FinishModel
- ================
- */
- void idCollisionModelManagerLocal::FinishModel( cm_model_t *model ) {
- // try to merge polygons
- checkCount++;
- MergeTreePolygons( model, model->node );
- // find internal edges (no mesh can ever collide with internal edges)
- checkCount++;
- FindInternalEdges( model, model->node );
- // calculate edge normals
- checkCount++;
- CalculateEdgeNormals( model, model->node );
- //common->Printf( "%s vertex hash spread is %d\n", model->name.c_str(), cm_vertexHash->GetSpread() );
- //common->Printf( "%s edge hash spread is %d\n", model->name.c_str(), cm_edgeHash->GetSpread() );
- // remove all unused vertices and edges
- OptimizeArrays( model );
- // get model bounds from brush and polygon bounds
- CM_GetNodeBounds( &model->bounds, model->node );
- // get model contents
- model->contents = CM_GetNodeContents( model->node );
- // total memory used by this model
- model->usedMemory = model->numVertices * sizeof(cm_vertex_t) +
- model->numEdges * sizeof(cm_edge_t) +
- model->polygonMemory +
- model->brushMemory +
- model->numNodes * sizeof(cm_node_t) +
- model->numPolygonRefs * sizeof(cm_polygonRef_t) +
- model->numBrushRefs * sizeof(cm_brushRef_t);
- }
- static const byte BCM_VERSION = 100;
- static const unsigned int BCM_MAGIC = ( 'B' << 24 ) | ( 'C' << 16 ) | ( 'M' << 16 ) | BCM_VERSION;
- /*
- ================
- idCollisionModelManagerLocal::LoadBinaryModel
- ================
- */
- cm_model_t * idCollisionModelManagerLocal::LoadBinaryModelFromFile( idFile *file, ID_TIME_T sourceTimeStamp ) {
- unsigned int magic = 0;
- file->ReadBig( magic );
- if ( magic != BCM_MAGIC ) {
- return NULL;
- }
- ID_TIME_T storedTimeStamp = FILE_NOT_FOUND_TIMESTAMP;
- file->ReadBig( storedTimeStamp );
- if ( !fileSystem->InProductionMode() && storedTimeStamp != sourceTimeStamp ) {
- return NULL;
- }
- cm_model_t * model = AllocModel();
- file->ReadString( model->name );
- file->ReadBig( model->bounds );
- file->ReadBig( model->contents );
- file->ReadBig( model->isConvex );
- file->ReadBig( model->numVertices );
- file->ReadBig( model->numEdges );
- file->ReadBig( model->numPolygons );
- file->ReadBig( model->numBrushes );
- file->ReadBig( model->numNodes );
- file->ReadBig( model->numBrushRefs );
- file->ReadBig( model->numPolygonRefs );
- file->ReadBig( model->numInternalEdges );
- file->ReadBig( model->numSharpEdges );
- file->ReadBig( model->numRemovedPolys );
- file->ReadBig( model->numMergedPolys );
- model->maxVertices = model->numVertices;
- model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t), TAG_COLLISION );
- for ( int i = 0; i < model->numVertices; i++ ) {
- file->ReadBig( model->vertices[i].p );
- file->ReadBig( model->vertices[i].checkcount );
- file->ReadBig( model->vertices[i].side );
- file->ReadBig( model->vertices[i].sideSet );
- }
- model->maxEdges = model->numEdges;
- model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t), TAG_COLLISION );
- for ( int i = 0; i < model->numEdges; i++ ) {
- file->ReadBig( model->edges[i].checkcount );
- file->ReadBig( model->edges[i].internal );
- file->ReadBig( model->edges[i].numUsers );
- file->ReadBig( model->edges[i].side );
- file->ReadBig( model->edges[i].sideSet );
- file->ReadBig( model->edges[i].vertexNum[0] );
- file->ReadBig( model->edges[i].vertexNum[1] );
- file->ReadBig( model->edges[i].normal );
- }
- file->ReadBig( model->polygonMemory );
- model->polygonBlock = (cm_polygonBlock_t *) Mem_ClearedAlloc( sizeof( cm_polygonBlock_t ) + model->polygonMemory, TAG_COLLISION );
- model->polygonBlock->bytesRemaining = model->polygonMemory;
- model->polygonBlock->next = ( (byte *) model->polygonBlock ) + sizeof( cm_polygonBlock_t );
- file->ReadBig( model->brushMemory );
- model->brushBlock = (cm_brushBlock_t *) Mem_ClearedAlloc( sizeof( cm_brushBlock_t ) + model->brushMemory, TAG_COLLISION );
- model->brushBlock->bytesRemaining = model->brushMemory;
- model->brushBlock->next = ( (byte *) model->brushBlock ) + sizeof( cm_brushBlock_t );
- int numMaterials = 0;
- file->ReadBig( numMaterials );
- idList< const idMaterial * > materials;
- materials.SetNum( numMaterials );
- idStr materialName;
- for ( int i = 0; i < materials.Num(); i++ ) {
- file->ReadString( materialName );
- if ( materialName.IsEmpty() ) {
- materials[i] = NULL;
- } else {
- materials[i] = declManager->FindMaterial( materialName );
- }
- }
- idList< cm_polygon_t * > polys;
- idList< cm_brush_t * > brushes;
- polys.SetNum( model->numPolygons );
- brushes.SetNum( model->numBrushes );
- for ( int i = 0; i < polys.Num(); i++ ) {
- int materialIndex = 0;
- file->ReadBig( materialIndex );
- int numEdges = 0;
- file->ReadBig( numEdges );
- polys[i] = AllocPolygon( model, numEdges );
- polys[i]->numEdges = numEdges;
- polys[i]->material = materials[materialIndex];
- file->ReadBig( polys[i]->bounds );
- file->ReadBig( polys[i]->checkcount );
- file->ReadBig( polys[i]->contents );
- file->ReadBig( polys[i]->plane );
- file->ReadBigArray( polys[i]->edges, polys[i]->numEdges );
- }
- for ( int i = 0; i < brushes.Num(); i++ ) {
- int materialIndex = 0;
- file->ReadBig( materialIndex );
- int numPlanes = 0;
- file->ReadBig( numPlanes );
- brushes[i] = AllocBrush( model, numPlanes );
- brushes[i]->numPlanes = numPlanes;
- brushes[i]->material = materials[materialIndex];
- file->ReadBig( brushes[i]->checkcount );
- file->ReadBig( brushes[i]->bounds );
- file->ReadBig( brushes[i]->contents );
- file->ReadBig( brushes[i]->primitiveNum );
- file->ReadBigArray( brushes[i]->planes, brushes[i]->numPlanes );
- }
- struct local {
- static void ReadNodeTree( idFile * file, cm_model_t * model, cm_node_t * node, idList< cm_polygon_t * > & polys, idList< cm_brush_t * > & brushes ) {
- file->ReadBig( node->planeType );
- file->ReadBig( node->planeDist );
- int i = 0;
- while ( file->ReadBig( i ) == sizeof( i ) && ( i >= 0 ) ) {
- cm_polygonRef_t * pref = collisionModelManagerLocal.AllocPolygonReference( model, model->numPolygonRefs );
- pref->p = polys[i];
- pref->next = node->polygons;
- node->polygons = pref;
- }
- while ( file->ReadBig( i ) == sizeof( i ) && ( i >= 0 ) ) {
- cm_brushRef_t * bref = collisionModelManagerLocal.AllocBrushReference( model, model->numBrushRefs );
- bref->b = brushes[i];
- bref->next = node->brushes;
- node->brushes = bref;
- }
- if ( node->planeType != -1 ) {
- node->children[0] = collisionModelManagerLocal.AllocNode( model, model->numNodes );
- node->children[1] = collisionModelManagerLocal.AllocNode( model, model->numNodes );
- node->children[0]->parent = node;
- node->children[1]->parent = node;
- ReadNodeTree( file, model, node->children[0], polys, brushes );
- ReadNodeTree( file, model, node->children[1], polys, brushes );
- }
- }
- };
- model->node = AllocNode( model, model->numNodes + 1 );
- local::ReadNodeTree( file, model, model->node, polys, brushes );
- // We should have only allocated a single block, and used every entry in the block
- // assert( model->nodeBlocks != NULL && model->nodeBlocks->next == NULL && model->nodeBlocks->nextNode == NULL );
- assert( model->brushRefBlocks == NULL || ( model->brushRefBlocks->next == NULL && model->brushRefBlocks->nextRef == NULL ) );
- assert( model->polygonRefBlocks == NULL || ( model->polygonRefBlocks->next == NULL && model->polygonRefBlocks->nextRef == NULL ) );
- assert( model->polygonBlock->bytesRemaining == 0 );
- assert( model->brushBlock->bytesRemaining == 0 );
- model->usedMemory = model->numVertices * sizeof(cm_vertex_t) +
- model->numEdges * sizeof(cm_edge_t) +
- model->polygonMemory +
- model->brushMemory +
- model->numNodes * sizeof(cm_node_t) +
- model->numPolygonRefs * sizeof(cm_polygonRef_t) +
- model->numBrushRefs * sizeof(cm_brushRef_t);
- return model;
- }
- /*
- ================
- idCollisionModelManagerLocal::LoadBinaryModel
- ================
- */
- cm_model_t * idCollisionModelManagerLocal::LoadBinaryModel( const char *fileName, ID_TIME_T sourceTimeStamp ) {
- idFileLocal file( fileSystem->OpenFileReadMemory( fileName ) );
- if ( file == NULL ) {
- return NULL;
- }
- return LoadBinaryModelFromFile( file, sourceTimeStamp );
- }
- /*
- ================
- idCollisionModelManagerLocal::WriteBinaryModel
- ================
- */
- void idCollisionModelManagerLocal::WriteBinaryModelToFile( cm_model_t *model, idFile *file, ID_TIME_T sourceTimeStamp ) {
- file->WriteBig( BCM_MAGIC );
- file->WriteBig( sourceTimeStamp );
- file->WriteString( model->name );
- file->WriteBig( model->bounds );
- file->WriteBig( model->contents );
- file->WriteBig( model->isConvex );
- file->WriteBig( model->numVertices );
- file->WriteBig( model->numEdges );
- file->WriteBig( model->numPolygons );
- file->WriteBig( model->numBrushes );
- file->WriteBig( model->numNodes );
- file->WriteBig( model->numBrushRefs );
- file->WriteBig( model->numPolygonRefs );
- file->WriteBig( model->numInternalEdges );
- file->WriteBig( model->numSharpEdges );
- file->WriteBig( model->numRemovedPolys );
- file->WriteBig( model->numMergedPolys );
- for ( int i = 0; i < model->numVertices; i++ ) {
- file->WriteBig( model->vertices[i].p );
- file->WriteBig( model->vertices[i].checkcount );
- file->WriteBig( model->vertices[i].side );
- file->WriteBig( model->vertices[i].sideSet );
- }
- for ( int i = 0; i < model->numEdges; i++ ) {
- file->WriteBig( model->edges[i].checkcount );
- file->WriteBig( model->edges[i].internal );
- file->WriteBig( model->edges[i].numUsers );
- file->WriteBig( model->edges[i].side );
- file->WriteBig( model->edges[i].sideSet );
- file->WriteBig( model->edges[i].vertexNum[0] );
- file->WriteBig( model->edges[i].vertexNum[1] );
- file->WriteBig( model->edges[i].normal );
- }
- file->WriteBig( model->polygonMemory );
- file->WriteBig( model->brushMemory );
- struct local {
- static void BuildUniqueLists( cm_node_t * node, idList< cm_polygon_t * > & polys, idList< cm_brush_t * > & brushes ) {
- for ( cm_polygonRef_t * pr = node->polygons; pr != NULL; pr = pr->next ) {
- polys.AddUnique( pr->p );
- }
- for ( cm_brushRef_t * br = node->brushes; br != NULL; br = br->next ) {
- brushes.AddUnique( br->b );
- }
- if ( node->planeType != -1 ) {
- BuildUniqueLists( node->children[0], polys, brushes );
- BuildUniqueLists( node->children[1], polys, brushes );
- }
- }
- static void WriteNodeTree( idFile * file, cm_node_t * node, idList< cm_polygon_t * > & polys, idList< cm_brush_t * > & brushes ) {
- file->WriteBig( node->planeType );
- file->WriteBig( node->planeDist );
- for ( cm_polygonRef_t * pr = node->polygons; pr != NULL; pr = pr->next ) {
- file->WriteBig( polys.FindIndex( pr->p ) );
- }
- file->WriteBig( -1 );
- for ( cm_brushRef_t * br = node->brushes; br != NULL; br = br->next ) {
- file->WriteBig( brushes.FindIndex( br->b ) );
- }
- file->WriteBig( -1 );
- if ( node->planeType != -1 ) {
- WriteNodeTree( file, node->children[0], polys, brushes );
- WriteNodeTree( file, node->children[1], polys, brushes );
- }
- }
- };
- idList< cm_polygon_t * > polys;
- idList< cm_brush_t * > brushes;
- local::BuildUniqueLists( model->node, polys, brushes );
- assert( polys.Num() == model->numPolygons );
- assert( brushes.Num() == model->numBrushes );
- idList< const idMaterial * > materials;
- for ( int i = 0; i < polys.Num(); i++ ) {
- materials.AddUnique( polys[i]->material );
- }
- for ( int i = 0; i < brushes.Num(); i++ ) {
- materials.AddUnique( brushes[i]->material );
- }
- file->WriteBig( materials.Num() );
- for ( int i = 0; i < materials.Num(); i++ ) {
- if ( materials[i] == NULL ) {
- file->WriteString( "" );
- } else {
- file->WriteString( materials[i]->GetName() );
- }
- }
- for ( int i = 0; i < polys.Num(); i++ ) {
- file->WriteBig( ( int )materials.FindIndex( polys[i]->material ) );
- file->WriteBig( polys[i]->numEdges );
- file->WriteBig( polys[i]->bounds );
- file->WriteBig( polys[i]->checkcount );
- file->WriteBig( polys[i]->contents );
- file->WriteBig( polys[i]->plane );
- file->WriteBigArray( polys[i]->edges, polys[i]->numEdges );
- }
- for ( int i = 0; i < brushes.Num(); i++ ) {
- file->WriteBig( ( int )materials.FindIndex( brushes[i]->material ) );
- file->WriteBig( brushes[i]->numPlanes );
- file->WriteBig( brushes[i]->checkcount );
- file->WriteBig( brushes[i]->bounds );
- file->WriteBig( brushes[i]->contents );
- file->WriteBig( brushes[i]->primitiveNum );
- file->WriteBigArray( brushes[i]->planes, brushes[i]->numPlanes );
- }
- local::WriteNodeTree( file, model->node, polys, brushes );
- }
- /*
- ================
- idCollisionModelManagerLocal::WriteBinaryModel
- ================
- */
- void idCollisionModelManagerLocal::WriteBinaryModel( cm_model_t *model, const char *fileName, ID_TIME_T sourceTimeStamp ) {
- idFileLocal file( fileSystem->OpenFileWrite( fileName, "fs_basepath" ) );
- if ( file == NULL ) {
- common->Printf( "Failed to open %s\n", fileName );
- return;
- }
- WriteBinaryModelToFile( model, file, sourceTimeStamp );
- }
- /*
- ================
- idCollisionModelManagerLocal::LoadRenderModel
- ================
- */
- cm_model_t *idCollisionModelManagerLocal::LoadRenderModel( const char *fileName ) {
- int i, j;
- idRenderModel *renderModel;
- const modelSurface_t *surf;
- idFixedWinding w;
- cm_node_t *node;
- cm_model_t *model;
- idPlane plane;
- idBounds bounds;
- bool collisionSurface;
- idStr extension;
- // only load ASE and LWO models
- idStr( fileName ).ExtractFileExtension( extension );
- if ( ( extension.Icmp( "ase" ) != 0 ) && ( extension.Icmp( "lwo" ) != 0 ) && ( extension.Icmp( "ma" ) != 0 ) ) {
- return NULL;
- }
- renderModel = renderModelManager->CheckModel( fileName );
- if ( !renderModel ) {
- return NULL;
- }
- idStrStatic< MAX_OSPATH > generatedFileName = "generated/collision/";
- generatedFileName.AppendPath( fileName );
- generatedFileName.SetFileExtension( CMODEL_BINARYFILE_EXT );
- ID_TIME_T sourceTimeStamp = renderModel->Timestamp();
- model = LoadBinaryModel( generatedFileName, sourceTimeStamp );
- if ( model != NULL ) {
- return model;
- }
- idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
- model = AllocModel();
- model->name = fileName;
- node = AllocNode( model, NODE_BLOCK_SIZE_SMALL );
- node->planeType = -1;
- model->node = node;
- model->maxVertices = 0;
- model->numVertices = 0;
- model->maxEdges = 0;
- model->numEdges = 0;
- bounds = renderModel->Bounds( NULL );
- collisionSurface = false;
- for ( i = 0; i < renderModel->NumSurfaces(); i++ ) {
- surf = renderModel->Surface( i );
- if ( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) {
- collisionSurface = true;
- }
- }
- for ( i = 0; i < renderModel->NumSurfaces(); i++ ) {
- surf = renderModel->Surface( i );
- // if this surface has no contents
- if ( ! ( surf->shader->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
- continue;
- }
- // if the model has a collision surface and this surface is not a collision surface
- if ( collisionSurface && !( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) ) {
- continue;
- }
- // get max verts and edges
- model->maxVertices += surf->geometry->numVerts;
- model->maxEdges += surf->geometry->numIndexes;
- }
- model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t), TAG_COLLISION );
- model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t), TAG_COLLISION );
- // setup hash to speed up finding shared vertices and edges
- SetupHash();
- cm_vertexHash->ResizeIndex( model->maxVertices );
- cm_edgeHash->ResizeIndex( model->maxEdges );
- ClearHash( bounds );
- for ( i = 0; i < renderModel->NumSurfaces(); i++ ) {
- surf = renderModel->Surface( i );
- // if this surface has no contents
- if ( ! ( surf->shader->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
- continue;
- }
- // if the model has a collision surface and this surface is not a collision surface
- if ( collisionSurface && !( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) ) {
- continue;
- }
- for ( j = 0; j < surf->geometry->numIndexes; j += 3 ) {
- w.Clear();
- w += surf->geometry->verts[ surf->geometry->indexes[ j + 2 ] ].xyz;
- w += surf->geometry->verts[ surf->geometry->indexes[ j + 1 ] ].xyz;
- w += surf->geometry->verts[ surf->geometry->indexes[ j + 0 ] ].xyz;
- w.GetPlane( plane );
- plane = -plane;
- PolygonFromWinding( model, &w, plane, surf->shader, 1 );
- }
- }
- // create a BSP tree for the model
- model->node = CreateAxialBSPTree( model, model->node );
- model->isConvex = false;
- FinishModel( model );
- // shutdown the hash
- ShutdownHash();
- WriteBinaryModel( model, generatedFileName, sourceTimeStamp );
- return model;
- }
- /*
- ================
- idCollisionModelManagerLocal::CollisionModelForMapEntity
- ================
- */
- cm_model_t *idCollisionModelManagerLocal::CollisionModelForMapEntity( const idMapEntity *mapEnt ) {
- cm_model_t *model;
- idBounds bounds;
- const char *name;
- int i, brushCount;
- // if the entity has no primitives
- if ( mapEnt->GetNumPrimitives() < 1 ) {
- return NULL;
- }
- // get a name for the collision model
- mapEnt->epairs.GetString( "model", "", &name );
- if ( !name[0] ) {
- mapEnt->epairs.GetString( "name", "", &name );
- if ( !name[0] ) {
- if ( !numModels ) {
- // first model is always the world
- name = "worldMap";
- }
- else {
- name = "unnamed inline model";
- }
- }
- }
- model = AllocModel();
- model->node = AllocNode( model, NODE_BLOCK_SIZE_SMALL );
- CM_EstimateVertsAndEdges( mapEnt, &model->maxVertices, &model->maxEdges );
- model->numVertices = 0;
- model->numEdges = 0;
- model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t), TAG_COLLISION );
- model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t), TAG_COLLISION );
- cm_vertexHash->ResizeIndex( model->maxVertices );
- cm_edgeHash->ResizeIndex( model->maxEdges );
- model->name = name;
- model->isConvex = false;
- // convert brushes
- for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) {
- idMapPrimitive *mapPrim;
- mapPrim = mapEnt->GetPrimitive(i);
- if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
- ConvertBrush( model, static_cast<idMapBrush*>(mapPrim), i );
- continue;
- }
- }
- // create an axial bsp tree for the model if it has more than just a bunch brushes
- brushCount = CM_CountNodeBrushes( model->node );
- if ( brushCount > 4 ) {
- model->node = CreateAxialBSPTree( model, model->node );
- } else {
- model->node->planeType = -1;
- }
- // get bounds for hash
- if ( brushCount ) {
- CM_GetNodeBounds( &bounds, model->node );
- } else {
- bounds[0].Set( -256, -256, -256 );
- bounds[1].Set( 256, 256, 256 );
- }
- // different models do not share edges and vertices with each other, so clear the hash
- ClearHash( bounds );
- // create polygons from patches and brushes
- for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) {
- idMapPrimitive *mapPrim;
- mapPrim = mapEnt->GetPrimitive(i);
- if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) {
- ConvertPatch( model, static_cast<idMapPatch*>(mapPrim), i );
- continue;
- }
- if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
- ConvertBrushSides( model, static_cast<idMapBrush*>(mapPrim), i );
- continue;
- }
- }
- FinishModel( model );
- return model;
- }
- /*
- ================
- idCollisionModelManagerLocal::FindModel
- ================
- */
- cmHandle_t idCollisionModelManagerLocal::FindModel( const char *name ) {
- int i;
- // check if this model is already loaded
- for ( i = 0; i < numModels; i++ ) {
- if ( !models[i]->name.Icmp( name ) ) {
- break;
- }
- }
- // if the model is already loaded
- if ( i < numModels ) {
- return i;
- }
- return -1;
- }
- /*
- ==================
- idCollisionModelManagerLocal::PrintModelInfo
- ==================
- */
- void idCollisionModelManagerLocal::PrintModelInfo( const cm_model_t *model ) {
- common->Printf( "%6i vertices (%i KB)\n", model->numVertices, (model->numVertices * sizeof(cm_vertex_t))>>10 );
- common->Printf( "%6i edges (%i KB)\n", model->numEdges, (model->numEdges * sizeof(cm_edge_t))>>10 );
- common->Printf( "%6i polygons (%i KB)\n", model->numPolygons, model->polygonMemory>>10 );
- common->Printf( "%6i brushes (%i KB)\n", model->numBrushes, model->brushMemory>>10 );
- common->Printf( "%6i nodes (%i KB)\n", model->numNodes, (model->numNodes * sizeof(cm_node_t))>>10 );
- common->Printf( "%6i polygon refs (%i KB)\n", model->numPolygonRefs, (model->numPolygonRefs * sizeof(cm_polygonRef_t))>>10 );
- common->Printf( "%6i brush refs (%i KB)\n", model->numBrushRefs, (model->numBrushRefs * sizeof(cm_brushRef_t))>>10 );
- common->Printf( "%6i internal edges\n", model->numInternalEdges );
- common->Printf( "%6i sharp edges\n", model->numSharpEdges );
- common->Printf( "%6i contained polygons removed\n", model->numRemovedPolys );
- common->Printf( "%6i polygons merged\n", model->numMergedPolys );
- common->Printf( "%6i KB total memory used\n", model->usedMemory>>10 );
- }
- /*
- ================
- idCollisionModelManagerLocal::AccumulateModelInfo
- ================
- */
- void idCollisionModelManagerLocal::AccumulateModelInfo( cm_model_t *model ) {
- int i;
- memset( model, 0, sizeof( *model ) );
- // accumulate statistics of all loaded models
- for ( i = 0; i < numModels; i++ ) {
- model->numVertices += models[i]->numVertices;
- model->numEdges += models[i]->numEdges;
- model->numPolygons += models[i]->numPolygons;
- model->polygonMemory += models[i]->polygonMemory;
- model->numBrushes += models[i]->numBrushes;
- model->brushMemory += models[i]->brushMemory;
- model->numNodes += models[i]->numNodes;
- model->numBrushRefs += models[i]->numBrushRefs;
- model->numPolygonRefs += models[i]->numPolygonRefs;
- model->numInternalEdges += models[i]->numInternalEdges;
- model->numSharpEdges += models[i]->numSharpEdges;
- model->numRemovedPolys += models[i]->numRemovedPolys;
- model->numMergedPolys += models[i]->numMergedPolys;
- model->usedMemory += models[i]->usedMemory;
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::ModelInfo
- ================
- */
- void idCollisionModelManagerLocal::ModelInfo( cmHandle_t model ) {
- cm_model_t modelInfo;
- if ( model == -1 ) {
- AccumulateModelInfo( &modelInfo );
- PrintModelInfo( &modelInfo );
- return;
- }
- if ( model < 0 || model > MAX_SUBMODELS || model > maxModels ) {
- common->Printf( "idCollisionModelManagerLocal::ModelInfo: invalid model handle\n" );
- return;
- }
- if ( !models[model] ) {
- common->Printf( "idCollisionModelManagerLocal::ModelInfo: invalid model\n" );
- return;
- }
- PrintModelInfo( models[model] );
- }
- /*
- ================
- idCollisionModelManagerLocal::ListModels
- ================
- */
- void idCollisionModelManagerLocal::ListModels() {
- int i, totalMemory;
- totalMemory = 0;
- for ( i = 0; i < numModels; i++ ) {
- common->Printf( "%4d: %5d KB %s\n", i, (models[i]->usedMemory>>10), models[i]->name.c_str() );
- totalMemory += models[i]->usedMemory;
- }
- common->Printf( "%4d KB in %d models\n", (totalMemory>>10), numModels );
- }
- /*
- ================
- idCollisionModelManagerLocal::BuildModels
- ================
- */
- void idCollisionModelManagerLocal::BuildModels( const idMapFile *mapFile ) {
- int i;
- const idMapEntity *mapEnt;
- idTimer timer;
- timer.Start();
- if ( !LoadCollisionModelFile( mapFile->GetName(), mapFile->GetGeometryCRC() ) ) {
- if ( !mapFile->GetNumEntities() ) {
- return;
- }
- // load the .proc file bsp for data optimisation
- LoadProcBSP( mapFile->GetName() );
- // convert brushes and patches to collision data
- for ( i = 0; i < mapFile->GetNumEntities(); i++ ) {
- mapEnt = mapFile->GetEntity(i);
- if ( numModels >= MAX_SUBMODELS ) {
- common->Error( "idCollisionModelManagerLocal::BuildModels: more than %d collision models", MAX_SUBMODELS );
- break;
- }
- models[numModels] = CollisionModelForMapEntity( mapEnt );
- if ( models[ numModels] ) {
- numModels++;
- }
- }
- // free the proc bsp which is only used for data optimization
- Mem_Free( procNodes );
- procNodes = NULL;
- // write the collision models to a file
- WriteCollisionModelsToFile( mapFile->GetName(), 0, numModels, mapFile->GetGeometryCRC() );
- }
- timer.Stop();
- // print statistics on collision data
- cm_model_t model;
- AccumulateModelInfo( &model );
- common->Printf( "collision data:\n" );
- common->Printf( "%6i models\n", numModels );
- PrintModelInfo( &model );
- common->Printf( "%.0f msec to load collision data.\n", timer.Milliseconds() );
- }
- /*
- ================
- idCollisionModelManagerLocal::Preload
- ================
- */
- void idCollisionModelManagerLocal::Preload( const char *mapName ) {
- if ( !preLoad_Collision.GetBool() ) {
- return;
- }
- idStrStatic< MAX_OSPATH > manifestName = mapName;
- manifestName.Replace( "game/", "maps/" );
- manifestName.Replace( "maps/maps/", "maps/" );
- manifestName.SetFileExtension( ".preload" );
- idPreloadManifest manifest;
- manifest.LoadManifest( manifestName );
- if ( manifest.NumResources() >= 0 ) {
- common->Printf( "Preloading collision models...\n" );
- int start = Sys_Milliseconds();
- int numLoaded = 0;
- for ( int i = 0; i < manifest.NumResources(); i++ ) {
- const preloadEntry_s & p = manifest.GetPreloadByIndex( i );
- if ( p.resType == PRELOAD_COLLISION ) {
- LoadModel( p.resourceName );
- numLoaded++;
- }
- }
- int end = Sys_Milliseconds();
- common->Printf( "%05d collision models preloaded ( or were already loaded ) in %5.1f seconds\n", numLoaded, ( end - start ) * 0.001 );
- common->Printf( "----------------------------------------\n" );
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::LoadMap
- ================
- */
- void idCollisionModelManagerLocal::LoadMap( const idMapFile *mapFile ) {
- if ( mapFile == NULL ) {
- common->Error( "idCollisionModelManagerLocal::LoadMap: NULL mapFile" );
- return;
- }
- // check whether we can keep the current collision map based on the mapName and mapFileTime
- if ( loaded ) {
- if ( mapName.Icmp( mapFile->GetName() ) == 0 ) {
- if ( mapFile->GetFileTime() == mapFileTime ) {
- common->DPrintf( "Using loaded version\n" );
- return;
- }
- common->DPrintf( "Reloading modified map\n" );
- }
- FreeMap();
- }
- // clear the collision map
- Clear();
- // models
- maxModels = MAX_SUBMODELS;
- numModels = 0;
- models = (cm_model_t **) Mem_ClearedAlloc( (maxModels+1) * sizeof(cm_model_t *), TAG_COLLISION );
- // setup hash to speed up finding shared vertices and edges
- SetupHash();
- common->UpdateLevelLoadPacifier();
- // setup trace model structure
- SetupTrmModelStructure();
- common->UpdateLevelLoadPacifier();
- // build collision models
- BuildModels( mapFile );
- common->UpdateLevelLoadPacifier();
- // save name and time stamp
- mapName = mapFile->GetName();
- mapFileTime = mapFile->GetFileTime();
- loaded = true;
- // shutdown the hash
- ShutdownHash();
- }
- /*
- ===================
- idCollisionModelManagerLocal::GetModelName
- ===================
- */
- const char *idCollisionModelManagerLocal::GetModelName( cmHandle_t model ) const {
- if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelBounds: invalid model handle\n" );
- return "";
- }
- return models[model]->name.c_str();
- }
- /*
- ===================
- idCollisionModelManagerLocal::GetModelBounds
- ===================
- */
- bool idCollisionModelManagerLocal::GetModelBounds( cmHandle_t model, idBounds &bounds ) const {
- if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelBounds: invalid model handle\n" );
- return false;
- }
- bounds = models[model]->bounds;
- return true;
- }
- /*
- ===================
- idCollisionModelManagerLocal::GetModelContents
- ===================
- */
- bool idCollisionModelManagerLocal::GetModelContents( cmHandle_t model, int &contents ) const {
- if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelContents: invalid model handle\n" );
- return false;
- }
- contents = models[model]->contents;
- return true;
- }
- /*
- ===================
- idCollisionModelManagerLocal::GetModelVertex
- ===================
- */
- bool idCollisionModelManagerLocal::GetModelVertex( cmHandle_t model, int vertexNum, idVec3 &vertex ) const {
- if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelVertex: invalid model handle\n" );
- return false;
- }
- if ( vertexNum < 0 || vertexNum >= models[model]->numVertices ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelVertex: invalid vertex number\n" );
- return false;
- }
- vertex = models[model]->vertices[vertexNum].p;
- return true;
- }
- /*
- ===================
- idCollisionModelManagerLocal::GetModelEdge
- ===================
- */
- bool idCollisionModelManagerLocal::GetModelEdge( cmHandle_t model, int edgeNum, idVec3 &start, idVec3 &end ) const {
- if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelEdge: invalid model handle\n" );
- return false;
- }
- edgeNum = abs( edgeNum );
- if ( edgeNum >= models[model]->numEdges ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelEdge: invalid edge number\n" );
- return false;
- }
- start = models[model]->vertices[models[model]->edges[edgeNum].vertexNum[0]].p;
- end = models[model]->vertices[models[model]->edges[edgeNum].vertexNum[1]].p;
- return true;
- }
- /*
- ===================
- idCollisionModelManagerLocal::GetModelPolygon
- ===================
- */
- bool idCollisionModelManagerLocal::GetModelPolygon( cmHandle_t model, int polygonNum, idFixedWinding &winding ) const {
- int i, edgeNum;
- cm_polygon_t *poly;
- if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
- common->Printf( "idCollisionModelManagerLocal::GetModelPolygon: invalid model handle\n" );
- return false;
- }
- poly = *reinterpret_cast<cm_polygon_t **>(&polygonNum);
- winding.Clear();
- for ( i = 0; i < poly->numEdges; i++ ) {
- edgeNum = poly->edges[i];
- winding += models[model]->vertices[ models[model]->edges[abs(edgeNum)].vertexNum[INT32_SIGNBITSET(edgeNum)] ].p;
- }
- return true;
- }
- /*
- ==================
- idCollisionModelManagerLocal::LoadModel
- ==================
- */
- cmHandle_t idCollisionModelManagerLocal::LoadModel( const char *modelName ) {
- int handle;
- handle = FindModel( modelName );
- if ( handle >= 0 ) {
- return handle;
- }
- if ( numModels >= MAX_SUBMODELS ) {
- common->Error( "idCollisionModelManagerLocal::LoadModel: no free slots\n" );
- return 0;
- }
- idStrStatic< MAX_OSPATH > generatedFileName = "generated/collision/";
- generatedFileName.AppendPath( modelName );
- generatedFileName.SetFileExtension( CMODEL_BINARYFILE_EXT );
- ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( modelName );
- models[ numModels ] = LoadBinaryModel( generatedFileName, sourceTimeStamp );
- if ( models[ numModels ] != NULL ) {
- numModels++;
- if ( cvarSystem->GetCVarBool( "fs_buildresources" ) ) {
- // for resource gathering write this model to the preload file for this map
- fileSystem->AddCollisionPreload( modelName );
- }
- return ( numModels - 1 );
- }
- // try to load a .cm file
- if ( LoadCollisionModelFile( modelName, 0 ) ) {
- handle = FindModel( modelName );
- if ( handle >= 0 && handle < numModels ) {
- cm_model_t * cm = models[ handle ];
- WriteBinaryModel( cm, generatedFileName, sourceTimeStamp );
- return handle;
- } else {
- common->Warning( "idCollisionModelManagerLocal::LoadModel: collision file for '%s' contains different model", modelName );
- }
- }
- // try to load a .ASE or .LWO model and convert it to a collision model
- models[ numModels ] = LoadRenderModel( modelName );
- if ( models[ numModels ] != NULL ) {
- numModels++;
- return ( numModels - 1 );
- }
- return 0;
- }
- /*
- ==================
- idCollisionModelManagerLocal::TrmFromModel_r
- ==================
- */
- bool idCollisionModelManagerLocal::TrmFromModel_r( idTraceModel &trm, cm_node_t *node ) {
- cm_polygonRef_t *pref;
- cm_polygon_t *p;
- int i;
- while ( 1 ) {
- for ( pref = node->polygons; pref; pref = pref->next ) {
- p = pref->p;
- if ( p->checkcount == checkCount ) {
- continue;
- }
- p->checkcount = checkCount;
- if ( trm.numPolys >= MAX_TRACEMODEL_POLYS ) {
- return false;
- }
- // copy polygon properties
- trm.polys[ trm.numPolys ].bounds = p->bounds;
- trm.polys[ trm.numPolys ].normal = p->plane.Normal();
- trm.polys[ trm.numPolys ].dist = p->plane.Dist();
- trm.polys[ trm.numPolys ].numEdges = p->numEdges;
- // copy edge index
- for ( i = 0; i < p->numEdges; i++ ) {
- trm.polys[ trm.numPolys ].edges[ i ] = p->edges[ i ];
- }
- trm.numPolys++;
- }
- if ( node->planeType == -1 ) {
- break;
- }
- if ( !TrmFromModel_r( trm, node->children[1] ) ) {
- return false;
- }
- node = node->children[0];
- }
- return true;
- }
- /*
- ==================
- idCollisionModelManagerLocal::TrmFromModel
- NOTE: polygon merging can merge colinear edges and as such might cause dangling edges.
- ==================
- */
- bool idCollisionModelManagerLocal::TrmFromModel( const cm_model_t *model, idTraceModel &trm ) {
- int i, j, numEdgeUsers[MAX_TRACEMODEL_EDGES+1];
- // if the model has too many vertices to fit in a trace model
- if ( model->numVertices > MAX_TRACEMODEL_VERTS ) {
- common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many vertices.\n", model->name.c_str() );
- PrintModelInfo( model );
- return false;
- }
- // plus one because the collision model accounts for the first unused edge
- if ( model->numEdges > MAX_TRACEMODEL_EDGES+1 ) {
- common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many edges.\n", model->name.c_str() );
- PrintModelInfo( model );
- return false;
- }
- trm.type = TRM_CUSTOM;
- trm.numVerts = 0;
- trm.numEdges = 1;
- trm.numPolys = 0;
- trm.bounds.Clear();
- // copy polygons
- checkCount++;
- if ( !TrmFromModel_r( trm, model->node ) ) {
- common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many polygons.\n", model->name.c_str() );
- PrintModelInfo( model );
- return false;
- }
- // copy vertices
- for ( i = 0; i < model->numVertices; i++ ) {
- trm.verts[ i ] = model->vertices[ i ].p;
- trm.bounds.AddPoint( trm.verts[ i ] );
- }
- trm.numVerts = model->numVertices;
- // copy edges
- for ( i = 0; i < model->numEdges; i++ ) {
- trm.edges[ i ].v[0] = model->edges[ i ].vertexNum[0];
- trm.edges[ i ].v[1] = model->edges[ i ].vertexNum[1];
- }
- // minus one because the collision model accounts for the first unused edge
- trm.numEdges = model->numEdges - 1;
- // each edge should be used exactly twice
- memset( numEdgeUsers, 0, sizeof(numEdgeUsers) );
- for ( i = 0; i < trm.numPolys; i++ ) {
- for ( j = 0; j < trm.polys[i].numEdges; j++ ) {
- numEdgeUsers[ abs( trm.polys[i].edges[j] ) ]++;
- }
- }
- for ( i = 1; i <= trm.numEdges; i++ ) {
- if ( numEdgeUsers[i] != 2 ) {
- common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has dangling edges, the model has to be an enclosed hull.\n", model->name.c_str() );
- PrintModelInfo( model );
- return false;
- }
- }
- // assume convex
- trm.isConvex = true;
- // check if really convex
- for ( i = 0; i < trm.numPolys; i++ ) {
- // to be convex no vertices should be in front of any polygon plane
- for ( j = 0; j < trm.numVerts; j++ ) {
- if ( trm.polys[ i ].normal * trm.verts[ j ] - trm.polys[ i ].dist > 0.01f ) {
- trm.isConvex = false;
- break;
- }
- }
- if ( j < trm.numVerts ) {
- break;
- }
- }
- // offset to center of model
- trm.offset = trm.bounds.GetCenter();
- trm.GenerateEdgeNormals();
- return true;
- }
- /*
- ==================
- idCollisionModelManagerLocal::TrmFromModel
- ==================
- */
- bool idCollisionModelManagerLocal::TrmFromModel( const char *modelName, idTraceModel &trm ) {
- cmHandle_t handle;
- handle = LoadModel( modelName );
- if ( !handle ) {
- common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s not found.\n", modelName );
- return false;
- }
- return TrmFromModel( models[ handle ], trm );
- }
|