#include "ProxyHeaders.hpp" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BuildMegatexturesProcess::BuildMegatexturesProcess(NotationFile *mega_file): megaFile(mega_file) { Check_Pointer(this); Check_Object(megaFile); Check_Object(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BuildMegatexturesProcess::BuildMegatexturesProcess( NotationFile *config_file, NotationFile *mega_file ): Process(config_file), megaFile(mega_file) { Check_Pointer(this); Check_Object(config_file); Check_Object(megaFile); Check_Object(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BuildMegatexturesProcess::ProcessCallback( int error, const char* megatexture, const char* texture ) { Check_Object(this); Check_Pointer(megatexture); switch (error) { case BadPageSize: PAUSE(("[%s] has a bad PageSize!", megatexture)); break; case BadTextureLine: Check_Pointer(texture); PAUSE(("[%s] has a bad Texture= specification!", megatexture)); break; case SizesDontMatch: Check_Pointer(texture); PAUSE(("[%s]%s does not match its stated size!", megatexture, texture)); break; case BadTexture: Check_Pointer(texture); PAUSE(("[%s]%s is a bad texture!", megatexture, texture)); break; case TextureDoesntFit: Check_Pointer(texture); PAUSE(("[%s]%s doesn't fit!", megatexture, texture)); break; case TextureOverlap: Check_Pointer(texture); PAUSE(("[%s]%s overlaps a previous texture!", megatexture, texture)); break; case EmptyMegatexture: PAUSE(("[%s] is empty!", megatexture)); break; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void TextureProxy::BuildMegatextures(BuildMegatexturesProcess *process) { Check_Object(this); Check_Object(process); // //-------------------------- // Get the mega texture data //-------------------------- // NotationFile *mega_file = process->megaFile; Check_Object(mega_file); MString mega_name; GetName(&mega_name); Check_Object(&mega_name); Vector2DOf mega_size; GetImageSize(&mega_size); Verify( mega_size.x >= 16 && mega_size.x <= 512 && mega_size.y >= 16 && mega_size.y <= 512 && mega_size.x == (mega_size.x & -mega_size.x) && mega_size.y == (mega_size.y & -mega_size.y) ); TextureLibrary *library = GetTextureLibrary(); Check_Object(library); // //----------------------------------------------------------------- // Create the megatexture proxy and clear it out to invisible black //----------------------------------------------------------------- // unsigned channel_size = mega_size.x*mega_size.y; DynamicArrayOf red(channel_size), green(channel_size), blue(channel_size), alpha(channel_size); memset(red.GetData(), 0, channel_size); memset(green.GetData(), 0, channel_size); memset(blue.GetData(), 0, channel_size); memset(alpha.GetData(), 0, channel_size); DynamicArrayOf used(false, channel_size); // //---------------------------------------------------------------------- // Now start stepping through the entries for the page and skip the page // size entry //---------------------------------------------------------------------- // bool alpha_used = false; int texture_count = 0; ChainOf *textures = NULL; Note *entry = NULL; unsigned errors = 0; Page *mega_page = mega_file->FindPage(mega_name); if (mega_page) { textures = mega_page->MakeNoteChain("Texture"); Check_Object(textures); Page::NoteIterator texture_itr(textures); while ((entry = texture_itr.ReadAndNext()) != NULL) { Check_Object(entry); // //-------------------------------------------- // Extract the texture data from the tron file //-------------------------------------------- // const char* data; entry->GetEntry(&data); Check_Pointer(data); Vector2DOf offset, size(0,0); char texture_name[80]; int param_count = sscanf( data, "%s %d %d %d %d", texture_name, &offset.x, &offset.y, &size.x, &size.y ); if (param_count != 5 || !strlen(texture_name)) { process->ProcessCallback( BuildMegatexturesProcess::BadTextureLine, mega_name, texture_name ); if (!process->continueProcess) { Unwind_1: Check_Object(textures); delete textures; return; #undef UNWIND #define UNWIND() goto Unwind_1 } else { ++errors; continue; } } // //-------------------------- // Do the status check again //-------------------------- // process->ProcessCallback( BuildMegatexturesProcess::StatusCheck, mega_name, texture_name ); if (!process->continueProcess) UNWIND(); // //----------------------------------------------------------------- // We now have a texture name, so get the actual texture proxy from // the library //----------------------------------------------------------------- // TextureProxy *texture = library->UseTextureProxy(texture_name); Check_Object(texture); // //--------------------------- // Make sure the size is good //--------------------------- // Vector2DOf texture_size; texture->GetImageSize(&texture_size); if (!texture_size.x && !texture_size.y) { process->ProcessCallback( BuildMegatexturesProcess::BadTexture, mega_name, texture_name ); if (!process->continueProcess) { Unwind_2: texture->DetachReference(); UNWIND(); #undef UNWIND #define UNWIND() goto Unwind_2 } else { texture->DetachReference(); ++errors; continue; } } // //----------------------------------------------- // Make sure the size matches what is in the file //----------------------------------------------- // if (size != texture_size) { process->ProcessCallback( BuildMegatexturesProcess::SizesDontMatch, mega_name, texture_name ); if (!process->continueProcess) UNWIND(); else { texture->DetachReference(); ++errors; continue; } } ++texture_count; // //-------------------------------------------------------------- // Figure out where this texture wishes to be in the megatexture //-------------------------------------------------------------- // if ( offset.x < 0 || offset.x+size.x > mega_size.x || offset.y < 0 || offset.y+size.y > mega_size.y ) { process->ProcessCallback( BuildMegatexturesProcess::TextureDoesntFit, mega_name, texture_name ); if (!process->continueProcess) UNWIND(); else { ++errors; texture->DetachReference(); continue; } } // //----------------------- // Get the texture depths //----------------------- // unsigned red_depth, green_depth, blue_depth, alpha_depth; texture->GetChannelDepth( &red_depth, &green_depth, &blue_depth, &alpha_depth ); if (alpha_depth) alpha_used = true; // //--------------------- // Get the texture data //--------------------- // DynamicArrayOf r, g, b, a; if (alpha_depth) texture->GetChannels(&r, &g, &b, &a); else { texture->GetChannels(&r, &g, &b, NULL); alpha_depth = 0; } // //------------------------- // Copy the data row by row //------------------------- // Vector2DOf source(0,0); for (source.y=0; source.y dest; dest.Add(offset, source); unsigned source_pixel = texture->GetPixelIndex(source); unsigned dest_pixel = GetPixelIndex(dest); // //-------------------- // Look for an overlap //-------------------- // void *overlap = memchr(&used[dest_pixel], true, size.x); if (overlap) { process->ProcessCallback( BuildMegatexturesProcess::TextureOverlap, mega_name, texture_name ); if (!process->continueProcess) UNWIND(); else { ++errors; source.y = size.y; continue; } } memset(&used[dest_pixel], true, size.x); // //--------------------- // Copy the row of data //--------------------- // Mem_Copy( &red[dest_pixel], &r[source_pixel], size.x, red.GetLength() - dest_pixel ); Mem_Copy( &green[dest_pixel], &g[source_pixel], size.x, green.GetLength() - dest_pixel ); Mem_Copy( &blue[dest_pixel], &b[source_pixel], size.x, blue.GetLength() - dest_pixel ); // //---------------------------------------------------------------- // Copy the alpha data if enabled, otherwise just set it to opaque //---------------------------------------------------------------- // if (alpha_depth) { Mem_Copy( &alpha[dest_pixel], &a[source_pixel], size.x, alpha.GetLength() - dest_pixel ); } else memset(&alpha[dest_pixel], static_cast(255), size.x); } // //--------------------- // Clean up the texture //--------------------- // texture->DetachReference(); } } // //------------------------------------------------- // Tell the application if the megatexture is empty //------------------------------------------------- // if (!texture_count) { process->ProcessCallback( BuildMegatexturesProcess::EmptyMegatexture, mega_name, NULL ); if (!process->continueProcess) { Check_Object(textures); delete textures; } } // //--------------------------------------------------------------------- // If we didn't have any errors and aren't empty, copy the texture into // the texture library //--------------------------------------------------------------------- // else if (!errors) { SetChannels( &red, &green, &blue, (alpha_used) ? &alpha : NULL ); TextureProxy *proxy = library->UseMatchingTextureProxy(this); Check_Object(proxy); Verify(IsEqualTo(proxy)); proxy->DetachReference(); } // //--------- // Clean up //--------- // Check_Object(textures); delete textures; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void TextureLibrary::BuildMegatextures(BuildMegatexturesProcess *process) { Check_Object(this); Check_Object(process); // //------------------------------------------------------------------------ // Get the page list from the mega file and process each page after making // sure the app says it's OK //------------------------------------------------------------------------ // NotationFile *mega_file = process->megaFile; Check_Object(mega_file); NotationFile::PageIterator *pages = mega_file->MakePageIterator(); Check_Object(pages); Page *page; while ((page = pages->ReadAndNext()) != NULL) { Check_Object(page); const char* mega_name = page->GetName(); Check_Pointer(mega_name); process->ProcessCallback( BuildMegatexturesProcess::StatusCheck, mega_name, NULL ); if (!process->continueProcess) { Unwind_1: Check_Object(pages); delete pages; return; #undef UNWIND #define UNWIND() goto Unwind_1 } // //---------------------------------------------------------------------- // For each page, get a list of all the entries. If the page is missing // its PageSize page, it is a final uncoalesced texture, so skip it //---------------------------------------------------------------------- // const char* size_string; if (!page->GetEntry("PageSize", &size_string)) continue; // //------------------------------ // Make sure the page size is OK //------------------------------ // Vector2DOf mega_size; #if defined(_ARMOR) int param_count = #endif sscanf(size_string, "%d %d", &mega_size.x, &mega_size.y); Verify(param_count); if ( mega_size.x < 16 || mega_size.x > 512 || mega_size.y < 16 || mega_size.y > 512 || mega_size.x != (mega_size.x & -mega_size.x) || mega_size.y != (mega_size.y & -mega_size.y) ) { process->ProcessCallback( BuildMegatexturesProcess::BadPageSize, mega_name, NULL ); if (!process->continueProcess) UNWIND(); else continue; } // //--------------------------------------------------- // Create the megatexture proxy run the process on it //--------------------------------------------------- // TextureProxy *mega_texture = TextureProxy::MakeProxy(mega_name, mega_size, this); Check_Object(mega_texture); mega_texture->BuildMegatextures(process); mega_texture->DetachReference(); if (!process->continueProcess) UNWIND(); } // //----------------------- // Clean up the page list //----------------------- // Check_Object(pages); delete pages; }