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