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