ResourceBase ResourceManager::find(const Torque::Path &path)
{
#ifdef TORQUE_DEBUG_RES_MANAGER
   Con::printf( "ResourceManager::find : [%s]", path.getFullPath().c_str() );
#endif

   ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() );

   if ( iter == mResourceHeaderMap.end() )
      return ResourceBase();

   ResourceHeaderMap::Pair &pair = *iter;

   ResourceBase::Header	*header = pair.value;

   return ResourceBase(header);
}
void ResourceManager::reloadResource( const Torque::Path &path, bool showMessage )
{
   if ( showMessage )
      Con::warnf( "[ResourceManager::notifiedFileChanged] : File changed [%s]", path.getFullPath().c_str() );

   ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() );
   if ( iter != mResourceHeaderMap.end() )
   {
      ResourceBase::Header	*header = (*iter).value;
      mResourceHeaderMap.erase( iter );

      // Move the resource into the previous resource map.
      iter = mPrevResourceHeaderMap.findOrInsert( path );
      iter->value = header;
   }
	
   // Now notify users of the resource change so they 
   // can release and reload.
   mChangeSignal.trigger( path );
}
bool MacFileSystemChangeNotifier::internalAddNotification( const Torque::Path& dir )
{
    // Map the path.

    Torque::Path fullFSPath = mFS->mapTo( dir );
    String osPath = PathToOS( fullFSPath );

    // Create event stream.

    Event* event = new Event;

    CFStringRef path = CFStringCreateWithCharacters( NULL, osPath.utf16(), osPath.numChars() );
    CFArrayRef paths = CFArrayCreate( NULL, ( const void** ) &path, 1, NULL );

    FSEventStreamRef stream;
    CFAbsoluteTime latency = 3.f;

    FSEventStreamContext context;
    dMemset( &context, 0, sizeof( context ) );
    context.info = event;

    stream = FSEventStreamCreate(
                 NULL,
                 &fsNotifyCallback,
                 &context,
                 paths,
                 kFSEventStreamEventIdSinceNow,
                 latency,
                 kFSEventStreamCreateFlagNone
             );

    event->mStream = stream;
    event->mDir = dir;
    event->mHasChanged = false;

    mEvents.push_back( event );

    // Put it in the run loop and start the stream.

    FSEventStreamScheduleWithRunLoop( stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
    FSEventStreamStart( stream );

    CFRelease( path );
    CFRelease( paths );

#ifdef DEBUG_SPEW
    Platform::outputDebugString( "[MacFileSystemChangeNotifier] Added change notification %i to '%s' (full path: %s)",
                                 mEvents.size(), dir.getFullPath().c_str(), osPath.c_str() );
#endif

    return true;
}
//-----------------------------------------------------------------------------
/// Copy a texture from a KMZ to a cache. Note that the texture filename is modified
void copySketchupTexture(const Torque::Path &path, String &textureFilename)
{
   if (textureFilename.isEmpty())
      return;

   Torque::Path texturePath(textureFilename);
   texturePath.setExtension(findTextureExtension(texturePath));

   String cachedTexFilename = String::ToString("%s_%s.cached",
      TSShapeLoader::getShapePath().getFileName().c_str(), texturePath.getFileName().c_str());

   Torque::Path cachedTexPath;
   cachedTexPath.setRoot(path.getRoot());
   cachedTexPath.setPath(path.getPath());
   cachedTexPath.setFileName(cachedTexFilename);
   cachedTexPath.setExtension(texturePath.getExtension());

   FileStream *source;
   FileStream *dest;
   if ((source = FileStream::createAndOpen(texturePath.getFullPath(), Torque::FS::File::Read)) == NULL)
      return;

   if ((dest = FileStream::createAndOpen(cachedTexPath.getFullPath(), Torque::FS::File::Write)) == NULL)
   {
      delete source;
      return;
   }

   dest->copyFrom(source);

   delete dest;
   delete source;

   // Update the filename in the material
   cachedTexPath.setExtension("");
   textureFilename = cachedTexPath.getFullPath();
}
//-----------------------------------------------------------------------------
/// Check if an up-to-date cached DTS is available for this DAE file
bool ColladaShapeLoader::canLoadCachedDTS(const Torque::Path& path)
{
   // Generate the cached filename
   Torque::Path cachedPath(path);
   cachedPath.setExtension("cached.dts");

   // Check if a cached DTS newer than this file is available
   FileTime cachedModifyTime;
   if (Platform::getFileTimes(cachedPath.getFullPath(), NULL, &cachedModifyTime))
   {
      bool forceLoadDAE = Con::getBoolVariable("$collada::forceLoadDAE", false);

      FileTime daeModifyTime;
      if (!Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime) ||
         (!forceLoadDAE && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0) ))
      {
         // DAE not found, or cached DTS is newer
         return true;
      }
   }

   return false;
}
bool MacFileSystemChangeNotifier::internalRemoveNotification( const Torque::Path& dir )
{
    for( U32 i = 0, num = mEvents.size(); i < num; ++ i )
        if( mEvents[ i ]->mDir == dir )
        {
#ifdef DEBUG_SPEW
            Platform::outputDebugString( "[MacFileSystemChangeNotifier] Removing change notification %i from '%s'",
                                         i + 1, dir.getFullPath().c_str() );
#endif

            FSEventStreamStop( mEvents[ i ]->mStream );
            FSEventStreamInvalidate( mEvents[ i ]->mStream );
            FSEventStreamRelease( mEvents[ i ]->mStream );

            SAFE_DELETE( mEvents[ i ] );

            mEvents.erase( i );

            return true;
        }

    return false;
}
static S32 buildFileList(const char* pattern, bool recurse, bool multiMatch)
{
   static const String sSlash( "/" );

   sgFindFilesResults.clear();

   String sPattern(Torque::Path::CleanSeparators(pattern));
   if(sPattern.isEmpty())
   {
      Con::errorf("findFirstFile() requires a search pattern");
      return -1;
   }

   if(!Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), sPattern.c_str()))
   {
      Con::errorf("findFirstFile() given initial directory cannot be expanded: '%s'", pattern);
      return -1;
   }
   sPattern = String::ToString(sgScriptFilenameBuffer);

   String::SizeType slashPos = sPattern.find('/', 0, String::Right);
//    if(slashPos == String::NPos)
//    {
//       Con::errorf("findFirstFile() missing search directory or expression: '%s'", sPattern.c_str());
//       return -1;
//    }

   // Build the initial search path
   Torque::Path givenPath(Torque::Path::CompressPath(sPattern));
   givenPath.setFileName("*");
   givenPath.setExtension("*");

   if(givenPath.getPath().length() > 0 && givenPath.getPath().find('*', 0, String::Right) == givenPath.getPath().length()-1)
   {
      // Deal with legacy searches of the form '*/*.*'
      String suspectPath = givenPath.getPath();
      String::SizeType newLen = suspectPath.length()-1;
      if(newLen > 0 && suspectPath.find('/', 0, String::Right) == suspectPath.length()-2)
      {
         --newLen;
      }
      givenPath.setPath(suspectPath.substr(0, newLen));
   }

   Torque::FS::FileSystemRef fs = Torque::FS::GetFileSystem(givenPath);
   //Torque::Path path = fs->mapTo(givenPath);
   Torque::Path path = givenPath;
   
   // Make sure that we have a root so the correct file system can be determined when using zips
   if(givenPath.isRelative())
      path = Torque::Path::Join(Torque::FS::GetCwd(), '/', givenPath);
   
   path.setFileName(String::EmptyString);
   path.setExtension(String::EmptyString);
   if(!Torque::FS::IsDirectory(path))
   {
      Con::errorf("findFirstFile() invalid initial search directory: '%s'", path.getFullPath().c_str());
      return -1;
   }

   // Build the search expression
   const String expression(slashPos != String::NPos ? sPattern.substr(slashPos+1) : sPattern);
   if(expression.isEmpty())
   {
      Con::errorf("findFirstFile() requires a search expression: '%s'", sPattern.c_str());
      return -1;
   }

   S32 results = Torque::FS::FindByPattern(path, expression, recurse, sgFindFilesResults, multiMatch );
   if(givenPath.isRelative() && results > 0)
   {
      // Strip the CWD out of the returned paths
      // MakeRelativePath() returns incorrect results (it adds a leading ..) so doing this the dirty way
      const String cwd = Torque::FS::GetCwd().getFullPath();
      for(S32 i = 0;i < sgFindFilesResults.size();++i)
      {
         String str = sgFindFilesResults[i];
         if(str.compare(cwd, cwd.length(), String::NoCase) == 0)
            str = str.substr(cwd.length());
         sgFindFilesResults[i] = str;
      }
   }
   return results;
}
Beispiel #8
0
char* GFXGLShader::_handleIncludes( const Torque::Path& path, FileStream *s )
{
   // TODO:  The #line pragma on GLSL takes something called a
   // "source-string-number" which it then never explains.
   //
   // Until i resolve this mystery i disabled this.
   //
   //String linePragma = String::ToString( "#line 1 \r\n");
   //U32 linePragmaLen = linePragma.length();

   U32 shaderLen = s->getStreamSize();
   char* buffer = (char*)dMalloc(shaderLen + 1);
   //dStrncpy( buffer, linePragma.c_str(), linePragmaLen );
   s->read(shaderLen, buffer);
   buffer[shaderLen] = 0;
   
   char* p = dStrstr(buffer, "#include");
   while(p)
   {
      char* q = p;
      p += 8;
      if(dIsspace(*p))
      {
         U32 n = 0;
         while(dIsspace(*p)) ++p;
         AssertFatal(*p == '"', "Bad #include directive");
         ++p;
         static char includeFile[256];
         while(*p != '"')
         {
            AssertFatal(*p != 0, "Bad #include directive");
            includeFile[n++] = *p++;
            AssertFatal(n < sizeof(includeFile), "#include directive too long");
         }
         ++p;
         includeFile[n] = 0;

         // First try it as a local file.
         Torque::Path includePath = Torque::Path::Join(path.getPath(), '/', includeFile);
         includePath = Torque::Path::CompressPath(includePath);
         
         FileStream includeStream;

         if ( !includeStream.open( includePath, Torque::FS::File::Read ) )
         {
            // Try again assuming the path is absolute 
            // and/or relative.
            includePath = String( includeFile );
            includePath = Torque::Path::CompressPath(includePath);
            if ( !includeStream.open( includePath, Torque::FS::File::Read ) )
            {
               AssertISV(false, avar("failed to open include '%s'.", includePath.getFullPath().c_str()));

               if ( smLogErrors )
                  Con::errorf( "GFXGLShader::_handleIncludes - Failed to open include '%s'.", 
                     includePath.getFullPath().c_str() );

               // Fail... don't return the buffer.
               dFree(buffer);
               return NULL;
            }
         }

         char* includedText = _handleIncludes(includePath, &includeStream);
         
         // If a sub-include fails... cleanup and return.
         if ( !includedText )
         {
            dFree(buffer);
            return NULL;
         }
         
         // TODO: Disabled till this is fixed correctly.
         //
         // Count the number of lines in the file 
         // before the include.
         /*
         U32 includeLine = 0;
         {
            char* nl = dStrstr( buffer, "\n" );
            while ( nl )
            {
               includeLine++;
               nl = dStrstr( nl, "\n" );
               if(nl) ++nl;
            }
         }
         */

         String manip(buffer);
         manip.erase(q-buffer, p-q);
         String sItx(includedText);

         // TODO: Disabled till this is fixed correctly.
         //
         // Add a new line pragma to restore the proper
         // file and line number after the include.
         //sItx += String::ToString( "\r\n#line %d \r\n", includeLine );
         
         dFree(includedText);
         manip.insert(q-buffer, sItx);
         char* manipBuf = dStrdup(manip.c_str());
         p = manipBuf + (p - buffer);
         dFree(buffer);
         buffer = manipBuf;
      }
      p = dStrstr(p, "#include");
   }
   
   return buffer;
}
Beispiel #9
0
bool GFXGLShader::_loadShaderFromStream(  GLuint shader, 
                                          const Torque::Path &path, 
                                          FileStream *s, 
                                          const Vector<GFXShaderMacro> &macros )
{
   Vector<char*> buffers;
   Vector<U32> lengths;
   
   // The GLSL version declaration must go first!
   const char *versionDecl = "#version 150\r\n";
   buffers.push_back( dStrdup( versionDecl ) );
   lengths.push_back( dStrlen( versionDecl ) );

   if(GFXGL->mCapabilities.shaderModel5)
   {
      const char *extension = "#extension GL_ARB_gpu_shader5 : enable\r\n";
      buffers.push_back( dStrdup( extension ) );
      lengths.push_back( dStrlen( extension ) );
   }

   const char *newLine = "\r\n";
   buffers.push_back( dStrdup( newLine ) );
   lengths.push_back( dStrlen( newLine ) );

   // Now add all the macros.
   for( U32 i = 0; i < macros.size(); i++ )
   {
      if(macros[i].name.isEmpty())  // TODO OPENGL
         continue;

      String define = String::ToString( "#define %s %s\n", macros[i].name.c_str(), macros[i].value.c_str() );
      buffers.push_back( dStrdup( define.c_str() ) );
      lengths.push_back( define.length() );
   }
   
   // Now finally add the shader source.
   U32 shaderLen = s->getStreamSize();
   char *buffer = _handleIncludes(path, s);
   if ( !buffer )
      return false;
   
   buffers.push_back(buffer);
   lengths.push_back(shaderLen);
   
   glShaderSource(shader, buffers.size(), (const GLchar**)const_cast<const char**>(buffers.address()), NULL);

#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX)
   FileStream stream;
   if ( !stream.open( path.getFullPath()+"_DEBUG", Torque::FS::File::Write ) )
   {
      AssertISV(false, avar("GFXGLShader::initShader - failed to write debug shader '%s'.", path.getFullPath().c_str()));
   }

   for(int i = 0; i < buffers.size(); ++i)
         stream.writeText(buffers[i]);
#endif

   // Cleanup the shader source buffer.
   for ( U32 i=0; i < buffers.size(); i++ )
      dFree( buffers[i] );

   glCompileShader(shader);

   return true;
}
Beispiel #10
0
bool GFXD3D9Shader::_compileShader( const Torque::Path &filePath, 
                                    const String& target,                                  
                                    const D3DXMACRO *defines, 
                                    GenericConstBufferLayout* bufferLayoutF, 
                                    GenericConstBufferLayout* bufferLayoutI,
                                    Vector<GFXShaderConstDesc> &samplerDescriptions )
{
   PROFILE_SCOPE( GFXD3D9Shader_CompileShader );

   HRESULT res = D3DERR_INVALIDCALL;
   LPD3DXBUFFER code = NULL;
   LPD3DXBUFFER errorBuff = NULL;

#ifdef TORQUE_DEBUG
   U32 flags = D3DXSHADER_DEBUG;
#else
   U32 flags = 0;
#endif

#ifdef TORQUE_OS_XENON
   flags |= D3DXSHADER_PREFER_FLOW_CONTROL;
#endif

#ifdef D3DXSHADER_USE_LEGACY_D3DX9_31_DLL
   if( D3DX_SDK_VERSION >= 32 )
   {
      // will need to use old compiler for 1_1 shaders - check for pixel
      // or vertex shader with appropriate version.
      if ((target.compare("vs1", 3) == 0) || (target.compare("vs_1", 4) == 0))      
         flags |= D3DXSHADER_USE_LEGACY_D3DX9_31_DLL;

      if ((target.compare("ps1", 3) == 0) || (target.compare("ps_1", 4) == 0))
         flags |= D3DXSHADER_USE_LEGACY_D3DX9_31_DLL;
   }
#endif

#if !defined(TORQUE_OS_XENON) && (D3DX_SDK_VERSION <= 40)
#error This version of the DirectX SDK is too old. Please install a newer version of the DirectX SDK: http://msdn.microsoft.com/en-us/directx/default.aspx
#endif

   ID3DXConstantTable* table = NULL;

   static String sHLSLStr( "hlsl" );
   static String sOBJStr( "obj" );

   // Is it an HLSL shader?
   if ( filePath.getExtension().equal(sHLSLStr, String::NoCase) )   
   {
      FrameAllocatorMarker fam;
      char *buffer = NULL;

      // Set this so that the D3DXInclude::Open will have this 
      // information for relative paths.
      smD3DXInclude->setPath( filePath.getRootAndPath() );

      FileStream s;
      if ( !s.open( filePath, Torque::FS::File::Read ) )
      {
         AssertISV(false, avar("GFXD3D9Shader::initShader - failed to open shader '%s'.", filePath.getFullPath().c_str()));

         if ( smLogErrors )
            Con::errorf( "GFXD3D9Shader::_compileShader - Failed to open shader file '%s'.", 
               filePath.getFullPath().c_str() );

         return false;
      }

      // Convert the path which might have virtualized
      // mount paths to a real file system path.
      Torque::Path realPath;
      if ( !FS::GetFSPath( filePath, realPath ) )
         realPath = filePath;

      // Add a #line pragma so that error and warning messages
      // returned by the HLSL compiler report the right file.
      String linePragma = String::ToString( "#line 1 \"%s\"\r\n", realPath.getFullPath().c_str() );
      U32 linePragmaLen = linePragma.length();

      U32 bufSize = s.getStreamSize();
      buffer = (char *)fam.alloc( bufSize + linePragmaLen + 1 );
      dStrncpy( buffer, linePragma.c_str(), linePragmaLen );
      s.read( bufSize, buffer + linePragmaLen );
      buffer[bufSize+linePragmaLen] = 0;

      res = GFXD3DX.D3DXCompileShader( buffer, bufSize + linePragmaLen, defines, smD3DXInclude, "main", 
         target, flags, &code, &errorBuff, &table );
   }

   // Is it a precompiled obj shader?
   else if ( filePath.getExtension().equal( sOBJStr, String::NoCase ) )
   {     
      FileStream  s;
      if(!s.open(filePath, Torque::FS::File::Read))
      {
         AssertISV(false, avar("GFXD3D9Shader::initShader - failed to open shader '%s'.", filePath.getFullPath().c_str()));

         if ( smLogErrors )
            Con::errorf( "GFXD3D9Shader::_compileShader - Failed to open shader file '%s'.", 
               filePath.getFullPath().c_str() );

         return false;
      }

      res = GFXD3DX.D3DXCreateBuffer(s.getStreamSize(), &code);
      AssertISV(res == D3D_OK, "Unable to create buffer!");
      s.read(s.getStreamSize(), code->GetBufferPointer());
      
      if (res == D3D_OK)
      {
         DWORD* data = (DWORD*) code->GetBufferPointer();
         res = GFXD3DX.D3DXGetShaderConstantTable(data, &table);
      }
   }
   else
   {
      if ( smLogErrors )
         Con::errorf( "GFXD3D9Shader::_compileShader - Unsupported shader file type '%s'.", 
            filePath.getFullPath().c_str() );

      return false;
   }

   if ( res != D3D_OK && smLogErrors )
      Con::errorf( "GFXD3D9Shader::_compileShader - Error compiling shader: %s: %s (%x)", 
         DXGetErrorStringA(res), DXGetErrorDescriptionA(res), res );

   if ( errorBuff )
   {
      // remove \n at end of buffer
      U8 *buffPtr = (U8*) errorBuff->GetBufferPointer();
      U32 len = dStrlen( (const char*) buffPtr );
      buffPtr[len-1] = '\0';

      if( res != D3D_OK )
      {
         if ( smLogErrors )
            Con::errorf( "   %s", (const char*) errorBuff->GetBufferPointer() );
      }
      else
      {
         if ( smLogWarnings )
            Con::warnf( "%s", (const char*) errorBuff->GetBufferPointer() );
      }
   }
   else if ( code == NULL && smLogErrors )
      Con::errorf( "GFXD3D9Shader::_compileShader - no compiled code produced; possibly missing file '%s'.", 
         filePath.getFullPath().c_str() );

   // Create the proper shader if we have code
   if( code != NULL )
   {
      #ifndef TORQUE_SHIPPING

         LPD3DXBUFFER disassem = NULL;
         D3DXDisassembleShader( (DWORD*)code->GetBufferPointer(), false, NULL, &disassem );
         mDissasembly = (const char*)disassem->GetBufferPointer();         
         SAFE_RELEASE( disassem );

         if ( gDisassembleAllShaders )
         {
             String filename = filePath.getFullPath();
            filename.replace( ".hlsl", "_dis.txt" );

            FileStream *fstream = FileStream::createAndOpen( filename, Torque::FS::File::Write );
            if ( fstream )
            {            
               fstream->write( mDissasembly );
               fstream->close();
               delete fstream;   
            }
         }

      #endif

      if (target.compare("ps_", 3) == 0)      
         res = mD3D9Device->CreatePixelShader( (DWORD*)code->GetBufferPointer(), &mPixShader );
      else
         res = mD3D9Device->CreateVertexShader( (DWORD*)code->GetBufferPointer(), &mVertShader );

      if (res == S_OK)
         _getShaderConstants(table, bufferLayoutF, bufferLayoutI, samplerDescriptions);

#ifdef TORQUE_ENABLE_CSF_GENERATION

      // Ok, we've got a valid shader and constants, let's write them all out.
      if ( !_saveCompiledOutput(filePath, code, bufferLayoutF, bufferLayoutI) && smLogErrors )
         Con::errorf( "GFXD3D9Shader::_compileShader - Unable to save shader compile output for: %s", 
            filePath.getFullPath().c_str() );

#endif

      SAFE_RELEASE(table);

      if ( res != S_OK && smLogErrors )
         Con::errorf( "GFXD3D9Shader::_compileShader - Unable to create shader for '%s'.", 
            filePath.getFullPath().c_str() );
   }

   bool result = code != NULL && res == S_OK;

   SAFE_RELEASE( code );
   SAFE_RELEASE( errorBuff );

   return result;
}
void TerrainFile::_loadLegacy(  FileStream &stream )
{
   // Some legacy constants.
   enum 
   {
      MaterialGroups = 8,
      BlockSquareWidth = 256,
   };

   const U32 sampleCount = BlockSquareWidth * BlockSquareWidth;
   mSize = BlockSquareWidth;

   // Load the heightmap.
   mHeightMap.setSize( sampleCount );
   for ( U32 i=0; i < mHeightMap.size(); i++ )
      stream.read( &mHeightMap[i] );

   // Prior to version 7 we stored this weird material struct.
   const U32 MATERIAL_GROUP_MASK = 0x7;
   struct Material 
   {
      enum Flags 
      {
         Plain          = 0,
         Rotate         = 1,
         FlipX          = 2,
         FlipXRotate    = 3,
         FlipY          = 4,
         FlipYRotate    = 5,
         FlipXY         = 6,
         FlipXYRotate   = 7,
         RotateMask     = 7,
         Empty          = 8,
         Modified       = BIT(7),

         // must not clobber TerrainFile::MATERIAL_GROUP_MASK bits!
         PersistMask       = BIT(7)
      };

      U8 flags;
      U8 index;
   };

   // Temp locals for loading before we convert to the new
   // version 7+ format.
   U8 baseMaterialMap[sampleCount] = { 0 };
   U8 *materialAlphaMap[MaterialGroups] = { 0 };
   Material materialMap[BlockSquareWidth * BlockSquareWidth];

   // read the material group map and flags...
   dMemset(materialMap, 0, sizeof(materialMap));

   AssertFatal(!(Material::PersistMask & MATERIAL_GROUP_MASK),
      "Doh! We have flag clobberage...");

   for (S32 j=0; j < sampleCount; j++)
   {
      U8 val;
      stream.read(&val);

      //
      baseMaterialMap[j] = val & MATERIAL_GROUP_MASK;
      materialMap[j].flags = val & Material::PersistMask;
   }

   // Load the material names.
   Vector<String> materials;
   for ( U32 i=0; i < MaterialGroups; i++ )
   {
      String matName;
      stream.read( &matName );
      if ( matName.isEmpty() )
         continue;

      if ( mFileVersion > 3 && mFileVersion < 6 )
      {
         // Between version 3 and 5 we store the texture file names 
         // relative to the terrain file.  We restore the full path
         // here so that we can create a TerrainMaterial from it.
         materials.push_back( Torque::Path::CompressPath( mFilePath.getRoot() + mFilePath.getPath() + '/' + matName ) );
      }
      else
         materials.push_back( matName );
   }

   if ( mFileVersion <= 3 )
   {
      GFXTexHandle terrainMat;
      Torque::Path matRelPath;

      // Try to automatically fix up our material file names
      for (U32 i = 0; i < materials.size(); i++)
      {
         if ( materials[i].isEmpty() )
            continue;
            
         terrainMat.set( materials[i], &GFXDefaultPersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
         if ( terrainMat )
            continue;

         matRelPath = materials[i];

         String path = matRelPath.getPath();

         String::SizeType n = path.find( '/', 0, String::NoCase );
         if ( n != String::NPos )
         {
            matRelPath.setPath( String(Con::getVariable( "$defaultGame" )) + path.substr( n, path.length() - n ) );

            terrainMat.set( matRelPath, &GFXDefaultPersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
            if ( terrainMat )
            {
               materials[i] = matRelPath.getFullPath();
               mNeedsResaving = true;
            }
         }
         
      } // for (U32 i = 0; i < TerrainBlock::MaterialGroups; i++)
      
   } // if ( mFileVersion <= 3 )

   if ( mFileVersion == 1 )
   {
      for( S32 j = 0; j < sampleCount; j++ )
      {
         if ( materialAlphaMap[baseMaterialMap[j]] == NULL )
         {
            materialAlphaMap[baseMaterialMap[j]] = new U8[sampleCount];
            dMemset(materialAlphaMap[baseMaterialMap[j]], 0, sampleCount);
         }

         materialAlphaMap[baseMaterialMap[j]][j] = 255;
      }
   }
   else
   {
      for( S32 k=0; k < materials.size(); k++ )
      {
         AssertFatal(materialAlphaMap[k] == NULL, "Bad assumption.  There should be no alpha map at this point...");
         materialAlphaMap[k] = new U8[sampleCount];
         stream.read(sampleCount, materialAlphaMap[k]);
      }
   }

   // Throw away the old texture and heightfield scripts.
   if ( mFileVersion >= 3 )
   {
      U32 len;
      stream.read(&len);
      char *textureScript = (char *)dMalloc(len + 1);
      stream.read(len, textureScript);
      dFree( textureScript );

      stream.read(&len);
      char *heightfieldScript = (char *)dMalloc(len + 1);
      stream.read(len, heightfieldScript);
      dFree( heightfieldScript );
   }

   // Load and throw away the old edge terrain paths.
   if ( mFileVersion >= 5 )
   {
      stream.readSTString(true);
      stream.readSTString(true);
   }

   U32 layerCount = materials.size() - 1;

   // Ok... time to convert all this mess to the layer index map!
   for ( U32 i=0; i < sampleCount; i++ )
   {
      // Find the greatest layer.
      U32 layer = 0;
      U32 lastValue = 0;
      for ( U32 k=0; k < MaterialGroups; k++ )
      {
         if ( materialAlphaMap[k] && materialAlphaMap[k][i] > lastValue )
         {
            layer = k;
            lastValue = materialAlphaMap[k][i];
         }
      }

      // Set the layer index.   
      mLayerMap[i] = getMin( layer, layerCount );
   }
   
   // Cleanup.
   for ( U32 i=0; i < MaterialGroups; i++ )
      delete [] materialAlphaMap[i];

   // Force resaving on these old file versions.
   //mNeedsResaving = false;

   // Resolve the TerrainMaterial objects from the names.
   _resolveMaterials( materials );
}