1 /*--License:
2 Kyra Sprite Engine
3 Copyright Lee Thomason (Grinning Lizard Software) 2001-2005
4 www.grinninglizard.com/kyra
5 www.sourceforge.net/projects/kyra
6
7 Kyra is provided under the LGPL.
8
9 I kindly request you display a splash screen (provided in the HTML documentation)
10 to promote Kyra and acknowledge the software and everyone who has contributed to it,
11 but it is not required by the license.
12
13 --- LGPL License --
14
15 This library is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Lesser General Public
17 License as published by the Free Software Foundation; either
18 version 2.1 of the License, or (at your option) any later version.
19
20 This library is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 Lesser General Public License for more details.
24
25 You should have received a copy of the GNU Lesser General Public
26 License along with this library; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
29 The full text of the license can be found in lgpl.txt
30 */
31
32 #include "imagetree.h"
33 #include "SDL.h"
34 #include "engine.h"
35 #include "../../grinliz/glgeometry.h"
36 //#include "../util/glprimitive.h"
37
38 #ifndef KYRA_BUILD_INCLUDED
39 #error Build file missing.
40 #endif
41
42 using namespace grinliz;
43
KrImageTree(KrEngine * _engine)44 KrImageTree::KrImageTree( KrEngine* _engine )
45 {
46 int i;
47
48 engine = _engine;
49 root = new KrImNode;
50 offsetRoot = new KrImNode;
51
52 AddNode( root, offsetRoot );
53
54 for( i=0; i<engine->NumWindows(); ++i )
55 {
56 root->SetPos( engine->ScreenBounds(i).min.x,
57 engine->ScreenBounds(i).min.y,
58 i );
59 root->CalcTransform( i );
60 }
61
62 //GLOUTPUT(( "Root: %x Offset: %x\n", root, offsetRoot ));
63 }
64
65
~KrImageTree()66 KrImageTree::~KrImageTree()
67 {
68 Clear( root );
69 }
70
71
Clear(KrImNode * parent)72 void KrImageTree::Clear( KrImNode* parent )
73 {
74 // Recursively delete the tree. It is important that
75 // we delete from the outside in.
76
77 GlInsideNode< KrImNode* >* child = &(parent->child);
78 child = child->next; // skip the sentinel
79 while ( !child->IsSentinel() )
80 {
81 GlInsideNode< KrImNode* >* temp = child;
82 child = child->next;
83
84 Clear( temp->data );
85 }
86 delete parent;
87 }
88
89
AddNode(KrImNode * parent,KrImNode * addMe)90 void KrImageTree::AddNode( KrImNode* parent, KrImNode* addMe )
91 {
92 if ( !parent )
93 {
94 parent = offsetRoot;
95 }
96
97 addMe->parent = parent;
98 addMe->engine = engine;
99
100 // Adds a node, keeping the depth sorted correctly.
101 GlInsideNodeIt< KrImNode* > it = parent->ChildTreeIterator();
102
103 for( it.Begin(); !it.Done(); it.Next() )
104 {
105 if ( addMe->ZDepth() < it.CurrentData()->ZDepth() )
106 {
107 it.InsertBefore( addMe->treeNode );
108 break;
109 }
110 }
111 if ( it.Done() )
112 {
113 // Add at the end. (It's a circular list - the end
114 // is just before the sentinel.)
115 it.InsertBefore( addMe->treeNode );
116 }
117
118 #ifdef VALIDATE_DEBUG
119 // Validate!
120 for( it.Begin(); !it.Done(); it.Next() )
121 {
122 GlInsideNode< KrImNode* >* prev = it.CurrentNode()->prev;
123 GlInsideNode< KrImNode* >* next = it.CurrentNode()->next;
124
125 if ( !prev->IsSentinel() )
126 GLASSERT( it.CurrentData()->ZDepth() >= prev->data->ZDepth() );
127 if ( !next->IsSentinel() )
128 GLASSERT( it.CurrentData()->ZDepth() <= next->data->ZDepth() );
129 }
130 #endif
131
132 addMe->Invalidate( KR_ALL_WINDOWS );
133
134 if ( addMe->NodeId() >= 0 )
135 {
136 idMap[ addMe->NodeId() ] = addMe;
137 GLOUTPUT(( "Node id=%d added\n", addMe->NodeId() ));
138 }
139 if ( !addMe->NodeName().empty() && addMe->NodeName().length() > 0 )
140 {
141 nameMap[ addMe->NodeName() ] = addMe;
142 }
143
144 // WARNING: the widgets will add nodes during this call, which makes
145 // this function recursive.
146 addMe->AddedtoTree();
147
148 #ifdef VALIDATE_DEBUG
149 ValidateTree( root );
150 #endif
151 }
152
153
DeleteNode(KrImNode * removeMe)154 bool KrImageTree::DeleteNode( KrImNode* removeMe )
155 {
156 bool ok = true;
157 // This call is recursive -- it takes out the given
158 // node and all the children.
159 GlInsideNode< KrImNode* >* child = &(removeMe->child);
160 child = child->next; // skip the sentinel
161 while ( !child->IsSentinel() )
162 {
163 GlInsideNode< KrImNode* >* temp = child;
164 child = child->next;
165
166 DeleteNode( temp->data );
167 }
168
169 // Unlink the node.
170 removeMe->treeNode.Remove();
171 // Give it a chance to clean up.
172 removeMe->LeavingTree();
173
174 if ( removeMe->NodeId() >= 0 )
175 {
176 idMap.erase( removeMe->NodeId() );
177 }
178 if ( !removeMe->NodeName().empty() )
179 {
180 nameMap.erase( removeMe->NodeName() );
181 }
182 delete removeMe;
183
184 #ifdef VALIDATE_DEBUG
185 ValidateTree( root );
186 #endif
187 return ok;
188 }
189
190
Walk()191 void KrImageTree::Walk()
192 {
193 for( int i=0; i<engine->NumWindows(); ++i )
194 {
195 treeDepth = 1; // reserve depth 0 for background. Note that windows don't overlap,
196 // so we can save some depths doing this
197 Walk( root, root->IsInvalid( i ), root->IsThisVisible(), i );
198 // if ( i == 2 && engine->DirtyRectangle( i )->NumRect())
199 // engine->DirtyRectangle( i )->PrintRects( "KrImageTree::Walk done tree 2" );
200 }
201 }
202
203
Walk(KrImNode * walkNode,bool invalid,bool visible,int currentWin)204 void KrImageTree::Walk( KrImNode* walkNode, bool invalid, bool visible, int currentWin )
205 {
206 // The child will use the parents composite transformation
207 // in its calculation, so parent nodes *must* be transformed
208 // before the children.
209 if ( invalid || walkNode->IsInvalid(currentWin) )
210 {
211 walkNode->FlushInvalid( currentWin, true ); // Adds current bounds to DR
212
213 // Even if invisible, we need the previous FlushInvalid, because
214 // the visibility could have changed between the last frame and
215 // this one. However, if the walkNode is now invisible,
216 // its new position can't cause a repaint.
217 //
218 // This means that invisible nodes don't have correct transforms.
219
220 if ( visible )
221 {
222 walkNode->CalcTransform( currentWin ); // Calculates new bounds.
223 walkNode->FlushInvalid( currentWin, false ); // Adds new bounds to DR
224 }
225 invalid = true;
226 }
227
228 // Our children will be drawn on top of us. Only used for OpenGL. In normal mode,
229 // we draw the tree in order and don't need an explicit z-value.
230 //GLASSERT( walkNode != root || treeDepth == 1 );
231 // walkNode->treeDepth = ++treeDepth;
232
233 // A check for openGL. See notes where constant is defined.
234 // GLASSERT( treeDepth < kKrOglDistance );
235
236 if ( visible )
237 {
238 // As we walk each child node, add its composite bounds to ours.
239 walkNode->compositeBounds[currentWin] = walkNode->bounds[currentWin];
240 }
241
242 #ifdef DEBUG
243 // Text -- damn it -- can have invalid bounds.
244 // if ( walkNode->ToImage() ) GLASSERT( walkNode->bounds.IsValid() );
245 if ( !walkNode->ToImage() ) GLASSERT( !walkNode->bounds[currentWin].IsValid() );
246 #endif
247
248 // Even if not visible, it is critical to walk the children, in case
249 // they were visible on the last frame.
250 //
251 for( GlInsideNode< KrImNode* >* childNode = walkNode->child.next; // start after the sentinel
252 childNode != &walkNode->child; // look for the sentinel
253 childNode = childNode->next )
254 {
255 KrImNode* child = childNode->data;
256 Walk( child, invalid, visible, currentWin );
257
258 // Check the child's composite bounds and add them in.
259 // Note here, we can use visibility.
260 if ( visible && child->CompositeBounds(currentWin).IsValid() )
261 {
262 if ( walkNode->compositeBounds[currentWin].IsValid() )
263 walkNode->compositeBounds[currentWin].DoUnion( child->CompositeBounds(currentWin) );
264 else
265 walkNode->compositeBounds[currentWin] = child->CompositeBounds(currentWin);
266 }
267 }
268
269 // We are done with this node and all its children, so
270 // its invalid state will be cleared. It will be re-drawn
271 // based on the dirty rectangle, not the invalid state.
272 walkNode->ClearInvalid( currentWin );
273 }
274
275
DrawWalk(const Rectangle2I & dr,KrPaintInfo * info,int win)276 void KrImageTree::DrawWalk( const Rectangle2I& dr, KrPaintInfo* info, int win )
277 {
278 #ifdef DEBUG
279 if ( info->OpenGL() )
280 {
281 GLASSERT( dr == engine->ScreenBounds( win ) );
282 //GLASSERT( root->CompositeBounds(win).IsValid() ); Can be invalid for tree with no images.
283 //GLASSERT( root->CompositeBounds(win).Intersect( dr ) );
284 }
285 #endif
286
287 if ( root->CompositeBounds(win).IsValid()
288 && root->CompositeBounds(win).Intersect( dr ) )
289 {
290 DrawWalk( dr, root, info, win );
291 }
292 }
293
294
295 // Child nodes in Kyra are drawn on top of their parents. This recursive
296 // call needs to be organized so that this property is maintained.
297 // It is assumed that the caller has called with valid composite
298 // bounds.
299
DrawWalk(const Rectangle2I & dr,KrImNode * node,KrPaintInfo * info,int win)300 void KrImageTree::DrawWalk( const Rectangle2I& dr, KrImNode* node, KrPaintInfo* info, int win )
301 {
302 GLASSERT( node->CompositeBounds(win).IsValid() );
303 GLASSERT( dr.Intersect( node->CompositeBounds(win) ) );
304
305 // Draw the children first on top, so the parent draws first.
306 // For opengl mode, the depth has been written to the image nodes,
307 // since there is no draw order.
308 if ( node->Bounds(win).IsValid() && node->IsVisible(win) )
309 {
310 if ( dr.Intersect( node->Bounds(win) ) )
311 {
312 GLASSERT( node->ToImage() );
313 KrImage* image = node->ToImage();
314 if ( image )
315 {
316 image->Draw( info, dr, win );
317 }
318 }
319 }
320
321 GlInsideNodeIt< KrImNode* > it( node->child );
322
323 for( it.Begin(); !it.Done(); it.Next() )
324 {
325 //TLW: here's the expensive part of the code... by changing the order of
326 // operations, get a time savings, as the most expensive to least expense goes
327 // Intersect(quickest), IsVisible, IsValid(slowest)
328 //
329 // Also, If its not valid, it'll never intersect... unnecessary test
330 // it.CurrentData()->CompositeBounds(win).IsValid()
331 //
332 if ( it.CurrentData()->CompositeBounds(win).Intersect( dr )
333 && it.CurrentData()->IsVisible(win) )
334 {
335 DrawWalk( dr, it.CurrentData(), info, win );
336 }
337 }
338 }
339
340
AddNodeNameHash(const std::string & name,KrImNode * node)341 void KrImageTree::AddNodeNameHash( const std::string& name, KrImNode* node )
342 {
343 nameMap[ name ] = node;
344 }
345
346
RemoveNodeNameHash(const std::string & name)347 void KrImageTree::RemoveNodeNameHash( const std::string& name )
348 {
349 nameMap.erase( name );
350 }
351
352
RemoveNodeIdHash(int id)353 void KrImageTree::RemoveNodeIdHash( int id )
354 {
355 idMap.erase( id );
356 }
357
358
AddNodeIdHash(int id,KrImNode * node)359 void KrImageTree::AddNodeIdHash( int id, KrImNode* node )
360 {
361 idMap[ id ] = node;
362 }
363
FindNodeById(int id)364 KrImNode* KrImageTree::FindNodeById( int id )
365 {
366 // KrImNode* ret = 0;
367 // idMap.Find( id, &ret );
368 std::map< U32, KrImNode* >::iterator it = idMap.find( id );
369 if ( it != idMap.end() )
370 return it->second;
371 return 0;
372 }
373
374
FindNodeByName(const std::string & name)375 KrImNode* KrImageTree::FindNodeByName( const std::string& name )
376 {
377 // KrImNode* ret = 0;
378 // nameMap.Find( name, &ret );
379 // return ret;
380 std::map< std::string, KrImNode* >::iterator it = nameMap.find( name );
381 if ( it != nameMap.end() )
382 return it->second;
383 return 0;
384 }
385
386 #ifdef DEBUG
ValidateTree(KrImNode * root)387 void KrImageTree::ValidateTree( KrImNode* root )
388 {
389 GlInsideNodeIt< KrImNode* > it( root->child );
390
391 for( it.Begin(); !it.Done(); it.Next() )
392 {
393 // Check the depths and the parents.
394 GLASSERT( it.CurrentData()->Parent() == root );
395
396 if ( !it.CurrentNode()->prev->IsSentinel() )
397 GLASSERT( it.CurrentNode()->prev->data->ZDepth() <= it.CurrentData()->ZDepth() );
398 if ( !it.CurrentNode()->next->IsSentinel() )
399 GLASSERT( it.CurrentNode()->next->data->ZDepth() >= it.CurrentData()->ZDepth() );
400
401 ValidateTree( it.CurrentData() );
402 }
403 }
404 #endif
405
406
HitTest(int x,int y,KrImNode * startHere,int flags,std::vector<KrImage * > * outputArray,int * window)407 void KrImageTree::HitTest( int x, int y, KrImNode* startHere, int flags, std::vector<KrImage*>* outputArray, int* window )
408 {
409 outputArray->resize(0);
410 *window = -1;
411
412 // Figure out which window this hit-test is in.
413 for( int i=0; i<engine->NumWindows(); ++i )
414 {
415 Rectangle2I bounds = engine->ScreenBounds( i );
416 if ( bounds.Intersect( x, y ) )
417 {
418 *window = i;
419 break;
420 }
421 }
422
423 if ( !startHere )
424 startHere = offsetRoot;
425
426 if ( *window >= 0 )
427 {
428 HitTestRec( startHere, x, y, flags, outputArray, *window );
429 }
430 }
431
432
HitTestRec(KrImNode * node,int x,int y,int flags,std::vector<KrImage * > * output,int window)433 bool KrImageTree::HitTestRec( KrImNode* node, int x, int y, int flags, std::vector<KrImage*>* output, int window )
434 {
435 // Check our composite bounds, if these don't intersect, no dice.
436 if ( !node->CompositeBounds( window ).Intersect( x, y ) )
437 {
438 return false;
439 }
440
441 // Walk the children first, since they are on top of the parent.
442 // Note that we need to go in reverse order, so the object
443 // closest to the user is clicked first.
444 GlInsideNodeIt< KrImNode* > it( node->child );
445
446 for( it.Last(); !it.Done(); it.Prev() )
447 {
448 // HitTestRec( it.CurrentData(), x, y, flags, output, window );
449 bool abort = HitTestRec( it.CurrentData(), x, y, flags, output, window );
450 if ( abort == true )
451 return true;
452 }
453
454 if ( node->ToImage() )
455 {
456 // Ignore transparent and invisible items.
457 // if ( node->CompositeCForm().Alpha() > 0 )
458 // // // || flags & HIT_TRANSPARENT_IMAGES )
459 // {
460 // if ( node->IsVisible() )
461 // {
462 bool hit = node->HitTest( x, y, flags, output, window );
463 if ( hit )
464 {
465 if ( flags & GET_ALL_HITS )
466 return false; // don't abort: get everything.
467 else
468 return true; // return on the first hit.
469 }
470 // }
471 // }
472 }
473 return false; // keep going.
474 }
475
476
CheckSiblingCollision(KrImNode * node,std::vector<KrImage * > * outputArray,int window)477 bool KrImageTree::CheckSiblingCollision( KrImNode* node, std::vector<KrImage*>* outputArray, int window )
478 {
479 bool ret = false;
480 outputArray->resize(0);
481
482 KrImNode* parent = node->Parent();
483 KrImage* checkThis = node->ToImage();
484 if ( parent
485 && checkThis )
486 {
487 GlInsideNodeIt< KrImNode* > it( parent->child );
488
489 for( it.Begin(); !it.Done(); it.Next() )
490 {
491 if ( it.CurrentData() != checkThis
492 && it.CurrentData()->ToImage()
493 && checkThis->CheckCollision( it.CurrentData()->ToImage(), window ) )
494 {
495 ret = true;
496 outputArray->push_back( it.CurrentData()->ToImage() );
497 }
498 }
499 }
500 return ret;
501 }
502
503
CheckChildCollision(KrImNode * check,KrImNode * parent,std::vector<KrImage * > * outputArray,int window)504 bool KrImageTree::CheckChildCollision( KrImNode* check, KrImNode* parent, std::vector<KrImage*>* outputArray, int window )
505 {
506 bool ret = false;
507 outputArray->resize(0);
508
509 KrImage* checkThis = check->ToImage();
510 if ( checkThis
511 && parent->CompositeBounds( window ).Intersect( checkThis->Bounds( window ) ) )
512 {
513 GlInsideNodeIt< KrImNode* > it( parent->child );
514
515 for( it.Begin(); !it.Done(); it.Next() )
516 {
517 if ( it.CurrentData() != checkThis
518 && it.CurrentData()->ToImage()
519 && checkThis->CheckCollision( it.CurrentData()->ToImage(), window ) )
520 {
521 ret = true;
522 outputArray->push_back( it.CurrentData()->ToImage() );
523 }
524 }
525 }
526 return ret;
527 }
528
529
CheckAllCollision(KrImNode * checkThis,std::vector<KrImage * > * outputArray,int window)530 bool KrImageTree::CheckAllCollision( KrImNode* checkThis, std::vector<KrImage*>* outputArray, int window )
531 {
532 bool ret = false;
533 outputArray->resize(0);
534
535 if ( checkThis->ToImage() )
536 CheckAllCollisionWalk( &ret, Root(), checkThis->ToImage(), outputArray, window );
537 return ret;
538 }
539
540
CheckAllCollisionWalk(bool * hit,KrImNode * node,KrImage * checkThis,std::vector<KrImage * > * outputArray,int window)541 void KrImageTree::CheckAllCollisionWalk( bool* hit, KrImNode* node, KrImage* checkThis, std::vector<KrImage*>* outputArray, int window )
542 {
543 // check all the children:
544 GlInsideNodeIt< KrImNode* > it( node->child );
545
546 for( it.Begin(); !it.Done(); it.Next() )
547 {
548 if ( it.CurrentData() != checkThis
549 && it.CurrentData()->ToImage()
550 && checkThis->CheckCollision( it.CurrentData()->ToImage(), window ) )
551 {
552 *hit = true;
553 outputArray->push_back( it.CurrentData()->ToImage() );
554 }
555
556 if ( it.CurrentData()->CompositeBounds( window ).Intersect( checkThis->Bounds( window ) ) )
557 CheckAllCollisionWalk( hit, it.CurrentData(), checkThis, outputArray, window );
558 }
559 }
560
561