Skip to content

Commit

Permalink
#5565: Use blend shortcuts (diffusemap, bumpmap, specularmap) if poss…
Browse files Browse the repository at this point in the history
…ible
  • Loading branch information
codereader committed Mar 27, 2021
1 parent 112972d commit 909f950
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 82 deletions.
2 changes: 1 addition & 1 deletion install/ui/materialeditor.fbp
Expand Up @@ -609,7 +609,7 @@
<object class="sizeritem" expanded="1">
<property name="border">6</property>
<property name="flag">wxEXPAND|wxLEFT</property>
<property name="proportion">3</property>
<property name="proportion">2</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer81</property>
Expand Down
2 changes: 1 addition & 1 deletion install/ui/materialeditor.xrc
Expand Up @@ -102,7 +102,7 @@
</object>
</object>
<object class="sizeritem">
<option>3</option>
<option>2</option>
<flag>wxEXPAND|wxLEFT</flag>
<border>6</border>
<object class="wxBoxSizer">
Expand Down
227 changes: 147 additions & 80 deletions radiantcore/shaders/MaterialSourceGenerator.cpp
Expand Up @@ -9,83 +9,17 @@
namespace shaders
{

// Write a single layer to the given stream, including curly braces (contents indented by two tabs)
std::ostream& operator<<(std::ostream& stream, Doom3ShaderLayer& layer)
void writeStageCondition(std::ostream& stream, Doom3ShaderLayer& layer)
{
stream << "\t{\n";

// Condition goes first
if (layer.getConditionExpression())
{
stream << "\t\tif " << layer.getConditionExpression()->getExpressionString() << "\n";
}
}

// Blend types
const auto& blendFunc = layer.getBlendFuncStrings();

if (!blendFunc.first.empty())
{
stream << "\t\tblend " << blendFunc.first;

if (!blendFunc.second.empty())
{
stream << ", " << blendFunc.second << "\n";
}
else
{
stream << "\n";
}
}

// Map
auto mapExpr = layer.getMapExpression();

if (layer.getMapExpression())
{
stream << "\t\t";

switch (layer.getMapType())
{
case IShaderLayer::MapType::Map:
stream << "map " << mapExpr->getExpressionString() << "\n";
break;
case IShaderLayer::MapType::CubeMap:
stream << "cubeMap " << mapExpr->getExpressionString() << "\n";
break;
case IShaderLayer::MapType::CameraCubeMap:
stream << "cameraCubeMap " << mapExpr->getExpressionString() << "\n";
break;
case IShaderLayer::MapType::MirrorRenderMap:
stream << "mirrorRenderMap " << static_cast<int>(layer.getRenderMapSize().x()) << ", "
<< static_cast<int>(layer.getRenderMapSize().y()) << "\n";
break;
case IShaderLayer::MapType::RemoteRenderMap:
stream << "remoteRenderMap " << static_cast<int>(layer.getRenderMapSize().x()) << ", "
<< static_cast<int>(layer.getRenderMapSize().y()) << "\n";
break;
case IShaderLayer::MapType::VideoMap:
{
auto videoMap = std::dynamic_pointer_cast<IVideoMapExpression>(mapExpr);

if (videoMap)
{
stream << "videoMap " << (videoMap->isLooping() ? "loop " : "") << videoMap->getExpressionString() << "\n";
}
break;
}
case IShaderLayer::MapType::SoundMap:
{
auto soundMap = std::dynamic_pointer_cast<ISoundMapExpression>(mapExpr);

if (soundMap)
{
stream << "soundMap " << (soundMap->isWaveform() ? "waveform\n" : "\n");
}
break;
}
} // switch
}

void writeStageModifiers(std::ostream& stream, Doom3ShaderLayer& layer)
{
// Alpha Test
if (layer.hasAlphaTest())
{
Expand Down Expand Up @@ -133,8 +67,8 @@ std::ostream& operator<<(std::ostream& stream, Doom3ShaderLayer& layer)
auto expr0 = layer.getTexGenExpression(0);
auto expr1 = layer.getTexGenExpression(1);
auto expr2 = layer.getTexGenExpression(2);
stream << "\t\ttexgen wobblesky " <<

stream << "\t\ttexgen wobblesky " <<
(expr0 ? expr0->getExpressionString() : "") << " " <<
(expr1 ? expr1->getExpressionString() : "") << " " <<
(expr2 ? expr2->getExpressionString() : "") << "\n";
Expand Down Expand Up @@ -229,9 +163,9 @@ std::ostream& operator<<(std::ostream& stream, Doom3ShaderLayer& layer)
}
else // make use of the color shortcut to define all 4 in one line
{
stream << "\t\tcolor " << redExpr->getExpressionString() << ", "
<< greenExpr->getExpressionString() << ", "
<< blueExpr->getExpressionString() << ", "
stream << "\t\tcolor " << redExpr->getExpressionString() << ", "
<< greenExpr->getExpressionString() << ", "
<< blueExpr->getExpressionString() << ", "
<< alphaExpr->getExpressionString() << "\n";
}
}
Expand Down Expand Up @@ -298,7 +232,7 @@ std::ostream& operator<<(std::ostream& stream, Doom3ShaderLayer& layer)
{
stream << "\t\tprogram " << layer.getVertexProgram() << "\n";
}
else if(!layer.getVertexProgram().empty())
else if (!layer.getVertexProgram().empty())
{
stream << "\t\tvertexProgram " << layer.getVertexProgram() << "\n";
}
Expand All @@ -319,7 +253,7 @@ std::ostream& operator<<(std::ostream& stream, Doom3ShaderLayer& layer)
continue; // skip empty parms
}

stream << "\t\tvertexParm " << i << " "
stream << "\t\tvertexParm " << i << " "
<< (parm.expressions[0] ? parm.expressions[0]->getExpressionString() : "")
<< (parm.expressions[1] ? ", " + parm.expressions[1]->getExpressionString() : "")
<< (parm.expressions[2] ? ", " + parm.expressions[2]->getExpressionString() : "")
Expand All @@ -339,13 +273,146 @@ std::ostream& operator<<(std::ostream& stream, Doom3ShaderLayer& layer)
continue;
}

stream << "\t\tfragmentMap " << i << " "
<< (fragmentMap.options.empty() ? "" : string::join(fragmentMap.options, " ") + " ")
stream << "\t\tfragmentMap " << i << " "
<< (fragmentMap.options.empty() ? "" : string::join(fragmentMap.options, " ") + " ")
<< fragmentMap.map->getExpressionString() << "\n";
}
}
}

stream << "\t}\n";
void writeBlendMap(std::ostream& stream, Doom3ShaderLayer& layer)
{
// Blend types
const auto& blendFunc = layer.getBlendFuncStrings();

if (!blendFunc.first.empty())
{
stream << "\t\tblend " << blendFunc.first;

if (!blendFunc.second.empty())
{
stream << ", " << blendFunc.second << "\n";
}
else
{
stream << "\n";
}
}

// Map
auto mapExpr = layer.getMapExpression();

if (layer.getMapExpression())
{
stream << "\t\t";

switch (layer.getMapType())
{
case IShaderLayer::MapType::Map:
stream << "map " << mapExpr->getExpressionString() << "\n";
break;
case IShaderLayer::MapType::CubeMap:
stream << "cubeMap " << mapExpr->getExpressionString() << "\n";
break;
case IShaderLayer::MapType::CameraCubeMap:
stream << "cameraCubeMap " << mapExpr->getExpressionString() << "\n";
break;
case IShaderLayer::MapType::MirrorRenderMap:
stream << "mirrorRenderMap " << static_cast<int>(layer.getRenderMapSize().x()) << ", "
<< static_cast<int>(layer.getRenderMapSize().y()) << "\n";
break;
case IShaderLayer::MapType::RemoteRenderMap:
stream << "remoteRenderMap " << static_cast<int>(layer.getRenderMapSize().x()) << ", "
<< static_cast<int>(layer.getRenderMapSize().y()) << "\n";
break;
case IShaderLayer::MapType::VideoMap:
{
auto videoMap = std::dynamic_pointer_cast<IVideoMapExpression>(mapExpr);

if (videoMap)
{
stream << "videoMap " << (videoMap->isLooping() ? "loop " : "") << videoMap->getExpressionString() << "\n";
}
break;
}
case IShaderLayer::MapType::SoundMap:
{
auto soundMap = std::dynamic_pointer_cast<ISoundMapExpression>(mapExpr);

if (soundMap)
{
stream << "soundMap " << (soundMap->isWaveform() ? "waveform\n" : "\n");
}
break;
}
} // switch
}
}

bool stageQualifiesForShortcut(Doom3ShaderLayer& layer)
{
if (layer.getConditionExpression())
{
return false;
}

auto mapExpr = layer.getMapExpression();

if (!mapExpr)
{
return false; // no map expression => disqualified
}

// Only DBS qualify for shortcuts
if (layer.getType() != IShaderLayer::DIFFUSE &&
layer.getType() != IShaderLayer::BUMP &&
layer.getType() != IShaderLayer::SPECULAR)
{
return false;
}

// Check the map type, it must be a regular "map"
return layer.getMapType() == IShaderLayer::MapType::Map;
}

void writeBlendShortcut(std::ostream& stream, Doom3ShaderLayer& layer)
{
assert(!layer.getConditionExpression());

auto mapExpr = layer.getMapExpression();
assert(mapExpr); // this has already been checked by "stageQualifiesForShortcut"

switch (layer.getType())
{
case IShaderLayer::DIFFUSE: stream << "\tdiffusemap " << mapExpr->getExpressionString() << "\n"; break;
case IShaderLayer::BUMP: stream << "\tbumpmap " << mapExpr->getExpressionString() << "\n"; break;
case IShaderLayer::SPECULAR: stream << "\tspecularmap " << mapExpr->getExpressionString() << "\n"; break;
default:
throw std::logic_error("Wrong stage type stranded in writeBlendShortcut");
};
}

// Write a single layer to the given stream, including curly braces (contents indented by two tabs)
std::ostream& operator<<(std::ostream& stream, Doom3ShaderLayer& layer)
{
// We're writing all the options to a separate buffer first
// if the buffer turns out to be empty we can safely switch to the simpler stage shortcuts like "diffusemap _white"
std::stringstream stageModifierStream;
writeStageModifiers(stageModifierStream, layer);

// If we didn't get any modifiers exported, check if the stage has a simple enough image mapping
if (stageModifierStream.tellp() == 0 && stageQualifiesForShortcut(layer))
{
writeBlendShortcut(stream, layer);
}
else // Stage is too complex, write the proper block
{
stream << "\t{\n";
writeStageCondition(stream, layer);
writeBlendMap(stream, layer);
stream << stageModifierStream.str();
stream << "\t}\n";
}

return stream;
}
Expand Down
41 changes: 41 additions & 0 deletions test/MaterialExport.cpp
Expand Up @@ -1029,4 +1029,45 @@ TEST_F(MaterialExportTest, AmbientRimColour)
expectDefinitionContains(material, "ambientRimColor parm1 * 3.0, 0.0, time * 6.0");
}

TEST_F(MaterialExportTest, BlendShortcuts)
{
auto material = GlobalMaterialManager().getMaterial("textures/exporttest/empty");

EXPECT_EQ(string::trim_copy(material->getDefinition()), "");

auto layer = material->getEditableLayer(material->addLayer(IShaderLayer::DIFFUSE));
layer->setMapExpressionFromString("_white");

expectDefinitionContains(material, "diffusemap _white");

// Adding a piece of complexity should prevent the shortcut from being used
layer->setClampType(CLAMP_ZEROCLAMP);
expectDefinitionContains(material, "blend diffusemap");
expectDefinitionDoesNotContain(material, "diffusemap _white");

material->revertModifications();

layer = material->getEditableLayer(material->addLayer(IShaderLayer::BUMP));
layer->setMapExpressionFromString("_flat");

expectDefinitionContains(material, "bumpmap _flat");

layer->setClampType(CLAMP_ZEROCLAMP);
expectDefinitionContains(material, "blend bumpmap");
expectDefinitionDoesNotContain(material, "bumpmap _flat");

material->revertModifications();

layer = material->getEditableLayer(material->addLayer(IShaderLayer::SPECULAR));
layer->setMapExpressionFromString("_black");

expectDefinitionContains(material, "specularmap _black");

layer->setClampType(CLAMP_ZEROCLAMP);
expectDefinitionContains(material, "blend specularmap");
expectDefinitionDoesNotContain(material, "specularmap _black");

material->revertModifications();
}

}

0 comments on commit 909f950

Please sign in to comment.