initial
[divverent/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     if(string_equal(key, "killtarget"))
228     {
229       index = -1;
230           return true;
231     }
232     return false;
233   }
234 public:
235   void setTargetsChanged(const Callback& targetsChanged)
236   {
237     m_targetsChanged = targetsChanged;
238   }
239   void targetsChanged()
240   {
241     m_targetsChanged();
242   }
243
244   void insert(const char* key, EntityKeyValue& value)
245   {
246     std::size_t index;
247     if(readTargetKey(key, index))
248     {
249       TargetingEntities::iterator i = m_targetingEntities.insert(TargetingEntities::value_type(index, TargetingEntity())).first;
250       value.attach(TargetingEntity::TargetChangedCaller((*i).second));
251       targetsChanged();
252     }
253   }
254   void erase(const char* key, EntityKeyValue& value)
255   {
256     std::size_t index;
257     if(readTargetKey(key, index))
258     {
259       TargetingEntities::iterator i = m_targetingEntities.find(index);
260       value.detach(TargetingEntity::TargetChangedCaller((*i).second));
261       m_targetingEntities.erase(i);
262       targetsChanged();
263     }
264   }
265   const TargetingEntities& get() const
266   {
267     return m_targetingEntities;
268   }
269 };
270
271
272
273 class RenderableTargetingEntity
274 {
275   TargetingEntity& m_targets;
276   mutable RenderablePointVector m_target_lines;
277 public:
278   static Shader* m_state;
279
280   RenderableTargetingEntity(TargetingEntity& targets)
281     : m_targets(targets), m_target_lines(GL_LINES)
282   {
283   }
284   void compile(const VolumeTest& volume, const Vector3& world_position) const
285   {
286     m_target_lines.clear();
287     m_target_lines.reserve(m_targets.size() * 2);
288     TargetingEntity_forEach(m_targets, TargetLinesPushBack(m_target_lines, world_position, volume));
289   }
290   void render(Renderer& renderer, const VolumeTest& volume, const Vector3& world_position) const
291   {
292     if(!m_targets.empty())
293     {
294       compile(volume, world_position);
295       if(!m_target_lines.empty())
296       {
297         renderer.addRenderable(m_target_lines, g_matrix4_identity);
298       }
299     }
300   }
301 };
302
303 class RenderableTargetingEntities
304 {
305   const TargetingEntities& m_targets;
306   mutable RenderablePointVector m_target_lines;
307 public:
308   static Shader* m_state;
309
310   RenderableTargetingEntities(const TargetingEntities& targets)
311     : m_targets(targets), m_target_lines(GL_LINES)
312   {
313   }
314   void compile(const VolumeTest& volume, const Vector3& world_position) const
315   {
316     m_target_lines.clear();
317     TargetingEntities_forEach(m_targets, TargetLinesPushBack(m_target_lines, world_position, volume));
318   }
319   void render(Renderer& renderer, const VolumeTest& volume, const Vector3& world_position) const
320   {
321     if(!m_targets.empty())
322     {
323       compile(volume, world_position);
324       if(!m_target_lines.empty())
325       {
326         renderer.addRenderable(m_target_lines, g_matrix4_identity);
327       }
328     }
329   }
330 };
331
332
333 class TargetableInstance :
334 public SelectableInstance,
335 public Targetable,
336 public Entity::Observer
337 {
338   mutable Vertex3f m_position;
339   EntityKeyValues& m_entity;
340   TargetKeys m_targeting;
341   TargetedEntity m_targeted;
342   RenderableTargetingEntities m_renderable;
343 public:
344
345   TargetableInstance(
346     const scene::Path& path,
347     scene::Instance* parent,
348     void* instance,
349     InstanceTypeCastTable& casts,
350     EntityKeyValues& entity,
351     Targetable& targetable
352   ) :
353     SelectableInstance(path, parent, instance, casts),
354     m_entity(entity),
355     m_targeted(targetable),
356     m_renderable(m_targeting.get())
357   {
358     m_entity.attach(*this);
359     m_entity.attach(m_targeting);
360   }
361   ~TargetableInstance()
362   {
363     m_entity.detach(m_targeting);
364     m_entity.detach(*this);
365   }
366
367   void setTargetsChanged(const Callback& targetsChanged)
368   {
369     m_targeting.setTargetsChanged(targetsChanged);
370   }
371   void targetsChanged()
372   {
373     m_targeting.targetsChanged();
374   }
375
376   void insert(const char* key, EntityKeyValue& value)
377   {
378     if(string_equal(key, g_targetable_nameKey))
379     {
380       value.attach(TargetedEntity::TargetnameChangedCaller(m_targeted));
381     }
382   }
383   void erase(const char* key, EntityKeyValue& value)
384   {
385     if(string_equal(key, g_targetable_nameKey))
386     {
387       value.detach(TargetedEntity::TargetnameChangedCaller(m_targeted));
388     }
389   }
390
391   const Vector3& world_position() const
392   {
393 #if 1
394     const AABB& bounds = Instance::worldAABB();
395     if(aabb_valid(bounds))
396     {
397       return bounds.origin;
398     }
399 #else
400     const AABB& childBounds = Instance::childBounds();
401     if(aabb_valid(childBounds))
402     {
403       return childBounds.origin;
404     }
405 #endif
406     return vector4_to_vector3(localToWorld().t());
407   }
408
409   void render(Renderer& renderer, const VolumeTest& volume) const
410   {
411     renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly);
412     renderer.SetState(m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials);
413     m_renderable.render(renderer, volume, world_position());
414   }
415
416   const TargetingEntities& getTargeting() const
417   {
418     return m_targeting.get();
419   }
420 };
421
422
423 class RenderableConnectionLines : public Renderable
424 {
425   typedef std::set<TargetableInstance*> TargetableInstances;
426   TargetableInstances m_instances;
427 public:
428   void attach(TargetableInstance& instance)
429   {
430     ASSERT_MESSAGE(m_instances.find(&instance) == m_instances.end(), "cannot attach instance");
431     m_instances.insert(&instance);
432   }
433   void detach(TargetableInstance& instance)
434   {
435     ASSERT_MESSAGE(m_instances.find(&instance) != m_instances.end(), "cannot detach instance");
436     m_instances.erase(&instance);
437   }
438
439   void renderSolid(Renderer& renderer, const VolumeTest& volume) const
440   {
441     for(TargetableInstances::const_iterator i = m_instances.begin(); i != m_instances.end(); ++i)
442     {
443       if((*i)->path().top().get().visible())
444       {
445         (*i)->render(renderer, volume);
446       }
447     }
448   }
449   void renderWireframe(Renderer& renderer, const VolumeTest& volume) const
450   {
451     renderSolid(renderer, volume);
452   }
453 };
454
455 typedef Static<RenderableConnectionLines> StaticRenderableConnectionLines;
456
457 #endif