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