1 /** @file sector.h  World map sector.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2016 Daniel Swanson <danij@dengine.net>
5  *
6  * @par License
7  * GPL: http://www.gnu.org/licenses/gpl.html
8  *
9  * <small>This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version. This program is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details. You should have received a copy of the GNU
16  * General Public License along with this program; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA</small>
19  */
20 
21 #include "world/sector.h"
22 
23 #include "world/map.h"
24 #include "world/p_object.h"
25 #include "ConvexSubspace"
26 #include "Line"
27 #include "Plane"
28 #include "Subsector"
29 #include "Surface"
30 
31 #include "dd_main.h"  // App_World()
32 
33 #include <doomsday/console/cmd.h>
34 #include <de/LogBuffer>
35 #include <de/vector1.h>
36 #include <de/Rectangle>
37 #include <QList>
38 #include <QtAlgorithms>
39 
40 using namespace de;
41 using namespace world;
42 
43 static Sector::SubsectorConstructor subsectorConstructor;
44 
DENG2_PIMPL(Sector)45 DENG2_PIMPL(Sector)
46 , DENG2_OBSERVES(Plane, HeightChange)
47 {
48     /**
49      * POD: Metrics describing the geometry of the sector (the subsectors).
50      */
51     struct GeomData
52     {
53         AABoxd bounds;              ///< Bounding box for the whole sector (all subsectors).
54         ddouble roughArea = 0;      ///< Rough approximation.
55     };
56 
57     struct MapObjects
58     {
59         mobj_t *head = nullptr;     ///< The list of map objects.
60 
61         /**
62          * Returns @c true if the map-object @a mob is linked.
63          */
64         bool contains(mobj_t const *mob) const
65         {
66             if (mob)
67             {
68                 for (mobj_t const *it = head; it; it = it->sNext)
69                 {
70                     if (it == mob) return true;
71                 }
72             }
73             return false;
74         }
75 
76         void add(mobj_t *mob)
77         {
78             if (!mob) return;
79 
80             // Ensure this isn't already included.
81             DENG2_ASSERT(!contains(mob));
82 
83             // Prev pointers point to the pointer that points back to us.
84             // (Which practically disallows traversing the list backwards.)
85             if ((mob->sNext = head))
86             {
87                 mob->sNext->sPrev = &mob->sNext;
88             }
89             *(mob->sPrev = &head) = mob;
90         }
91 
92         /**
93          * Two links to update:
94          * 1) The link to the mobj from the previous node (sprev, always set) will
95          *    be modified to point to the node following it.
96          * 2) If there is a node following the mobj, set its sprev pointer to point
97          *    to the pointer that points back to it (the mobj's sprev, just modified).
98          */
99         void remove(mobj_t *mob)
100         {
101             if (!mob || !Mobj_IsSectorLinked(*mob)) return;
102 
103             if ((*mob->sPrev = mob->sNext))
104             {
105                 mob->sNext->sPrev = mob->sPrev;
106             }
107             // Not linked any more.
108             mob->sNext = nullptr;
109             mob->sPrev = nullptr;
110 
111             // Ensure this has been completely unlinked.
112             DENG2_ASSERT(!contains(mob));
113         }
114     };
115 
116     struct Planes : public QVector<Plane *>
117     {
118         ~Planes() { clear(); }
119 
120         void clear() {
121             qDeleteAll(*this);
122             QVector<Plane *>::clear();
123         }
124     };
125 
126     struct Subsectors : public QVector<Subsector *>
127     {
128         ~Subsectors() { clear(); }
129 
130         void clear() {
131             qDeleteAll(*this);
132             QVector<Subsector *>::clear();
133         }
134     };
135 
136     Planes planes;                   ///< Planes of the sector.
137     MapObjects mapObjects;           ///< All map-objects "in" one of the subsectors (not owned).
138     QVector<LineSide *> sides;       ///< All line sides referencing the sector (not owned).
139     Subsectors subsectors;           ///< Traversable subsectors of the sector.
140     ThinkerT<SoundEmitter> emitter;  ///< Head of the sound emitter chain.
141     int visPlaneLinkSector = MapElement::NoIndex;
142     int visPlaneLinkBits = 0;
143     dfloat lightLevel = 0;           ///< Ambient light level.
144     Vector3f lightColor;             ///< Ambient light color.
145 
146     std::unique_ptr<GeomData> gdata; ///< Additional geometry info/metrics (cache).
147 
148     dint validCount = 0;             ///< Used by legacy algorithms to prevent repeated processing.
149 
150     Impl(Public *i) : Base(i) {}
151 
152     ~Impl()
153     {
154         // Ensure planes are cleared first (subsectors may include mappings).
155         planes.clear();
156 
157         delete [] self()._lookupPlanes;
158     }
159 
160     /**
161      * Returns the additional geometry info/metrics from the cache.
162      */
163     GeomData &geom()
164     {
165         if (!gdata)
166         {
167             // Time to prepare this info.
168             std::unique_ptr<GeomData> gd(new GeomData);
169             gd->bounds    = findBounds();
170             gd->roughArea = findRoughArea();
171             gdata.reset(gd.get());
172             gd.release();
173 
174             // As the bounds are now known; update the origin of the primary SoundEmitter.
175             emitter->origin[0] = (gdata->bounds.minX + gdata->bounds.maxX) / 2;
176             emitter->origin[1] = (gdata->bounds.minY + gdata->bounds.maxY) / 2;
177         }
178         return *gdata;
179     }
180 
181     /**
182      * Calculate the minimum bounding rectangle containing all the subsector geometries.
183      */
184     AABoxd findBounds() const
185     {
186         bool inited = false;
187         AABoxd bounds;
188         for (Subsector const *subsec : subsectors)
189         {
190             if (inited)
191             {
192                 V2d_UniteBox(bounds.arvec2, subsec->bounds().arvec2);
193             }
194             else
195             {
196                 bounds = subsec->bounds();
197                 inited = true;
198             }
199         }
200         return bounds;
201     }
202 
203     /**
204      * Approximate the total area of all the subsector geometries.
205      */
206     ddouble findRoughArea() const
207     {
208         ddouble roughArea = 0;
209         for (Subsector const *subsec : subsectors)
210         {
211             roughArea += subsec->roughArea();
212         }
213         return roughArea;
214     }
215 
216     void updateEmitterOriginZ()
217     {
218         emitter->origin[2] = (self().floor().height() + self().ceiling().height()) / 2;
219     }
220 
221     void updateSideEmitterOrigins()
222     {
223         for (LineSide *side : sides)
224         {
225             side->updateAllSoundEmitterOrigins();
226             side->back().updateAllSoundEmitterOrigins();
227         }
228     }
229 
230     void updateAllEmitterOrigins()
231     {
232         updateEmitterOriginZ();
233         updateSideEmitterOrigins();
234     }
235 
236     void planeHeightChanged(Plane &)
237     {
238         updateAllEmitterOrigins();
239     }
240 
241     void updatePlanesLookup()
242     {
243         delete [] self()._lookupPlanes;
244 
245         self()._lookupPlanes = new Plane *[planes.size()];
246         Plane **ptr = self()._lookupPlanes;
247         for (Plane *p : planes)
248         {
249             *ptr++ = p;
250         }
251     }
252 
253     DENG2_PIMPL_AUDIENCE(LightLevelChange)
254     DENG2_PIMPL_AUDIENCE(LightColorChange)
255 };
256 
DENG2_AUDIENCE_METHOD(Sector,LightLevelChange)257 DENG2_AUDIENCE_METHOD(Sector, LightLevelChange)
258 DENG2_AUDIENCE_METHOD(Sector, LightColorChange)
259 
260 Sector::Sector(dfloat lightLevel, Vector3f const &lightColor)
261     : MapElement(DMU_SECTOR)
262     , d(new Impl(this))
263     , _lookupPlanes(nullptr)
264 {
265     d->lightLevel = de::clamp(0.f, lightLevel, 1.f);
266     d->lightColor = lightColor.min(Vector3f(1, 1, 1)).max(Vector3f(0, 0, 0));
267 }
268 
unlink(mobj_t * mob)269 void Sector::unlink(mobj_t *mob)
270 {
271     d->mapObjects.remove(mob);
272 }
273 
link(mobj_t * mob)274 void Sector::link(mobj_t *mob)
275 {
276     d->mapObjects.add(mob);
277 }
278 
firstMobj() const279 struct mobj_s *Sector::firstMobj() const
280 {
281     return d->mapObjects.head;
282 }
283 
hasSkyMaskPlane() const284 bool Sector::hasSkyMaskPlane() const
285 {
286     for (Plane *plane : d->planes)
287     {
288         if (plane->surface().hasSkyMaskedMaterial())
289             return true;
290     }
291     return false;
292 }
293 
planeCount() const294 dint Sector::planeCount() const
295 {
296     return d->planes.count();
297 }
298 
forAllPlanes(std::function<LoopResult (Plane &)> func)299 LoopResult Sector::forAllPlanes(std::function<LoopResult (Plane &)> func)
300 {
301     for (Plane *plane : d->planes)
302     {
303         if(auto result = func(*plane)) return result;
304     }
305     return LoopContinue;
306 }
307 
forAllPlanes(std::function<LoopResult (Plane const &)> func) const308 LoopResult Sector::forAllPlanes(std::function<LoopResult (Plane const &)> func) const
309 {
310     for (Plane const *plane : d->planes)
311     {
312         if(auto result = func(*plane)) return result;
313     }
314     return LoopContinue;
315 }
316 
addPlane(Vector3f const & normal,ddouble height)317 Plane *Sector::addPlane(Vector3f const &normal, ddouble height)
318 {
319     auto *plane = new Plane(*this, normal, height);
320 
321     plane->setIndexInSector(d->planes.count());
322     d->planes.append(plane);
323     d->updatePlanesLookup();
324 
325     if (plane->isSectorFloor() || plane->isSectorCeiling())
326     {
327         // We want notification of height changes so that we can update sound emitter
328         // origins of all the dependent surfaces.
329         plane->audienceForHeightChange() += d;
330     }
331 
332     // Once both floor and ceiling are known we can determine the z-height origin
333     // of our sound emitter.
334     /// @todo fixme: Assume planes are defined in order.
335     if (planeCount() == 2)
336     {
337         d->updateEmitterOriginZ();
338     }
339 
340     return plane;
341 }
342 
setVisPlaneLinks(int sectorArchiveIndex,int planeBits)343 void Sector::setVisPlaneLinks(int sectorArchiveIndex, int planeBits)
344 {
345     d->visPlaneLinkSector = sectorArchiveIndex;
346     d->visPlaneLinkBits   = planeBits;
347 }
348 
visPlaneLinkTargetSector() const349 int Sector::visPlaneLinkTargetSector() const
350 {
351     return d->visPlaneLinkSector;
352 }
353 
isVisPlaneLinked(int planeIndex) const354 bool Sector::isVisPlaneLinked(int planeIndex) const
355 {
356     return (d->visPlaneLinkBits & (1 << planeIndex)) != 0;
357 }
358 
visPlaneBits() const359 int Sector::visPlaneBits() const
360 {
361     return d->visPlaneLinkBits;
362 }
363 
hasSubsectors() const364 bool Sector::hasSubsectors() const
365 {
366     return !d->subsectors.isEmpty();
367 }
368 
subsectorCount() const369 dint Sector::subsectorCount() const
370 {
371     return d->subsectors.count();
372 }
373 
subsector(int index) const374 Subsector &Sector::subsector(int index) const
375 {
376     DENG2_ASSERT(index >= 0 && index < d->subsectors.count());
377     return *d->subsectors.at(index);
378 }
379 
forAllSubsectors(const std::function<LoopResult (Subsector &)> & callback) const380 LoopResult Sector::forAllSubsectors(const std::function<LoopResult(Subsector &)> &callback) const
381 {
382     for (Subsector *subsec : d->subsectors)
383     {
384         if (auto result = callback(*subsec)) return result;
385     }
386     return LoopContinue;
387 }
388 
addSubsector(QVector<ConvexSubspace * > const & subspaces)389 Subsector *Sector::addSubsector(QVector<ConvexSubspace *> const &subspaces)
390 {
391     DENG2_ASSERT(subsectorConstructor);
392     /// @todo Add/move debug logic for ensuring the set is valid here. -ds
393     std::unique_ptr<Subsector> subsec(subsectorConstructor(subspaces));
394     d->subsectors << subsec.get();
395     LOG_MAP_XVERBOSE("New Subsector %s (sector-%s)", subsec->id().asText() << indexInMap());
396     return subsec.release();
397 }
398 
sideCount() const399 dint Sector::sideCount() const
400 {
401     return d->sides.count();
402 }
403 
forAllSides(std::function<LoopResult (LineSide &)> func) const404 LoopResult Sector::forAllSides(std::function<LoopResult (LineSide &)> func) const
405 {
406     for (LineSide *side : d->sides)
407     {
408         if (auto result = func(*side)) return result;
409     }
410     return LoopContinue;
411 }
412 
buildSides()413 void Sector::buildSides()
414 {
415     d->sides.clear();
416 
417     dint count = 0;
418     map().forAllLines([this, &count] (Line &line)
419     {
420         if (line.front().sectorPtr() == this || line.back().sectorPtr() == this)
421         {
422             count += 1;
423         }
424         return LoopContinue;
425     });
426 
427     if (!count) return;
428 
429     d->sides.reserve(count);
430 
431     map().forAllLines([this] (Line &line)
432     {
433         if (line.front().sectorPtr() == this)
434         {
435             d->sides.append(&line.front()); // Ownership not given.
436         }
437         else if (line.back().sectorPtr()  == this)
438         {
439             d->sides.append(&line.back()); // Ownership not given.
440         }
441         return LoopContinue;
442     });
443 
444     d->updateAllEmitterOrigins();
445 }
446 
soundEmitter()447 SoundEmitter &Sector::soundEmitter()
448 {
449     // Emitter origin depends on the axis-aligned bounding box.
450     (void) d->geom();
451     return d->emitter;
452 }
453 
soundEmitter() const454 SoundEmitter const &Sector::soundEmitter() const
455 {
456     return const_cast<SoundEmitter const &>(const_cast<Sector &>(*this).soundEmitter());
457 }
458 
linkSoundEmitter(SoundEmitter & root,SoundEmitter & newEmitter)459 static void linkSoundEmitter(SoundEmitter &root, SoundEmitter &newEmitter)
460 {
461     // The sector's base is always root of the chain, so link the other after it.
462     newEmitter.thinker.prev = &root.thinker;
463     newEmitter.thinker.next = root.thinker.next;
464     if (newEmitter.thinker.next)
465         newEmitter.thinker.next->prev = &newEmitter.thinker;
466     root.thinker.next = &newEmitter.thinker;
467 }
468 
chainSoundEmitters()469 void Sector::chainSoundEmitters()
470 {
471     SoundEmitter &root = d->emitter;
472 
473     // Clear the root of the emitter chain.
474     root.thinker.next = root.thinker.prev = nullptr;
475 
476     // Link emitters for planes.
477     for (Plane *plane : d->planes)
478     {
479         linkSoundEmitter(root, plane->soundEmitter());
480     }
481 
482     // Link emitters for LineSide sections.
483     for (LineSide *side : d->sides)
484     {
485         if (side->hasSections())
486         {
487             linkSoundEmitter(root, side->middleSoundEmitter());
488             linkSoundEmitter(root, side->bottomSoundEmitter());
489             linkSoundEmitter(root, side->topSoundEmitter   ());
490         }
491         if (side->line().isSelfReferencing() && side->back().hasSections())
492         {
493             LineSide &back = side->back();
494             linkSoundEmitter(root, back.middleSoundEmitter());
495             linkSoundEmitter(root, back.bottomSoundEmitter());
496             linkSoundEmitter(root, back.topSoundEmitter   ());
497         }
498     }
499 }
500 
lightLevel() const501 dfloat Sector::lightLevel() const
502 {
503     return d->lightLevel;
504 }
505 
setLightLevel(dfloat newLightLevel)506 void Sector::setLightLevel(dfloat newLightLevel)
507 {
508     newLightLevel = de::clamp(0.f, newLightLevel, 1.f);
509     if (!de::fequal(d->lightLevel, newLightLevel))
510     {
511         d->lightLevel = newLightLevel;
512         DENG2_FOR_AUDIENCE2(LightLevelChange, i) i->sectorLightLevelChanged(*this);
513     }
514 }
515 
lightColor() const516 Vector3f const &Sector::lightColor() const
517 {
518     return d->lightColor;
519 }
520 
setLightColor(Vector3f const & newLightColor)521 void Sector::setLightColor(Vector3f const &newLightColor)
522 {
523     auto newColorClamped = newLightColor.min(Vector3f(1, 1, 1)).max(Vector3f(0, 0, 0));
524     if (d->lightColor != newColorClamped)
525     {
526         d->lightColor = newColorClamped;
527         DENG2_FOR_AUDIENCE2(LightColorChange, i) i->sectorLightColorChanged(*this);
528     }
529 }
530 
validCount() const531 dint Sector::validCount() const
532 {
533     return d->validCount;
534 }
535 
setValidCount(dint newValidCount)536 void Sector::setValidCount(dint newValidCount)
537 {
538     d->validCount = newValidCount;
539 }
540 
bounds() const541 AABoxd const &Sector::bounds() const
542 {
543     return d->geom().bounds;
544 }
545 
546 #ifdef __CLIENT__
roughArea() const547 ddouble Sector::roughArea() const
548 {
549     return d->geom().roughArea;
550 }
551 #endif
552 
property(DmuArgs & args) const553 dint Sector::property(DmuArgs &args) const
554 {
555     switch (args.prop)
556     {
557     case DMU_LIGHT_LEVEL:
558         args.setValue(DMT_SECTOR_LIGHTLEVEL, &d->lightLevel, 0);
559         break;
560     case DMU_COLOR:
561         args.setValue(DMT_SECTOR_RGB, &d->lightColor.x, 0);
562         args.setValue(DMT_SECTOR_RGB, &d->lightColor.y, 1);
563         args.setValue(DMT_SECTOR_RGB, &d->lightColor.z, 2);
564         break;
565     case DMU_COLOR_RED:
566         args.setValue(DMT_SECTOR_RGB, &d->lightColor.x, 0);
567         break;
568     case DMU_COLOR_GREEN:
569         args.setValue(DMT_SECTOR_RGB, &d->lightColor.y, 0);
570         break;
571     case DMU_COLOR_BLUE:
572         args.setValue(DMT_SECTOR_RGB, &d->lightColor.z, 0);
573         break;
574     case DMU_EMITTER: {
575         SoundEmitter const *emitterAdr = d->emitter;
576         args.setValue(DMT_SECTOR_EMITTER, &emitterAdr, 0);
577         break; }
578     case DMT_MOBJS:
579         args.setValue(DMT_SECTOR_MOBJLIST, &d->mapObjects.head, 0);
580         break;
581     case DMU_VALID_COUNT:
582         args.setValue(DMT_SECTOR_VALIDCOUNT, &d->validCount, 0);
583         break;
584     case DMU_FLOOR_PLANE: {
585         Plane *pln = d->planes.at(Floor);
586         args.setValue(DMT_SECTOR_FLOORPLANE, &pln, 0);
587         break; }
588     case DMU_CEILING_PLANE: {
589         Plane *pln = d->planes.at(Ceiling);
590         args.setValue(DMT_SECTOR_CEILINGPLANE, &pln, 0);
591         break; }
592     default:
593         return MapElement::property(args);
594     }
595 
596     return false;  // Continue iteration.
597 }
598 
setProperty(DmuArgs const & args)599 dint Sector::setProperty(DmuArgs const &args)
600 {
601     switch (args.prop)
602     {
603     case DMU_COLOR: {
604         Vector3f newColor = d->lightColor;
605         args.value(DMT_SECTOR_RGB, &newColor.x, 0);
606         args.value(DMT_SECTOR_RGB, &newColor.y, 1);
607         args.value(DMT_SECTOR_RGB, &newColor.z, 2);
608         setLightColor(newColor);
609         break; }
610     case DMU_COLOR_RED: {
611         Vector3f newColor = d->lightColor;
612         args.value(DMT_SECTOR_RGB, &newColor.x, 0);
613         setLightColor(newColor);
614         break; }
615     case DMU_COLOR_GREEN: {
616         Vector3f newColor = d->lightColor;
617         args.value(DMT_SECTOR_RGB, &newColor.y, 0);
618         setLightColor(newColor);
619         break; }
620     case DMU_COLOR_BLUE: {
621         Vector3f newColor = d->lightColor;
622         args.value(DMT_SECTOR_RGB, &newColor.z, 0);
623         setLightColor(newColor);
624         break; }
625     case DMU_LIGHT_LEVEL: {
626         dfloat newLightLevel;
627         args.value(DMT_SECTOR_LIGHTLEVEL, &newLightLevel, 0);
628         setLightLevel(newLightLevel);
629         break; }
630     case DMU_VALID_COUNT:
631         args.value(DMT_SECTOR_VALIDCOUNT, &d->validCount, 0);
632         break;
633     default:
634         return MapElement::setProperty(args);
635     }
636 
637     return false;  // Continue iteration.
638 }
639 
D_CMD(InspectSector)640 D_CMD(InspectSector)
641 {
642     DENG2_UNUSED(src);
643 
644     LOG_AS("inspectsector (Cmd)");
645 
646     if (argc != 2)
647     {
648         LOG_SCR_NOTE("Usage: %s (sector-id)") << argv[0];
649         return true;
650     }
651 
652     if (!App_World().hasMap())
653     {
654         LOG_SCR_ERROR("No map is currently loaded");
655         return false;
656     }
657 
658     // Find the sector.
659     dint const index  = String(argv[1]).toInt();
660     Sector const *sec = App_World().map().sectorPtr(index);
661     if (!sec)
662     {
663         LOG_SCR_ERROR("Sector #%i not found") << index;
664         return false;
665     }
666 
667     LOG_SCR_MSG(_E(b) "Sector %s" _E(.) " [%p]")
668             << Id(sec->indexInMap()).asText() << sec;
669     LOG_SCR_MSG(    _E(l) "Bounds: "      _E(.)_E(i) "%s" _E(.)
670                 " " _E(l) "Light Color: " _E(.)_E(i) "%s" _E(.)
671                 " " _E(l) "Light Level: " _E(.)_E(i) "%f")
672             << Rectangled(sec->bounds().min, sec->bounds().max).asText()
673             << sec->lightColor().asText()
674             << sec->lightLevel();
675     if (sec->planeCount())
676     {
677         LOG_SCR_MSG(_E(D) "Planes (%i):") << sec->planeCount();
678         sec->forAllPlanes([] (Plane const &plane)
679         {
680             LOG_SCR_MSG("%s: " _E(>))
681                 << Sector::planeIdAsText(plane.indexInSector()).upperFirstChar()
682                 << plane.description();
683             return LoopContinue;
684         });
685     }
686     if (sec->subsectorCount())
687     {
688         LOG_SCR_MSG(_E(D) "Subsectors (%i):") << sec->subsectorCount();
689         dint subsectorIndex = 0;
690         sec->forAllSubsectors([&subsectorIndex] (Subsector const &subsec)
691         {
692             LOG_SCR_MSG("%i: " _E(>))
693                 << subsectorIndex
694                 << subsec.description();
695             subsectorIndex += 1;
696             return LoopContinue;
697         });
698     }
699 
700     return true;
701 }
702 
planeIdAsText(dint planeId)703 String Sector::planeIdAsText(dint planeId)
704 {
705     switch (planeId)
706     {
707     case Floor:   return "floor";
708     case Ceiling: return "ceiling";
709 
710     default:      return "plane-" + String::number(planeId);
711     }
712 }
713 
consoleRegister()714 void Sector::consoleRegister()  // static
715 {
716     C_CMD("inspectsector", "i", InspectSector);
717 }
718 
setSubsectorConstructor(SubsectorConstructor func)719 void Sector::setSubsectorConstructor(SubsectorConstructor func) // static
720 {
721     subsectorConstructor = func;
722 }
723 
724