/*********************************************************************************************** * * Copyright © DreamWorks Interactive. 1997 * * Implementation of texture manager * *********************************************************************************************** * * $Log:: /JP2_PC/Source/Lib/Loader/TextureManager.cpp $ * * 28 98.08.13 4:26p Mmouni * Changes for VC++ 5.0sp3 compatibility. * * 27 98.07.27 7:11p Mmouni * Checks for bump-maps now include a check of the pixel format as well as checking the bit * depth. * * 26 98.07.08 12:10p Mmouni * Now saves the palette for parent bump maps. * * 25 7/02/98 7:11p Rwyatt * New VM allocation stratergy for curved bump maps * * 24 4/29/98 7:05p Rwyatt * Texture manager now resets properly * * 23 4/22/98 12:35p Rwyatt * Added VER_TEST to the always assert on textures larger than 256 * * 22 4/21/98 8:50p Rwyatt * Terminal error for maps larger than 256 * * 21 4/21/98 3:13p Rwyatt * Significantly changed to support the new VM system. * * 20 3/06/98 8:40p Rwyatt * When generating the swap file, if curved parent bump maps are above 256x256 in size it now * allocates virtual memory rather than trying to pack them which caused a stack fault. * * 19 98/02/26 14:02 Speter * Moved seterfFeatures to CTexture from SSurface, removing redundant flags. * * 18 2/24/98 6:54p Rwyatt * Use the new texture packer * * 17 2/18/98 1:05p Rwyatt * Removed the image loader into its own file * * 16 2/09/98 12:24p Rwyatt * Fixed palette instancing bug while using virtual loader * * 15 2/03/98 5:35p Rwyatt * Curved bump maps are handled in a special way because they can be less than 8x8 in size. * * 14 2/03/98 2:40p Rwyatt * Now files are shared the virtual loader can be used by default. * * 13 2/03/98 2:24p Rwyatt * Removed AlwaysAssert so if a file is not found in the image it will be loaded via the * conventional method. * * 12 1/30/98 4:35p Rwyatt * Opps * * 11 1/30/98 4:25p Rwyatt * Conventional loader is the default loader * * 10 1/29/98 7:39p Rwyatt * Mip 0 is packed alone to aid the loading process when it is not used, * Create a virtiual image directory so the textures in the image can be relocated on load. * Textures that are too big to pack just get copied into virtual memory, so do curved bump maps * when the automatic packer is active. * All allocations within the texture manager are logged so the image directory can be built and * verified. * * 9 1/19/98 7:30p Pkeet * Added support for 16 bit bumpmaps by adding lower colour resolution and a smaller size for * 'CBumpAnglePair.' * * 8 1/14/98 6:26p Rwyatt * Has a callback function that will pack bump maps after they have all been loaded, and * optionally pack curved maps. * The pack search function has been isolated so it can be used by the deferred bump packing * * 7 1/09/98 7:07p Rwyatt * The pack function now takes paramters that specify the curved state and mip level of a * texture/bump map. * Bump maps are kept in a list and not packed, they will be packed later after loading is * complete. * * 6 12/04/97 4:13p Rwyatt * New header file order * Texture manager now create a VM handler with a default memory pool of 256Megs. Texture memory * is commited to this pool as it is used by the VM manager, upto the prescribed limit. * * 5 10/10/97 1:36p Mmouni * Now updates tiling masks when copying rasters. * * 4 9/01/97 7:56p Rwyatt * Will only pack surfaces that have not already been packed. * Has a member function to return the current list of packed surfaces, this is mainly used by * the texture pack dialog. * * 3 8/19/97 7:03p Rwyatt * Sub allocated mem rasters (such as textures) now have an rpt back to the parent Texture Pack * surface. This is so the packer can be infomred when the texture is deleted, it is an rptr to * avoid static destructor dependencies. * * 2 7/29/97 1:58a Rwyatt * Very basic texture manager but works for the time being. At the moment the upper limit on * texture pages is 2048 (4096 256x256 surfaces). * Logs everything to the LoadLog.txt console file. * * 1 7/29/97 1:00a Rwyatt * Initial implementation * **********************************************************************************************/ #include "Common.hpp" #include "stdio.h" #include "stdlib.h" #include "Lib/W95/WinInclude.hpp" #include "Lib/Sys/VirtualMem.hpp" #include "TextureManager.hpp" #include "ImageLoader.hpp" #include "TexturePackSurface.hpp" #include "Loader.hpp" #include "Lib/View/RasterVid.hpp" #include "Lib/Sys/MemoryLog.hpp" #include "Lib/Std/StdLibEx.hpp" #include "Lib/Sys/DebugConsole.hpp" #include "Lib/Sys/FileEx.hpp" //********************************************************************************************* // This is the size of the virtual block that VM system will allocate while loading textures.. // #define u4TEXTURE_MEMORY_POOL_SIZE (128*1024*1024) #define u4TEXTURE_MEMORY_PAGE_SIZE (64*1024) #define u4TEXTURE_MEMORY_MAX_PHYSICAL (16*1024*1024) #define u4SMALL_TEXTURE_MAX_MEM (6*1024*1024) //********************************************************************************************* // Global texture manager. // CTextureManager gtxmTexMan; //********************************************************************************************* // CTextureManager::CTextureManager() { // this class does not have to be stored, the allocator address is stored within a static // inside itself. This way here can be only one. // Create an allocate VM manager, this will just keep allocating memory until it is // purged. At that point it will start to demand load texture pages. pvmeTextures = NULL; // Allocator for smallest textures, these textures are never decommited so are never // required to be reloaded. pvmsaSmallTextures = NULL; // Set the amount of memory that is to be reserved u4SmallTextureSubAllocateSize = u4SMALL_TEXTURE_MAX_MEM; // no STL vector until a bump map is loaded pblBumpMaps = NULL; pvilVirtualImageMaps = NULL; pcplParentBumpMaps = NULL; // Pack curved bump maps bPackCurved = true; // a mip can only be packed with a neighbouring sized mip. // This only applies to mips that are above level 0. SetMipLevelPackingDifference(2); // delete the render texture packed pages. for (int i=0; ibFileBased()); // VM is not paging Assert(!pvmeTextures->bAnythingAllocated()); // VM has nothing allocated pvmsaSmallTextures = new CVirtualMem::CVMSubAllocator(pvmeTextures,u4SmallTextureSubAllocateSize); } //********************************************************************************************* // Remove the sub allocator that we was using for small textures. Once this is done small // textures are allocated as normal. void CTextureManager::DestroySmallTextureAllocator ( ) //************************************* { if (pvmsaSmallTextures) { delete pvmsaSmallTextures; pvmsaSmallTextures = NULL; } } //********************************************************************************************* // Reset/init the texture manager // The texture manager cannot be used until this function is called. // The world database calls this function every time it is reset. void CTextureManager::Reset ( ) //************************************* { uint32 u4_max_phys; uint32 u4_load_page; if (pvmeTextures) { u4_max_phys = pvmeTextures->u4GetMaxPhysicalMem(); u4_load_page = pvmeTextures->u4GetLoadPageSize(); Destroy(); } else { u4_load_page = u4TEXTURE_MEMORY_PAGE_SIZE; u4_max_phys = u4TEXTURE_MEMORY_MAX_PHYSICAL; } pvmeTextures = new CVirtualMem(u4TEXTURE_MEMORY_POOL_SIZE, u4_load_page,u4_max_phys); } //********************************************************************************************* rptr CTextureManager::prasPackTexture ( rptr pras_src, bool b_curved, ETexturePackTypes ept ) //************************************* { rptr pras_copy; if (b_curved) { if ((pras_src->iWidth<16) || (pras_src->iHeight<16) ) { ept = eptSMALLEST; } } else { // If this texture has maps that are less than u4SMALL_TEXTURE_SIZE in both directions // then it is a small texture... if ((pras_src->iWidthiHeightiWidth<16) || (pras_src->iHeight<16) ) { ept = eptSMALLEST; } if ( (pras_src->iWidth>256) || (pras_src->iHeight>256) ) { // the source texture is bigger than a pack surface therefore it cannot be packed. conLoadLog.Print(" Texture/Bump too big to pack: w=%d, h=%d\n", pras_src->iWidth, pras_src->iHeight); // // we cannot pack this texture or bump but we can just allocate it in Virtual Memory // if (pras_src->iPixelBits == iBUMPMAP_RESOLUTION && !(pras_src->pxf.cposG == 0x00F0)) { if (b_curved) { // // curved maps can be bigger than 256 x 256 because they are grown by 5 pixels. // There is an assert elsewhere to ensure that the curved bump map is not // tiled. // MEMLOG_ADD_COUNTER(emlBumpNoPack,1); MEMLOG_ADD_COUNTER(emlBumpNoPackMem,pras_src->iLineBytes()*pras_src->iHeight); AllocateBumpMapVector(); TrackBumpMap(pras_src, b_curved, false, ept); return pras_src; } } // // maps cannot be bigger than 256x256, unless they are curved, see above // #if (VER_TEST == TRUE) char str_buffer[1024]; sprintf(str_buffer, "A map on the current object is greater than 256 (%dx%d)",pras_src->iWidth,pras_src->iHeight); if (bTerminalError(ERROR_ASSERTFAIL, true, str_buffer, __LINE__)) DebuggerBreak(); #endif return rptr0; } if ( (pras_src->iWidth<8) || (pras_src->iHeight<8) ) { // source texture map is too small to pack, adjust the counters and log it in the // file.. if (pras_src->iPixelBits == iBUMPMAP_RESOLUTION && !(pras_src->pxf.cposG == 0x00F0)) { // we can only have curved bump maps that are less than 8x8 AlwaysAssert(b_curved); // we have not packed the bump map yet. MEMLOG_ADD_COUNTER(emlBumpNoPack,1); MEMLOG_ADD_COUNTER(emlBumpNoPackMem,pras_src->iLineBytes()*pras_src->iHeight); AllocateBumpMapVector(); TrackBumpMap(pras_src, b_curved, false, ept); return pras_src; } MEMLOG_ADD_COUNTER(emlTextureNoPack,1); MEMLOG_ADD_COUNTER(emlTextureNoPackMem,pras_src->iLineBytes()*pras_src->iHeight); conLoadLog.Print(" Texture too small to pack: w=%d, h=%d\n", pras_src->iWidth, pras_src->iHeight); // we should never get a texture that is this small... // This must be an always assert because the Virtual loader will fail. AlwaysAssert(0); return pras_src; } // // this surface has already been packed just return and don't log anything.... // if (pras_src->iLinePixels == 512) { // just enaure that this texture is packed in virtual memory Assert( gtxmTexMan.pvmeTextures->bVirtualAddressValid(pras_src->pSurface) ); return pras_src; } // // If we have a map which is not a curve and it is not a power of 2, report it // in the log file. // if (!b_curved) { // if the source is not a power of two then log it in the LoadLog file if ( (!bPowerOfTwo(pras_src->iWidth)) || (!bPowerOfTwo(pras_src->iHeight)) ) { conLoadLog.Print(" Not power of 2: w=%d, h=%d\n", pras_src->iWidth, pras_src->iHeight); conLoadLog.Print(" Rounded up to: w=%d, h=%d (wastage = %d bytes)\n", NextPowerOfTwo(pras_src->iWidth), NextPowerOfTwo(pras_src->iHeight), ((NextPowerOfTwo(pras_src->iWidth)*NextPowerOfTwo(pras_src->iHeight)) - (pras_src->iWidth*pras_src->iHeight)) * pras_src->iPixelBytes() ); } } // // are we packing a bump map, if so just log it because we cannot pack bumps until all // meshes have been loaded. // if (pras_src->iPixelBits == iBUMPMAP_RESOLUTION && !(pras_src->pxf.cposG == 0x00F0)) { AllocateBumpMapVector(); TrackBumpMap(pras_src, b_curved, false, ept); // return the source MEMLOG_ADD_COUNTER(emlBumpNoPack,1); MEMLOG_ADD_COUNTER(emlBumpNoPackMem,pras_src->iLineBytes()*pras_src->iHeight); return pras_src; } else { // we cannot have a curved texture... Assert(b_curved == false); } // // Create a packed raster that we can use to hold the source data, cast it from // a CRasterMem to the base class CRaster // pras_copy = rptr_cast(CRaster, prasFindPackLocation(pras_src, ept) ); AlwaysAssert(pras_copy); CopyRasterData(pras_src, pras_copy); // // Return the new raster as the one to use // return pras_copy; } //********************************************************************************************* // Function to copy 1 raster into another. The destination raster must be at least the same // size as the source and must be the same bit depth // void CTextureManager::CopyRasterData ( rptr pras_src, // original raster rptr pras_copy // new raster to be filled in ) // //************************************* { // // Copy the source data into the new raster // uint8* src; uint8* dst; // ensure that the new raster is big enough to take the source raster Assert(pras_copy->iWidth>=pras_src->iWidth); Assert(pras_copy->iHeight>=pras_src->iHeight); Assert(pras_copy->iPixelBits>=pras_src->iPixelBits); pras_src->Lock(); pras_copy->Lock(); // copy the source raster into the new raster which is a sub region of a pack // surface. src = (uint8*)pras_src->pAddress(0); dst = (uint8*)pras_copy->pAddress(0); for (int y = 0; yiHeight; y++) { for (int x = 0; xiWidth*(pras_src->iPixelBits/8); x++) { dst[x] = src[x]; } src = ((uint8*)src) + pras_src->iLineBytes(); dst = ((uint8*)dst) + pras_copy->iLineBytes(); } pras_src->Unlock(); pras_copy->Unlock(); // set the width and height of the new raster to be exaclty the same a the source, this // is done because the new raster may have been bigger than the source. pras_copy->iWidth = pras_src->iWidth; pras_copy->iHeight = pras_src->iHeight; // Since we have messed with the width and height, adjust the tiling info. pras_copy->UpdateTilingMasks(); } //********************************************************************************************* // rptr CTextureManager::prasFindPackLocation ( rptr pras_src, ETexturePackTypes ept ) // //************************************* { rptr pras_pack; int32 i4_empty = -1; int32 i4_start = 0; while (1) { for (int i = i4_start; iiGetPixelBits() == pras_src->iPixelBits) { // The bit depths match so check the mip level ETexturePackTypes ept_surface= aptpsTexturePageList[i]->eptGetType(); if (ept>eptTOP_LEVEL) { // we specified a mip level that is not 0 so obey the rule of packing // mip maps. Currently this is the mip level has to be within // some range of the packed mip level if ( (ept_surface <= eptTOP_LEVEL) || ( abs((int32)ept_surface - (int32)ept) > i4MipLevelDifference) ) continue; } else { // mip type 0 and all negative types are packed alone no matter what the // mip packing range is. This is so we can remove the top mip level and // no of trace of them will have to be loaded. if (ept_surface != ept) continue; } // // So lets try to add the map to this surface. // (find a block of the correct size in the quad tree // Sizes are rounded up to the next power of two) // pras_pack = aptpsTexturePageList[i]->prasAllocateRaster(pras_src); if (!pras_pack) { // cannot add the texture, go onto the next page continue; } return pras_pack; } } else { // we have a NULL pointer, so rememebr where it is in case we have to a page. if (i4_empty == -1) { i4_empty = i; } } } // at this point we have failed to add a texture to an existing page, either there // is not one at the correct bit depth or the source is too big to fit into a space // on an existing surface.... // i4_empty is the location in the array of an empty slot that we can allocate a page // into, if i4_empty is -1 then all available pages have been alloctaed and drastic // measures now need to be taken.... if (i4_empty == -1) { conLoadLog.Print(" Out of texture pages\n"); return rptr0; } // // allocate a new texture page in the array ay i4_empty, this page should be the same // bit depth as the source texture. // The mip level of a new texture page is that of the first texture to be packed in it, // aptpsTexturePageList[i4_empty] = new CRenderTexturePackSurface(pras_src->iPixelBits,ept); // start looking on the next pass from the element we have just added i4_start = i4_empty; } // we can never get to here but we have to keep the compiler happy. return rptr0; } //********************************************************************************************* // This should be called when loading is complete. // This function will go through all loaded bump maps and pack them, this cannot be done while // the bumps are being loaded in case they are curved after being packed. // void CTextureManager::PackBumpMaps ( ) // //************************************* { // if there is no bump list then there is nothing to pack if (pblBumpMaps) { // go through all of the packed bump maps and check for ones that are not packed for (TBumpList::iterator i = (*pblBumpMaps).begin(); i != (*pblBumpMaps).end() ; i++) { // if we are not packing curves and this map is curved, ignroe it and go on to the next if ( (!bPackCurved) && ((*i).bCurved) ) continue; // if the map is not already packed, process it if ((*i).bPacked == false) { rptr pras_packed; // pack the source bump map if ( ((*i).prasBumpMap->iWidth>256) || ((*i).prasBumpMap->iHeight>256) || (*i).bCurved ) { // // allocate a raster in virtual memory, it is too big to allocate from a // pack surface or if it is curved. // EMemType emt; if (((*i).prasBumpMap->iWidthiHeightiWidth<16) || ((*i).prasBumpMap->iHeight<16) ) { emt = emtTexManSubVirtual; } pras_packed = rptr_cast(CRasterMem, rptr_new CRasterMem((*i).prasBumpMap->iWidth, (*i).prasBumpMap->iHeight, (*i).prasBumpMap->iPixelBits, 0, &(*i).prasBumpMap->pxf, emt) ); } else { if (((*i).prasBumpMap->iWidth<16) || ((*i).prasBumpMap->iHeight<16) ) { Assert( (*i).eptMipLevel == eptSMALLEST); } // // allocate a raster in an existing pack surface... // pras_packed = prasFindPackLocation( (*i).prasBumpMap, (*i).eptMipLevel); } // if we get a packed raster we have succeded, so mark the surface as packed if (pras_packed) { MEMLOG_SUB_COUNTER(emlBumpNoPack,1); MEMLOG_SUB_COUNTER(emlBumpNoPackMem, (*i).prasBumpMap->iLineBytes()*(*i).prasBumpMap->iHeight); // change the bump raster to be like the copy // pras_copy if going to get corrupted by this call but that does not matter // because it is a local and will be destroyed before the end of this function. (*i).prasBumpMap->ChangeRaster( pras_packed ); (*i).prasBumpMap->UpdateTilingMasks(); // WE DO NOT NEED TO MODIFY THE IMAGE LOG FOR RASTERS THAT ARE CHANGED BECAUSE THE // RASTER ADDRESS DOES NOT CHANGE, ONLY THE CLASS CONTENTS WHICH ARE REFERENCED WHEN // WE SAVE THE IMAGE FILE. // mark this element as packed. (*i).bPacked = true; } } } } } //********************************************************************************************** // Create Image directory file // #define MAX_PAL 128 // void CTextureManager::CreatePackedImageDirectory ( const char* str_dir_name ) //************************************* { uint32 u4_palid[MAX_PAL]; CPal* ppal[MAX_PAL]; uint32 u4_next_pal = 0; uint32 u4_texture = 0; uint32 u4_bump_map = 0; uint32 u4_parent_bump = 0; TVirtualImageList::iterator i; TCurvedParentList::iterator j; memset(u4_palid, 0, sizeof(uint32)*MAX_PAL); memset(ppal, 0, sizeof(CPal*)*MAX_PAL); dprintf("Creating Packed Image Directory\n"); dprintf("********************************************************\n"); // if there is image log list then return if (pvilVirtualImageMaps == NULL) { dprintf("ABORT: No textures\n"); return; } //****************************************************************************************** // // Remove parent bump maps that are also used as normal bump maps. After this operation the // parent list should contain textures that are never seen. See Below // dprintf("Validating parent bump maps...\n"); if (pcplParentBumpMaps) { for (i = (*pvilVirtualImageMaps).begin(); i != (*pvilVirtualImageMaps).end() ; i++) { // we have to be a bump map.... if ( (*i).ptexTexture->seterfFeatures[erfBUMP]) { // the top 32 bits of our hash value must be zero, otherwise we cannot be a parent map.. if ( (uint32)(((*i).u8HashValue)>>32) == 0 ) { bool b_found; while (1) { b_found = false; // go through all of the parent maps in the SET.... for (j = (*pcplParentBumpMaps).begin(); j != (*pcplParentBumpMaps).end() ; ++j) { // if we find a matching hash value, remove the parent. Break out and go through the list again // keep doing this until we do not find a parent. if ( (*j).u4HashValue == (uint32)((*i).u8HashValue & 0xffffffff) ) { dprintf("WARNING: Parent of curved bump map also used as a non curved bump map, updating directory\n"); (*pcplParentBumpMaps).erase(j); b_found = true; break; } } if (!b_found) break; } } } } } //****************************************************************************************** // // make a unique list of palette IDs // dprintf("Creating palette ID array...\n"); for (i = (*pvilVirtualImageMaps).begin(); i != (*pvilVirtualImageMaps).end() ; i++) { if ((*i).prasTexture->pxf) { if ((*i).prasTexture->pxf.ppalAttached) { bool b_found = false; for (int32 i4 = 0; i4pxf.ppalAttached->u4GetHashValue() && ppal[i4] == (*i).prasTexture->pxf.ppalAttached ) { b_found = true; break; } } if (!b_found) { u4_palid[u4_next_pal] = (*i).prasTexture->pxf.ppalAttached->u4GetHashValue(); ppal[u4_next_pal] = (*i).prasTexture->pxf.ppalAttached; u4_next_pal++; } } } } //****************************************************************************************** // // Go through the list of parents of curved bump maps and pack them. These maps are going // to be at the end of the VM image because nothing can get packed after here. Also nothing // can get loaded after this. // // Parent bump maps are bump maps that are the source of curved/unique bump maps. These // maps although packed should never be referenced or loaded. They are only here to keep // the loader happy. // dprintf("Packing parent bump maps...\n"); // if there is no parent bump list then there is nothing to pack if (pcplParentBumpMaps) { // go through all of the packed bump maps and check for ones that are not packed for (j = (*pcplParentBumpMaps).begin(); j != (*pcplParentBumpMaps).end() ; j++) { // if the map is not already packed, process it. // NOTE: The mapp cannot be already packed because we only call this function once and duplicates have // been removed. if ((*j).bPacked == false) { // pack the source bump map in a surface type of a curved parent rptr pras_packed;// = prasFindPackLocation( (*j).prasBumpMap, eptCURVED_PARENT); if ( ((*j).prasBumpMap->iWidth>256) || ((*j).prasBumpMap->iHeight>256) ) { // // allocate a raster in virtual memory, it is too big to allocate from a // pack surface or if it is curved. // pras_packed = rptr_cast(CRasterMem, rptr_new CRasterMem((*j).prasBumpMap->iWidth, (*j).prasBumpMap->iHeight, (*j).prasBumpMap->iPixelBits, 0, (CPixelFormat*)&(*j).prasBumpMap->pxf, emtTexManVirtual) ); } else { // // allocate a raster in an existing pack surface... // pras_packed = prasFindPackLocation( (*j).prasBumpMap, eptCURVED_PARENT); } // if we get a packed raster we have succeded, so mark the surface as packed if (pras_packed) { // cast away the STL const on the SCurvedParentElement structure. SCurvedParentElement* cpe = (SCurvedParentElement*)&(*j); // change the packed raster to be like pras_packed cpe->prasBumpMap->ChangeRaster( pras_packed ); // we have chnaged the raster so update the tiling masks cpe->prasBumpMap->UpdateTilingMasks(); // mark this element as packed. cpe->bPacked = true; } else { AlwaysAssert(0); } } else { // we cannot have already packed this map Assert(0); } u4_parent_bump++; } } //****************************************************************************************** // // Save out the directory file // int32 i4_count; uint32 u4_bytes; HANDLE h_save; SDirectoryFileHeader dfh; SDirectoryFileChunk* pdfc = NULL; dprintf("Creating PID file...\n"); // Create the save file h_save=CreateFile(str_dir_name,GENERIC_WRITE,0,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH,NULL); if (h_save==INVALID_HANDLE_VALUE) { AlwaysAssert(0); return; } //****************************************************************************************** // // Check how many of the logged textures are still in system memory, this should // only apply to bump maps. You cannot get an entry in this list for a texture that // is in system memory. // uint32 u4_tex_valid = 0; for (i = (*pvilVirtualImageMaps).begin(); i != (*pvilVirtualImageMaps).end() ; i++) { if (!gtxmTexMan.pvmeTextures->bVirtualAddressValid( (*i).prasTexture->pSurface ) ) { // address is invalid, this has got to be a bump map... AlwaysAssert( (*i).ptexTexture->seterfFeatures[erfBUMP] ); continue; } u4_tex_valid++; } // we should have counted all the elements in the list if (u4_tex_valid != ((*pvilVirtualImageMaps).size()) ) { dprintf("ERROR: SWP file invalid - unpacked bump maps\n"); } dprintf("Creating PID directory header...\n"); //****************************************************************************************** // // Directory file header... // dfh.u4Version = u4DIRECTORY_FILE_VERSION; dfh.u4BumpMapBitDepth = iBUMPMAP_RESOLUTION; dfh.u4RasterChunkOffset = 0; if (pcplParentBumpMaps) dfh.u4RasterChunkCount = u4_tex_valid + ((*pcplParentBumpMaps).size()); else dfh.u4RasterChunkCount = u4_tex_valid; dfh.u4PaletteOffset = sizeof(dfh); dfh.u4PaletteCount = u4_next_pal; dfh.u4PageableOffset = pvmsaSmallTextures->u4AllocatorLength(); dfh.u4NonPageableCount = pvmsaSmallTextures->u4MemoryAllocated(); //****************************************************************************************** // // Write out the partial header, the offset to the rasters in not complete // if (WriteFile(h_save,&dfh,sizeof(dfh),(DWORD*)&u4_bytes,NULL) == false) { CloseHandle(h_save); AlwaysAssert(0); return; } AlwaysAssert(u4_bytes == sizeof(dfh)); //****************************************************************************************** // // Write out all of the palettes // dprintf("Writing ordered/unique palettes...\n"); for (uint32 u4_pal = 0; u4_palaclrPalette.uLen * sizeof(CColour)); SDirectoryPaletteChunk* pdpc = (SDirectoryPaletteChunk*) new char[u4_size]; pdpc->u4Size = u4_size; pdpc->u4ColoursUsed = ppal[u4_pal]->aclrPalette.uLen; pdpc->u4HashValue = ppal[u4_pal]->u4GetHashValue(); memcpy(&pdpc->clr[0], &ppal[u4_pal]->aclrPalette[0], ppal[u4_pal]->aclrPalette.uLen * sizeof(CColour) ); // // Write out the palette chunk // if (WriteFile(h_save,pdpc,u4_size,(DWORD*)&u4_bytes,NULL) == false) { CloseHandle(h_save); AlwaysAssert(0); return; } AlwaysAssert(u4_bytes == u4_size); delete pdpc; } //****************************************************************************************** // //Fix up the file header so the offset to the raster chunks is correct // dprintf("Updating directory header...\n"); uint32 u4_current_pos = SetFilePointer(h_save,0,NULL,FILE_CURRENT); SetFilePointer(h_save,0,NULL,FILE_BEGIN); dfh.u4RasterChunkOffset = u4_current_pos; if (WriteFile(h_save,&dfh,sizeof(dfh),(DWORD*)&u4_bytes,NULL) == false) { CloseHandle(h_save); AlwaysAssert(0); return; } AlwaysAssert(u4_bytes == sizeof(dfh)); // put the file pointer back to where it was. SetFilePointer(h_save,u4_current_pos,NULL,FILE_BEGIN); //****************************************************************************************** // //Write out the raster chunks // dprintf("Writing texture directory entries...\n"); for (i = (*pvilVirtualImageMaps).begin(); i != (*pvilVirtualImageMaps).end() ; i++) { if (!gtxmTexMan.pvmeTextures->bVirtualAddressValid( (*i).prasTexture->pSurface ) ) continue; if ( (*i).ptexTexture->seterfFeatures[erfBUMP] ) { u4_bump_map++; } else { u4_texture++; } uint32 u4_size = sizeof(SDirectoryFileChunk); pdfc = (SDirectoryFileChunk*) new char[u4_size]; // fill in the allocated file chunk pdfc->u4Size = u4_size; pdfc->u4VMOffset = (uint32)(*i).prasTexture->pSurface - (uint32)gtxmTexMan.pvmeTextures->pvGetBase(); pdfc->u4Width = (*i).prasTexture->iWidth; pdfc->u4Height = (*i).prasTexture->iHeight; pdfc->u4Stride = (*i).prasTexture->iLineBytes(); pdfc->u4Bits = (*i).prasTexture->iPixelBits; pdfc->clrConstCol = (*i).prasTexture->clrFromPixel( (*i).ptexTexture->tpSolid ); pdfc->iTransparent = (*i).ptexTexture->seterfFeatures[erfTRANSPARENT]; pdfc->iBumpMap = (*i).ptexTexture->seterfFeatures[erfBUMP]; pdfc->iOcclusion = (*i).ptexTexture->seterfFeatures[erfOCCLUDE]; pdfc->u8HashValue = (*i).u8HashValue; // // set the palette ID // if ((*i).prasTexture->pxf) { if ((*i).prasTexture->pxf.ppalAttached) { bool b_found = false; for (i4_count = 0; i4_count<(int32)u4_next_pal; i4_count++) { if ( u4_palid[i4_count] == (*i).prasTexture->pxf.ppalAttached->u4GetHashValue() && ppal[i4_count] == (*i).prasTexture->pxf.ppalAttached ) { b_found = true; break; } } // we must always find the palette AlwaysAssert(b_found); pdfc->u4Palette = (uint32)i4_count; } else { pdfc->u4Palette = 0xffffffff; } } else { pdfc->u4Palette = 0xffffffff; } // // Write out the chunk // if (WriteFile(h_save,pdfc,u4_size,(DWORD*)&u4_bytes,NULL) == false) { CloseHandle(h_save); AlwaysAssert(0); return; } AlwaysAssert(u4_bytes == u4_size); // delete and go around to the next delete pdfc; pdfc = NULL; } //****************************************************************************************** // // The bumps maps below are the maps that are the source or parent of a curved map. These // source maps themselves are neve referenced or used. The parent bump maps that are used // as flat bump maps on other faces/meshes should have been removed from this list. // The hash value of these parent faces is the same as any other top level texture map, // the bottom 32 bits being the hash of the name and the top 32 bits being zero. // These are packed as normal rasters, which are packed at the end of the VM image so // they never get loaded and never have to be seeked over. These textures have an invalid // constant colour and the default palette. These textures are never drawn and are only // present so the loader does not fail. // // NOTE: CURVED TEXTURES THAT ARE USED ELSEWHERE SHOULD NOT BE IN THIS LIST // dprintf("Writing parent bump map directory entries...\n"); if (pcplParentBumpMaps) { for (j = (*pcplParentBumpMaps).begin(); j != (*pcplParentBumpMaps).end() ; j++) { uint32 u4_size = sizeof(SDirectoryFileChunk); pdfc = (SDirectoryFileChunk*) new char[u4_size]; // fill in the allocated file chunk pdfc->u4Size = u4_size; pdfc->u4VMOffset = (uint32)(*j).prasBumpMap->pSurface - (uint32)gtxmTexMan.pvmeTextures->pvGetBase(); pdfc->u4Width = (*j).prasBumpMap->iWidth; pdfc->u4Height = (*j).prasBumpMap->iHeight; pdfc->u4Stride = (*j).prasBumpMap->iLineBytes(); pdfc->u4Bits = (*j).prasBumpMap->iPixelBits; pdfc->clrConstCol = 0xffffffff; pdfc->iBumpMap = 1; pdfc->iTransparent = (*j).bTransparent; pdfc->u8HashValue = (uint64) ((*j).u4HashValue); pdfc->iOcclusion = 0; // // set the palette ID // if ((*j).prasBumpMap->pxf) { if ((*j).prasBumpMap->pxf.ppalAttached) { bool b_found = false; for (i4_count = 0; i4_count<(int32)u4_next_pal; i4_count++) { if ( u4_palid[i4_count] == (*j).prasBumpMap->pxf.ppalAttached->u4GetHashValue() && ppal[i4_count] == (*j).prasBumpMap->pxf.ppalAttached ) { b_found = true; break; } } // we must always find the palette AlwaysAssert(b_found); pdfc->u4Palette = (uint32)i4_count; } else { pdfc->u4Palette = 0xffffffff; } } else { pdfc->u4Palette = 0xffffffff; } // // Write out the chunk // if (WriteFile(h_save,pdfc,u4_size,(DWORD*)&u4_bytes,NULL) == false) { CloseHandle(h_save); AlwaysAssert(0); return; } AlwaysAssert(u4_bytes == u4_size); // delete and go around to the next delete pdfc; pdfc = NULL; } } CloseHandle(h_save); dprintf("********************************************************\n"); dprintf("Packed Image Directory Created\n"); dprintf("%d Palettes\n",u4_next_pal); dprintf("%d Texture maps\n",u4_texture); dprintf("%d Bump maps\n",u4_bump_map); dprintf("%d Curved bump map parents\n",u4_parent_bump); }