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