00001 #include "global.h"
00002 #include "MercuryDisplay.h"
00003 #include "MercuryLog.h"
00004 #include "MercuryUtil.h"
00005 #include "MercuryINI.h"
00006 #include "MercuryMath.h"
00007 #include "MercuryTexture.h"
00008 #include "BMPWriter.h"
00009
00010 #if !defined(_EE)
00011 #if !defined(USE_FRAMEBUFFER)
00012 #include "MercuryOGL.h"
00013 #endif
00014 #include "MercuryDisplaySoftwareC.h"
00015 #else
00016 #include "MercuryDisplayEE.h"
00017 #endif
00018
00019
00020 #include "MercuryInput.h"
00021
00022 #if defined (WIN32)
00023 #include "Win32Window.h"
00024 #if defined( USE_D3D )
00025 #include "MercuryDisplay_D3D.h"
00026 #endif
00027 #else
00028 #if !defined(_EE)
00029 #if !defined(USE_FRAMEBUFFER)
00030 #include "MercuryWindowSDL.h"
00031 #else
00032 #include "MercuryWindowFB.h"
00033 #endif
00034 #else
00035 #include "MercuryWindowEE.h"
00036 #endif
00037 #endif
00038
00039 #if defined(_MSC_VER) && (_MSC_VER > 1100)
00040 #pragma warning (disable : 4786) //char truncation dealing with MStrings
00041 #endif
00042
00043 MercuryMatrixStack WorldStack;
00044
00045 MercuryMatrixStack TextureStack;
00046
00047 KeyMappingWithCode( print_scr, "0-316" );
00048
00049 REGISTER_STATEMENT_TO_MESSAGE( quit, quit, DISPLAY->CloseNextCycle(); )
00050 REGISTER_STATEMENT_TO_COMMAND( quit, DISPLAY->CloseNextCycle(); )
00051
00052 REGISTER_STATEMENT_TO_MESSAGE( screenshot, mappedinput,
00053 InputMessageStruct c(args);
00054 if (c.code == print_scr && c.type == IET_RELEASE )
00055 DISPLAY->TakeScreenShot();
00056 )
00057
00058 MercuryDisplay::MercuryDisplay()
00059 :m_renderObjects(200)
00060 {
00061 m_framesDrawn = 0;
00062 m_verticesDrawn = 0;
00063 m_bCloseNextCycle = 0;
00064 m_maxTextures = 1;
00065 m_maxLights = 0;
00066 m_initalized = false;
00067 m_maxDrawBuffers = m_maxVertexAttributes = m_maxTextureCoordinates =
00068 m_maxShaderVertUniformComponents = m_maxShaderFragUniformComponents =
00069 m_shaderLanngVersion = 0;
00070 m_maxAnisotropy = 0;
00071
00072 m_based_width = PREFSMAN->GetValueI( "Rendering", "BasedWidth", InitialDisplayProperties::InitialBasedWidth, true );
00073 m_based_height = PREFSMAN->GetValueI( "Rendering", "BasedHeight", InitialDisplayProperties::InitialBasedHeight, true );
00074
00075 m_window = NULL;
00076
00077 m_disableTextures = false;
00078 m_useMaterialColors = true;
00079 }
00080
00081 MercuryDisplay::~MercuryDisplay()
00082 {
00083 SAFE_DELETE(m_window);
00084 m_globalState.pop_back();
00085 }
00086
00087 void* MercuryDisplay::ThreadCallback(void* display)
00088 {
00089 ((MercuryDisplay*)display)->ThreadLoop();
00090 return NULL;
00091 }
00092
00093 void MercuryDisplay::CreateThread()
00094 {
00095 #if !defined( NO_THREADS )
00096 m_thread.Create(MercuryDisplay::ThreadCallback, this);
00097 #endif
00098 }
00099
00100 void MercuryDisplay::ThreadLoop()
00101 {
00102 if ( !DISPLAY->Init(
00103 PREFSMAN->GetValueS( "Window", "Name", "Mercury Engine", true ).c_str(),
00104 PREFSMAN->GetValueI( "Window", "Width", 640, true ),
00105 PREFSMAN->GetValueI( "Window", "Height", 480, true ),
00106 PREFSMAN->GetValueI( "Window", "BPP", 32, true ),
00107 PREFSMAN->GetValueB( "Window", "Fullscreen", false, true )
00108 ) )
00109 {
00110 LOG.Warn("Failed to initalize display.");
00111 ASSERT(!"Failed to initalize display.");
00112 }
00113
00114 while(true)
00115 {
00116 }
00117 }
00118
00119 bool MercuryDisplay::Init(const MString& name, unsigned int width, unsigned int height, unsigned short bpp, bool fullscreen)
00120 {
00121 m_initalized = true;
00122 m_view.Identity();
00123
00124 #if defined (WIN32)
00125 AttachWindow(new Win32Window);
00126 #else
00127
00128 #if !defined(_EE)
00129 #if !defined(USE_FRAMEBUFFER)
00130 AttachWindow(new MercuryWindowSDL);
00131 #else
00132 AttachWindow(new MercuryWindowFB);
00133 #endif
00134 #else
00135 AttachWindow(new MercuryWindowEE);
00136 #endif
00137
00138 #endif
00139 if ( !MakeWindow(name, width, height, bpp, fullscreen) )
00140 {
00141 LOG.Warn("Failed to make window.");
00142 return false;
00143 }
00144
00145 MercuryGLState gls;
00146 gls.Enable(MGLS_ALL);
00147 m_globalState.push_front(gls);
00148 m_currentState = gls;
00149 gls.Disable(MGLS_ALL);
00150 SetStates(gls);
00151
00152 return true;
00153 }
00154
00155 bool MercuryDisplay::Update(const float dTime)
00156 {
00157 double age = m_timer.Age();
00158
00159 if (m_window != NULL)
00160 if (!m_window->Update(dTime))
00161 return false;
00162
00163 if ( m_window->HasFocus() != m_bLastHadFocus )
00164 {
00165 m_bLastHadFocus = m_window->HasFocus();
00166 MESSAGEMAN->BroadcastMessage( "changefocus", PStack( PSElement( m_bLastHadFocus ) ) );
00167 }
00168
00169 if (age >= 0.5)
00170 {
00171 m_timer.Touch();
00172 PStack stats;
00173 stats.PushItemBack( PSElement ((float)(m_verticesDrawn/age)) );
00174 stats.PushItemBack( PSElement ((float)(m_verticesDrawn/(float)m_framesDrawn)) );
00175 stats.PushItemBack( PSElement ((float)(m_framesDrawn/age)) );
00176
00177
00178 MESSAGEMAN->PostSystemMessage( "RenderStats", stats, 0 );
00179
00180 m_framesDrawn = 0;
00181 m_verticesDrawn = 0;
00182 }
00183
00184 return !m_bCloseNextCycle;
00185 }
00186
00187 void MercuryDisplay::Resize(int width, int height)
00188 {
00189 if (height <= 0)
00190 height = 1;
00191
00192 if (width <= 0)
00193 width = 1;
00194
00195 m_window->SetDemensions(width, height);
00196
00198 Viewport(0, 0, width, height);
00199
00200
00201 if ( !m_window->IsMinimized() )
00202 {
00203 if ((width > 1) && (height > 1))
00204 {
00205 DisplayDimensions dimensions = GetDimensions();
00206 MESSAGEMAN->BroadcastMessage( "DisplayResized", PStack( PSElement( dimensions.height ), PSElement( dimensions.width) ) );
00207 }
00208 }
00209 }
00210
00211 void MercuryDisplay::SetProjection(const Projection& projection)
00212 {
00213 m_projection = projection;
00214 ResetFrustumClippingPlanes();
00215 SendProjectionMatrixData();
00216 }
00217
00218 bool MercuryDisplay::AttachWindow(MercuryWindow* window)
00219 {
00220 SAFE_DELETE(m_window);
00221 m_window = window;
00222
00223 return true;
00224 }
00225
00226 bool MercuryDisplay::MakeWindow(const char* title, int width, int height, int bits, bool fullscreenflag)
00227 {
00228 RendererInfo();
00229 Resize(m_window->GetWidth(), m_window->GetHeight());
00230
00231 return true;
00232 }
00233
00234 DisplayDimensions MercuryDisplay::GetDimensions() const
00235 {
00236 DisplayDimensions dimensions;
00237 dimensions.width = (float)m_window->GetWidth();
00238 dimensions.height = (float)m_window->GetHeight();
00239 return dimensions;
00240 }
00241
00242 DisplayDimensions MercuryDisplay::GetBasedDimensions() const
00243 {
00244 DisplayDimensions dimensions;
00245 dimensions.width = (float)m_based_width;
00246 dimensions.height = (float)m_based_height;
00247 return dimensions;
00248 }
00249
00250 void MercuryDisplay::LookAt( float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ )
00251 {
00252 MercuryPoint eye(eyeX,eyeY,eyeZ);
00253 MercuryPoint center(centerX,centerY,centerZ);
00254 MercuryPoint up(upX,upY,upZ);
00255
00256 LookAt(eye, center, up);
00257 }
00258
00259 MercuryPoint MercuryDisplay::GetLastCameraPosition( )
00260 {
00261 return m_pLastCameraPos;
00262 }
00263
00264 void MercuryDisplay::LookAt(const MercuryPoint& eye, const MercuryPoint& center,
00265 const MercuryPoint& up)
00266 {
00267 LookAtInternals lai = CalculateLookAtMatrix(eye, center, up, m_view);
00268 m_pLastCameraPos = eye;
00269 SetupClippingPlanes( eye, lai.m_direction, lai.m_up, lai.m_x );
00270 }
00271
00272 LookAtInternals CalculateLookAtMatrix(const MercuryPoint& eye, const MercuryPoint& center,
00273 const MercuryPoint& up, MercuryMatrix& m)
00274 {
00275 MercuryPoint viewDirection = (center-eye).Normalize();
00276
00277 MercuryPoint viewX = viewDirection.CrossProduct( up.Normalize() );
00278 viewX.NormalizeSelf();
00279
00280 MercuryPoint viewUp = viewX.CrossProduct( viewDirection );
00281 viewUp.NormalizeSelf();
00282
00283 m.Identity();
00284
00285
00286 m[0][0] = viewX.GetX();
00287 m[0][1] = viewX.GetY();
00288 m[0][2] = viewX.GetZ();
00289
00290 m[1][0] = viewUp.GetX();
00291 m[1][1] = viewUp.GetY();
00292 m[1][2] = viewUp.GetZ();
00293
00294 m[2][0] = -viewDirection.GetX();
00295 m[2][1] = -viewDirection.GetY();
00296 m[2][2] = -viewDirection.GetZ();
00297
00298 m.Translate( -eye.GetX(), -eye.GetY(), -eye.GetZ() );
00299
00300 return LookAtInternals(viewDirection, viewX, viewUp);
00301 }
00302
00303 void MercuryDisplay::SetupClippingPlanes(const MercuryPoint& position, MercuryPoint F, MercuryPoint u, MercuryPoint s )
00304 {
00305 u.NormalizeSelf();
00306 F.NormalizeSelf();
00307 s.NormalizeSelf();
00308
00309
00310 m_nViewPlanes[0] = MercuryPoint(0,0,0)-F;
00311
00312 m_nViewPlanes[1] = F;
00313
00314 MercuryMatrix mNormalRot;
00315
00316
00317
00318
00319
00320
00321
00322
00323 mNormalRot.RotateAngAxis( -m_projection.m_frustumInfo.m_fov/2, s.x, s.y, s.z );
00324 VectorRotate( MercuryPoint()-u, mNormalRot, m_nViewPlanes[2] );
00325
00326 mNormalRot.Identity();
00327 mNormalRot.RotateAngAxis( m_projection.m_frustumInfo.m_fov/2, s.x, s.y, s.z );
00328 VectorRotate( u, mNormalRot, m_nViewPlanes[3] );
00329
00330 mNormalRot.Identity();
00331 mNormalRot.RotateAngAxis( m_projection.m_frustumInfo.m_fov/2 * m_projection.m_frustumInfo.m_aspect, u.x, u.y, u.z );
00332 VectorRotate( MercuryPoint()-s, mNormalRot, m_nViewPlanes[4] );
00333
00334 mNormalRot.Identity();
00335 mNormalRot.RotateAngAxis( -m_projection.m_frustumInfo.m_fov/2 * m_projection.m_frustumInfo.m_aspect, u.x, u.y, u.z );
00336 VectorRotate( s, mNormalRot, m_nViewPlanes[5] );
00337
00338 m_pNearPoint = F * m_projection.m_frustumInfo.m_znear + position;
00339 m_pFarPoint = F * m_projection.m_frustumInfo.m_zfar + position;
00340 }
00341
00342 void MercuryDisplay::LookAt(const MQuaternion& q, const MercuryPoint& eye)
00343 {
00344 MercuryMatrix& view = m_view;
00345
00346 q.toMatrix4(view);
00347
00348 view.Translate( -eye.GetX(), -eye.GetY(), -eye.GetZ() );
00349 m_pLastCameraPos = eye;
00350 SetupClippingPlanes( eye, MercuryPoint(0,0,0)-MercuryPoint(view[2]), view[1], view[0] );
00351 }
00352
00353 void MercuryDisplay::ResetFrustumClippingPlanes()
00354 {
00355 m_pLastCameraPos = MercuryPoint(0,0,0);
00356 SetupClippingPlanes( MercuryPoint(0,0,0), MercuryPoint( 0,0,-1 ), MercuryPoint( 0,1, 0 ), MercuryPoint(1,0,0 ) );
00357 }
00358
00359 bool MercuryDisplay::IsVisible( const MercuryPoint & pCenter, float fRadius, const MercuryMatrix& world )
00360 {
00361 if ( fRadius < 0 )
00362 return true;
00363
00364 const float * f = world.Ptr();
00365 MercuryPoint pAdjustedCenter(f[3], f[7], f[11]);
00366
00367 if ( (DotProduct(m_nViewPlanes[0],(pAdjustedCenter-m_pNearPoint))) > fRadius )
00368 return false;
00369 if ( (DotProduct(m_nViewPlanes[1],(pAdjustedCenter-m_pFarPoint))) > fRadius )
00370 return false;
00371 if ( (DotProduct(m_nViewPlanes[2],(pAdjustedCenter-m_pLastCameraPos))) > fRadius )
00372 return false;
00373 if ( (DotProduct(m_nViewPlanes[3],(pAdjustedCenter-m_pLastCameraPos))) > fRadius )
00374 return false;
00375 if ( (DotProduct(m_nViewPlanes[4],(pAdjustedCenter-m_pLastCameraPos))) > fRadius )
00376 return false;
00377 if ( (DotProduct(m_nViewPlanes[5],(pAdjustedCenter-m_pLastCameraPos))) > fRadius )
00378 return false;
00379
00380 return true;
00381 }
00382
00383 void MercuryDisplay::AddLight(MercuryLight* light)
00384 {
00385 ASSERT(light->IsInitalized());
00386 m_lights.push_back(light);
00387 }
00388
00389 void MercuryDisplay::RemoveLight(MercuryLight* light)
00390 {
00391 for( unsigned int i = 0; i < m_lights.size(); ++i)
00392 if (m_lights[i] == light)
00393 {
00394 m_lights.remove(i);
00395 return;
00396 }
00397 }
00398
00399 void MercuryDisplay::TakeScreenShot()
00400 {
00401 RawImageData image;
00402 DISPLAY->ReadFrameBuffer(image);
00403 int ssn = PREFSMAN->GetValueI("Data", "ScreenShotNum", 0, true);
00404
00405 WriteBMP(ssprintf("screenshot%d.bmp", ssn), image);
00406 PREFSMAN->SetValue("Data", "ScreenShotNum", ssprintf("%d", ++ssn));
00407 SAFE_DELETE(image.data);
00408 LOG.Log("screen shot");
00409 }
00410
00411 MercuryDisplay* MakeDisplayDriver()
00412 {
00413 MString sDisplayDriver = PREFSMAN->GetValueS( "Options", "DisplayDriver", "OGL", true );
00414 #if !defined(_EE) && !defined(USE_FRAMEBUFFER)
00415 if ( sDisplayDriver == "OGL" )
00416 return new MercuryOGL();
00417 #if defined( WIN32 )
00418 #if defined( USE_D3D )
00419 if ( sDisplayDriver == "D3D" )
00420 return new MercuryDisplay_D3D();
00421 #endif
00422 #endif
00423 #elif defined(_EE)
00424 return new MercuryDisplayEE();
00425 #endif
00426 if ( sDisplayDriver == "SWC" )
00427 return new MercuryDisplaySoftwareC();
00428
00429 LOG.Warn( "Cannot make display driver. Pick OGL." );
00430 mercury_crash( "Cannot make display driver. Pick OGL." );
00431 return NULL;
00432 }
00433
00434 void MercuryDisplay::AddTranslucentObject(MercuryObject* obj)
00435 {
00436
00437 const MercuryMatrix& m = obj->GetFinalMatrix();
00438 TranslucentPair p;
00439 MercuryPoint gp(m[0][3], m[1][3], m[2][3]);
00440 p.dist = -(gp - GetLastCameraPosition()).Magnitude();
00441 p.obj = obj;
00442 m_translucentObjects.push_back(p);
00443
00444 TSortDrawOrder(m_translucentObjects);
00445 }
00446
00447 MercuryPoint PointToScreenCoord(const MercuryMatrix& proj, const MercuryMatrix& view, const MercuryPoint& p, const DisplayDimensions& bd)
00448 {
00449 float ip[4];
00450 float a[4];
00451 MercuryMatrix mFinal;
00452 MercuryPoint tmp;
00453
00454 mFinal = proj * view;
00455
00456 ip[0] = p.x;
00457 ip[1] = p.y;
00458 ip[2] = p.z;
00459 ip[3] = 1;
00460
00461 VectorMultiply4f( mFinal.Ptr(), ip, a );
00462
00463
00464 tmp.x = a[0]/a[3];
00465 tmp.y = a[1]/a[3];
00466 tmp.z = a[2]/fabs(a[3]);
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477 tmp.x *= (bd.width)/2.0f;
00478 tmp.x += (bd.width)/2.0f;
00479 tmp.y *= (-bd.height)/2.0f;
00480 tmp.y += (bd.height)/2.0f;
00481
00482 return tmp;
00483 }
00484
00485 void MercuryDisplay::Viewport(int x, int y, int width, int height)
00486 {
00487 m_VPx = x;
00488 m_VPy = y;
00489 m_VPwidth = width;
00490 m_VPheight = height;
00491 }
00492
00493 void MercuryDisplay::FindLights(const MercuryMatrix& world, float radius)
00494 {
00495 m_foundLights.clear();
00496
00497 if ( radius < 0 )
00498 return;
00499
00500 const float * f = world.Ptr();
00501 MercuryPoint objectPosition(f[3], f[7], f[11]);
00502
00503 for (unsigned int i = 0; i < m_lights.size(); ++i)
00504 {
00505 float x = (objectPosition - m_lights[i]->GetPosition()).Magnitude();
00506 float y = m_lights[i]->GetRadius() + radius;
00507
00508 if (m_lights[i]->GetLightType() == LT_DIRECTIONAL)
00509 m_foundLights.push_back(m_lights[i]);
00510 else if ( x < y )
00511 m_foundLights.push_back(m_lights[i]);
00512 }
00513
00514 }
00515
00516 int MercuryDisplay::Next3Lights(const MercuryLight* light[3], unsigned int& count)
00517 {
00518 unsigned int i;
00519 if (m_currentState.GetState(MGLS_LIGHTING))
00520 {
00521 for (i = 0; i < 3; ++i)
00522 light[i] = NULL;
00523 for (i = 0; (i < 3) && (count+i < m_foundLights.size()); ++i)
00524 light[i] = m_foundLights[count+i];
00525 count += i;
00526 return m_foundLights.size() - count;
00527 }
00528 return 0;
00529 }
00530
00531 MercuryGLState MercuryDisplay::SetStates(MercuryGLState states)
00532 {
00533 MercuryGLState newstate = m_globalState.back()&states;
00534 MercuryGLState changedStates = m_currentState^newstate;
00535 m_currentState = newstate;
00536 return changedStates;
00537 }
00538
00539 void MercuryDisplay::SetView(const MercuryPoint& position, const MercuryMatrix& m, const LookAtInternals& lai)
00540 {
00541 m_view = m;
00542 m_pLastCameraPos = position;
00543 SetupClippingPlanes(position, lai.m_direction, lai.m_up, lai.m_x);
00544 }
00545
00546 void MercuryDisplay::EnqueueObject(const RenderPacket& rp)
00547 {
00548 m_renderObjects.push_back(rp);
00549 }
00550
00551 Projection Perspective( float fov, float aspect, float znear, float zfar )
00552 {
00553 Projection p;
00554 p.m_frustumInfo.m_fov = fov;
00555 p.m_frustumInfo.m_aspect = aspect;
00556 p.m_frustumInfo.m_znear = znear;
00557 p.m_frustumInfo.m_zfar = zfar;
00558
00559 float xmin, xmax, ymin, ymax;
00560
00561 ymax = znear * TAN(fov * Q_PI / 360.0);
00562 ymin = -ymax;
00563 xmin = ymin * aspect;
00564 xmax = ymax * aspect;
00565
00566 p.m_matrix = Frustum(xmin, xmax, ymin, ymax, znear, zfar);
00567 return p;
00568 }
00569
00570 Projection Ortho(float left, float right, float bottom, float top, float zNear, float zFar)
00571 {
00572 Projection p;
00573 MercuryMatrix& PERSP = p.m_matrix;
00574
00575
00576 PERSP[0][0] = 2/(right-left);
00577 PERSP[0][3] = -(right+left)/(right-left);
00578 PERSP[1][1] = 2/(top-bottom);
00579 PERSP[1][3] = -(top+bottom)/(top-bottom);
00580 PERSP[2][2] = -2/(zFar-zNear);
00581 PERSP[2][3] = (zFar + zNear)/(zFar-zNear);
00582 PERSP[3][3] = 1;
00583
00584 return p;
00585 }
00586
00587 MercuryMatrix Frustum(float left, float right, float bottom, float top, float zNear, float zFar)
00588 {
00589
00590
00591 MercuryMatrix frustum;
00592
00593 float near2 = 2*zNear;
00594 float rml = right-left;
00595 float tmb = top - bottom;
00596 float fmn = zFar - zNear;
00597
00598 float A = (right+left)/rml;
00599 float B = (top+bottom)/tmb;
00600 float C = -(zFar+zNear)/fmn;
00601 float D = -(near2*zFar)/fmn;
00602
00603 frustum.Zero();
00604
00605
00606 frustum[0][0] = near2/rml;
00607 frustum[0][2] = A;
00608 frustum[1][1] = near2/tmb;
00609 frustum[1][2] = B;
00610 frustum[2][2] = C;
00611 frustum[2][3] = D;
00612 frustum[3][2] = -1;
00613
00614 return frustum;
00615 }
00616
00617 MercuryDisplay * GetDisplay()
00618 {
00619 return DISPLAY;
00620 }
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648