]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/targetable.h
Merge commit 'cac514541c3ae6f72c4c00c19c215ff62a023df4' into garux-merge
[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 {
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         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
93 void construct(){
94         if ( m_targets != 0 ) {
95                 m_targets->insert( &m_targetable );
96         }
97 }
98 void destroy(){
99         if ( m_targets != 0 ) {
100                 m_targets->erase( &m_targetable );
101         }
102 }
103 public:
104 TargetedEntity( Targetable& targetable )
105         : m_targetable( targetable ), m_targets( getTargetables( "" ) ){
106         construct();
107 }
108 ~TargetedEntity(){
109         destroy();
110 }
111 void targetnameChanged( const char* name ){
112         destroy();
113         m_targets = getTargetables( name );
114         construct();
115 }
116 typedef MemberCaller<TargetedEntity, void(const char*), &TargetedEntity::targetnameChanged> TargetnameChangedCaller;
117 };
118
119
120 class TargetingEntity
121 {
122 targetables_t* m_targets;
123 public:
124 TargetingEntity() :
125         m_targets( getTargetables( "" ) ){
126 }
127 void targetChanged( const char* target ){
128         m_targets = getTargetables( target );
129 }
130 typedef MemberCaller<TargetingEntity, void(const char*), &TargetingEntity::targetChanged> TargetChangedCaller;
131
132 typedef targetables_t::iterator iterator;
133
134 iterator begin() const {
135         if ( m_targets == 0 ) {
136                 return iterator();
137         }
138         return m_targets->begin();
139 }
140 iterator end() const {
141         if ( m_targets == 0 ) {
142                 return iterator();
143         }
144         return m_targets->end();
145 }
146 size_t size() const {
147         if ( m_targets == 0 ) {
148                 return 0;
149         }
150         return m_targets->size();
151 }
152 bool empty() const {
153         return m_targets == 0 || m_targets->empty();
154 }
155 };
156
157
158
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 )
162         {
163                 functor( ( *i )->world_position() );
164         }
165 }
166
167 typedef std::map<std::size_t, TargetingEntity> TargetingEntities;
168
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 )
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:
183 TargetLinesPushBack( RenderablePointVector& targetLines, const Vector3& worldPosition, const VolumeTest& volume ) :
184         m_targetLines( targetLines ), m_worldPosition( worldPosition ), m_volume( volume ){
185 }
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 ) ) );
192
193                 Vector3 mid( ( worldPosition + m_worldPosition ) * 0.5f );
194                 //vector3_normalise( dir );
195                 dir /= len;
196                 Vector3 hack( 0.57735, 0.57735, 0.57735 );
197                 int maxI = 0;
198                 float max = 0;
199                 for ( int i = 0; i < 3 ; ++i ){
200                         if ( dir[i] < 0 ){
201                                 hack[i] *= -1;
202                         }
203                         if ( fabs( dir[i] ) > max ){
204                                 maxI = i;
205                         }
206                 }
207                 hack[maxI] *= -1;
208
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 );
213
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 ) ) );
219                 }
220                 if( len > 512 ){
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 ) ) );
237                 }
238         }
239 }
240 };
241
242 class TargetKeys : public Entity::Observer
243 {
244 TargetingEntities m_targetingEntities;
245 Callback<void()> m_targetsChanged;
246
247 bool readTargetKey( const char* key, std::size_t& index ){
248         if ( string_equal_n( key, "target", 6 ) ) {
249                 index = 0;
250                 if ( string_empty( key + 6 ) || string_parse_size( key + 6, index ) ) {
251                         return true;
252                 }
253         }
254         if ( string_equal( key, "killtarget" ) ) {
255                 index = -1;
256                 return true;
257         }
258         return false;
259 }
260 public:
261 void setTargetsChanged( const Callback<void()>& targetsChanged ){
262         m_targetsChanged = targetsChanged;
263 }
264 void targetsChanged(){
265         m_targetsChanged();
266 }
267
268 void insert( const char* key, EntityKeyValue& value ){
269         std::size_t index;
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 ) );
273                 targetsChanged();
274         }
275 }
276 void erase( const char* key, EntityKeyValue& value ){
277         std::size_t index;
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 );
282                 targetsChanged();
283         }
284 }
285 const TargetingEntities& get() const {
286         return m_targetingEntities;
287 }
288 };
289
290
291
292 class RenderableTargetingEntity
293 {
294 TargetingEntity& m_targets;
295 mutable RenderablePointVector m_target_lines;
296 public:
297 static Shader* m_state;
298
299 RenderableTargetingEntity( TargetingEntity& targets )
300         : m_targets( targets ), m_target_lines( GL_LINES ){
301 }
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 ) );
306 }
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 );
312                 }
313         }
314 }
315 };
316
317 class RenderableTargetingEntities
318 {
319 const TargetingEntities& m_targets;
320 mutable RenderablePointVector m_target_lines;
321 public:
322 static Shader* m_state;
323
324 RenderableTargetingEntities( const TargetingEntities& targets )
325         : m_targets( targets ), m_target_lines( GL_LINES ){
326 }
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 ) );
330 }
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 );
336                 }
337         }
338 }
339 };
340
341
342 class TargetableInstance :
343         public SelectableInstance,
344         public Targetable,
345         public Entity::Observer
346 {
347 mutable Vertex3f m_position;
348 EntityKeyValues& m_entity;
349 TargetKeys m_targeting;
350 TargetedEntity m_targeted;
351 RenderableTargetingEntities m_renderable;
352 public:
353
354 TargetableInstance(
355         const scene::Path& path,
356         scene::Instance* parent,
357         void* instance,
358         InstanceTypeCastTable& casts,
359         EntityKeyValues& entity,
360         Targetable& targetable
361         ) :
362         SelectableInstance( path, parent, instance, casts ),
363         m_entity( entity ),
364         m_targeted( targetable ),
365         m_renderable( m_targeting.get() ){
366         m_entity.attach( *this );
367         m_entity.attach( m_targeting );
368 }
369 ~TargetableInstance(){
370         m_entity.detach( m_targeting );
371         m_entity.detach( *this );
372 }
373
374 void setTargetsChanged( const Callback<void()>& targetsChanged ){
375         m_targeting.setTargetsChanged( targetsChanged );
376 }
377 void targetsChanged(){
378         m_targeting.targetsChanged();
379 }
380
381 void insert( const char* key, EntityKeyValue& value ){
382         if ( string_equal( key, g_targetable_nameKey ) ) {
383                 value.attach( TargetedEntity::TargetnameChangedCaller( m_targeted ) );
384         }
385 }
386 void erase( const char* key, EntityKeyValue& value ){
387         if ( string_equal( key, g_targetable_nameKey ) ) {
388                 value.detach( TargetedEntity::TargetnameChangedCaller( m_targeted ) );
389         }
390 }
391
392 const Vector3& world_position() const {
393 #if 1
394         const AABB& bounds = Instance::worldAABB();
395         if ( aabb_valid( bounds ) ) {
396                 return bounds.origin;
397         }
398 #else
399         const AABB& childBounds = Instance::childBounds();
400         if ( aabb_valid( childBounds ) ) {
401                 return childBounds.origin;
402         }
403 #endif
404         return vector4_to_vector3( localToWorld().t() );
405 }
406
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() );
411 }
412
413 const TargetingEntities& getTargeting() const {
414         return m_targeting.get();
415 }
416 };
417
418
419 class RenderableConnectionLines : public Renderable
420 {
421 typedef std::set<TargetableInstance*> TargetableInstances;
422 TargetableInstances m_instances;
423 public:
424 void attach( TargetableInstance& instance ){
425         ASSERT_MESSAGE( m_instances.find( &instance ) == m_instances.end(), "cannot attach instance" );
426         m_instances.insert( &instance );
427 }
428 void detach( TargetableInstance& instance ){
429         ASSERT_MESSAGE( m_instances.find( &instance ) != m_instances.end(), "cannot detach instance" );
430         m_instances.erase( &instance );
431 }
432
433 void renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
434         for ( TargetableInstances::const_iterator i = m_instances.begin(); i != m_instances.end(); ++i )
435         {
436                 if ( ( *i )->path().top().get().visible() ) {
437                         ( *i )->render( renderer, volume );
438                 }
439         }
440 }
441 void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const {
442         renderSolid( renderer, volume );
443 }
444 };
445
446 typedef Static<RenderableConnectionLines> StaticRenderableConnectionLines;
447
448 #endif