@@ -629,50 +629,7 @@ GFXTextureObject *GFXTextureManager::createTexture( const Torque::Path &path, GF

// We need to handle path's that have had "incorrect"
// extensions parsed out of the file name
Torque::Path correctPath = path;

bool textureExt = false;

// Easiest case to handle is when there isn't an extension
if (path.getExtension().isEmpty())
textureExt = true;

// Since "dds" isn't registered with GBitmap currently we
// have to test it separately
if (sDDSExt.equal( path.getExtension(), String::NoCase ) )
textureExt = true;

// Now loop through the rest of the GBitmap extensions
// to see if we have any matches
for ( U32 i = 0; i < GBitmap::sRegistrations.size(); i++ )
{
// If we have gotten a match (either in this loop or before)
// then we can exit
if (textureExt)
break;

const GBitmap::Registration &reg = GBitmap::sRegistrations[i];
const Vector<String> &extensions = reg.extensions;

for ( U32 j = 0; j < extensions.size(); ++j )
{
if ( extensions[j].equal( path.getExtension(), String::NoCase ) )
{
// Found a valid texture extension
textureExt = true;
break;
}
}
}

// If we didn't find a valid texture extension then assume that
// the parsed out "extension" was actually intended to be part of
// the texture name so add it back
if (!textureExt)
{
correctPath.setFileName( Torque::Path::Join( path.getFileName(), '.', path.getExtension() ) );
correctPath.setExtension( String::EmptyString );
}
Torque::Path correctPath = validatePath(path);

// Check the cache first...
String pathNoExt = Torque::Path::Join( correctPath.getRoot(), ':', correctPath.getPath() );
@@ -868,6 +825,277 @@ GFXTextureObject *GFXTextureManager::createTexture( U32 width,
return ret;
}

Torque::Path GFXTextureManager::validatePath(const Torque::Path &path)
{
// We need to handle path's that have had "incorrect"
// extensions parsed out of the file name
Torque::Path correctPath = path;

bool textureExt = false;

// Easiest case to handle is when there isn't an extension
if (path.getExtension().isEmpty())
textureExt = true;

// Since "dds" isn't registered with GBitmap currently we
// have to test it separately
if (sDDSExt.equal(path.getExtension(), String::NoCase))
textureExt = true;

// Now loop through the rest of the GBitmap extensions
// to see if we have any matches
for (U32 i = 0; i < GBitmap::sRegistrations.size(); i++)
{
// If we have gotten a match (either in this loop or before)
// then we can exit
if (textureExt)
break;

const GBitmap::Registration &reg = GBitmap::sRegistrations[i];
const Vector<String> &extensions = reg.extensions;

for (U32 j = 0; j < extensions.size(); ++j)
{
if (extensions[j].equal(path.getExtension(), String::NoCase))
{
// Found a valid texture extension
textureExt = true;
break;
}
}
}

// If we didn't find a valid texture extension then assume that
// the parsed out "extension" was actually intended to be part of
// the texture name so add it back
if (!textureExt)
{
correctPath.setFileName(Torque::Path::Join(path.getFileName(), '.', path.getExtension()));
correctPath.setExtension(String::EmptyString);
}
return correctPath;
}

GBitmap *GFXTextureManager::loadUncompressedTexture(const Torque::Path &path, GFXTextureProfile *profile)
{
PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture);

GBitmap *retBitmap = NULL;

// Resource handles used for loading. Hold on to them
// throughout this function so that change notifications
// don't get added, then removed, and then re-added.

Resource< DDSFile > dds;
Resource< GBitmap > bitmap;

// We need to handle path's that have had "incorrect"
// extensions parsed out of the file name
Torque::Path correctPath = validatePath(path);

U32 scalePower = profile ? getTextureDownscalePower(profile) : 0;

// Check the cache first...
String pathNoExt = Torque::Path::Join(correctPath.getRoot(), ':', correctPath.getPath());
pathNoExt = Torque::Path::Join(pathNoExt, '/', correctPath.getFileName());

// If this is a valid file (has an extension) than load it
Path realPath;
if (Torque::FS::IsFile(correctPath))
{
PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture_INNNER1)
// Check for DDS
if (sDDSExt.equal(correctPath.getExtension(), String::NoCase))
{
dds = DDSFile::load(correctPath, scalePower);
if (dds != NULL)
{
realPath = dds.getPath();
retBitmap = new GBitmap();
if (!dds->decompressToGBitmap(retBitmap))
{
delete retBitmap;
retBitmap = NULL;
}
}
}
else // Let GBitmap take care of it
{
bitmap = GBitmap::load(correctPath);
if (bitmap != NULL)
{
realPath = bitmap.getPath();
retBitmap = new GBitmap(*bitmap);

if (scalePower &&
isPow2(retBitmap->getWidth()) &&
isPow2(retBitmap->getHeight()) &&
profile->canDownscale())
{
retBitmap->extrudeMipLevels();
retBitmap->chopTopMips(scalePower);
}
}
}
}
else
{
PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture_INNNER2)
// NOTE -- We should probably remove the code from GBitmap that tries different
// extensions for things GBitmap loads, and move it here. I think it should
// be a bit more involved than just a list of extensions. Some kind of
// extension registration thing, maybe.

// Check to see if there is a .DDS file with this name (if no extension is provided)
Torque::Path tryDDSPath = pathNoExt;
if (tryDDSPath.getExtension().isNotEmpty())
tryDDSPath.setFileName(tryDDSPath.getFullFileName());
tryDDSPath.setExtension(sDDSExt);

if (Torque::FS::IsFile(tryDDSPath))
{
dds = DDSFile::load(tryDDSPath, scalePower);
if (dds != NULL)
{
realPath = dds.getPath();
// Decompress dds into the GBitmap
retBitmap = new GBitmap();
if (!dds->decompressToGBitmap(retBitmap))
{
delete retBitmap;
retBitmap = NULL;
}
}
}

// Otherwise, retTexObj stays NULL, and fall through to the generic GBitmap
// load.
}

// If we still don't have a texture object yet, feed the correctPath to GBitmap and
// it will try a bunch of extensions
if (retBitmap == NULL)
{
PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture_INNNER3)
// Find and load the texture.
bitmap = GBitmap::load(correctPath);

if (bitmap != NULL)
{
retBitmap = new GBitmap(*bitmap);

if (scalePower &&
isPow2(retBitmap->getWidth()) &&
isPow2(retBitmap->getHeight()) &&
profile->canDownscale())
{
retBitmap->extrudeMipLevels();
retBitmap->chopTopMips(scalePower);
}
}
}

return retBitmap;
}

GFXTextureObject *GFXTextureManager::createCompositeTexture(const Torque::Path &pathR, const Torque::Path &pathG, const Torque::Path &pathB, const Torque::Path &pathA, U32 inputKey[4],
GFXTextureProfile *profile)
{
PROFILE_SCOPE(GFXTextureManager_createCompositeTexture);

String inputKeyStr = String::ToString("%d%d%d%d", inputKey[0], inputKey[1], inputKey[2], inputKey[3]);

String resourceTag = pathR.getFileName() + pathG.getFileName() + pathB.getFileName() + pathA.getFileName() + inputKeyStr; //associate texture object with a key combo

GFXTextureObject *cacheHit = _lookupTexture(resourceTag, profile);
if (cacheHit != NULL) return cacheHit;

GBitmap*bitmap[4];
bitmap[0] = loadUncompressedTexture(pathR, profile);
if (!pathG.isEmpty())
bitmap[1] = loadUncompressedTexture(pathG, profile);
else
bitmap[1] = NULL;

if (!pathB.isEmpty())
bitmap[2] = loadUncompressedTexture(pathB, profile);
else
bitmap[2] = NULL;
if (!pathA.isEmpty())
bitmap[3] = loadUncompressedTexture(pathA, profile);
else
bitmap[3] = NULL;


Path realPath;
GFXTextureObject *retTexObj = NULL;
realPath = validatePath(pathR); //associate path with r channel texture in.

retTexObj = createCompositeTexture(bitmap, inputKey, resourceTag, profile, false);

if (retTexObj)
{
// Store the path for later use.
retTexObj->mPath = resourceTag;

// Register the texture file for change notifications.
FS::AddChangeNotification(retTexObj->getPath(), this, &GFXTextureManager::_onFileChanged);
}

// Could put in a final check for 'retTexObj == NULL' here as an error message.

return retTexObj;
}

GFXTextureObject *GFXTextureManager::createCompositeTexture(GBitmap*bmp[4], U32 inputKey[4],
const String &resourceName, GFXTextureProfile *profile, bool deleteBmp)
{
if (!bmp[0])
{
Con::errorf(ConsoleLogEntry::General, "GFXTextureManager::createCompositeTexture() - Got NULL bitmap(R)!");
return NULL;
}

U8 rChan, gChan, bChan, aChan;

//pack additional bitmaps into the origional
for (U32 x = 0; x < bmp[0]->getWidth(); x++)
{
for (U32 y = 0; y < bmp[0]->getHeight(); y++)
{
rChan = bmp[0]->getChanelValueAt(x, y, inputKey[0]);

if (bmp[1])
gChan = bmp[1]->getChanelValueAt(x, y, inputKey[1]);
else
gChan = 255;

if (bmp[2])
bChan = bmp[2]->getChanelValueAt(x, y, inputKey[2]);
else
bChan = 255;

if (bmp[3])
aChan = bmp[3]->getChanelValueAt(x, y, inputKey[3]);
else
aChan = 255;

bmp[0]->setColor(x, y, ColorI(rChan, gChan, bChan, aChan));
}
}

GFXTextureObject *cacheHit = _lookupTexture(resourceName, profile);
if (cacheHit != NULL)
{
// Con::errorf("Cached texture '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
if (deleteBmp)
delete bmp[0];
return cacheHit;
}

return _createTexture(bmp[0], resourceName, profile, deleteBmp, NULL);
}

GFXTextureObject* GFXTextureManager::_findPooledTexure( U32 width,
U32 height,
GFXFormat format,
@@ -125,6 +125,16 @@ class GFXTextureManager
U32 numMipLevels,
S32 antialiasLevel);

Torque::Path validatePath(const Torque::Path &path);
GBitmap *loadUncompressedTexture(const Torque::Path &path, GFXTextureProfile *profile);
virtual GFXTextureObject *createCompositeTexture(const Torque::Path &pathR, const Torque::Path &pathG, const Torque::Path &pathB, const Torque::Path &pathA, U32 inputKey[4],
GFXTextureProfile *profile);

virtual GFXTextureObject *createCompositeTexture(GBitmap*bmp[4], U32 inputKey[4],
const String &resourceName,
GFXTextureProfile *profile,
bool deleteBmp);

void deleteTexture( GFXTextureObject *texture );
void reloadTexture( GFXTextureObject *texture );

@@ -63,7 +63,7 @@ void DeferredSpecMapGLSL::processPix( Vector<ShaderComponent*> &componentList, c
specularMap->sampler = true;
specularMap->constNum = Var::getTexUnitNum();
LangElement *texOp = new GenOp( "tex2D(@, @)", specularMap, texCoord );
meta->addStatement(new GenOp(" @.gba = tex2D(@, @).bga;\r\n", material, specularMap, texCoord));
meta->addStatement(new GenOp(" @.bga = tex2D(@, @).rgb;\r\n", material, specularMap, texCoord));
output = meta;
}

@@ -64,7 +64,7 @@ void DeferredSpecMapHLSL::processPix( Vector<ShaderComponent*> &componentList, c
specularMap->constNum = Var::getTexUnitNum();
LangElement *texOp = new GenOp( "tex2D(@, @)", specularMap, texCoord );

meta->addStatement(new GenOp(" @.gba = tex2D(@, @).bga;\r\n", material, specularMap, texCoord));
meta->addStatement(new GenOp(" @.bga = tex2D(@, @).rgb;\r\n", material, specularMap, texCoord));
output = meta;
}

@@ -119,6 +119,10 @@ Material::Material()
mRoughness[i] = 0.0f;
mMetalness[i] = 0.0f;
mPixelSpecular[i] = false;

mRoughnessChan[i] = 0;
mAOChan[i] = 1;
mMetalChan[i] = 2;

mAccuEnabled[i] = false;
mAccuScale[i] = 1.0f;
@@ -164,6 +168,9 @@ Material::Material()

// Deferred Shading
mMatInfoFlags[i] = 0.0f;
mRoughMapFilename[i].clear();
mAOMapFilename[i].clear();
mMetalMapFilename[i].clear();
}

dMemset(mCellIndex, 0, sizeof(mCellIndex));
@@ -284,9 +291,24 @@ void Material::initPersistFields()
"Changes specularity to this value where the accumulated material is present.");

addField( "specularMap", TypeImageFilename, Offset(mSpecularMapFilename, Material), MAX_STAGES,
"The specular map texture. The RGB channels of this texture provide a per-pixel replacement for the 'specular' parameter on the material. "
"If this texture contains alpha information, the alpha channel of the texture will be used as the gloss map. "
"This provides a per-pixel replacement for the 'specularPower' on the material" );
"Prepacked specular map texture. The RGB channels of this texture provide per-pixel reference values for: "
"roughness (R), Ambient Occlusion (G), and metalness(B)");

addField("roughMap", TypeImageFilename, Offset(mRoughMapFilename, Material), MAX_STAGES,
"Roughness map. will be packed into the R channel of a packed 'specular' map");
addField("roughnessChan", TypeF32, Offset(mRoughnessChan, Material), MAX_STAGES,
"The input channel roughness maps use.");

addField("aoMap", TypeImageFilename, Offset(mAOMapFilename, Material), MAX_STAGES,
"Ambient Occlusion map. will be packed into the G channel of a packed 'specular' map");
addField("AOChan", TypeF32, Offset(mAOChan, Material), MAX_STAGES,
"The input channel AO maps use.");

addField("metalMap", TypeImageFilename, Offset(mMetalMapFilename, Material), MAX_STAGES,
"Metalness map. will be packed into the B channel of a packed 'specular' map");
addField("metalChan", TypeF32, Offset(mMetalChan, Material), MAX_STAGES,
"The input channel metalness maps use.");


addField( "parallaxScale", TypeF32, Offset(mParallaxScale, Material), MAX_STAGES,
"Enables parallax mapping and defines the scale factor for the parallax effect. Typically "
@@ -213,6 +213,12 @@ class Material : public BaseMaterialDefinition
FileName mNormalMapFilename[MAX_STAGES];

FileName mSpecularMapFilename[MAX_STAGES];
FileName mRoughMapFilename[MAX_STAGES];
F32 mRoughnessChan[MAX_STAGES];
FileName mAOMapFilename[MAX_STAGES];
F32 mAOChan[MAX_STAGES];
FileName mMetalMapFilename[MAX_STAGES];
F32 mMetalChan[MAX_STAGES];

/// A second normal map which repeats at the detail map
/// scale and blended with the base normal map.
@@ -174,6 +174,12 @@ GFXTexHandle ProcessedMaterial::_createTexture( const char* filename, GFXTexture
return GFXTexHandle( _getTexturePath(filename), profile, avar("%s() - NA (line %d)", __FUNCTION__, __LINE__) );
}

GFXTexHandle ProcessedMaterial::_createCompositeTexture(const char *filenameR, const char *filenameG, const char *filenameB, const char *filenameA, U32 inputKey[4], GFXTextureProfile *profile)
{
return GFXTexHandle(_getTexturePath(filenameR), _getTexturePath(filenameG), _getTexturePath(filenameB), _getTexturePath(filenameA), inputKey, profile, avar("%s() - NA (line %d)", __FUNCTION__, __LINE__));
}


void ProcessedMaterial::addStateBlockDesc(const GFXStateBlockDesc& sb)
{
mUserDefined = sb;
@@ -457,6 +463,20 @@ void ProcessedMaterial::_setStageData()
if(!mStages[i].getTex( MFT_SpecularMap ))
mMaterial->logError("Failed to load specular map %s for stage %i", _getTexturePath(mMaterial->mSpecularMapFilename[i]).c_str(), i);
}
else
if (mMaterial->mRoughMapFilename[i].isNotEmpty() && mMaterial->mMetalMapFilename[i].isNotEmpty())
{
U32 inputKey[4];
inputKey[0] = mMaterial->mRoughnessChan[i];
inputKey[1] = mMaterial->mAOChan[i];
inputKey[2] = mMaterial->mMetalChan[i];
mStages[i].setTex(MFT_SpecularMap, _createCompositeTexture(mMaterial->mRoughMapFilename[i], mMaterial->mAOMapFilename[i],
mMaterial->mMetalMapFilename[i], "",
inputKey, &GFXDefaultStaticDiffuseProfile));
if (!mStages[i].getTex(MFT_SpecularMap))
mMaterial->logError("Failed to load specular map %s for stage %i", _getTexturePath(mMaterial->mSpecularMapFilename[i]).c_str(), i);
}

}

mMaterial->mCubemapData = dynamic_cast<CubemapData*>(Sim::findObject( mMaterial->mCubemapName ));
@@ -282,6 +282,7 @@ class ProcessedMaterial

/// Loads the texture located at _getTexturePath(filename) and gives it the specified profile
GFXTexHandle _createTexture( const char *filename, GFXTextureProfile *profile );
GFXTexHandle _createCompositeTexture(const char *filenameR, const char *filenameG, const char *filenameB, const char *filenameA, U32 inputKey[4], GFXTextureProfile *profile);

/// @name State blocks
///

Large diffs are not rendered by default.

@@ -120,7 +120,10 @@ function destroyMaterialEditor()
}else{
$wasInWireFrameMode = false;
}

advancedTextureMapsRollout.Expanded = false;
accumulationPropertiesRollout.Expanded = false;
lightingPropertiesRollout.Expanded = false;
materialAnimationPropertiesRollout.Expanded = false;
materialAdvancedPropertiesRollout.Expanded = false;
WorldEditorPlugin.onActivated();
@@ -593,6 +593,27 @@ singleton Material(notDirtyMaterial)
%specMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %specMap);
MaterialEditorGui.currentMaterial.specularMap[%specI] = %specMap;
}

for(%roughI = 0; %roughI < 4; %roughI++)
{
%roughMap = MaterialEditorGui.currentMaterial.roughMap[%roughI];
%roughMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %roughMap);
MaterialEditorGui.currentMaterial.roughMap[%specI] = %roughMap;
}

for(%aoI = 0; %aoI < 4; %aoI++)
{
%aoMap = MaterialEditorGui.currentMaterial.aoMap[%aoI];
%aoMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %aoMap);
MaterialEditorGui.currentMaterial.aoMap[%specI] = %aoMap;
}

for(%metalI = 0; %metalI < 4; %metalI++)
{
%metalMap = MaterialEditorGui.currentMaterial.metalMap[%metalI];
%metalMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %metalMap);
MaterialEditorGui.currentMaterial.metalMap[%metalI] = %metalMap;
}
}

// still needs to be optimized further
@@ -898,6 +919,39 @@ singleton Material(notDirtyMaterial)
MaterialEditorPropertiesWindow-->specMapDisplayBitmap.setBitmap( (%material).specularMap[%layer] );
}

if((%material).roughMap[%layer] $= "")
{
MaterialEditorPropertiesWindow-->roughMapNameText.setText( "None" );
MaterialEditorPropertiesWindow-->roughMapDisplayBitmap.setBitmap( "tools/materialeditor/gui/unknownImage" );
}
else
{
MaterialEditorPropertiesWindow-->roughMapNameText.setText( (%material).roughMap[%layer] );
MaterialEditorPropertiesWindow-->roughMapDisplayBitmap.setBitmap( (%material).roughMap[%layer] );
}

if((%material).aoMap[%layer] $= "")
{
MaterialEditorPropertiesWindow-->aoMapNameText.setText( "None" );
MaterialEditorPropertiesWindow-->aoMapDisplayBitmap.setBitmap( "tools/materialeditor/gui/unknownImage" );
}
else
{
MaterialEditorPropertiesWindow-->aoMapNameText.setText( (%material).aoMap[%layer] );
MaterialEditorPropertiesWindow-->aoMapDisplayBitmap.setBitmap( (%material).aoMap[%layer] );
}

if((%material).metalMap[%layer] $= "")
{
MaterialEditorPropertiesWindow-->metalMapNameText.setText( "None" );
MaterialEditorPropertiesWindow-->metalMapDisplayBitmap.setBitmap( "tools/materialeditor/gui/unknownImage" );
}
else
{
MaterialEditorPropertiesWindow-->metalMapNameText.setText( (%material).metalMap[%layer] );
MaterialEditorPropertiesWindow-->metalMapDisplayBitmap.setBitmap( (%material).metalMap[%layer] );
}

MaterialEditorPropertiesWindow-->accuScaleTextEdit.setText((%material).accuScale[%layer]);
MaterialEditorPropertiesWindow-->accuScaleTextEdit.setText((%material).accuScale[%layer]);
MaterialEditorPropertiesWindow-->accuDirectionTextEdit.setText((%material).accuDirection[%layer]);
@@ -990,11 +1044,34 @@ singleton Material(notDirtyMaterial)
MaterialEditorPropertiesWindow-->SequenceSliderSSS.setValue( %numFrames );

// Accumulation
MaterialEditorPropertiesWindow-->accuCheckbox.setValue((%material).accuEnabled[%layer]);
MaterialEditorPropertiesWindow-->accuCheckbox.setValue((%material).accuEnabled[%layer]);

MaterialEditorPropertiesWindow-->accuCheckbox.setValue((%material).accuEnabled[%layer]);

%this.getRoughChan((%material).roughnessChan[%layer]);
%this.getAOChan((%material).AOChan[%layer]);
%this.getMetalChan((%material).metalChan[%layer]);
%this.preventUndo = false;
}

//=======================================
function MaterialEditorGui::getRoughChan(%this, %channel)
{
%guiElement = roughChanBtn @ %channel;
%guiElement.setStateOn(true);
}

function MaterialEditorGui::getAOChan(%this, %channel)
{
%guiElement = AOChanBtn @ %channel;
%guiElement.setStateOn(true);
}

function MaterialEditorGui::getMetalChan(%this, %channel)
{
%guiElement = metalChanBtn @ %channel;
%guiElement.setStateOn(true);
}
//=======================================
// Material Update Functionality

@@ -1206,6 +1283,90 @@ singleton Material(notDirtyMaterial)
MaterialEditorGui.guiSync( materialEd_previewMaterial );
}

function MaterialEditorGui::updateRoughMap(%this,%action)
{
%layer = MaterialEditorGui.currentLayer;

if( %action )
{
%texture = MaterialEditorGui.openFile("texture");
if( %texture !$= "" )
{
MaterialEditorPropertiesWindow-->roughMapDisplayBitmap.setBitmap(%texture);

%bitmap = MaterialEditorPropertiesWindow-->roughMapDisplayBitmap.bitmap;
%bitmap = strreplace(%bitmap,"tools/materialEditor/scripts/","");
MaterialEditorPropertiesWindow-->roughMapDisplayBitmap.setBitmap(%bitmap);
MaterialEditorPropertiesWindow-->roughMapNameText.setText(%bitmap);
MaterialEditorGui.updateActiveMaterial("roughMap[" @ %layer @ "]","\"" @ %bitmap @ "\"");
}
}
else
{
MaterialEditorPropertiesWindow-->roughMapNameText.setText("None");
MaterialEditorPropertiesWindow-->roughMapDisplayBitmap.setBitmap("tools/materialeditor/gui/unknownImage");
MaterialEditorGui.updateActiveMaterial("roughMap[" @ %layer @ "]","");
}

MaterialEditorGui.guiSync( materialEd_previewMaterial );
}

function MaterialEditorGui::updateaoMap(%this,%action)
{
%layer = MaterialEditorGui.currentLayer;

if( %action )
{
%texture = MaterialEditorGui.openFile("texture");
if( %texture !$= "" )
{
MaterialEditorPropertiesWindow-->aoMapDisplayBitmap.setBitmap(%texture);

%bitmap = MaterialEditorPropertiesWindow-->aoMapDisplayBitmap.bitmap;
%bitmap = strreplace(%bitmap,"tools/materialEditor/scripts/","");
MaterialEditorPropertiesWindow-->aoMapDisplayBitmap.setBitmap(%bitmap);
MaterialEditorPropertiesWindow-->aoMapNameText.setText(%bitmap);
MaterialEditorGui.updateActiveMaterial("aoMap[" @ %layer @ "]","\"" @ %bitmap @ "\"");
}
}
else
{
MaterialEditorPropertiesWindow-->aoMapNameText.setText("None");
MaterialEditorPropertiesWindow-->aoMapDisplayBitmap.setBitmap("tools/materialeditor/gui/unknownImage");
MaterialEditorGui.updateActiveMaterial("aoMap[" @ %layer @ "]","");
}

MaterialEditorGui.guiSync( materialEd_previewMaterial );
}

function MaterialEditorGui::updatemetalMap(%this,%action)
{
%layer = MaterialEditorGui.currentLayer;

if( %action )
{
%texture = MaterialEditorGui.openFile("texture");
if( %texture !$= "" )
{
MaterialEditorPropertiesWindow-->metalMapDisplayBitmap.setBitmap(%texture);

%bitmap = MaterialEditorPropertiesWindow-->metalMapDisplayBitmap.bitmap;
%bitmap = strreplace(%bitmap,"tools/materialEditor/scripts/","");
MaterialEditorPropertiesWindow-->metalMapDisplayBitmap.setBitmap(%bitmap);
MaterialEditorPropertiesWindow-->metalMapNameText.setText(%bitmap);
MaterialEditorGui.updateActiveMaterial("metalMap[" @ %layer @ "]","\"" @ %bitmap @ "\"");
}
}
else
{
MaterialEditorPropertiesWindow-->metalMapNameText.setText("None");
MaterialEditorPropertiesWindow-->metalMapDisplayBitmap.setBitmap("tools/materialeditor/gui/unknownImage");
MaterialEditorGui.updateActiveMaterial("metalMap[" @ %layer @ "]","");
}

MaterialEditorGui.guiSync( materialEd_previewMaterial );
}

function MaterialEditorGui::updateRotationOffset(%this, %isSlider, %onMouseUp)
{
%layer = MaterialEditorGui.currentLayer;
@@ -2282,3 +2443,22 @@ singleton Material(notDirtyMaterial)
MaterialEditorGui.updateActiveMaterial("accuEnabled[" @ MaterialEditorGui.currentLayer @ "]", %value);
MaterialEditorGui.guiSync( materialEd_previewMaterial );
}

// channel in selectors
function MaterialEditorGui::setRoughChan(%this, %value)
{
MaterialEditorGui.updateActiveMaterial("roughnessChan[" @ MaterialEditorGui.currentLayer @ "]", %value);
MaterialEditorGui.guiSync( materialEd_previewMaterial );
}

function MaterialEditorGui::setAOChan(%this, %value)
{
MaterialEditorGui.updateActiveMaterial("aoChan[" @ MaterialEditorGui.currentLayer @ "]", %value);
MaterialEditorGui.guiSync( materialEd_previewMaterial );
}

function MaterialEditorGui::setMetalChan(%this, %value)
{
MaterialEditorGui.updateActiveMaterial("metalChan[" @ MaterialEditorGui.currentLayer @ "]", %value);
MaterialEditorGui.guiSync( materialEd_previewMaterial );
}