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