]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/entitylib.h
Merge branch 'osxnetradiant'
[xonotic/netradiant.git] / libs / entitylib.h
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #if !defined (INCLUDED_ENTITYLIB_H)
23 #define INCLUDED_ENTITYLIB_H
24
25 #include "ireference.h"
26 #include "debugging/debugging.h"
27
28 #include "ientity.h"
29 #include "irender.h"
30 #include "igl.h"
31 #include "selectable.h"
32
33 #include "generic/callback.h"
34 #include "math/vector.h"
35 #include "math/aabb.h"
36 #include "undolib.h"
37 #include "string/pooledstring.h"
38 #include "generic/referencecounted.h"
39 #include "scenelib.h"
40 #include "container/container.h"
41 #include "eclasslib.h"
42
43 #include <list>
44 #include <set>
45
46 inline void arrow_draw(const Vector3& origin, const Vector3& direction_forward, const Vector3& direction_left, const Vector3& direction_up)
47 {
48         Vector3 endpoint(vector3_added(origin, vector3_scaled(direction_forward, 32.0)));
49
50   Vector3 tip1(vector3_added(vector3_added(endpoint, vector3_scaled(direction_forward, -8.0)), vector3_scaled(direction_up, -4.0)));
51         Vector3 tip2(vector3_added(tip1, vector3_scaled(direction_up, 8.0)));
52   Vector3 tip3(vector3_added(vector3_added(endpoint, vector3_scaled(direction_forward, -8.0)), vector3_scaled(direction_left, -4.0)));
53         Vector3 tip4(vector3_added(tip3, vector3_scaled(direction_left, 8.0)));
54
55   glBegin (GL_LINES);
56
57   glVertex3fv(vector3_to_array(origin));
58   glVertex3fv(vector3_to_array(endpoint));
59
60   glVertex3fv(vector3_to_array(endpoint));
61   glVertex3fv(vector3_to_array(tip1));
62
63   glVertex3fv(vector3_to_array(endpoint));
64   glVertex3fv(vector3_to_array(tip2));
65
66   glVertex3fv(vector3_to_array(endpoint));
67   glVertex3fv(vector3_to_array(tip3));
68
69   glVertex3fv(vector3_to_array(endpoint));
70   glVertex3fv(vector3_to_array(tip4));
71
72   glVertex3fv(vector3_to_array(tip1));
73   glVertex3fv(vector3_to_array(tip3));
74
75   glVertex3fv(vector3_to_array(tip3));
76   glVertex3fv(vector3_to_array(tip2));
77
78   glVertex3fv(vector3_to_array(tip2));
79   glVertex3fv(vector3_to_array(tip4));
80
81   glVertex3fv(vector3_to_array(tip4));
82   glVertex3fv(vector3_to_array(tip1));
83
84   glEnd();
85 }
86
87 class SelectionIntersection;
88
89 inline void aabb_testselect(const AABB& aabb, SelectionTest& test, SelectionIntersection& best)
90 {
91   const IndexPointer::index_type indices[24] = {
92     2, 1, 5, 6,
93     1, 0, 4, 5,
94     0, 1, 2, 3,
95     3, 7, 4, 0,
96     3, 2, 6, 7,
97     7, 6, 5, 4,
98   };
99
100   Vector3 points[8];
101   aabb_corners(aabb, points);
102   test.TestQuads(VertexPointer(reinterpret_cast<VertexPointer::pointer>(points), sizeof(Vector3)), IndexPointer(indices, 24), best);
103 }
104
105 inline void aabb_draw_wire(const Vector3 points[8])
106 {
107   typedef std::size_t index_t;
108   index_t indices[24] = {
109     0, 1, 1, 2, 2, 3, 3, 0,
110     4, 5, 5, 6, 6, 7, 7, 4,
111     0, 4, 1, 5, 2, 6, 3, 7,
112   };
113 #if 1
114   glVertexPointer(3, GL_FLOAT, 0, points);
115   glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices);
116 #else
117   glBegin(GL_LINES);
118   for(std::size_t i = 0; i < sizeof(indices)/sizeof(index_t); ++i)
119   {
120     glVertex3fv(points[indices[i]]);
121   }
122   glEnd();
123 #endif
124 }
125
126 inline void aabb_draw_flatshade(const Vector3 points[8])
127 {
128   glBegin(GL_QUADS);
129
130   glNormal3fv(vector3_to_array(aabb_normals[0]));
131   glVertex3fv(vector3_to_array(points[2]));
132   glVertex3fv(vector3_to_array(points[1]));
133   glVertex3fv(vector3_to_array(points[5]));
134   glVertex3fv(vector3_to_array(points[6]));
135
136   glNormal3fv(vector3_to_array(aabb_normals[1]));
137   glVertex3fv(vector3_to_array(points[1]));
138   glVertex3fv(vector3_to_array(points[0]));
139   glVertex3fv(vector3_to_array(points[4]));
140   glVertex3fv(vector3_to_array(points[5]));
141
142   glNormal3fv(vector3_to_array(aabb_normals[2]));
143   glVertex3fv(vector3_to_array(points[0]));
144   glVertex3fv(vector3_to_array(points[1]));
145   glVertex3fv(vector3_to_array(points[2]));
146   glVertex3fv(vector3_to_array(points[3]));
147
148   glNormal3fv(vector3_to_array(aabb_normals[3]));
149   glVertex3fv(vector3_to_array(points[0]));
150   glVertex3fv(vector3_to_array(points[3]));
151   glVertex3fv(vector3_to_array(points[7]));
152   glVertex3fv(vector3_to_array(points[4]));
153
154   glNormal3fv(vector3_to_array(aabb_normals[4]));
155   glVertex3fv(vector3_to_array(points[3]));
156   glVertex3fv(vector3_to_array(points[2]));
157   glVertex3fv(vector3_to_array(points[6]));
158   glVertex3fv(vector3_to_array(points[7]));
159
160   glNormal3fv(vector3_to_array(aabb_normals[5]));
161   glVertex3fv(vector3_to_array(points[7]));
162   glVertex3fv(vector3_to_array(points[6]));
163   glVertex3fv(vector3_to_array(points[5]));
164   glVertex3fv(vector3_to_array(points[4]));
165
166   glEnd();
167 }
168
169 inline void aabb_draw_wire(const AABB& aabb)
170 {
171   Vector3 points[8];
172         aabb_corners(aabb, points);
173   aabb_draw_wire(points);
174 }
175
176 inline void aabb_draw_flatshade(const AABB& aabb)
177 {
178   Vector3 points[8];
179         aabb_corners(aabb, points);
180   aabb_draw_flatshade(points);
181 }
182
183 inline void aabb_draw_textured(const AABB& aabb)
184 {
185   Vector3 points[8];
186         aabb_corners(aabb, points);
187
188   glBegin(GL_QUADS);
189
190   glNormal3fv(vector3_to_array(aabb_normals[0]));
191   glTexCoord2fv(aabb_texcoord_topleft);
192   glVertex3fv(vector3_to_array(points[2]));
193   glTexCoord2fv(aabb_texcoord_topright);
194   glVertex3fv(vector3_to_array(points[1]));
195   glTexCoord2fv(aabb_texcoord_botright);
196   glVertex3fv(vector3_to_array(points[5]));
197   glTexCoord2fv(aabb_texcoord_botleft);
198   glVertex3fv(vector3_to_array(points[6]));
199
200   glNormal3fv(vector3_to_array(aabb_normals[1]));
201   glTexCoord2fv(aabb_texcoord_topleft);
202   glVertex3fv(vector3_to_array(points[1]));
203   glTexCoord2fv(aabb_texcoord_topright);
204   glVertex3fv(vector3_to_array(points[0]));
205   glTexCoord2fv(aabb_texcoord_botright);
206   glVertex3fv(vector3_to_array(points[4]));
207   glTexCoord2fv(aabb_texcoord_botleft);
208   glVertex3fv(vector3_to_array(points[5]));
209
210   glNormal3fv(vector3_to_array(aabb_normals[2]));
211   glTexCoord2fv(aabb_texcoord_topleft);
212   glVertex3fv(vector3_to_array(points[0]));
213   glTexCoord2fv(aabb_texcoord_topright);
214   glVertex3fv(vector3_to_array(points[1]));
215   glTexCoord2fv(aabb_texcoord_botright);
216   glVertex3fv(vector3_to_array(points[2]));
217   glTexCoord2fv(aabb_texcoord_botleft);
218   glVertex3fv(vector3_to_array(points[3]));
219
220   glNormal3fv(vector3_to_array(aabb_normals[3]));
221   glTexCoord2fv(aabb_texcoord_topleft);
222   glVertex3fv(vector3_to_array(points[0]));
223   glTexCoord2fv(aabb_texcoord_topright);
224   glVertex3fv(vector3_to_array(points[3]));
225   glTexCoord2fv(aabb_texcoord_botright);
226   glVertex3fv(vector3_to_array(points[7]));
227   glTexCoord2fv(aabb_texcoord_botleft);
228   glVertex3fv(vector3_to_array(points[4]));
229
230   glNormal3fv(vector3_to_array(aabb_normals[4]));
231   glTexCoord2fv(aabb_texcoord_topleft);
232   glVertex3fv(vector3_to_array(points[3]));
233   glTexCoord2fv(aabb_texcoord_topright);
234   glVertex3fv(vector3_to_array(points[2]));
235   glTexCoord2fv(aabb_texcoord_botright);
236   glVertex3fv(vector3_to_array(points[6]));
237   glTexCoord2fv(aabb_texcoord_botleft);
238   glVertex3fv(vector3_to_array(points[7]));
239
240   glNormal3fv(vector3_to_array(aabb_normals[5]));
241   glTexCoord2fv(aabb_texcoord_topleft);
242   glVertex3fv(vector3_to_array(points[7]));
243   glTexCoord2fv(aabb_texcoord_topright);
244   glVertex3fv(vector3_to_array(points[6]));
245   glTexCoord2fv(aabb_texcoord_botright);
246   glVertex3fv(vector3_to_array(points[5]));
247   glTexCoord2fv(aabb_texcoord_botleft);
248   glVertex3fv(vector3_to_array(points[4]));
249
250   glEnd();
251 }
252
253 inline void aabb_draw_solid(const AABB& aabb, RenderStateFlags state)
254 {
255   if(state & RENDER_TEXTURE)
256   {
257     aabb_draw_textured(aabb);
258   }
259   else
260   {
261     aabb_draw_flatshade(aabb);
262   }
263 }
264
265 inline void aabb_draw(const AABB& aabb, RenderStateFlags state)
266 {
267   if(state & RENDER_FILL)
268   {
269     aabb_draw_solid(aabb, state);
270   }
271   else
272   {
273     aabb_draw_wire(aabb);
274   }
275 }
276
277 class RenderableSolidAABB : public OpenGLRenderable
278 {
279   const AABB& m_aabb;
280 public:
281   RenderableSolidAABB(const AABB& aabb) : m_aabb(aabb)
282   {
283   }
284   void render(RenderStateFlags state) const
285   {
286     aabb_draw_solid(m_aabb, state);
287   }
288 };
289
290 class RenderableWireframeAABB : public OpenGLRenderable
291 {
292   const AABB& m_aabb;
293 public:
294   RenderableWireframeAABB(const AABB& aabb) : m_aabb(aabb)
295   {
296   }
297   void render(RenderStateFlags state) const
298   {
299     aabb_draw_wire(m_aabb);
300   }
301 };
302
303
304 /// \brief A key/value pair of strings.
305 ///
306 /// - Notifies observers when value changes - value changes to "" on destruction.
307 /// - Provides undo support through the global undo system.
308 class KeyValue : public EntityKeyValue
309 {
310   typedef UnsortedSet<KeyObserver> KeyObservers;
311
312   std::size_t m_refcount;
313   KeyObservers m_observers;
314   CopiedString m_string;
315   const char* m_empty;
316   ObservedUndoableObject<CopiedString> m_undo;
317   static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged;
318 public:
319
320   KeyValue(const char* string, const char* empty)
321     : m_refcount(0), m_string(string), m_empty(empty), m_undo(m_string, UndoImportCaller(*this))
322   {
323     notify();
324   }
325   ~KeyValue()
326   {
327     ASSERT_MESSAGE(m_observers.empty(), "KeyValue::~KeyValue: observers still attached");
328   }
329
330   static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func)
331   {
332     m_entityKeyValueChanged = func;
333   }
334
335   void IncRef()
336   {
337     ++m_refcount;
338   }
339   void DecRef()
340   {
341     if(--m_refcount == 0)
342     {
343       delete this;
344     }
345   }
346
347   void instanceAttach(MapFile* map)
348   {
349     m_undo.instanceAttach(map);
350   }
351   void instanceDetach(MapFile* map)
352   {
353     m_undo.instanceDetach(map);
354   }
355
356   void attach(const KeyObserver& observer)
357   {
358     (*m_observers.insert(observer))(c_str());
359   }
360   void detach(const KeyObserver& observer)
361   {
362     observer(m_empty);
363     m_observers.erase(observer);
364   }
365   const char* c_str() const
366   {
367     if(string_empty(m_string.c_str()))
368     {
369       return m_empty;
370     }
371     return m_string.c_str();
372   }
373   void assign(const char* other)
374   {
375     if(!string_equal(m_string.c_str(), other))
376     {
377       m_undo.save();
378       m_string = other;
379       notify();
380     }
381   }
382
383   void notify()
384   {
385     m_entityKeyValueChanged();
386     KeyObservers::reverse_iterator i = m_observers.rbegin();
387     while(i != m_observers.rend())
388     {
389       (*i++)(c_str());
390     }
391   }
392
393   void importState(const CopiedString& string)
394   {
395     m_string = string;
396
397     notify();
398   }
399   typedef MemberCaller1<KeyValue, const CopiedString&, &KeyValue::importState> UndoImportCaller;
400 };
401
402 /// \brief An unsorted list of key/value pairs.
403 ///
404 /// - Notifies observers when a pair is inserted or removed.
405 /// - Provides undo support through the global undo system.
406 /// - New keys are appended to the end of the list.
407 class EntityKeyValues : public Entity
408 {
409 public:
410   typedef KeyValue Value;
411
412   static StringPool& getPool()
413   {
414     return Static<StringPool, KeyContext>::instance();
415   }
416 private:
417   static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged;
418   static Counter* m_counter;
419
420   EntityClass* m_eclass;
421
422   class KeyContext{};
423   typedef Static<StringPool, KeyContext> KeyPool;
424   typedef PooledString<KeyPool> Key;
425   typedef SmartPointer<KeyValue> KeyValuePtr;
426   typedef UnsortedMap<Key, KeyValuePtr> KeyValues;
427   KeyValues m_keyValues;
428
429   typedef UnsortedSet<Observer*> Observers;
430   Observers m_observers;
431
432   ObservedUndoableObject<KeyValues> m_undo;
433   bool m_instanced;
434
435   bool m_observerMutex;
436
437   void notifyInsert(const char* key, Value& value)
438   {
439     m_observerMutex = true;
440     for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
441     {
442       (*i)->insert(key, value);
443     }
444     m_observerMutex = false;
445   }
446   void notifyErase(const char* key, Value& value)
447   {
448     m_observerMutex = true;
449     for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
450     {
451       (*i)->erase(key, value);
452     }
453     m_observerMutex = false;
454   }
455   void forEachKeyValue_notifyInsert()
456   {
457     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
458     {
459       notifyInsert((*i).first.c_str(), *(*i).second);
460     }
461   }
462   void forEachKeyValue_notifyErase()
463   {
464     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
465     {
466       notifyErase((*i).first.c_str(), *(*i).second);
467     }
468   }
469
470   void insert(const char* key, const KeyValuePtr& keyValue)
471   {
472     KeyValues::iterator i = m_keyValues.insert(KeyValues::value_type(key, keyValue));
473     notifyInsert(key, *(*i).second);
474
475     if(m_instanced)
476     {
477       (*i).second->instanceAttach(m_undo.map());
478     }
479   }
480
481   void insert(const char* key, const char* value)
482   {
483     KeyValues::iterator i = m_keyValues.find(key);
484     if(i != m_keyValues.end())
485     {
486       (*i).second->assign(value);
487     }
488     else
489     {
490       m_undo.save();
491       insert(key, KeyValuePtr(new KeyValue(value, EntityClass_valueForKey(*m_eclass, key))));
492     }
493   }
494
495   void erase(KeyValues::iterator i)
496   {
497     if(m_instanced)
498     {
499       (*i).second->instanceDetach(m_undo.map());
500     }
501
502     Key key((*i).first);
503     KeyValuePtr value((*i).second);
504     m_keyValues.erase(i);
505     notifyErase(key.c_str(), *value);
506   }
507
508   void erase(const char* key)
509   {
510     KeyValues::iterator i = m_keyValues.find(key);
511     if(i != m_keyValues.end())
512     {
513       m_undo.save();
514       erase(i);
515     }
516   }
517
518 public:
519   bool m_isContainer;
520
521   EntityKeyValues(EntityClass* eclass) :
522     m_eclass(eclass),
523     m_undo(m_keyValues, UndoImportCaller(*this)),
524     m_instanced(false),
525     m_observerMutex(false),
526     m_isContainer(!eclass->fixedsize)
527   {
528   }
529   EntityKeyValues(const EntityKeyValues& other) :
530     Entity(other),
531     m_eclass(&other.getEntityClass()),
532     m_undo(m_keyValues, UndoImportCaller(*this)),
533     m_instanced(false),
534     m_observerMutex(false),
535     m_isContainer(other.m_isContainer)
536   {
537     for(KeyValues::const_iterator i = other.m_keyValues.begin(); i != other.m_keyValues.end(); ++i)
538     {
539       insert((*i).first.c_str(), (*i).second->c_str());
540     }
541   }
542   ~EntityKeyValues()
543   {
544     for(Observers::iterator i = m_observers.begin(); i != m_observers.end();)
545     {
546       // post-increment to allow current element to be removed safely
547       (*i++)->clear();
548     }
549     ASSERT_MESSAGE(m_observers.empty(), "EntityKeyValues::~EntityKeyValues: observers still attached");
550   }
551
552   static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func)
553   {
554     m_entityKeyValueChanged = func;
555     KeyValue::setKeyValueChangedFunc(func);
556   }
557   static void setCounter(Counter* counter)
558   {
559     m_counter = counter;
560   }
561
562   void importState(const KeyValues& keyValues)
563   {
564     for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end();)
565     {
566       erase(i++);
567     }
568
569     for(KeyValues::const_iterator i = keyValues.begin(); i != keyValues.end(); ++i)
570     {
571       insert((*i).first.c_str(), (*i).second);
572     }
573
574     m_entityKeyValueChanged();
575   }
576   typedef MemberCaller1<EntityKeyValues, const KeyValues&, &EntityKeyValues::importState> UndoImportCaller;
577
578   void attach(Observer& observer)
579   {
580     ASSERT_MESSAGE(!m_observerMutex, "observer cannot be attached during iteration");
581     m_observers.insert(&observer);
582     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
583     {
584       observer.insert((*i).first.c_str(), *(*i).second);
585     }
586   }
587   void detach(Observer& observer)
588   {
589     ASSERT_MESSAGE(!m_observerMutex, "observer cannot be detached during iteration");
590     m_observers.erase(&observer);
591     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
592     {
593       observer.erase((*i).first.c_str(), *(*i).second);
594     }
595   }
596
597   void forEachKeyValue_instanceAttach(MapFile* map)
598   {
599     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
600     {
601       (*i).second->instanceAttach(map);
602     }
603   }
604   void forEachKeyValue_instanceDetach(MapFile* map)
605   {
606     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
607     {
608       (*i).second->instanceDetach(map);
609     }
610   }
611
612   void instanceAttach(MapFile* map)
613   {
614     if(m_counter != 0)
615     {
616       m_counter->increment();
617     }
618
619     m_instanced = true;
620     forEachKeyValue_instanceAttach(map);
621     m_undo.instanceAttach(map);
622   }
623   void instanceDetach(MapFile* map)
624   {
625     if(m_counter != 0)
626     {
627       m_counter->decrement();
628     }
629
630     m_undo.instanceDetach(map);
631     forEachKeyValue_instanceDetach(map);
632     m_instanced = false;
633   }
634
635   // entity
636   EntityClass& getEntityClass() const
637   {
638     return *m_eclass;
639   }
640   void forEachKeyValue(Visitor& visitor) const
641   {
642     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
643     {
644       visitor.visit((*i).first.c_str(), (*i).second->c_str());
645     }
646   }
647   void setKeyValue(const char* key, const char* value)
648   {
649     if(value[0] == '\0'
650       /*|| string_equal(EntityClass_valueForKey(*m_eclass, key), value)*/) // don't delete values equal to default
651     {
652       erase(key);
653     }
654     else
655     {
656       insert(key, value);
657     }
658     m_entityKeyValueChanged();
659   }
660   const char* getKeyValue(const char* key) const
661   {
662     KeyValues::const_iterator i = m_keyValues.find(key);
663     if(i != m_keyValues.end())
664     {
665       return (*i).second->c_str();
666     }
667
668     return EntityClass_valueForKey(*m_eclass, key);
669   }
670
671   bool isContainer() const
672   {
673     return m_isContainer;
674   }
675 };
676
677 /// \brief A Resource reference with a controlled lifetime.
678 /// \brief The resource is released when the ResourceReference is destroyed.
679 class ResourceReference
680 {
681   CopiedString m_name;
682   Resource* m_resource;
683 public:
684   ResourceReference(const char* name)
685     : m_name(name)
686   {
687     capture();
688   }
689   ResourceReference(const ResourceReference& other)
690     : m_name(other.m_name)
691   {
692     capture();
693   }
694   ResourceReference& operator=(const ResourceReference& other)
695   {
696     ResourceReference tmp(other);
697     tmp.swap(*this);
698     return *this;
699   }
700   ~ResourceReference()
701   {
702     release();
703   }
704
705   void capture()
706   {
707     m_resource = GlobalReferenceCache().capture(m_name.c_str());
708   }
709   void release()
710   {
711     GlobalReferenceCache().release(m_name.c_str());
712   }
713
714   const char* getName() const
715   {
716     return m_name.c_str();
717   }
718   void setName(const char* name)
719   {
720     ResourceReference tmp(name);
721     tmp.swap(*this);
722   }
723
724   void swap(ResourceReference& other)
725   {
726     std::swap(m_resource, other.m_resource);
727     std::swap(m_name, other.m_name);
728   }
729
730   void attach(ModuleObserver& observer)
731   {
732     m_resource->attach(observer);
733   }
734   void detach(ModuleObserver& observer)
735   {
736     m_resource->detach(observer);
737   }
738
739   Resource* get()
740   {
741     return m_resource;
742   }
743 };
744
745 namespace std
746 {
747   /// \brief Swaps the values of \p self and \p other.
748   /// Overloads std::swap.
749   inline void swap(ResourceReference& self, ResourceReference& other)
750   {
751     self.swap(other);
752   }
753 }
754
755 #endif