1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 
14 #include "common.h"
15 
16 // implementation header
17 #include "ObstacleMgr.h"
18 
19 // system headers
20 #include <string.h>
21 #include <string>
22 #include <sstream>
23 #include <vector>
24 #include <map>
25 #include <iostream>
26 
27 // common headers
28 #include "Pack.h"
29 #include "MeshTransform.h"
30 #include "ObstacleModifier.h"
31 #include "StateDatabase.h"
32 #include "PhysicsDriver.h"
33 #include "BzMaterial.h"
34 #include "MeshDrawInfo.h"
35 
36 // obstacle headers
37 #include "Obstacle.h"
38 #include "WallObstacle.h"
39 #include "BoxBuilding.h"
40 #include "PyramidBuilding.h"
41 #include "BaseBuilding.h"
42 #include "Teleporter.h"
43 #include "MeshObstacle.h"
44 #include "ArcObstacle.h"
45 #include "ConeObstacle.h"
46 #include "SphereObstacle.h"
47 #include "TetraBuilding.h"
48 
49 
50 //////////////////////////////////////////////////////////////////////////////
51 //
52 // Group Instance
53 // - uses a group definition and a transform to produce obstacles
54 //
55 
56 
init()57 void GroupInstance::init()
58 {
59     modifyTeam = false;
60     team = 0;
61     modifyColor = false;
62     tint[0] = tint[1] = tint[2] = tint[3] = 1.0f;
63     modifyPhysicsDriver = false;
64     phydrv = -1;
65     modifyMaterial = false;
66     material = NULL;
67     driveThrough = false;
68     shootThrough = false;
69     ricochet     = false;
70 
71     return;
72 }
73 
74 
GroupInstance(const std::string & _groupdef)75 GroupInstance::GroupInstance(const std::string& _groupdef)
76 {
77     groupdef = _groupdef;
78     init();
79     return;
80 }
81 
82 
GroupInstance()83 GroupInstance::GroupInstance()
84 {
85     init();
86     return;
87 }
88 
89 
~GroupInstance()90 GroupInstance::~GroupInstance()
91 {
92     return;
93 }
94 
95 
setTransform(const MeshTransform & _transform)96 void GroupInstance::setTransform(const MeshTransform& _transform)
97 {
98     transform = _transform;
99     return;
100 }
101 
102 
setName(const std::string & _name)103 void GroupInstance::setName(const std::string& _name)
104 {
105     name = _name;
106     return;
107 }
108 
109 
getName() const110 const std::string& GroupInstance::getName() const
111 {
112     return name;
113 }
114 
115 
setTeam(int _team)116 void GroupInstance::setTeam(int _team)
117 {
118     team = _team;
119     modifyTeam = true;
120     return;
121 }
122 
123 
setTint(const float _tint[4])124 void GroupInstance::setTint(const float _tint[4])
125 {
126     memcpy(tint, _tint, sizeof(float[4]));
127     modifyColor = true;
128     return;
129 }
130 
131 
setPhysicsDriver(int _phydrv)132 void GroupInstance::setPhysicsDriver(int _phydrv)
133 {
134     phydrv = _phydrv;
135     modifyPhysicsDriver = true;
136     return;
137 }
138 
139 
setMaterial(const BzMaterial * _material)140 void GroupInstance::setMaterial(const BzMaterial* _material)
141 {
142     material = _material;
143     modifyMaterial = true;
144     return;
145 }
146 
147 
setDriveThrough()148 void GroupInstance::setDriveThrough()
149 {
150     driveThrough = true;
151     return;
152 }
153 
154 
setShootThrough()155 void GroupInstance::setShootThrough()
156 {
157     shootThrough = true;
158     return;
159 }
160 
setCanRicochet()161 void GroupInstance::setCanRicochet()
162 {
163     ricochet = true;
164     return;
165 }
166 
167 
addMaterialSwap(const BzMaterial * srcMat,const BzMaterial * dstMat)168 void GroupInstance::addMaterialSwap(const BzMaterial* srcMat,
169                                     const BzMaterial* dstMat)
170 {
171     matMap[srcMat] = dstMat;
172     return;
173 }
174 
175 
getGroupDef() const176 const std::string& GroupInstance::getGroupDef() const
177 {
178     return groupdef;
179 }
180 
181 
getTransform() const182 const MeshTransform& GroupInstance::getTransform() const
183 {
184     return transform;
185 }
186 
187 
pack(void * buf)188 void* GroupInstance::pack(void* buf)
189 {
190     buf = nboPackStdString(buf, groupdef);
191 
192     if (matMap.size() <= 0)
193         buf = nboPackStdString(buf, name);
194     else
195     {
196         // hack to stuff in material map data
197         std::string fakeString = name;
198         const unsigned int origSize = fakeString.size();
199         const unsigned int fakeSize =
200             (1 + sizeof(int32_t) + (matMap.size() * 2 * sizeof(int32_t)));
201         fakeString.resize(origSize + fakeSize);
202         char* buffer = new char[fakeSize];
203         void* p;
204         int count = matMap.size();
205         p = nboPackUByte(buffer, 0); // terminate
206         p = nboPackInt(p, count);
207         MaterialMap::const_iterator it;
208         for (it = matMap.begin(); it != matMap.end(); ++it)
209         {
210             int srcIndex = MATERIALMGR.getIndex(it->first);
211             int dstIndex = MATERIALMGR.getIndex(it->second);
212             p = nboPackInt(p, srcIndex);
213             p = nboPackInt(p, dstIndex);
214         }
215         for (unsigned int i = 0; i < fakeSize; i++)
216             fakeString[origSize + i] = buffer[i];
217         delete[] buffer;
218         buf = nboPackStdString(buf, fakeString);
219     }
220 
221     buf = transform.pack(buf);
222 
223     uint8_t bits = 0;
224     if (modifyTeam)      bits |= (1 << 0);
225     if (modifyColor)     bits |= (1 << 1);
226     if (modifyPhysicsDriver) bits |= (1 << 2);
227     if (modifyMaterial)      bits |= (1 << 3);
228     if (driveThrough)    bits |= (1 << 4);
229     if (shootThrough)    bits |= (1 << 5);
230     if (ricochet)     bits |= (1 << 6);
231     buf = nboPackUByte(buf, bits);
232 
233     if (modifyTeam)
234         buf = nboPackUShort(buf, team);
235     if (modifyColor)
236     {
237         buf = nboPackVector(buf, tint);
238         buf = nboPackFloat(buf, tint[3]);
239     }
240     if (modifyPhysicsDriver)
241         buf = nboPackInt(buf, phydrv);
242     if (modifyMaterial)
243     {
244         int matindex = MATERIALMGR.getIndex(material);
245         buf = nboPackInt(buf, (int32_t) matindex);
246     }
247 
248     return buf;
249 }
250 
251 
unpack(const void * buf)252 const void* GroupInstance::unpack(const void* buf)
253 {
254     buf = nboUnpackStdString(buf, groupdef);
255 
256     buf = nboUnpackStdStringRaw(buf, name);
257     if (strlen(name.c_str()) != name.size())
258     {
259         // hack to extract material map data
260         const void* p = name.c_str() + strlen(name.c_str()) + 1;
261         nboUseErrorChecking(false);
262         {
263             int32_t count;
264             p = nboUnpackInt(p, count);
265             for (int i = 0; i < count; i++)
266             {
267                 int32_t srcIndex, dstIndex;
268                 p = nboUnpackInt(p, srcIndex);
269                 p = nboUnpackInt(p, dstIndex);
270                 const BzMaterial* srcMat = MATERIALMGR.getMaterial(srcIndex);
271                 const BzMaterial* dstMat = MATERIALMGR.getMaterial(dstIndex);
272                 matMap[srcMat] = dstMat;
273             }
274         }
275         nboUseErrorChecking(true);
276         name.resize(strlen(name.c_str())); // clean up the name
277     }
278 
279     buf = transform.unpack(buf);
280 
281     uint8_t bits;
282     buf = nboUnpackUByte(buf, bits);
283     modifyTeam =      ((bits & (1 << 0)) == 0) ? false : true;
284     modifyColor =     ((bits & (1 << 1)) == 0) ? false : true;
285     modifyPhysicsDriver = ((bits & (1 << 2)) == 0) ? false : true;
286     modifyMaterial =  ((bits & (1 << 3)) == 0) ? false : true;
287     driveThrough =    ((bits & (1 << 4)) == 0) ? false : true;
288     shootThrough =    ((bits & (1 << 5)) == 0) ? false : true;
289     ricochet =        ((bits & (1 << 6)) == 0) ? false : true;
290 
291     if (modifyTeam)
292     {
293         uint16_t tmpTeam;
294         buf = nboUnpackUShort(buf, tmpTeam);
295         team = (int)tmpTeam;
296     }
297     if (modifyColor)
298     {
299         buf = nboUnpackVector(buf, tint);
300         buf = nboUnpackFloat(buf, tint[3]);
301     }
302     if (modifyPhysicsDriver)
303     {
304         int32_t inPhyDrv;
305         buf = nboUnpackInt(buf, inPhyDrv);
306         phydrv = int(inPhyDrv);
307     }
308     if (modifyMaterial)
309     {
310         int32_t matindex;
311         buf = nboUnpackInt(buf, matindex);
312         material = MATERIALMGR.getMaterial(matindex);
313     }
314 
315     return buf;
316 }
317 
318 
packSize()319 int GroupInstance::packSize()
320 {
321     int fullSize = 0;
322     fullSize += nboStdStringPackSize(groupdef);
323 
324     fullSize += nboStdStringPackSize(name);
325     if (matMap.size() > 0)
326     {
327         fullSize += 1; // terminator
328         fullSize += sizeof(int32_t); // count;
329         fullSize += matMap.size() * 2 * sizeof(int32_t);
330     }
331 
332     fullSize += transform.packSize();
333     fullSize += sizeof(uint8_t);
334     if (modifyTeam)
335         fullSize += sizeof(uint16_t);
336     if (modifyColor)
337         fullSize += sizeof(float[4]);
338     if (modifyPhysicsDriver)
339         fullSize += sizeof(int32_t);
340     if (modifyMaterial)
341         fullSize += sizeof(int32_t);
342     return fullSize;
343 }
344 
345 
print(std::ostream & out,const std::string & indent) const346 void GroupInstance::print(std::ostream& out, const std::string& indent) const
347 {
348     out << indent << "group " << groupdef << std::endl;
349 
350     if (name.size() > 0)
351         out << indent << "  name " << name << std::endl;
352 
353     transform.printTransforms(out, indent);
354     if (modifyTeam)
355         out << indent << "  team " << team << std::endl;
356     if (modifyColor)
357     {
358         out << indent << "  tint " << tint[0] << " " << tint[1] << " "
359             << tint[2] << " " << tint[3] << " "
360             << std::endl;
361     }
362     if (modifyPhysicsDriver)
363     {
364         const PhysicsDriver* driver = PHYDRVMGR.getDriver(phydrv);
365         if (driver != NULL)
366         {
367             out << indent << "  phydrv ";
368             if (driver->getName().size() > 0)
369                 out << driver->getName();
370             else
371                 out << phydrv;
372             out << std::endl;
373         }
374     }
375     if (modifyMaterial)
376     {
377         out << indent << "  matref ";
378         MATERIALMGR.printReference(out, material);
379         out << std::endl;
380     }
381     else if (matMap.size() > 0)
382     {
383         MaterialMap::const_iterator it;
384         for (it = matMap.begin(); it != matMap.end(); ++it)
385         {
386             out << indent << "  matswap ";
387             MATERIALMGR.printReference(out, it->first);
388             out << " ";
389             MATERIALMGR.printReference(out, it->second);
390             out << std::endl;
391         }
392     }
393 
394     if (driveThrough)
395         out << indent << "  driveThrough"  << std::endl;
396     if (shootThrough)
397         out << indent << "  shootThrough"  << std::endl;
398     if (ricochet)
399         out << indent << "  ricochet"      << std::endl;
400     out << indent << "end" << std::endl;
401 
402     return;
403 }
404 
405 
406 //////////////////////////////////////////////////////////////////////////////
407 //
408 // Group Definition
409 // - defines an obstacle group
410 //
411 
412 std::string GroupDefinition::depthName;
413 
414 
GroupDefinition(const std::string & _name)415 GroupDefinition::GroupDefinition(const std::string& _name)
416 {
417     name = _name;
418     active = false;
419     return;
420 }
421 
422 
~GroupDefinition()423 GroupDefinition::~GroupDefinition()
424 {
425     return;
426 }
427 
428 
newObstacle(int type)429 Obstacle* GroupDefinition::newObstacle(int type)
430 {
431     Obstacle* obs = NULL;
432 
433     if (type == wallType)
434         obs = new WallObstacle();
435     else if (type == boxType)
436         obs = new BoxBuilding();
437     else if (type == pyrType)
438         obs = new PyramidBuilding();
439     else if (type == baseType)
440         obs = new BaseBuilding();
441     else if (type == teleType)
442         obs = new Teleporter();
443     else if (type == meshType)
444         obs = new MeshObstacle();
445     else if (type == arcType)
446         obs = new ArcObstacle();
447     else if (type == coneType)
448         obs = new ConeObstacle();
449     else if (type == sphereType)
450         obs = new SphereObstacle();
451     else if (type == tetraType)
452         obs = new TetraBuilding();
453 
454     return obs;
455 }
456 
457 
addObstacle(Obstacle * obstacle)458 void GroupDefinition::addObstacle(Obstacle* obstacle)
459 {
460     const char* type = obstacle->getType();
461 
462     if (WallObstacle::getClassName() == type)
463         lists[wallType].push_back(obstacle);
464     else if (BoxBuilding::getClassName() == type)
465         lists[boxType].push_back(obstacle);
466     else if (BaseBuilding::getClassName() == type)
467         lists[baseType].push_back(obstacle);
468     else if (PyramidBuilding::getClassName() == type)
469         lists[pyrType].push_back(obstacle);
470     else if (Teleporter::getClassName() == type)
471         lists[teleType].push_back(obstacle);
472     else if (MeshObstacle::getClassName() == type)
473         lists[meshType].push_back(obstacle);
474     else if (ArcObstacle::getClassName() == type)
475         lists[arcType].push_back(obstacle);
476     else if (ConeObstacle::getClassName() == type)
477         lists[coneType].push_back(obstacle);
478     else if (SphereObstacle::getClassName() == type)
479         lists[sphereType].push_back(obstacle);
480     else if (TetraBuilding::getClassName() == type)
481         lists[tetraType].push_back(obstacle);
482     else
483     {
484         printf ("GroupDefinition::addObstacle() ERROR: type = %s\n", type);
485         exit(1);
486     }
487 
488     return;
489 }
490 
491 
addGroupInstance(GroupInstance * group)492 void GroupDefinition::addGroupInstance(GroupInstance* group)
493 {
494     groups.push_back(group);
495     return;
496 }
497 
498 
isContainer(int type)499 static bool isContainer(int type)
500 {
501     switch (type)
502     {
503     case GroupDefinition::arcType:
504     case GroupDefinition::coneType:
505     case GroupDefinition::sphereType:
506     case GroupDefinition::tetraType:
507         return true;
508     default:
509         return false;
510     }
511 }
512 
513 
makeTeleName(Obstacle * obs,unsigned int pos) const514 void GroupDefinition::makeTeleName(Obstacle* obs, unsigned int pos) const
515 {
516     Teleporter* tele = (Teleporter*) obs;
517     std::string fullname = depthName;
518     if (tele->getName().size() > 0)
519         fullname += tele->getName();
520     else
521     {
522         // make the default name
523         fullname += "/t";
524         char buffer[8];
525         sprintf (buffer, "%i", pos);
526         fullname += buffer;
527     }
528     tele->setName(fullname);
529     return;
530 }
531 
532 
appendGroupName(const GroupInstance * group) const533 void GroupDefinition::appendGroupName(const GroupInstance* group) const
534 {
535     std::string newName;
536     if (group->getName().size() > 0)
537         newName = group->getName();
538     else
539     {
540         // make the default name
541         int count = 0;
542         for (unsigned int i = 0; i < groups.size(); i++)
543         {
544             const GroupInstance* g = groups[i];
545             if (g == group)
546                 break;
547             if (g->getGroupDef() == group->getGroupDef())
548                 count++;
549         }
550         newName = "/";
551         newName += group->getGroupDef();
552         newName += "/";
553         std::stringstream buffer;
554         buffer << count;
555         newName += buffer.str();
556     }
557     depthName += newName;
558     depthName += ":";
559     return;
560 }
561 
562 
makeContainedMesh(int type,Obstacle * obs)563 static MeshObstacle* makeContainedMesh(int type, Obstacle* obs)
564 {
565     MeshObstacle* mesh = NULL;
566     switch (type)
567     {
568     case GroupDefinition::arcType:
569     {
570         mesh = ((ArcObstacle*)obs)->makeMesh();
571         break;
572     }
573     case GroupDefinition::coneType:
574     {
575         mesh = ((ConeObstacle*)obs)->makeMesh();
576         break;
577     }
578     case GroupDefinition::sphereType:
579     {
580         mesh = ((SphereObstacle*)obs)->makeMesh();
581         break;
582     }
583     case GroupDefinition::tetraType:
584     {
585         mesh = ((TetraBuilding*)obs)->makeMesh();
586         break;
587     }
588     }
589     return mesh;
590 }
591 
592 
makeGroups(const MeshTransform & xform,const ObstacleModifier & obsMod) const593 void GroupDefinition::makeGroups(const MeshTransform& xform,
594                                  const ObstacleModifier& obsMod) const
595 {
596     if (active)
597     {
598         logDebugMessage(1,"warning: avoided recursion, groupdef \"%s\"\n", name.c_str());
599         return; // avoid recursion
600     }
601 
602     active = true;
603 
604     const bool isWorld = (this == OBSTACLEMGR.getWorld());
605     char groupDefBit = isWorld ? Obstacle::WorldSource :
606                        Obstacle::GroupDefSource;
607 
608     for (int type = 0; type < ObstacleTypeCount; type++)
609     {
610         const ObstacleList& list = lists[type];
611         for (unsigned int i = 0; i < list.size(); i++)
612         {
613             Obstacle* obs;
614             if (isWorld)
615             {
616                 obs = list[i]; // no need to copy
617             }
618             else
619                 obs = list[i]->copyWithTransform(xform);
620 
621             // the tele names are setup with default names if
622             // they are not named (even for those in the world
623             // groupd def). invalid teleporters are also named
624             if (type == teleType)
625                 makeTeleName(obs, i);
626 
627             if (obs->isValid())
628             {
629                 if (!isWorld)
630                 {
631                     // add it to the world
632                     // (this will also add container obstacles into the world group)
633                     obs->setSource(Obstacle::GroupDefSource);
634                     obsMod.execute(obs);
635                     OBSTACLEMGR.addWorldObstacle(obs);
636                     // add a modified MeshDrawInfo to the new mesh, if applicable
637                     if (type == meshType)
638                     {
639                         const MeshObstacle* source = (const MeshObstacle*) list[i];
640                         const MeshDrawInfo* diSource = source->getDrawInfo();
641                         if ((diSource != NULL) &&
642                                 (!diSource->isServerSide()) && diSource->isValid())
643                         {
644                             MaterialSet matSet;
645                             MaterialMap matMap;
646                             diSource->getMaterials(matSet);
647                             obsMod.getMaterialMap(matSet, matMap);
648                             MeshDrawInfo* diDest = new MeshDrawInfo(diSource, xform, matMap);
649                             MeshObstacle* dest = (MeshObstacle*) obs;
650                             dest->setDrawInfo(diDest);
651                         }
652                     }
653                 }
654                 // generate contained meshes
655                 // (always get placed into the world group)
656                 MeshObstacle* mesh = makeContainedMesh(type, obs);
657                 if ((mesh != NULL) && mesh->isValid())
658                 {
659                     mesh->setSource(Obstacle::ContainerSource | groupDefBit);
660                     obsMod.execute(mesh);
661                     OBSTACLEMGR.addWorldObstacle(mesh);
662                 }
663             }
664         }
665     }
666 
667     for (unsigned int i = 0; i < groups.size(); i++)
668     {
669         const GroupInstance* group = groups[i];
670         const GroupDefinition* groupDef =
671             OBSTACLEMGR.findGroupDef(group->getGroupDef());
672         if (groupDef != NULL)
673         {
674             // make the new depth name
675             std::string tmpDepthName = depthName;
676             appendGroupName(group);
677 
678             // setup the transform and modifier
679             ObstacleModifier newObsMod(obsMod, *group);
680             MeshTransform tmpXform = xform;
681             tmpXform.prepend(group->getTransform());
682 
683             // recurse and make plentiful
684             groupDef->makeGroups(tmpXform, newObsMod);
685 
686             // revert to the old depth name
687             depthName = tmpDepthName;
688         }
689         else
690         {
691             logDebugMessage(1,"warning: group definition \"%s\" is missing\n",
692                             group->getGroupDef().c_str());
693         }
694     }
695 
696     active = false;
697 
698     return;
699 }
700 
701 
replaceBasesWithBoxes()702 void GroupDefinition::replaceBasesWithBoxes()
703 {
704     ObstacleList& list = lists[baseType];
705     for (unsigned int i = 0; i < list.size(); i++)
706     {
707         BaseBuilding* base = (BaseBuilding*) list[i];
708         const float* baseSize = base->getSize();
709         BoxBuilding* box =
710             new BoxBuilding(base->getPosition(), base->getRotation(),
711                             baseSize[0], baseSize[1], baseSize[2],
712                             base->isDriveThrough(), base->isShootThrough(),
713                             base->canRicochet(), false);
714         delete base;
715         list.remove(i);
716         i--;
717         addObstacle(box);
718     }
719     return;
720 }
721 
722 
clear()723 void GroupDefinition::clear()
724 {
725     for (int type = 0; type < ObstacleTypeCount; type++)
726     {
727         ObstacleList& list = lists[type];
728         for (unsigned int i = 0; i < list.size(); i++)
729             delete list[i];
730         list.clear();
731     }
732 
733     for (unsigned int i = 0; i < groups.size(); i++)
734         delete groups[i];
735     groups.clear();
736 
737     return;
738 }
739 
740 
tighten()741 void GroupDefinition::tighten()
742 {
743     for (int type = 0; type < ObstacleTypeCount; type++)
744         lists[type].tighten();
745     return;
746 }
747 
748 
sort(int (* compare)(const void * a,const void * b))749 void GroupDefinition::sort(int (*compare)(const void* a, const void* b))
750 {
751     if (this != OBSTACLEMGR.getWorld())
752     {
753         return; // only sort the world groupdef
754     }
755     for (int type = 0; type < ObstacleTypeCount; type++)
756         lists[type].sort(compare);
757     return;
758 }
759 
760 
deleteInvalidObstacles()761 void GroupDefinition::deleteInvalidObstacles()
762 {
763     if (this != OBSTACLEMGR.getWorld())
764     {
765         return; // only delete invalid obstacles in the world groupdef
766     }
767     for (int type = 0; type < ObstacleTypeCount; type++)
768     {
769         ObstacleList& list = lists[teleType];
770         for (unsigned int i = 0; i < list.size(); i++)
771         {
772             Obstacle* obs = list[i];
773             if (!obs->isValid())
774             {
775                 logDebugMessage(1,"Deleted invalid %s obstacle\n", obs->getType());
776                 delete obs;
777                 list.remove(i);
778                 i--; // don't miss the substitute
779             }
780         }
781     }
782     return;
783 }
784 
785 
getSourceMeshes(std::vector<MeshObstacle * > & meshes) const786 void GroupDefinition::getSourceMeshes(std::vector<MeshObstacle*>& meshes) const
787 {
788     const bool isWorld = (this == OBSTACLEMGR.getWorld());
789 
790     const ObstacleList& list = lists[meshType];
791     for (unsigned int i = 0; i < list.size(); i++)
792     {
793         MeshObstacle* mesh = (MeshObstacle*)list[i];
794         if (!isWorld || mesh->isFromWorldFile())
795         {
796             const int listSize = (int)meshes.size();
797             int j;
798             for (j = 0; j < listSize; j++)
799             {
800                 if (meshes[j] == mesh)
801                     break;
802             }
803             if (j == listSize)
804             {
805                 meshes.push_back(mesh); // a new entry
806             }
807         }
808     }
809 
810     for (unsigned int i = 0; i < groups.size(); i++)
811     {
812         const GroupInstance* group = groups[i];
813         const GroupDefinition* groupDef =
814             OBSTACLEMGR.findGroupDef(group->getGroupDef());
815         if (groupDef != NULL)
816             groupDef->getSourceMeshes(meshes);
817     }
818 
819     return;
820 }
821 
822 
pack(void * buf) const823 void* GroupDefinition::pack(void* buf) const
824 {
825     buf = nboPackStdString(buf, name);
826 
827     unsigned int i;
828     for (int type = 0; type < ObstacleTypeCount; type++)
829     {
830         const ObstacleList& list = getList(type);
831         int count = 0;
832         for (i = 0; i < list.size(); i++)
833         {
834             if (list[i]->isFromWorldFile())
835                 count++;
836         }
837         buf = nboPackUInt(buf, count);
838         for (i = 0; i < list.size(); i++)
839         {
840             if (list[i]->isFromWorldFile())
841                 buf = list[i]->pack(buf);
842         }
843     }
844 
845     buf = nboPackUInt(buf, groups.size());
846     for (i = 0; i < groups.size(); i++)
847         buf = groups[i]->pack(buf);
848 
849     return buf;
850 }
851 
852 
unpack(const void * buf)853 const void* GroupDefinition::unpack(const void* buf)
854 {
855     buf = nboUnpackStdString(buf, name);
856 
857     uint32_t i, count;
858 
859     for (int type = 0; type < ObstacleTypeCount; type++)
860     {
861         buf = nboUnpackUInt(buf, count);
862         for (i = 0; i < count; i++)
863         {
864             Obstacle* obs = newObstacle(type);
865             if (obs != NULL)
866             {
867                 buf = obs->unpack(buf);
868                 if (obs->isValid())
869                     lists[type].push_back(obs);
870             }
871         }
872     }
873 
874     buf = nboUnpackUInt(buf, count);
875     for (i = 0; i < count; i++)
876     {
877         GroupInstance* group = new GroupInstance;
878         buf = group->unpack(buf);
879         addGroupInstance(group);
880     }
881 
882     return buf;
883 }
884 
885 
packSize() const886 int GroupDefinition::packSize() const
887 {
888     int fullSize = 0;
889 
890     fullSize += nboStdStringPackSize(name);
891 
892     for (int type = 0; type < ObstacleTypeCount; type++)
893     {
894         fullSize += sizeof(uint32_t);
895         const ObstacleList& list = getList(type);
896         for (unsigned int i = 0; i < list.size(); i++)
897         {
898             if (list[i]->isFromWorldFile())
899                 fullSize += list[i]->packSize();
900         }
901     }
902     fullSize += sizeof(uint32_t);
903     for (unsigned int i = 0; i < groups.size(); i++)
904         fullSize += groups[i]->packSize();
905 
906     return fullSize;
907 }
908 
909 
printGrouped(std::ostream & out,const std::string & indent) const910 void GroupDefinition::printGrouped(std::ostream& out,
911                                    const std::string& indent) const
912 {
913     const bool isWorld = (this == OBSTACLEMGR.getWorld());
914     const bool saveAsMeshes = BZDB.isTrue("saveAsMeshes");
915     std::string myIndent = indent;
916 
917     // deal with indenting
918     if (!isWorld)
919     {
920         myIndent += "  ";
921         out << indent << "define " << name << std::endl;
922     }
923 
924     // print the obstacles
925     for (int type = 0; type < ObstacleTypeCount; type++)
926     {
927         const ObstacleList& list = getList(type);
928         for (unsigned int i = 0; i < list.size(); i++)
929         {
930             Obstacle* obs = list[i];
931 
932             if (!obs->isFromGroupDef() && !obs->isFromContainer())
933             {
934                 if (!saveAsMeshes)
935                 {
936                     if (!obs->isFromContainer())
937                         obs->print(out, myIndent);
938                 }
939                 else
940                 {
941                     if (!isContainer(type))
942                         obs->print(out, myIndent);
943                     else
944                     {
945                         // rebuild the mesh  (ya, just to print it)
946                         MeshObstacle* mesh = makeContainedMesh(type, obs);
947                         if ((mesh != NULL) && (mesh->isValid()))
948                             mesh->print(out, myIndent);
949                         delete mesh;
950                     }
951                 }
952             }
953         }
954     }
955 
956     // print the groups
957     for (unsigned int i = 0; i < groups.size(); i++)
958         groups[i]->print(out, myIndent);
959 
960     // deal with indenting
961     if (!isWorld)
962         out << indent << "enddef" << std::endl << std::endl;
963 
964     return;
965 }
966 
967 
printFlatFile(std::ostream & out,const std::string & indent) const968 void GroupDefinition::printFlatFile(std::ostream& out,
969                                     const std::string& indent) const
970 {
971     const bool saveAsOBJ = BZDB.isTrue("saveAsOBJ");
972     const bool saveAsMeshes = BZDB.isTrue("saveAsMeshes");
973 
974     // print the obstacles
975     for (int type = 0; type < ObstacleTypeCount; type++)
976     {
977         const ObstacleList& list = getList(type);
978         for (unsigned int i = 0; i < list.size(); i++)
979         {
980             const Obstacle* obs = list[i];
981 
982             if (!saveAsMeshes)
983             {
984                 if (!obs->isFromContainer())
985                     obs->print(out, indent);
986             }
987             else
988             {
989                 if (!isContainer(type))
990                 {
991                     if (!saveAsOBJ)
992                         obs->print(out, indent);
993                     else
994                         obs->printOBJ(out, indent);
995                 }
996             }
997         }
998     }
999 
1000     return;
1001 }
1002 
1003 
clearDepthName()1004 void GroupDefinition::clearDepthName()
1005 {
1006     depthName = "";
1007     return;
1008 }
1009 
1010 
1011 //////////////////////////////////////////////////////////////////////////////
1012 //
1013 // Group Definition Manager
1014 // - utility class to keep track of group definitions
1015 //
1016 
1017 GroupDefinitionMgr OBSTACLEMGR;
1018 
1019 
GroupDefinitionMgr()1020 GroupDefinitionMgr::GroupDefinitionMgr() : world("")
1021 {
1022     return;
1023 }
1024 
1025 
~GroupDefinitionMgr()1026 GroupDefinitionMgr::~GroupDefinitionMgr()
1027 {
1028     clear();
1029     return;
1030 }
1031 
1032 
clear()1033 void GroupDefinitionMgr::clear()
1034 {
1035     world.clear();
1036     for (unsigned int i = 0; i < list.size(); i++)
1037     {
1038         list[i]->clear();
1039         delete list[i];
1040     }
1041     list.clear();
1042     return;
1043 }
1044 
1045 
tighten()1046 void GroupDefinitionMgr::tighten()
1047 {
1048     for (unsigned int i = 0; i < list.size(); i++)
1049         list[i]->tighten();
1050     world.tighten();
1051     return;
1052 }
1053 
1054 
compareHeights(const void * a,const void * b)1055 static int compareHeights(const void* a, const void* b)
1056 {
1057     const Obstacle* obsA = *((const Obstacle* const *)a);
1058     const Obstacle* obsB = *((const Obstacle* const *)b);
1059     const Extents& eA = obsA->getExtents();
1060     const Extents& eB = obsB->getExtents();
1061 
1062     if (eA.maxs[2] > eB.maxs[2])
1063         return -1;
1064     else
1065         return +1;
1066 }
1067 
1068 
makeWorld()1069 void GroupDefinitionMgr::makeWorld()
1070 {
1071     GroupDefinition::clearDepthName();
1072 
1073     MeshTransform noXform;
1074     ObstacleModifier noMods;
1075 
1076     world.makeGroups(noXform, noMods);
1077 
1078     world.deleteInvalidObstacles();
1079 
1080     // sort from top to bottom for enhanced radar
1081     for (int type = 0; type < GroupDefinition::ObstacleTypeCount; type++)
1082         world.sort(compareHeights);
1083 
1084     tighten();
1085 
1086     return;
1087 }
1088 
1089 
replaceBasesWithBoxes()1090 void GroupDefinitionMgr::replaceBasesWithBoxes()
1091 {
1092     world.replaceBasesWithBoxes();
1093 }
1094 
1095 
addWorldObstacle(Obstacle * obstacle)1096 void GroupDefinitionMgr::addWorldObstacle(Obstacle* obstacle)
1097 {
1098     world.addObstacle(obstacle);
1099     return;
1100 }
1101 
1102 
addGroupDef(GroupDefinition * groupdef)1103 void GroupDefinitionMgr::addGroupDef(GroupDefinition* groupdef)
1104 {
1105     if (groupdef->getName().size() > 0)
1106         list.push_back(groupdef);
1107     else
1108         delete groupdef;
1109     return;
1110 }
1111 
1112 
findGroupDef(const std::string & name) const1113 GroupDefinition* GroupDefinitionMgr::findGroupDef(const std::string& name) const
1114 {
1115     if (name.size() <= 0)
1116         return NULL;
1117     for (unsigned int i = 0; i < list.size(); i++)
1118     {
1119         if (name == list[i]->getName())
1120             return list[i];
1121     }
1122     return NULL;
1123 }
1124 
1125 
getSourceMeshes(std::vector<MeshObstacle * > & meshes) const1126 void GroupDefinitionMgr::getSourceMeshes(std::vector<MeshObstacle*>& meshes) const
1127 {
1128     meshes.clear();
1129     world.getSourceMeshes(meshes);
1130     return;
1131 }
1132 
1133 
pack(void * buf) const1134 void* GroupDefinitionMgr::pack(void* buf) const
1135 {
1136     buf = world.pack(buf);
1137     buf = nboPackUInt(buf, list.size());
1138     for (unsigned int i = 0; i < list.size(); i++)
1139         buf = list[i]->pack(buf);
1140     return buf;
1141 }
1142 
1143 
unpack(const void * buf)1144 const void* GroupDefinitionMgr::unpack(const void* buf)
1145 {
1146     buf = world.unpack(buf);
1147     uint32_t i, count;
1148     buf = nboUnpackUInt(buf, count);
1149     for (i = 0; i < count; i++)
1150     {
1151         GroupDefinition* groupdef = new GroupDefinition("");
1152         buf = groupdef->unpack(buf);
1153         addGroupDef(groupdef);
1154     }
1155     return buf;
1156 }
1157 
1158 
packSize() const1159 int GroupDefinitionMgr::packSize() const
1160 {
1161     int fullSize = 0;
1162 
1163     fullSize += world.packSize();
1164     fullSize += sizeof(uint32_t);
1165     for (unsigned int i = 0; i < list.size(); i++)
1166         fullSize += list[i]->packSize();
1167     return fullSize;
1168 }
1169 
1170 
print(std::ostream & out,const std::string & indent) const1171 void GroupDefinitionMgr::print(std::ostream& out,
1172                                const std::string& indent) const
1173 {
1174     const bool saveAsOBJ = BZDB.isTrue("saveAsOBJ");
1175     const bool saveFlatFile = BZDB.isTrue("saveFlatFile");
1176     const bool saveAsMeshes = BZDB.isTrue("saveAsMeshes");
1177     if (saveAsOBJ)
1178     {
1179         BZDB.set("saveAsMeshes", "1");
1180         BZDB.set("saveFlatFile", "1");
1181     }
1182 
1183     // for unique OBJ mesh ids
1184     Obstacle::resetObjCounter();
1185 
1186     if (!(saveFlatFile || saveAsOBJ))
1187     {
1188         // print the group definitions
1189         for (unsigned int i = 0; i < list.size(); i++)
1190             list[i]->printGrouped(out, indent);
1191         // print the world
1192         world.printGrouped(out, indent);
1193     }
1194     else
1195     {
1196         // print the world
1197         world.printFlatFile(out, indent);
1198     }
1199 
1200     if (saveAsOBJ)
1201     {
1202         BZDB.set("saveAsMeshes", saveAsMeshes ? "1" : "0");
1203         BZDB.set("saveFlatFile", saveFlatFile ? "1" : "0");
1204     }
1205 
1206     return;
1207 }
1208 
1209 
1210 // Local Variables: ***
1211 // mode: C++ ***
1212 // tab-width: 4 ***
1213 // c-basic-offset: 4 ***
1214 // indent-tabs-mode: nil ***
1215 // End: ***
1216 // ex: shiftwidth=4 tabstop=4
1217