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 "canvas.h"
33 #include "engine.h"
34 #ifdef KYRA_SUPPORT_OPENGL
35 	#include "SDL_opengl.h"
36 	#include "ogltexture.h"
37 #endif
38 #include "canvasresource.h"
39 #include "../../grinliz/glgeometry.h"
40 
41 #ifdef _MSC_VER
42 // Yes, we use 'this' in a member initialization list. Quote from
43 // Microsoft help: "This is a level-1 warning when Microsoft extensions
44 // are enabled (/Ze) and a level-4 warning otherwise." I'm sure that
45 // made sense to someone. -lee
46 #pragma warning ( disable : 4355 )
47 #endif
48 
49 using namespace grinliz;
50 
51 
52 // Check some SDL stuff here:
53 #if !( SDL_VERSION_ATLEAST(1, 2, 0) )
54 	#error Kyra requires SDL 1.2.0 or greater.
55 #endif
56 
57 
58 /*static*/ int KrEngine::maxOglTextureSize = 0;
59 
KrEngine(SDL_Surface * _screen)60 KrEngine::KrEngine( SDL_Surface* _screen )
61 	: paintInfo( _screen )
62 {
63 	Rectangle2I bounds;
64 	bounds.Set( 0, 0, _screen->w - 1, _screen->h - 1 );
65 
66 	Init( _screen, 1, &bounds, 0 );
67 }
68 
69 
KrEngine(SDL_Surface * _screen,const Rectangle2I & bounds,const KrRGBA * extra)70 KrEngine::KrEngine( SDL_Surface* _screen, const Rectangle2I& bounds, const KrRGBA* extra )
71 	: paintInfo( _screen )
72 {
73 	Init( _screen, 1, &bounds, extra );
74 }
75 
76 
KrEngine(SDL_Surface * _screen,int _nWindows,const Rectangle2I * bounds,const KrRGBA * extra)77 KrEngine::KrEngine( SDL_Surface* _screen, int _nWindows, const Rectangle2I* bounds, const KrRGBA* extra )
78 	  : paintInfo( _screen )
79 {
80 	Init( _screen, _nWindows, bounds, extra );
81 }
82 
83 
~KrEngine()84 KrEngine::~KrEngine()
85 {
86 	// Note that the tree must be deleted before the vault.
87 	delete tree;
88 	delete vault;
89 }
90 
Restart(SDL_Surface * _screen,int _nWindows,const Rectangle2I * bounds,const KrRGBA * extra)91 void KrEngine::Restart( SDL_Surface* _screen,
92 						int _nWindows,
93 						const Rectangle2I* bounds,
94 						const KrRGBA* extra )
95 {
96 	GLASSERT(bounds);
97 
98 	KrPaintInfo paintNew(_screen);
99 	memcpy(&paintInfo, &paintNew, sizeof(paintInfo));
100 
101 	screen = _screen;
102 	nWindows = _nWindows;
103 
104 	windowBounds.Set( 0, 0, screen->w-1, screen->h-1 );
105 	extraBackground.Set( 0, 0, 0, 255 );
106 
107 	// If this assert is thrown, increase KR_MAX_WINDOWS to an
108 	// appropriate value and re-compile.
109 	GLASSERT( nWindows <= KR_MAX_WINDOWS );
110 
111 	// fullScreenUpdate draws *outside* of the windows.
112 	needFullScreenUpdate = ( extra != 0 );
113 
114 	if ( extra )
115 	{
116 		extraBackground = *extra;
117 
118 		if ( !paintInfo.OpenGL() )
119 		{
120 			U32 color = SDL_MapRGB( screen->format, extra->c.red, extra->c.green, extra->c.blue );
121 			SDL_FillRect( screen, 0, color );
122 		}
123 	}
124 
125 	int i;
126 	for( i=0; i<nWindows; ++i )
127 	{
128 		// Default to filling the background to black.
129 		fillBackground[i] = true;
130 		backgroundColor[i].Set( 0, 0, 0, 255 );
131 
132 		// Set the screenbounds to a window.
133 		screenBounds[i] = bounds[i];
134 
135 		GLASSERT( bounds[i].min.x >= 0 );
136 		GLASSERT( bounds[i].min.y >= 0 );
137 		GLASSERT( bounds[i].max.x < screen->w );
138 		GLASSERT( bounds[i].max.y < screen->h );
139 
140 		// Really aweful bugs result if the indivual windows
141 		// aren't clipped to the screen.
142 		screenBounds[i].DoClip( windowBounds );
143 
144 		// Initialize the DR to repaint everything and clip to the screen
145 		// IMPORTANT: Set clip before adding any rectangles.
146 		dirtyRectangle[i].SetClipping( screenBounds[i] );
147 		dirtyRectangle[i].AddRectangle( screenBounds[i] );
148 	}
149 
150 	// Check that none overlap.
151 	#ifdef DEBUG
152 		int j;
153 		for( i=0; i<nWindows; ++i )
154 		{
155 			for( j=i+1; j<nWindows; ++j )
156 			{
157 				GLASSERT( !bounds[i].Intersect( bounds[j] ) );
158 			}
159 		}
160 	#endif
161 
162 	if ( paintInfo.openGL )
163 		InitOpenGL();
164 
165 	SDL_EnableUNICODE( true );
166 }
167 
168 
Init(SDL_Surface * _screen,int _nWindows,const Rectangle2I * bounds,const KrRGBA * extra)169 void KrEngine::Init(	SDL_Surface* _screen,
170 						int _nWindows,
171 						const Rectangle2I* bounds,
172 						const KrRGBA* extra )
173 {
174 	splashStart = 0;
175 	splashVault = 0;
176 	splash = splashText = 0;
177 
178 	Restart(_screen, _nWindows, bounds, extra);
179 
180 	// Initialization of stuff that has "this" usage.
181 	vault = new KrResourceVault;
182 	tree = new KrImageTree( this );
183 
184 	//start out with about 64 rects, shouldn't need much more than that for most instances
185 //	nNumSDLRects = 16;
186 //	sdlRects = new SDL_Rect[16];
187 	sdlRects.resize( 16 );
188 //	memset( sdlRects.Memory(), 0, sizeof(SDL_Rect) * sdlRects.Count() );
189 }
190 
191 
InitOpenGL()192 void KrEngine::InitOpenGL()
193 {
194 	#ifdef KYRA_SUPPORT_OPENGL
195 	GLASSERT( KrTextureManager::TextureIndex() == 0 );
196 
197 	int w = windowBounds.Width();
198 	int h =  windowBounds.Height();
199 	glViewport( 0, 0, w, h );
200 	glClearColor( extraBackground.Redf(),
201 				  extraBackground.Greenf(),
202 				  extraBackground.Bluef(),
203 				  255.0f );
204 
205 	//glClearDepth(1.0);
206 
207 	// The depth buffer isn't actually used. This only occured to me at the end:
208 	// Kyra sorts everything and draws in order...so you don't need the depth testing.
209 	// Oddly...if you turn this on, it creates a bug in 16bit mode. I'de really
210 	// like to understand that.  -- lee
211 	//	glDepthFunc(GL_LESS);
212 	//	glEnable(GL_DEPTH_TEST);	// disabled by default.
213 
214 	glDisable( GL_DEPTH_TEST );		// Don't depth test
215 	glDepthMask( GL_FALSE );		// Don't write the depth buffer
216 
217 	// Done: after reading the above comment...we don't need the openGLZ parameter.
218 	// Implemented ignoring the z-buffer.
219 
220 	glShadeModel(GL_FLAT);		// Don't need smooth for 2D.
221 
222 	// Create and reset the projection matrix
223 	glMatrixMode(GL_PROJECTION);
224 
225 	// This matrix is set up to map pixels to screen the same way the bitbuffer does.
226 	// Trick from the OpenGL docs.
227 
228 	glLoadIdentity();
229 	glOrtho( 0, w,
230 			 h, 0,
231 			 -1.0f, 1.0f );
232 
233 	glMatrixMode(GL_MODELVIEW);
234 	glLoadIdentity();
235 	glTranslatef(0.375, 0.375, 0.0);
236 
237 	// A more reasonable mode to start in.
238 	glMatrixMode(GL_MODELVIEW);
239 
240 	//glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
241 	glClear( GL_COLOR_BUFFER_BIT );
242 
243 	// Enable the texturing and the blending mode needed.
244 	glEnable( GL_TEXTURE_2D );
245 	glEnable( GL_BLEND );
246 	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
247 
248 	GLASSERT( glGetError() == GL_NO_ERROR );
249 	#endif
250 }
251 
252 
FillBackground(const KrRGBA * fillColor)253 void KrEngine::FillBackground( const KrRGBA* fillColor )
254 {
255 	for( int i=0; i<nWindows; ++i )
256 	{
257 		FillBackgroundWindow( i, fillColor );
258 	}
259 }
260 
261 
FillBackgroundWindow(int i,const KrRGBA * fillColor)262 void KrEngine::FillBackgroundWindow( int i, const KrRGBA* fillColor )
263 {
264 	if ( fillColor )
265 	{
266 		fillBackground[i]   = true;
267 		backgroundColor[i]	= *fillColor;
268 		//SDL_MapRGB( screen->format, fillColor->c.red, fillColor->c.green, fillColor->c.blue );
269 	}
270 	else
271 	{
272 		fillBackground[i] = false;
273 		backgroundColor[i].Set( 0, 0, 0 );
274 	}
275 	dirtyRectangle[i].AddRectangle( screenBounds[i] );
276 }
277 
278 
279 //void KrEngine::ClearScreen( int red, int green, int blue )
280 //{
281 //	U32 color = SDL_MapRGB( screen->format, red, green, blue );
282 //	SDL_FillRect( screen, 0, color );
283 //
284 //	SDL_UpdateRect( screen, 0, 0, 0, 0 );
285 //	Tree()->Root()->Invalidate( KR_ALL_WINDOWS );
286 //}
287 
288 
289 
290 #if defined( DRAWDEBUG_RLE ) || defined( DRAWDEBUG_BLTRECTS )
291 	extern int debugFrameCounter;
292 #endif
293 
294 
UpdateScreen(std::vector<Rectangle2I> * rectArray)295 void KrEngine::UpdateScreen( std::vector< Rectangle2I >* rectArray )
296 {
297 	if ( paintInfo.openGL )
298 	{
299 		SDL_GL_SwapBuffers();
300 	}
301 	else
302 	{
303 		if ( rectArray->size() == 0 )
304 			return;
305 
306 //		if(	rectArray->Count() > sdlRects.AllocatedSize() )
307 //		{
308 //			sdlRects.ResizePower2( rectArray->Count() );
309 //
310 //			//resize the array, to next multiple of 8
311 //			nNumSDLRects = rectArray->Count() + (8 - (rectArray->Count() % 8));
312 //			sdlRects = new SDL_Rect[nNumSDLRects];
313 //			memset(sdlRects, 0, sizeof(SDL_Rect) * nNumSDLRects);
314 //		}
315 
316 		sdlRects.resize( rectArray->size() );
317 		for( unsigned i=0; i<rectArray->size(); i++ )
318 		{
319 			sdlRects[i].x = rectArray->at( i ).min.x;
320 			sdlRects[i].y = rectArray->at( i ).min.y;
321 			sdlRects[i].w = rectArray->at( i ).Width();
322 			sdlRects[i].h = rectArray->at( i ).Height();
323 
324 			GLASSERT( sdlRects[i].x >= 0 );
325 			GLASSERT( sdlRects[i].y >= 0 );
326 			GLASSERT( sdlRects[i].w > 0 && sdlRects[i].w <= screen->w );
327 			GLASSERT( sdlRects[i].h > 0 && sdlRects[i].h <= screen->h );
328 			GLASSERT( sdlRects[i].x + sdlRects[i].w <= screen->w );
329 			GLASSERT( sdlRects[i].y + sdlRects[i].h <= screen->h );
330 		}
331 
332 //		GLOUTPUT( "Updating %d rects\n", rectArray->Count() );
333 		SDL_UpdateRects(screen, rectArray->size(), &sdlRects[0] );
334 	}
335 }
336 
337 
Draw(bool updateRect,std::vector<Rectangle2I> * _rectangles)338 void KrEngine::Draw( bool updateRect, std::vector< Rectangle2I >* _rectangles )
339 {
340 	std::vector< Rectangle2I > rectArrayOnStack;
341 
342 	#if defined( DRAWDEBUG_RLE ) || defined( DRAWDEBUG_BLTRECTS )
343 		debugFrameCounter++;
344 	#endif
345 
346 //	GLOUTPUT( "Engine::Draw Walk\n" );
347 	tree->Walk();
348 
349 		// We either use the passed in rectangles,
350 		// or the one here on the stack. Set the pointer
351 		// rectArray to the right thing.
352 	std::vector< Rectangle2I >* rectArray = ( _rectangles ) ? _rectangles : &rectArrayOnStack;
353 	rectArray->resize(0);
354 
355 	if ( !paintInfo.openGL )
356 	{
357 		// Queue up the rectangles that will be used to blit to screen:
358 
359 		if ( needFullScreenUpdate )
360 		{
361 			needFullScreenUpdate = false;
362 			Rectangle2I rect;
363 			rect.Set( 0, 0, screen->w-1, screen->h-1 );
364 			rectArray->push_back( rect );
365 		}
366 		else
367 		{
368 			for ( int i=0; i<nWindows; ++i )
369 			{
370 				for ( int j=0; j<dirtyRectangle[i].NumRect(); ++j )
371 				{
372 					rectArray->push_back( dirtyRectangle[i].Rect(j) );
373 				}
374 			}
375 		}
376 	}
377 
378 	if ( paintInfo.openGL )
379 	{
380 		#ifdef KYRA_SUPPORT_OPENGL
381 
382 		// OpenGL drawing
383 		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
384 
385 		for( int j=0; j<nWindows; ++j )
386 		{
387 			if ( fillBackground[j] )
388 			{
389 				glBindTexture( GL_TEXTURE_2D, 0 );
390 				glColor4f( backgroundColor[j].Redf(), backgroundColor[j].Greenf(), backgroundColor[j].Bluef(), 1.0f );
391 				glBegin( GL_QUADS );
392 				{
393 					glVertex3i( screenBounds[j].min.x, screenBounds[j].min.y, 0 );
394 					glVertex3i( screenBounds[j].min.x + screenBounds[j].Width(), screenBounds[j].min.y, 0 );
395 					glVertex3i( screenBounds[j].min.x + screenBounds[j].Width(), screenBounds[j].min.y + screenBounds[j].Height(), 0 );
396 					glVertex3i( screenBounds[j].min.x, screenBounds[j].min.y + screenBounds[j].Height(), 0 );
397 				}
398 				glEnd();
399 			}
400 
401 			bool clipping = ( screenBounds[j] != windowBounds );
402 
403 			if ( clipping )
404 			{
405 				glEnable(GL_CLIP_PLANE0);
406 				glEnable(GL_CLIP_PLANE1);
407 				glEnable(GL_CLIP_PLANE2);
408 				glEnable(GL_CLIP_PLANE3);
409 
410 				double plane0[4] = { 1.0, 0.0, 0.0, -screenBounds[j].min.x };
411 				double plane1[4] = { -1.0, 0.0, 0.0, (screenBounds[j].min.x + screenBounds[j].Width() ) };
412 				double plane2[4] = { 0.0, 1.0, 0.0, -screenBounds[j].min.y };
413 				double plane3[4] = { 0.0, -1.0, 0.0, (screenBounds[j].min.y + screenBounds[j].Height() ) };
414 
415 				glClipPlane( GL_CLIP_PLANE0, plane0 );
416 				glClipPlane( GL_CLIP_PLANE1, plane1 );
417 				glClipPlane( GL_CLIP_PLANE2, plane2 );
418 				glClipPlane( GL_CLIP_PLANE3, plane3 );
419 			}
420 
421 			tree->DrawWalk( screenBounds[j], &paintInfo, j );
422 
423 			if ( clipping )
424 			{
425 				glDisable(GL_CLIP_PLANE0);
426 				glDisable(GL_CLIP_PLANE1);
427 				glDisable(GL_CLIP_PLANE2);
428 				glDisable(GL_CLIP_PLANE3);
429 			}
430 			dirtyRectangle[j].Clear();
431 		}
432 		UpdateScreen( 0 );
433 		#else
434 			// No openGl support, but openGl surface used
435 			GLASSERT( 0 );
436 		#endif
437 	}
438 	else
439 	{
440 		// Bitmap drawing.
441 
442 		// Draw the background, if necessary. Then
443 		// do a draw walk for every DR.
444 		for( int win=0; win<nWindows; ++win  )
445 		{
446 			for( int i=0; i<dirtyRectangle[win].NumRect(); ++i )
447 			{
448 				const Rectangle2I& rect = dirtyRectangle[win].Rect( i );
449 
450 				// Draw the background.
451 				//GLASSERT( fillBackground[j] );
452 				if ( fillBackground[win] )
453 				{
454 					SDL_Rect sdlrect = { rect.min.x, rect.min.y, rect.Width(), rect.Height() };
455 					U32 sdlColor = SDL_MapRGB( screen->format,	backgroundColor[win].c.red,
456 																backgroundColor[win].c.green,
457 																backgroundColor[win].c.blue );
458 					//GLASSERT( sdlColor == 0 );
459 					SDL_FillRect( screen, &sdlrect, sdlColor );
460 				}
461 				tree->DrawWalk( rect, &paintInfo, win );
462 
463 				/*
464 				#ifdef DRAWDEBUG_BLTRECTS
465 					KrPainter painter( &paintInfo );
466 					painter.DrawBox( rect.xmin, rect.ymin, rect.Width(), rect.Height(), 200, 0, 0 );
467 				#endif
468 				*/
469 			}
470 
471 			#ifdef DRAWDEBUG_BLTRECTS
472 				dirtyRectangle[win].DrawRects( screen );
473 			#endif
474 			dirtyRectangle[win].Clear();
475 		}
476 
477 		// The windows and DRs have been walked. Now transfer to physical screen.
478 		if ( updateRect )
479 		{
480 			// Use the composite list of rectangles.
481 			UpdateScreen( rectArray );
482 		}
483 	}
484 }
485 
486 
QueryRenderDesc(std::string * desc)487 void KrEngine::QueryRenderDesc( std::string* desc )
488 {
489 	QueryRenderDesc( screen, desc );
490 }
491 
492 
QueryRenderDesc(SDL_Surface * screen,std::string * desc)493 /* static */ void KrEngine::QueryRenderDesc( SDL_Surface* screen, std::string* desc )
494 {
495 	char buf[ 256 ];
496 	sprintf( buf, "v%d.%d.%d %dbbp Fullscreen=%d %s ",
497 					KyraVersionMajor, KyraVersionMinor, KyraVersionBuild,
498 					screen->format->BitsPerPixel,
499 					( screen->flags & SDL_FULLSCREEN ) ? 1 : 0,
500 					#ifdef WIN32
501 						"Win32"
502 					#elif defined ( linux )
503 						"Linux"
504 					#else
505 						"UnknownPlatform"
506 					#endif
507 		);
508 
509 	char render[256];
510 	#ifdef KYRA_SUPPORT_OPENGL
511 	if ( screen->flags & SDL_OPENGL )
512 	{
513 		const unsigned char* vendor   = glGetString( GL_VENDOR );
514 		const unsigned char* renderer = glGetString( GL_RENDERER );
515 		const unsigned char* version  = glGetString( GL_VERSION );
516 		sprintf( render, "OpenGL render: Vendor: '%s'  Renderer: '%s'  Version: '%s'",
517 					vendor, renderer, version );
518 	} else
519 	#endif
520 	{
521 		sprintf( render, "Software render" );
522 	}
523 	*desc = buf;
524 	desc->append( render );
525 }
526 
527 
GetWindowFromPoint(int x,int y)528 int KrEngine::GetWindowFromPoint( int x, int y )
529 {
530 	for( int i=0; i<nWindows; ++i )
531 	{
532 		if ( screenBounds[i].Intersect( x, y ) )
533 			return i;
534 	}
535 	return -1;
536 }
537 
538 /*
539 void KrEngine::StartSplash( U32 msec )
540 {
541 	splashStart = msec;
542 	GLASSERT( splashVault == 0 );
543 
544 	splashVault = new KrResourceVault();
545 	splashVault->LoadDatFileFromMemory( splash_DAT, splash_SIZE );
546 
547 	KrSpriteResource* splashRes     = splashVault->GetSpriteResource( "splash" );
548 	KrSpriteResource* splashTextRes = splashVault->GetSpriteResource( "splashText" );
549 
550 	splash = new KrSprite( splashRes );
551 	splashText = new KrSprite( splashTextRes );
552 
553 	Rectangle2I bounds, boundsText;
554 	splash->QueryBoundingBox( &bounds, 0 );
555 	splashText->QueryBoundingBox( &boundsText, 0 );
556 
557 	tree->AddNode( 0, splash );
558 	tree->AddNode( 0, splashText );
559 
560 	splash->SetPos( screenBounds[0].Width() / 2  - bounds.Width() / 2,
561 					screenBounds[0].Height() / 4 - bounds.Height() / 2 );
562 	splash->SetZDepth( 5000 );
563 
564 	splashText->SetPos( screenBounds[0].Width() / 2  - boundsText.Width() / 2,
565 						splash->Y() + bounds.Height() + 20 );
566 
567 	splashText->SetZDepth( 5000 );
568 }
569 
570 
571 bool KrEngine::UpdateSplash( U32 msec )
572 {
573 	U32 delta = msec - splashStart;
574 
575 	KrColorTransform xcolor;
576 
577 	if ( delta < 1000 )
578 	{
579 		xcolor.SetAlpha( 255 * delta / 1000 );
580 	}
581 	// TODO - I got a crash here becayse the noded had already been deleted at the end of a splash screen cycle. This problem should be fixed properly in the caller, so this is a temporary hack - comment by CRC.
582 	if (splash != 0)
583 	splash->SetColor( xcolor );
584 	if (splashText != 0)
585 	splashText->SetColor( xcolor );
586 
587 	return ( delta >= 2000 );
588 }
589 
590 
591 void KrEngine::EndSplash()
592 {
593   if (splash != 0)
594 	tree->DeleteNode( splash );
595   if (splashText != 0)
596 	tree->DeleteNode( splashText );
597 	delete splashVault;
598 
599 	splash = 0;
600 	splashText = 0;
601 	splashVault = 0;
602 }
603 */
604 
605