1 /** @file contact.cpp  World object => BSP leaf "contact" and contact lists.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2015 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, see:
17  * http://www.gnu.org/licenses</small>
18  */
19 
20 #include "de_platform.h"
21 #include "world/contact.h"
22 
23 #include <de/memoryzone.h>
24 #include <de/Error>
25 #include "world/map.h"
26 #include "world/p_object.h"
27 #include "BspLeaf"
28 #include "ConvexSubspace"
29 
30 using namespace de;
31 
32 namespace world {
33 
type() const34 ContactType Contact::type() const
35 {
36     return _type;
37 }
38 
objectPtr() const39 void *Contact::objectPtr() const
40 {
41     return _object;
42 }
43 
objectOrigin() const44 Vector3d Contact::objectOrigin() const
45 {
46     switch(_type)
47     {
48     case ContactLumobj: return objectAs<Lumobj>().origin();
49     case ContactMobj:   return Mobj_Origin(objectAs<mobj_t>());
50 
51     default: break;
52     }
53     DENG2_ASSERT(false);
54     return Vector3d();
55 }
56 
objectRadius() const57 ddouble Contact::objectRadius() const
58 {
59     switch(_type)
60     {
61     case ContactLumobj: return objectAs<Lumobj>().radius();
62     case ContactMobj:   return Mobj_VisualRadius(objectAs<mobj_t>());
63 
64     default: break;
65     }
66     DENG2_ASSERT(false);
67     return 0;
68 }
69 
objectBounds() const70 AABoxd Contact::objectBounds() const
71 {
72     switch(_type)
73     {
74     case ContactLumobj: return objectAs<Lumobj>().bounds();
75     case ContactMobj:   return Mobj_Bounds(objectAs<mobj_t>());
76 
77     default: break;
78     }
79     DENG2_ASSERT(false);
80     return AABoxd();
81 }
82 
objectBspLeafAtOrigin() const83 BspLeaf &Contact::objectBspLeafAtOrigin() const
84 {
85     switch(_type)
86     {
87     case ContactLumobj: return objectAs<Lumobj>().bspLeafAtOrigin();
88     case ContactMobj:   return Mobj_BspLeafAtOrigin(objectAs<mobj_t>());
89 
90     default: throw Error("Contact::objectBspLeafAtOrigin", "Invalid type");
91     }
92 }
93 
94 }  // namespace world
95 
96 //- ContactList -------------------------------------------------------------------------
97 
98 namespace world {
99 
100 struct ContactList::Node
101 {
102     Node *next;      ///< Next in the BSP leaf.
103     Node *nextUsed;  ///< Next used contact.
104     void *obj;
105 };
106 static ContactList::Node *firstNode;  ///< First unused list node.
107 static ContactList::Node *cursor;     ///< Current list node.
108 
reset()109 void ContactList::reset()  // static
110 {
111     cursor = firstNode;
112 }
113 
link(Contact * contact)114 void ContactList::link(Contact *contact)
115 {
116     if(!contact) return;
117 
118     Node *node = newNode(contact->objectPtr());
119 
120     node->next = _head;
121     _head = node;
122 }
123 
begin() const124 ContactList::Node *ContactList::begin() const
125 {
126     return _head;
127 }
128 
newNode(void * object)129 ContactList::Node *ContactList::newNode(void *object) // static
130 {
131     DENG2_ASSERT(object);
132 
133     Node *node;
134     if(!cursor)
135     {
136         node = (Node *) Z_Malloc(sizeof(*node), PU_APPSTATIC, nullptr);
137 
138         // Link in the global list of used nodes.
139         node->nextUsed = firstNode;
140         firstNode = node;
141     }
142     else
143     {
144         node = cursor;
145         cursor = cursor->nextUsed;
146     }
147 
148     node->obj  = object;
149     node->next = nullptr;
150 
151     return node;
152 }
153 
154 // Separate contact lists for each BSP leaf and contact type.
155 static ContactList *subspaceContactLists;
156 
R_ContactList(world::ConvexSubspace & subspace,ContactType type)157 ContactList &R_ContactList(world::ConvexSubspace &subspace, ContactType type)
158 {
159     return subspaceContactLists[subspace.indexInMap() * ContactTypeCount + dint( type )];
160 }
161 
162 static Contact *contacts;
163 static Contact *contactFirst, *contactCursor;
164 
newContact(void * object,ContactType type)165 static Contact *newContact(void *object, ContactType type)
166 {
167     DENG2_ASSERT(object);
168 
169     Contact *contact;
170     if(!contactCursor)
171     {
172         contact = (Contact *) Z_Malloc(sizeof *contact, PU_APPSTATIC, nullptr);
173 
174         // Link in the global list of used contacts.
175         contact->nextUsed = contactFirst;
176         contactFirst = contact;
177     }
178     else
179     {
180         contact = contactCursor;
181         contactCursor = contactCursor->nextUsed;
182     }
183 
184     // Link in the list of in-use contacts.
185     contact->next = contacts;
186     contacts = contact;
187 
188     contact->_object = object;
189     contact->_type   = type;
190 
191     return contact;
192 }
193 
R_InitContactLists(Map & map)194 void R_InitContactLists(Map &map)
195 {
196     // Initialize object => BspLeaf contact lists.
197     subspaceContactLists = (ContactList *)
198         Z_Calloc(map.subspaceCount() * ContactTypeCount * sizeof(*subspaceContactLists),
199                  PU_MAPSTATIC, nullptr);
200 }
201 
R_DestroyContactLists()202 void R_DestroyContactLists()
203 {
204     Z_Free(subspaceContactLists); subspaceContactLists = nullptr;
205 }
206 
R_ClearContactLists(Map & map)207 void R_ClearContactLists(Map &map)
208 {
209     // Start reusing contacts.
210     contactCursor = contactFirst;
211     contacts = nullptr;
212 
213     // Start reusing nodes from the first one in the list.
214     ContactList::reset();
215 
216     if(subspaceContactLists)
217     {
218         std::memset(subspaceContactLists, 0,
219                     map.subspaceCount() * ContactTypeCount * sizeof(*subspaceContactLists));
220     }
221 }
222 
R_AddContact(mobj_t & mobj)223 void R_AddContact(mobj_t &mobj)
224 {
225     // BspLeafs with no geometry cannot be contacted (zero world volume).
226     if(Mobj_BspLeafAtOrigin(mobj).hasSubspace())
227     {
228         newContact(&mobj, ContactMobj);
229     }
230 }
231 
R_AddContact(Lumobj & lum)232 void R_AddContact(Lumobj &lum)
233 {
234     // BspLeafs with no geometry cannot be contacted (zero world volume).
235     if(lum.bspLeafAtOrigin().hasSubspace())
236     {
237         newContact(&lum, ContactLumobj);
238     }
239 }
240 
R_ForAllContacts(std::function<LoopResult (world::Contact const &)> func)241 LoopResult R_ForAllContacts(std::function<LoopResult (world::Contact const &)> func)
242 {
243     for(Contact *contact = contacts; contact; contact = contact->next)
244     {
245         if(auto result = func(*contact))
246             return result;
247     }
248     return LoopContinue;
249 }
250 
R_ForAllSubspaceMobContacts(world::ConvexSubspace & subspace,std::function<LoopResult (mobj_s &)> func)251 LoopResult R_ForAllSubspaceMobContacts(world::ConvexSubspace &subspace, std::function<LoopResult (mobj_s &)> func)
252 {
253     ContactList &list = R_ContactList(subspace, ContactMobj);
254     for(ContactList::Node *node = list.begin(); node; node = node->next)
255     {
256         if(auto result = func(*static_cast<mobj_t *>(node->obj)))
257             return result;
258     }
259     return LoopContinue;
260 }
261 
R_ForAllSubspaceLumContacts(ConvexSubspace & subspace,std::function<LoopResult (Lumobj &)> func)262 LoopResult R_ForAllSubspaceLumContacts(ConvexSubspace &subspace, std::function<LoopResult (Lumobj &)> func)
263 {
264     ContactList &list = R_ContactList(subspace, ContactLumobj);
265     for(ContactList::Node *node = list.begin(); node; node = node->next)
266     {
267         if(auto result = func(*static_cast<Lumobj *>(node->obj)))
268             return result;
269     }
270     return LoopContinue;
271 }
272 
273 }  // namespace world
274