]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/targetable.h
refactored plugin api; refactored callback library; added signals library
[xonotic/netradiant.git] / plugins / entity / targetable.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_TARGETABLE_H)
23 #define INCLUDED_TARGETABLE_H
24
25 #include <set>
26 #include <map>
27
28 #include "cullable.h"
29 #include "renderable.h"
30
31 #include "math/line.h"
32 #include "render.h"
33 #include "generic/callback.h"
34 #include "selectionlib.h"
35 #include "entitylib.h"
36 #include "eclasslib.h"
37 #include "stringio.h"
38
39 class Targetable
40 {
41 public:
42   virtual const Vector3& world_position() const = 0;
43 };
44
45 typedef std::set<Targetable*> targetables_t;
46
47 extern const char* g_targetable_nameKey;
48
49 targetables_t* getTargetables(const char* targetname);
50
51 class EntityConnectionLine : public OpenGLRenderable
52 {
53 public:
54   Vector3 start;
55   Vector3 end;
56
57   void render(RenderStateFlags state) const
58   {
59     float s1[2], s2[2];
60     Vector3 dir(vector3_subtracted(end, start));
61     double len = vector3_length(dir);
62     vector3_scale(dir, 8.0 * (1.0 / len));
63     s1[0] = dir[0] - dir[1];
64     s1[1] = dir[0] + dir[1];
65     s2[0] = dir[0] + dir[1];
66     s2[1] = -dir[0] + dir[1];
67     
68     glBegin(GL_LINES);
69
70     glVertex3fv(vector3_to_array(start));
71     glVertex3fv(vector3_to_array(end));
72
73     len*=0.0625; // half / 8
74
75     Vector3 arrow(start);
76     for (unsigned int i = 0, count = (len<32)? 1 : static_cast<unsigned int>(len*0.0625); i < count; i++)
77     {
78       vector3_add(arrow, vector3_scaled(dir, (len<32)?len:32));
79       glVertex3fv(vector3_to_array(arrow));
80       glVertex3f(arrow[0]+s1[0], arrow[1]+s1[1], arrow[2]+dir[2]);
81       glVertex3fv(vector3_to_array(arrow));
82       glVertex3f(arrow[0]+s2[0], arrow[1]+s2[1], arrow[2]+dir[2]);
83     }
84     
85     glEnd();
86   }
87 };
88
89 class TargetedEntity
90 {
91   Targetable& m_targetable;
92   targetables_t* m_targets;
93
94   void construct()
95   {
96     if(m_targets != 0)
97       m_targets->insert(&m_targetable);
98   }
99   void destroy()
100   {
101     if(m_targets != 0)
102       m_targets->erase(&m_targetable);
103   }
104 public:
105   TargetedEntity(Targetable& targetable)
106     : m_targetable(targetable), m_targets(getTargetables(""))
107   {
108     construct();
109   }
110   ~TargetedEntity()
111   {
112     destroy();
113   }
114   void targetnameChanged(const char* name)
115   {
116     destroy();
117     m_targets = getTargetables(name);
118     construct();
119   }
120   typedef MemberCaller1<TargetedEntity, const char*, &TargetedEntity::targetnameChanged> TargetnameChangedCaller;
121 };
122
123
124 class TargetingEntity
125 {
126   targetables_t* m_targets;
127 public:
128   TargetingEntity() :
129     m_targets(getTargetables(""))
130   {
131   }
132   void targetChanged(const char* target)
133   {
134     m_targets = getTargetables(target);
135   }
136   typedef MemberCaller1<TargetingEntity, const char*, &TargetingEntity::targetChanged> TargetChangedCaller;
137
138   typedef targetables_t::iterator iterator;
139
140   iterator begin() const
141   {
142     if(m_targets == 0)
143     {
144       return iterator();
145     }
146     return m_targets->begin();
147   }
148   iterator end() const
149   {
150     if(m_targets == 0)
151     {
152       return iterator();
153     }
154     return m_targets->end();
155   }
156   size_t size() const
157   {
158     if(m_targets == 0)
159     {
160       return 0;
161     }
162     return m_targets->size();
163   }
164   bool empty() const
165   {
166     return m_targets == 0 || m_targets->empty();
167   }
168 };
169
170
171
172 template<typename Functor>
173 void TargetingEntity_forEach(const TargetingEntity& targets, const Functor& functor)
174 {
175   for(TargetingEntity::iterator i = targets.begin(); i != targets.end(); ++i)
176   {
177     functor((*i)->world_position());
178   }
179 }
180
181 typedef std::map<std::size_t, TargetingEntity> TargetingEntities;
182
183 template<typename Functor>
184 void TargetingEntities_forEach(const TargetingEntities& targetingEntities, const Functor& functor)
185 {
186   for(TargetingEntities::const_iterator i = targetingEntities.begin(); i != targetingEntities.end(); ++i)
187   {
188     TargetingEntity_forEach((*i).second, functor);
189   }
190 }
191
192 class TargetLinesPushBack
193 {
194   RenderablePointVector& m_targetLines;
195   const Vector3& m_worldPosition;
196   const VolumeTest& m_volume;
197 public:
198   TargetLinesPushBack(RenderablePointVector& targetLines, const Vector3& worldPosition, const VolumeTest& volume) :
199     m_targetLines(targetLines), m_worldPosition(worldPosition), m_volume(volume)
200   {
201   }
202   void operator()(const Vector3& worldPosition) const
203   {
204     if(m_volume.TestLine(segment_for_startend(m_worldPosition, worldPosition)))
205     {
206       m_targetLines.push_back(PointVertex(reinterpret_cast<const Vertex3f&>(m_worldPosition)));
207       m_targetLines.push_back(PointVertex(reinterpret_cast<const Vertex3f&>(worldPosition)));
208     }
209   }
210 };
211
212 class TargetKeys : public Entity::Observer
213 {
214   TargetingEntities m_targetingEntities;
215   Callback m_targetsChanged;
216
217   bool readTargetKey(const char* key, std::size_t& index)
218   {
219     if(string_equal_n(key, "target", 6))
220     {
221       index = 0;
222       if(string_empty(key + 6) || string_parse_size(key + 6, index))
223       {
224         return true;
225       }
226     }
227     return false;
228   }
229 public:
230   void setTargetsChanged(const Callback& targetsChanged)
231   {
232     m_targetsChanged = targetsChanged;
233   }
234   void targetsChanged()
235   {
236     m_targetsChanged();
237   }
238
239   void insert(const char* key, EntityKeyValue& value)
240   {
241     std::size_t index;
242     if(readTargetKey(key, index))
243     {
244       TargetingEntities::iterator i = m_targetingEntities.insert(TargetingEntities::value_type(index, TargetingEntity())).first;
245       value.attach(TargetingEntity::TargetChangedCaller((*i).second));
246       targetsChanged();
247     }
248   }
249   void erase(const char* key, EntityKeyValue& value)
250   {
251     std::size_t index;
252     if(readTargetKey(key, index))
253     {
254       TargetingEntities::iterator i = m_targetingEntities.find(index);
255       value.detach(TargetingEntity::TargetChangedCaller((*i).second));
256       m_targetingEntities.erase(i);
257       targetsChanged();
258     }
259   }
260   const TargetingEntities& get() const
261   {
262     return m_targetingEntities;
263   }
264 };
265
266
267
268 class RenderableTargetingEntity
269 {
270   TargetingEntity& m_targets;
271   mutable RenderablePointVector m_target_lines;
272 public:
273   static Shader* m_state;
274
275   RenderableTargetingEntity(TargetingEntity& targets)
276     : m_targets(targets), m_target_lines(GL_LINES)
277   {
278   }
279   void compile(const VolumeTest& volume, const Vector3& world_position) const
280   {
281     m_target_lines.clear();
282     m_target_lines.reserve(m_targets.size() * 2);
283     TargetingEntity_forEach(m_targets, TargetLinesPushBack(m_target_lines, world_position, volume));
284   }
285   void render(Renderer& renderer, const VolumeTest& volume, const Vector3& world_position) const
286   {
287     if(!m_targets.empty())
288     {
289       compile(volume, world_position);
290       if(!m_target_lines.empty())
291       {
292         renderer.addRenderable(m_target_lines, g_matrix4_identity);
293       }
294     }
295   }
296 };
297
298 class RenderableTargetingEntities
299 {
300   const TargetingEntities& m_targets;
301   mutable RenderablePointVector m_target_lines;
302 public:
303   static Shader* m_state;
304
305   RenderableTargetingEntities(const TargetingEntities& targets)
306     : m_targets(targets), m_target_lines(GL_LINES)
307   {
308   }
309   void compile(const VolumeTest& volume, const Vector3& world_position) const
310   {
311     m_target_lines.clear();
312     TargetingEntities_forEach(m_targets, TargetLinesPushBack(m_target_lines, world_position, volume));
313   }
314   void render(Renderer& renderer, const VolumeTest& volume, const Vector3& world_position) const
315   {
316     if(!m_targets.empty())
317     {
318       compile(volume, world_position);
319       if(!m_target_lines.empty())
320       {
321         renderer.addRenderable(m_target_lines, g_matrix4_identity);
322       }
323     }
324   }
325 };
326
327
328 class TargetableInstance :
329 public SelectableInstance,
330 public Targetable,
331 public Entity::Observer
332 {
333   mutable Vertex3f m_position;
334   EntityKeyValues& m_entity;
335   TargetKeys m_targeting;
336   TargetedEntity m_targeted;
337   RenderableTargetingEntities m_renderable;
338 public:
339
340   TargetableInstance(
341     const scene::Path& path,
342     scene::Instance* parent,
343     void* instance,
344     InstanceTypeCastTable& casts,
345     EntityKeyValues& entity,
346     Targetable& targetable
347   ) :
348     SelectableInstance(path, parent, instance, casts),
349     m_entity(entity),
350     m_targeted(targetable),
351     m_renderable(m_targeting.get())
352   {
353     m_entity.attach(*this);
354     m_entity.attach(m_targeting);
355   }
356   ~TargetableInstance()
357   {
358     m_entity.detach(m_targeting);
359     m_entity.detach(*this);
360   }
361
362   void setTargetsChanged(const Callback& targetsChanged)
363   {
364     m_targeting.setTargetsChanged(targetsChanged);
365   }
366   void targetsChanged()
367   {
368     m_targeting.targetsChanged();
369   }
370
371   void insert(const char* key, EntityKeyValue& value)
372   {
373     if(string_equal(key, g_targetable_nameKey))
374     {
375       value.attach(TargetedEntity::TargetnameChangedCaller(m_targeted));
376     }
377   }
378   void erase(const char* key, EntityKeyValue& value)
379   {
380     if(string_equal(key, g_targetable_nameKey))
381     {
382       value.detach(TargetedEntity::TargetnameChangedCaller(m_targeted));
383     }
384   }
385
386   const Vector3& world_position() const
387   {
388 #if 1
389     const AABB& bounds = Instance::worldAABB();
390     if(aabb_valid(bounds))
391     {
392       return bounds.origin;
393     }
394 #else
395     const AABB& childBounds = Instance::childBounds();
396     if(aabb_valid(childBounds))
397     {
398       return childBounds.origin;
399     }
400 #endif
401     return localToWorld().t();
402   }
403
404   void render(Renderer& renderer, const VolumeTest& volume) const
405   {
406     renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
407     renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials);
408     m_renderable.render(renderer, volume, world_position());
409   }
410
411   const TargetingEntities& getTargeting() const
412   {
413     return m_targeting.get();
414   }
415 };
416
417
418 class RenderableConnectionLines : public Renderable
419 {
420   typedef std::set<TargetableInstance*> TargetableInstances;
421   TargetableInstances m_instances;
422 public:
423   void attach(TargetableInstance& instance)
424   {
425     ASSERT_MESSAGE(m_instances.find(&instance) == m_instances.end(), "cannot attach instance");
426     m_instances.insert(&instance);
427   }
428   void detach(TargetableInstance& instance)
429   {
430     ASSERT_MESSAGE(m_instances.find(&instance) != m_instances.end(), "cannot detach instance");
431     m_instances.erase(&instance);
432   }
433
434   void renderSolid(Renderer& renderer, const VolumeTest& volume) const
435   {
436     for(TargetableInstances::const_iterator i = m_instances.begin(); i != m_instances.end(); ++i)
437     {
438       if((*i)->path().top().get().visible())
439       {
440         (*i)->render(renderer, volume);
441       }
442     }
443   }
444   void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
445   {
446     renderSolid(renderer, volume);
447   }
448 };
449
450 typedef Static<RenderableConnectionLines> StaticRenderableConnectionLines;
451
452 #endif