| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146 |
- /**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file pandaNode.cxx
- * @author drose
- * @date 2002-02-20
- */
- #include "pandaNode.h"
- #include "config_pgraph.h"
- #include "nodePathComponent.h"
- #include "bamReader.h"
- #include "bamWriter.h"
- #include "indent.h"
- #include "geometricBoundingVolume.h"
- #include "sceneGraphReducer.h"
- #include "accumulatedAttribs.h"
- #include "clipPlaneAttrib.h"
- #include "boundingSphere.h"
- #include "boundingBox.h"
- #include "pStatTimer.h"
- #include "config_mathutil.h"
- #include "lightReMutexHolder.h"
- #include "graphicsStateGuardianBase.h"
- using std::ostream;
- using std::ostringstream;
- using std::string;
- // This category is just temporary for debugging convenience.
- NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
- NotifyCategoryDef(drawmask, "");
- TypeHandle PandaNode::BamReaderAuxDataDown::_type_handle;
- PandaNode::SceneRootFunc *PandaNode::_scene_root_func;
- PandaNodeChain PandaNode::_dirty_prev_transforms("_dirty_prev_transforms");
- DrawMask PandaNode::_overall_bit = DrawMask::bit(31);
- PStatCollector PandaNode::_reset_prev_pcollector("App:Collisions:Reset");
- PStatCollector PandaNode::_update_bounds_pcollector("*:Bounds");
- TypeHandle PandaNode::_type_handle;
- TypeHandle PandaNode::CData::_type_handle;
- TypeHandle PandaNodePipelineReader::_type_handle;
- /*
- * There are two different interfaces here for making and breaking parent-
- * child connections: the fundamental PandaNode interface, via add_child() and
- * remove_child() (and related functions), and the NodePath support interface,
- * via attach(), detach(), and reparent(). They both do essentially the same
- * thing, but with slightly different inputs. The PandaNode interfaces try to
- * guess which NodePaths should be updated as a result of the scene graph
- * change, while the NodePath interfaces already know. The NodePath support
- * interface functions are strictly called from within the NodePath class, and
- * are used to implement NodePath::reparent_to() and NodePath::remove_node(),
- * etc. The fundamental interface, on the other hand, is intended to be
- * called directly by the user. The fundamental interface has a slightly
- * lower overhead because it does not need to create a NodePathComponent chain
- * where one does not already exist; however, the NodePath support interface
- * is more useful when the NodePath already does exist, because it ensures
- * that the particular NodePath calling it is kept appropriately up-to-date.
- */
- /**
- *
- */
- PandaNode::
- PandaNode(const string &name) :
- Namable(name),
- _paths_lock("PandaNode::_paths_lock"),
- _dirty_prev_transform(false)
- {
- if (pgraph_cat.is_debug()) {
- pgraph_cat.debug()
- << "Constructing " << (void *)this << ", " << get_name() << "\n";
- }
- #ifdef DO_MEMORY_USAGE
- MemoryUsage::update_type(this, this);
- #endif
- }
- /**
- *
- */
- PandaNode::
- ~PandaNode() {
- if (pgraph_cat.is_debug()) {
- pgraph_cat.debug()
- << "Destructing " << (void *)this << ", " << get_name() << "\n";
- }
- if (_dirty_prev_transform) {
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- do_clear_dirty_prev_transform();
- }
- // We shouldn't have any parents left by the time we destruct, or there's a
- // refcount fault somewhere.
- // Actually, that's not necessarily true anymore, since we might be updating
- // a node dynamically via the bam reader, which doesn't necessarily keep
- // related pairs of nodes in sync with each other.
- /*
- #ifndef NDEBUG
- {
- CDReader cdata(_cycler);
- nassertv(cdata->get_up()->empty());
- }
- #endif // NDEBUG
- */
- remove_all_children();
- }
- /**
- * Do not call the copy constructor directly; instead, use make_copy() or
- * copy_subgraph() to make a copy of a node.
- */
- PandaNode::
- PandaNode(const PandaNode ©) :
- TypedWritableReferenceCount(copy),
- Namable(copy),
- _paths_lock("PandaNode::_paths_lock"),
- _dirty_prev_transform(false),
- _python_tag_data(copy._python_tag_data),
- _unexpected_change_flags(0)
- {
- if (pgraph_cat.is_debug()) {
- pgraph_cat.debug()
- << "Copying " << (void *)this << ", " << get_name() << "\n";
- }
- #ifdef DO_MEMORY_USAGE
- MemoryUsage::update_type(this, this);
- #endif
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- // Copy the other node's state.
- {
- CDReader copy_cdata(copy._cycler);
- CDWriter cdata(_cycler, true);
- cdata->_state = copy_cdata->_state;
- cdata->_transform = copy_cdata->_transform;
- cdata->_prev_transform = copy_cdata->_prev_transform;
- if (cdata->_transform != cdata->_prev_transform) {
- do_set_dirty_prev_transform();
- }
- cdata->_effects = copy_cdata->_effects;
- cdata->_tag_data = copy_cdata->_tag_data;
- cdata->_draw_control_mask = copy_cdata->_draw_control_mask;
- cdata->_draw_show_mask = copy_cdata->_draw_show_mask;
- cdata->_into_collide_mask = copy_cdata->_into_collide_mask;
- cdata->_bounds_type = copy_cdata->_bounds_type;
- cdata->_user_bounds = copy_cdata->_user_bounds;
- cdata->_internal_bounds = nullptr;
- cdata->_internal_bounds_computed = UpdateSeq::initial();
- cdata->_internal_bounds_mark = UpdateSeq::initial();
- ++cdata->_internal_bounds_mark;
- cdata->_final_bounds = copy_cdata->_final_bounds;
- cdata->_fancy_bits = copy_cdata->_fancy_bits;
- }
- }
- /**
- * This is similar to make_copy(), but it makes a copy for the specific
- * purpose of flatten. Typically, this will be a new PandaNode with a new
- * pointer, but all of the internal data will always be shared with the
- * original; whereas the new node returned by make_copy() might not share the
- * internal data.
- */
- PandaNode *PandaNode::
- dupe_for_flatten() const {
- return make_copy();
- }
- /**
- * Returns true if it is generally safe to flatten out this particular kind of
- * PandaNode by duplicating instances (by calling dupe_for_flatten()), false
- * otherwise (for instance, a Camera cannot be safely flattened, because the
- * Camera pointer itself is meaningful).
- */
- bool PandaNode::
- safe_to_flatten() const {
- return true;
- }
- /**
- * Returns true if it is generally safe to transform this particular kind of
- * PandaNode by calling the xform() method, false otherwise.
- */
- bool PandaNode::
- safe_to_transform() const {
- return true;
- }
- /**
- * Returns true if it is safe to automatically adjust the transform on this
- * kind of node. Usually, this is only a bad idea if the user expects to find
- * a particular transform on the node.
- *
- * ModelNodes with the preserve_transform flag set are presently the only
- * kinds of nodes that should not have their transform even adjusted.
- */
- bool PandaNode::
- safe_to_modify_transform() const {
- return true;
- }
- /**
- * Returns true if it is generally safe to combine this particular kind of
- * PandaNode with other kinds of PandaNodes of compatible type, adding
- * children or whatever. For instance, an LODNode should not be combined with
- * any other PandaNode, because its set of children is meaningful.
- */
- bool PandaNode::
- safe_to_combine() const {
- return true;
- }
- /**
- * Returns true if it is generally safe to combine the children of this
- * PandaNode with each other. For instance, an LODNode's children should not
- * be combined with each other, because the set of children is meaningful.
- */
- bool PandaNode::
- safe_to_combine_children() const {
- return true;
- }
- /**
- * Returns true if a flatten operation may safely continue past this node, or
- * false if nodes below this node may not be molested.
- */
- bool PandaNode::
- safe_to_flatten_below() const {
- return true;
- }
- /**
- * Returns true if the node's name has extrinsic meaning and must be preserved
- * across a flatten operation, false otherwise.
- */
- bool PandaNode::
- preserve_name() const {
- return false;
- }
- /**
- * Returns the union of all attributes from SceneGraphReducer::AttribTypes
- * that may not safely be applied to the vertices of this node. If this is
- * nonzero, these attributes must be dropped at this node as a state change.
- *
- * This is a generalization of safe_to_transform().
- */
- int PandaNode::
- get_unsafe_to_apply_attribs() const {
- return 0;
- }
- /**
- * Applies whatever attributes are specified in the AccumulatedAttribs object
- * (and by the attrib_types bitmask) to the vertices on this node, if
- * appropriate. If this node uses geom arrays like a GeomNode, the supplied
- * GeomTransformer may be used to unify shared arrays across multiple
- * different nodes.
- *
- * This is a generalization of xform().
- */
- void PandaNode::
- apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
- GeomTransformer &transformer) {
- if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
- const LMatrix4 &mat = attribs._transform->get_mat();
- xform(mat);
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_effects = cdata->_effects->xform(mat);
- cdata->set_fancy_bit(FB_effects, !cdata->_effects->is_empty());
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- }
- mark_bam_modified();
- }
- /**
- * Transforms the contents of this PandaNode by the indicated matrix, if it
- * means anything to do so. For most kinds of PandaNodes, this does nothing.
- */
- void PandaNode::
- xform(const LMatrix4 &) {
- }
- /**
- * Collapses this PandaNode with the other PandaNode, if possible, and returns
- * a pointer to the combined PandaNode, or NULL if the two PandaNodes cannot
- * safely be combined.
- *
- * The return value may be this, other, or a new PandaNode altogether.
- *
- * This function is called from GraphReducer::flatten(), and need not deal
- * with children; its job is just to decide whether to collapse the two
- * PandaNodes and what the collapsed PandaNode should look like.
- */
- PandaNode *PandaNode::
- combine_with(PandaNode *other) {
- // An unadorned PandaNode always combines with any other PandaNodes by
- // yielding completely. However, if we are actually some fancy PandaNode
- // type that derives from PandaNode but didn't redefine this function, we
- // should refuse to combine.
- if (is_exact_type(get_class_type())) {
- // No, we're an ordinary PandaNode.
- return other;
- } else if (other->is_exact_type(get_class_type())) {
- // We're not an ordinary PandaNode, but the other one is.
- return this;
- }
- // We're something other than an ordinary PandaNode. Don't combine.
- return nullptr;
- }
- /**
- * This is used to support NodePath::calc_tight_bounds(). It is not intended
- * to be called directly, and it has nothing to do with the normal Panda
- * bounding-volume computation.
- *
- * If the node contains any geometry, this updates min_point and max_point to
- * enclose its bounding box. found_any is to be set true if the node has any
- * geometry at all, or left alone if it has none. This method may be called
- * over several nodes, so it may enter with min_point, max_point, and
- * found_any already set.
- *
- * This function is recursive, and the return value is the transform after it
- * has been modified by this node's transform.
- */
- CPT(TransformState) PandaNode::
- calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
- const TransformState *transform, Thread *current_thread) const {
- CPT(TransformState) next_transform = transform->compose(get_transform());
- Children cr = get_children(current_thread);
- int num_children = cr.get_num_children();
- for (int i = 0; i < num_children; i++) {
- cr.get_child(i)->calc_tight_bounds(min_point, max_point,
- found_any, next_transform,
- current_thread);
- }
- return next_transform;
- }
- /**
- * This function will be called during the cull traversal to perform any
- * additional operations that should be performed at cull time. This may
- * include additional manipulation of render state or additional
- * visible/invisible decisions, or any other arbitrary operation.
- *
- * Note that this function will *not* be called unless set_cull_callback() is
- * called in the constructor of the derived class. It is necessary to call
- * set_cull_callback() to indicated that we require cull_callback() to be
- * called.
- *
- * By the time this function is called, the node has already passed the
- * bounding-volume test for the viewing frustum, and the node's transform and
- * state have already been applied to the indicated CullTraverserData object.
- *
- * The return value is true if this node should be visible, or false if it
- * should be culled.
- */
- bool PandaNode::
- cull_callback(CullTraverser *, CullTraverserData &) {
- return true;
- }
- /**
- * Should be overridden by derived classes to return true if this kind of node
- * has some restrictions on the set of children that should be rendered. Node
- * with this property include LODNodes, SwitchNodes, and SequenceNodes.
- *
- * If this function returns true, get_first_visible_child() and
- * get_next_visible_child() will be called to walk through the list of
- * children during cull, instead of iterating through the entire list. This
- * method is called after cull_callback(), so cull_callback() may be
- * responsible for the decisions as to which children are visible at the
- * moment.
- */
- bool PandaNode::
- has_selective_visibility() const {
- return false;
- }
- /**
- * Returns the index number of the first visible child of this node, or a
- * number >= get_num_children() if there are no visible children of this node.
- * This is called during the cull traversal, but only if
- * has_selective_visibility() has already returned true. See
- * has_selective_visibility().
- */
- int PandaNode::
- get_first_visible_child() const {
- return 0;
- }
- /**
- * Returns the index number of the next visible child of this node following
- * the indicated child, or a number >= get_num_children() if there are no more
- * visible children of this node. See has_selective_visibility() and
- * get_first_visible_child().
- */
- int PandaNode::
- get_next_visible_child(int n) const {
- return n + 1;
- }
- /**
- * Should be overridden by derived classes to return true if this kind of node
- * has the special property that just one of its children is visible at any
- * given time, and furthermore that the particular visible child can be
- * determined without reference to any external information (such as a
- * camera). At present, only SequenceNodes and SwitchNodes fall into this
- * category.
- *
- * If this function returns true, get_visible_child() can be called to return
- * the index of the currently-visible child.
- */
- bool PandaNode::
- has_single_child_visibility() const {
- return false;
- }
- /**
- * Returns the index number of the currently visible child of this node. This
- * is only meaningful if has_single_child_visibility() has returned true.
- */
- int PandaNode::
- get_visible_child() const {
- return 0;
- }
- /**
- * Returns true if there is some value to visiting this particular node during
- * the cull traversal for any camera, false otherwise. This will be used to
- * optimize the result of get_net_draw_show_mask(), so that any subtrees that
- * contain only nodes for which is_renderable() is false need not be visited.
- */
- bool PandaNode::
- is_renderable() const {
- return false;
- }
- /**
- * Adds the node's contents to the CullResult we are building up during the
- * cull traversal, so that it will be drawn at render time. For most nodes
- * other than GeomNodes, this is a do-nothing operation.
- */
- void PandaNode::
- add_for_draw(CullTraverser *, CullTraverserData &) {
- }
- /**
- * Returns a newly-allocated PandaNode that is a shallow copy of this one. It
- * will be a different pointer, but its internal data may or may not be shared
- * with that of the original PandaNode. No children will be copied.
- */
- PandaNode *PandaNode::
- make_copy() const {
- return new PandaNode(*this);
- }
- /**
- * Allocates and returns a complete copy of this PandaNode and the entire
- * scene graph rooted at this PandaNode. Some data may still be shared from
- * the original (e.g. vertex index tables), but nothing that will impede
- * normal use of the PandaNode.
- */
- PT(PandaNode) PandaNode::
- copy_subgraph(Thread *current_thread) const {
- InstanceMap inst_map;
- return r_copy_subgraph(inst_map, current_thread);
- }
- /**
- * Returns the number of nodes at and below this level.
- */
- int PandaNode::
- count_num_descendants() const {
- int count = 1;
- Children children = get_children();
- int num_children = children.get_num_children();
- for (int i = 0; i < num_children; ++i) {
- PandaNode *child = children.get_child(i);
- count += child->count_num_descendants();
- }
- return count;
- }
- /**
- * Adds a new child to the node. The child is added in the relative position
- * indicated by sort; if all children have the same sort index, the child is
- * added at the end.
- *
- * If the same child is added to a node more than once, the previous instance
- * is first removed.
- */
- void PandaNode::
- add_child(PandaNode *child_node, int sort, Thread *current_thread) {
- nassertv(child_node != nullptr);
- if (!verify_child_no_cycles(child_node)) {
- // Whoops, adding this child node would introduce a cycle in the scene
- // graph.
- return;
- }
- // Ensure the child_node is not deleted while we do this.
- PT(PandaNode) keep_child = child_node;
- remove_child(child_node);
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- cdata->modify_down()->insert(DownConnection(child_node, sort));
- cdata_child->modify_up()->insert(UpConnection(this));
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
- new_connection(this, child_node, pipeline_stage, current_thread);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
- force_bounds_stale();
- children_changed();
- child_node->parents_changed();
- mark_bam_modified();
- child_node->mark_bam_modified();
- }
- /**
- * Removes the nth child from the node.
- */
- void PandaNode::
- remove_child(int child_index, Thread *current_thread) {
- int pipeline_stage = current_thread->get_pipeline_stage();
- nassertv(pipeline_stage == 0);
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- PT(Down) down = cdata->modify_down();
- nassertv(child_index >= 0 && child_index < (int)down->size());
- PT(PandaNode) child_node = (*down)[child_index].get_child();
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
- current_thread);
- PT(Up) up = cdata_child->modify_up();
- down->erase(down->begin() + child_index);
- int num_erased = up->erase(UpConnection(this));
- nassertv(num_erased == 1);
- sever_connection(this, child_node, pipeline_stage, current_thread);
- force_bounds_stale(pipeline_stage, current_thread);
- children_changed();
- child_node->parents_changed();
- mark_bam_modified();
- child_node->mark_bam_modified();
- }
- /**
- * Removes the indicated child from the node. Returns true if the child was
- * removed, false if it was not already a child of the node. This will also
- * successfully remove the child if it had been stashed.
- */
- bool PandaNode::
- remove_child(PandaNode *child_node, Thread *current_thread) {
- nassertr(child_node != nullptr, false);
- // Make sure the child node is not destructed during the execution of this
- // method.
- PT(PandaNode) keep_child = child_node;
- // We have to do this for each upstream pipeline stage.
- bool any_removed = false;
- OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
- if (stage_remove_child(child_node, pipeline_stage, current_thread)) {
- any_removed = true;
- sever_connection(this, child_node, pipeline_stage, current_thread);
- force_bounds_stale(pipeline_stage, current_thread);
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
- if (any_removed) {
- // Call callback hooks.
- children_changed();
- child_node->parents_changed();
- }
- return any_removed;
- }
- /**
- * Searches for the orig_child node in the node's list of children, and
- * replaces it with the new_child instead. Returns true if the replacement is
- * made, or false if the node is not a child or if there is some other
- * problem.
- */
- bool PandaNode::
- replace_child(PandaNode *orig_child, PandaNode *new_child,
- Thread *current_thread) {
- nassertr(orig_child != nullptr, false);
- nassertr(new_child != nullptr, false);
- if (orig_child == new_child) {
- // Trivial no-op.
- return true;
- }
- if (!verify_child_no_cycles(new_child)) {
- // Whoops, adding this child node would introduce a cycle in the scene
- // graph.
- return false;
- }
- // Make sure the orig_child node is not destructed during the execution of
- // this method.
- PT(PandaNode) keep_orig_child = orig_child;
- // We have to do this for each upstream pipeline stage.
- bool any_replaced = false;
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- if (stage_replace_child(orig_child, new_child, pipeline_stage, current_thread)) {
- any_replaced = true;
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- if (any_replaced) {
- children_changed();
- orig_child->parents_changed();
- new_child->parents_changed();
- }
- return any_replaced;
- }
- /**
- * Stashes the indicated child node. This removes the child from the list of
- * active children and puts it on a special list of stashed children. This
- * child node no longer contributes to the bounding volume of the PandaNode,
- * and is not visited in normal traversals. It is invisible and uncollidable.
- * The child may later be restored by calling unstash_child().
- *
- * This can only be called from the top pipeline stage (i.e. from App).
- */
- void PandaNode::
- stash_child(int child_index, Thread *current_thread) {
- int pipeline_stage = current_thread->get_pipeline_stage();
- nassertv(pipeline_stage == 0);
- nassertv(child_index >= 0 && child_index < get_num_children());
- // Save a reference count for ourselves.
- PT(PandaNode) self = this;
- PT(PandaNode) child_node = get_child(child_index);
- int sort = get_child_sort(child_index);
- remove_child(child_index);
- {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- cdata->modify_stashed()->insert(DownConnection(child_node, sort));
- cdata_child->modify_up()->insert(UpConnection(this));
- }
- new_connection(this, child_node, pipeline_stage, current_thread);
- force_bounds_stale(pipeline_stage, current_thread);
- children_changed();
- child_node->parents_changed();
- mark_bam_modified();
- child_node->mark_bam_modified();
- }
- /**
- * Returns the indicated stashed node to normal child status. This removes
- * the child from the list of stashed children and puts it on the normal list
- * of active children. This child node once again contributes to the bounding
- * volume of the PandaNode, and will be visited in normal traversals. It is
- * visible and collidable.
- *
- * This can only be called from the top pipeline stage (i.e. from App).
- */
- void PandaNode::
- unstash_child(int stashed_index, Thread *current_thread) {
- int pipeline_stage = current_thread->get_pipeline_stage();
- nassertv(pipeline_stage == 0);
- nassertv(stashed_index >= 0 && stashed_index < get_num_stashed());
- // Save a reference count for ourselves. I don't think this should be
- // necessary, but there are occasional crashes in stash() during furniture
- // moving mode. Perhaps this will eliminate those crashes.
- PT(PandaNode) self = this;
- PT(PandaNode) child_node = get_stashed(stashed_index);
- int sort = get_stashed_sort(stashed_index);
- remove_stashed(stashed_index);
- {
- CDWriter cdata(_cycler);
- CDWriter cdata_child(child_node->_cycler);
- cdata->modify_down()->insert(DownConnection(child_node, sort));
- cdata_child->modify_up()->insert(UpConnection(this));
- }
- new_connection(this, child_node, pipeline_stage, current_thread);
- force_bounds_stale();
- children_changed();
- child_node->parents_changed();
- mark_bam_modified();
- child_node->mark_bam_modified();
- }
- /**
- * Adds a new child to the node, directly as a stashed child. The child is
- * not added in the normal sense, but will be revealed if unstash_child() is
- * called on it later.
- *
- * If the same child is added to a node more than once, the previous instance
- * is first removed.
- *
- * This can only be called from the top pipeline stage (i.e. from App).
- */
- void PandaNode::
- add_stashed(PandaNode *child_node, int sort, Thread *current_thread) {
- int pipeline_stage = current_thread->get_pipeline_stage();
- nassertv(pipeline_stage == 0);
- if (!verify_child_no_cycles(child_node)) {
- // Whoops, adding this child node would introduce a cycle in the scene
- // graph.
- return;
- }
- // Ensure the child_node is not deleted while we do this.
- PT(PandaNode) keep_child = child_node;
- remove_child(child_node);
- {
- CDWriter cdata(_cycler);
- CDWriter cdata_child(child_node->_cycler);
- cdata->modify_stashed()->insert(DownConnection(child_node, sort));
- cdata_child->modify_up()->insert(UpConnection(this));
- }
- new_connection(this, child_node, pipeline_stage, current_thread);
- // Call callback hooks.
- children_changed();
- child_node->parents_changed();
- mark_bam_modified();
- child_node->mark_bam_modified();
- }
- /**
- * Removes the nth stashed child from the node.
- */
- void PandaNode::
- remove_stashed(int child_index, Thread *current_thread) {
- int pipeline_stage = current_thread->get_pipeline_stage();
- nassertv(pipeline_stage == 0);
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- Down &stashed = *cdata->modify_stashed();
- nassertv(child_index >= 0 && child_index < (int)stashed.size());
- PT(PandaNode) child_node = stashed[child_index].get_child();
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- stashed.erase(stashed.begin() + child_index);
- int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
- nassertv(num_erased == 1);
- sever_connection(this, child_node, pipeline_stage, current_thread);
- force_bounds_stale(pipeline_stage, current_thread);
- children_changed();
- child_node->parents_changed();
- mark_bam_modified();
- child_node->mark_bam_modified();
- }
- /**
- * Removes all the children from the node at once, including stashed children.
- *
- * This can only be called from the top pipeline stage (i.e. from App).
- */
- void PandaNode::
- remove_all_children(Thread *current_thread) {
- // We have to do this for each upstream pipeline stage.
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- PT(Down) down = cdata->modify_down();
- Down::iterator di;
- for (di = down->begin(); di != down->end(); ++di) {
- PT(PandaNode) child_node = (*di).get_child();
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
- current_thread);
- cdata_child->modify_up()->erase(UpConnection(this));
- sever_connection(this, child_node, pipeline_stage, current_thread);
- child_node->parents_changed();
- child_node->mark_bam_modified();
- }
- down->clear();
- Down &stashed = *cdata->modify_stashed();
- for (di = stashed.begin(); di != stashed.end(); ++di) {
- PT(PandaNode) child_node = (*di).get_child();
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
- current_thread);
- cdata_child->modify_up()->erase(UpConnection(this));
- sever_connection(this, child_node, pipeline_stage, current_thread);
- child_node->parents_changed();
- child_node->mark_bam_modified();
- }
- stashed.clear();
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- force_bounds_stale();
- children_changed();
- mark_bam_modified();
- }
- /**
- * Moves all the children from the other node onto this node.
- *
- * Any NodePaths to child nodes of the other node are truncated, rather than
- * moved to the new parent.
- */
- void PandaNode::
- steal_children(PandaNode *other, Thread *current_thread) {
- if (other == this) {
- // Trivial.
- return;
- }
- // We do this through the high-level interface for convenience. This could
- // begin to be a problem if we have a node with hundreds of children to
- // copy; this could break down the ov_set.insert() method, which is an
- // O(n^2) operation. If this happens, we should rewrite this to do a
- // simpler add_child() operation that involves push_back() instead of
- // insert(), and then sort the down list at the end.
- int num_children = other->get_num_children();
- int i;
- for (i = 0; i < num_children; i++) {
- PandaNode *child_node = other->get_child(i);
- int sort = other->get_child_sort(i);
- add_child(child_node, sort, current_thread);
- }
- int num_stashed = other->get_num_stashed();
- for (i = 0; i < num_stashed; i++) {
- PandaNode *child_node = other->get_stashed(i);
- int sort = other->get_stashed_sort(i);
- add_stashed(child_node, sort, current_thread);
- }
- other->remove_all_children(current_thread);
- }
- /**
- * Makes another instance of all the children of the other node, copying them
- * to this node.
- */
- void PandaNode::
- copy_children(PandaNode *other, Thread *current_thread) {
- if (other == this) {
- // Trivial.
- return;
- }
- Children children = other->get_children(current_thread);
- Stashed stashed = other->get_stashed(current_thread);
- int num_children = children.get_num_children();
- int i;
- for (i = 0; i < num_children; i++) {
- PandaNode *child_node = children.get_child(i);
- int sort = children.get_child_sort(i);
- add_child(child_node, sort, current_thread);
- }
- int num_stashed = stashed.get_num_stashed();
- for (i = 0; i < num_stashed; i++) {
- PandaNode *child_node = stashed.get_stashed(i);
- int sort = stashed.get_stashed_sort(i);
- add_stashed(child_node, sort, current_thread);
- }
- }
- /**
- * Adds the indicated render attribute to the scene graph on this node. This
- * attribute will now apply to this node and everything below. If there was
- * already an attribute of the same type, it is replaced.
- */
- void PandaNode::
- set_attrib(const RenderAttrib *attrib, int override) {
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- bool any_changed = false;
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- CPT(RenderState) new_state = cdata->_state->set_attrib(attrib, override);
- if (cdata->_state != new_state) {
- cdata->_state = new_state;
- cdata->set_fancy_bit(FB_state, true);
- any_changed = true;
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- // Maybe we changed a ClipPlaneAttrib.
- if (any_changed) {
- mark_bounds_stale(current_thread);
- state_changed();
- mark_bam_modified();
- }
- }
- /**
- * Removes the render attribute of the given type from this node. This node,
- * and the subgraph below, will now inherit the indicated render attribute
- * from the nodes above this one.
- */
- void PandaNode::
- clear_attrib(int slot) {
- bool any_changed = false;
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- CPT(RenderState) new_state = cdata->_state->remove_attrib(slot);
- if (cdata->_state != new_state) {
- cdata->_state = new_state;
- cdata->set_fancy_bit(FB_state, !new_state->is_empty());
- any_changed = true;
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- // We mark the bounds stale when the state changes, in case we have changed
- // a ClipPlaneAttrib.
- if (any_changed) {
- mark_bounds_stale(current_thread);
- state_changed();
- mark_bam_modified();
- }
- }
- /**
- * Adds the indicated render effect to the scene graph on this node. If there
- * was already an effect of the same type, it is replaced.
- */
- void PandaNode::
- set_effect(const RenderEffect *effect) {
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_effects = cdata->_effects->add_effect(effect);
- cdata->set_fancy_bit(FB_effects, true);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * Removes the render effect of the given type from this node.
- */
- void PandaNode::
- clear_effect(TypeHandle type) {
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_effects = cdata->_effects->remove_effect(type);
- cdata->set_fancy_bit(FB_effects, !cdata->_effects->is_empty());
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * Sets the complete RenderState that will be applied to all nodes at this
- * level and below. (The actual state that will be applied to lower nodes is
- * based on the composition of RenderStates from above this node as well).
- * This completely replaces whatever has been set on this node via repeated
- * calls to set_attrib().
- */
- void PandaNode::
- set_state(const RenderState *state, Thread *current_thread) {
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- bool any_changed = false;
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_state != state) {
- cdata->_state = state;
- cdata->set_fancy_bit(FB_state, !state->is_empty());
- any_changed = true;
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- // Maybe we have changed a ClipPlaneAttrib.
- if (any_changed) {
- mark_bounds_stale(current_thread);
- state_changed();
- mark_bam_modified();
- }
- }
- /**
- * Sets the complete RenderEffects that will be applied this node. This
- * completely replaces whatever has been set on this node via repeated calls
- * to set_attrib().
- */
- void PandaNode::
- set_effects(const RenderEffects *effects, Thread *current_thread) {
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_effects = effects;
- cdata->set_fancy_bit(FB_effects, !effects->is_empty());
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * Sets the transform that will be applied to this node and below. This
- * defines a new coordinate space at this point in the scene graph and below.
- */
- void PandaNode::
- set_transform(const TransformState *transform, Thread *current_thread) {
- nassertv(!transform->is_invalid());
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- bool any_changed = false;
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_transform != transform) {
- cdata->_transform = transform;
- cdata->set_fancy_bit(FB_transform, !transform->is_identity());
- any_changed = true;
- if (pipeline_stage == 0) {
- if (cdata->_transform != cdata->_prev_transform) {
- do_set_dirty_prev_transform();
- }
- }
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- if (any_changed) {
- mark_bounds_stale(current_thread);
- transform_changed();
- mark_bam_modified();
- }
- }
- /**
- * Sets the transform that represents this node's "previous" position, one
- * frame ago, for the purposes of detecting motion for accurate collision
- * calculations.
- */
- void PandaNode::
- set_prev_transform(const TransformState *transform, Thread *current_thread) {
- nassertv(!transform->is_invalid());
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_prev_transform = transform;
- if (pipeline_stage == 0) {
- if (cdata->_transform != cdata->_prev_transform) {
- do_set_dirty_prev_transform();
- } else {
- do_clear_dirty_prev_transform();
- }
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * Resets the transform that represents this node's "previous" position to the
- * same as the current transform. This is not the same thing as clearing it
- * to identity.
- */
- void PandaNode::
- reset_prev_transform(Thread *current_thread) {
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- do_clear_dirty_prev_transform();
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_prev_transform = cdata->_transform;
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * Visits all nodes in the world with the _dirty_prev_transform flag--that is,
- * all nodes whose _prev_transform is different from the _transform in
- * pipeline stage 0--and resets the _prev_transform to be the same as
- * _transform.
- */
- void PandaNode::
- reset_all_prev_transform(Thread *current_thread) {
- nassertv(current_thread->get_pipeline_stage() == 0);
- PStatTimer timer(_reset_prev_pcollector, current_thread);
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- LinkedListNode *list_node = _dirty_prev_transforms._next;
- while (list_node != &_dirty_prev_transforms) {
- PandaNode *panda_node = (PandaNode *)list_node;
- nassertv(panda_node->_dirty_prev_transform);
- panda_node->_dirty_prev_transform = false;
- CDStageWriter cdata(panda_node->_cycler, 0, current_thread);
- cdata->_prev_transform = cdata->_transform;
- list_node = panda_node->_next;
- #ifndef NDEBUG
- panda_node->_prev = nullptr;
- panda_node->_next = nullptr;
- #endif // NDEBUG
- panda_node->mark_bam_modified();
- }
- _dirty_prev_transforms._prev = &_dirty_prev_transforms;
- _dirty_prev_transforms._next = &_dirty_prev_transforms;
- }
- /**
- * Associates a user-defined value with a user-defined key which is stored on
- * the node. This value has no meaning to Panda; but it is stored
- * indefinitely on the node until it is requested again.
- *
- * Each unique key stores a different string value. There is no effective
- * limit on the number of different keys that may be stored or on the length
- * of any one key's value.
- */
- void PandaNode::
- set_tag(const string &key, const string &value, Thread *current_thread) {
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_tag_data.store(key, value);
- cdata->set_fancy_bit(FB_tag, true);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * Removes the value defined for this key on this particular node. After a
- * call to clear_tag(), has_tag() will return false for the indicated key.
- */
- void PandaNode::
- clear_tag(const string &key, Thread *current_thread) {
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_tag_data.remove(key);
- cdata->set_fancy_bit(FB_tag, !cdata->_tag_data.is_empty());
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * Copies all of the tags stored on the other node onto this node. If a
- * particular tag exists on both nodes, the contents of this node's value is
- * replaced by that of the other.
- */
- void PandaNode::
- copy_tags(PandaNode *other) {
- if (other == this) {
- // Trivial.
- return;
- }
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
- CDStageReader cdatar(other->_cycler, pipeline_stage, current_thread);
- for (size_t n = 0; n < cdatar->_tag_data.size(); ++n) {
- cdataw->_tag_data.store(cdatar->_tag_data.get_key(n), cdatar->_tag_data.get_data(n));
- }
- cdataw->set_fancy_bit(FB_tag, !cdataw->_tag_data.is_empty());
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- // It's okay to copy the tags by pointer, because get_python_tags does a
- // copy-on-write.
- _python_tag_data = other->_python_tag_data;
- mark_bam_modified();
- }
- /**
- * Writes a list of all the tag keys assigned to the node to the indicated
- * stream. Writes one instance of the separator following each key (but does
- * not write a terminal separator). The value associated with each key is not
- * written.
- *
- * This is mainly for the benefit of the realtime user, to see the list of all
- * of the associated tag keys.
- */
- void PandaNode::
- list_tags(ostream &out, const string &separator) const {
- CDReader cdata(_cycler);
- for (size_t n = 0; n < cdata->_tag_data.size(); ++n) {
- if (n > 0) {
- out << separator;
- }
- out << cdata->_tag_data.get_key(n);
- }
- // We used to list the Python tags here. That's a bit awkward, though,
- // since that means calling up into Python code to print the keys. If
- // someone finds it useful, we can implement it in an extension method.
- }
- /**
- * Fills the given vector up with the list of tags on this PandaNode.
- *
- * It is the user's responsibility to ensure that the keys vector is empty
- * before making this call; otherwise, the new keys will be appended to it.
- */
- void PandaNode::
- get_tag_keys(vector_string &keys) const {
- CDReader cdata(_cycler);
- for (size_t n = 0; n < cdata->_tag_data.size(); ++n) {
- keys.push_back(cdata->_tag_data.get_key(n));
- }
- }
- /**
- * Returns a number less than 0, 0, or greater than 0, to indicate the
- * similarity of tags between this node and the other one. If this returns 0,
- * the tags are identical. If it returns other than 0, then the tags are
- * different; and the nodes may be sorted into a consistent (but arbitrary)
- * ordering based on this number.
- */
- int PandaNode::
- compare_tags(const PandaNode *other) const {
- CDReader cdata(_cycler);
- CDReader cdata_other(other->_cycler);
- const TagData &a_data = cdata->_tag_data;
- const TagData &b_data = cdata_other->_tag_data;
- size_t ai = 0;
- size_t bi = 0;
- while (ai < a_data.size() && bi < b_data.size()) {
- int cmp = strcmp(a_data.get_key(ai).c_str(), b_data.get_key(bi).c_str());
- if (cmp != 0) {
- return cmp;
- }
- cmp = strcmp(a_data.get_key(ai).c_str(), b_data.get_key(bi).c_str());
- if (cmp != 0) {
- return cmp;
- }
- ++ai;
- ++bi;
- }
- if (ai < a_data.size()) {
- // list A is longer.
- return 1;
- }
- if (bi < b_data.size()) {
- // list B is longer.
- return -1;
- }
- // We compare these by pointer, since it's problematic to call up into
- // Python from arbitrary C++ code.
- if (_python_tag_data != other->_python_tag_data) {
- return (_python_tag_data < other->_python_tag_data) ? -1 : 1;
- }
- return 0;
- }
- /**
- * Copies the TransformState, RenderState, RenderEffects, tags, Python tags,
- * and the show/hide state from the other node onto this one. Typically this
- * is used to prepare a node to replace another node in the scene graph (also
- * see replace_node()).
- */
- void PandaNode::
- copy_all_properties(PandaNode *other) {
- if (other == this) {
- // Trivial.
- return;
- }
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- bool any_transform_changed = false;
- bool any_state_changed = false;
- bool any_draw_mask_changed = false;
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
- CDStageReader cdatar(other->_cycler, pipeline_stage, current_thread);
- if (cdataw->_transform != cdatar->_transform) {
- any_transform_changed = true;
- }
- if (cdataw->_state != cdatar->_state) {
- any_state_changed = true;
- }
- if (cdataw->_draw_control_mask != cdatar->_draw_control_mask ||
- cdataw->_draw_show_mask != cdatar->_draw_show_mask) {
- any_draw_mask_changed = true;
- }
- cdataw->_transform = cdatar->_transform;
- cdataw->_prev_transform = cdatar->_prev_transform;
- cdataw->_state = cdatar->_state;
- cdataw->_effects = cdatar->_effects;
- cdataw->_draw_control_mask = cdatar->_draw_control_mask;
- cdataw->_draw_show_mask = cdatar->_draw_show_mask;
- // The collide mask becomes the union of the two masks. This is important
- // to preserve properties such as the default GeomNode bitmask.
- cdataw->_into_collide_mask |= cdatar->_into_collide_mask;
- for (size_t n = 0; n < cdatar->_tag_data.size(); ++n) {
- cdataw->_tag_data.store(cdatar->_tag_data.get_key(n), cdatar->_tag_data.get_data(n));
- }
- static const int change_bits = (FB_transform | FB_state | FB_effects |
- FB_tag | FB_draw_mask);
- cdataw->_fancy_bits =
- (cdataw->_fancy_bits & ~change_bits) |
- (cdatar->_fancy_bits & change_bits);
- if (pipeline_stage == 0) {
- if (cdataw->_transform != cdataw->_prev_transform) {
- do_set_dirty_prev_transform();
- }
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- // It's okay to copy the tags by pointer, because get_python_tags does a
- // copy-on-write.
- _python_tag_data = other->_python_tag_data;
- if (any_transform_changed || any_state_changed || any_draw_mask_changed) {
- mark_bounds_stale(current_thread);
- if (any_transform_changed) {
- transform_changed();
- }
- if (any_state_changed) {
- state_changed();
- }
- if (any_draw_mask_changed) {
- draw_mask_changed();
- }
- mark_bam_modified();
- }
- }
- /**
- * Inserts this node into the scene graph in place of the other one, and
- * removes the other node. All scene graph attributes (TransformState,
- * RenderState, etc.) are copied to this node.
- *
- * All children are moved to this node, and removed from the old node. The
- * new node is left in the same place in the old node's parent's list of
- * children.
- *
- * Even NodePaths that reference the old node are updated in-place to
- * reference the new node instead.
- *
- * This method is intended to be used to replace a node of a given type in the
- * scene graph with a node of a different type.
- */
- void PandaNode::
- replace_node(PandaNode *other) {
- // nassertv(Thread::get_current_pipeline_stage() == 0);
- if (other == this) {
- // Trivial.
- return;
- }
- // Make sure the other node is not destructed during the execution of this
- // method.
- PT(PandaNode) keep_other = other;
- // Get all the important scene graph properties.
- copy_all_properties(other);
- // Fix up the NodePaths.
- {
- LightReMutexHolder holder1(other->_paths_lock);
- LightReMutexHolder holder2(_paths_lock);
- Paths::iterator pi;
- for (pi = other->_paths.begin(); pi != other->_paths.end(); ++pi) {
- (*pi)->_node = this;
- _paths.insert(*pi);
- }
- other->_paths.clear();
- }
- // Get the children.
- steal_children(other);
- // Switch the parents.
- Thread *current_thread = Thread::get_current_thread();
- Parents other_parents = other->get_parents();
- for (size_t i = 0; i < other_parents.get_num_parents(); ++i) {
- PandaNode *parent = other_parents.get_parent(i);
- if (find_parent(parent) != -1) {
- // This node was already a child of this parent; don't change it.
- parent->remove_child(other);
- } else {
- // This node was not yet a child of this parent; now it is.
- parent->replace_child(other, this, current_thread);
- }
- }
- }
- /**
- * Sets one or more of the PandaNode::UnexpectedChange bits on, indicating
- * that the corresponding property should not change again on this node. Once
- * one of these bits has been set, if the property changes, an assertion
- * failure will be raised, which is designed to assist the developer in
- * identifying the troublesome code that modified the property unexpectedly.
- *
- * The input parameter is the union of bits that are to be set. To clear
- * these bits later, use clear_unexpected_change().
- *
- * Since this is a developer debugging tool only, this function does nothing
- * in a production (NDEBUG) build.
- */
- void PandaNode::
- set_unexpected_change(unsigned int flags) {
- #ifndef NDEBUG
- _unexpected_change_flags |= flags;
- #endif // !NDEBUG
- }
- /**
- * Returns nonzero if any of the bits in the input parameter are set on this
- * node, or zero if none of them are set. More specifically, this returns the
- * particular set of bits (masked by the input parameter) that have been set
- * on this node. See set_unexpected_change().
- *
- * Since this is a developer debugging tool only, this function always returns
- * zero in a production (NDEBUG) build.
- */
- unsigned int PandaNode::
- get_unexpected_change(unsigned int flags) const {
- #ifndef NDEBUG
- return _unexpected_change_flags & flags;
- #else
- return 0;
- #endif // !NDEBUG
- }
- /**
- * Sets one or more of the PandaNode::UnexpectedChange bits off, indicating
- * that the corresponding property may once again change on this node. See
- * set_unexpected_change().
- *
- * The input parameter is the union of bits that are to be cleared.
- *
- * Since this is a developer debugging tool only, this function does nothing
- * in a production (NDEBUG) build.
- */
- void PandaNode::
- clear_unexpected_change(unsigned int flags) {
- #ifndef NDEBUG
- _unexpected_change_flags &= ~flags;
- #endif // !NDEBUG
- }
- /**
- * Adjusts the hide/show bits of this particular node.
- *
- * These three parameters can be used to adjust the _draw_control_mask and
- * _draw_show_mask independently, which work together to provide per-camera
- * visibility for the node and its descendents.
- *
- * _draw_control_mask indicates the bits in _draw_show_mask that are
- * significant. Each different bit corresponds to a different camera (and
- * these bits are assigned via Camera::set_camera_mask()).
- *
- * Where _draw_control_mask has a 1 bit, a 1 bit in _draw_show_mask indicates
- * the node is visible to that camera, and a 0 bit indicates the node is
- * hidden to that camera. Where _draw_control_mask is 0, the node is hidden
- * only if a parent node is hidden.
- *
- * The meaning of the three parameters is as follows:
- *
- * * Wherever show_mask is 1, _draw_show_mask and _draw_control_mask will be
- * set 1. Thus, show_mask indicates the set of cameras to which the node
- * should be shown.
- *
- * * Wherever hide_mask is 1, _draw_show_mask will be set 0 and
- * _draw_control_mask will be set 1. Thus, hide_mask indicates the set of
- * cameras from which the node should be hidden.
- *
- * * Wherever clear_mask is 1, _draw_control_mask will be set 0. Thus,
- * clear_mask indicates the set of cameras from which the hidden state should
- * be inherited from a parent.
- */
- void PandaNode::
- adjust_draw_mask(DrawMask show_mask, DrawMask hide_mask, DrawMask clear_mask) {
- bool any_changed = false;
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- DrawMask draw_control_mask = (cdata->_draw_control_mask | show_mask | hide_mask) & ~clear_mask;
- DrawMask draw_show_mask = (cdata->_draw_show_mask | show_mask) & ~hide_mask;
- // The uncontrolled bits are implicitly on.
- draw_show_mask |= ~draw_control_mask;
- if (cdata->_draw_control_mask != draw_control_mask ||
- cdata->_draw_show_mask != draw_show_mask) {
- cdata->_draw_control_mask = draw_control_mask;
- cdata->_draw_show_mask = draw_show_mask;
- any_changed = true;
- }
- cdata->set_fancy_bit(FB_draw_mask, !draw_control_mask.is_zero());
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- if (any_changed) {
- mark_bounds_stale(current_thread);
- draw_mask_changed();
- mark_bam_modified();
- }
- }
- /**
- * Returns the set of bits in get_net_draw_show_mask() that have been
- * explicitly set via adjust_draw_mask(), rather than implicitly inherited.
- *
- * A 1 bit in any position of this mask indicates that (a) this node has
- * renderable children, and (b) some child of this node has made an explicit
- * hide() or show_through() call for the corresponding bit.
- */
- DrawMask PandaNode::
- get_net_draw_control_mask() const {
- Thread *current_thread = Thread::get_current_thread();
- int pipeline_stage = current_thread->get_pipeline_stage();
- CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_update != cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- PStatTimer timer(_update_bounds_pcollector);
- CDStageWriter cdataw =
- ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
- return cdataw->_net_draw_control_mask;
- }
- return cdata->_net_draw_control_mask;
- }
- /**
- * Returns the union of all draw_show_mask values--of renderable nodes only--
- * at this level and below. If any bit in this mask is 0, there is no reason
- * to traverse below this node for a camera with the corresponding
- * camera_mask.
- *
- * The bits in this mask that do not correspond to a 1 bit in the
- * net_draw_control_mask are meaningless (and will be set to 1). For bits
- * that *do* correspond to a 1 bit in the net_draw_control_mask, a 1 bit
- * indicates that at least one child should be visible, while a 0 bit
- * indicates that all children are hidden.
- */
- DrawMask PandaNode::
- get_net_draw_show_mask() const {
- Thread *current_thread = Thread::get_current_thread();
- int pipeline_stage = current_thread->get_pipeline_stage();
- CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_update != cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- PStatTimer timer(_update_bounds_pcollector);
- CDStageWriter cdataw =
- ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
- return cdataw->_net_draw_show_mask;
- }
- return cdata->_net_draw_show_mask;
- }
- /**
- * Sets the "into" CollideMask.
- *
- * This specifies the set of bits that must be shared with a CollisionNode's
- * "from" CollideMask in order for the CollisionNode to detect a collision
- * with this particular node.
- *
- * The actual CollideMask that will be set is masked by the return value from
- * get_legal_collide_mask(). Thus, the into_collide_mask cannot be set to
- * anything other than nonzero except for those types of nodes that can be
- * collided into, such as CollisionNodes and GeomNodes.
- */
- void PandaNode::
- set_into_collide_mask(CollideMask mask) {
- mask &= get_legal_collide_mask();
- bool any_changed = false;
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_into_collide_mask != mask) {
- cdata->_into_collide_mask = mask;
- any_changed = true;
- }
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- if (any_changed) {
- mark_bounds_stale(current_thread);
- mark_bam_modified();
- }
- }
- /**
- * Returns the subset of CollideMask bits that may be set for this particular
- * type of PandaNode. For most nodes, this is 0; it doesn't make sense to set
- * a CollideMask for most kinds of nodes.
- *
- * For nodes that can be collided with, such as GeomNode and CollisionNode,
- * this returns all bits on.
- */
- CollideMask PandaNode::
- get_legal_collide_mask() const {
- return CollideMask::all_off();
- }
- /**
- * Returns the union of all into_collide_mask() values set at CollisionNodes
- * at this level and below.
- */
- CollideMask PandaNode::
- get_net_collide_mask(Thread *current_thread) const {
- int pipeline_stage = current_thread->get_pipeline_stage();
- CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_update != cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- PStatTimer timer(_update_bounds_pcollector);
- CDStageWriter cdataw =
- ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
- return cdataw->_net_collide_mask;
- }
- return cdata->_net_collide_mask;
- }
- /**
- * Returns a ClipPlaneAttrib which represents the union of all of the clip
- * planes that have been turned *off* at this level and below.
- */
- CPT(RenderAttrib) PandaNode::
- get_off_clip_planes(Thread *current_thread) const {
- int pipeline_stage = current_thread->get_pipeline_stage();
- CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_update != cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- PStatTimer timer(_update_bounds_pcollector);
- CDStageWriter cdataw =
- ((PandaNode *)this)->update_cached(false, pipeline_stage, cdata);
- return cdataw->_off_clip_planes;
- }
- return cdata->_off_clip_planes;
- }
- /**
- * Walks through the scene graph beginning at this node, and does whatever
- * initialization is required to render the scene properly with the indicated
- * GSG. It is not strictly necessary to call this, since the GSG will
- * initialize itself when the scene is rendered, but this may take some of the
- * overhead away from that process.
- *
- * In particular, this will ensure that textures and vertex buffers within the
- * scene are loaded into graphics memory.
- */
- void PandaNode::
- prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state) {
- GeomTransformer transformer;
- Thread *current_thread = Thread::get_current_thread();
- r_prepare_scene(gsg, node_state, transformer, current_thread);
- }
- /**
- * Returns true if this particular node is known to be the render root of some
- * active DisplayRegion associated with the global GraphicsEngine, false
- * otherwise.
- */
- bool PandaNode::
- is_scene_root() const {
- // This function pointer has to be filled in when the global GraphicsEngine
- // is created, because we can't link with the GraphicsEngine functions
- // directly.
- if (_scene_root_func != nullptr) {
- return (*_scene_root_func)(this);
- }
- return false;
- }
- /**
- * Returns true if this particular node is in a live scene graph: that is, it
- * is a child or descendent of a node that is itself a scene root. If this is
- * true, this node may potentially be traversed by the render traverser.
- * Stashed nodes don't count for this purpose, but hidden nodes do.
- */
- bool PandaNode::
- is_under_scene_root() const {
- if (is_scene_root()) {
- return true;
- }
- Parents parents = get_parents();
- for (size_t i = 0; i < parents.get_num_parents(); ++i) {
- PandaNode *parent = parents.get_parent(i);
- if (parent->find_stashed((PandaNode *)this) == -1) {
- if (parent->is_under_scene_root()) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- *
- */
- void PandaNode::
- output(ostream &out) const {
- out << get_type() << " " << get_name();
- }
- /**
- *
- */
- void PandaNode::
- write(ostream &out, int indent_level) const {
- indent(out, indent_level) << *this;
- if (has_tags()) {
- out << " [";
- list_tags(out, " ");
- out << "]";
- }
- CPT(TransformState) transform = get_transform();
- if (!transform->is_identity()) {
- out << " " << *transform;
- }
- CPT(RenderState) state = get_state();
- if (!state->is_empty()) {
- out << " " << *state;
- }
- CPT(RenderEffects) effects = get_effects();
- if (!effects->is_empty()) {
- out << " " << *effects;
- }
- DrawMask draw_control_mask = get_draw_control_mask();
- if (!draw_control_mask.is_zero()) {
- DrawMask draw_show_mask = get_draw_show_mask();
- if (!(draw_control_mask & _overall_bit).is_zero()) {
- if (!(draw_show_mask & _overall_bit).is_zero()) {
- out << " (show_through)";
- } else {
- out << " (hidden)";
- }
- }
- if (!(draw_control_mask & ~_overall_bit).is_zero()) {
- draw_control_mask &= ~_overall_bit;
- if (!(draw_show_mask & draw_control_mask).is_zero()) {
- out << " (per-camera show_through)";
- }
- if (!(~draw_show_mask & draw_control_mask).is_zero()) {
- out << " (per-camera hidden)";
- }
- }
- }
- out << "\n";
- }
- /**
- * Specifies the desired type of bounding volume that will be created for this
- * node. This is normally BoundingVolume::BT_default, which means to set the
- * type according to the config variable "bounds-type".
- *
- * If this is BT_sphere or BT_box, a BoundingSphere or BoundingBox is
- * explicitly created. If it is BT_best, the appropriate type to best enclose
- * the node's children is created.
- *
- * This affects the bounding volume returned by get_bounds(), which is not
- * exactly the same bounding volume modified by set_bounds(), because a new
- * bounding volume has to be created that includes this node and all of its
- * children.
- */
- void PandaNode::
- set_bounds_type(BoundingVolume::BoundsType bounds_type) {
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->_bounds_type = bounds_type;
- mark_bounds_stale(pipeline_stage, current_thread);
- // GeomNodes, CollisionNodes, and PGItems all have an internal bounds that
- // may need to be updated when the bounds_type changes.
- mark_internal_bounds_stale(pipeline_stage, current_thread);
- mark_bam_modified();
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- }
- /**
- * Returns the bounding volume type set with set_bounds_type().
- */
- BoundingVolume::BoundsType PandaNode::
- get_bounds_type() const {
- CDReader cdata(_cycler);
- return cdata->_bounds_type;
- }
- /**
- * Resets the bounding volume so that it is the indicated volume. When it is
- * explicitly set, the bounding volume will no longer be automatically
- * computed according to the contents of the node itself, for nodes like
- * GeomNodes and TextNodes that contain substance (but the bounding volume
- * will still be automatically expanded to include its children).
- *
- * Call clear_bounds() if you would like to return the bounding volume to its
- * default behavior later.
- */
- void PandaNode::
- set_bounds(const BoundingVolume *volume) {
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- if (volume == nullptr) {
- cdata->_user_bounds = nullptr;
- } else {
- cdata->_user_bounds = volume->make_copy();
- }
- mark_bounds_stale(pipeline_stage, current_thread);
- mark_bam_modified();
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- }
- /**
- * Deprecated. Use set_bounds() instead.
- */
- void PandaNode::
- set_bound(const BoundingVolume *volume) {
- pgraph_cat.warning()
- << "Deprecated PandaNode::set_bound() called. Use set_bounds() instead.\n";
- set_bounds(volume);
- }
- /**
- * Returns the external bounding volume of this node: a bounding volume that
- * contains the user bounding volume, the internal bounding volume, and all of
- * the children's bounding volumes.
- */
- CPT(BoundingVolume) PandaNode::
- get_bounds(Thread *current_thread) const {
- int pipeline_stage = current_thread->get_pipeline_stage();
- CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_bounds_update != cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- CPT(BoundingVolume) result;
- {
- PStatTimer timer(_update_bounds_pcollector);
- CDStageWriter cdataw =
- ((PandaNode *)this)->update_cached(true, pipeline_stage, cdata);
- result = cdataw->_external_bounds;
- }
- return result;
- }
- return cdata->_external_bounds;
- }
- /**
- * This flavor of get_bounds() return the external bounding volume, and also
- * fills in seq with the bounding volume's current sequence number. When this
- * sequence number changes, it indicates that the bounding volume might have
- * changed, e.g. because some nested child's bounding volume has changed.
- *
- * Although this might occasionally increment without changing the bounding
- * volume, the bounding volume will never change without incrementing this
- * counter, so as long as this counter remains unchanged you can be confident
- * the bounding volume is also unchanged.
- */
- CPT(BoundingVolume) PandaNode::
- get_bounds(UpdateSeq &seq, Thread *current_thread) const {
- int pipeline_stage = current_thread->get_pipeline_stage();
- CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_bounds_update != cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- CPT(BoundingVolume) result;
- {
- PStatTimer timer(_update_bounds_pcollector);
- CDStageWriter cdataw =
- ((PandaNode *)this)->update_cached(true, pipeline_stage, cdata);
- result = cdataw->_external_bounds;
- seq = cdataw->_last_bounds_update;
- }
- return result;
- }
- seq = cdata->_last_bounds_update;
- return cdata->_external_bounds;
- }
- /**
- * Returns the total number of vertices that will be rendered by this node and
- * all of its descendents.
- *
- * This is not necessarily an accurate count of vertices that will actually be
- * rendered, since this will include all vertices of all LOD's, and it will
- * also include hidden nodes. It may also omit or only approximate certain
- * kinds of dynamic geometry. However, it will not include stashed nodes.
- */
- int PandaNode::
- get_nested_vertices(Thread *current_thread) const {
- int pipeline_stage = current_thread->get_pipeline_stage();
- CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_bounds_update != cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- int result;
- {
- PStatTimer timer(_update_bounds_pcollector);
- CDStageWriter cdataw =
- ((PandaNode *)this)->update_cached(true, pipeline_stage, cdata);
- result = cdataw->_nested_vertices;
- }
- return result;
- }
- return cdata->_nested_vertices;
- }
- /**
- * Indicates that the bounding volume, or something that influences the
- * bounding volume (or any of the other things stored in CData, like
- * net_collide_mask), may have changed for this node, and that it must be
- * recomputed.
- *
- * With no parameters, this means to iterate through all stages including and
- * upstream of the current pipeline stage.
- *
- * This method is intended for internal use; usually it is not necessary for a
- * user to call this directly. It will be called automatically by derived
- * classes when appropriate.
- */
- void PandaNode::
- mark_bounds_stale(Thread *current_thread) const {
- OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
- mark_bounds_stale(pipeline_stage, current_thread);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
- }
- /**
- * Should be called by a derived class to mark the internal bounding volume
- * stale, so that compute_internal_bounds() will be called when the bounding
- * volume is next requested.
- *
- * With no parameters, this means to iterate through all stages including and
- * upstream of the current pipeline stage.
- *
- * It is normally not necessary to call this method directly; each node should
- * be responsible for calling it when its internals have changed.
- */
- void PandaNode::
- mark_internal_bounds_stale(Thread *current_thread) {
- OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
- mark_internal_bounds_stale(pipeline_stage, current_thread);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
- }
- /**
- * A simple downcast check. Returns true if this kind of node happens to
- * inherit from GeomNode, false otherwise.
- *
- * This is provided as a a faster alternative to calling
- * is_of_type(GeomNode::get_class_type()), since this test is so important to
- * rendering.
- */
- bool PandaNode::
- is_geom_node() const {
- return false;
- }
- /**
- * A simple downcast check. Returns true if this kind of node happens to
- * inherit from LODNode, false otherwise.
- *
- * This is provided as a a faster alternative to calling
- * is_of_type(LODNode::get_class_type()).
- */
- bool PandaNode::
- is_lod_node() const {
- return false;
- }
- /**
- * A simple downcast check. Returns true if this kind of node happens to
- * inherit from CollisionNode, false otherwise.
- *
- * This is provided as a a faster alternative to calling
- * is_of_type(CollisionNode::get_class_type()).
- */
- bool PandaNode::
- is_collision_node() const {
- return false;
- }
- /**
- * Cross-casts the node to a Light pointer, if it is one of the four kinds of
- * Light nodes, or returns NULL if it is not.
- */
- Light *PandaNode::
- as_light() {
- return nullptr;
- }
- /**
- * Returns true if this is an AmbientLight, false if it is not a light, or it
- * is some other kind of light.
- */
- bool PandaNode::
- is_ambient_light() const {
- return false;
- }
- /**
- * Reads the bytes created by a previous call to encode_to_bam_stream(), and
- * extracts and returns the single object on those bytes. Returns NULL on
- * error.
- *
- * This method is intended to replace decode_raw_from_bam_stream() when you
- * know the stream in question returns an object of type PandaNode, allowing
- * for easier reference count management. Note that the caller is still
- * responsible for maintaining the reference count on the return value.
- */
- PT(PandaNode) PandaNode::
- decode_from_bam_stream(vector_uchar data, BamReader *reader) {
- TypedWritable *object;
- ReferenceCount *ref_ptr;
- if (TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, std::move(data), reader)) {
- return DCAST(PandaNode, object);
- } else {
- return nullptr;
- }
- }
- /**
- * Returns the node's internal bounding volume. This is the bounding volume
- * around the node alone, without including children.
- */
- CPT(BoundingVolume) PandaNode::
- get_internal_bounds(int pipeline_stage, Thread *current_thread) const {
- while (true) {
- UpdateSeq mark;
- {
- CDStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_user_bounds != nullptr) {
- return cdata->_user_bounds;
- }
- if (cdata->_internal_bounds_mark == cdata->_internal_bounds_computed) {
- return cdata->_internal_bounds;
- }
- mark = cdata->_internal_bounds_mark;
- }
- // First, call compute_internal_bounds without acquiring the lock. This
- // avoids a deadlock condition.
- CPT(BoundingVolume) internal_bounds;
- int internal_vertices;
- compute_internal_bounds(internal_bounds, internal_vertices,
- pipeline_stage, current_thread);
- nassertr(!internal_bounds.is_null(), nullptr);
- // Now, acquire the lock, and apply the above-computed bounds.
- CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage);
- if (cdataw->_internal_bounds_mark == mark) {
- cdataw->_internal_bounds_computed = mark;
- cdataw->_internal_bounds = internal_bounds;
- cdataw->_internal_vertices = internal_vertices;
- ((PandaNode *)this)->mark_bam_modified();
- return cdataw->_internal_bounds;
- }
- // Dang, someone in another thread incremented _internal_bounds_mark while
- // we weren't holding the lock. That means we need to go back and do it
- // again.
- }
- }
- /**
- * Returns the total number of vertices that will be rendered by this
- * particular node alone, not accounting for its children.
- *
- * This may not include all vertices for certain dynamic effects.
- */
- int PandaNode::
- get_internal_vertices(int pipeline_stage, Thread *current_thread) const {
- while (true) {
- UpdateSeq mark;
- {
- CDStageReader cdata(_cycler, pipeline_stage, current_thread);
- if (cdata->_internal_bounds_mark == cdata->_internal_bounds_computed) {
- return cdata->_internal_vertices;
- }
- mark = cdata->_internal_bounds_mark;
- }
- // First, call compute_internal_bounds without acquiring the lock. This
- // avoids a deadlock condition.
- CPT(BoundingVolume) internal_bounds;
- int internal_vertices;
- compute_internal_bounds(internal_bounds, internal_vertices,
- pipeline_stage, current_thread);
- nassertr(!internal_bounds.is_null(), 0);
- // Now, acquire the lock, and apply the above-computed bounds.
- CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage);
- if (cdataw->_internal_bounds_mark == mark) {
- cdataw->_internal_bounds_computed = mark;
- cdataw->_internal_bounds = internal_bounds;
- cdataw->_internal_vertices = internal_vertices;
- ((PandaNode *)this)->mark_bam_modified();
- return cdataw->_internal_vertices;
- }
- // Dang, someone in another thread incremented _internal_bounds_mark while
- // we weren't holding the lock. That means we need to go back and do it
- // again.
- }
- }
- /**
- * This is provided as an alternate way for a node to set its own internal
- * bounds, rather than overloading compute_internal_bounds(). If this method
- * is called, the internal bounding volume will immediately be set to the
- * indicated pointer.
- */
- void PandaNode::
- set_internal_bounds(const BoundingVolume *volume) {
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
- cdataw->_internal_bounds = volume;
- cdataw->_internal_bounds_computed = cdataw->_internal_bounds_mark;
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bounds_stale(current_thread);
- mark_bam_modified();
- }
- /**
- * Similar to mark_bounds_stale(), except that the parents of this node marked
- * stale even if this node was already considered stale.
- *
- * With no parameters, this means to iterate through all stages including and
- * upstream of the current pipeline stage.
- */
- void PandaNode::
- force_bounds_stale(Thread *current_thread) {
- OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler, current_thread) {
- force_bounds_stale(pipeline_stage, current_thread);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(_cycler);
- }
- /**
- * Similar to mark_bounds_stale(), except that the parents of this node marked
- * stale even if this node was already considered stale.
- */
- void PandaNode::
- force_bounds_stale(int pipeline_stage, Thread *current_thread) {
- {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- ++cdata->_next_update;
- mark_bam_modified();
- // It is important that we allow this lock to be dropped before we
- // continue up the graph; otherwise, we risk deadlock from another thread
- // walking down the graph.
- }
- // It is similarly important that we use get_parents() here to copy the
- // parents list, instead of keeping the lock open while we walk through the
- // parents list directly on the node.
- Parents parents;
- {
- CDStageReader cdata(_cycler, pipeline_stage, current_thread);
- parents = Parents(cdata);
- }
- int num_parents = parents.get_num_parents();
- for (int i = 0; i < num_parents; ++i) {
- PandaNode *parent = parents.get_parent(i);
- parent->mark_bounds_stale(pipeline_stage, current_thread);
- }
- }
- /**
- * Recursively calls Geom::mark_bounds_stale() on every Geom at this node and
- * below.
- */
- void PandaNode::
- r_mark_geom_bounds_stale(Thread *current_thread) {
- Children children = get_children(current_thread);
- size_t i;
- for (i = 0; i < children.get_num_children(); i++) {
- PandaNode *child = children.get_child(i);
- child->r_mark_geom_bounds_stale(current_thread);
- }
- Stashed stashed = get_stashed(current_thread);
- for (i = 0; i < stashed.get_num_stashed(); i++) {
- PandaNode *child = stashed.get_stashed(i);
- child->r_mark_geom_bounds_stale(current_thread);
- }
- }
- /**
- * Returns a newly-allocated BoundingVolume that represents the internal
- * contents of the node. Should be overridden by PandaNode classes that
- * contain something internally.
- */
- void PandaNode::
- compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
- int &internal_vertices,
- int pipeline_stage,
- Thread *current_thread) const {
- internal_bounds = new BoundingSphere;
- internal_vertices = 0;
- }
- /**
- * Called after a scene graph update that either adds or remove parents from
- * this node, this just provides a hook for derived PandaNode objects that
- * need to update themselves based on the set of parents the node has.
- */
- void PandaNode::
- parents_changed() {
- nassertv((_unexpected_change_flags & UC_parents) == 0);
- }
- /**
- * Called after a scene graph update that either adds or remove children from
- * this node, this just provides a hook for derived PandaNode objects that
- * need to update themselves based on the set of children the node has.
- */
- void PandaNode::
- children_changed() {
- nassertv((_unexpected_change_flags & UC_children) == 0);
- }
- /**
- * Called after the node's transform has been changed for any reason, this
- * just provides a hook so derived classes can do something special in this
- * case.
- */
- void PandaNode::
- transform_changed() {
- nassertv((_unexpected_change_flags & UC_transform) == 0);
- }
- /**
- * Called after the node's RenderState has been changed for any reason, this
- * just provides a hook so derived classes can do something special in this
- * case.
- */
- void PandaNode::
- state_changed() {
- nassertv((_unexpected_change_flags & UC_state) == 0);
- }
- /**
- * Called after the node's DrawMask has been changed for any reason, this just
- * provides a hook so derived classes can do something special in this case.
- */
- void PandaNode::
- draw_mask_changed() {
- nassertv((_unexpected_change_flags & UC_draw_mask) == 0);
- }
- /**
- * This is the recursive implementation of copy_subgraph(). It returns a copy
- * of the entire subgraph rooted at this node.
- *
- * Note that it includes the parameter inst_map, which is a map type, and is
- * not (and cannot be) exported from PANDA.DLL. Thus, any derivative of
- * PandaNode that is not also a member of PANDA.DLL *cannot* access this map.
- */
- PT(PandaNode) PandaNode::
- r_copy_subgraph(PandaNode::InstanceMap &inst_map, Thread *current_thread) const {
- PT(PandaNode) copy = make_copy();
- nassertr(copy != nullptr, nullptr);
- if (copy->get_type() != get_type()) {
- pgraph_cat.warning()
- << "Don't know how to copy nodes of type " << get_type() << "\n";
- if (no_unsupported_copy) {
- nassert_raise("unsupported copy");
- return nullptr;
- }
- }
- copy->r_copy_children(this, inst_map, current_thread);
- return copy;
- }
- /**
- * This is called by r_copy_subgraph(); the copy has already been made of this
- * particular node (and this is the copy); this function's job is to copy all
- * of the children from the original.
- *
- * Note that it includes the parameter inst_map, which is a map type, and is
- * not (and cannot be) exported from PANDA.DLL. Thus, any derivative of
- * PandaNode that is not also a member of PANDA.DLL *cannot* access this map,
- * and probably should not even override this function.
- */
- void PandaNode::
- r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
- Thread *current_thread) {
- CDReader from_cdata(from->_cycler, current_thread);
- CPT(Down) from_down = from_cdata->get_down();
- Down::const_iterator di;
- for (di = from_down->begin(); di != from_down->end(); ++di) {
- int sort = (*di).get_sort();
- PandaNode *source_child = (*di).get_child();
- PT(PandaNode) dest_child;
- // Check to see if we have already copied this child. If we have, use the
- // copy. In this way, a subgraph that contains instances will be
- // correctly duplicated into another subgraph that also contains its own
- // instances.
- InstanceMap::const_iterator ci;
- ci = inst_map.find(source_child);
- if (ci != inst_map.end()) {
- dest_child = (*ci).second;
- } else {
- dest_child = source_child->r_copy_subgraph(inst_map, current_thread);
- inst_map[source_child] = dest_child;
- }
- quick_add_new_child(dest_child, sort, current_thread);
- }
- }
- /**
- * The recursive implementation of prepare_scene(). Don't call this directly;
- * call PandaNode::prepare_scene() or NodePath::prepare_scene() instead.
- */
- void PandaNode::
- r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
- GeomTransformer &transformer, Thread *current_thread) {
- Children children = get_children(current_thread);
- // We must call get_num_children() each time through the loop, in case we're
- // running SIMPLE_THREADS and we get interrupted.
- size_t i;
- for (i = 0; i < children.get_num_children(); i++) {
- PandaNode *child = children.get_child(i);
- CPT(RenderState) child_state = node_state->compose(child->get_state());
- child->r_prepare_scene(gsg, child_state, transformer, current_thread);
- }
- Stashed stashed = get_stashed(current_thread);
- for (i = 0; i < stashed.get_num_stashed(); i++) {
- PandaNode *child = stashed.get_stashed(i);
- CPT(RenderState) child_state = node_state->compose(child->get_state());
- child->r_prepare_scene(gsg, child_state, transformer, current_thread);
- }
- }
- /**
- * Intended to be called in the constructor by any subclass that defines
- * cull_callback(), this sets up the flags to indicate that the cullback needs
- * to be called.
- */
- void PandaNode::
- set_cull_callback() {
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->set_fancy_bit(FB_cull_callback, true);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * disables the call back
- */
- void PandaNode::
- disable_cull_callback() {
- Thread *current_thread = Thread::get_current_thread();
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- cdata->set_fancy_bit(FB_cull_callback, false);
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
- }
- /**
- * The private implementation of remove_child(), for a particular pipeline
- * stage.
- */
- bool PandaNode::
- stage_remove_child(PandaNode *child_node, int pipeline_stage,
- Thread *current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- // First, look for the parent in the child's up list, to ensure the child is
- // known.
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
- current_thread);
- int parent_index = child_node->do_find_parent(this, cdata_child);
- if (parent_index < 0) {
- // Nope, no relation.
- return false;
- }
- PT(Down) down = cdata->modify_down();
- int child_index = do_find_child(child_node, down);
- if (child_index >= 0) {
- // The child exists; remove it.
- down->erase(down->begin() + child_index);
- int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
- nassertr(num_erased == 1, false);
- return true;
- }
- PT(Down) stashed = cdata->modify_stashed();
- int stashed_index = do_find_child(child_node, stashed);
- if (stashed_index >= 0) {
- // The child has been stashed; remove it.
- stashed->erase(stashed->begin() + stashed_index);
- int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
- nassertr(num_erased == 1, false);
- return true;
- }
- // Never heard of this child. This shouldn't be possible, because the
- // parent was in the child's up list, above. Must be some internal error.
- nassertr(false, false);
- return false;
- }
- /**
- * The private implementation of replace_child(), for a particular pipeline
- * stage.
- */
- bool PandaNode::
- stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
- int pipeline_stage, Thread *current_thread) {
- {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- CDStageWriter cdata_orig_child(orig_child->_cycler, pipeline_stage, current_thread);
- CDStageWriter cdata_new_child(new_child->_cycler, pipeline_stage, current_thread);
- // First, look for the parent in the child's up list, to ensure the child
- // is known.
- int parent_index = orig_child->do_find_parent(this, cdata_orig_child);
- if (parent_index < 0) {
- // Nope, no relation.
- return false;
- }
- if (orig_child == new_child) {
- // Trivial no-op.
- return true;
- }
- // Don't let orig_child be destructed yet.
- PT(PandaNode) keep_orig_child = orig_child;
- // If we already have new_child as a child, remove it first.
- if (stage_remove_child(new_child, pipeline_stage, current_thread)) {
- sever_connection(this, new_child, pipeline_stage, current_thread);
- }
- PT(Down) down = cdata->modify_down();
- int child_index = do_find_child(orig_child, down);
- if (child_index >= 0) {
- // The child exists; replace it.
- DownConnection &dc = (*down)[child_index];
- nassertr(dc.get_child() == orig_child, false);
- dc.set_child(new_child);
- } else {
- PT(Down) stashed = cdata->modify_stashed();
- int stashed_index = do_find_child(orig_child, stashed);
- if (stashed_index >= 0) {
- // The child has been stashed; remove it.
- DownConnection &dc = (*stashed)[stashed_index];
- nassertr(dc.get_child() == orig_child, false);
- dc.set_child(new_child);
- } else {
- // Never heard of this child. This shouldn't be possible, because the
- // parent was in the child's up list, above. Must be some internal
- // error.
- nassertr(false, false);
- return false;
- }
- }
- // Now adjust the bookkeeping on both children.
- cdata_new_child->modify_up()->insert(UpConnection(this));
- int num_erased = cdata_orig_child->modify_up()->erase(UpConnection(this));
- nassertr(num_erased == 1, false);
- }
- sever_connection(this, orig_child, pipeline_stage, current_thread);
- new_connection(this, new_child, pipeline_stage, current_thread);
- force_bounds_stale(pipeline_stage, current_thread);
- orig_child->parents_changed();
- new_child->parents_changed();
- mark_bam_modified();
- orig_child->mark_bam_modified();
- new_child->mark_bam_modified();
- return true;
- }
- /**
- * Similar to add_child(), but performs fewer checks. The purpose of this
- * method is to add a child node that was newly constructed, to a parent node
- * that was newly constructed, so we know we have to make fewer sanity checks.
- * This is a private method; do not call it directly.
- */
- void PandaNode::
- quick_add_new_child(PandaNode *child_node, int sort, Thread *current_thread) {
- // Apply this operation to the current stage as well as to all upstream
- // stages.
- OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
- CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- cdata->modify_down()->insert(DownConnection(child_node, sort));
- cdata_child->modify_up()->insert(UpConnection(this));
- }
- CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- }
- /**
- * Raises an assertion when a graph cycle attempt is detected (and aborted).
- */
- void PandaNode::
- report_cycle(PandaNode *child_node) {
- ostringstream strm;
- strm << "Detected attempt to create a cycle in the scene graph: "
- << NodePath::any_path(this) << " : " << *child_node;
- nassert_raise(strm.str());
- }
- /**
- * Returns true if the indicated node is this node, or any ancestor of this
- * node; or false if it is not in this node's ancestry.
- */
- bool PandaNode::
- find_node_above(PandaNode *node) {
- if (node == this) {
- return true;
- }
- Parents parents = get_parents();
- for (size_t i = 0; i < parents.get_num_parents(); ++i) {
- PandaNode *parent = parents.get_parent(i);
- if (parent->find_node_above(node)) {
- return true;
- }
- }
- return false;
- }
- /**
- * Creates a new parent-child relationship, and returns the new
- * NodePathComponent. If the child was already attached to the indicated
- * parent, repositions it and returns the original NodePathComponent.
- *
- * This operation is automatically propagated back up to pipeline 0, from the
- * specified pipeline stage.
- */
- PT(NodePathComponent) PandaNode::
- attach(NodePathComponent *parent, PandaNode *child_node, int sort,
- int pipeline_stage, Thread *current_thread) {
- if (parent == nullptr) {
- // Attaching to NULL means to create a new "instance" with no attachments,
- // and no questions asked.
- PT(NodePathComponent) child =
- new NodePathComponent(child_node, nullptr,
- pipeline_stage, current_thread);
- LightReMutexHolder holder(child_node->_paths_lock);
- child_node->_paths.insert(child);
- return child;
- }
- // See if the child was already attached to the parent. If it was, we'll
- // use that same NodePathComponent.
- PT(NodePathComponent) child = get_component(parent, child_node, pipeline_stage, current_thread);
- if (child == nullptr) {
- // The child was not already attached to the parent, so get a new
- // component.
- child = get_top_component(child_node, true, pipeline_stage, current_thread);
- }
- reparent(parent, child, sort, false, pipeline_stage, current_thread);
- return child;
- }
- /**
- * Breaks a parent-child relationship.
- *
- * This operation is automatically propagated back up to pipeline 0, from the
- * specified pipeline stage.
- */
- void PandaNode::
- detach(NodePathComponent *child, int pipeline_stage, Thread *current_thread) {
- nassertv(child != nullptr);
- for (int pipeline_stage_i = pipeline_stage;
- pipeline_stage_i >= 0;
- --pipeline_stage_i) {
- detach_one_stage(child, pipeline_stage_i, current_thread);
- }
- child->get_node()->parents_changed();
- }
- /**
- * Breaks a parent-child relationship.
- *
- * This operation is not automatically propagated upstream. It is applied to
- * the indicated pipeline stage only.
- */
- void PandaNode::
- detach_one_stage(NodePathComponent *child, int pipeline_stage,
- Thread *current_thread) {
- nassertv(child != nullptr);
- if (child->is_top_node(pipeline_stage, current_thread)) {
- return;
- }
- PT(PandaNode) child_node = child->get_node();
- PT(PandaNode) parent_node = child->get_next(pipeline_stage, current_thread)->get_node();
- CDStageWriter cdata_parent(parent_node->_cycler, pipeline_stage, current_thread);
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- int parent_index = child_node->do_find_parent(parent_node, cdata_child);
- if (parent_index >= 0) {
- // Now look for the child and break the actual connection.
- // First, look for and remove the parent node from the child's up list.
- int num_erased = cdata_child->modify_up()->erase(UpConnection(parent_node));
- nassertv(num_erased == 1);
- // Now, look for and remove the child node from the parent's down list.
- // We also check in the stashed list, in case the child node has been
- // stashed.
- Down::iterator di;
- bool found = false;
- PT(Down) down = cdata_parent->modify_down();
- for (di = down->begin(); di != down->end(); ++di) {
- if ((*di).get_child() == child_node) {
- down->erase(di);
- found = true;
- break;
- }
- }
- if (!found) {
- PT(Down) stashed = cdata_parent->modify_stashed();
- for (di = stashed->begin(); di != stashed->end(); ++di) {
- if ((*di).get_child() == child_node) {
- stashed->erase(di);
- found = true;
- break;
- }
- }
- }
- nassertv(found);
- }
- // Finally, break the NodePathComponent connection.
- sever_connection(parent_node, child_node, pipeline_stage, current_thread);
- parent_node->force_bounds_stale(pipeline_stage, current_thread);
- parent_node->children_changed();
- parent_node->mark_bam_modified();
- }
- /**
- * Switches a node from one parent to another. Returns true if the new
- * connection is allowed, or false if it conflicts with another instance (that
- * is, another instance of the child is already attached to the indicated
- * parent).
- *
- * This operation is automatically propagated back up to pipeline 0, from the
- * specified pipeline stage.
- */
- bool PandaNode::
- reparent(NodePathComponent *new_parent, NodePathComponent *child, int sort,
- bool as_stashed, int pipeline_stage, Thread *current_thread) {
- bool any_ok = false;
- if (new_parent != nullptr &&
- !new_parent->get_node()->verify_child_no_cycles(child->get_node())) {
- // Whoops, adding this child node would introduce a cycle in the scene
- // graph.
- return false;
- }
- for (int pipeline_stage_i = pipeline_stage;
- pipeline_stage_i >= 0;
- --pipeline_stage_i) {
- if (reparent_one_stage(new_parent, child, sort, as_stashed,
- pipeline_stage_i, current_thread)) {
- any_ok = true;
- }
- }
- if (new_parent != nullptr) {
- new_parent->get_node()->children_changed();
- new_parent->get_node()->mark_bam_modified();
- }
- child->get_node()->parents_changed();
- child->get_node()->mark_bam_modified();
- return any_ok;
- }
- /**
- * Switches a node from one parent to another. Returns true if the new
- * connection is allowed, or false if it conflicts with another instance (that
- * is, another instance of the child is already attached to the indicated
- * parent).
- *
- * This operation is not automatically propagated upstream. It is applied to
- * the indicated pipeline stage only.
- */
- bool PandaNode::
- reparent_one_stage(NodePathComponent *new_parent, NodePathComponent *child,
- int sort, bool as_stashed, int pipeline_stage,
- Thread *current_thread) {
- nassertr(child != nullptr, false);
- // Keep a reference count to the new parent, since detaching the child might
- // lose the count.
- PT(NodePathComponent) keep_parent = new_parent;
- if (!child->is_top_node(pipeline_stage, current_thread)) {
- detach(child, pipeline_stage, current_thread);
- }
- if (new_parent != nullptr) {
- PandaNode *child_node = child->get_node();
- PandaNode *parent_node = new_parent->get_node();
- {
- CDStageReader cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- int parent_index = child_node->do_find_parent(parent_node, cdata_child);
- if (parent_index >= 0) {
- // Whoops, there's already another instance of the child there.
- return false;
- }
- }
- // Redirect the connection to the indicated new parent.
- child->set_next(new_parent, pipeline_stage, current_thread);
- // Now reattach the child node at the indicated sort position.
- {
- CDStageWriter cdata_parent(parent_node->_cycler, pipeline_stage, current_thread);
- CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- if (as_stashed) {
- cdata_parent->modify_stashed()->insert(DownConnection(child_node, sort));
- } else {
- cdata_parent->modify_down()->insert(DownConnection(child_node, sort));
- }
- cdata_child->modify_up()->insert(UpConnection(parent_node));
- #ifndef NDEBUG
- // The NodePathComponent should already be in the set.
- {
- LightReMutexHolder holder(child_node->_paths_lock);
- nassertr(child_node->_paths.find(child) != child_node->_paths.end(), false);
- }
- #endif // NDEBUG
- }
- child_node->fix_path_lengths(pipeline_stage, current_thread);
- parent_node->force_bounds_stale(pipeline_stage, current_thread);
- }
- return true;
- }
- /**
- * Returns the NodePathComponent based on the indicated child of the given
- * parent, or NULL if there is no such parent-child relationship.
- */
- PT(NodePathComponent) PandaNode::
- get_component(NodePathComponent *parent, PandaNode *child_node,
- int pipeline_stage, Thread *current_thread) {
- nassertr(parent != nullptr, nullptr);
- PandaNode *parent_node = parent->get_node();
- LightReMutexHolder holder(child_node->_paths_lock);
- // First, walk through the list of NodePathComponents we already have on the
- // child, looking for one that already exists, referencing the indicated
- // parent component.
- Paths::const_iterator pi;
- for (pi = child_node->_paths.begin(); pi != child_node->_paths.end(); ++pi) {
- if ((*pi)->get_next(pipeline_stage, current_thread) == parent) {
- // If we already have such a component, just return it.
- return (*pi);
- }
- }
- // We don't already have a NodePathComponent referring to this parent-child
- // relationship. Are they actually related?
- CDStageReader cdata_child(child_node->_cycler, pipeline_stage, current_thread);
- int parent_index = child_node->do_find_parent(parent_node, cdata_child);
- if (parent_index >= 0) {
- // They are. Create and return a new one.
- PT(NodePathComponent) child =
- new NodePathComponent(child_node, parent, pipeline_stage, current_thread);
- child_node->_paths.insert(child);
- return child;
- } else {
- // They aren't related. Return NULL.
- return nullptr;
- }
- }
- /**
- * Returns a NodePathComponent referencing the indicated node as a singleton.
- * It is invalid to call this for a node that has parents, unless you are
- * about to create a new instance (and immediately reconnect the
- * NodePathComponent elsewhere).
- *
- * If force is true, this will always return something, even if it needs to
- * create a new top component; otherwise, if force is false, it will return
- * NULL if there is not already a top component available.
- */
- PT(NodePathComponent) PandaNode::
- get_top_component(PandaNode *child_node, bool force, int pipeline_stage,
- Thread *current_thread) {
- LightReMutexHolder holder(child_node->_paths_lock);
- // Walk through the list of NodePathComponents we already have on the child,
- // looking for one that already exists as a top node.
- Paths::const_iterator pi;
- for (pi = child_node->_paths.begin(); pi != child_node->_paths.end(); ++pi) {
- if ((*pi)->is_top_node(pipeline_stage, current_thread)) {
- // If we already have such a component, just return it.
- return (*pi);
- }
- }
- if (!force) {
- // If we don't care to force the point, return NULL to indicate there's
- // not already a top component.
- return nullptr;
- }
- // We don't already have such a NodePathComponent; create and return a new
- // one.
- PT(NodePathComponent) child =
- new NodePathComponent(child_node, nullptr,
- pipeline_stage, current_thread);
- child_node->_paths.insert(child);
- return child;
- }
- /**
- * Returns a NodePathComponent referencing this node as a path from the root.
- *
- * Unless accept_ambiguity is true, it is only valid to call this if there is
- * an unambiguous path from the root; otherwise, a warning will be issued and
- * one path will be chosen arbitrarily.
- */
- PT(NodePathComponent) PandaNode::
- get_generic_component(bool accept_ambiguity, int pipeline_stage,
- Thread *current_thread) {
- bool ambiguity_detected = false;
- PT(NodePathComponent) result =
- r_get_generic_component(accept_ambiguity, ambiguity_detected,
- pipeline_stage, current_thread);
- if (!accept_ambiguity && ambiguity_detected) {
- pgraph_cat.warning()
- << "Chose: " << *result << "\n";
- nassertr(!unambiguous_graph, result);
- }
- return result;
- }
- /**
- * The recursive implementation of get_generic_component, this simply sets the
- * flag when the ambiguity is detected (so we can report the bottom node that
- * started the ambiguous search).
- */
- PT(NodePathComponent) PandaNode::
- r_get_generic_component(bool accept_ambiguity, bool &ambiguity_detected,
- int pipeline_stage, Thread *current_thread) {
- PT(PandaNode) parent_node;
- {
- CDStageReader cdata(_cycler, pipeline_stage, current_thread);
- int num_parents = cdata->get_up()->size();
- if (num_parents == 0) {
- // No parents; no ambiguity. This is the root.
- return get_top_component(this, true, pipeline_stage, current_thread);
- }
- PT(NodePathComponent) result;
- if (num_parents == 1) {
- // Only one parent; no ambiguity.
- PT(NodePathComponent) parent =
- get_parent(0)->r_get_generic_component(accept_ambiguity, ambiguity_detected, pipeline_stage, current_thread);
- return get_component(parent, this, pipeline_stage, current_thread);
- }
- // Oops, multiple parents; the NodePath is ambiguous.
- if (!accept_ambiguity) {
- pgraph_cat.warning()
- << *this << " has " << num_parents
- << " parents; choosing arbitrary path to root.\n";
- }
- ambiguity_detected = true;
- CPT(Up) up = cdata->get_up();
- parent_node = (*up)[0].get_parent();
- }
- // Now that the lock is released, it's safe to recurse.
- PT(NodePathComponent) parent =
- parent_node->r_get_generic_component(accept_ambiguity, ambiguity_detected, pipeline_stage, current_thread);
- return get_component(parent, this, pipeline_stage, current_thread);
- }
- /**
- * Removes a NodePathComponent from the set prior to its deletion. This
- * should only be called by the NodePathComponent destructor.
- */
- void PandaNode::
- delete_component(NodePathComponent *component) {
- LightReMutexHolder holder(_paths_lock);
- int num_erased = _paths.erase(component);
- nassertv(num_erased == 1);
- }
- /**
- * This is called internally when a parent-child connection is broken to
- * update the NodePathComponents that reflected this connection.
- *
- * It severs any NodePathComponents on the child node that reference the
- * indicated parent node. These components remain unattached; there may
- * therefore be multiple "instances" of a node that all have no parent, even
- * while there are other instances that do have parents.
- *
- * This operation is not automatically propagated upstream. It is applied to
- * the indicated pipeline stage only.
- */
- void PandaNode::
- sever_connection(PandaNode *parent_node, PandaNode *child_node,
- int pipeline_stage, Thread *current_thread) {
- {
- LightReMutexHolder holder(child_node->_paths_lock);
- Paths::iterator pi;
- for (pi = child_node->_paths.begin(); pi != child_node->_paths.end(); ++pi) {
- if (!(*pi)->is_top_node(pipeline_stage, current_thread) &&
- (*pi)->get_next(pipeline_stage, current_thread)->get_node() == parent_node) {
- // Sever the component here.
- (*pi)->set_top_node(pipeline_stage, current_thread);
- }
- }
- }
- child_node->fix_path_lengths(pipeline_stage, current_thread);
- }
- /**
- * This is called internally when a parent-child connection is established to
- * update the NodePathComponents that might be involved.
- *
- * It adjusts any NodePathComponents the child has that reference the child as
- * a top node. Any other components we can leave alone, because we are making
- * a new instance of the child.
- *
- * This operation is not automatically propagated upstream. It is applied to
- * the indicated pipeline stage only.
- */
- void PandaNode::
- new_connection(PandaNode *parent_node, PandaNode *child_node,
- int pipeline_stage, Thread *current_thread) {
- {
- LightReMutexHolder holder(child_node->_paths_lock);
- Paths::iterator pi;
- for (pi = child_node->_paths.begin(); pi != child_node->_paths.end(); ++pi) {
- if ((*pi)->is_top_node(pipeline_stage, current_thread)) {
- (*pi)->set_next(parent_node->get_generic_component(false, pipeline_stage, current_thread), pipeline_stage, current_thread);
- }
- }
- }
- child_node->fix_path_lengths(pipeline_stage, current_thread);
- }
- /**
- * Recursively fixes the _length member of each NodePathComponent at this
- * level and below, after an add or delete child operation that might have
- * messed these up.
- *
- * This operation is not automatically propagated upstream. It is applied to
- * the indicated pipeline stage only.
- */
- void PandaNode::
- fix_path_lengths(int pipeline_stage, Thread *current_thread) {
- LightReMutexHolder holder(_paths_lock);
- bool any_wrong = false;
- Paths::const_iterator pi;
- for (pi = _paths.begin(); pi != _paths.end(); ++pi) {
- if ((*pi)->fix_length(pipeline_stage, current_thread)) {
- any_wrong = true;
- }
- }
- // If any paths were updated, we have to recurse on all of our children,
- // since any one of those paths might be shared by any of our child nodes.
- // Don't hold any locks while we recurse.
- if (any_wrong) {
- Children children;
- Stashed stashed;
- {
- CDStageReader cdata(_cycler, pipeline_stage, current_thread);
- children = Children(cdata);
- stashed = Stashed(cdata);
- }
- int num_children = children.get_num_children();
- int i;
- for (i = 0; i < num_children; ++i) {
- PandaNode *child_node = children.get_child(i);
- child_node->fix_path_lengths(pipeline_stage, current_thread);
- }
- int num_stashed = stashed.get_num_stashed();
- for (i = 0; i < num_stashed; ++i) {
- PandaNode *child_node = stashed.get_stashed(i);
- child_node->fix_path_lengths(pipeline_stage, current_thread);
- }
- }
- }
- /**
- * The recursive implementation of ls().
- */
- void PandaNode::
- r_list_descendants(ostream &out, int indent_level) const {
- write(out, indent_level);
- Children children = get_children();
- int num_children = children.get_num_children();
- for (int i = 0; i < num_children; ++i) {
- PandaNode *child = children.get_child(i);
- child->r_list_descendants(out, indent_level + 2);
- }
- // Also report the number of stashed nodes at this level.
- int num_stashed = get_num_stashed();
- if (num_stashed != 0) {
- indent(out, indent_level) << "(" << num_stashed << " stashed)\n";
- }
- }
- /**
- * The private implementation of find_child().
- */
- int PandaNode::
- do_find_child(PandaNode *node, const PandaNode::Down *down) const {
- nassertr(node != nullptr, -1);
- // We have to search for the child by brute force, since we don't know what
- // sort index it was added as.
- Down::const_iterator di;
- for (di = down->begin(); di != down->end(); ++di) {
- if ((*di).get_child() == node) {
- return di - down->begin();
- }
- }
- return -1;
- }
- /**
- * Updates the cached values of the node that are dependent on its children,
- * such as the external bounding volume, the _net_collide_mask, and the
- * _off_clip_planes.
- *
- * If update_bounds is false, it will not update the bounding volume or vertex
- * count.
- *
- * The old value should be passed in; it will be released. The new value is
- * returned.
- */
- PandaNode::CDStageWriter PandaNode::
- update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
- // We might need to try this a couple of times, in case someone else steps
- // on our result.
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << *this << "::update_cached() {\n";
- }
- Thread *current_thread = cdata.get_current_thread();
- do {
- // Grab the last_update counter.
- UpdateSeq last_update = cdata->_last_update;
- UpdateSeq next_update = cdata->_next_update;
- UpdateSeq last_bounds_update = cdata->_last_bounds_update;
- nassertr(last_update != next_update ||
- (update_bounds && last_bounds_update != next_update),
- CDStageWriter(_cycler, pipeline_stage, cdata));
- // Start with a clean slate.
- CollideMask net_collide_mask = cdata->_into_collide_mask;
- DrawMask net_draw_control_mask, net_draw_show_mask;
- bool renderable = is_renderable();
- if (renderable) {
- // If this node is itself renderable, it contributes to the net draw
- // mask.
- net_draw_control_mask = cdata->_draw_control_mask;
- net_draw_show_mask = cdata->_draw_show_mask;
- }
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "net_draw_control_mask = " << net_draw_control_mask
- << "\nnet_draw_show_mask = " << net_draw_show_mask
- << "\n";
- }
- CPT(RenderAttrib) off_clip_planes = cdata->_state->get_attrib(ClipPlaneAttrib::get_class_slot());
- if (off_clip_planes == nullptr) {
- off_clip_planes = ClipPlaneAttrib::make();
- }
- // Also get the list of the node's children.
- Children children(cdata);
- int num_vertices = cdata->_internal_vertices;
- // Now that we've got all the data we need from the node, we can release
- // the lock.
- _cycler.release_read_stage(pipeline_stage, cdata.take_pointer());
- int num_children = children.get_num_children();
- // We need to keep references to the bounding volumes, since in a threaded
- // environment the pointers might go away while we're working (since we're
- // not holding a lock on our set of children right now). But we also need
- // the regular pointers, to pass to BoundingVolume::around().
- const BoundingVolume **child_volumes;
- #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
- pvector<CPT(BoundingVolume) > child_volumes_ref;
- if (update_bounds) {
- child_volumes_ref.reserve(num_children + 1);
- }
- #endif
- int child_volumes_i = 0;
- bool all_box = true;
- CPT(BoundingVolume) internal_bounds = nullptr;
- if (update_bounds) {
- child_volumes = (const BoundingVolume **)alloca(sizeof(BoundingVolume *) * (num_children + 1));
- internal_bounds = get_internal_bounds(pipeline_stage, current_thread);
- if (!internal_bounds->is_empty()) {
- #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
- child_volumes_ref.push_back(internal_bounds);
- #endif
- nassertr(child_volumes_i < num_children + 1, CDStageWriter(_cycler, pipeline_stage, cdata));
- child_volumes[child_volumes_i++] = internal_bounds;
- if (internal_bounds->as_bounding_box() == nullptr) {
- all_box = false;
- }
- }
- }
- // Now expand those contents to include all of our children.
- for (int i = 0; i < num_children; ++i) {
- PandaNode *child = children.get_child(i);
- const ClipPlaneAttrib *orig_cp = DCAST(ClipPlaneAttrib, off_clip_planes);
- CDLockedStageReader child_cdata(child->_cycler, pipeline_stage, current_thread);
- UpdateSeq last_child_update = update_bounds
- ? child_cdata->_last_bounds_update
- : child_cdata->_last_update;
- if (last_child_update != child_cdata->_next_update) {
- // Child needs update.
- CDStageWriter child_cdataw = child->update_cached(update_bounds, pipeline_stage, child_cdata);
- net_collide_mask |= child_cdataw->_net_collide_mask;
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "\nchild update " << *child << ":\n";
- }
- DrawMask child_control_mask = child_cdataw->_net_draw_control_mask;
- DrawMask child_show_mask = child_cdataw->_net_draw_show_mask;
- if (!(child_control_mask | child_show_mask).is_zero()) {
- // This child includes a renderable node or subtree. Thus, we
- // should propagate its draw masks.
- renderable = true;
- // For each bit position in the masks, we have assigned the
- // following semantic meaning. The number on the left represents
- // the pairing of the corresponding bit from the control mask and
- // from the show mask:
- // 00 : not a renderable node (control 0, show 0) 01 : a normally
- // visible node (control 0, show 1) 10 : a hidden node
- // (control 1, show 0) 11 : a show-through node (control 1, show
- // 1)
- // Now, when we accumulate these masks, we want to do so according
- // to the following table, for each bit position:
- // 00 01 10 11 (child) --------------------- 00 | 00 01
- // 10 11 01 | 01 01 01* 11 10 | 10 01* 10 11 11 | 11
- // 11 11 11 (parent)
- // This table is almost the same as the union of both masks, with
- // one exception, marked with a * in the above table: if one is 10
- // and the other is 01--that is, one is hidden and the other is
- // normally visible--then the result should be 01, normally visible.
- // This is because we only want to propagate the hidden bit upwards
- // if *all* renderable nodes are hidden.
- // Get the set of exception bits for which the above rule applies.
- // These are the bits for which both bits have flipped, but which
- // were not the same in the original.
- DrawMask exception_mask = (net_draw_control_mask ^ child_control_mask) & (net_draw_show_mask ^ child_show_mask);
- exception_mask &= (net_draw_control_mask ^ net_draw_show_mask);
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "exception_mask = " << exception_mask << "\n";
- }
- // Now compute the union, applying the above exception.
- net_draw_control_mask |= child_control_mask;
- net_draw_show_mask |= child_show_mask;
- net_draw_control_mask &= ~exception_mask;
- net_draw_show_mask |= exception_mask;
- }
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "child_control_mask = " << child_control_mask
- << "\nchild_show_mask = " << child_show_mask
- << "\nnet_draw_control_mask = " << net_draw_control_mask
- << "\nnet_draw_show_mask = " << net_draw_show_mask
- << "\n";
- }
- off_clip_planes = orig_cp->compose_off(child_cdataw->_off_clip_planes);
- if (update_bounds) {
- if (!child_cdataw->_external_bounds->is_empty()) {
- #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
- child_volumes_ref.push_back(child_cdataw->_external_bounds);
- #endif
- nassertr(child_volumes_i < num_children + 1, CDStageWriter(_cycler, pipeline_stage, cdata));
- child_volumes[child_volumes_i++] = child_cdataw->_external_bounds;
- if (child_cdataw->_external_bounds->as_bounding_box() == nullptr) {
- all_box = false;
- }
- }
- num_vertices += child_cdataw->_nested_vertices;
- }
- } else {
- // Child is good.
- net_collide_mask |= child_cdata->_net_collide_mask;
- // See comments in similar block above.
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "\nchild fresh " << *child << ":\n";
- }
- DrawMask child_control_mask = child_cdata->_net_draw_control_mask;
- DrawMask child_show_mask = child_cdata->_net_draw_show_mask;
- if (!(child_control_mask | child_show_mask).is_zero()) {
- renderable = true;
- DrawMask exception_mask = (net_draw_control_mask ^ child_control_mask) & (net_draw_show_mask ^ child_show_mask);
- exception_mask &= (net_draw_control_mask ^ net_draw_show_mask);
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "exception_mask = " << exception_mask << "\n";
- }
- // Now compute the union, applying the above exception.
- net_draw_control_mask |= child_control_mask;
- net_draw_show_mask |= child_show_mask;
- net_draw_control_mask &= ~exception_mask;
- net_draw_show_mask |= exception_mask;
- }
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "child_control_mask = " << child_control_mask
- << "\nchild_show_mask = " << child_show_mask
- << "\nnet_draw_control_mask = " << net_draw_control_mask
- << "\nnet_draw_show_mask = " << net_draw_show_mask
- << "\n";
- }
- off_clip_planes = orig_cp->compose_off(child_cdata->_off_clip_planes);
- if (update_bounds) {
- if (!child_cdata->_external_bounds->is_empty()) {
- #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
- child_volumes_ref.push_back(child_cdata->_external_bounds);
- #endif
- nassertr(child_volumes_i < num_children + 1, CDStageWriter(_cycler, pipeline_stage, cdata));
- child_volumes[child_volumes_i++] = child_cdata->_external_bounds;
- if (child_cdata->_external_bounds->as_bounding_box() == nullptr) {
- all_box = false;
- }
- }
- num_vertices += child_cdata->_nested_vertices;
- }
- }
- }
- {
- // Now grab the write lock on this node.
- CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
- if (last_update == cdataw->_last_update &&
- next_update == cdataw->_next_update) {
- // Great, no one has monkeyed with these while we were computing the
- // cache. Safe to store the computed values and return.
- cdataw->_net_collide_mask = net_collide_mask;
- if (renderable) {
- // Any explicit draw control mask on this node trumps anything
- // inherited from below, except a show-through.
- DrawMask draw_control_mask = cdataw->_draw_control_mask;
- DrawMask draw_show_mask = cdataw->_draw_show_mask;
- DrawMask show_through_mask = net_draw_control_mask & net_draw_show_mask;
- net_draw_control_mask |= draw_control_mask;
- net_draw_show_mask = (net_draw_show_mask & ~draw_control_mask) | (draw_show_mask & draw_control_mask);
- net_draw_show_mask |= show_through_mask;
- // There are renderable nodes below, so the implicit draw bits are
- // all on.
- cdataw->_net_draw_control_mask = net_draw_control_mask;
- cdataw->_net_draw_show_mask = net_draw_show_mask | ~net_draw_control_mask;
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "renderable, set mask " << cdataw->_net_draw_show_mask << "\n";
- }
- } else {
- // There are no renderable nodes below, so the implicit draw bits
- // are all off. Also, we don't care about the draw mask on this
- // particular node (since nothing below it is renderable anyway).
- cdataw->_net_draw_control_mask = net_draw_control_mask;
- cdataw->_net_draw_show_mask = net_draw_show_mask;
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "not renderable, set mask " << cdataw->_net_draw_show_mask << "\n";
- }
- }
- cdataw->_off_clip_planes = off_clip_planes;
- if (update_bounds) {
- cdataw->_nested_vertices = num_vertices;
- CPT(TransformState) transform = get_transform(current_thread);
- PT(GeometricBoundingVolume) gbv;
- BoundingVolume::BoundsType btype = cdataw->_bounds_type;
- if (btype == BoundingVolume::BT_default) {
- btype = bounds_type;
- }
- if (btype == BoundingVolume::BT_box ||
- (btype != BoundingVolume::BT_sphere && all_box && transform->is_identity())) {
- // If all of the child volumes are a BoundingBox, and we have no
- // transform, then our volume is also a BoundingBox.
- gbv = new BoundingBox;
- } else {
- // Otherwise, it's a sphere.
- gbv = new BoundingSphere;
- }
- if (child_volumes_i > 0) {
- const BoundingVolume **child_begin = &child_volumes[0];
- const BoundingVolume **child_end = child_begin + child_volumes_i;
- ((BoundingVolume *)gbv)->around(child_begin, child_end);
- // If we have a transform, apply it to the bounding volume we just
- // computed.
- if (!transform->is_identity()) {
- gbv->xform(transform->get_mat());
- }
- }
- cdataw->_external_bounds = gbv;
- cdataw->_last_bounds_update = next_update;
- }
- cdataw->_last_update = next_update;
- if (drawmask_cat.is_debug()) {
- drawmask_cat.debug(false)
- << "} " << *this << "::update_cached();\n";
- }
- nassertr(cdataw->_last_update == cdataw->_next_update, cdataw);
- // Even though implicit bounding volume is not (yet?) part of the bam
- // stream.
- mark_bam_modified();
- return cdataw;
- }
- if (cdataw->_last_update == cdataw->_next_update &&
- (!update_bounds || cdataw->_last_bounds_update == cdataw->_next_update)) {
- // Someone else has computed the cache for us. OK.
- return cdataw;
- }
- }
- // We need to go around again. Release the write lock, and grab the read
- // lock back.
- cdata = CDLockedStageReader(_cycler, pipeline_stage, current_thread);
- if (cdata->_last_update == cdata->_next_update &&
- (!update_bounds || cdata->_last_bounds_update == cdata->_next_update)) {
- // Someone else has computed the cache for us while we were diddling
- // with the locks. OK.
- return CDStageWriter(_cycler, pipeline_stage, cdata);
- }
- } while (true);
- }
- /**
- * This is used by the GraphicsEngine to hook in a pointer to the
- * scene_root_func(), the function to determine whether the node is an active
- * scene root. This back-pointer is necessary because we can't make calls
- * directly into GraphicsEngine, which is in the display module.
- */
- void PandaNode::
- set_scene_root_func(SceneRootFunc *func) {
- _scene_root_func = func;
- }
- /**
- * Tells the BamReader how to create objects of type PandaNode.
- */
- void PandaNode::
- register_with_read_factory() {
- BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
- }
- /**
- * Writes the contents of this object to the datagram for shipping out to a
- * Bam file.
- */
- void PandaNode::
- write_datagram(BamWriter *manager, Datagram &dg) {
- TypedWritable::write_datagram(manager, dg);
- dg.add_string(get_name());
- manager->write_cdata(dg, _cycler);
- }
- /**
- * Called by the BamWriter when this object has not itself been modified
- * recently, but it should check its nested objects for updates.
- */
- void PandaNode::
- update_bam_nested(BamWriter *manager) {
- CDReader cdata(_cycler);
- cdata->update_bam_nested(manager);
- }
- /**
- * This method is provided for the benefit of classes (like MouseRecorder)
- * that inherit from PandaMode and also RecorderBase. It's not virtual at
- * this level since it doesn't need to be (it's called up from the derived
- * class).
- *
- * This method acts very like write_datagram, but it writes the node as
- * appropriate for writing a RecorderBase object as described in the beginning
- * of a session file, meaning it doesn't need to write things such as
- * children. It balances with fillin_recorder().
- */
- void PandaNode::
- write_recorder(BamWriter *, Datagram &dg) {
- dg.add_string(get_name());
- }
- /**
- * This function is called by the BamReader's factory when a new object of
- * type PandaNode is encountered in the Bam file. It should create the
- * PandaNode and extract its information from the file.
- */
- TypedWritable *PandaNode::
- make_from_bam(const FactoryParams ¶ms) {
- PandaNode *node = new PandaNode("");
- DatagramIterator scan;
- BamReader *manager;
- parse_params(params, scan, manager);
- node->fillin(scan, manager);
- return node;
- }
- /**
- * This internal function is called by make_from_bam to read in all of the
- * relevant data from the BamFile for the new PandaNode.
- */
- void PandaNode::
- fillin(DatagramIterator &scan, BamReader *manager) {
- TypedWritable::fillin(scan, manager);
- remove_all_children();
- string name = scan.get_string();
- set_name(name);
- manager->read_cdata(scan, _cycler);
- }
- /**
- * This internal function is called by make_recorder (in classes derived from
- * RecorderBase, such as MouseRecorder) to read in all of the relevant data
- * from the session file. It balances with write_recorder().
- */
- void PandaNode::
- fillin_recorder(DatagramIterator &scan, BamReader *) {
- string name = scan.get_string();
- set_name(name);
- }
- /**
- *
- */
- PandaNode::CData::
- CData() :
- _state(RenderState::make_empty()),
- _transform(TransformState::make_identity()),
- _prev_transform(TransformState::make_identity()),
- _effects(RenderEffects::make_empty()),
- _draw_control_mask(DrawMask::all_off()),
- _draw_show_mask(DrawMask::all_on()),
- _into_collide_mask(CollideMask::all_off()),
- _bounds_type(BoundingVolume::BT_default),
- _user_bounds(nullptr),
- _final_bounds(false),
- _fancy_bits(0),
- _net_collide_mask(CollideMask::all_off()),
- _net_draw_control_mask(DrawMask::all_off()),
- _net_draw_show_mask(DrawMask::all_off()),
- _down(new PandaNode::Down(PandaNode::get_class_type())),
- _stashed(new PandaNode::Down(PandaNode::get_class_type())),
- _up(new PandaNode::Up(PandaNode::get_class_type()))
- {
- ++_next_update;
- }
- /**
- *
- */
- PandaNode::CData::
- CData(const PandaNode::CData ©) :
- BoundsData(copy),
- _state(copy._state),
- _transform(copy._transform),
- _prev_transform(copy._prev_transform),
- _effects(copy._effects),
- _tag_data(copy._tag_data),
- _draw_control_mask(copy._draw_control_mask),
- _draw_show_mask(copy._draw_show_mask),
- _into_collide_mask(copy._into_collide_mask),
- _bounds_type(copy._bounds_type),
- _user_bounds(copy._user_bounds),
- _final_bounds(copy._final_bounds),
- _fancy_bits(copy._fancy_bits),
- _net_collide_mask(copy._net_collide_mask),
- _net_draw_control_mask(copy._net_draw_control_mask),
- _net_draw_show_mask(copy._net_draw_show_mask),
- _off_clip_planes(copy._off_clip_planes),
- _nested_vertices(copy._nested_vertices),
- _external_bounds(copy._external_bounds),
- _last_update(copy._last_update),
- _next_update(copy._next_update),
- _last_bounds_update(copy._last_bounds_update),
- _down(copy._down),
- _stashed(copy._stashed),
- _up(copy._up)
- {
- // Note that this copy constructor is not used by the PandaNode copy
- // constructor! Any elements that must be copied between nodes should also
- // be explicitly copied there.
- }
- /**
- *
- */
- PandaNode::CData::
- ~CData() {
- }
- /**
- *
- */
- CycleData *PandaNode::CData::
- make_copy() const {
- return new CData(*this);
- }
- /**
- * Writes the contents of this object to the datagram for shipping out to a
- * Bam file.
- */
- void PandaNode::CData::
- write_datagram(BamWriter *manager, Datagram &dg) const {
- manager->write_pointer(dg, _state);
- manager->write_pointer(dg, _transform);
- manager->write_pointer(dg, _effects);
- dg.add_uint32(_draw_control_mask.get_word());
- dg.add_uint32(_draw_show_mask.get_word());
- dg.add_uint32(_into_collide_mask.get_word());
- dg.add_uint8(_bounds_type);
- dg.add_uint32(_tag_data.size());
- for (size_t n = 0; n < _tag_data.size(); ++n) {
- dg.add_string(_tag_data.get_key(n));
- dg.add_string(_tag_data.get_data(n));
- }
- write_up_list(*get_up(), manager, dg);
- write_down_list(*get_down(), manager, dg);
- write_down_list(*get_stashed(), manager, dg);
- }
- /**
- * Called by the BamWriter when this object has not itself been modified
- * recently, but it should check its nested objects for updates.
- */
- void PandaNode::CData::
- update_bam_nested(BamWriter *manager) const {
- // No need to check the state pointers for updates, since they're all
- // immutable objects. manager->consider_update(_state);
- // manager->consider_update(_transform); manager->consider_update(_effects);
- update_up_list(*get_up(), manager);
- update_down_list(*get_down(), manager);
- update_down_list(*get_stashed(), manager);
- }
- /**
- * Receives an array of pointers, one for each time manager->read_pointer()
- * was called in fillin(). Returns the number of pointers processed.
- */
- int PandaNode::CData::
- complete_pointers(TypedWritable **p_list, BamReader *manager) {
- int pi = CycleData::complete_pointers(p_list, manager);
- // Get the state and transform pointers.
- RenderState *state;
- DCAST_INTO_R(state, p_list[pi++], pi);
- _state = state;
- TransformState *transform;
- DCAST_INTO_R(transform, p_list[pi++], pi);
- _prev_transform = _transform = transform;
- /*
- * Finalize these pointers now to decrement their artificially-held reference
- * counts. We do this now, rather than later, in case some other object
- * reassigns them a little later on during initialization, before they can
- * finalize themselves normally (for instance, the character may change the
- * node's transform). If that happens, the pointer may discover that no one
- * else holds its reference count when it finalizes, which will constitute a
- * memory leak (see the comments in TransformState::finalize(), etc.).
- */
- manager->finalize_now((RenderState *)_state.p());
- manager->finalize_now((TransformState *)_transform.p());
- // Get the effects pointer.
- RenderEffects *effects;
- DCAST_INTO_R(effects, p_list[pi++], pi);
- _effects = effects;
- /*
- * Finalize these pointers now to decrement their artificially-held reference
- * counts. We do this now, rather than later, in case some other object
- * reassigns them a little later on during initialization, before they can
- * finalize themselves normally (for instance, the character may change the
- * node's transform). If that happens, the pointer may discover that no one
- * else holds its reference count when it finalizes, which will constitute a
- * memory leak (see the comments in TransformState::finalize(), etc.).
- */
- manager->finalize_now((RenderEffects *)_effects.p());
- // Get the parent and child pointers.
- pi += complete_up_list(*modify_up(), "up", p_list + pi, manager);
- pi += complete_down_list(*modify_down(), "down", p_list + pi, manager);
- pi += complete_down_list(*modify_stashed(), "stashed", p_list + pi, manager);
- // Since the _effects and _states members have been finalized by now, this
- // should be safe.
- set_fancy_bit(FB_transform, !_transform->is_identity());
- set_fancy_bit(FB_state, !_state->is_empty());
- set_fancy_bit(FB_effects, !_effects->is_empty());
- set_fancy_bit(FB_tag, !_tag_data.is_empty());
- // Mark the bounds stale.
- ++_next_update;
- nassertr(!_transform->is_invalid(), pi);
- nassertr(!_prev_transform->is_invalid(), pi);
- return pi;
- }
- /**
- * This internal function is called by make_from_bam to read in all of the
- * relevant data from the BamFile for the new PandaNode.
- */
- void PandaNode::CData::
- fillin(DatagramIterator &scan, BamReader *manager) {
- // Read the state and transform pointers.
- manager->read_pointer(scan);
- manager->read_pointer(scan);
- // Read the effects pointer.
- manager->read_pointer(scan);
- if (manager->get_file_minor_ver() < 2) {
- DrawMask draw_mask;
- draw_mask.set_word(scan.get_uint32());
- if (draw_mask == DrawMask::all_off()) {
- // Hidden.
- _draw_control_mask = _overall_bit;
- _draw_show_mask = ~_overall_bit;
- } else if (draw_mask == DrawMask::all_on()) {
- // Normally visible.
- _draw_control_mask = DrawMask::all_off();
- _draw_show_mask = DrawMask::all_on();
- } else {
- // Some per-camera combination.
- draw_mask &= ~_overall_bit;
- _draw_control_mask = ~draw_mask;
- _draw_show_mask = draw_mask;
- }
- } else {
- _draw_control_mask.set_word(scan.get_uint32());
- _draw_show_mask.set_word(scan.get_uint32());
- }
- _into_collide_mask.set_word(scan.get_uint32());
- _bounds_type = BoundingVolume::BT_default;
- if (manager->get_file_minor_ver() >= 19) {
- _bounds_type = (BoundingVolume::BoundsType)scan.get_uint8();
- }
- // Read in the tag list.
- int num_tags = scan.get_uint32();
- for (int i = 0; i < num_tags; i++) {
- string key = scan.get_string();
- string value = scan.get_string();
- _tag_data.store(key, value);
- }
- fillin_up_list(*modify_up(), "up", scan, manager);
- fillin_down_list(*modify_down(), "down", scan, manager);
- fillin_down_list(*modify_stashed(), "stashed", scan, manager);
- }
- /**
- * Writes the indicated list of parent node pointers to the datagram.
- */
- void PandaNode::CData::
- write_up_list(const PandaNode::Up &up_list,
- BamWriter *manager, Datagram &dg) const {
- /*
- * When we write a PandaNode, we write out its complete list of child node
- * pointers, but we only write out the parent node pointers that have already
- * been added to the bam file by a previous write operation. This is a bit of
- * trickery that allows us to write out just a subgraph (instead of the
- * complete graph) when we write out an arbitrary node in the graph, yet also
- * allows us to keep nodes completely in sync when we use the bam format for
- * streaming scene graph operations over the network.
- */
- int num_parents = 0;
- Up::const_iterator ui;
- for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
- PandaNode *parent_node = (*ui).get_parent();
- if (manager->has_object(parent_node)) {
- num_parents++;
- }
- }
- nassertv(num_parents == (int)(uint16_t)num_parents);
- dg.add_uint16(num_parents);
- for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
- PandaNode *parent_node = (*ui).get_parent();
- if (manager->has_object(parent_node)) {
- manager->write_pointer(dg, parent_node);
- }
- }
- }
- /**
- * Writes the indicated list of child node pointers to the datagram.
- */
- void PandaNode::CData::
- write_down_list(const PandaNode::Down &down_list,
- BamWriter *manager, Datagram &dg) const {
- int num_children = down_list.size();
- nassertv(num_children == (int)(uint16_t)num_children);
- dg.add_uint16(num_children);
- // Should we smarten up the writing of the sort number? Most of the time
- // these will all be zero.
- Down::const_iterator di;
- for (di = down_list.begin(); di != down_list.end(); ++di) {
- PandaNode *child_node = (*di).get_child();
- int sort = (*di).get_sort();
- manager->write_pointer(dg, child_node);
- dg.add_int32(sort);
- }
- }
- /**
- * Calls consider_update on each node of the indicated up list.
- */
- void PandaNode::CData::
- update_up_list(const PandaNode::Up &up_list, BamWriter *manager) const {
- Up::const_iterator ui;
- for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
- PandaNode *parent_node = (*ui).get_parent();
- if (manager->has_object(parent_node)) {
- manager->consider_update(parent_node);
- }
- }
- }
- /**
- * Calls consider_update on each node of the indicated up list.
- */
- void PandaNode::CData::
- update_down_list(const PandaNode::Down &down_list, BamWriter *manager) const {
- Down::const_iterator di;
- for (di = down_list.begin(); di != down_list.end(); ++di) {
- PandaNode *child_node = (*di).get_child();
- manager->consider_update(child_node);
- }
- }
- /**
- * Calls complete_pointers() on the list of parent node pointers.
- */
- int PandaNode::CData::
- complete_up_list(PandaNode::Up &up_list, const string &tag,
- TypedWritable **p_list, BamReader *manager) {
- int pi = 0;
- int num_parents = manager->get_int_tag(tag);
- Up new_up_list(PandaNode::get_class_type());
- new_up_list.reserve(num_parents);
- for (int i = 0; i < num_parents; i++) {
- PandaNode *parent_node = DCAST(PandaNode, p_list[pi++]);
- UpConnection connection(parent_node);
- new_up_list.push_back(connection);
- }
- // Now we should sort the list, since the sorting is based on pointer order,
- // which might be different from one session to the next.
- new_up_list.sort();
- // Make it permanent.
- up_list.swap(new_up_list);
- new_up_list.clear();
- return pi;
- }
- /**
- * Calls complete_pointers() on the list of child node pointers.
- */
- int PandaNode::CData::
- complete_down_list(PandaNode::Down &down_list, const string &tag,
- TypedWritable **p_list, BamReader *manager) {
- int pi = 0;
- BamReaderAuxDataDown *aux;
- DCAST_INTO_R(aux, manager->get_aux_tag(tag), pi);
- Down &new_down_list = aux->_down_list;
- for (Down::iterator di = new_down_list.begin();
- di != new_down_list.end();
- ++di) {
- PandaNode *child_node = DCAST(PandaNode, p_list[pi++]);
- (*di).set_child(child_node);
- }
- // Unlike the up list, we should *not* sort the down list. The down list is
- // stored in a specific order, not related to pointer order; and this order
- // should be preserved from one session to the next.
- // Make it permanent.
- down_list.swap(new_down_list);
- new_down_list.clear();
- return pi;
- }
- /**
- * Reads the indicated list parent node pointers from the datagram (or at
- * least calls read_pointer() for each one).
- */
- void PandaNode::CData::
- fillin_up_list(PandaNode::Up &up_list, const string &tag,
- DatagramIterator &scan, BamReader *manager) {
- int num_parents = scan.get_uint16();
- manager->set_int_tag(tag, num_parents);
- manager->read_pointers(scan, num_parents);
- }
- /**
- * Reads the indicated list child node pointers from the datagram (or at least
- * calls read_pointer() for each one).
- */
- void PandaNode::CData::
- fillin_down_list(PandaNode::Down &down_list, const string &tag,
- DatagramIterator &scan, BamReader *manager) {
- int num_children = scan.get_uint16();
- // Create a temporary down_list, with the right number of elements, but a
- // NULL value for each pointer (we'll fill in the pointers later). We need
- // to do this to associate the sort values with their pointers.
- Down new_down_list(PandaNode::get_class_type());
- new_down_list.reserve(num_children);
- for (int i = 0; i < num_children; i++) {
- manager->read_pointer(scan);
- int sort = scan.get_int32();
- DownConnection connection(nullptr, sort);
- new_down_list.push_back(connection);
- }
- // Now store the temporary down_list in the BamReader, so we can get it
- // during the call to complete_down_list().
- PT(BamReaderAuxDataDown) aux = new BamReaderAuxDataDown;
- aux->_down_list.swap(new_down_list);
- manager->set_aux_tag(tag, aux);
- }
- /**
- * Ensures that the draw masks etc. are properly computed on this node. If
- * update_bounds is true, also checks the bounding volume.
- */
- void PandaNodePipelineReader::
- check_cached(bool update_bounds) const {
- UpdateSeq last_update = update_bounds
- ? _cdata->_last_bounds_update
- : _cdata->_last_update;
- if (last_update != _cdata->_next_update) {
- // The cache is stale; it needs to be rebuilt.
- // We'll need to get a fresh read pointer, since another thread might
- // already have modified the pointer on the object since we queried it.
- #ifdef DO_PIPELINING
- node_unref_delete((CycleData *)_cdata);
- #endif // DO_PIPELINING
- ((PandaNodePipelineReader *)this)->_cdata = nullptr;
- int pipeline_stage = _current_thread->get_pipeline_stage();
- PandaNode::CDLockedStageReader fresh_cdata(_node->_cycler, pipeline_stage, _current_thread);
- if (fresh_cdata->_last_update == fresh_cdata->_next_update &&
- (!update_bounds || fresh_cdata->_last_bounds_update == fresh_cdata->_next_update)) {
- // What luck, some other thread has already freshened the cache for us.
- // Save the new pointer, and let the lock release itself.
- if (_cdata != (const PandaNode::CData *)fresh_cdata) {
- ((PandaNodePipelineReader *)this)->_cdata = fresh_cdata;
- #ifdef DO_PIPELINING
- _cdata->node_ref();
- #endif // DO_PIPELINING
- }
- } else {
- // No, the cache is still stale. We have to do the work of freshening
- // it.
- PStatTimer timer(PandaNode::_update_bounds_pcollector);
- PandaNode::CDStageWriter cdataw = ((PandaNode *)_node)->update_cached(update_bounds, pipeline_stage, fresh_cdata);
- nassertv(cdataw->_last_update == cdataw->_next_update);
- // As above, we save the new pointer, and then let the lock release
- // itself.
- if (_cdata != (const PandaNode::CData *)cdataw) {
- ((PandaNodePipelineReader *)this)->_cdata = cdataw;
- #ifdef DO_PIPELINING
- _cdata->node_ref();
- #endif // DO_PIPELINING
- }
- }
- }
- nassertv(_cdata->_last_update == _cdata->_next_update);
- nassertv(!update_bounds || _cdata->_last_bounds_update == _cdata->_next_update);
- }
|