00001 #include "global.h"
00002 #include "MercuryTextureManager.h"
00003 #include "MercuryDisplay.h"
00004 #include "MercuryLog.h"
00005 #include "MercuryUtil.h"
00006 #include "MercuryFiles.h"
00007
00008 #define MC_TEXTURESTATS 1
00009 #define MC_LOADTEXTURE 2
00010 #define MC_UNREGISTER 3
00011 #define MC_LOADTEXTURERAW 4
00012
00013
00014 TextureID::~TextureID()
00015 {
00016 unsigned int num = m_textures.size();
00017 for (unsigned int i = 0; i < num; ++i)
00018 m_textures[i] = NULL;
00019 DISPLAY->DeleteTextures(1, &m_ID);
00020 }
00021
00022 TextureID::TextureID(const MString& path, bool loadPending)
00023 {
00024 m_ID = 0;
00025 m_path = path;
00026 m_loadPending = loadPending;
00027 }
00028
00029 bool TextureID::RemoveTexturePtr(const MercuryTexture* texture)
00030 {
00031 unsigned int i;
00032 for( i = 0; i < m_textures.size(); i++ )
00033 if( m_textures[i] == texture )
00034 {
00035 m_textures[i] = NULL;
00036 m_textures.remove(i);
00037 return true;
00038 }
00039 return false;
00040 }
00041
00042 void TextureID::UpdateTextureData(MercuryTexture* texture)
00043 {
00044 DISPLAY->UpdateTextureData(texture, m_ID);
00045 }
00046
00047 MercuryTextureManager::MercuryTextureManager()
00048 {
00049 MercuryMessageHandler::MercuryMessageHandler();
00050 m_textureCount = 0;
00051
00052 MercuryMessageHandler::RegisterMessage( MC_TEXTURESTATS, "RenderStats" );
00053 MercuryMessageHandler::RegisterMessage( MC_LOADTEXTURE, "LoadTexture" );
00054 MercuryMessageHandler::RegisterMessage( MC_UNREGISTER, "UnregisterTexture" );
00055 MercuryMessageHandler::RegisterMessage( MC_LOADTEXTURERAW, "LoadTextureRaw" );
00056 }
00057
00058 MercuryTextureManager::~MercuryTextureManager()
00059 {
00060 MLockPtr< map<MString, TextureID*> > textureMap(m_textureMap, m_mtxMap);
00061 for(map<MString, TextureID*>::iterator it = textureMap->begin();
00062 it != textureMap->end(); ++it)
00063 SAFE_DELETE(it->second);
00064 }
00065
00066 void MercuryTextureManager::Message( int Message, PStack & data, const MString & name )
00067 {
00068 switch ( Message )
00069 {
00070 case MC_TEXTURESTATS:
00071 {
00072 PStack stats;
00073 stats.PushItemBack( PSElement ((int)(UniqueTextureCount())) );
00074 stats.PushItemBack( PSElement ((int)(TextureCount())) );
00075 MESSAGEMAN->PostSystemMessage( "TextureStats", stats, 0 );
00076 }
00077 break;
00078 case MC_LOADTEXTURE:
00079 {
00080 MString path = data.PeekItem(0).GetValueS();
00081 MercuryTexture* t = (MercuryTexture*)data.PeekItem(1).GetValueV();
00082
00083 if (!path.empty())
00084 {
00085 ThreadLoadData* tld = new ThreadLoadData(path,t);
00086 if (!TryFromExisting(tld))
00087 MLockPtr< MDeque<ThreadLoadData*> >(m_loadQueue, m_mtxLoadQueue)->push_front(tld);
00088 }
00089 }
00090 break;
00091 case MC_UNREGISTER:
00092 {
00093 MString path = data.PeekItem(0).GetValueS();
00094 MercuryTexture* t = (MercuryTexture*)data.PeekItem(1).GetValueV();
00095 Remove(path, t);
00096 }
00097 break;
00098 case MC_LOADTEXTURERAW:
00099 {
00100 MString path = data.PeekItem(0).GetValueS();
00101 MercuryTexture* t = (MercuryTexture*)data.PeekItem(1).GetValueV();
00102 RawImageData* imagedata = (RawImageData*)data.PeekItem(2).GetValueV();
00103
00104 if (!path.empty())
00105 {
00106 ThreadLoadData* tld = new ThreadLoadData(path,t,imagedata);
00107 if (!TryFromExisting(tld))
00108 MLockPtr< MDeque<ThreadLoadData*> >(m_loadQueue, m_mtxLoadQueue)->push_front(tld);
00109 }
00110 }
00111 break;
00112 }
00113 }
00114
00115 bool MercuryTextureManager::TryFromExisting(ThreadLoadData* tld)
00116 {
00117 TextureID* tid = FindTexture(tld->path);
00118 if (tid)
00119 {
00120 tid->m_textures.push_back(tld->texture);
00121 ++m_textureCount;
00122
00123 PStack data;
00124 data.PushItemBack(PSElement((void*)tld->texture));
00125 data.PushItemBack(PSElement(&(tid->m_attrs)));
00126 MESSAGEMAN->PostSystemMessage("GetTextureID", data);
00127
00128 SAFE_DELETE(tld);
00129 return true;
00130 }
00131 return false;
00132 }
00133
00134 void* TheadFunctCaller(void* x)
00135 {
00136 MercuryTextureManager* y = (MercuryTextureManager*)x;
00137 y->ThreadLoop();
00138 return 0;
00139 }
00140
00141 void MercuryTextureManager::CreateThread()
00142 {
00143 #if !defined( NO_THREADS )
00144 m_thread.Create(TheadFunctCaller, this);
00145 #endif
00146 }
00147
00148 void MercuryTextureManager::Update( const float dTime )
00149 {
00150 #if defined( NO_THREADS )
00151 ProcessLoadQueue();
00152 #endif
00153 ProcessFinalizeQueue();
00154 }
00155
00156 RawImageData * MercuryTextureManager::LoadDyn( const MString & sFullName, MString & sShortName )
00157 {
00158 if( sFullName.length() < 7 )
00159 return NULL;
00160 MVector< MString > vSplit;
00161 SplitStrings( sFullName.substr( 7 ), vSplit, ",", " ", 1, 1 );
00162 if( vSplit.size() != 4 )
00163 {
00164 LOG.Warn( "ERROR: Dynamic Textures MUST be reated in the following form: (Name),(Width),(Height),(bitmode=0|1)" );
00165 return NULL;
00166 }
00167 RawImageData * k = new RawImageData;
00168 k->attrs.m_ColorByteType = atoi(vSplit[3].c_str())?RGBA_FLOAT:RGBA;
00169 k->attrs.m_height = k->attrs.m_height_original = atoi( vSplit[2].c_str() );
00170 k->attrs.m_width = k->attrs.m_width_original = atoi( vSplit[1].c_str() );
00171
00172 k->attrs.m_ID = 0;
00173
00174
00175 k->data = 0;
00176 sShortName = vSplit[0];
00177 return k;
00178 }
00179
00180 RawImageData * MercuryTextureManager::LoadDynShadow( const MString & sFullName, MString & sShortName )
00181 {
00182
00183
00184 *((unsigned int*)(NULL)) = 1;
00185
00186
00187 if( sFullName.length() < 13 )
00188 return NULL;
00189 MVector< MString > vSplit;
00190 SplitStrings( sFullName.substr( 13 ), vSplit, ",", " ", 1, 1 );
00191 if( vSplit.size() != 4 )
00192 {
00193 LOG.Warn( "ERROR: Dynamic Textures MUST be reated in the following form: (Name),(Width),(Height),(bitmode=0|1)" );
00194 return NULL;
00195 }
00196 RawImageData * k = new RawImageData;
00197 k->attrs.m_ColorByteType = DEPTH_COMPONENT24;
00198 k->attrs.m_height = k->attrs.m_height_original = atoi( vSplit[2].c_str() );
00199 k->attrs.m_width = k->attrs.m_width_original = atoi( vSplit[1].c_str() );
00200
00201 k->attrs.m_ID = 0;
00202
00203
00204 k->data = 0;
00205 sShortName = vSplit[0];
00206 return k;
00207 }
00208
00209 RawImageData * MercuryTextureManager::LoadDynCube( const MString & sFullName, MString & sShortName )
00210 {
00211 if( sFullName.length() < 8 )
00212 return NULL;
00213 MVector< MString > vSplit;
00214 SplitStrings( sFullName.substr( 8 ), vSplit, ",", " ", 1, 1 );
00215 if( vSplit.size() != 4 )
00216 {
00217 LOG.Warn( "ERROR: Dynamic Textures MUST be reated in the following form: (Name),(Width),(Height),(bitmode=0|1)" );
00218 return NULL;
00219 }
00220 RawImageData * k = new RawImageData;
00221 k->attrs.m_ColorByteType = atoi(vSplit[3].c_str())?RGBA_FLOAT:RGBA;
00222 k->attrs.m_height = k->attrs.m_height_original = atoi( vSplit[2].c_str() );
00223 k->attrs.m_width = k->attrs.m_width_original = atoi( vSplit[1].c_str() );
00224
00225 k->attrs.m_ID = 0;
00226
00227
00228
00229 k->data = 0;
00230 sShortName = vSplit[0];
00231 return k;
00232 }
00233
00234
00235 RawImageData *MercuryTextureManager::LoadCube( const MString & sFullName, MString & sShortName )
00236 {
00237 unsigned int strLen = sFullName.size();
00238 unsigned int i = 0, j = 0, c = 0;
00239
00240 MString cubeStr[6];
00241
00242 RawImageData *pCubeRaw[6];
00243
00244
00245 RawImageData *pFinalRaw;
00246
00247
00248 while (c < strLen && sFullName[c] != ':')
00249 c++;
00250 c++;
00251
00252
00253 for (i = 0; i < 6 && c < strLen; i++)
00254 {
00255 j = 0;
00256
00257
00258 while (c < strLen && sFullName[c] != ',')
00259 {
00260 cubeStr[i] += sFullName[c++];
00261 j++;
00262 }
00263
00264 LOG.Log(cubeStr[i]);
00265
00266
00267
00268 c++;
00269 }
00270
00271
00272 if (i < 6)
00273 {
00274 LOG.Warn( "ERROR: Cube Textures MUST be treated in the following form: minusXpath, plusXpath, minusYpath, plusYpath, minusZpath, plusZpath" );
00275 return NULL;
00276 }
00277
00278
00279 for (i = 0; i < 6; i++)
00280 pCubeRaw[i] = ImageLoader(cubeStr[i]);
00281
00282
00283 for (i = 0; i < 6; i++)
00284 {
00285 if (pCubeRaw[i]->data == 0)
00286 {
00287
00288 for (j = 0; j < 6; j++)
00289 {
00290 SAFE_DELETE (pCubeRaw[i]->data);
00291 SAFE_DELETE (pCubeRaw[i]);
00292 }
00293
00294
00295 LOG.Warn( (MString)("ERROR: cube face \"") + cubeStr[i] + "\" is not loading properly, cube map aborted ()\n" );
00296 return NULL;
00297 }
00298 }
00299
00300
00301 for (i = 2; i < 6; i++)
00302 {
00303 if (pCubeRaw[i]->attrs.m_width != pCubeRaw[i-1]->attrs.m_width ||
00304 pCubeRaw[i]->attrs.m_height != pCubeRaw[i-1]->attrs.m_height||
00305 pCubeRaw[i]->attrs.m_ColorByteType != pCubeRaw[i]->attrs.m_ColorByteType)
00306 {
00307
00308 for (j = 0; j < 6; j++)
00309 {
00310 SAFE_DELETE (pCubeRaw[i]->data);
00311 SAFE_DELETE (pCubeRaw[i]);
00312 }
00313
00314
00315 LOG.Warn( (MString)("ERROR: cube faces have inconsistent data\n") );
00316 return NULL;
00317 }
00318 }
00319
00320
00321
00322 pFinalRaw = new RawImageData();
00323 pFinalRaw->attrs = pCubeRaw[0]->attrs;
00324
00325 pFinalRaw->attrs.m_ID = 0;
00326 unsigned int faceLength = pFinalRaw->attrs.m_width * pFinalRaw->attrs.m_height * ColorBytesToSize(pFinalRaw->attrs.m_ColorByteType);
00327 unsigned int totalLength = 6*faceLength;
00328 pFinalRaw->data = new unsigned char [totalLength];
00329
00330 printf ("pData = %lu\nfaceLength = %lu\ntotalLength = %lu\nfinished newing CubeMap data\n", pFinalRaw->data, faceLength, totalLength);
00331
00332 c = 0;
00333 for (i = 0; i < 6; i++)
00334 {
00335 for (j = 0; j < faceLength; j++)
00336 {
00337 pFinalRaw->data[c++] = pCubeRaw[i]->data[j];
00338 }
00339 }
00340
00341
00342 for (i = 0; i < 6; i++)
00343 {
00344 SAFE_DELETE (pCubeRaw[i]->data);
00345 SAFE_DELETE (pCubeRaw[i]);
00346 }
00347
00348 LOG.Log("done with cube map raw image loading");
00349
00350
00351 return pFinalRaw;
00352 }
00353
00354 MercuryTexture* MercuryTextureManager::CreateTexture(const MString& path)
00355 {
00356 MercuryTexture* texture = new MercuryTexture;
00357 texture->Init();
00358 texture->SetPath(path);
00359 texture->SendLoadMessage();
00360 return texture;
00361 }
00362
00363 TextureID* MercuryTextureManager::RegisterNewTexture(const MercuryTexture* t, RawImageData* d, const MString& path)
00364 {
00365
00366
00367
00368
00369
00370
00371
00372 TextureID* tid = FindTexture(path);
00373 if (!tid)
00374 {
00375 tid = new TextureID(path);
00376 MLockPtr< map<MString, TextureID*> >(m_textureMap, m_mtxMap)->operator[](tid->m_path) = tid;
00377 }
00378
00379 tid = FindTexture(path);
00380 ASSERT(tid);
00381 tid->m_loadPending = false;
00382 d->CorrectSize();
00383 DISPLAY->CreateCache(d, tid->m_ID);
00384 d->attrs.m_ID = tid->m_ID;
00385 tid->m_attrs = d->attrs;
00386
00387 SAFE_DELETE(d);
00388
00389 return tid;
00390 }
00391
00392 void MercuryTextureManager::ThreadLoop()
00393 {
00394 while(true)
00395 {
00396 ProcessLoadQueue();
00397 Sleep(10);
00398 }
00399 }
00400
00401 void MercuryTextureManager::ProcessLoadQueue()
00402 {
00403
00404
00405
00406 MDeque<ThreadLoadData*> loadQueue;
00407 MLockPtr< MDeque<ThreadLoadData*> >* loadQueueTmp = new MLockPtr< MDeque<ThreadLoadData*> >(m_loadQueue, m_mtxLoadQueue);
00408
00409
00410
00411 if ((*loadQueueTmp)->empty())
00412 {
00413 SAFE_DELETE(loadQueueTmp);
00414 return;
00415 }
00416 else
00417 {
00418 while (!(*loadQueueTmp)->empty())
00419 {
00420 loadQueue.push_back((*loadQueueTmp)->front());
00421 (*loadQueueTmp)->pop_front();
00422 }
00423 SAFE_DELETE(loadQueueTmp);
00424 }
00425
00426 while (!loadQueue.empty())
00427 {
00428 ThreadLoadData* tld = loadQueue.front();
00429 loadQueue.pop_front();
00430
00431 const TextureID* tid = FindTexture(tld->path);
00432 if (!tid)
00433 {
00434
00435 LOG.Log("Loading texture data from " + tld->path + " (loadQueue).");
00436
00437 if (!tld->d)
00438 {
00439 RawImageData* d = ImageLoader(tld->path);
00440 if (!d)
00441 return;
00442
00443 tld->d = d;
00444 }
00445
00446
00447
00448 TextureID* tid = new TextureID(tld->path, true);
00449 MLockPtr< map<MString, TextureID*> >(m_textureMap, m_mtxMap)->operator[](tid->m_path) = tid;
00450 }
00451 else
00452 {
00453
00454 tld->d = NULL;
00455 }
00456
00457 MLockPtr< MDeque<ThreadLoadData*> >(m_finalizeLoadQueue, m_mtxFinalize)->push_back(tld);
00458 }
00459 }
00460
00461 void MercuryTextureManager::ProcessFinalizeQueue()
00462 {
00463 TextureID* tid = NULL;
00464
00465 MLockPtr< MDeque<ThreadLoadData*> > finalizeQueue(m_finalizeLoadQueue, m_mtxFinalize);
00466
00467 while(!finalizeQueue->empty())
00468 {
00469 tid = NULL;
00470
00471
00472 ThreadLoadData* tld = finalizeQueue->front();
00473 finalizeQueue->pop_front();
00474
00475 if (!tld->d)
00476 {
00477
00478 tid = FindTexture(tld->path);
00479
00480
00481 }
00482 else
00483 {
00484 LOG.Log("Texture cache for " + tld->path + " not found, generating cache (finalizeQueue).");
00485 tid = RegisterNewTexture(tld->texture, tld->d, tld->path);
00486 }
00487
00488 tid->m_textures.push_back(tld->texture);
00489 ++m_textureCount;
00490
00491 PStack data;
00492 data.PushItemBack(PSElement((void*)tld->texture));
00493 data.PushItemBack(PSElement(&(tid->m_attrs)));
00494 MESSAGEMAN->PostSystemMessage("GetTextureID", data);
00495
00496 SAFE_DELETE(tld);
00497 }
00498 }
00499
00500
00501 void MercuryTextureManager::Remove(MercuryTexture* texture)
00502 {
00503 if (texture->GetID() <= 0)
00504 return;
00505
00506 MDequeIterator<MercuryTexture*> i;
00507
00508 TextureID* tmp = FindTexture(texture);
00509 if (tmp)
00510 {
00511 if(tmp->RemoveTexturePtr(texture))
00512 --m_textureCount;
00513
00514 if (tmp->m_textures.empty())
00515 {
00516 MLockPtr< map<MString, TextureID*> > textureMap(m_textureMap, m_mtxMap);
00517 textureMap->erase(texture->GetName());
00518 }
00519 }
00520 }
00521
00522 void MercuryTextureManager::Remove(const MString& path, const MercuryTexture* texture)
00523 {
00524
00525
00526
00527 MDequeIterator<MercuryTexture*> i;
00528
00529 TextureID* tmp = FindTexture(path);
00530 if (tmp)
00531 {
00532 if(tmp->RemoveTexturePtr(texture))
00533 --m_textureCount;
00534
00535 if (tmp->m_textures.empty())
00536 {
00537 MLockPtr< map<MString, TextureID*> > textureMap(m_textureMap, m_mtxMap);
00538 SAFE_DELETE(tmp);
00539 textureMap->erase(path);
00540 }
00541 }
00542 }
00543
00544 TextureID* MercuryTextureManager::FindTexture(const MString& path) const
00545 {
00546 MString name = path;
00547
00548 TextureID* tID = NULL;
00549 MLockPtr< map<MString, TextureID*> > textureMap(m_textureMap, m_mtxMap);
00550 map<MString, TextureID*>::const_iterator i = textureMap->find(name);
00551
00552 if (i != textureMap->end())
00553 tID = (i->second);
00554
00555 return tID;
00556 }
00557
00558 void MercuryTextureManager::UpdateTexture(MercuryTexture* texture)
00559 {
00560 TextureID* tmp = FindTexture(texture);
00561 if (!tmp)
00562 {
00563 LOG.Log("Texture " + texture->GetPath() + " not found, can not update.");
00564 return;
00565 }
00566 tmp->UpdateTextureData(texture);
00567 }
00568
00569 bool MercuryTextureManager::IsTexture(const MercuryTexture* texture)
00570 {
00571 if (!FindTexture(texture))
00572 return false;
00573 return true;
00574 }
00575
00576 RawImageData* MercuryTextureManager::ImageLoader(const MString& path)
00577 {
00578 LOG.Log("about to load texture: " + path);
00579
00580 if(path.length() > 7 && path.substr( 0, 7 ) == "DYNTEX:" )
00581 {
00582 MString sDiscardName;
00583 return LoadDyn( path, sDiscardName );
00584 }
00585 else if(path.length() > 8 && path.substr( 0, 8 ) == "CUBETEX:" )
00586 {
00587 LOG.Log("WE've GOT A CUBE TEX");
00588
00589 MString sDiscardName;
00590 return LoadCube( path, sDiscardName );
00591
00592 }
00593 else if(path.length() > 8 && path.substr(0, 8) == "DYNCUBE:" )
00594 {
00595 MString sDiscardName;
00596 return LoadDynCube( path, sDiscardName );
00597 }
00598 else if(path.length() > 13 && path.substr(0, 13) == "DYNTEXSHADOW:" )
00599 {
00600 LOG.Log("WE've GOT A DYN TEX SHADOW");
00601 MString sDiscardName;
00602 return LoadDynShadow( path, sDiscardName);
00603 }
00604
00605 if (path.empty())
00606 return NULL;
00607
00608 MercuryFile * f = FILEMAN.Open( path );
00609 if (!f)
00610 {
00611 LOG.Warn("Could not open " + path);
00612 return false;
00613 }
00614
00615 RawImageData* raw = IMAGEREADERREGISTER.Decode( f );
00616
00617 SAFE_DELETE(f);
00618
00619 if( !raw )
00620 return NULL;
00621
00622 return raw;
00623 }
00624
00625 void MercuryImageReaderRegister::CheckInit()
00626 {
00627 if( !bInit )
00628 {
00629 bInit = true;
00630 m_hDecoders = new MHash< ImageDecoder >;
00631 }
00632 }
00633
00634 void MercuryImageReaderRegister::AddDecoder( ImageDecoder p, const char * sFingerPrint )
00635 {
00636 CheckInit();
00637 MString sFP( sFingerPrint, 3 );
00638 (*m_hDecoders)[sFP] = p;
00639 }
00640
00641 RawImageData * MercuryImageReaderRegister::Decode( MercuryFile * f )
00642 {
00643 if( !m_hDecoders )
00644 {
00645 FAIL( "ERROR: There are no image loaders dynamically included. Cannot operate." );
00646 return NULL;
00647 }
00648 char fingerprint[4];
00649 f->Read( fingerprint, 3 );
00650 MString sFP( fingerprint, 3 );
00651
00652 f->Seek( 0 );
00653 if( m_hDecoders->get( sFP ) )
00654 {
00655 return (*m_hDecoders)[sFP](f);
00656 }
00657 else
00658 return NULL;
00659 }
00660
00661 MercuryImageReaderRegister IMAGEREADERREGISTER;
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689