2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
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.
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.
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
22 #if !defined( INCLUDED_TARGETABLE_H )
23 #define INCLUDED_TARGETABLE_H
29 #include "renderable.h"
31 #include "math/line.h"
33 #include "generic/callback.h"
34 #include "selectionlib.h"
35 #include "entitylib.h"
36 #include "eclasslib.h"
41 virtual const Vector3 &world_position() const = 0;
44 typedef std::set<Targetable *> targetables_t;
46 extern const char *g_targetable_nameKey;
48 targetables_t *getTargetables(const char *targetname);
50 class EntityConnectionLine : public OpenGLRenderable {
55 void render(RenderStateFlags state) const
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];
68 glVertex3fv(vector3_to_array(start));
69 glVertex3fv(vector3_to_array(end));
71 len *= 0.0625; // half / 8
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]);
86 class TargetedEntity {
87 Targetable &m_targetable;
88 targetables_t *m_targets;
93 m_targets->insert(&m_targetable);
100 m_targets->erase(&m_targetable);
105 TargetedEntity(Targetable &targetable)
106 : m_targetable(targetable), m_targets(getTargetables(""))
116 void targetnameChanged(const char *name)
119 m_targets = getTargetables(name);
123 typedef MemberCaller<TargetedEntity, void(
124 const char *), &TargetedEntity::targetnameChanged> TargetnameChangedCaller;
128 class TargetingEntity {
129 targetables_t *m_targets;
132 m_targets(getTargetables(""))
136 void targetChanged(const char *target)
138 m_targets = getTargetables(target);
141 typedef MemberCaller<TargetingEntity, void(const char *), &TargetingEntity::targetChanged> TargetChangedCaller;
143 typedef targetables_t::iterator iterator;
145 iterator begin() const
147 if (m_targets == 0) {
150 return m_targets->begin();
155 if (m_targets == 0) {
158 return m_targets->end();
163 if (m_targets == 0) {
166 return m_targets->size();
171 return m_targets == 0 || m_targets->empty();
176 template<typename Functor>
177 void TargetingEntity_forEach(const TargetingEntity &targets, const Functor &functor)
179 for (TargetingEntity::iterator i = targets.begin(); i != targets.end(); ++i) {
180 functor((*i)->world_position());
184 typedef std::map<std::size_t, TargetingEntity> TargetingEntities;
186 template<typename Functor>
187 void TargetingEntities_forEach(const TargetingEntities &targetingEntities, const Functor &functor)
189 for (TargetingEntities::const_iterator i = targetingEntities.begin(); i != targetingEntities.end(); ++i) {
190 TargetingEntity_forEach((*i).second, functor);
194 class TargetLinesPushBack {
195 RenderablePointVector &m_targetLines;
196 const Vector3 &m_worldPosition;
197 const VolumeTest &m_volume;
199 TargetLinesPushBack(RenderablePointVector &targetLines, const Vector3 &worldPosition, const VolumeTest &volume) :
200 m_targetLines(targetLines), m_worldPosition(worldPosition), m_volume(volume)
204 void operator()(const Vector3 &worldPosition) const
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 )));
213 class TargetKeys : public Entity::Observer {
214 TargetingEntities m_targetingEntities;
215 Callback<void()> m_targetsChanged;
217 bool readTargetKey(const char *key, std::size_t &index)
219 if (string_equal_n(key, "target", 6)) {
221 if (string_empty(key + 6) || string_parse_size(key + 6, index)) {
225 if (string_equal(key, "killtarget")) {
233 void setTargetsChanged(const Callback<void()> &targetsChanged)
235 m_targetsChanged = targetsChanged;
238 void targetsChanged()
243 void insert(const char *key, EntityKeyValue &value)
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));
254 void erase(const char *key, EntityKeyValue &value)
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);
265 const TargetingEntities &get() const
267 return m_targetingEntities;
272 class RenderableTargetingEntity {
273 TargetingEntity &m_targets;
274 mutable RenderablePointVector m_target_lines;
276 static Shader *m_state;
278 RenderableTargetingEntity(TargetingEntity &targets)
279 : m_targets(targets), m_target_lines(GL_LINES)
283 void compile(const VolumeTest &volume, const Vector3 &world_position) const
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));
290 void render(Renderer &renderer, const VolumeTest &volume, const Vector3 &world_position) const
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);
301 class RenderableTargetingEntities {
302 const TargetingEntities &m_targets;
303 mutable RenderablePointVector m_target_lines;
305 static Shader *m_state;
307 RenderableTargetingEntities(const TargetingEntities &targets)
308 : m_targets(targets), m_target_lines(GL_LINES)
312 void compile(const VolumeTest &volume, const Vector3 &world_position) const
314 m_target_lines.clear();
315 TargetingEntities_forEach(m_targets, TargetLinesPushBack(m_target_lines, world_position, volume));
318 void render(Renderer &renderer, const VolumeTest &volume, const Vector3 &world_position) const
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);
330 class TargetableInstance :
331 public SelectableInstance,
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;
342 const scene::Path &path,
343 scene::Instance *parent,
345 InstanceTypeCastTable &casts,
346 EntityKeyValues &entity,
347 Targetable &targetable
349 SelectableInstance(path, parent, instance, casts),
351 m_targeted(targetable),
352 m_renderable(m_targeting.get())
354 m_entity.attach(*this);
355 m_entity.attach(m_targeting);
358 ~TargetableInstance()
360 m_entity.detach(m_targeting);
361 m_entity.detach(*this);
364 void setTargetsChanged(const Callback<void()> &targetsChanged)
366 m_targeting.setTargetsChanged(targetsChanged);
369 void targetsChanged()
371 m_targeting.targetsChanged();
374 void insert(const char *key, EntityKeyValue &value)
376 if (string_equal(key, g_targetable_nameKey)) {
377 value.attach(TargetedEntity::TargetnameChangedCaller(m_targeted));
381 void erase(const char *key, EntityKeyValue &value)
383 if (string_equal(key, g_targetable_nameKey)) {
384 value.detach(TargetedEntity::TargetnameChangedCaller(m_targeted));
388 const Vector3 &world_position() const
391 const AABB &bounds = Instance::worldAABB();
392 if (aabb_valid(bounds)) {
393 return bounds.origin;
396 const AABB& childBounds = Instance::childBounds();
397 if ( aabb_valid( childBounds ) ) {
398 return childBounds.origin;
401 return vector4_to_vector3(localToWorld().t());
404 void render(Renderer &renderer, const VolumeTest &volume) const
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());
411 const TargetingEntities &getTargeting() const
413 return m_targeting.get();
418 class RenderableConnectionLines : public Renderable {
419 typedef std::set<TargetableInstance *> TargetableInstances;
420 TargetableInstances m_instances;
422 void attach(TargetableInstance &instance)
424 ASSERT_MESSAGE(m_instances.find(&instance) == m_instances.end(), "cannot attach instance");
425 m_instances.insert(&instance);
428 void detach(TargetableInstance &instance)
430 ASSERT_MESSAGE(m_instances.find(&instance) != m_instances.end(), "cannot detach instance");
431 m_instances.erase(&instance);
434 void renderSolid(Renderer &renderer, const VolumeTest &volume) const
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);
443 void renderWireframe(Renderer &renderer, const VolumeTest &volume) const
445 renderSolid(renderer, volume);
449 typedef Static<RenderableConnectionLines> StaticRenderableConnectionLines;