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