Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

344 lines
9.2 KiB
Plaintext

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXRenderMsl/MslMaterial.h>
#include <MaterialXGenMsl/MslShaderGenerator.h>
#include <MaterialXRenderMsl/MetalTextureHandler.h>
#include <MaterialXRenderMsl/MslPipelineStateObject.h>
#include <MaterialXRender/Util.h>
#include <MaterialXFormat/Util.h>
#include <MaterialXRenderMsl/MetalState.h>
MATERIALX_NAMESPACE_BEGIN
const std::string DISTANCE_UNIT_TARGET_NAME = "u_distanceUnitTarget";
//
// Material methods
//
bool MslMaterial::loadSource(const FilePath& vertexShaderFile, const FilePath& pixelShaderFile, bool hasTransparency)
{
_hasTransparency = hasTransparency;
std::string vertexShader = readFile(vertexShaderFile);
if (vertexShader.empty())
{
return false;
}
std::string pixelShader = readFile(pixelShaderFile);
if (pixelShader.empty())
{
return false;
}
// TODO:
// Here we set new source code on the _glProgram without rebuilding
// the _hwShader instance. So the _hwShader is not in sync with the
// _glProgram after this operation.
_glProgram = MslProgram::create();
_glProgram->addStage(Stage::VERTEX, vertexShader);
_glProgram->addStage(Stage::PIXEL, pixelShader);
_glProgram->build(MTL(device), MTL(currentFramebuffer()));
return true;
}
void MslMaterial::clearShader()
{
_hwShader = nullptr;
_glProgram = nullptr;
}
bool MslMaterial::generateShader(GenContext& context)
{
if (!_elem)
{
return false;
}
_hasTransparency = isTransparentSurface(_elem, context.getShaderGenerator().getTarget());
GenContext materialContext = context;
materialContext.getOptions().hwTransparency = _hasTransparency;
// Initialize in case creation fails and throws an exception
clearShader();
_hwShader = createShader("Shader", materialContext, _elem);
if (!_hwShader)
{
return false;
}
_glProgram = MslProgram::create();
_glProgram->setStages(_hwShader);
_glProgram->build(MTL(device), MTL(currentFramebuffer()));
return true;
}
bool MslMaterial::generateShader(ShaderPtr hwShader)
{
_hwShader = hwShader;
_glProgram = MslProgram::create();
_glProgram->setStages(hwShader);
_glProgram->build(MTL(device), MTL(currentFramebuffer()));
return true;
}
bool MslMaterial::bindShader() const
{
if (_glProgram)
{
_glProgram->bind(MTL(renderCmdEncoder));
return true;
}
return false;
}
void MslMaterial::prepareUsedResources(CameraPtr cam,
GeometryHandlerPtr geometryHandler,
ImageHandlerPtr imageHandler,
LightHandlerPtr lightHandler)
{
if (!_glProgram)
{
return;
}
_glProgram->prepareUsedResources(MTL(renderCmdEncoder),
cam, geometryHandler,
imageHandler,
lightHandler);
}
void MslMaterial::bindMesh(MeshPtr mesh)
{
if (!mesh || !_glProgram)
{
return;
}
_glProgram->bind(MTL(renderCmdEncoder));
if (_boundMesh && mesh != _boundMesh)
{
_glProgram->unbindGeometry();
}
_glProgram->bindMesh(MTL(renderCmdEncoder), mesh);
_boundMesh = mesh;
}
bool MslMaterial::bindPartition(MeshPartitionPtr part) const
{
if (!_glProgram)
{
return false;
}
_glProgram->bind(MTL(renderCmdEncoder));
_glProgram->bindPartition(part);
return true;
}
void MslMaterial::bindViewInformation(CameraPtr camera)
{
if (!_glProgram)
{
return;
}
_glProgram->bindViewInformation(camera);
}
void MslMaterial::unbindImages(ImageHandlerPtr imageHandler)
{
for (ImagePtr image : _boundImages)
{
imageHandler->unbindImage(image);
}
}
void MslMaterial::bindImages(ImageHandlerPtr imageHandler, const FileSearchPath& searchPath, bool enableMipmaps)
{
if (!_glProgram)
{
return;
}
_boundImages.clear();
_glProgram->setEnableMipMaps(enableMipmaps);
// Texture and Samplers being bound to the right texture and sampler in MslPipelineStateObject automatically.
}
ImagePtr MslMaterial::bindImage(const FilePath& filePath,
const std::string& uniformName,
ImageHandlerPtr imageHandler,
const ImageSamplingProperties& samplingProperties)
{
if (!_glProgram)
{
return nullptr;
}
// Create a filename resolver for geometric properties.
StringResolverPtr resolver = StringResolver::create();
if (!getUdim().empty())
{
resolver->setUdimString(getUdim());
}
imageHandler->setFilenameResolver(resolver);
// Acquire the given image.
return imageHandler->acquireImage(filePath, samplingProperties.defaultColor);
}
void MslMaterial::bindLighting(LightHandlerPtr lightHandler,
ImageHandlerPtr imageHandler,
const ShadowState& shadowState)
{
if (!_glProgram)
{
return;
}
// Bind environment and local lighting.
_glProgram->bindLighting(lightHandler, imageHandler);
// Bind shadow map properties
if (shadowState.shadowMap && _glProgram->hasUniform(TEXTURE_NAME(HW::SHADOW_MAP)))
{
ImageSamplingProperties samplingProperties;
samplingProperties.uaddressMode = ImageSamplingProperties::AddressMode::CLAMP;
samplingProperties.vaddressMode = ImageSamplingProperties::AddressMode::CLAMP;
samplingProperties.filterType = ImageSamplingProperties::FilterType::LINEAR;
// Bind the shadow map.
_glProgram->bindTexture(imageHandler, TEXTURE_NAME(HW::SHADOW_MAP), shadowState.shadowMap, samplingProperties);
_glProgram->bindUniform(HW::SHADOW_MATRIX, Value::createValue(shadowState.shadowMatrix));
}
// Bind ambient occlusion properties.
if (shadowState.ambientOcclusionMap && _glProgram->hasUniform(TEXTURE_NAME(HW::AMB_OCC_MAP)))
{
ImageSamplingProperties samplingProperties;
samplingProperties.uaddressMode = ImageSamplingProperties::AddressMode::PERIODIC;
samplingProperties.vaddressMode = ImageSamplingProperties::AddressMode::PERIODIC;
samplingProperties.filterType = ImageSamplingProperties::FilterType::LINEAR;
// Bind the ambient occlusion map.
_glProgram->bindTexture(imageHandler, TEXTURE_NAME(HW::AMB_OCC_MAP),
shadowState.ambientOcclusionMap,
samplingProperties);
_glProgram->bindUniform(HW::AMB_OCC_GAIN, Value::createValue(shadowState.ambientOcclusionGain));
}
}
void MslMaterial::drawPartition(MeshPartitionPtr part) const
{
if (!part || !bindPartition(part))
{
return;
}
MeshIndexBuffer& indexData = part->getIndices();
[MTL(renderCmdEncoder) drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:indexData.size()
indexType:MTLIndexTypeUInt32
indexBuffer:_glProgram->getIndexBuffer(part)
indexBufferOffset:0];
}
void MslMaterial::unbindGeometry()
{
if (_glProgram)
{
_glProgram->unbindGeometry();
}
_boundMesh = nullptr;
}
VariableBlock* MslMaterial::getPublicUniforms() const
{
if (!_hwShader)
{
return nullptr;
}
ShaderStage& stage = _hwShader->getStage(Stage::PIXEL);
VariableBlock& block = stage.getUniformBlock(HW::PUBLIC_UNIFORMS);
return &block;
}
ShaderPort* MslMaterial::findUniform(const std::string& path) const
{
ShaderPort* port = nullptr;
VariableBlock* publicUniforms = getPublicUniforms();
if (publicUniforms)
{
// Scan block based on path match predicate
port = publicUniforms->find([path](ShaderPort* port)
{
return (port && stringEndsWith(port->getPath(), path));
});
if (!port)
{
port = publicUniforms->find([path](ShaderPort* port)
{
return (port && stringEndsWith(path, port->getName()));
});
}
// Check if the uniform exists in the shader program
if (port && !_glProgram->getUniformsList().count(
publicUniforms->getInstance() + "." +
port->getVariable()))
{
port = nullptr;
}
}
return port;
}
void MslMaterial::modifyUniform(const std::string& path, ConstValuePtr value, std::string valueString)
{
ShaderPort* uniform = findUniform(path);
if (!uniform)
{
return;
}
_glProgram->bindUniform(uniform->getVariable(), value);
if (valueString.empty())
{
valueString = value->getValueString();
}
uniform->setValue(uniform->getType().createValueFromStrings(valueString));
if (_doc)
{
ElementPtr element = _doc->getDescendant(uniform->getPath());
if (element)
{
ValueElementPtr valueElement = element->asA<ValueElement>();
if (valueElement)
{
valueElement->setValueString(valueString);
}
}
}
}
MATERIALX_NAMESPACE_END