]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/entitylib.h
Fix wireframe entity display in 2D window
[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   unsigned int indices[24] = {
108     0, 1, 1, 2, 2, 3, 3, 0,
109     4, 5, 5, 6, 6, 7, 7, 4,
110     0, 4, 1, 5, 2, 6, 3, 7,
111   };
112 #if 1
113   glVertexPointer(3, GL_FLOAT, 0, points);
114   glDrawElements(GL_LINES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices);
115 #else
116   glBegin(GL_LINES);
117   for(std::size_t i = 0; i < sizeof(indices)/sizeof(indices[0]); ++i)
118   {
119     glVertex3fv(points[indices[i]]);
120   }
121   glEnd();
122 #endif
123 }
124
125 inline void aabb_draw_flatshade(const Vector3 points[8])
126 {
127   glBegin(GL_QUADS);
128
129   glNormal3fv(vector3_to_array(aabb_normals[0]));
130   glVertex3fv(vector3_to_array(points[2]));
131   glVertex3fv(vector3_to_array(points[1]));
132   glVertex3fv(vector3_to_array(points[5]));
133   glVertex3fv(vector3_to_array(points[6]));
134
135   glNormal3fv(vector3_to_array(aabb_normals[1]));
136   glVertex3fv(vector3_to_array(points[1]));
137   glVertex3fv(vector3_to_array(points[0]));
138   glVertex3fv(vector3_to_array(points[4]));
139   glVertex3fv(vector3_to_array(points[5]));
140
141   glNormal3fv(vector3_to_array(aabb_normals[2]));
142   glVertex3fv(vector3_to_array(points[0]));
143   glVertex3fv(vector3_to_array(points[1]));
144   glVertex3fv(vector3_to_array(points[2]));
145   glVertex3fv(vector3_to_array(points[3]));
146
147   glNormal3fv(vector3_to_array(aabb_normals[3]));
148   glVertex3fv(vector3_to_array(points[0]));
149   glVertex3fv(vector3_to_array(points[3]));
150   glVertex3fv(vector3_to_array(points[7]));
151   glVertex3fv(vector3_to_array(points[4]));
152
153   glNormal3fv(vector3_to_array(aabb_normals[4]));
154   glVertex3fv(vector3_to_array(points[3]));
155   glVertex3fv(vector3_to_array(points[2]));
156   glVertex3fv(vector3_to_array(points[6]));
157   glVertex3fv(vector3_to_array(points[7]));
158
159   glNormal3fv(vector3_to_array(aabb_normals[5]));
160   glVertex3fv(vector3_to_array(points[7]));
161   glVertex3fv(vector3_to_array(points[6]));
162   glVertex3fv(vector3_to_array(points[5]));
163   glVertex3fv(vector3_to_array(points[4]));
164
165   glEnd();
166 }
167
168 inline void aabb_draw_wire(const AABB& aabb)
169 {
170   Vector3 points[8];
171         aabb_corners(aabb, points);
172   aabb_draw_wire(points);
173 }
174
175 inline void aabb_draw_flatshade(const AABB& aabb)
176 {
177   Vector3 points[8];
178         aabb_corners(aabb, points);
179   aabb_draw_flatshade(points);
180 }
181
182 inline void aabb_draw_textured(const AABB& aabb)
183 {
184   Vector3 points[8];
185         aabb_corners(aabb, points);
186
187   glBegin(GL_QUADS);
188
189   glNormal3fv(vector3_to_array(aabb_normals[0]));
190   glTexCoord2fv(aabb_texcoord_topleft);
191   glVertex3fv(vector3_to_array(points[2]));
192   glTexCoord2fv(aabb_texcoord_topright);
193   glVertex3fv(vector3_to_array(points[1]));
194   glTexCoord2fv(aabb_texcoord_botright);
195   glVertex3fv(vector3_to_array(points[5]));
196   glTexCoord2fv(aabb_texcoord_botleft);
197   glVertex3fv(vector3_to_array(points[6]));
198
199   glNormal3fv(vector3_to_array(aabb_normals[1]));
200   glTexCoord2fv(aabb_texcoord_topleft);
201   glVertex3fv(vector3_to_array(points[1]));
202   glTexCoord2fv(aabb_texcoord_topright);
203   glVertex3fv(vector3_to_array(points[0]));
204   glTexCoord2fv(aabb_texcoord_botright);
205   glVertex3fv(vector3_to_array(points[4]));
206   glTexCoord2fv(aabb_texcoord_botleft);
207   glVertex3fv(vector3_to_array(points[5]));
208
209   glNormal3fv(vector3_to_array(aabb_normals[2]));
210   glTexCoord2fv(aabb_texcoord_topleft);
211   glVertex3fv(vector3_to_array(points[0]));
212   glTexCoord2fv(aabb_texcoord_topright);
213   glVertex3fv(vector3_to_array(points[1]));
214   glTexCoord2fv(aabb_texcoord_botright);
215   glVertex3fv(vector3_to_array(points[2]));
216   glTexCoord2fv(aabb_texcoord_botleft);
217   glVertex3fv(vector3_to_array(points[3]));
218
219   glNormal3fv(vector3_to_array(aabb_normals[3]));
220   glTexCoord2fv(aabb_texcoord_topleft);
221   glVertex3fv(vector3_to_array(points[0]));
222   glTexCoord2fv(aabb_texcoord_topright);
223   glVertex3fv(vector3_to_array(points[3]));
224   glTexCoord2fv(aabb_texcoord_botright);
225   glVertex3fv(vector3_to_array(points[7]));
226   glTexCoord2fv(aabb_texcoord_botleft);
227   glVertex3fv(vector3_to_array(points[4]));
228
229   glNormal3fv(vector3_to_array(aabb_normals[4]));
230   glTexCoord2fv(aabb_texcoord_topleft);
231   glVertex3fv(vector3_to_array(points[3]));
232   glTexCoord2fv(aabb_texcoord_topright);
233   glVertex3fv(vector3_to_array(points[2]));
234   glTexCoord2fv(aabb_texcoord_botright);
235   glVertex3fv(vector3_to_array(points[6]));
236   glTexCoord2fv(aabb_texcoord_botleft);
237   glVertex3fv(vector3_to_array(points[7]));
238
239   glNormal3fv(vector3_to_array(aabb_normals[5]));
240   glTexCoord2fv(aabb_texcoord_topleft);
241   glVertex3fv(vector3_to_array(points[7]));
242   glTexCoord2fv(aabb_texcoord_topright);
243   glVertex3fv(vector3_to_array(points[6]));
244   glTexCoord2fv(aabb_texcoord_botright);
245   glVertex3fv(vector3_to_array(points[5]));
246   glTexCoord2fv(aabb_texcoord_botleft);
247   glVertex3fv(vector3_to_array(points[4]));
248
249   glEnd();
250 }
251
252 inline void aabb_draw_solid(const AABB& aabb, RenderStateFlags state)
253 {
254   if(state & RENDER_TEXTURE)
255   {
256     aabb_draw_textured(aabb);
257   }
258   else
259   {
260     aabb_draw_flatshade(aabb);
261   }
262 }
263
264 inline void aabb_draw(const AABB& aabb, RenderStateFlags state)
265 {
266   if(state & RENDER_FILL)
267   {
268     aabb_draw_solid(aabb, state);
269   }
270   else
271   {
272     aabb_draw_wire(aabb);
273   }
274 }
275
276 class RenderableSolidAABB : public OpenGLRenderable
277 {
278   const AABB& m_aabb;
279 public:
280   RenderableSolidAABB(const AABB& aabb) : m_aabb(aabb)
281   {
282   }
283   void render(RenderStateFlags state) const
284   {
285     aabb_draw_solid(m_aabb, state);
286   }
287 };
288
289 class RenderableWireframeAABB : public OpenGLRenderable
290 {
291   const AABB& m_aabb;
292 public:
293   RenderableWireframeAABB(const AABB& aabb) : m_aabb(aabb)
294   {
295   }
296   void render(RenderStateFlags state) const
297   {
298     aabb_draw_wire(m_aabb);
299   }
300 };
301
302
303 /// \brief A key/value pair of strings.
304 ///
305 /// - Notifies observers when value changes - value changes to "" on destruction.
306 /// - Provides undo support through the global undo system.
307 class KeyValue : public EntityKeyValue
308 {
309   typedef UnsortedSet<KeyObserver> KeyObservers;
310
311   std::size_t m_refcount;
312   KeyObservers m_observers;
313   CopiedString m_string;
314   const char* m_empty;
315   ObservedUndoableObject<CopiedString> m_undo;
316   static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged;
317 public:
318
319   KeyValue(const char* string, const char* empty)
320     : m_refcount(0), m_string(string), m_empty(empty), m_undo(m_string, UndoImportCaller(*this))
321   {
322     notify();
323   }
324   ~KeyValue()
325   {
326     ASSERT_MESSAGE(m_observers.empty(), "KeyValue::~KeyValue: observers still attached");
327   }
328
329   static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func)
330   {
331     m_entityKeyValueChanged = func;
332   }
333
334   void IncRef()
335   {
336     ++m_refcount;
337   }
338   void DecRef()
339   {
340     if(--m_refcount == 0)
341     {
342       delete this;
343     }
344   }
345
346   void instanceAttach(MapFile* map)
347   {
348     m_undo.instanceAttach(map);
349   }
350   void instanceDetach(MapFile* map)
351   {
352     m_undo.instanceDetach(map);
353   }
354
355   void attach(const KeyObserver& observer)
356   {
357     (*m_observers.insert(observer))(c_str());
358   }
359   void detach(const KeyObserver& observer)
360   {
361     observer(m_empty);
362     m_observers.erase(observer);
363   }
364   const char* c_str() const
365   {
366     if(string_empty(m_string.c_str()))
367     {
368       return m_empty;
369     }
370     return m_string.c_str();
371   }
372   void assign(const char* other)
373   {
374     if(!string_equal(m_string.c_str(), other))
375     {
376       m_undo.save();
377       m_string = other;
378       notify();
379     }
380   }
381
382   void notify()
383   {
384     m_entityKeyValueChanged();
385     KeyObservers::reverse_iterator i = m_observers.rbegin();
386     while(i != m_observers.rend())
387     {
388       (*i++)(c_str());
389     }
390   }
391
392   void importState(const CopiedString& string)
393   {
394     m_string = string;
395
396     notify();
397   }
398   typedef MemberCaller1<KeyValue, const CopiedString&, &KeyValue::importState> UndoImportCaller;
399 };
400
401 /// \brief An unsorted list of key/value pairs.
402 ///
403 /// - Notifies observers when a pair is inserted or removed.
404 /// - Provides undo support through the global undo system.
405 /// - New keys are appended to the end of the list.
406 class EntityKeyValues : public Entity
407 {
408 public:
409   typedef KeyValue Value;
410
411   static StringPool& getPool()
412   {
413     return Static<StringPool, KeyContext>::instance();
414   }
415 private:
416   static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged;
417   static Counter* m_counter;
418
419   EntityClass* m_eclass;
420
421   class KeyContext{};
422   typedef Static<StringPool, KeyContext> KeyPool;
423   typedef PooledString<KeyPool> Key;
424   typedef SmartPointer<KeyValue> KeyValuePtr;
425   typedef UnsortedMap<Key, KeyValuePtr> KeyValues;
426   KeyValues m_keyValues;
427
428   typedef UnsortedSet<Observer*> Observers;
429   Observers m_observers;
430
431   ObservedUndoableObject<KeyValues> m_undo;
432   bool m_instanced;
433
434   bool m_observerMutex;
435
436   void notifyInsert(const char* key, Value& value)
437   {
438     m_observerMutex = true;
439     for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
440     {
441       (*i)->insert(key, value);
442     }
443     m_observerMutex = false;
444   }
445   void notifyErase(const char* key, Value& value)
446   {
447     m_observerMutex = true;
448     for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
449     {
450       (*i)->erase(key, value);
451     }
452     m_observerMutex = false;
453   }
454   void forEachKeyValue_notifyInsert()
455   {
456     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
457     {
458       notifyInsert((*i).first.c_str(), *(*i).second);
459     }
460   }
461   void forEachKeyValue_notifyErase()
462   {
463     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
464     {
465       notifyErase((*i).first.c_str(), *(*i).second);
466     }
467   }
468
469   void insert(const char* key, const KeyValuePtr& keyValue)
470   {
471     KeyValues::iterator i = m_keyValues.insert(KeyValues::value_type(key, keyValue));
472     notifyInsert(key, *(*i).second);
473
474     if(m_instanced)
475     {
476       (*i).second->instanceAttach(m_undo.map());
477     }
478   }
479
480   void insert(const char* key, const char* value)
481   {
482     KeyValues::iterator i = m_keyValues.find(key);
483     if(i != m_keyValues.end())
484     {
485       (*i).second->assign(value);
486     }
487     else
488     {
489       m_undo.save();
490       insert(key, KeyValuePtr(new KeyValue(value, EntityClass_valueForKey(*m_eclass, key))));
491     }
492   }
493
494   void erase(KeyValues::iterator i)
495   {
496     if(m_instanced)
497     {
498       (*i).second->instanceDetach(m_undo.map());
499     }
500
501     Key key((*i).first);
502     KeyValuePtr value((*i).second);
503     m_keyValues.erase(i);
504     notifyErase(key.c_str(), *value);
505   }
506
507   void erase(const char* key)
508   {
509     KeyValues::iterator i = m_keyValues.find(key);
510     if(i != m_keyValues.end())
511     {
512       m_undo.save();
513       erase(i);
514     }
515   }
516
517 public:
518   bool m_isContainer;
519
520   EntityKeyValues(EntityClass* eclass) :
521     m_eclass(eclass),
522     m_undo(m_keyValues, UndoImportCaller(*this)),
523     m_instanced(false),
524     m_observerMutex(false),
525     m_isContainer(!eclass->fixedsize)
526   {
527   }
528   EntityKeyValues(const EntityKeyValues& other) :
529     Entity(other),
530     m_eclass(&other.getEntityClass()),
531     m_undo(m_keyValues, UndoImportCaller(*this)),
532     m_instanced(false),
533     m_observerMutex(false),
534     m_isContainer(other.m_isContainer)
535   {
536     for(KeyValues::const_iterator i = other.m_keyValues.begin(); i != other.m_keyValues.end(); ++i)
537     {
538       insert((*i).first.c_str(), (*i).second->c_str());
539     }
540   }
541   ~EntityKeyValues()
542   {
543     for(Observers::iterator i = m_observers.begin(); i != m_observers.end();)
544     {
545       // post-increment to allow current element to be removed safely
546       (*i++)->clear();
547     }
548     ASSERT_MESSAGE(m_observers.empty(), "EntityKeyValues::~EntityKeyValues: observers still attached");
549   }
550
551   static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func)
552   {
553     m_entityKeyValueChanged = func;
554     KeyValue::setKeyValueChangedFunc(func);
555   }
556   static void setCounter(Counter* counter)
557   {
558     m_counter = counter;
559   }
560
561   void importState(const KeyValues& keyValues)
562   {
563     for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end();)
564     {
565       erase(i++);
566     }
567
568     for(KeyValues::const_iterator i = keyValues.begin(); i != keyValues.end(); ++i)
569     {
570       insert((*i).first.c_str(), (*i).second);
571     }
572
573     m_entityKeyValueChanged();
574   }
575   typedef MemberCaller1<EntityKeyValues, const KeyValues&, &EntityKeyValues::importState> UndoImportCaller;
576
577   void attach(Observer& observer)
578   {
579     ASSERT_MESSAGE(!m_observerMutex, "observer cannot be attached during iteration");
580     m_observers.insert(&observer);
581     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
582     {
583       observer.insert((*i).first.c_str(), *(*i).second);
584     }
585   }
586   void detach(Observer& observer)
587   {
588     ASSERT_MESSAGE(!m_observerMutex, "observer cannot be detached during iteration");
589     m_observers.erase(&observer);
590     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
591     {
592       observer.erase((*i).first.c_str(), *(*i).second);
593     }
594   }
595
596   void forEachKeyValue_instanceAttach(MapFile* map)
597   {
598     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
599     {
600       (*i).second->instanceAttach(map);
601     }
602   }
603   void forEachKeyValue_instanceDetach(MapFile* map)
604   {
605     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
606     {
607       (*i).second->instanceDetach(map);
608     }
609   }
610
611   void instanceAttach(MapFile* map)
612   {
613     if(m_counter != 0)
614     {
615       m_counter->increment();
616     }
617
618     m_instanced = true;
619     forEachKeyValue_instanceAttach(map);
620     m_undo.instanceAttach(map);
621   }
622   void instanceDetach(MapFile* map)
623   {
624     if(m_counter != 0)
625     {
626       m_counter->decrement();
627     }
628
629     m_undo.instanceDetach(map);
630     forEachKeyValue_instanceDetach(map);
631     m_instanced = false;
632   }
633
634   // entity
635   EntityClass& getEntityClass() const
636   {
637     return *m_eclass;
638   }
639   void forEachKeyValue(Visitor& visitor) const
640   {
641     for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
642     {
643       visitor.visit((*i).first.c_str(), (*i).second->c_str());
644     }
645   }
646   void setKeyValue(const char* key, const char* value)
647   {
648     if(value[0] == '\0'
649       /*|| string_equal(EntityClass_valueForKey(*m_eclass, key), value)*/) // don't delete values equal to default
650     {
651       erase(key);
652     }
653     else
654     {
655       insert(key, value);
656     }
657     m_entityKeyValueChanged();
658   }
659   const char* getKeyValue(const char* key) const
660   {
661     KeyValues::const_iterator i = m_keyValues.find(key);
662     if(i != m_keyValues.end())
663     {
664       return (*i).second->c_str();
665     }
666
667     return EntityClass_valueForKey(*m_eclass, key);
668   }
669
670   bool isContainer() const
671   {
672     return m_isContainer;
673   }
674 };
675
676 /// \brief A Resource reference with a controlled lifetime.
677 /// \brief The resource is released when the ResourceReference is destroyed.
678 class ResourceReference
679 {
680   CopiedString m_name;
681   Resource* m_resource;
682 public:
683   ResourceReference(const char* name)
684     : m_name(name)
685   {
686     capture();
687   }
688   ResourceReference(const ResourceReference& other)
689     : m_name(other.m_name)
690   {
691     capture();
692   }
693   ResourceReference& operator=(const ResourceReference& other)
694   {
695     ResourceReference tmp(other);
696     tmp.swap(*this);
697     return *this;
698   }
699   ~ResourceReference()
700   {
701     release();
702   }
703
704   void capture()
705   {
706     m_resource = GlobalReferenceCache().capture(m_name.c_str());
707   }
708   void release()
709   {
710     GlobalReferenceCache().release(m_name.c_str());
711   }
712
713   const char* getName() const
714   {
715     return m_name.c_str();
716   }
717   void setName(const char* name)
718   {
719     ResourceReference tmp(name);
720     tmp.swap(*this);
721   }
722
723   void swap(ResourceReference& other)
724   {
725     std::swap(m_resource, other.m_resource);
726     std::swap(m_name, other.m_name);
727   }
728
729   void attach(ModuleObserver& observer)
730   {
731     m_resource->attach(observer);
732   }
733   void detach(ModuleObserver& observer)
734   {
735     m_resource->detach(observer);
736   }
737
738   Resource* get()
739   {
740     return m_resource;
741   }
742 };
743
744 namespace std
745 {
746   /// \brief Swaps the values of \p self and \p other.
747   /// Overloads std::swap.
748   inline void swap(ResourceReference& self, ResourceReference& other)
749   {
750     self.swap(other);
751   }
752 }
753
754 #endif