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"
42 virtual const Vector3& world_position() const = 0;
45 typedef std::set<Targetable*> targetables_t;
47 extern const char* g_targetable_nameKey;
49 targetables_t* getTargetables( const char* targetname );
51 class EntityConnectionLine : public OpenGLRenderable
57 void render( RenderStateFlags state ) const {
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];
69 glVertex3fv( vector3_to_array( start ) );
70 glVertex3fv( vector3_to_array( end ) );
72 len *= 0.0625; // half / 8
74 Vector3 arrow( start );
75 for ( unsigned int i = 0, count = ( len < 32 ) ? 1 : static_cast<unsigned int>( len * 0.0625 ); i < count; i++ )
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] );
90 Targetable& m_targetable;
91 targetables_t* m_targets;
94 if ( m_targets != 0 ) {
95 m_targets->insert( &m_targetable );
99 if ( m_targets != 0 ) {
100 m_targets->erase( &m_targetable );
104 TargetedEntity( Targetable& targetable )
105 : m_targetable( targetable ), m_targets( getTargetables( "" ) ){
111 void targetnameChanged( const char* name ){
113 m_targets = getTargetables( name );
116 typedef MemberCaller<TargetedEntity, void(const char*), &TargetedEntity::targetnameChanged> TargetnameChangedCaller;
120 class TargetingEntity
122 targetables_t* m_targets;
125 m_targets( getTargetables( "" ) ){
127 void targetChanged( const char* target ){
128 m_targets = getTargetables( target );
130 typedef MemberCaller<TargetingEntity, void(const char*), &TargetingEntity::targetChanged> TargetChangedCaller;
132 typedef targetables_t::iterator iterator;
134 iterator begin() const {
135 if ( m_targets == 0 ) {
138 return m_targets->begin();
140 iterator end() const {
141 if ( m_targets == 0 ) {
144 return m_targets->end();
146 size_t size() const {
147 if ( m_targets == 0 ) {
150 return m_targets->size();
153 return m_targets == 0 || m_targets->empty();
159 template<typename Functor>
160 void TargetingEntity_forEach( const TargetingEntity& targets, const Functor& functor ){
161 for ( TargetingEntity::iterator i = targets.begin(); i != targets.end(); ++i )
163 functor( ( *i )->world_position() );
167 typedef std::map<std::size_t, TargetingEntity> TargetingEntities;
169 template<typename Functor>
170 void TargetingEntities_forEach( const TargetingEntities& targetingEntities, const Functor& functor ){
171 for ( TargetingEntities::const_iterator i = targetingEntities.begin(); i != targetingEntities.end(); ++i )
173 TargetingEntity_forEach( ( *i ).second, functor );
177 class TargetLinesPushBack
179 RenderablePointVector& m_targetLines;
180 const Vector3& m_worldPosition;
181 const VolumeTest& m_volume;
183 TargetLinesPushBack( RenderablePointVector& targetLines, const Vector3& worldPosition, const VolumeTest& volume ) :
184 m_targetLines( targetLines ), m_worldPosition( worldPosition ), m_volume( volume ){
186 void operator()( const Vector3& worldPosition ) const {
187 Vector3 dir( worldPosition - m_worldPosition );//end - start
188 double len = vector3_length( dir );
189 if ( len != 0 && m_volume.TestLine( segment_for_startend( m_worldPosition, worldPosition ) ) ) {
190 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( m_worldPosition ) ) );
191 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( worldPosition ) ) );
193 Vector3 mid( ( worldPosition + m_worldPosition ) * 0.5f );
194 //vector3_normalise( dir );
196 Vector3 hack( 0.57735, 0.57735, 0.57735 );
199 for ( int i = 0; i < 3 ; ++i ){
203 if ( fabs( dir[i] ) > max ){
209 Vector3 ort( vector3_cross( dir, hack ) );
210 //vector3_normalise( ort );
211 Vector3 wing1( mid - dir*12 + ort*6 );
212 Vector3 wing2( wing1 - ort*12 );
214 if( len <= 512 || len > 768 ){
215 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( mid ) ) );
216 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( wing1 ) ) );
217 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( mid ) ) );
218 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( wing2 ) ) );
221 Vector3 wing1_delta( mid - wing1 );
222 Vector3 wing2_delta( mid - wing2 );
223 Vector3 point( m_worldPosition + dir*256 );
224 wing1 = point - wing1_delta;
225 wing2 = point - wing2_delta;
226 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( point ) ) );
227 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( wing1 ) ) );
228 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( point ) ) );
229 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( wing2 ) ) );
230 point = worldPosition - dir*256;
231 wing1 = point - wing1_delta;
232 wing2 = point - wing2_delta;
233 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( point ) ) );
234 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( wing1 ) ) );
235 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( point ) ) );
236 m_targetLines.push_back( PointVertex( reinterpret_cast<const Vertex3f&>( wing2 ) ) );
242 class TargetKeys : public Entity::Observer
244 TargetingEntities m_targetingEntities;
245 Callback<void()> m_targetsChanged;
247 bool readTargetKey( const char* key, std::size_t& index ){
248 if ( string_equal_n( key, "target", 6 ) ) {
250 if ( string_empty( key + 6 ) || string_parse_size( key + 6, index ) ) {
254 if ( string_equal( key, "killtarget" ) ) {
261 void setTargetsChanged( const Callback<void()>& targetsChanged ){
262 m_targetsChanged = targetsChanged;
264 void targetsChanged(){
268 void insert( const char* key, EntityKeyValue& value ){
270 if ( readTargetKey( key, index ) ) {
271 TargetingEntities::iterator i = m_targetingEntities.insert( TargetingEntities::value_type( index, TargetingEntity() ) ).first;
272 value.attach( TargetingEntity::TargetChangedCaller( ( *i ).second ) );
276 void erase( const char* key, EntityKeyValue& value ){
278 if ( readTargetKey( key, index ) ) {
279 TargetingEntities::iterator i = m_targetingEntities.find( index );
280 value.detach( TargetingEntity::TargetChangedCaller( ( *i ).second ) );
281 m_targetingEntities.erase( i );
285 const TargetingEntities& get() const {
286 return m_targetingEntities;
292 class RenderableTargetingEntity
294 TargetingEntity& m_targets;
295 mutable RenderablePointVector m_target_lines;
297 static Shader* m_state;
299 RenderableTargetingEntity( TargetingEntity& targets )
300 : m_targets( targets ), m_target_lines( GL_LINES ){
302 void compile( const VolumeTest& volume, const Vector3& world_position ) const {
303 m_target_lines.clear();
304 m_target_lines.reserve( m_targets.size() * 14 );
305 TargetingEntity_forEach( m_targets, TargetLinesPushBack( m_target_lines, world_position, volume ) );
307 void render( Renderer& renderer, const VolumeTest& volume, const Vector3& world_position ) const {
308 if ( !m_targets.empty() ) {
309 compile( volume, world_position );
310 if ( !m_target_lines.empty() ) {
311 renderer.addRenderable( m_target_lines, g_matrix4_identity );
317 class RenderableTargetingEntities
319 const TargetingEntities& m_targets;
320 mutable RenderablePointVector m_target_lines;
322 static Shader* m_state;
324 RenderableTargetingEntities( const TargetingEntities& targets )
325 : m_targets( targets ), m_target_lines( GL_LINES ){
327 void compile( const VolumeTest& volume, const Vector3& world_position ) const {
328 m_target_lines.clear();
329 TargetingEntities_forEach( m_targets, TargetLinesPushBack( m_target_lines, world_position, volume ) );
331 void render( Renderer& renderer, const VolumeTest& volume, const Vector3& world_position ) const {
332 if ( !m_targets.empty() ) {
333 compile( volume, world_position );
334 if ( !m_target_lines.empty() ) {
335 renderer.addRenderable( m_target_lines, g_matrix4_identity );
342 class TargetableInstance :
343 public SelectableInstance,
345 public Entity::Observer
347 mutable Vertex3f m_position;
348 EntityKeyValues& m_entity;
349 TargetKeys m_targeting;
350 TargetedEntity m_targeted;
351 RenderableTargetingEntities m_renderable;
355 const scene::Path& path,
356 scene::Instance* parent,
358 InstanceTypeCastTable& casts,
359 EntityKeyValues& entity,
360 Targetable& targetable
362 SelectableInstance( path, parent, instance, casts ),
364 m_targeted( targetable ),
365 m_renderable( m_targeting.get() ){
366 m_entity.attach( *this );
367 m_entity.attach( m_targeting );
369 ~TargetableInstance(){
370 m_entity.detach( m_targeting );
371 m_entity.detach( *this );
374 void setTargetsChanged( const Callback<void()>& targetsChanged ){
375 m_targeting.setTargetsChanged( targetsChanged );
377 void targetsChanged(){
378 m_targeting.targetsChanged();
381 void insert( const char* key, EntityKeyValue& value ){
382 if ( string_equal( key, g_targetable_nameKey ) ) {
383 value.attach( TargetedEntity::TargetnameChangedCaller( m_targeted ) );
386 void erase( const char* key, EntityKeyValue& value ){
387 if ( string_equal( key, g_targetable_nameKey ) ) {
388 value.detach( TargetedEntity::TargetnameChangedCaller( m_targeted ) );
392 const Vector3& world_position() const {
394 const AABB& bounds = Instance::worldAABB();
395 if ( aabb_valid( bounds ) ) {
396 return bounds.origin;
399 const AABB& childBounds = Instance::childBounds();
400 if ( aabb_valid( childBounds ) ) {
401 return childBounds.origin;
404 return vector4_to_vector3( localToWorld().t() );
407 void render( Renderer& renderer, const VolumeTest& volume ) const {
408 renderer.SetState( m_entity.getEntityClass().m_state_wire, Renderer::eWireframeOnly );
409 renderer.SetState( m_entity.getEntityClass().m_state_wire, Renderer::eFullMaterials );
410 m_renderable.render( renderer, volume, world_position() );
413 const TargetingEntities& getTargeting() const {
414 return m_targeting.get();
419 class RenderableConnectionLines : public Renderable
421 typedef std::set<TargetableInstance*> TargetableInstances;
422 TargetableInstances m_instances;
424 void attach( TargetableInstance& instance ){
425 ASSERT_MESSAGE( m_instances.find( &instance ) == m_instances.end(), "cannot attach instance" );
426 m_instances.insert( &instance );
428 void detach( TargetableInstance& instance ){
429 ASSERT_MESSAGE( m_instances.find( &instance ) != m_instances.end(), "cannot detach instance" );
430 m_instances.erase( &instance );
433 void renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
434 for ( TargetableInstances::const_iterator i = m_instances.begin(); i != m_instances.end(); ++i )
436 if ( ( *i )->path().top().get().visible() ) {
437 ( *i )->render( renderer, volume );
441 void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const {
442 renderSolid( renderer, volume );
446 typedef Static<RenderableConnectionLines> StaticRenderableConnectionLines;