diff --git a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt index 0467d350..f7731843 100644 --- a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt @@ -28,22 +28,37 @@ if(OTK_USE_MDL) find_package(mdl CONFIG REQUIRED) set(DEMAND_PBRT_SCENE_MDL_CUDA_SOURCES FourierMaterial.cu - MdlSmokeMaterial.cu + MdlMaterial.cu ) set(DEMAND_PBRT_SCENE_MDL_LIBRARIES mdl::mdl_sdk) set(DEMAND_PBRT_SCENE_MDL_SOURCES FourierBsdfTable.cpp FourierBsdfTableResource.cpp MdlBsdfCompiler.cpp + MdlKeyBuilder.cpp + MdlMaterialModelBuilder.cpp + MdlParameterBinder.cpp + MdlSdkSession.cpp MdlShaderCache.cpp + MdlTextureGraphGenerator.cpp + MdlUtils.cpp + PbrtMaterialKind.cpp include/DemandPbrtScene/FourierBsdfEval.h include/DemandPbrtScene/FourierBsdfTable.h include/DemandPbrtScene/FourierBsdfTableResource.h include/DemandPbrtScene/FourierMdlMeasuredBsdfCapability.h include/DemandPbrtScene/MdlBumpMap.h include/DemandPbrtScene/MdlBsdfCompiler.h + include/DemandPbrtScene/MdlHandleTypes.h + include/DemandPbrtScene/MdlKeyBuilder.h + include/DemandPbrtScene/MdlMaterialModelBuilder.h + include/DemandPbrtScene/MdlParameterBinder.h + include/DemandPbrtScene/MdlSdkSession.h include/DemandPbrtScene/MdlShaderCache.h include/DemandPbrtScene/MdlShaderCompileCacheStatistics.h + include/DemandPbrtScene/MdlTextureGraphGenerator.h + include/DemandPbrtScene/MdlUtils.h + include/DemandPbrtScene/PbrtMaterialKind.h ) endif() diff --git a/examples/DemandLoading/DemandPbrtScene/FourierBsdfTable.cpp b/examples/DemandLoading/DemandPbrtScene/FourierBsdfTable.cpp index 3058e9f1..cf266b22 100644 --- a/examples/DemandLoading/DemandPbrtScene/FourierBsdfTable.cpp +++ b/examples/DemandLoading/DemandPbrtScene/FourierBsdfTable.cpp @@ -28,6 +28,13 @@ FourierBsdfTableLoadResult makeFailure( FourierBsdfTableLoadStatus status, const return result; } +FourierBsdfTableLoadResult makeSuccess() +{ + FourierBsdfTableLoadResult result{}; + result.status = FourierBsdfTableLoadStatus::SUCCESS; + return result; +} + std::string sectionDiagnostic( const std::string& fileName, const std::string& section ) { return "Truncated Fourier BSDF table \"" + fileName + "\" while reading " + section; @@ -155,18 +162,19 @@ std::string unsupportedDiagnostic( const std::string& fileName, const FourierBsd return out.str(); } -} // namespace - -FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) +FourierBsdfTableLoadResult readTableHeader( const std::string& fileName, + std::ifstream& input, + std::streamoff& fileSize, + FourierBsdfTable& table ) { - std::ifstream input{ fileName, std::ios::binary | std::ios::ate }; + input.open( fileName, std::ios::binary | std::ios::ate ); if( !input ) { return makeFailure( FourierBsdfTableLoadStatus::FILE_NOT_FOUND, "Unable to open Fourier BSDF table file \"" + fileName + "\"" ); } - const std::streamoff fileSize{ input.tellg() }; + fileSize = input.tellg(); input.seekg( 0, std::ios::beg ); char header[8]{}; @@ -180,8 +188,7 @@ FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) "Invalid Fourier BSDF table header in \"" + fileName + "\"" ); } - FourierBsdfTable table{}; - int unused{}; + int unused{}; if( !readInt32( input, table.flags ) || !readInt32( input, table.nMu ) || !readInt32( input, table.nCoefficients ) || !readInt32( input, table.maxOrder ) || !readInt32( input, table.nChannels ) || !readInt32( input, table.nBases ) || !readInt32( input, unused ) || !readInt32( input, unused ) @@ -190,7 +197,11 @@ FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) { return makeFailure( FourierBsdfTableLoadStatus::TRUNCATED, sectionDiagnostic( fileName, "metadata" ) ); } + return makeSuccess(); +} +FourierBsdfTableLoadResult validateMetadata( const std::string& fileName, const FourierBsdfTable& table ) +{ if( table.flags != 1 || ( table.nChannels != 1 && table.nChannels != 3 ) || table.nBases != 1 ) { return makeFailure( FourierBsdfTableLoadStatus::UNSUPPORTED, unsupportedDiagnostic( fileName, table ) ); @@ -200,15 +211,29 @@ FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) return makeFailure( FourierBsdfTableLoadStatus::MALFORMED, "Malformed Fourier BSDF table \"" + fileName + "\": invalid dimensions" ); } + return makeSuccess(); +} +FourierBsdfTableLoadResult computeGridSize( const std::string& fileName, + const FourierBsdfTable& table, + std::size_t& gridSize ) +{ const std::size_t nMu{ static_cast( table.nMu ) }; - std::size_t gridSize{}; if( !checkedMultiply( nMu, nMu, gridSize ) ) { return makeFailure( FourierBsdfTableLoadStatus::MALFORMED, "Malformed Fourier BSDF table \"" + fileName + "\": dimension overflow" ); } + return makeSuccess(); +} +FourierBsdfTableLoadResult readArrays( std::istream& input, + const std::string& fileName, + std::size_t gridSize, + FourierBsdfTable& table, + std::vector& offsetsAndLengths ) +{ + const std::size_t nMu{ static_cast( table.nMu ) }; if( !readFloatVector( input, table.mu, nMu ) ) { return makeFailure( FourierBsdfTableLoadStatus::TRUNCATED, sectionDiagnostic( fileName, "mu values" ) ); @@ -224,8 +249,7 @@ FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) return makeFailure( FourierBsdfTableLoadStatus::MALFORMED, "Malformed Fourier BSDF table \"" + fileName + "\": offset table overflow" ); } - std::vector offsetAndLength; - if( !readInt32Vector( input, offsetAndLength, offsetPairCount ) ) + if( !readInt32Vector( input, offsetsAndLengths, offsetPairCount ) ) { return makeFailure( FourierBsdfTableLoadStatus::TRUNCATED, sectionDiagnostic( fileName, "coefficient offsets" ) ); @@ -234,25 +258,35 @@ FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) { return makeFailure( FourierBsdfTableLoadStatus::TRUNCATED, sectionDiagnostic( fileName, "coefficients" ) ); } + return makeSuccess(); +} +FourierBsdfTableLoadResult validateCoefficientSpans( const std::string& fileName, + const std::vector& offsetsAndLengths, + std::size_t gridSize, + FourierBsdfTable& table ) +{ table.coefficientOffsets.resize( gridSize ); table.coefficientCounts.resize( gridSize ); table.zeroOrderCoefficients.resize( gridSize ); for( std::size_t i = 0; i < gridSize; ++i ) { - const int offset{ offsetAndLength[2U * i] }; - const int length{ offsetAndLength[2U * i + 1U] }; + const int offset{ offsetsAndLengths[2U * i] }; + const int length{ offsetsAndLengths[2U * i + 1U] }; if( offset < 0 || length < 0 || length > table.maxOrder ) { return makeFailure( FourierBsdfTableLoadStatus::MALFORMED, "Malformed Fourier BSDF table \"" + fileName + "\": invalid coefficient span" ); } + std::size_t channelCoefficientCount{}; - if( !checkedMultiply( static_cast( length ), static_cast( table.nChannels ), channelCoefficientCount ) ) + if( !checkedMultiply( static_cast( length ), static_cast( table.nChannels ), + channelCoefficientCount ) ) { return makeFailure( FourierBsdfTableLoadStatus::MALFORMED, "Malformed Fourier BSDF table \"" + fileName + "\": coefficient span overflow" ); } + std::size_t spanEnd{}; if( !checkedAdd( static_cast( offset ), channelCoefficientCount, spanEnd ) || spanEnd > table.coefficients.size() ) @@ -266,17 +300,61 @@ FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) table.coefficientCounts[i] = length; table.zeroOrderCoefficients[i] = length > 0 ? table.coefficients[static_cast( offset )] : 0.0f; } + return makeSuccess(); +} +void recordTrailingByteCount( std::istream& input, std::streamoff fileSize, FourierBsdfTable& table ) +{ const std::streamoff tableEnd{ input.tellg() }; if( tableEnd >= 0 && fileSize >= tableEnd ) { table.trailingByteCount = static_cast( fileSize - tableEnd ); } +} - FourierBsdfTableLoadResult result{}; - result.status = FourierBsdfTableLoadStatus::SUCCESS; - result.table = std::move( table ); +FourierBsdfTableLoadResult makeSuccess( FourierBsdfTable&& table ) +{ + FourierBsdfTableLoadResult result{ makeSuccess() }; + result.table = std::move( table ); return result; } +} // namespace + +FourierBsdfTableLoadResult loadFourierBsdfTable( const std::string& fileName ) +{ + std::ifstream input; + std::streamoff fileSize{}; + FourierBsdfTable table{}; + std::size_t gridSize{}; + std::vector offsetsAndLengths; + + if( const FourierBsdfTableLoadResult result{ readTableHeader( fileName, input, fileSize, table ) }; !result ) + { + return result; + } + if( const FourierBsdfTableLoadResult result{ validateMetadata( fileName, table ) }; !result ) + { + return result; + } + if( const FourierBsdfTableLoadResult result{ computeGridSize( fileName, table, gridSize ) }; !result ) + { + return result; + } + if( const FourierBsdfTableLoadResult result{ readArrays( input, fileName, gridSize, table, offsetsAndLengths ) }; + !result ) + { + return result; + } + if( const FourierBsdfTableLoadResult result{ + validateCoefficientSpans( fileName, offsetsAndLengths, gridSize, table ) }; + !result ) + { + return result; + } + + recordTrailingByteCount( input, fileSize, table ); + return makeSuccess( std::move( table ) ); +} + } // namespace demandPbrtScene diff --git a/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp b/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp index 0766f36b..3e0951f2 100644 --- a/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp @@ -12,6 +12,7 @@ #include "DemandPbrtScene/FourierBsdfTable.h" #include "DemandPbrtScene/MaterialAdapters.h" #include "DemandPbrtScene/MdlShaderCache.h" +#include "DemandPbrtScene/PbrtMaterialKind.h" #endif #include "DemandPbrtScene/Options.h" #include "DemandPbrtScene/ProgramGroups.h" @@ -176,17 +177,14 @@ MaterialState fourierTableReadyState( uint_t materialId, uint_t resourceId ) return makeMaterialState( materialId, MaterialBackend::FOURIER_TABLE_READY, resourceId ); } -bool supportsGeneratedMdlMaterial( const std::string& type ) +bool supportsGeneratedMdlMaterial( const PbrtMaterialDescriptor& material ) { - return type == "matte" || type == "plastic" || type == "uber" || type == "mirror" || type == "glass" - || type == "metal" || type == "substrate" || type == "translucent" || type == "subsurface" - || type == "kdsubsurface" || type == "mix"; + return material.has( PbrtMaterialCapability::GENERATED_MDL ); } -bool supportsGeneratedMdlNamedMaterialType( const std::string& type ) +bool supportsGeneratedMdlNamedMaterial( const PbrtMaterialDescriptor& material ) { - return type == "matte" || type == "plastic" || type == "uber" || type == "mirror" || type == "glass" - || type == "metal" || type == "substrate" || type == "translucent"; + return material.has( PbrtMaterialCapability::NAMED_MDL ); } std::string generatedMdlNamedMaterialType( const otk::pbrt::PbrtNamedMaterial& material ) @@ -208,6 +206,85 @@ otk::pbrt::PbrtMaterial generatedMdlMaterialForNamedMaterial( const otk::pbrt::P return material; } +enum class MdlTextureValue +{ + COLOR, + FLOAT, + COLOR_OR_FLOAT, +}; + +enum class MdlTextureUse +{ + NONE, + DIFFUSE, + SPECULAR, + REFLECTION, + TRANSMISSION, + ROUGHNESS, + ALPHA, + AMOUNT, + BUMP, +}; + +struct MdlTexturePolicy +{ + const char* name; + MdlTextureValue value; + MdlTextureUse use; +}; + +constexpr MdlTexturePolicy MDL_TEXTURE_POLICIES[] = { + { "Kd", MdlTextureValue::COLOR, MdlTextureUse::DIFFUSE }, + { "Kr", MdlTextureValue::COLOR, MdlTextureUse::REFLECTION }, + { "Ks", MdlTextureValue::COLOR, MdlTextureUse::SPECULAR }, + { "Kt", MdlTextureValue::COLOR, MdlTextureUse::TRANSMISSION }, + { "alpha", MdlTextureValue::FLOAT, MdlTextureUse::ALPHA }, + { "amount", MdlTextureValue::COLOR_OR_FLOAT, MdlTextureUse::AMOUNT }, + { "bumpmap", MdlTextureValue::FLOAT, MdlTextureUse::BUMP }, + { "eta", MdlTextureValue::COLOR, MdlTextureUse::NONE }, + { "index", MdlTextureValue::FLOAT, MdlTextureUse::NONE }, + { "k", MdlTextureValue::COLOR, MdlTextureUse::NONE }, + { "mfp", MdlTextureValue::COLOR, MdlTextureUse::NONE }, + { "opacity", MdlTextureValue::FLOAT, MdlTextureUse::ALPHA }, + { "reflect", MdlTextureValue::COLOR, MdlTextureUse::NONE }, + { "roughness", MdlTextureValue::FLOAT, MdlTextureUse::ROUGHNESS }, + { "shadowalpha", MdlTextureValue::FLOAT, MdlTextureUse::ALPHA }, + { "sigma", MdlTextureValue::FLOAT, MdlTextureUse::NONE }, + { "sigma_a", MdlTextureValue::COLOR, MdlTextureUse::NONE }, + { "sigma_s", MdlTextureValue::COLOR, MdlTextureUse::NONE }, + { "transmit", MdlTextureValue::COLOR, MdlTextureUse::NONE }, + { "uroughness", MdlTextureValue::FLOAT, MdlTextureUse::ROUGHNESS }, + { "vroughness", MdlTextureValue::FLOAT, MdlTextureUse::ROUGHNESS }, +}; + +const MdlTexturePolicy* mdlTexturePolicy( const std::string& name ) +{ + for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) + { + if( name == policy.name ) + { + return &policy; + } + } + return nullptr; +} + +PbrtDemandTextureBinding generatedMdlTextureBinding( const otk::pbrt::PbrtMaterial& material, + const MdlTexturePolicy& policy ) +{ + if( policy.value == MdlTextureValue::FLOAT ) + { + return pbrtFloatTextureBinding( material, policy.name ); + } + + PbrtDemandTextureBinding binding{ pbrtColorTextureBinding( material, policy.name ) }; + if( policy.value == MdlTextureValue::COLOR_OR_FLOAT && !hasPbrtDemandTextureBinding( binding ) ) + { + binding = pbrtFloatTextureBinding( material, policy.name ); + } + return binding; +} + bool hasGeneratedMdlConstantAmountTexture( const otk::pbrt::PbrtMaterial& material, const std::string& textureName ) { const std::vector parameters{ makeMdlBoundMaterialParameters( material ) }; @@ -240,66 +317,22 @@ bool hasGeneratedMdlFoldableTextureParameter( const otk::pbrt::PbrtMaterial& mat return false; } -bool isGeneratedMdlNamedMaterialAlphaTextureParam( const std::string& paramName ) -{ - return paramName == "alpha" || paramName == "shadowalpha" || paramName == "opacity"; -} - -bool isGeneratedMdlNamedMaterialFloatTextureParam( const std::string& paramName ) -{ - return paramName == "bumpmap" || isGeneratedMdlNamedMaterialAlphaTextureParam( paramName ); -} - -bool isGeneratedMdlRuntimeFloatTextureParam( const std::string& paramName ) -{ - return paramName == "bumpmap" || paramName == "roughness" || paramName == "uroughness" || paramName == "vroughness"; -} - -bool usesGeneratedMdlNamedMaterialKd( const std::string& type ) +bool hasGeneratedMdlFoldableTextureParameter( const otk::pbrt::PbrtMaterial& material, + const MdlTexturePolicy& policy ) { - return type == "matte" || type == "plastic" || type == "uber" || type == "substrate" || type == "translucent" - || type == "kdsubsurface"; + return hasGeneratedMdlFoldableTextureParameter( material, policy.name, MdlBoundParameterType::COLOR ) + || hasGeneratedMdlFoldableTextureParameter( material, policy.name, MdlBoundParameterType::FLOAT ); } -bool usesGeneratedMdlNamedMaterialKs( const std::string& type ) -{ - return type == "plastic" || type == "uber" || type == "substrate" || type == "translucent"; -} - -bool usesGeneratedMdlKs( const std::string& type ) -{ - return usesGeneratedMdlNamedMaterialKs( type ); -} - -bool usesGeneratedMdlNamedMaterialKr( const std::string& type ) -{ - return type == "uber" || type == "mirror" || type == "glass"; -} - -bool usesGeneratedMdlKt( const std::string& type ) -{ - return type == "uber" || type == "glass"; -} - -bool usesGeneratedMdlRoughness( const std::string& type ) -{ - return type == "plastic" || type == "uber" || type == "metal" || type == "translucent"; -} - -bool usesGeneratedMdlAxisRoughness( const std::string& type ) -{ - return type == "uber" || type == "metal" || type == "substrate"; -} - -bool usesGeneratedMdlRoughnessTextureParam( const std::string& type, const std::string& paramName ) +bool usesGeneratedMdlRoughnessTextureParam( const PbrtMaterialDescriptor& material, const std::string& paramName ) { if( paramName == "roughness" ) { - return usesGeneratedMdlRoughness( type ); + return material.has( PbrtMaterialCapability::ROUGHNESS ); } if( paramName == "uroughness" || paramName == "vroughness" ) { - return usesGeneratedMdlAxisRoughness( type ); + return material.has( PbrtMaterialCapability::AXIS_ROUGHNESS ); } return false; } @@ -314,76 +347,79 @@ bool isDirectGeneratedMdlDemandTexture( const PbrtDemandTextureBinding& binding return hasPbrtDemandTextureBinding( binding ) && !binding.transformed; } -PbrtDemandTextureBinding generatedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const std::string& paramName ) +PbrtDemandTextureBinding namedRuntimeBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MdlTexturePolicy& policy ) { - const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; - const PbrtDemandTextureBinding binding{ isGeneratedMdlNamedMaterialFloatTextureParam( paramName ) ? - pbrtFloatTextureBinding( material, paramName.c_str() ) : - pbrtColorTextureBinding( material, paramName.c_str() ) }; + const PbrtDemandTextureBinding binding{ generatedMdlTextureBinding( material, policy ) }; if( !hasPbrtDemandTextureBinding( binding ) ) { return pbrtDemandTextureBinding(); } - const std::string type{ material.type }; - if( paramName == "Kd" && usesGeneratedMdlNamedMaterialKd( type ) ) + if( policy.use == MdlTextureUse::DIFFUSE && descriptor.has( PbrtMaterialCapability::KD ) ) { return binding; } - if( paramName == "Ks" && usesGeneratedMdlNamedMaterialKs( type ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::SPECULAR && descriptor.has( PbrtMaterialCapability::KS ) + && isDirectGeneratedMdlDemandTexture( binding ) ) { return binding; } - if( paramName == "Kr" && usesGeneratedMdlNamedMaterialKr( type ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::REFLECTION && descriptor.has( PbrtMaterialCapability::KR ) + && isDirectGeneratedMdlDemandTexture( binding ) ) { return binding; } - if( paramName == "bumpmap" || isGeneratedMdlNamedMaterialAlphaTextureParam( paramName ) ) + if( policy.use == MdlTextureUse::BUMP || policy.use == MdlTextureUse::ALPHA ) { return binding; } return pbrtDemandTextureBinding(); } -bool hasGeneratedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const std::string& paramName ) +PbrtDemandTextureBinding namedRuntimeBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const std::string& paramName ) +{ + const MdlTexturePolicy* const policy{ mdlTexturePolicy( paramName ) }; + return policy ? namedRuntimeBinding( material, descriptor, *policy ) : pbrtDemandTextureBinding(); +} + +bool hasGeneratedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MdlTexturePolicy& policy ) { - return hasPbrtDemandTextureBinding( generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, paramName ) ); + return hasPbrtDemandTextureBinding( namedRuntimeBinding( material, descriptor, policy ) ); } -bool supportsGeneratedMdlNamedMaterialTextureReference( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const std::string& paramName ) +bool supportsGeneratedMdlNamedMaterialTextureReference( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MdlTexturePolicy& policy ) { - if( namedMaterial.params.FindTexture( paramName ).empty() ) + if( material.params.FindTexture( policy.name ).empty() ) { return true; } - const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; - if( hasGeneratedMdlFoldableTextureParameter( material, paramName, MdlBoundParameterType::COLOR ) - || hasGeneratedMdlFoldableTextureParameter( material, paramName, MdlBoundParameterType::FLOAT ) ) + if( hasGeneratedMdlFoldableTextureParameter( material, policy ) ) { return true; } - return hasGeneratedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, paramName ); + return hasGeneratedMdlNamedMaterialRuntimeTextureBinding( material, descriptor, policy ); } -bool supportsGeneratedMdlNamedMaterialTextureReferences( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial ) +bool supportsNamedTextures( const otk::pbrt::PbrtMaterial& parent, const otk::pbrt::PbrtNamedMaterial& namedMaterial ) { - static const char* const textureParams[] = { - "Kd", "Kr", "Ks", "Kt", "alpha", "amount", "bumpmap", - "eta", "index", "k", "mfp", "opacity", "reflect", "roughness", - "shadowalpha", "sigma", "sigma_a", "sigma_s", "transmit", "uroughness", "vroughness", - }; - - for( const char* const param : textureParams ) + const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; + const PbrtMaterialDescriptor& descriptor{ pbrtMaterialDescriptor( material.type ) }; + if( !supportsGeneratedMdlNamedMaterial( descriptor ) ) { - if( !supportsGeneratedMdlNamedMaterialTextureReference( parent, namedMaterial, param ) ) + return false; + } + for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) + { + if( !supportsGeneratedMdlNamedMaterialTextureReference( material, descriptor, policy ) ) { return false; } @@ -405,17 +441,17 @@ bool supportsGeneratedMdlNamedMaterialReference( const otk::pbrt::PbrtMaterial& return false; } - return supportsGeneratedMdlNamedMaterialType( generatedMdlNamedMaterialType( namedMaterial->second ) ) - && supportsGeneratedMdlNamedMaterialTextureReferences( material, namedMaterial->second ); + return supportsNamedTextures( material, namedMaterial->second ); } -bool supportsGeneratedMdlNamedMaterialReferences( const otk::pbrt::PbrtMaterial& material ) +bool supportsGeneratedMdlNamedMaterialReferences( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor ) { if( material.graph.namedMaterials.empty() ) { return true; } - if( material.type != "mix" ) + if( descriptor.kind != PbrtMaterialKind::MIX ) { return false; } @@ -423,196 +459,172 @@ bool supportsGeneratedMdlNamedMaterialReferences( const otk::pbrt::PbrtMaterial& && supportsGeneratedMdlNamedMaterialReference( material, "namedmaterial2" ); } -PbrtDemandTextureBinding generatedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, - const MaterialGroup& group, - const std::string& paramName ) +bool acceptsGeneratedMdlRuntimeTextureBinding( const PbrtMaterialDescriptor& material, + const MaterialGroup& group, + const MdlTexturePolicy& policy, + const PbrtDemandTextureBinding& binding ) { - PbrtDemandTextureBinding binding{ isGeneratedMdlRuntimeFloatTextureParam( paramName ) ? - pbrtFloatTextureBinding( material, paramName.c_str() ) : - pbrtColorTextureBinding( material, paramName.c_str() ) }; - if( paramName == "amount" && !hasPbrtDemandTextureBinding( binding ) ) - { - binding = pbrtFloatTextureBinding( material, paramName.c_str() ); - } - if( !hasPbrtDemandTextureBinding( binding ) ) - { - return pbrtDemandTextureBinding(); - } - - if( paramName == "Kd" ) + if( policy.use == MdlTextureUse::DIFFUSE ) { - if( flagSet( group.material.flags, MaterialFlags::DIFFUSE_MAP ) && binding.fileName == group.diffuseMapFileName ) - { - return binding; - } - return pbrtDemandTextureBinding(); + return flagSet( group.material.flags, MaterialFlags::DIFFUSE_MAP ) && binding.fileName == group.diffuseMapFileName; } - if( paramName == "Kr" && material.type == "mirror" ) + if( policy.use == MdlTextureUse::REFLECTION ) { - if( flagSet( group.material.flags, MaterialFlags::DIFFUSE_MAP ) && binding.fileName == group.diffuseMapFileName ) + if( material.kind == PbrtMaterialKind::MIRROR ) { - return binding; + return flagSet( group.material.flags, MaterialFlags::DIFFUSE_MAP ) + && binding.fileName == group.diffuseMapFileName; } - return pbrtDemandTextureBinding(); + return material.kind == PbrtMaterialKind::UBER && isDirectGeneratedMdlDemandTexture( binding ); } - if( paramName == "Ks" && usesGeneratedMdlKs( material.type ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::SPECULAR ) { - return binding; + return material.has( PbrtMaterialCapability::KS ) && isDirectGeneratedMdlDemandTexture( binding ); } - if( material.type == "uber" && paramName == "Kr" && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::TRANSMISSION ) { - return binding; + return material.has( PbrtMaterialCapability::KT ) && isDirectGeneratedMdlDemandTexture( binding ); } - if( paramName == "Kt" && usesGeneratedMdlKt( material.type ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::ROUGHNESS ) { - return binding; + return usesGeneratedMdlRoughnessTextureParam( material, policy.name ) + && isDirectGeneratedMdlDemandTexture( binding ); } - if( usesGeneratedMdlRoughnessTextureParam( material.type, paramName ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::AMOUNT ) { - return binding; + return material.kind == PbrtMaterialKind::MIX && isDirectGeneratedMdlDemandTexture( binding ); } - if( material.type == "mix" && paramName == "amount" && isDirectGeneratedMdlDemandTexture( binding ) ) - { - return binding; - } - if( paramName == "bumpmap" ) + return policy.use == MdlTextureUse::BUMP; +} + +PbrtDemandTextureBinding generatedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group, + const MdlTexturePolicy& policy ) +{ + const PbrtDemandTextureBinding binding{ generatedMdlTextureBinding( material, policy ) }; + if( hasPbrtDemandTextureBinding( binding ) + && acceptsGeneratedMdlRuntimeTextureBinding( descriptor, group, policy, binding ) ) { return binding; } return pbrtDemandTextureBinding(); } -bool hasGeneratedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group, const std::string& paramName ) +PbrtDemandTextureBinding generatedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group, + const std::string& paramName ) { - return hasPbrtDemandTextureBinding( generatedMdlRuntimeTextureBinding( material, group, paramName ) ); + const MdlTexturePolicy* const policy{ mdlTexturePolicy( paramName ) }; + return policy ? generatedMdlRuntimeTextureBinding( material, descriptor, group, *policy ) : pbrtDemandTextureBinding(); } -bool supportsGeneratedMdlTextureReferences( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group ) +bool hasGeneratedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group, + const MdlTexturePolicy& policy ) +{ + return hasPbrtDemandTextureBinding( generatedMdlRuntimeTextureBinding( material, descriptor, group, policy ) ); +} + +struct MdlTextureMaps +{ + bool diffuse{}; + bool alpha{}; +}; + +MdlTextureMaps generatedMdlTextureMaps( const MaterialGroup& group ) { const MaterialFlags flags{ group.material.flags }; - const bool hasDiffuseMap{ hasGeneratedMdlDemandTexture( flags, MaterialFlags::DIFFUSE_MAP, - MaterialFlags::DIFFUSE_MAP_ALLOCATED, group.diffuseMapFileName ) }; - const bool hasAlphaMap{ hasGeneratedMdlDemandTexture( flags, MaterialFlags::ALPHA_MAP, - MaterialFlags::ALPHA_MAP_ALLOCATED, group.alphaMapFileName ) }; - - static const char* const textureParams[] = { - "Kd", "Kr", "Ks", "Kt", "alpha", "amount", "bumpmap", - "eta", "index", "k", "mfp", "opacity", "reflect", "roughness", - "shadowalpha", "sigma", "sigma_a", "sigma_s", "transmit", "uroughness", "vroughness", + return MdlTextureMaps{ + hasGeneratedMdlDemandTexture( flags, MaterialFlags::DIFFUSE_MAP, MaterialFlags::DIFFUSE_MAP_ALLOCATED, + group.diffuseMapFileName ), + hasGeneratedMdlDemandTexture( flags, MaterialFlags::ALPHA_MAP, MaterialFlags::ALPHA_MAP_ALLOCATED, + group.alphaMapFileName ), }; +} + +bool needsGeneratedMdlDiffuseMap( const PbrtMaterialDescriptor& material, const MdlTexturePolicy& policy ) +{ + return policy.use == MdlTextureUse::DIFFUSE + || ( policy.use == MdlTextureUse::REFLECTION && material.kind == PbrtMaterialKind::MIRROR ); +} - for( const char* const param : textureParams ) +bool supportsGeneratedMdlTextureReference( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group, + const MdlTexturePolicy& policy, + const MdlTextureMaps& maps ) +{ + const std::string textureName{ material.params.FindTexture( policy.name ) }; + if( textureName.empty() ) + { + return true; + } + if( policy.use == MdlTextureUse::BUMP ) + { + return hasGeneratedMdlRuntimeTextureBinding( material, descriptor, group, policy ); + } + if( hasGeneratedMdlFoldableTextureParameter( material, policy ) ) + { + return true; + } + if( policy.use == MdlTextureUse::ALPHA ) + { + return maps.alpha; + } + if( policy.use == MdlTextureUse::AMOUNT && hasGeneratedMdlConstantAmountTexture( material, textureName ) ) + { + return true; + } + if( needsGeneratedMdlDiffuseMap( descriptor, policy ) && !maps.diffuse ) { - const std::string textureName{ material.params.FindTexture( param ) }; - if( textureName.empty() ) - { - continue; - } - const std::string paramName{ param }; - if( paramName == "bumpmap" ) - { - if( !hasGeneratedMdlRuntimeTextureBinding( material, group, paramName ) ) - { - return false; - } - continue; - } - if( hasGeneratedMdlFoldableTextureParameter( material, paramName, MdlBoundParameterType::COLOR ) ) - { - continue; - } - if( hasGeneratedMdlFoldableTextureParameter( material, paramName, MdlBoundParameterType::FLOAT ) ) - { - continue; - } - if( paramName == "Kd" ) - { - if( !hasDiffuseMap || !hasGeneratedMdlRuntimeTextureBinding( material, group, "Kd" ) ) - { - return false; - } - continue; - } - if( paramName == "Kr" && material.type == "mirror" ) - { - if( !hasDiffuseMap || !hasGeneratedMdlRuntimeTextureBinding( material, group, "Kr" ) ) - { - return false; - } - continue; - } - if( paramName == "Ks" && usesGeneratedMdlKs( material.type ) ) - { - if( !hasGeneratedMdlRuntimeTextureBinding( material, group, paramName ) ) - { - return false; - } - continue; - } - if( material.type == "uber" && paramName == "Kr" ) - { - if( !hasGeneratedMdlRuntimeTextureBinding( material, group, paramName ) ) - { - return false; - } - continue; - } - if( paramName == "Kt" && usesGeneratedMdlKt( material.type ) ) - { - if( !hasGeneratedMdlRuntimeTextureBinding( material, group, paramName ) ) - { - return false; - } - continue; - } - if( usesGeneratedMdlRoughnessTextureParam( material.type, paramName ) ) - { - if( !hasGeneratedMdlRuntimeTextureBinding( material, group, paramName ) ) - { - return false; - } - continue; - } - if( paramName == "alpha" || paramName == "shadowalpha" || paramName == "opacity" ) - { - if( !hasAlphaMap ) - { - return false; - } - continue; - } - if( paramName == "amount" ) - { - if( !hasGeneratedMdlConstantAmountTexture( material, textureName ) - && !hasGeneratedMdlRuntimeTextureBinding( material, group, paramName ) ) - { - return false; - } - continue; - } return false; } + return hasGeneratedMdlRuntimeTextureBinding( material, descriptor, group, policy ); +} +bool supportsGeneratedMdlTextureFlags( const MaterialGroup& group, const MdlTextureMaps& maps ) +{ + const MaterialFlags flags{ group.material.flags }; const MaterialFlags supportedFlags{ MaterialFlags::ALPHA_MAP | MaterialFlags::ALPHA_MAP_ALLOCATED | MaterialFlags::DIFFUSE_MAP | MaterialFlags::DIFFUSE_MAP_ALLOCATED }; if( ( flags & ~supportedFlags ) != MaterialFlags::NONE ) { return false; } - if( flagSet( flags, MaterialFlags::DIFFUSE_MAP ) && !hasDiffuseMap ) + if( flagSet( flags, MaterialFlags::DIFFUSE_MAP ) && !maps.diffuse ) { return false; } - if( flagSet( flags, MaterialFlags::ALPHA_MAP ) && !hasAlphaMap ) + if( flagSet( flags, MaterialFlags::ALPHA_MAP ) && !maps.alpha ) { return false; } return true; } -bool hasGeneratedMdlUnsupportedTextureReference( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group ) +bool supportsGeneratedMdlTextureReferences( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group ) { - if( !supportsGeneratedMdlTextureReferences( material, group ) ) + const MdlTextureMaps maps{ generatedMdlTextureMaps( group ) }; + for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) + { + if( !supportsGeneratedMdlTextureReference( material, descriptor, group, policy, maps ) ) + { + return false; + } + } + return supportsGeneratedMdlTextureFlags( group, maps ); +} + +bool hasGeneratedMdlUnsupportedTextureReference( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group ) +{ + if( !supportsGeneratedMdlTextureReferences( material, descriptor, group ) ) { return true; } @@ -642,15 +654,19 @@ void setMaterialGroupMdlTextureBinding( MaterialGroup& group, uint_t index, cons group.mdlTextureBindings[index].binding = MdlMaterialTextureBinding{ textureId, binding.scale, binding.bias }; } -void setGeneratedMdlDiffuseTextureBinding( MaterialGroup& group, SceneSyncState& sync, DemandTextureCache& demandTextureCache ) +void setGeneratedMdlDiffuseTextureBinding( MaterialGroup& group, + const PbrtMaterialDescriptor& material, + SceneSyncState& sync, + DemandTextureCache& demandTextureCache ) { if( !group.pbrtMaterial || !flagSet( group.material.flags, MaterialFlags::DIFFUSE_MAP_ALLOCATED ) ) { return; } - const char* const paramName{ group.pbrtMaterial->type == "mirror" ? "Kr" : "Kd" }; - const PbrtDemandTextureBinding binding{ generatedMdlRuntimeTextureBinding( *group.pbrtMaterial, group, paramName ) }; + const char* const paramName{ material.kind == PbrtMaterialKind::MIRROR ? "Kr" : "Kd" }; + const PbrtDemandTextureBinding binding{ + generatedMdlRuntimeTextureBinding( *group.pbrtMaterial, material, group, paramName ) }; if( hasPbrtDemandTextureBinding( binding ) ) { const uint_t textureId{ demandTextureCache.createLinearTextureFromFile( binding.fileName, binding.gamma ) }; @@ -676,13 +692,14 @@ void createGeneratedMdlTextureBinding( MaterialGroup& group, } void createGeneratedMdlTextureBinding( MaterialGroup& group, + const PbrtMaterialDescriptor& material, SceneSyncState& sync, DemandTextureCache& demandTextureCache, const char* paramName, uint_t index ) { createGeneratedMdlTextureBinding( group, sync, demandTextureCache, - generatedMdlRuntimeTextureBinding( *group.pbrtMaterial, group, paramName ), index ); + generatedMdlRuntimeTextureBinding( *group.pbrtMaterial, material, group, paramName ), index ); } uint_t generatedMdlMixNamedMaterialTextureBindingIndex( uint_t namedMaterialIndex, uint_t offset ) @@ -715,11 +732,13 @@ const otk::pbrt::PbrtNamedMaterial* findGeneratedMdlMixNamedMaterial( const Mate } PbrtDemandTextureBinding generatedMdlNamedMaterialAlphaCutoutBinding( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial ) + const otk::pbrt::PbrtNamedMaterial& namedMaterial ) { + const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; + const PbrtMaterialDescriptor& descriptor{ pbrtMaterialDescriptor( material.type ) }; for( const char* const paramName : { "alpha", "shadowalpha", "opacity" } ) { - const PbrtDemandTextureBinding binding{ generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, paramName ) }; + const PbrtDemandTextureBinding binding{ namedRuntimeBinding( material, descriptor, paramName ) }; if( hasPbrtDemandTextureBinding( binding ) ) { return binding; @@ -730,11 +749,13 @@ PbrtDemandTextureBinding generatedMdlNamedMaterialAlphaCutoutBinding( const otk: void setGeneratedMdlMixAlphaCutout( const Options& options, const GeometryInstance& instance, MaterialGroup& group ) { + const PbrtMaterialDescriptor& material{ + pbrtMaterialDescriptor( group.pbrtMaterial ? group.pbrtMaterial->type : std::string{} ) }; if( !options.useMdlMaterials || instance.primitive != GeometryPrimitive::TRIANGLE - || instance.groups.size() != 1 || !group.pbrtMaterial || group.pbrtMaterial->type != "mix" + || instance.groups.size() != 1 || !group.pbrtMaterial || material.kind != PbrtMaterialKind::MIX || flagSet( group.material.flags, MaterialFlags::ALPHA_MAP ) || !group.pbrtMaterial->graph.fallbackReasons.empty() - || !supportsGeneratedMdlNamedMaterialReferences( *group.pbrtMaterial ) - || !supportsGeneratedMdlTextureReferences( *group.pbrtMaterial, group ) ) + || !supportsGeneratedMdlNamedMaterialReferences( *group.pbrtMaterial, material ) + || !supportsGeneratedMdlTextureReferences( *group.pbrtMaterial, material, group ) ) { return; } @@ -757,26 +778,36 @@ void setGeneratedMdlMixAlphaCutout( const Options& options, const GeometryInstan } } -void createGeneratedMdlNamedMaterialTextureBinding( MaterialGroup& group, - SceneSyncState& sync, - DemandTextureCache& demandTextureCache, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - uint_t namedMaterialIndex, - const char* paramName, - uint_t offset ) +void createGeneratedMdlNamedMaterialTextureBinding( MaterialGroup& group, + SceneSyncState& sync, + DemandTextureCache& demandTextureCache, + const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + uint_t namedMaterialIndex, + const char* paramName, + uint_t offset ) { createGeneratedMdlTextureBinding( group, sync, demandTextureCache, - generatedMdlNamedMaterialRuntimeTextureBinding( *group.pbrtMaterial, namedMaterial, paramName ), + namedRuntimeBinding( material, descriptor, paramName ), generatedMdlMixNamedMaterialTextureBindingIndex( namedMaterialIndex, offset ) ); } -void createGeneratedMdlNamedMaterialAlphaTextureBinding( MaterialGroup& group, - SceneSyncState& sync, - DemandTextureCache& demandTextureCache, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - uint_t namedMaterialIndex ) +void createGeneratedMdlNamedMaterialAlphaTextureBinding( MaterialGroup& group, + SceneSyncState& sync, + DemandTextureCache& demandTextureCache, + const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + uint_t namedMaterialIndex ) { - const PbrtDemandTextureBinding binding{ generatedMdlNamedMaterialAlphaCutoutBinding( *group.pbrtMaterial, namedMaterial ) }; + PbrtDemandTextureBinding binding{}; + for( const char* const paramName : { "alpha", "shadowalpha", "opacity" } ) + { + binding = namedRuntimeBinding( material, descriptor, paramName ); + if( hasPbrtDemandTextureBinding( binding ) ) + { + break; + } + } if( !hasPbrtDemandTextureBinding( binding ) ) { return; @@ -798,20 +829,26 @@ void createGeneratedMdlMixNamedMaterialTextureBindings( MaterialGroup& grou return; } - createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, *namedMaterial, namedMaterialIndex, + const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( *group.pbrtMaterial, *namedMaterial ) }; + const PbrtMaterialDescriptor& descriptor{ pbrtMaterialDescriptor( material.type ) }; + createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, material, descriptor, namedMaterialIndex, "Kd", MDL_MATERIAL_MIX_NAMED_KD_TEXTURE_BINDING_OFFSET ); - createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, *namedMaterial, namedMaterialIndex, + createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, material, descriptor, namedMaterialIndex, "Ks", MDL_MATERIAL_MIX_NAMED_KS_TEXTURE_BINDING_OFFSET ); - createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, *namedMaterial, namedMaterialIndex, + createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, material, descriptor, namedMaterialIndex, "Kr", MDL_MATERIAL_MIX_NAMED_KR_TEXTURE_BINDING_OFFSET ); - createGeneratedMdlNamedMaterialAlphaTextureBinding( group, sync, demandTextureCache, *namedMaterial, namedMaterialIndex ); - createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, *namedMaterial, namedMaterialIndex, + createGeneratedMdlNamedMaterialAlphaTextureBinding( group, sync, demandTextureCache, material, descriptor, namedMaterialIndex ); + createGeneratedMdlNamedMaterialTextureBinding( group, sync, demandTextureCache, material, descriptor, namedMaterialIndex, "bumpmap", MDL_MATERIAL_MIX_NAMED_BUMPMAP_TEXTURE_BINDING_OFFSET ); } -void createGeneratedMdlMixTextureBindings( MaterialGroup& group, SceneSyncState& sync, DemandTextureCache& demandTextureCache ) +void createGeneratedMdlMixTextureBindings( MaterialGroup& group, + const PbrtMaterialDescriptor& material, + SceneSyncState& sync, + DemandTextureCache& demandTextureCache ) { - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "amount", MDL_MATERIAL_MIX_AMOUNT_TEXTURE_BINDING_INDEX ); + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "amount", + MDL_MATERIAL_MIX_AMOUNT_TEXTURE_BINDING_INDEX ); createGeneratedMdlMixNamedMaterialTextureBindings( group, sync, demandTextureCache, "namedmaterial1", 0U ); createGeneratedMdlMixNamedMaterialTextureBindings( group, sync, demandTextureCache, "namedmaterial2", 1U ); } @@ -823,56 +860,66 @@ void resolveGeneratedMdlTextureBindings( const Options& options, SceneSyncState& sync ) { clearMaterialGroupMdlTextureBindings( group ); + const PbrtMaterialDescriptor& material{ + pbrtMaterialDescriptor( group.pbrtMaterial ? group.pbrtMaterial->type : std::string{} ) }; if( !options.useMdlMaterials || instance.primitive != GeometryPrimitive::TRIANGLE - || instance.groups.size() != 1 || !group.pbrtMaterial || !supportsGeneratedMdlMaterial( group.pbrtMaterial->type ) - || !group.pbrtMaterial->graph.fallbackReasons.empty() || !supportsGeneratedMdlNamedMaterialReferences( *group.pbrtMaterial ) - || !supportsGeneratedMdlTextureReferences( *group.pbrtMaterial, group ) ) + || instance.groups.size() != 1 || !group.pbrtMaterial || !supportsGeneratedMdlMaterial( material ) + || !group.pbrtMaterial->graph.fallbackReasons.empty() + || !supportsGeneratedMdlNamedMaterialReferences( *group.pbrtMaterial, material ) + || !supportsGeneratedMdlTextureReferences( *group.pbrtMaterial, material, group ) ) { return; } - setGeneratedMdlDiffuseTextureBinding( group, sync, demandTextureCache ); - const std::string& type{ group.pbrtMaterial->type }; - if( usesGeneratedMdlKs( type ) ) + setGeneratedMdlDiffuseTextureBinding( group, material, sync, demandTextureCache ); + if( material.has( PbrtMaterialCapability::KS ) ) { - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "Ks", MDL_MATERIAL_KS_TEXTURE_BINDING_INDEX ); + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "Ks", MDL_MATERIAL_KS_TEXTURE_BINDING_INDEX ); } - if( type == "uber" ) + if( material.kind == PbrtMaterialKind::UBER ) { - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "Kr", MDL_MATERIAL_KR_TEXTURE_BINDING_INDEX ); + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "Kr", MDL_MATERIAL_KR_TEXTURE_BINDING_INDEX ); } - if( usesGeneratedMdlKt( type ) ) + if( material.has( PbrtMaterialCapability::KT ) ) { - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "Kt", MDL_MATERIAL_KT_TEXTURE_BINDING_INDEX ); + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "Kt", MDL_MATERIAL_KT_TEXTURE_BINDING_INDEX ); } - if( usesGeneratedMdlRoughness( type ) ) + if( material.has( PbrtMaterialCapability::ROUGHNESS ) ) { - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "roughness", MDL_MATERIAL_ROUGHNESS_TEXTURE_BINDING_INDEX ); + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "roughness", + MDL_MATERIAL_ROUGHNESS_TEXTURE_BINDING_INDEX ); } - if( usesGeneratedMdlAxisRoughness( type ) ) + if( material.has( PbrtMaterialCapability::AXIS_ROUGHNESS ) ) { - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "uroughness", MDL_MATERIAL_UROUGHNESS_TEXTURE_BINDING_INDEX ); - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "vroughness", MDL_MATERIAL_VROUGHNESS_TEXTURE_BINDING_INDEX ); + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "uroughness", + MDL_MATERIAL_UROUGHNESS_TEXTURE_BINDING_INDEX ); + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "vroughness", + MDL_MATERIAL_VROUGHNESS_TEXTURE_BINDING_INDEX ); } - createGeneratedMdlTextureBinding( group, sync, demandTextureCache, "bumpmap", MDL_MATERIAL_BUMPMAP_TEXTURE_BINDING_INDEX ); - if( type == "mix" ) + createGeneratedMdlTextureBinding( group, material, sync, demandTextureCache, "bumpmap", + MDL_MATERIAL_BUMPMAP_TEXTURE_BINDING_INDEX ); + if( material.kind == PbrtMaterialKind::MIX ) { - createGeneratedMdlMixTextureBindings( group, sync, demandTextureCache ); + createGeneratedMdlMixTextureBindings( group, material, sync, demandTextureCache ); } } bool usesGeneratedMdlMaterial( const Options& options, const GeometryInstance& instance, const MaterialGroup& group ) { + const PbrtMaterialDescriptor& material{ + pbrtMaterialDescriptor( group.pbrtMaterial ? group.pbrtMaterial->type : std::string{} ) }; return options.useMdlMaterials && instance.primitive == GeometryPrimitive::TRIANGLE - && instance.groups.size() == 1 && group.pbrtMaterial && supportsGeneratedMdlMaterial( group.pbrtMaterial->type ) - && group.pbrtMaterial->graph.fallbackReasons.empty() && supportsGeneratedMdlNamedMaterialReferences( *group.pbrtMaterial ) - && !hasGeneratedMdlUnsupportedTextureReference( *group.pbrtMaterial, group ); + && instance.groups.size() == 1 && group.pbrtMaterial && supportsGeneratedMdlMaterial( material ) + && group.pbrtMaterial->graph.fallbackReasons.empty() + && supportsGeneratedMdlNamedMaterialReferences( *group.pbrtMaterial, material ) + && !hasGeneratedMdlUnsupportedTextureReference( *group.pbrtMaterial, material, group ); } bool usesGeneratedMdlFourierMaterial( const Options& options, const GeometryInstance& instance, const MaterialGroup& group ) { return options.useMdlMaterials && instance.primitive == GeometryPrimitive::TRIANGLE - && instance.groups.size() == 1 && group.pbrtMaterial && group.pbrtMaterial->type == "fourier"; + && instance.groups.size() == 1 && group.pbrtMaterial + && pbrtMaterialKind( group.pbrtMaterial->type ) == PbrtMaterialKind::FOURIER; } bool usesGeneratedMdlUnsupportedFallback( const Options& options, const GeometryInstance& instance, const MaterialGroup& group ) @@ -1105,7 +1152,7 @@ MaterialState PbrtMaterialResolver::resolveMdlMaterialState( SceneSyncState& FourierBsdfTableLoadResult PbrtMaterialResolver::loadFourierBsdfTableResourceState( const MaterialGroup& group ) { - if( !group.pbrtMaterial || group.pbrtMaterial->type != "fourier" ) + if( !group.pbrtMaterial || pbrtMaterialKind( group.pbrtMaterial->type ) != PbrtMaterialKind::FOURIER ) { return FourierBsdfTableLoadResult{}; } diff --git a/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp b/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp index 3aec3f3f..7f586b37 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp @@ -6,43 +6,13 @@ #ifdef OTK_USE_MDL -#include +#include "DemandPbrtScene/MdlHandleTypes.h" -#include #include namespace demandPbrtScene { namespace { -std::string describeContextMessages( const mi::neuraylib::IMdl_execution_context* context ) -{ - if( !context ) - return {}; - - std::ostringstream out; - for( mi::Size i = 0; i < context->get_messages_count(); ++i ) - { - mi::base::Handle message( context->get_message( i ) ); - if( message.is_valid_interface() ) - out << message->get_string() << '\n'; - } - return out.str(); -} - -[[noreturn]] void failMdlBsdfCompile( const std::string& message, const mi::neuraylib::IMdl_execution_context* context = nullptr ) -{ - const std::string contextMessages{ describeContextMessages( context ) }; - throw std::runtime_error( contextMessages.empty() ? message : message + ":\n" + contextMessages ); -} - -void requireMdlBsdfCompile( bool condition, const std::string& message, const mi::neuraylib::IMdl_execution_context* context = nullptr ) -{ - if( !condition ) - { - failMdlBsdfCompile( message, context ); - } -} - std::string mdlBsdfVisibleFunctions( const std::string& baseFunctionName ) { return baseFunctionName + "_init," + baseFunctionName + "_sample," + baseFunctionName + "_evaluate," @@ -75,56 +45,6 @@ void captureBsdfCallableName( MdlBsdfCallablePtx& result, const std::string& fun throw std::runtime_error( "MDL generated unexpected BSDF callable function name " + functionName ); } -MdlTargetArgumentBlock captureTargetArgumentBlock( const mi::neuraylib::ITarget_code* targetCode, - const mi::neuraylib::ICompiled_material* compiledMaterial, - mi::neuraylib::IMdl_execution_context* context ) -{ - requireMdlBsdfCompile( targetCode != nullptr, "Cannot capture MDL argument block without target code", context ); - requireMdlBsdfCompile( compiledMaterial != nullptr, "Cannot capture MDL argument block without a compiled material", context ); - requireMdlBsdfCompile( targetCode->get_callable_function_count() > 0U, - "Cannot capture MDL argument block without callable functions", context ); - - const mi::Size argumentBlockIndex{ targetCode->get_callable_function_argument_block_index( 0U ) }; - if( argumentBlockIndex == ~mi::Size( 0 ) ) - { - return MdlTargetArgumentBlock{}; - } - - mi::base::Handle argumentBlock( targetCode->get_argument_block( argumentBlockIndex ) ); - requireMdlBsdfCompile( argumentBlock.is_valid_interface(), "MDL target code did not expose an argument block", context ); - - mi::base::Handle layout( targetCode->get_argument_block_layout( argumentBlockIndex ) ); - requireMdlBsdfCompile( layout.is_valid_interface(), "MDL target code did not expose an argument block layout", context ); - - MdlTargetArgumentBlock result; - result.data.assign( argumentBlock->get_data(), argumentBlock->get_data() + argumentBlock->get_size() ); - - const mi::Size parameterCount{ compiledMaterial->get_parameter_count() }; - requireMdlBsdfCompile( layout->get_num_elements() >= parameterCount, - "MDL argument block layout has fewer entries than the compiled material", context ); - for( mi::Size i = 0; i < parameterCount; ++i ) - { - const char* const name = compiledMaterial->get_parameter_name( i ); - requireMdlBsdfCompile( name != nullptr, "MDL compiled material exposed a null parameter name", context ); - - const mi::neuraylib::Target_value_layout_state state{ layout->get_nested_state( i ) }; - requireMdlBsdfCompile( state.m_state_offs != ~mi::Uint32( 0 ), - "MDL argument block layout did not expose parameter state for " + std::string{ name }, context ); - - mi::neuraylib::IValue::Kind kind{}; - mi::Size size{}; - const mi::Size offset{ layout->get_layout( kind, size, state ) }; - requireMdlBsdfCompile( offset != ~mi::Size( 0 ), - "MDL argument block layout did not expose parameter offset for " + std::string{ name }, context ); - requireMdlBsdfCompile( offset + size <= result.data.size(), - "MDL argument block layout parameter exceeds block size for " + std::string{ name }, context ); - - result.parameters.push_back( MdlTargetArgumentBlockParameter{ - name, static_cast( kind ), static_cast( offset ), static_cast( size ) } ); - } - return result; -} - } // namespace MdlBsdfCallablePtx compileMdlBsdfCallablesToPtx( mi::neuraylib::INeuray* neuray, @@ -134,51 +54,47 @@ MdlBsdfCallablePtx compileMdlBsdfCallablesToPtx( mi::neuraylib::INeuray* const std::string& expressionPath, const std::string& baseFunctionName ) { - requireMdlBsdfCompile( neuray != nullptr, "Cannot compile MDL BSDF callables without an MDL SDK instance" ); - requireMdlBsdfCompile( transaction != nullptr, "Cannot compile MDL BSDF callables without an MDL transaction" ); - requireMdlBsdfCompile( compiledMaterial != nullptr, - "Cannot compile MDL BSDF callables without a compiled material" ); - requireMdlBsdfCompile( context != nullptr, "Cannot compile MDL BSDF callables without an execution context" ); - requireMdlBsdfCompile( !expressionPath.empty(), "Cannot compile MDL BSDF callables without an expression path" ); - requireMdlBsdfCompile( !baseFunctionName.empty(), - "Cannot compile MDL BSDF callables without a base function " - "name" ); - - mi::base::Handle backendApi( neuray->get_api_component() ); - requireMdlBsdfCompile( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); - - mi::base::Handle ptxBackend( backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); - requireMdlBsdfCompile( ptxBackend.is_valid_interface(), "Failed to get MDL CUDA PTX backend" ); - requireMdlBsdfCompile( ptxBackend->set_option( "sm_version", "50" ) == 0, - "Failed to set MDL CUDA PTX target architecture" ); - requireMdlBsdfCompile( ptxBackend->set_option( "df_handle_slot_mode", "none" ) == 0, - "Failed to set MDL BSDF handle slot mode" ); + requireMdl( neuray != nullptr, "Cannot compile MDL BSDF callables without an MDL SDK instance" ); + requireMdl( transaction != nullptr, "Cannot compile MDL BSDF callables without an MDL transaction" ); + requireMdl( compiledMaterial != nullptr, "Cannot compile MDL BSDF callables without a compiled material" ); + requireMdl( context != nullptr, "Cannot compile MDL BSDF callables without an execution context" ); + requireMdl( !expressionPath.empty(), "Cannot compile MDL BSDF callables without an expression path" ); + requireMdl( !baseFunctionName.empty(), "Cannot compile MDL BSDF callables without a base function name" ); + + BackendApiHandle backendApi( neuray->get_api_component() ); + requireMdl( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); + + BackendHandle ptxBackend( backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); + requireMdl( ptxBackend.is_valid_interface(), "Failed to get MDL CUDA PTX backend" ); + requireMdl( ptxBackend->set_option( "sm_version", "50" ) == 0, "Failed to set MDL CUDA PTX target architecture" ); + requireMdl( ptxBackend->set_option( "df_handle_slot_mode", "none" ) == 0, + "Failed to set MDL BSDF handle slot mode" ); const std::string visibleFunctions{ mdlBsdfVisibleFunctions( baseFunctionName ) }; - requireMdlBsdfCompile( ptxBackend->set_option( "visible_functions", visibleFunctions.c_str() ) == 0, - "Failed to restrict MDL BSDF visible functions" ); + requireMdl( ptxBackend->set_option( "visible_functions", visibleFunctions.c_str() ) == 0, + "Failed to restrict MDL BSDF visible functions" ); context->clear_messages(); - mi::base::Handle targetCode( ptxBackend->translate_material_df( + TargetCodeHandle targetCode( ptxBackend->translate_material_df( transaction, compiledMaterial, expressionPath.c_str(), baseFunctionName.c_str(), context ) ); - requireMdlBsdfCompile( targetCode.is_valid_interface(), "Failed to translate MDL BSDF to PTX", context ); - requireMdlBsdfCompile( targetCode->get_code_size() > 0U, "MDL generated empty BSDF PTX target code" ); - requireMdlBsdfCompile( targetCode->get_callable_function_count() == 4U, - "MDL generated unexpected BSDF callable function count" ); + requireMdl( targetCode.is_valid_interface(), "Failed to translate MDL BSDF to PTX", context ); + requireMdl( targetCode->get_code_size() > 0U, "MDL generated empty BSDF PTX target code" ); + requireMdl( targetCode->get_callable_function_count() == 4U, + "MDL generated unexpected BSDF callable function count" ); MdlBsdfCallablePtx result; result.ptx.assign( targetCode->get_code(), static_cast( targetCode->get_code_size() ) ); for( mi::Size i = 0; i < targetCode->get_callable_function_count(); ++i ) { const char* const functionName = targetCode->get_callable_function( i ); - requireMdlBsdfCompile( functionName != nullptr, "MDL generated a null BSDF callable function name" ); + requireMdl( functionName != nullptr, "MDL generated a null BSDF callable function name" ); captureBsdfCallableName( result, functionName, baseFunctionName ); } - requireMdlBsdfCompile( !result.initFunctionName.empty(), "MDL did not generate a BSDF init callable" ); - requireMdlBsdfCompile( !result.sampleFunctionName.empty(), "MDL did not generate a BSDF sample callable" ); - requireMdlBsdfCompile( !result.evaluateFunctionName.empty(), "MDL did not generate a BSDF evaluate callable" ); - requireMdlBsdfCompile( !result.pdfFunctionName.empty(), "MDL did not generate a BSDF PDF callable" ); - result.argumentBlock = captureTargetArgumentBlock( targetCode.get(), compiledMaterial, context ); + requireMdl( !result.initFunctionName.empty(), "MDL did not generate a BSDF init callable" ); + requireMdl( !result.sampleFunctionName.empty(), "MDL did not generate a BSDF sample callable" ); + requireMdl( !result.evaluateFunctionName.empty(), "MDL did not generate a BSDF evaluate callable" ); + requireMdl( !result.pdfFunctionName.empty(), "MDL did not generate a BSDF PDF callable" ); + result.argumentBlock = captureMdlTargetArgumentBlock( targetCode.get(), compiledMaterial, context ); return result; } diff --git a/examples/DemandLoading/DemandPbrtScene/MdlKeyBuilder.cpp b/examples/DemandLoading/DemandPbrtScene/MdlKeyBuilder.cpp new file mode 100644 index 00000000..e605fa9c --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/MdlKeyBuilder.cpp @@ -0,0 +1,390 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "DemandPbrtScene/MdlKeyBuilder.h" + +#ifdef OTK_USE_MDL + +#include +#include +#include +#include +#include + +namespace demandPbrtScene { +namespace { + +std::vector sortedNames( std::initializer_list names ) +{ + std::vector result; + result.reserve( names.size() ); + std::copy( names.begin(), names.end(), std::back_inserter( result ) ); + std::sort( result.begin(), result.end() ); + return result; +} + +const std::vector& materialTextureParamNames() +{ + static const std::vector names{ sortedNames( { + "Kd", "Kr", "Ks", "Kt", "alpha", "amount", "bumpmap", + "eta", "index", "k", "mfp", "opacity", "reflect", "roughness", + "shadowalpha", "sigma", "sigma_a", "sigma_s", "transmit", "uroughness", "vroughness", + } ) }; + return names; +} + +const std::vector& textureTextureParamNames() +{ + static const std::vector names{ sortedNames( { + "amount", + "scale", + "tex", + "tex1", + "tex2", + } ) }; + return names; +} + +const std::vector& namedMaterialParamNames() +{ + static const std::vector names{ sortedNames( { + "material", + "material1", + "material2", + "namedmaterial1", + "namedmaterial2", + } ) }; + return names; +} + +bool contains( const std::vector& values, const std::string& value ) +{ + return std::find( values.begin(), values.end(), value ) != values.end(); +} + +std::string paramSetToString( const ::pbrt::ParamSet& params ) +{ + ::pbrt::ParamSet copy{ params }; + return copy.ToString(); +} + +struct SourceSignatureEmitter +{ + void appendTextureReference( std::ostringstream& out, const std::string& paramName, const std::string& ) const + { + out << "|texture-ref(" << paramName << ")="; + } + + void appendMaterialReference( std::ostringstream& out, const std::string& paramName, const std::string& ) const + { + out << "|material-ref(" << paramName << ")="; + } + + void appendRecursiveTexture( std::ostringstream& out, const std::string&, const otk::pbrt::PbrtTexture& texture ) const + { + out << "texture(" << texture.valueType << ":" << texture.type << ";recursive)"; + } + + void appendTexture( std::ostringstream& out, const std::string&, const otk::pbrt::PbrtTexture& texture ) const + { + out << "texture(" << texture.valueType << ":" << texture.type; + } + + void appendMaterial( std::ostringstream& out, const std::string& type, const ::pbrt::ParamSet& ) const + { + out << "material(" << type; + } +}; + +struct InstanceSignatureEmitter +{ + void appendTextureReference( std::ostringstream& out, const std::string& paramName, const std::string& textureName ) const + { + out << "|texture-ref(" << paramName << ")=" << textureName << ':'; + } + + void appendMaterialReference( std::ostringstream& out, const std::string& paramName, const std::string& materialName ) const + { + out << "|material-ref(" << paramName << ")=" << materialName << ':'; + } + + void appendRecursiveTexture( std::ostringstream& out, + const std::string& graphKey, + const otk::pbrt::PbrtTexture& texture ) const + { + out << "texture(key=" << graphKey << ",name=" << texture.name << ",kind=" << texture.valueType << ':' + << texture.type << ";recursive)"; + } + + void appendTexture( std::ostringstream& out, const std::string& graphKey, const otk::pbrt::PbrtTexture& texture ) const + { + out << "texture(key=" << graphKey << ",name=" << texture.name << ",kind=" << texture.valueType << ':' + << texture.type << "|params=" << paramSetToString( texture.params ); + } + + void appendMaterial( std::ostringstream& out, const std::string& type, const ::pbrt::ParamSet& params ) const + { + out << "material(" << type << "|params=" << paramSetToString( params ); + } +}; + +template +void appendTextureGraphSignature( std::ostringstream& out, + const std::string& graphKey, + const otk::pbrt::PbrtTexture& texture, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& textureStack, + const Emitter& emitter ); + +template +void appendTextureReference( std::ostringstream& out, + const std::string& paramName, + const std::string& textureName, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& textureStack, + const Emitter& emitter ) +{ + emitter.appendTextureReference( out, paramName, textureName ); + bool found{ false }; + for( otk::pbrt::PbrtTextureMap::const_iterator it = graph.textures.begin(); it != graph.textures.end(); ++it ) + { + if( it->second.name == textureName ) + { + if( found ) + out << ","; + appendTextureGraphSignature( out, it->first, it->second, graph, textureStack, emitter ); + found = true; + } + } + if( !found ) + { + out << "missing"; + } +} + +template +void appendTextureReferences( std::ostringstream& out, + const ::pbrt::ParamSet& params, + const std::vector& paramNames, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& textureStack, + const Emitter& emitter ) +{ + for( std::vector::const_iterator it = paramNames.begin(); it != paramNames.end(); ++it ) + { + const std::string textureName{ params.FindTexture( *it ) }; + if( !textureName.empty() ) + { + appendTextureReference( out, *it, textureName, graph, textureStack, emitter ); + } + } +} + +template +void appendMaterialGraphSignature( std::ostringstream& out, + const std::string& type, + const ::pbrt::ParamSet& params, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& materialStack, + std::vector& textureStack, + const Emitter& emitter ); + +template +void appendMaterialReference( std::ostringstream& out, + const std::string& paramName, + const std::string& materialName, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& materialStack, + std::vector& textureStack, + const Emitter& emitter ) +{ + emitter.appendMaterialReference( out, paramName, materialName ); + if( contains( materialStack, materialName ) ) + { + out << "recursive"; + return; + } + + const otk::pbrt::PbrtNamedMaterialMap::const_iterator material = graph.namedMaterials.find( materialName ); + if( material == graph.namedMaterials.end() ) + { + out << "missing"; + return; + } + + materialStack.push_back( materialName ); + appendMaterialGraphSignature( out, material->second.type, material->second.params, graph, materialStack, textureStack, + emitter ); + materialStack.pop_back(); +} + +template +void appendMaterialReferences( std::ostringstream& out, + const ::pbrt::ParamSet& params, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& materialStack, + std::vector& textureStack, + const Emitter& emitter ) +{ + const std::vector& paramNames{ namedMaterialParamNames() }; + for( std::vector::const_iterator it = paramNames.begin(); it != paramNames.end(); ++it ) + { + const std::string materialName{ params.FindOneString( *it, std::string{} ) }; + if( !materialName.empty() ) + { + appendMaterialReference( out, *it, materialName, graph, materialStack, textureStack, emitter ); + } + } +} + +template +void appendTextureGraphSignature( std::ostringstream& out, + const std::string& graphKey, + const otk::pbrt::PbrtTexture& texture, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& textureStack, + const Emitter& emitter ) +{ + if( contains( textureStack, graphKey ) ) + { + emitter.appendRecursiveTexture( out, graphKey, texture ); + return; + } + + textureStack.push_back( graphKey ); + emitter.appendTexture( out, graphKey, texture ); + appendTextureReferences( out, texture.params, textureTextureParamNames(), graph, textureStack, emitter ); + out << ")"; + textureStack.pop_back(); +} + +template +void appendMaterialGraphSignature( std::ostringstream& out, + const std::string& type, + const ::pbrt::ParamSet& params, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& materialStack, + std::vector& textureStack, + const Emitter& emitter ) +{ + emitter.appendMaterial( out, type, params ); + appendTextureReferences( out, params, materialTextureParamNames(), graph, textureStack, emitter ); + appendMaterialReferences( out, params, graph, materialStack, textureStack, emitter ); + out << ")"; +} + +void appendMaterialSignature( std::ostringstream& out, + const std::string& type, + const ::pbrt::ParamSet& params, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& materialStack, + std::vector& textureStack ) +{ + appendMaterialGraphSignature( out, type, params, graph, materialStack, textureStack, SourceSignatureEmitter{} ); +} + +void appendMaterialInstanceSignature( std::ostringstream& out, + const std::string& type, + const ::pbrt::ParamSet& params, + const otk::pbrt::PbrtMaterialGraph& graph, + std::vector& materialStack, + std::vector& textureStack ) +{ + appendMaterialGraphSignature( out, type, params, graph, materialStack, textureStack, InstanceSignatureEmitter{} ); +} + +} // namespace + +bool operator==( const MdlShaderKey& lhs, const MdlShaderKey& rhs ) +{ + return lhs.signature == rhs.signature; +} + +bool operator!=( const MdlShaderKey& lhs, const MdlShaderKey& rhs ) +{ + return !( lhs == rhs ); +} + +bool operator<( const MdlShaderKey& lhs, const MdlShaderKey& rhs ) +{ + return lhs.signature < rhs.signature; +} + +std::string toString( const MdlShaderKey& key ) +{ + return key.signature; +} + +MdlShaderKey makeMdlShaderKey( const otk::pbrt::PbrtMaterial& material ) +{ + std::ostringstream signature; + std::vector materialStack; + std::vector textureStack; + + signature << "pbrt-mdl-v1"; + if( !material.graph.fallbackReasons.empty() ) + { + signature << "|graph-fallback"; + } + appendMaterialSignature( signature, material.type, material.params, material.graph, materialStack, textureStack ); + + return MdlShaderKey{ signature.str() }; +} + +bool operator==( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ) +{ + return lhs.sourceKey == rhs.sourceKey && lhs.signature == rhs.signature + && lhs.sourceShapeProgramReusable == rhs.sourceShapeProgramReusable; +} + +bool operator!=( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ) +{ + return !( lhs == rhs ); +} + +bool operator<( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ) +{ + if( lhs.sourceKey != rhs.sourceKey ) + { + return lhs.sourceKey < rhs.sourceKey; + } + if( lhs.signature != rhs.signature ) + { + return lhs.signature < rhs.signature; + } + return lhs.sourceShapeProgramReusable < rhs.sourceShapeProgramReusable; +} + +std::string toString( const MdlMaterialInstanceKey& key ) +{ + return "source=" + toString( key.sourceKey ) + "|instance=" + key.signature + + ( key.sourceShapeProgramReusable ? "|source-shape-program=reusable" : "|source-shape-program=instance" ); +} + +MdlMaterialInstanceKey makeMdlMaterialInstanceKey( const otk::pbrt::PbrtMaterial& material ) +{ + MdlMaterialInstanceKey result; + result.sourceKey = makeMdlShaderKey( material ); + result.sourceShapeProgramReusable = true; + + std::ostringstream signature; + std::vector materialStack; + std::vector textureStack; + + signature << "pbrt-mdl-instance-v1"; + for( std::vector::const_iterator it = material.graph.fallbackReasons.begin(); + it != material.graph.fallbackReasons.end(); ++it ) + { + signature << "|graph-fallback=" << *it; + } + appendMaterialInstanceSignature( signature, material.type, material.params, material.graph, materialStack, textureStack ); + + result.signature = signature.str(); + return result; +} + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL + diff --git a/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu b/examples/DemandLoading/DemandPbrtScene/MdlMaterial.cu similarity index 78% rename from examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu rename to examples/DemandLoading/DemandPbrtScene/MdlMaterial.cu index 8bd42198..1783abd2 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu +++ b/examples/DemandLoading/DemandPbrtScene/MdlMaterial.cu @@ -847,185 +847,273 @@ __device__ __forceinline__ bool useMdlShader( const Params& params, uint_t mater return shader.callableCount >= 1U; } -extern "C" __global__ void __closesthit__mdlMesh() +struct MdlHit { - float3 worldNormal; float3 vertices[3]; - getTriangleData( vertices, worldNormal ); + float3 worldNormal; + float3 position; + float3 rayDirection; + float rayT; + uint_t instanceId; + uint_t materialId; +}; + +struct MdlShading +{ + mi::neuraylib::Shading_state_material state; + mi::neuraylib::Resource_data resourceData; + const TriangleUVs* triangleUVs; + float2 uv; + float worldSpaceTextureSize; + mi::neuraylib::tct_float3 textCoords[1]; + mi::neuraylib::tct_float3 tangentU[1]; + mi::neuraylib::tct_float3 tangentV[1]; +}; - if( triMeshMaterialDebugInfo( vertices, worldNormal, optixGetTriangleBarycentrics() ) ) +__device__ __forceinline__ bool loadMdlHit( const Params& params, MdlHit& hit ) +{ + getTriangleData( hit.vertices, hit.worldNormal ); + + if( triMeshMaterialDebugInfo( hit.vertices, hit.worldNormal, optixGetTriangleBarycentrics() ) ) { - return; + return false; } - const Params& params{ PARAMS_VAR_NAME }; - const uint_t instanceId{ optixGetInstanceId() }; - const uint_t materialId{ getMdlMaterialId( params, instanceId ) }; + hit.instanceId = optixGetInstanceId(); + hit.materialId = getMdlMaterialId( params, hit.instanceId ); #ifndef NDEBUG - if( materialId >= params.numRealizedMaterials ) + if( hit.materialId >= params.numRealizedMaterials ) { - printf( "Material id %u exceeds numRealizedMaterials %u\n", materialId, params.numRealizedMaterials ); - assert( materialId < params.numRealizedMaterials ); + printf( "Material id %u exceeds numRealizedMaterials %u\n", hit.materialId, params.numRealizedMaterials ); + assert( hit.materialId < params.numRealizedMaterials ); } #endif - const float3 rayOrigin{ optixGetWorldRayOrigin() }; - const float3 rayDirection{ optixGetWorldRayDirection() }; - const float rayT{ optixGetRayTmax() }; - PhongMaterial material{ params.realizedMaterials[materialId] }; - RayPayload* const prd{ getRayPayload() }; - MdlMaterialShader shader{}; + const float3 rayOrigin{ optixGetWorldRayOrigin() }; + hit.rayDirection = optixGetWorldRayDirection(); + hit.rayT = optixGetRayTmax(); + hit.position = rayOrigin + hit.rayT * hit.rayDirection; + return true; +} + +__device__ __forceinline__ void initializeMdlPayload( RayPayload& prd, const MdlHit& hit ) +{ + prd.diffuseTextureId = INVALID_TEXTURE_ID; + prd.material = nullptr; + prd.normal = hit.worldNormal; + prd.rayDistance = hit.rayT; + prd.hasMdlBsdfSample = false; +} - prd->diffuseTextureId = INVALID_TEXTURE_ID; - prd->material = nullptr; - prd->normal = worldNormal; - prd->rayDistance = rayT; - prd->hasMdlBsdfSample = false; +__device__ __forceinline__ void setMdlDirectColor( RayPayload& prd, const PhongMaterial& material, const float3& normal, + const float3& rayDirection ) +{ + prd.normal = normal; + prd.color = phongShade( material, normal, rayDirection ); + prd.hasDirectColor = true; +} - if( !useMdlShader( params, materialId, shader ) ) +__device__ __forceinline__ void shadeMdlFallback( const Params& params, RayPayload& prd, const PhongMaterial& material, + const MdlHit& hit ) +{ + if( hasAllocatedDiffuseMap( material ) ) { - if( hasAllocatedDiffuseMap( material ) ) - { - setMdlMaterialDiffuseTexturePayload( params, prd, material, worldNormal, vertices, instanceId, rayT ); - return; - } - prd->color = phongShade( material, worldNormal, rayDirection ); - prd->hasDirectColor = true; + setMdlMaterialDiffuseTexturePayload( params, &prd, material, hit.worldNormal, hit.vertices, hit.instanceId, hit.rayT ); return; } + setMdlDirectColor( prd, material, hit.worldNormal, hit.rayDirection ); +} - mi::neuraylib::Shading_state_material state{}; - state.normal = worldNormal; - state.geom_normal = worldNormal; - state.position = rayOrigin + rayT * rayDirection; - - mi::neuraylib::Resource_data resourceData{}; - mi::neuraylib::tct_float3 tint{}; - const bool hasDiffuseTexture{ hasAllocatedDiffuseMap( material ) }; - const bool useMdlDiffuseTexture{ hasMdlDiffuseTexture( shader ) && hasDiffuseTexture }; - const bool hasMaterialTextures{ hasMdlMaterialTextures( shader ) }; - const TriangleUVs* triangleUVs{}; - float2 uv{}; - float worldSpaceTextureSize{}; - mi::neuraylib::tct_float3 textCoords[1]{}; - mi::neuraylib::tct_float3 tangentU[1]{}; - mi::neuraylib::tct_float3 tangentV[1]{}; - tangentU[0] = makeMdlTangentU( worldNormal ); - tangentV[0] = makeMdlTangentV( worldNormal, tangentU[0] ); - state.text_coords = textCoords; - state.tangent_u = tangentU; - state.tangent_v = tangentV; - if( hasMaterialTextures || hasDiffuseTexture ) +__device__ __forceinline__ void initializeMdlShading( MdlShading& shading, const MdlHit& hit ) +{ + shading.state.normal = hit.worldNormal; + shading.state.geom_normal = hit.worldNormal; + shading.state.position = hit.position; + shading.tangentU[0] = makeMdlTangentU( hit.worldNormal ); + shading.tangentV[0] = makeMdlTangentV( hit.worldNormal, shading.tangentU[0] ); + shading.state.text_coords = shading.textCoords; + shading.state.tangent_u = shading.tangentU; + shading.state.tangent_v = shading.tangentV; +} + +__device__ __forceinline__ void prepareMdlTextureCoordinates( const Params& params, const MdlMaterialShader& shader, + const PhongMaterial& material, const MdlHit& hit, + MdlShading& shading ) +{ + if( !hasMdlMaterialTextures( shader ) && !hasAllocatedDiffuseMap( material ) ) { -#ifndef NDEBUG - if( instanceId >= params.numInstanceUVs ) - { - printf( "Instance id %u exceeds numInstanceUVs %u\n", instanceId, params.numInstanceUVs ); - assert( instanceId < params.numInstanceUVs ); - } -#endif - triangleUVs = &getMdlTriangleUVArray( params.instanceUVs, instanceId )[optixGetPrimitiveIndex()]; - uv = interpolateMdlUVs( *triangleUVs ); - worldSpaceTextureSize = getWorldSpaceTextureSize( vertices, *triangleUVs ); - textCoords[0] = make_float3( uv.x, uv.y, 0.0f ); + return; } - if( params.renderMode == RenderMode::PATH_TRACING || !hasDiffuseTexture ) + +#ifndef NDEBUG + if( hit.instanceId >= params.numInstanceUVs ) { - optixDirectCall( - shader.callableBaseIndex, &tint, &state, &resourceData, - reinterpret_cast( shader.tintArgumentBlock ) ); - material.Kd = make_float3( tint.x, tint.y, tint.z ); + printf( "Instance id %u exceeds numInstanceUVs %u\n", hit.instanceId, params.numInstanceUVs ); + assert( hit.instanceId < params.numInstanceUVs ); } +#endif + shading.triangleUVs = &getMdlTriangleUVArray( params.instanceUVs, hit.instanceId )[optixGetPrimitiveIndex()]; + shading.uv = interpolateMdlUVs( *shading.triangleUVs ); + shading.worldSpaceTextureSize = getWorldSpaceTextureSize( hit.vertices, *shading.triangleUVs ); + shading.textCoords[0] = make_float3( shading.uv.x, shading.uv.y, 0.0f ); +} - prd->materialCopy = material; - prd->color = phongShade( material, worldNormal, rayDirection ); - prd->hasDirectColor = true; - - MdlMaterialTextureSamples textureSamples{}; - uint_t nonResidentTextureId{}; - if( !sampleMdlMaterialTextures( shader, uv, textureSamples, nonResidentTextureId ) ) +__device__ __forceinline__ void applyMdlTint( const Params& params, const MdlMaterialShader& shader, + MdlShading& shading, PhongMaterial& material ) +{ + if( params.renderMode != RenderMode::PATH_TRACING && hasAllocatedDiffuseMap( material ) ) { - setMdlDiffuseTexturePayload( prd, material, worldNormal, rayT, nonResidentTextureId, uv, worldSpaceTextureSize ); return; } - float3 shadingNormal{ worldNormal }; - if( hasMdlBumpMapTexture( shader ) ) + mi::neuraylib::tct_float3 tint{}; + optixDirectCall( + shader.callableBaseIndex, &tint, &shading.state, &shading.resourceData, + reinterpret_cast( shader.tintArgumentBlock ) ); + material.Kd = make_float3( tint.x, tint.y, tint.z ); +} + +__device__ __forceinline__ void setMdlTextureRequest( RayPayload& prd, const PhongMaterial& material, const MdlHit& hit, + const MdlShading& shading, uint_t textureId ) +{ + setMdlDiffuseTexturePayload( &prd, material, hit.worldNormal, hit.rayT, textureId, shading.uv, + shading.worldSpaceTextureSize ); +} + +__device__ __forceinline__ bool applyMdlBumpMapping( const Params& params, const MdlMaterialShader& shader, + const MdlHit& hit, const RayPayload& prd, MdlShading& shading, + uint_t& nonResidentTextureId ) +{ + if( !hasMdlBumpMapTexture( shader ) ) { + return true; + } + #ifndef NDEBUG - assert( triangleUVs != nullptr ); + assert( shading.triangleUVs != nullptr ); #endif - float3 worldVertices[3]; - float3 worldNormals[3] = { worldNormal, worldNormal, worldNormal }; - float2 adjustedUVs[3]; + float3 worldVertices[3]; + float3 worldNormals[3] = { hit.worldNormal, hit.worldNormal, hit.worldNormal }; + float2 adjustedUVs[3]; + for( uint_t i = 0; i < 3U; ++i ) + { + worldVertices[i] = optixTransformPointFromObjectToWorldSpace( hit.vertices[i] ); + adjustedUVs[i] = adjustMdlUV( shading.triangleUVs->UV[i] ); + } + if( params.instanceNormals != nullptr && params.instanceNormals[hit.instanceId] != nullptr ) + { + const TriangleNormals& normals{ params.instanceNormals[hit.instanceId][optixGetPrimitiveIndex()] }; for( uint_t i = 0; i < 3U; ++i ) { - worldVertices[i] = optixTransformPointFromObjectToWorldSpace( vertices[i] ); - adjustedUVs[i] = adjustMdlUV( triangleUVs->UV[i] ); - } - if( params.instanceNormals != nullptr && params.instanceNormals[instanceId] != nullptr ) - { - const TriangleNormals& normals{ params.instanceNormals[instanceId][optixGetPrimitiveIndex()] }; - for( uint_t i = 0; i < 3U; ++i ) + worldNormals[i] = otk::normalize( optixTransformNormalFromObjectToWorldSpace( normals.N[i] ) ); + if( params.useFaceForward && optixIsBackFaceHit() ) { - worldNormals[i] = otk::normalize( optixTransformNormalFromObjectToWorldSpace( normals.N[i] ) ); - if( params.useFaceForward && optixIsBackFaceHit() ) - { - worldNormals[i] = -worldNormals[i]; - } + worldNormals[i] = -worldNormals[i]; } } + } - const MdlBumpDifferentialGeometry geometry{ makeMdlBumpDifferentialGeometry( worldVertices, adjustedUVs, worldNormals ) }; - float3 dPdx{}; - float3 dPdy{}; - const float rayConeWidth{ fabsf( prd->mdlRayConeWidth + prd->mdlRayConeAngle * rayT ) }; - projectToRayDifferentialsOnSurface( rayConeWidth, rayDirection, worldNormal, dPdx, dPdy ); - float2 ddx{}; - float2 ddy{}; - computeTexGradientsForTriangle( worldVertices[0], worldVertices[1], worldVertices[2], adjustedUVs[0], adjustedUVs[1], - adjustedUVs[2], dPdx, dPdy, ddx, ddy ); - const float du{ mdlBumpOffset( ddx.x, ddy.x ) }; - const float dv{ mdlBumpOffset( ddx.y, ddy.y ) }; - MdlBumpMapSamples bumpSamples{}; - if( !sampleMdlBumpMap( shader, uv, ddx, ddy, du, dv, bumpSamples, nonResidentTextureId ) ) - { - setMdlDiffuseTexturePayload( prd, material, worldNormal, rayT, nonResidentTextureId, uv, worldSpaceTextureSize ); - return; - } - const MdlBumpDifferentialGeometry bumpedGeometry{ - applyMdlBumpMap( geometry, worldNormal, bumpSamples.height, bumpSamples.heightU, bumpSamples.heightV, du, dv ) }; - shadingNormal = mdlBumpNormal( bumpedGeometry, worldNormal ); - tangentU[0] = otk::normalize( bumpedGeometry.dpdu ); - tangentV[0] = otk::normalize( bumpedGeometry.dpdv ); - } - state.normal = shadingNormal; - prd->normal = shadingNormal; - prd->color = phongShade( material, shadingNormal, rayDirection ); - - if( hasMdlBsdfCallables( shader ) && ( !hasDiffuseTexture || useMdlDiffuseTexture ) ) - { - alignas( 16 ) char bsdfArgumentBlockStorage[MDL_MATERIAL_ARGUMENT_BLOCK_STACK_SIZE]; - const char* const bsdfArgumentBlock{ makeMdlBsdfArgumentBlock( shader, textureSamples, bsdfArgumentBlockStorage ) }; - initializeMdlBsdf( shader, state, resourceData, bsdfArgumentBlock ); - prd->color = displayEncodeMdlColor( shadeMdlBsdf( shader, state, resourceData, shadingNormal, rayDirection, - textureSamples, - make_float2( prd->mdlBsdfSampleXi.z, prd->mdlBsdfSampleXi.w ), - bsdfArgumentBlock ) ); - prd->hasDirectColor = true; - prd->hasMdlBsdfSample = - PARAMS_VAR_NAME.renderMode == RenderMode::PATH_TRACING - && sampleMdlBsdf( shader, state, resourceData, -rayDirection, prd->mdlBsdfSampleXi, textureSamples, - bsdfArgumentBlock, prd->mdlBsdfSampleDirection, prd->mdlBsdfSampleThroughput ); + const MdlBumpDifferentialGeometry geometry{ makeMdlBumpDifferentialGeometry( worldVertices, adjustedUVs, worldNormals ) }; + float3 dPdx{}; + float3 dPdy{}; + const float rayConeWidth{ fabsf( prd.mdlRayConeWidth + prd.mdlRayConeAngle * hit.rayT ) }; + projectToRayDifferentialsOnSurface( rayConeWidth, hit.rayDirection, hit.worldNormal, dPdx, dPdy ); + float2 ddx{}; + float2 ddy{}; + computeTexGradientsForTriangle( worldVertices[0], worldVertices[1], worldVertices[2], adjustedUVs[0], adjustedUVs[1], + adjustedUVs[2], dPdx, dPdy, ddx, ddy ); + const float du{ mdlBumpOffset( ddx.x, ddy.x ) }; + const float dv{ mdlBumpOffset( ddx.y, ddy.y ) }; + MdlBumpMapSamples bumpSamples{}; + if( !sampleMdlBumpMap( shader, shading.uv, ddx, ddy, du, dv, bumpSamples, nonResidentTextureId ) ) + { + return false; + } + + const MdlBumpDifferentialGeometry bumpedGeometry{ + applyMdlBumpMap( geometry, hit.worldNormal, bumpSamples.height, bumpSamples.heightU, bumpSamples.heightV, du, dv ) }; + shading.state.normal = mdlBumpNormal( bumpedGeometry, hit.worldNormal ); + shading.tangentU[0] = otk::normalize( bumpedGeometry.dpdu ); + shading.tangentV[0] = otk::normalize( bumpedGeometry.dpdv ); + return true; +} + +__device__ __forceinline__ bool shadeWithMdl( const Params& params, const MdlMaterialShader& shader, + const PhongMaterial& material, const MdlHit& hit, + const MdlMaterialTextureSamples& textureSamples, MdlShading& shading, + RayPayload& prd ) +{ + if( !hasMdlBsdfCallables( shader ) || ( hasAllocatedDiffuseMap( material ) && !hasMdlDiffuseTexture( shader ) ) ) + { + return false; + } + + alignas( 16 ) char bsdfArgumentBlockStorage[MDL_MATERIAL_ARGUMENT_BLOCK_STACK_SIZE]; + const char* const bsdfArgumentBlock{ makeMdlBsdfArgumentBlock( shader, textureSamples, bsdfArgumentBlockStorage ) }; + initializeMdlBsdf( shader, shading.state, shading.resourceData, bsdfArgumentBlock ); + prd.color = displayEncodeMdlColor( shadeMdlBsdf( + shader, shading.state, shading.resourceData, shading.state.normal, hit.rayDirection, textureSamples, + make_float2( prd.mdlBsdfSampleXi.z, prd.mdlBsdfSampleXi.w ), bsdfArgumentBlock ) ); + prd.hasDirectColor = true; + prd.hasMdlBsdfSample = + params.renderMode == RenderMode::PATH_TRACING + && sampleMdlBsdf( shader, shading.state, shading.resourceData, -hit.rayDirection, prd.mdlBsdfSampleXi, + textureSamples, bsdfArgumentBlock, prd.mdlBsdfSampleDirection, prd.mdlBsdfSampleThroughput ); + return true; +} + +extern "C" __global__ void __closesthit__mdlMesh() +{ + const Params& params{ PARAMS_VAR_NAME }; + MdlHit hit{}; + if( !loadMdlHit( params, hit ) ) + { + return; + } + + PhongMaterial material{ params.realizedMaterials[hit.materialId] }; + RayPayload& prd{ *getRayPayload() }; + initializeMdlPayload( prd, hit ); + + MdlMaterialShader shader{}; + if( !useMdlShader( params, hit.materialId, shader ) ) + { + shadeMdlFallback( params, prd, material, hit ); + return; + } + + MdlShading shading{}; + initializeMdlShading( shading, hit ); + prepareMdlTextureCoordinates( params, shader, material, hit, shading ); + applyMdlTint( params, shader, shading, material ); + prd.materialCopy = material; + setMdlDirectColor( prd, material, hit.worldNormal, hit.rayDirection ); + + MdlMaterialTextureSamples textureSamples{}; + uint_t nonResidentTextureId{}; + if( !sampleMdlMaterialTextures( shader, shading.uv, textureSamples, nonResidentTextureId ) ) + { + setMdlTextureRequest( prd, material, hit, shading, nonResidentTextureId ); + return; + } + if( !applyMdlBumpMapping( params, shader, hit, prd, shading, nonResidentTextureId ) ) + { + setMdlTextureRequest( prd, material, hit, shading, nonResidentTextureId ); return; } - if( !hasAllocatedDiffuseMap( material ) ) + setMdlDirectColor( prd, material, shading.state.normal, hit.rayDirection ); + if( shadeWithMdl( params, shader, material, hit, textureSamples, shading, prd ) ) { return; } - setMdlMaterialDiffuseTexturePayload( params, prd, material, shadingNormal, vertices, instanceId, rayT ); + if( hasAllocatedDiffuseMap( material ) ) + { + setMdlMaterialDiffuseTexturePayload( params, &prd, material, shading.state.normal, hit.vertices, hit.instanceId, + hit.rayT ); + } } } // namespace demandPbrtScene + diff --git a/examples/DemandLoading/DemandPbrtScene/MdlMaterialModelBuilder.cpp b/examples/DemandLoading/DemandPbrtScene/MdlMaterialModelBuilder.cpp new file mode 100644 index 00000000..595d14e2 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/MdlMaterialModelBuilder.cpp @@ -0,0 +1,1255 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "DemandPbrtScene/MdlMaterialModelBuilder.h" + +#ifdef OTK_USE_MDL + +#include "DemandPbrtScene/MdlKeyBuilder.h" +#include "DemandPbrtScene/MdlParameterBinder.h" +#include "DemandPbrtScene/MdlTextureGraphGenerator.h" +#include "DemandPbrtScene/PbrtMaterialKind.h" + +#include +#include +#include +#include +#include +#include + +namespace demandPbrtScene { + +void appendUnsupportedReason( GeneratedMdlSource& result, const std::string& reason ) +{ + if( std::find( result.unsupportedReasons.begin(), result.unsupportedReasons.end(), reason ) + == result.unsupportedReasons.end() ) + { + result.unsupportedReasons.push_back( reason ); + } +} + +namespace { + +std::string stableHash( const std::string& text ) +{ + std::uint64_t hash{ 14695981039346656037ULL }; + for( std::string::const_iterator it = text.begin(); it != text.end(); ++it ) + { + hash ^= static_cast( *it ); + hash *= 1099511628211ULL; + } + + std::ostringstream out; + out << std::hex << std::setfill( '0' ) << std::setw( 16 ) << hash; + return out.str(); +} + +struct MdlMaterialParameter +{ + std::string type; + std::string name; + std::string defaultValue; +}; + +struct MdlMaterialModel +{ + std::vector parameters; + std::vector comments; + std::string helperDefinitions; + std::string body; +}; + +struct PbrtMaterialGapPolicy +{ + PbrtMaterialKind kind; + std::string policy; + std::string coverageReason; +}; + +void appendMaterialParameter( MdlMaterialModel& model, const std::string& type, const std::string& name, const std::string& defaultValue ) +{ + model.parameters.push_back( MdlMaterialParameter{ type, name, defaultValue } ); +} + +const PbrtMaterialGapPolicy* explicitMaterialGapPolicy( PbrtMaterialKind kind ) +{ + static const PbrtMaterialGapPolicy policies[] = { + { PbrtMaterialKind::FOURIER, "unsupported with visible fallback", + "PBRT Fourier tables are data-driven BSDF resources found in the corpus; DemandPbrtScene preserves the " + "resource metadata but does not yet evaluate the Fourier table on the GPU" }, + { PbrtMaterialKind::HAIR, "unsupported with visible fallback", + "low-frequency PBRT corpus material; no current target scene or reference fixture requires approximation" }, + { PbrtMaterialKind::MEASURED, "unsupported with visible fallback", + "PBRT parity completeness gap; current corpus sample did not find a target scene requiring support" }, + }; + + for( const PbrtMaterialGapPolicy& policy : policies ) + { + if( policy.kind == kind ) + { + return &policy; + } + } + return nullptr; +} + +bool hasFourierBsdfFile( const otk::pbrt::PbrtMaterial& material ) +{ + return !material.params.FindOneString( "bsdffile", std::string{} ).empty(); +} + +void appendRoughnessGapComment( MdlMaterialModel& model ) +{ + model.comments.push_back( "pbrt material gap: PBRT-exact roughness/remapping behavior is approximated" ); +} + +std::string mdlParameterList( const std::vector& parameters ) +{ + if( parameters.empty() ) + { + return "()"; + } + + std::ostringstream out; + out << "(\n"; + for( std::vector::const_iterator it = parameters.begin(); it != parameters.end(); ++it ) + { + out << " " << it->type << " " << it->name << " = " << it->defaultValue; + if( it + 1 != parameters.end() ) + { + out << ","; + } + out << "\n"; + } + out << ")"; + return out.str(); +} + +std::string materialTextureCommentExpression( MdlTextureGraphGenerator& textureGraph, + const ::pbrt::ParamSet& params, + const std::string& paramName, + const std::string& preferredValueType ) +{ + if( params.FindTexture( paramName ).empty() ) + { + return "none"; + } + return textureGraph.materialColorExpression( params, paramName, preferredValueType, "none" ); +} + +std::string materialBumpmapExpression( MdlTextureGraphGenerator& textureGraph, const ::pbrt::ParamSet& params ) +{ + if( params.FindTexture( "bumpmap" ).empty() ) + { + return "none"; + } + return textureGraph.materialFloatExpression( params, "bumpmap", "float", "0.0" ); +} + +bool hasBumpmapExpression( const std::string& bumpmap ) +{ + return bumpmap != "none"; +} + +void appendBumpmapCommentsAndHelpers( MdlMaterialModel& model, const std::string& bumpmap ) +{ + model.comments.push_back( "pbrt material input bumpmap: " + bumpmap ); + if( !hasBumpmapExpression( bumpmap ) ) + { + return; + } + + model.comments.push_back( "pbrt material implementation: bumpmap is evaluated with runtime finite differences" ); +} + +std::string materialGeometryExpression( const std::string& cutoutOpacity, const std::string& bumpmap ) +{ + (void)bumpmap; + if( cutoutOpacity.empty() ) + { + return std::string{}; + } + + std::ostringstream out; + out << " geometry: material_geometry(\n"; + out << " cutout_opacity: " << cutoutOpacity << "\n"; + out << " )\n"; + return out.str(); +} + +std::string namedMaterialColorExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index, + const std::string& paramName, + const std::string& defaultValue ) +{ + const std::string parameterName{ namedMaterialParameterName( index, paramName ) }; + appendMaterialParameter( model, "color", parameterName, defaultValue ); + return textureGraph.materialColorExpression( material.params, paramName, "color", parameterName ); +} + +std::string namedMaterialFloatExpression( MdlMaterialModel& model, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index, + const std::string& paramName, + const std::string& defaultValue ) +{ + const std::string parameterName{ namedMaterialParameterName( index, paramName ) }; + appendMaterialParameter( model, "float", parameterName, defaultValue ); + if( !material.params.FindTexture( paramName ).empty() ) + { + return defaultValue; + } + return parameterName; +} + +std::string namedMaterialMatteBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string kd{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; + const std::string sigma{ namedMaterialFloatExpression( model, material, index, "sigma", "0.0" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input sigma: " + sigma ); + return "::df::diffuse_reflection_bsdf(\n" + " tint: " + + kd + + ",\n" + " roughness: pbrt_mix_matte_sigma_roughness(" + + sigma + "))"; +} + +std::string namedMaterialPlasticBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string kd{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; + const std::string ks{ + namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.0, 0.0, 0.0)" ) }; + const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); + return "::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: " + + kd + + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + ks + + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: " + + roughness + + ",\n" + " roughness_v: " + + roughness + + ",\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect))))"; +} + +std::string namedMaterialSubstrateBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string kd{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.5, 0.5, 0.5)" ) }; + const std::string ks{ + namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.5, 0.5, 0.5)" ) }; + const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; + const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "-1.0" ) }; + const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "-1.0" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); + return "::df::color_weighted_layer(\n" + " weight: " + + ks + + ",\n" + " layer: ::df::simple_glossy_bsdf(\n" + " roughness_u: pbrt_mix_resolved_roughness(" + + roughness + ", " + uroughness + + "),\n" + " roughness_v: pbrt_mix_resolved_roughness(" + + roughness + ", " + vroughness + + "),\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect),\n" + " base: ::df::diffuse_reflection_bsdf(\n" + " tint: " + + kd + "))"; +} + +std::string namedMaterialUberBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string kd{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; + const std::string ks{ + namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.0, 0.0, 0.0)" ) }; + const std::string kr{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kr", "color(0.0, 0.0, 0.0)" ) }; + const std::string kt{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kt", "color(0.0, 0.0, 0.0)" ) }; + const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; + const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "-1.0" ) }; + const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "-1.0" ) }; + const std::string alpha{ namedMaterialFloatExpression( model, material, index, "alpha", "1.0" ) }; + const std::string opacity{ namedMaterialFloatExpression( model, material, index, "opacity", "1.0" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kr: " + kr ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kt: " + kt ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input opacity: " + opacity ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input alpha: " + alpha + + "; cutout does not compose through mix" ); + return std::string{ "::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: " } + + "pbrt_mix_opacity_weight(" + opacity + ") * " + kd + + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + "pbrt_mix_opacity_weight(" + opacity + ") * " + ks + + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: pbrt_mix_resolved_roughness(" + + roughness + ", " + uroughness + + "),\n" + " roughness_v: pbrt_mix_resolved_roughness(" + + roughness + ", " + vroughness + + "),\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect)),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + "pbrt_mix_opacity_weight(" + opacity + ") * " + kr + + ",\n" + " component: ::df::specular_bsdf(\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect)),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + "pbrt_mix_opacity_weight(" + opacity + ") * " + kt + + ",\n" + " component: ::df::specular_bsdf(\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_transmit)),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_mix_transparency_weight(" + + opacity + + "),\n" + " component: ::df::specular_bsdf(\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_transmit))))"; +} + +std::string namedMaterialMirrorBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string kr{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kr", "color(1.0, 1.0, 1.0)" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kr: " + kr ); + return "::df::specular_bsdf(\n" + " tint: " + + kr + + ",\n" + " mode: ::df::scatter_reflect)"; +} + +std::string namedMaterialGlassBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string kr{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kr", "color(1.0, 1.0, 1.0)" ) }; + const std::string kt{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kt", "color(1.0, 1.0, 1.0)" ) }; + const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.0" ) }; + const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "0.0" ) }; + const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "0.0" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kr: " + kr ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kt: " + kt ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); + return "::df::tint(\n" + " " + + kr + + ",\n" + " " + + kt + + ",\n" + " ::df::microfacet_ggx_smith_bsdf(\n" + " roughness_u: pbrt_mix_resolved_roughness(" + + roughness + ", " + uroughness + + "),\n" + " roughness_v: pbrt_mix_resolved_roughness(" + + roughness + ", " + vroughness + + "),\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect_transmit))"; +} + +std::string namedMaterialMetalBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string eta{ + namedMaterialColorExpression( model, textureGraph, material, index, "eta", "color(0.2, 0.2, 0.2)" ) }; + const std::string k{ + namedMaterialColorExpression( model, textureGraph, material, index, "k", "color(3.0, 3.0, 3.0)" ) }; + const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; + const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "-1.0" ) }; + const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "-1.0" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input eta: " + eta ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input k: " + k ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); + return "::df::microfacet_ggx_smith_bsdf(\n" + " roughness_u: pbrt_mix_resolved_roughness(" + + roughness + ", " + uroughness + + "),\n" + " roughness_v: pbrt_mix_resolved_roughness(" + + roughness + ", " + vroughness + + "),\n" + " tint: pbrt_mix_metal_conductor_tint(" + + eta + ", " + k + + "),\n" + " mode: ::df::scatter_reflect)"; +} + +std::string namedMaterialTranslucentBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string kd{ + namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; + const std::string ks{ + namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.0, 0.0, 0.0)" ) }; + const std::string reflect{ + namedMaterialColorExpression( model, textureGraph, material, index, "reflect", "color(0.5, 0.5, 0.5)" ) }; + const std::string transmit{ + namedMaterialColorExpression( model, textureGraph, material, index, "transmit", "color(0.5, 0.5, 0.5)" ) }; + const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input reflect: " + reflect ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input transmit: " + transmit ); + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); + return "::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: " + + kd + " * " + reflect + + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + kd + " * " + transmit + + ",\n" + " component: ::df::diffuse_transmission_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + ks + " * " + reflect + + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: " + + roughness + + ",\n" + " roughness_v: " + + roughness + + ",\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect)),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + ks + " * " + transmit + + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: " + + roughness + + ",\n" + " roughness_v: " + + roughness + + ",\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_transmit))))"; +} + +std::string unsupportedNamedMaterialBsdfExpression() +{ + return "::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 0.0, 1.0))"; +} + +std::string namedMaterialBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + GeneratedMdlSource& result, + const otk::pbrt::PbrtNamedMaterial& material, + unsigned int index ) +{ + const std::string type{ namedMaterialType( material ) }; + const PbrtMaterialKind kind{ pbrtMaterialKind( type ) }; + const std::string typeComment{ type.empty() ? std::string{ "" } : type }; + model.comments.push_back( "pbrt named material " + std::to_string( index ) + " model: " + typeComment ); + + switch( kind ) + { + case PbrtMaterialKind::MATTE: + return namedMaterialMatteBsdfExpression( model, textureGraph, material, index ); + case PbrtMaterialKind::PLASTIC: + return namedMaterialPlasticBsdfExpression( model, textureGraph, material, index ); + case PbrtMaterialKind::SUBSTRATE: + return namedMaterialSubstrateBsdfExpression( model, textureGraph, material, index ); + case PbrtMaterialKind::UBER: + return namedMaterialUberBsdfExpression( model, textureGraph, material, index ); + case PbrtMaterialKind::MIRROR: + return namedMaterialMirrorBsdfExpression( model, textureGraph, material, index ); + case PbrtMaterialKind::GLASS: + return namedMaterialGlassBsdfExpression( model, textureGraph, material, index ); + case PbrtMaterialKind::METAL: + return namedMaterialMetalBsdfExpression( model, textureGraph, material, index ); + case PbrtMaterialKind::TRANSLUCENT: + return namedMaterialTranslucentBsdfExpression( model, textureGraph, material, index ); + default: + appendUnsupportedReason( result, "Unsupported PBRT named material type " + typeComment ); + return unsupportedNamedMaterialBsdfExpression(); + } +} + +MdlMaterialModel makeMatteMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); + appendMaterialParameter( model, "float", "sigma", "0.0" ); + appendMaterialParameter( model, "float", "alpha", "1.0" ); + appendMaterialParameter( model, "float", "opacity", "1.0" ); + + const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; + const std::string alphaTexture{ + materialTextureCommentExpression( textureGraph, material.params, "alpha", "float" ) }; + const std::string shadowAlphaTexture{ + materialTextureCommentExpression( textureGraph, material.params, "shadowalpha", "float" ) }; + const std::string opacityTexture{ + materialTextureCommentExpression( textureGraph, material.params, "opacity", "float" ) }; + const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; + + model.comments.push_back( "pbrt material model: matte" ); + model.comments.push_back( "pbrt material input Kd: " + kd ); + model.comments.push_back( "pbrt material input sigma: sigma" ); + model.comments.push_back( "pbrt material approximation: sigma degrees map to MDL Oren-Nayar roughness sigma / 90" ); + model.comments.push_back( "pbrt material input alpha: alpha; texture=" + alphaTexture ); + model.comments.push_back( "pbrt material input shadowalpha: any-hit texture=" + shadowAlphaTexture ); + model.comments.push_back( "pbrt material input opacity: opacity; texture=" + opacityTexture ); + appendBumpmapCommentsAndHelpers( model, bumpmap ); + model.helperDefinitions = + "float pbrt_matte_sigma_roughness(float sigma_degrees) = ::math::clamp(sigma_degrees / 90.0, 0.0, 1.0);\n\n" + + model.helperDefinitions; + model.body = + " surface: material_surface(\n" + " scattering: ::df::diffuse_reflection_bsdf(\n" + " tint: " + + kd + ",\n" + " roughness: pbrt_matte_sigma_roughness(sigma))),\n" + + materialGeometryExpression( "alpha * opacity", bumpmap ); + return model; +} + +MdlMaterialModel makePlasticMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); + appendMaterialParameter( model, "color", "Ks", "color(0.0, 0.0, 0.0)" ); + appendMaterialParameter( model, "float", "roughness", "0.1" ); + + const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; + const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; + const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; + + model.comments.push_back( "pbrt material model: plastic" ); + model.comments.push_back( "pbrt material input Kd: " + kd ); + model.comments.push_back( "pbrt material input Ks: " + ks ); + model.comments.push_back( "pbrt material input roughness: roughness" ); + appendBumpmapCommentsAndHelpers( model, bumpmap ); + appendRoughnessGapComment( model ); + model.comments.push_back( + "pbrt material approximation: diffuse and glossy reflection use an MDL color-normalized mix" ); + model.body = + " surface: material_surface(\n" + " scattering: ::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: " + kd + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + ks + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: roughness,\n" + " roughness_v: roughness,\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect)))))" + + "\n"; + return model; +} + +MdlMaterialModel makeUberMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); + appendMaterialParameter( model, "color", "Ks", "color(0.0, 0.0, 0.0)" ); + appendMaterialParameter( model, "color", "Kr", "color(0.0, 0.0, 0.0)" ); + appendMaterialParameter( model, "color", "Kt", "color(0.0, 0.0, 0.0)" ); + appendMaterialParameter( model, "float", "roughness", "0.1" ); + appendMaterialParameter( model, "float", "uroughness", "-1.0" ); + appendMaterialParameter( model, "float", "vroughness", "-1.0" ); + appendMaterialParameter( model, "float", "index", "1.5" ); + appendMaterialParameter( model, "float", "alpha", "1.0" ); + appendMaterialParameter( model, "color", "opacity", "color(1.0, 1.0, 1.0)" ); + + const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; + const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; + const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; + const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; + const std::string alphaTexture{ + materialTextureCommentExpression( textureGraph, material.params, "alpha", "float" ) }; + const std::string opacityTexture{ + materialTextureCommentExpression( textureGraph, material.params, "opacity", "float" ) }; + const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; + + model.comments.push_back( "pbrt material model: uber" ); + model.comments.push_back( "pbrt material input Kd: " + kd ); + model.comments.push_back( "pbrt material input Ks: " + ks ); + model.comments.push_back( "pbrt material input Kr: " + kr ); + model.comments.push_back( "pbrt material input Kt: " + kt ); + model.comments.push_back( "pbrt material input roughness: roughness" ); + model.comments.push_back( "pbrt material input uroughness: uroughness" ); + model.comments.push_back( "pbrt material input vroughness: vroughness" ); + model.comments.push_back( "pbrt material input index: index" ); + model.comments.push_back( "pbrt material input alpha: alpha; texture=" + alphaTexture ); + model.comments.push_back( "pbrt material input opacity: opacity; texture=" + opacityTexture ); + appendBumpmapCommentsAndHelpers( model, bumpmap ); + appendRoughnessGapComment( model ); + model.comments.push_back( "pbrt material approximation: PBRT uber lobes use an MDL color-normalized mix" ); + model.comments.push_back( + "pbrt material approximation: spectrum opacity weights BSDF lobes and adds transparent transmission; alpha " + "remains " + "cutout" ); + model.helperDefinitions = + "float pbrt_uber_resolved_roughness(float roughness, float axis_roughness) = " + "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" + "color pbrt_uber_clamped_opacity(color opacity) = " + "::math::clamp(opacity, color(0.0, 0.0, 0.0), color(1.0, 1.0, 1.0));\n\n" + "color pbrt_uber_opacity_weight(color opacity) = pbrt_uber_clamped_opacity(opacity);\n\n" + "color pbrt_uber_transparency_weight(color opacity) = " + "color(1.0, 1.0, 1.0) - pbrt_uber_clamped_opacity(opacity);\n\n" + + model.helperDefinitions; + model.body = std::string{ " ior: color(index, index, index),\n" + " surface: material_surface(\n" + " scattering: ::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: " } + + "pbrt_uber_opacity_weight(opacity) * " + kd + + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + + "pbrt_uber_opacity_weight(opacity) * " + ks + + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: pbrt_uber_resolved_roughness(roughness, uroughness),\n" + " roughness_v: pbrt_uber_resolved_roughness(roughness, vroughness),\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect)),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_uber_opacity_weight(opacity) * " + + kr + + ",\n" + " component: ::df::specular_bsdf(\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect)),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_uber_opacity_weight(opacity) * " + + kt + + ",\n" + " component: ::df::specular_bsdf(\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_transmit)),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_uber_transparency_weight(opacity),\n" + " component: ::df::specular_bsdf(\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_transmit))))),\n" + + materialGeometryExpression( "alpha", bumpmap ); + return model; +} + +MdlMaterialModel makeMirrorMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); + + const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; + + model.comments.push_back( "pbrt material model: mirror" ); + model.comments.push_back( "pbrt material input Kr: " + kr ); + model.body = + " surface: material_surface(\n" + " scattering: ::df::specular_bsdf(\n" + " tint: " + kr + ",\n" + " mode: ::df::scatter_reflect))\n"; + return model; +} + +MdlMaterialModel makeGlassMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); + appendMaterialParameter( model, "color", "Kt", "color(1.0, 1.0, 1.0)" ); + appendMaterialParameter( model, "float", "index", "1.5" ); + appendMaterialParameter( model, "float", "roughness", "0.0" ); + appendMaterialParameter( model, "float", "uroughness", "0.0" ); + appendMaterialParameter( model, "float", "vroughness", "0.0" ); + + const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; + const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; + + model.comments.push_back( "pbrt material model: glass" ); + model.comments.push_back( "pbrt material input Kr: " + kr ); + model.comments.push_back( "pbrt material input Kt: " + kt ); + model.comments.push_back( "pbrt material input index/eta: index" ); + model.comments.push_back( "pbrt material input roughness: roughness" ); + model.comments.push_back( "pbrt material input uroughness: uroughness" ); + model.comments.push_back( "pbrt material input vroughness: vroughness" ); + model.comments.push_back( "pbrt material approximation: rough glass uses an MDL GGX microfacet dielectric lobe" ); + appendRoughnessGapComment( model ); + model.helperDefinitions = + "float pbrt_glass_resolved_roughness(float roughness, float axis_roughness) = " + "axis_roughness > 0.0 ? axis_roughness : roughness;\n\n"; + model.body = + " ior: color(index, index, index),\n" + " surface: material_surface(\n" + " scattering: ::df::tint(\n" + " " + + kr + ",\n" + " " + kt + + ",\n" + " ::df::microfacet_ggx_smith_bsdf(\n" + " roughness_u: pbrt_glass_resolved_roughness(roughness, uroughness),\n" + " roughness_v: pbrt_glass_resolved_roughness(roughness, vroughness),\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect_transmit)))\n"; + return model; +} + +MdlMaterialModel makeMetalMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "eta", "color(0.2, 0.2, 0.2)" ); + appendMaterialParameter( model, "color", "k", "color(3.0, 3.0, 3.0)" ); + appendMaterialParameter( model, "float", "roughness", "0.1" ); + appendMaterialParameter( model, "float", "uroughness", "-1.0" ); + appendMaterialParameter( model, "float", "vroughness", "-1.0" ); + + const std::string eta{ textureGraph.materialColorExpression( material.params, "eta", "color", "eta" ) }; + const std::string k{ textureGraph.materialColorExpression( material.params, "k", "color", "k" ) }; + + model.comments.push_back( "pbrt material model: metal" ); + model.comments.push_back( "pbrt material input eta: " + eta ); + model.comments.push_back( "pbrt material input k: " + k ); + model.comments.push_back( "pbrt material input roughness: roughness" ); + model.comments.push_back( "pbrt material input uroughness: uroughness" ); + model.comments.push_back( "pbrt material input vroughness: vroughness" ); + model.comments.push_back( "pbrt material gap: PBRT-exact spectral conductor behavior is approximated" ); + appendRoughnessGapComment( model ); + model.comments.push_back( + "pbrt material approximation: RGB eta/k maps to MDL microfacet tint using normal-incidence conductor " + "reflectance" ); + model.helperDefinitions = + "float pbrt_metal_resolved_roughness(float roughness, float axis_roughness) = " + "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" + "color pbrt_metal_conductor_tint(color eta, color k) =\n" + " ((eta - color(1.0, 1.0, 1.0)) * (eta - color(1.0, 1.0, 1.0)) + k * k) /\n" + " ((eta + color(1.0, 1.0, 1.0)) * (eta + color(1.0, 1.0, 1.0)) + k * k);\n\n"; + model.body = + " surface: material_surface(\n" + " scattering: ::df::microfacet_ggx_smith_bsdf(\n" + " roughness_u: pbrt_metal_resolved_roughness(roughness, uroughness),\n" + " roughness_v: pbrt_metal_resolved_roughness(roughness, vroughness),\n" + " tint: pbrt_metal_conductor_tint(" + + eta + ", " + k + + "),\n" + " mode: ::df::scatter_reflect))\n"; + return model; +} + +MdlMaterialModel makeSubstrateMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kd", "color(0.5, 0.5, 0.5)" ); + appendMaterialParameter( model, "color", "Ks", "color(0.5, 0.5, 0.5)" ); + appendMaterialParameter( model, "float", "roughness", "0.1" ); + appendMaterialParameter( model, "float", "uroughness", "-1.0" ); + appendMaterialParameter( model, "float", "vroughness", "-1.0" ); + + const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; + const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; + const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; + + model.comments.push_back( "pbrt material model: substrate" ); + model.comments.push_back( "pbrt material input Kd: " + kd ); + model.comments.push_back( "pbrt material input Ks: " + ks ); + model.comments.push_back( "pbrt material input roughness: roughness" ); + model.comments.push_back( "pbrt material input uroughness: uroughness" ); + model.comments.push_back( "pbrt material input vroughness: vroughness" ); + appendBumpmapCommentsAndHelpers( model, bumpmap ); + appendRoughnessGapComment( model ); + model.comments.push_back( + "pbrt material approximation: diffuse base and glossy layer use an MDL color-weighted layer" ); + model.helperDefinitions = + "float pbrt_substrate_resolved_roughness(float roughness, float axis_roughness) = " + "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" + + model.helperDefinitions; + model.body = + " surface: material_surface(\n" + " scattering: ::df::color_weighted_layer(\n" + " weight: " + ks + ",\n" + " layer: ::df::simple_glossy_bsdf(\n" + " roughness_u: pbrt_substrate_resolved_roughness(roughness, uroughness),\n" + " roughness_v: pbrt_substrate_resolved_roughness(roughness, vroughness),\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect),\n" + " base: ::df::diffuse_reflection_bsdf(\n" + " tint: " + + kd + ")))" + + "\n"; + return model; +} + +MdlMaterialModel makeTranslucentMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); + appendMaterialParameter( model, "color", "Ks", "color(0.0, 0.0, 0.0)" ); + appendMaterialParameter( model, "color", "reflect", "color(0.5, 0.5, 0.5)" ); + appendMaterialParameter( model, "color", "transmit", "color(0.5, 0.5, 0.5)" ); + appendMaterialParameter( model, "float", "roughness", "0.1" ); + appendMaterialParameter( model, "color", "opacity", "color(1.0, 1.0, 1.0)" ); + + const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; + const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; + const std::string reflect{ textureGraph.materialColorExpression( material.params, "reflect", "color", "reflect" ) }; + const std::string transmit{ + textureGraph.materialColorExpression( material.params, "transmit", "color", "transmit" ) }; + const std::string opacityTexture{ + materialTextureCommentExpression( textureGraph, material.params, "opacity", "float" ) }; + const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; + + model.comments.push_back( "pbrt material model: translucent" ); + model.comments.push_back( "pbrt material input Kd: " + kd ); + model.comments.push_back( "pbrt material input Ks: " + ks ); + model.comments.push_back( "pbrt material input reflect: " + reflect ); + model.comments.push_back( "pbrt material input transmit: " + transmit ); + model.comments.push_back( "pbrt material input roughness: roughness" ); + model.comments.push_back( "pbrt material input opacity: opacity; texture=" + opacityTexture ); + appendBumpmapCommentsAndHelpers( model, bumpmap ); + model.comments.push_back( "pbrt material input eta: fixed 1.5" ); + appendRoughnessGapComment( model ); + model.comments.push_back( + "pbrt material approximation: diffuse/glossy reflection and transmission use an MDL color-normalized mix" ); + model.comments.push_back( + "pbrt material approximation: spectrum opacity weights generated translucent lobes and adds transparent " + "transmission" ); + model.helperDefinitions = + "color pbrt_translucent_clamped_opacity(color opacity) = " + "::math::clamp(opacity, color(0.0, 0.0, 0.0), color(1.0, 1.0, 1.0));\n\n" + "color pbrt_translucent_opacity_weight(color opacity) = pbrt_translucent_clamped_opacity(opacity);\n\n" + "color pbrt_translucent_transparency_weight(color opacity) = " + "color(1.0, 1.0, 1.0) - pbrt_translucent_clamped_opacity(opacity);\n\n" + + model.helperDefinitions; + model.body = + " ior: color(1.5, 1.5, 1.5),\n" + " surface: material_surface(\n" + " scattering: ::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_translucent_opacity_weight(opacity) * " + kd + " * " + reflect + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_translucent_opacity_weight(opacity) * " + kd + " * " + transmit + ",\n" + " component: ::df::diffuse_transmission_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_translucent_opacity_weight(opacity) * " + ks + " * " + reflect + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: roughness,\n" + " roughness_v: roughness,\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_reflect)),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_translucent_opacity_weight(opacity) * " + ks + " * " + transmit + ",\n" + " component: ::df::simple_glossy_bsdf(\n" + " roughness_u: roughness,\n" + " roughness_v: roughness,\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_transmit)),\n" + " ::df::color_bsdf_component(\n" + " weight: pbrt_translucent_transparency_weight(opacity),\n" + " component: ::df::specular_bsdf(\n" + " tint: color(1.0, 1.0, 1.0),\n" + " mode: ::df::scatter_transmit)))))" + + ( hasBumpmapExpression( bumpmap ) ? std::string{ ",\n" } + materialGeometryExpression( "", bumpmap ) : "\n" ); + return model; +} + +MdlMaterialModel makeSubsurfaceMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); + appendMaterialParameter( model, "color", "Kt", "color(1.0, 1.0, 1.0)" ); + appendMaterialParameter( model, "color", "sigma_a", "color(0.0011, 0.0024, 0.014)" ); + appendMaterialParameter( model, "color", "sigma_s", "color(2.55, 3.21, 3.77)" ); + appendMaterialParameter( model, "float", "scale", "1.0" ); + appendMaterialParameter( model, "float", "g", "0.0" ); + appendMaterialParameter( model, "float", "eta", "1.33" ); + appendMaterialParameter( model, "float", "uroughness", "0.0" ); + appendMaterialParameter( model, "float", "vroughness", "0.0" ); + + const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; + const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; + const std::string sigmaA{ textureGraph.materialColorExpression( material.params, "sigma_a", "color", "sigma_a" ) }; + const std::string sigmaS{ textureGraph.materialColorExpression( material.params, "sigma_s", "color", "sigma_s" ) }; + const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; + const std::string albedo{ "pbrt_subsurface_albedo(" + sigmaA + ", " + sigmaS + ", scale)" }; + + model.comments.push_back( "pbrt material model: subsurface" ); + model.comments.push_back( "pbrt material input Kr: " + kr ); + model.comments.push_back( "pbrt material input Kt: " + kt ); + model.comments.push_back( "pbrt material input sigma_a: " + sigmaA ); + model.comments.push_back( "pbrt material input sigma_s: " + sigmaS ); + model.comments.push_back( "pbrt material input scale: scale" ); + model.comments.push_back( "pbrt material input g: g" ); + model.comments.push_back( "pbrt material input eta: eta" ); + model.comments.push_back( "pbrt material input uroughness: uroughness" ); + model.comments.push_back( "pbrt material input vroughness: vroughness" ); + model.comments.push_back( "pbrt material input name: named scattering database lookup is not modeled" ); + appendBumpmapCommentsAndHelpers( model, bumpmap ); + model.comments.push_back( + "pbrt material gap: full PBRT BSSRDF transport and named-medium scattering data are not evaluated" ); + model.comments.push_back( + "pbrt material approximation: sigma_a/sigma_s albedo drives diffuse reflection and transmission lobes" ); + model.helperDefinitions = + "color pbrt_subsurface_albedo(color sigma_a, color sigma_s, float scale) =\n" + " ::math::clamp((sigma_s * scale) / ((sigma_a + sigma_s) * scale + color(0.000001, 0.000001, 0.000001)), " + "color(0.0, 0.0, 0.0), color(1.0, 1.0, 1.0));\n\n" + + model.helperDefinitions; + model.body = + " ior: color(eta, eta, eta),\n" + " surface: material_surface(\n" + " scattering: ::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: " + kr + " * " + albedo + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + kt + " * " + albedo + ",\n" + " component: ::df::diffuse_transmission_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))))))" + + ( hasBumpmapExpression( bumpmap ) ? std::string{ ",\n" } + materialGeometryExpression( "", bumpmap ) : "\n" ); + return model; +} + +MdlMaterialModel makeKdSubsurfaceMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "Kd", "color(0.5, 0.5, 0.5)" ); + appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); + appendMaterialParameter( model, "color", "Kt", "color(1.0, 1.0, 1.0)" ); + appendMaterialParameter( model, "color", "mfp", "color(1.0, 1.0, 1.0)" ); + appendMaterialParameter( model, "float", "scale", "1.0" ); + appendMaterialParameter( model, "float", "g", "0.0" ); + appendMaterialParameter( model, "float", "eta", "1.33" ); + appendMaterialParameter( model, "float", "uroughness", "0.0" ); + appendMaterialParameter( model, "float", "vroughness", "0.0" ); + + const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; + const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; + const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; + const std::string mfp{ textureGraph.materialColorExpression( material.params, "mfp", "color", "mfp" ) }; + const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; + + model.comments.push_back( "pbrt material model: kdsubsurface" ); + model.comments.push_back( "pbrt material input Kd: " + kd ); + model.comments.push_back( "pbrt material input Kr: " + kr ); + model.comments.push_back( "pbrt material input Kt: " + kt ); + model.comments.push_back( "pbrt material input mfp: " + mfp ); + model.comments.push_back( "pbrt material input scale: scale" ); + model.comments.push_back( "pbrt material input g: g" ); + model.comments.push_back( "pbrt material input eta: eta" ); + model.comments.push_back( "pbrt material input uroughness: uroughness" ); + model.comments.push_back( "pbrt material input vroughness: vroughness" ); + appendBumpmapCommentsAndHelpers( model, bumpmap ); + model.comments.push_back( "pbrt material gap: full PBRT diffusion-profile BSSRDF transport is not evaluated" ); + model.comments.push_back( "pbrt material approximation: Kd drives diffuse reflection and transmission lobes" ); + model.body = + " ior: color(eta, eta, eta),\n" + " surface: material_surface(\n" + " scattering: ::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: " + kr + " * " + kd + ",\n" + " component: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))),\n" + " ::df::color_bsdf_component(\n" + " weight: " + kt + " * " + kd + ",\n" + " component: ::df::diffuse_transmission_bsdf(\n" + " tint: color(1.0, 1.0, 1.0))))))" + + ( hasBumpmapExpression( bumpmap ) ? std::string{ ",\n" } + materialGeometryExpression( "", bumpmap ) : "\n" ); + return model; +} + +std::string mixNamedMaterialBsdfExpression( MdlMaterialModel& model, + MdlTextureGraphGenerator& textureGraph, + GeneratedMdlSource& result, + const otk::pbrt::PbrtMaterial& material, + const std::string& paramName, + unsigned int index ) +{ + const std::string materialName{ material.params.FindOneString( paramName, std::string{} ) }; + if( materialName.empty() ) + { + model.comments.push_back( "pbrt material input " + paramName + ": missing" ); + appendUnsupportedReason( result, "Missing PBRT mix " + paramName ); + return unsupportedNamedMaterialBsdfExpression(); + } + + model.comments.push_back( "pbrt material input " + paramName + ": named material " + std::to_string( index ) ); + const otk::pbrt::PbrtNamedMaterialMap::const_iterator namedMaterial = material.graph.namedMaterials.find( materialName ); + if( namedMaterial == material.graph.namedMaterials.end() ) + { + appendUnsupportedReason( result, "Missing PBRT named material reference" ); + return unsupportedNamedMaterialBsdfExpression(); + } + + return namedMaterialBsdfExpression( model, textureGraph, result, namedMaterial->second, index ); +} + +MdlMaterialModel makeMixMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph, GeneratedMdlSource& result ) +{ + MdlMaterialModel model; + appendMaterialParameter( model, "color", "amount", "color(0.5, 0.5, 0.5)" ); + + model.comments.push_back( "pbrt material model: mix" ); + const std::string first{ mixNamedMaterialBsdfExpression( model, textureGraph, result, material, "namedmaterial1", 0U ) }; + const std::string second{ mixNamedMaterialBsdfExpression( model, textureGraph, result, material, "namedmaterial2", 1U ) }; + const std::string amountTexture{ + materialTextureCommentExpression( textureGraph, material.params, "amount", "color" ) }; + + model.comments.push_back( "pbrt material input amount: amount; texture=" + amountTexture ); + model.comments.push_back( "pbrt material weighting: namedmaterial1 uses amount; namedmaterial2 uses 1 - amount" ); + model.comments.push_back( "pbrt material approximation: mix composes supported named material MDL closures" ); + model.helperDefinitions = + "float pbrt_mix_matte_sigma_roughness(float sigma_degrees) = ::math::clamp(sigma_degrees / 90.0, 0.0, 1.0);\n\n" + "float pbrt_mix_resolved_roughness(float roughness, float axis_roughness) = " + "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" + "float pbrt_mix_clamped_opacity(float opacity) = ::math::clamp(opacity, 0.0, 1.0);\n\n" + "color pbrt_mix_opacity_weight(float opacity) =\n" + " color(pbrt_mix_clamped_opacity(opacity), pbrt_mix_clamped_opacity(opacity), " + "pbrt_mix_clamped_opacity(opacity));\n\n" + "color pbrt_mix_transparency_weight(float opacity) =\n" + " color(1.0 - pbrt_mix_clamped_opacity(opacity), 1.0 - pbrt_mix_clamped_opacity(opacity), " + "1.0 - pbrt_mix_clamped_opacity(opacity));\n\n" + "color pbrt_mix_metal_conductor_tint(color eta, color k) =\n" + " ((eta - color(1.0, 1.0, 1.0)) * (eta - color(1.0, 1.0, 1.0)) + k * k) /\n" + " ((eta + color(1.0, 1.0, 1.0)) * (eta + color(1.0, 1.0, 1.0)) + k * k);\n\n"; + model.body = + " surface: material_surface(\n" + " scattering: ::df::color_normalized_mix(\n" + " components: ::df::color_bsdf_component[](\n" + " ::df::color_bsdf_component(\n" + " weight: amount,\n" + " component: " + + first + + "),\n" + " ::df::color_bsdf_component(\n" + " weight: color(1.0, 1.0, 1.0) - amount,\n" + " component: " + + second + "))))\n"; + return model; +} + +MdlMaterialModel makeUnsupportedMaterialModel( const otk::pbrt::PbrtMaterial& material, + PbrtMaterialKind kind, + GeneratedMdlSource& result ) +{ + const std::string type{ material.type.empty() ? std::string{ "" } : material.type }; + + MdlMaterialModel model; + model.comments.push_back( "pbrt material model: " + type ); + const PbrtMaterialGapPolicy* const policy{ explicitMaterialGapPolicy( kind ) }; + if( policy != nullptr ) + { + model.comments.push_back( "pbrt material gap policy: " + policy->policy ); + model.comments.push_back( "pbrt material gap coverage: " + policy->coverageReason ); + appendUnsupportedReason( result, "Explicit PBRT material gap " + type + ": " + policy->policy ); + if( kind == PbrtMaterialKind::FOURIER ) + { + if( hasFourierBsdfFile( material ) ) + { + model.comments.push_back( "pbrt fourier bsdffile: preserved as material metadata" ); + } + else + { + model.comments.push_back( "pbrt fourier bsdffile: missing" ); + appendUnsupportedReason( result, "PBRT Fourier material missing bsdffile" ); + } + } + } + else + { + model.comments.push_back( "pbrt material gap policy: unknown material type" ); + appendUnsupportedReason( result, "Unsupported PBRT material type " + type ); + } + model.body = + " surface: material_surface(\n" + " scattering: ::df::diffuse_reflection_bsdf(\n" + " tint: color(1.0, 0.0, 1.0)))\n"; + return model; +} + +MdlMaterialModel makeMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph, GeneratedMdlSource& result ) +{ + const PbrtMaterialKind kind{ pbrtMaterialKind( material.type ) }; + switch( kind ) + { + case PbrtMaterialKind::MATTE: + return makeMatteMaterialModel( material, textureGraph ); + case PbrtMaterialKind::PLASTIC: + return makePlasticMaterialModel( material, textureGraph ); + case PbrtMaterialKind::UBER: + return makeUberMaterialModel( material, textureGraph ); + case PbrtMaterialKind::MIRROR: + return makeMirrorMaterialModel( material, textureGraph ); + case PbrtMaterialKind::GLASS: + return makeGlassMaterialModel( material, textureGraph ); + case PbrtMaterialKind::METAL: + return makeMetalMaterialModel( material, textureGraph ); + case PbrtMaterialKind::SUBSTRATE: + return makeSubstrateMaterialModel( material, textureGraph ); + case PbrtMaterialKind::TRANSLUCENT: + return makeTranslucentMaterialModel( material, textureGraph ); + case PbrtMaterialKind::SUBSURFACE: + return makeSubsurfaceMaterialModel( material, textureGraph ); + case PbrtMaterialKind::KD_SUBSURFACE: + return makeKdSubsurfaceMaterialModel( material, textureGraph ); + case PbrtMaterialKind::MIX: + return makeMixMaterialModel( material, textureGraph, result ); + default: + return makeUnsupportedMaterialModel( material, kind, result ); + } +} + +} // namespace + +GeneratedMdlSource generateMdlSource( const MdlShaderKey& key ) +{ + const std::string suffix{ stableHash( key.signature ) }; + + GeneratedMdlSource result; + result.moduleName = "::otk::demand_pbrt_scene::pbrt_" + suffix; + result.materialName = "material_" + suffix; + + std::ostringstream source; + source << "mdl 1.10;\n" + << "import ::df::*;\n" + << "import ::math::*;\n" + << "\n" + << "export material " << result.materialName << "() = material(\n" + << " surface: material_surface(\n" + << " scattering: ::df::diffuse_reflection_bsdf(\n" + << " tint: color(0.8, 0.8, 0.8))));\n"; + result.source = source.str(); + return result; +} + +GeneratedMdlSource generateMdlSource( const otk::pbrt::PbrtMaterial& material ) +{ + const MdlShaderKey key{ makeMdlShaderKey( material ) }; + const std::string suffix{ stableHash( key.signature ) }; + + GeneratedMdlSource result; + result.moduleName = "::otk::demand_pbrt_scene::pbrt_" + suffix; + result.materialName = "material_" + suffix; + + MdlTextureGraphGenerator textureGraph{ material.graph, result }; + const MdlMaterialModel materialModel{ makeMaterialModel( material, textureGraph, result ) }; + + std::ostringstream source; + source << "mdl 1.10;\n" + << "import ::df::*;\n" + << "import ::math::*;\n" + << "import ::state::*;\n" + << "\n"; + for( std::vector::const_iterator it = materialModel.comments.begin(); it != materialModel.comments.end(); ++it ) + { + source << "// " << *it << "\n"; + } + if( !materialModel.comments.empty() ) + { + source << "\n"; + } + for( std::vector::const_iterator it = result.unsupportedReasons.begin(); + it != result.unsupportedReasons.end(); ++it ) + { + source << "// unsupported: " << *it << "\n"; + } + if( !result.unsupportedReasons.empty() ) + { + source << "\n"; + } + source << textureGraph.sourcePreamble() << materialModel.helperDefinitions << textureGraph.functionDefinitions() + << "export material " << result.materialName << mdlParameterList( materialModel.parameters ) << " = material(\n" + << materialModel.body << ");\n"; + result.source = source.str(); + return result; +} + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL + diff --git a/examples/DemandLoading/DemandPbrtScene/MdlParameterBinder.cpp b/examples/DemandLoading/DemandPbrtScene/MdlParameterBinder.cpp new file mode 100644 index 00000000..82fd2db1 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/MdlParameterBinder.cpp @@ -0,0 +1,594 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "DemandPbrtScene/MdlParameterBinder.h" + +#ifdef OTK_USE_MDL + +#include "DemandPbrtScene/MdlTextureGraphGenerator.h" +#include "DemandPbrtScene/PbrtMaterialKind.h" + +#include +#include +#include +#include +#include + +namespace demandPbrtScene { + +std::string namedMaterialParameterName( unsigned int index, const std::string& paramName ) +{ + return "named_" + std::to_string( index ) + "_" + paramName; +} + +std::string namedMaterialType( const otk::pbrt::PbrtNamedMaterial& material ) +{ + if( !material.type.empty() ) + { + return material.type; + } + return material.params.FindOneString( "type", std::string{} ); +} + +namespace { + +struct BoundParameterSpec +{ + MdlBoundParameterType type; + const char* name; +}; + +struct FoldedColor +{ + float red{}; + float green{}; + float blue{}; +}; + +FoldedColor operator*( const FoldedColor& lhs, const FoldedColor& rhs ) +{ + return FoldedColor{ lhs.red * rhs.red, lhs.green * rhs.green, lhs.blue * rhs.blue }; +} + +FoldedColor mix( const FoldedColor& lhs, const FoldedColor& rhs, float amount ) +{ + return FoldedColor{ lhs.red * ( 1.0f - amount ) + rhs.red * amount, lhs.green * ( 1.0f - amount ) + rhs.green * amount, + lhs.blue * ( 1.0f - amount ) + rhs.blue * amount }; +} + +bool findConstantColor( const ::pbrt::ParamSet& params, const char* name, float& red, float& green, float& blue ) +{ + if( !params.FindTexture( name ).empty() ) + { + return false; + } + + int count{}; + const ::pbrt::Spectrum* values = params.FindSpectrum( name, &count ); + if( count <= 0 || values == nullptr ) + { + return false; + } + + float rgb[3]{}; + values[0].ToRGB( rgb ); + red = rgb[0]; + green = rgb[1]; + blue = rgb[2]; + return true; +} + +bool findConstantFloat( const ::pbrt::ParamSet& params, const char* name, float& value ) +{ + if( !params.FindTexture( name ).empty() ) + { + return false; + } + + int count{}; + const float* values = params.FindFloat( name, &count ); + if( count <= 0 || values == nullptr ) + { + return false; + } + + value = values[0]; + return true; +} + +bool scalarColorValue( const FoldedColor& color, float& value ) +{ + constexpr float epsilon{ 1.0e-6f }; + if( std::fabs( color.red - color.green ) > epsilon || std::fabs( color.red - color.blue ) > epsilon ) + { + return false; + } + value = color.red; + return true; +} + +bool promotesFloatToColorParameter( const char* name ) +{ + const std::string parameterName{ name }; + return parameterName == "opacity" || parameterName == "amount"; +} + +bool findTextureColorValue( const ::pbrt::ParamSet& params, const char* name, const FoldedColor& defaultValue, FoldedColor& value ) +{ + if( findConstantColor( params, name, value.red, value.green, value.blue ) ) + { + return true; + } + + float floatValue{}; + if( findConstantFloat( params, name, floatValue ) ) + { + value = FoldedColor{ floatValue, floatValue, floatValue }; + return true; + } + + value = defaultValue; + return true; +} + +bool findTextureFloatValue( const ::pbrt::ParamSet& params, const char* name, float defaultValue, float& value ) +{ + if( findConstantFloat( params, name, value ) ) + { + return true; + } + + FoldedColor color{}; + if( findConstantColor( params, name, color.red, color.green, color.blue ) ) + { + return scalarColorValue( color, value ); + } + + value = defaultValue; + return true; +} + +bool findFoldableTextureColor( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + const std::string& preferredValueType, + std::vector& textureStack, + FoldedColor& value ); + +bool findFoldableTextureFloat( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + std::vector& textureStack, + float& value ); + +bool findTextureInputColor( const otk::pbrt::PbrtMaterialGraph& graph, + const otk::pbrt::PbrtTexture& texture, + const char* name, + const FoldedColor& defaultValue, + std::vector& textureStack, + FoldedColor& value ) +{ + const std::string inputTextureName{ texture.params.FindTexture( name ) }; + if( !inputTextureName.empty() ) + { + return findFoldableTextureColor( graph, inputTextureName, texture.valueType, textureStack, value ); + } + return findTextureColorValue( texture.params, name, defaultValue, value ); +} + +bool findTextureInputFloat( const otk::pbrt::PbrtMaterialGraph& graph, + const otk::pbrt::PbrtTexture& texture, + const char* name, + float defaultValue, + std::vector& textureStack, + float& value ) +{ + const std::string inputTextureName{ texture.params.FindTexture( name ) }; + if( !inputTextureName.empty() ) + { + return findFoldableTextureFloat( graph, inputTextureName, textureStack, value ); + } + return findTextureFloatValue( texture.params, name, defaultValue, value ); +} + +bool findFoldableTextureColor( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + const std::string& preferredValueType, + std::vector& textureStack, + FoldedColor& value ) +{ + const MdlTextureLookup lookup{ findMdlTexture( graph, textureName, preferredValueType ) }; + if( lookup.texture == nullptr || std::find( textureStack.begin(), textureStack.end(), lookup.graphKey ) != textureStack.end() ) + { + return false; + } + + textureStack.push_back( lookup.graphKey ); + const otk::pbrt::PbrtTexture& texture{ *lookup.texture }; + bool folded{ false }; + if( texture.type == "constant" ) + { + folded = findTextureColorValue( texture.params, "value", FoldedColor{ 1.0f, 1.0f, 1.0f }, value ); + } + else if( texture.type == "scale" ) + { + FoldedColor tex1{}; + FoldedColor tex2{}; + folded = findTextureInputColor( graph, texture, "tex1", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex1 ) + && findTextureInputColor( graph, texture, "tex2", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex2 ); + if( folded ) + { + value = tex1 * tex2; + } + } + else if( texture.type == "mix" ) + { + FoldedColor tex1{}; + FoldedColor tex2{}; + float amount{}; + folded = findTextureInputColor( graph, texture, "tex1", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex1 ) + && findTextureInputColor( graph, texture, "tex2", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex2 ) + && findTextureInputFloat( graph, texture, "amount", 0.5f, textureStack, amount ); + if( folded ) + { + value = mix( tex1, tex2, amount ); + } + } + + textureStack.pop_back(); + return folded; +} + +bool findFoldableTextureFloat( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + std::vector& textureStack, + float& value ) +{ + const MdlTextureLookup lookup{ findMdlTexture( graph, textureName, "float" ) }; + if( lookup.texture == nullptr || std::find( textureStack.begin(), textureStack.end(), lookup.graphKey ) != textureStack.end() ) + { + return false; + } + + textureStack.push_back( lookup.graphKey ); + const otk::pbrt::PbrtTexture& texture{ *lookup.texture }; + bool folded{ false }; + if( texture.type == "constant" ) + { + folded = findTextureFloatValue( texture.params, "value", 1.0f, value ); + } + else if( texture.type == "scale" ) + { + float tex1{}; + float tex2{}; + folded = findTextureInputFloat( graph, texture, "tex1", 1.0f, textureStack, tex1 ) + && findTextureInputFloat( graph, texture, "tex2", 1.0f, textureStack, tex2 ); + if( folded ) + { + value = tex1 * tex2; + } + } + else if( texture.type == "mix" ) + { + float tex1{}; + float tex2{}; + float amount{}; + folded = findTextureInputFloat( graph, texture, "tex1", 1.0f, textureStack, tex1 ) + && findTextureInputFloat( graph, texture, "tex2", 1.0f, textureStack, tex2 ) + && findTextureInputFloat( graph, texture, "amount", 0.5f, textureStack, amount ); + if( folded ) + { + value = tex1 * ( 1.0f - amount ) + tex2 * amount; + } + } + + textureStack.pop_back(); + return folded; +} + +bool findFoldableTextureColor( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + const std::string& preferredValueType, + FoldedColor& value ) +{ + std::vector textureStack; + return findFoldableTextureColor( graph, textureName, preferredValueType, textureStack, value ); +} + +bool findFoldableTextureFloat( const otk::pbrt::PbrtMaterialGraph& graph, const std::string& textureName, float& value ) +{ + std::vector textureStack; + return findFoldableTextureFloat( graph, textureName, textureStack, value ); +} + +void appendBoundParameter( std::vector& result, const ::pbrt::ParamSet& params, const BoundParameterSpec& spec ) +{ + MdlBoundMaterialParameter parameter{}; + parameter.name = spec.name; + parameter.type = spec.type; + if( spec.type == MdlBoundParameterType::COLOR ) + { + if( findConstantColor( params, spec.name, parameter.red, parameter.green, parameter.blue ) ) + { + result.push_back( parameter ); + } + else if( promotesFloatToColorParameter( spec.name ) && findConstantFloat( params, spec.name, parameter.value ) ) + { + parameter.red = parameter.green = parameter.blue = parameter.value; + result.push_back( parameter ); + } + return; + } + + if( findConstantFloat( params, spec.name, parameter.value ) ) + { + result.push_back( parameter ); + return; + } +} + +void appendTextureBackedBoundParameter( std::vector& result, + const otk::pbrt::PbrtMaterial& material, + const BoundParameterSpec& spec ) +{ + const std::string textureName{ material.params.FindTexture( spec.name ) }; + if( textureName.empty() ) + { + return; + } + + MdlBoundMaterialParameter parameter{}; + parameter.name = spec.name; + parameter.type = spec.type; + if( spec.type == MdlBoundParameterType::COLOR ) + { + FoldedColor value{}; + if( findFoldableTextureColor( material.graph, textureName, "color", value ) ) + { + parameter.red = value.red; + parameter.green = value.green; + parameter.blue = value.blue; + result.push_back( parameter ); + } + return; + } + + if( findFoldableTextureFloat( material.graph, textureName, parameter.value ) ) + { + result.push_back( parameter ); + } +} + +void appendBoundParameters( std::vector& result, + const ::pbrt::ParamSet& params, + const BoundParameterSpec* begin, + const BoundParameterSpec* end ) +{ + for( const BoundParameterSpec* it = begin; it != end; ++it ) + { + appendBoundParameter( result, params, *it ); + } +} + +void appendMaterialBoundParameters( std::vector& result, + const otk::pbrt::PbrtMaterial& material, + const BoundParameterSpec* begin, + const BoundParameterSpec* end ) +{ + for( const BoundParameterSpec* it = begin; it != end; ++it ) + { + appendBoundParameter( result, material.params, *it ); + appendTextureBackedBoundParameter( result, material, *it ); + } +} + +void appendNamedBoundParameter( std::vector& result, + const ::pbrt::ParamSet& params, + unsigned int index, + const BoundParameterSpec& spec ) +{ + MdlBoundMaterialParameter parameter{}; + parameter.name = namedMaterialParameterName( index, spec.name ); + parameter.type = spec.type; + if( spec.type == MdlBoundParameterType::COLOR ) + { + if( findConstantColor( params, spec.name, parameter.red, parameter.green, parameter.blue ) ) + { + result.push_back( parameter ); + } + return; + } + + if( findConstantFloat( params, spec.name, parameter.value ) ) + { + result.push_back( parameter ); + } +} + +void appendNamedBoundParameters( std::vector& result, + const ::pbrt::ParamSet& params, + unsigned int index, + const BoundParameterSpec* begin, + const BoundParameterSpec* end ) +{ + for( const BoundParameterSpec* it = begin; it != end; ++it ) + { + appendNamedBoundParameter( result, params, index, *it ); + } +} + +constexpr BoundParameterSpec matteParams[] = { + { MdlBoundParameterType::COLOR, "Kd" }, + { MdlBoundParameterType::FLOAT, "sigma" }, + { MdlBoundParameterType::FLOAT, "alpha" }, + { MdlBoundParameterType::FLOAT, "opacity" }, +}; +constexpr BoundParameterSpec plasticParams[] = { + { MdlBoundParameterType::COLOR, "Kd" }, + { MdlBoundParameterType::COLOR, "Ks" }, + { MdlBoundParameterType::FLOAT, "roughness" }, +}; +constexpr BoundParameterSpec uberParams[] = { + { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, + { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, + { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, + { MdlBoundParameterType::FLOAT, "vroughness" }, { MdlBoundParameterType::FLOAT, "index" }, + { MdlBoundParameterType::FLOAT, "alpha" }, { MdlBoundParameterType::COLOR, "opacity" }, +}; +constexpr BoundParameterSpec namedUberParams[] = { + { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, + { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, + { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, + { MdlBoundParameterType::FLOAT, "vroughness" }, { MdlBoundParameterType::FLOAT, "alpha" }, + { MdlBoundParameterType::FLOAT, "opacity" }, +}; +constexpr BoundParameterSpec mirrorParams[] = { + { MdlBoundParameterType::COLOR, "Kr" }, +}; +constexpr BoundParameterSpec glassParams[] = { + { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, + { MdlBoundParameterType::FLOAT, "index" }, { MdlBoundParameterType::FLOAT, "roughness" }, + { MdlBoundParameterType::FLOAT, "uroughness" }, { MdlBoundParameterType::FLOAT, "vroughness" }, +}; +constexpr BoundParameterSpec metalParams[] = { + { MdlBoundParameterType::COLOR, "eta" }, { MdlBoundParameterType::COLOR, "k" }, + { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, + { MdlBoundParameterType::FLOAT, "vroughness" }, +}; +constexpr BoundParameterSpec substrateParams[] = { + { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, + { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, + { MdlBoundParameterType::FLOAT, "vroughness" }, +}; +constexpr BoundParameterSpec translucentParams[] = { + { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, + { MdlBoundParameterType::COLOR, "reflect" }, { MdlBoundParameterType::COLOR, "transmit" }, + { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::COLOR, "opacity" }, +}; +constexpr BoundParameterSpec subsurfaceParams[] = { + { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, + { MdlBoundParameterType::COLOR, "sigma_a" }, { MdlBoundParameterType::COLOR, "sigma_s" }, + { MdlBoundParameterType::FLOAT, "scale" }, { MdlBoundParameterType::FLOAT, "g" }, + { MdlBoundParameterType::FLOAT, "eta" }, { MdlBoundParameterType::FLOAT, "uroughness" }, + { MdlBoundParameterType::FLOAT, "vroughness" }, +}; +constexpr BoundParameterSpec kdSubsurfaceParams[] = { + { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Kr" }, + { MdlBoundParameterType::COLOR, "Kt" }, { MdlBoundParameterType::COLOR, "mfp" }, + { MdlBoundParameterType::FLOAT, "scale" }, { MdlBoundParameterType::FLOAT, "g" }, + { MdlBoundParameterType::FLOAT, "eta" }, { MdlBoundParameterType::FLOAT, "uroughness" }, + { MdlBoundParameterType::FLOAT, "vroughness" }, +}; +constexpr BoundParameterSpec mixParams[] = { + { MdlBoundParameterType::COLOR, "amount" }, +}; + +struct BoundParameterSpecs +{ + const BoundParameterSpec* begin{}; + const BoundParameterSpec* end{}; +}; + +template +constexpr BoundParameterSpecs makeBoundParameterSpecs( const BoundParameterSpec ( &specs )[N] ) +{ + return { specs, specs + N }; +} + +enum class BoundMaterialKind +{ + ROOT, + NAMED, +}; + +struct MaterialBoundParameterSpecs +{ + PbrtMaterialKind kind; + BoundParameterSpecs root; + BoundParameterSpecs named; +}; + +constexpr MaterialBoundParameterSpecs materialBoundParameterSpecs[] = { + { PbrtMaterialKind::MATTE, makeBoundParameterSpecs( matteParams ), makeBoundParameterSpecs( matteParams ) }, + { PbrtMaterialKind::PLASTIC, makeBoundParameterSpecs( plasticParams ), makeBoundParameterSpecs( plasticParams ) }, + { PbrtMaterialKind::UBER, makeBoundParameterSpecs( uberParams ), makeBoundParameterSpecs( namedUberParams ) }, + { PbrtMaterialKind::MIRROR, makeBoundParameterSpecs( mirrorParams ), makeBoundParameterSpecs( mirrorParams ) }, + { PbrtMaterialKind::GLASS, makeBoundParameterSpecs( glassParams ), makeBoundParameterSpecs( glassParams ) }, + { PbrtMaterialKind::METAL, makeBoundParameterSpecs( metalParams ), makeBoundParameterSpecs( metalParams ) }, + { PbrtMaterialKind::SUBSTRATE, makeBoundParameterSpecs( substrateParams ), makeBoundParameterSpecs( substrateParams ) }, + { PbrtMaterialKind::TRANSLUCENT, makeBoundParameterSpecs( translucentParams ), + makeBoundParameterSpecs( translucentParams ) }, + { PbrtMaterialKind::SUBSURFACE, makeBoundParameterSpecs( subsurfaceParams ), {} }, + { PbrtMaterialKind::KD_SUBSURFACE, makeBoundParameterSpecs( kdSubsurfaceParams ), {} }, + { PbrtMaterialKind::MIX, makeBoundParameterSpecs( mixParams ), {} }, +}; + +BoundParameterSpecs boundParameterSpecs( PbrtMaterialKind materialKind, BoundMaterialKind boundKind ) +{ + for( const MaterialBoundParameterSpecs& specs : materialBoundParameterSpecs ) + { + if( materialKind == specs.kind ) + { + return boundKind == BoundMaterialKind::ROOT ? specs.root : specs.named; + } + } + return {}; +} + +void appendRootMaterialBoundParameters( std::vector& result, + const otk::pbrt::PbrtMaterial& material, + PbrtMaterialKind kind ) +{ + const BoundParameterSpecs specs{ boundParameterSpecs( kind, BoundMaterialKind::ROOT ) }; + appendMaterialBoundParameters( result, material, specs.begin, specs.end ); +} + +void appendNamedMaterialBoundParameters( std::vector& result, + const otk::pbrt::PbrtMaterial& material, + const std::string& paramName, + unsigned int index ) +{ + const std::string materialName{ material.params.FindOneString( paramName, std::string{} ) }; + if( materialName.empty() ) + { + return; + } + + const otk::pbrt::PbrtNamedMaterialMap::const_iterator namedMaterial = material.graph.namedMaterials.find( materialName ); + if( namedMaterial == material.graph.namedMaterials.end() ) + { + return; + } + + const PbrtMaterialKind kind{ pbrtMaterialKind( namedMaterialType( namedMaterial->second ) ) }; + const BoundParameterSpecs specs{ boundParameterSpecs( kind, BoundMaterialKind::NAMED ) }; + appendNamedBoundParameters( result, namedMaterial->second.params, index, specs.begin, specs.end ); +} + +void appendNamedMaterialBoundParameters( std::vector& result, + const otk::pbrt::PbrtMaterial& material, + PbrtMaterialKind kind ) +{ + if( kind != PbrtMaterialKind::MIX ) + { + return; + } + + appendNamedMaterialBoundParameters( result, material, "namedmaterial1", 0U ); + appendNamedMaterialBoundParameters( result, material, "namedmaterial2", 1U ); +} + +} // namespace + +std::vector makeMdlBoundMaterialParameters( const otk::pbrt::PbrtMaterial& material ) +{ + std::vector result; + const PbrtMaterialKind kind{ pbrtMaterialKind( material.type ) }; + appendRootMaterialBoundParameters( result, material, kind ); + appendNamedMaterialBoundParameters( result, material, kind ); + return result; +} + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL + diff --git a/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp b/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp new file mode 100644 index 00000000..425290c9 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "DemandPbrtScene/MdlSdkSession.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include +#include + +namespace demandPbrtScene { +namespace { + +#ifdef _WIN32 + +using MdlLibraryHandle = HMODULE; + +std::string lastLibraryError() +{ + std::ostringstream out; + out << "Windows error " << GetLastError(); + return out.str(); +} + +MdlLibraryHandle loadMdlSdkLibrary( std::string& error ) +{ + const char* const libraryName = "libmdl_sdk" MI_BASE_DLL_FILE_EXT; + MdlLibraryHandle handle = LoadLibraryA( libraryName ); + if( handle ) + return handle; + + const std::string fallback = std::string( "../../../bin/" ) + libraryName; + handle = LoadLibraryA( fallback.c_str() ); + if( handle ) + return handle; + + error = "Failed to load " + std::string( libraryName ) + ": " + lastLibraryError(); + return nullptr; +} + +void* loadMdlFactorySymbol( MdlLibraryHandle handle, std::string& error ) +{ + void* symbol = GetProcAddress( handle, "mi_factory" ); + if( !symbol ) + error = "Failed to find mi_factory: " + lastLibraryError(); + return symbol; +} + +void unloadMdlSdkLibrary( MdlLibraryHandle handle ) +{ + if( handle ) + FreeLibrary( handle ); +} + +#else + +using MdlLibraryHandle = void*; + +MdlLibraryHandle loadMdlSdkLibrary( std::string& error ) +{ + const char* const libraryName = "libmdl_sdk" MI_BASE_DLL_FILE_EXT; + MdlLibraryHandle handle = dlopen( libraryName, RTLD_LAZY ); + if( !handle ) + error = dlerror(); + return handle; +} + +void* loadMdlFactorySymbol( MdlLibraryHandle handle, std::string& error ) +{ + void* symbol = dlsym( handle, "mi_factory" ); + if( !symbol ) + error = dlerror(); + return symbol; +} + +void unloadMdlSdkLibrary( MdlLibraryHandle handle ) +{ + if( handle ) + dlclose( handle ); +} + +#endif + +} // namespace + +struct MdlSdkSession::Impl +{ + MdlLibraryHandle library{}; + NeurayHandle neuray; + std::string error; + bool started{}; +}; + +MdlSdkSession::MdlSdkSession() + : m_impl( std::make_unique() ) +{ + m_impl->library = loadMdlSdkLibrary( m_impl->error ); + if( !m_impl->library ) + return; + + void* symbol = loadMdlFactorySymbol( m_impl->library, m_impl->error ); + if( !symbol ) + return; + + m_impl->neuray = mi::neuraylib::mi_factory( symbol ); + if( !m_impl->neuray.is_valid_interface() ) + { + VersionHandle version( mi::neuraylib::mi_factory( symbol ) ); + m_impl->error = version.is_valid_interface() ? "MDL SDK library version does not match header version " + + std::string( MI_NEURAYLIB_PRODUCT_VERSION_STRING ) : + "MDL SDK library is incompatible with this header"; + return; + } + + const mi::Sint32 startResult = m_impl->neuray->start( true ); + if( startResult != 0 ) + { + std::ostringstream out; + out << "Failed to start MDL SDK: " << startResult; + m_impl->error = out.str(); + return; + } + + m_impl->started = true; +} + +MdlSdkSession::~MdlSdkSession() +{ + shutdown(); + unloadMdlSdkLibrary( m_impl->library ); +} + +bool MdlSdkSession::isStarted() const +{ + return m_impl->started; +} + +const std::string& MdlSdkSession::error() const +{ + return m_impl->error; +} + +const NeurayHandle& MdlSdkSession::handle() const +{ + return m_impl->neuray; +} + +mi::neuraylib::INeuray* MdlSdkSession::neuray() const +{ + return m_impl->neuray.get(); +} + +mi::Sint32 MdlSdkSession::shutdown() +{ + mi::Sint32 result = 0; + if( m_impl->started ) + { + result = m_impl->neuray->shutdown( true ); + m_impl->started = false; + } + m_impl->neuray.reset(); + return result; +} + +void MdlSdkSession::close() +{ + if( shutdown() != 0 ) + { + throw std::runtime_error( "Failed to shut down MDL SDK" ); + } +} + +} // namespace demandPbrtScene diff --git a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp index 3da9e39d..9b4adaab 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp @@ -5,2551 +5,13 @@ #include "DemandPbrtScene/MdlShaderCache.h" #ifdef OTK_USE_MDL -#include "DemandPbrtScene/PbrtCheckerboardImageSource.h" -#include -#include -#include -#include -#include -#include +#include "DemandPbrtScene/MdlMaterialModelBuilder.h" + #include -#include #include -#include namespace demandPbrtScene { -namespace { - -std::vector sortedNames( std::initializer_list names ) -{ - std::vector result; - result.reserve( names.size() ); - std::copy( names.begin(), names.end(), std::back_inserter( result ) ); - std::sort( result.begin(), result.end() ); - return result; -} - -const std::vector& materialTextureParamNames() -{ - static const std::vector names{ sortedNames( { - "Kd", "Kr", "Ks", "Kt", "alpha", "amount", "bumpmap", - "eta", "index", "k", "mfp", "opacity", "reflect", "roughness", - "shadowalpha", "sigma", "sigma_a", "sigma_s", "transmit", "uroughness", "vroughness", - } ) }; - return names; -} - -const std::vector& textureTextureParamNames() -{ - static const std::vector names{ sortedNames( { - "amount", - "scale", - "tex", - "tex1", - "tex2", - } ) }; - return names; -} - -const std::vector& namedMaterialParamNames() -{ - static const std::vector names{ sortedNames( { - "material", - "material1", - "material2", - "namedmaterial1", - "namedmaterial2", - } ) }; - return names; -} - -bool contains( const std::vector& values, const std::string& value ) -{ - return std::find( values.begin(), values.end(), value ) != values.end(); -} - -void appendUnsupportedReason( GeneratedMdlSource& result, const std::string& reason ) -{ - if( !contains( result.unsupportedReasons, reason ) ) - { - result.unsupportedReasons.push_back( reason ); - } -} - -void appendTextureSignature( std::ostringstream& out, - const std::string& graphKey, - const otk::pbrt::PbrtTexture& texture, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ); - -void appendTextureReference( std::ostringstream& out, - const std::string& paramName, - const std::string& textureName, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) -{ - out << "|texture-ref(" << paramName << ")="; - bool found{ false }; - for( otk::pbrt::PbrtTextureMap::const_iterator it = graph.textures.begin(); it != graph.textures.end(); ++it ) - { - if( it->second.name == textureName ) - { - if( found ) - out << ","; - appendTextureSignature( out, it->first, it->second, graph, textureStack ); - found = true; - } - } - if( !found ) - { - out << "missing"; - } -} - -void appendTextureReferences( std::ostringstream& out, - const ::pbrt::ParamSet& params, - const std::vector& paramNames, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) -{ - for( std::vector::const_iterator it = paramNames.begin(); it != paramNames.end(); ++it ) - { - const std::string textureName{ params.FindTexture( *it ) }; - if( !textureName.empty() ) - { - appendTextureReference( out, *it, textureName, graph, textureStack ); - } - } -} - -void appendMaterialSignature( std::ostringstream& out, - const std::string& type, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ); - -void appendMaterialReference( std::ostringstream& out, - const std::string& paramName, - const std::string& materialName, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) -{ - out << "|material-ref(" << paramName << ")="; - if( contains( materialStack, materialName ) ) - { - out << "recursive"; - return; - } - - const otk::pbrt::PbrtNamedMaterialMap::const_iterator material = graph.namedMaterials.find( materialName ); - if( material == graph.namedMaterials.end() ) - { - out << "missing"; - return; - } - - materialStack.push_back( materialName ); - appendMaterialSignature( out, material->second.type, material->second.params, graph, materialStack, textureStack ); - materialStack.pop_back(); -} - -void appendMaterialReferences( std::ostringstream& out, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) -{ - const std::vector& paramNames{ namedMaterialParamNames() }; - for( std::vector::const_iterator it = paramNames.begin(); it != paramNames.end(); ++it ) - { - const std::string materialName{ params.FindOneString( *it, std::string{} ) }; - if( !materialName.empty() ) - { - appendMaterialReference( out, *it, materialName, graph, materialStack, textureStack ); - } - } -} - -void appendTextureSignature( std::ostringstream& out, - const std::string& graphKey, - const otk::pbrt::PbrtTexture& texture, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) -{ - if( contains( textureStack, graphKey ) ) - { - out << "texture(" << texture.valueType << ":" << texture.type << ";recursive)"; - return; - } - - textureStack.push_back( graphKey ); - out << "texture(" << texture.valueType << ":" << texture.type; - appendTextureReferences( out, texture.params, textureTextureParamNames(), graph, textureStack ); - out << ")"; - textureStack.pop_back(); -} - -void appendMaterialSignature( std::ostringstream& out, - const std::string& type, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) -{ - out << "material(" << type; - appendTextureReferences( out, params, materialTextureParamNames(), graph, textureStack ); - appendMaterialReferences( out, params, graph, materialStack, textureStack ); - out << ")"; -} - -std::string paramSetToString( const ::pbrt::ParamSet& params ) -{ - ::pbrt::ParamSet copy{ params }; - return copy.ToString(); -} - -void appendTextureInstanceSignature( std::ostringstream& out, - const std::string& graphKey, - const otk::pbrt::PbrtTexture& texture, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ); - -void appendTextureInstanceReference( std::ostringstream& out, - const std::string& paramName, - const std::string& textureName, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) -{ - out << "|texture-ref(" << paramName << ")=" << textureName << ':'; - bool found{ false }; - for( otk::pbrt::PbrtTextureMap::const_iterator it = graph.textures.begin(); it != graph.textures.end(); ++it ) - { - if( it->second.name == textureName ) - { - if( found ) - out << ","; - appendTextureInstanceSignature( out, it->first, it->second, graph, textureStack ); - found = true; - } - } - if( !found ) - { - out << "missing"; - } -} - -void appendTextureInstanceReferences( std::ostringstream& out, - const ::pbrt::ParamSet& params, - const std::vector& paramNames, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) -{ - for( std::vector::const_iterator it = paramNames.begin(); it != paramNames.end(); ++it ) - { - const std::string textureName{ params.FindTexture( *it ) }; - if( !textureName.empty() ) - { - appendTextureInstanceReference( out, *it, textureName, graph, textureStack ); - } - } -} - -void appendMaterialInstanceSignature( std::ostringstream& out, - const std::string& type, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ); - -void appendMaterialInstanceReference( std::ostringstream& out, - const std::string& paramName, - const std::string& materialName, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) -{ - out << "|material-ref(" << paramName << ")=" << materialName << ':'; - if( contains( materialStack, materialName ) ) - { - out << "recursive"; - return; - } - - const otk::pbrt::PbrtNamedMaterialMap::const_iterator material = graph.namedMaterials.find( materialName ); - if( material == graph.namedMaterials.end() ) - { - out << "missing"; - return; - } - - materialStack.push_back( materialName ); - appendMaterialInstanceSignature( out, material->second.type, material->second.params, graph, materialStack, textureStack ); - materialStack.pop_back(); -} - -void appendMaterialInstanceReferences( std::ostringstream& out, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) -{ - const std::vector& paramNames{ namedMaterialParamNames() }; - for( std::vector::const_iterator it = paramNames.begin(); it != paramNames.end(); ++it ) - { - const std::string materialName{ params.FindOneString( *it, std::string{} ) }; - if( !materialName.empty() ) - { - appendMaterialInstanceReference( out, *it, materialName, graph, materialStack, textureStack ); - } - } -} - -void appendTextureInstanceSignature( std::ostringstream& out, - const std::string& graphKey, - const otk::pbrt::PbrtTexture& texture, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) -{ - if( contains( textureStack, graphKey ) ) - { - out << "texture(key=" << graphKey << ",name=" << texture.name << ",kind=" << texture.valueType << ':' - << texture.type << ";recursive)"; - return; - } - - textureStack.push_back( graphKey ); - out << "texture(key=" << graphKey << ",name=" << texture.name << ",kind=" << texture.valueType << ':' - << texture.type << "|params=" << paramSetToString( texture.params ); - appendTextureInstanceReferences( out, texture.params, textureTextureParamNames(), graph, textureStack ); - out << ")"; - textureStack.pop_back(); -} - -void appendMaterialInstanceSignature( std::ostringstream& out, - const std::string& type, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) -{ - out << "material(" << type << "|params=" << paramSetToString( params ); - appendTextureInstanceReferences( out, params, materialTextureParamNames(), graph, textureStack ); - appendMaterialInstanceReferences( out, params, graph, materialStack, textureStack ); - out << ")"; -} - -std::string stableHash( const std::string& text ) -{ - std::uint64_t hash{ 14695981039346656037ULL }; - for( std::string::const_iterator it = text.begin(); it != text.end(); ++it ) - { - hash ^= static_cast( *it ); - hash *= 1099511628211ULL; - } - - std::ostringstream out; - out << std::hex << std::setfill( '0' ) << std::setw( 16 ) << hash; - return out.str(); -} - -struct TextureLookup -{ - std::string graphKey; - const otk::pbrt::PbrtTexture* texture; -}; - -TextureLookup findTexture( const otk::pbrt::PbrtMaterialGraph& graph, const std::string& textureName, const std::string& preferredValueType ) -{ - TextureLookup fallback{ std::string{}, nullptr }; - for( otk::pbrt::PbrtTextureMap::const_iterator it = graph.textures.begin(); it != graph.textures.end(); ++it ) - { - if( it->second.name != textureName ) - { - continue; - } - if( preferredValueType.empty() || it->second.valueType == preferredValueType ) - { - return TextureLookup{ it->first, &it->second }; - } - if( fallback.texture == nullptr ) - { - fallback = TextureLookup{ it->first, &it->second }; - } - } - return fallback; -} - -std::string textureKind( const otk::pbrt::PbrtTexture& texture ) -{ - return texture.valueType + ":" + texture.type; -} - -bool isUnsupportedProceduralTexture( const otk::pbrt::PbrtTexture& texture ) -{ - return texture.type == "marble" || texture.type == "fbm" || texture.type == "windy" || texture.type == "wrinkled"; -} - -class MdlTextureGraphGenerator -{ - public: - MdlTextureGraphGenerator( const otk::pbrt::PbrtMaterialGraph& graph, GeneratedMdlSource& result ) - : m_graph( graph ) - , m_result( result ) - { - for( std::vector::const_iterator it = m_graph.fallbackReasons.begin(); - it != m_graph.fallbackReasons.end(); ++it ) - { - appendUnsupportedReason( m_result, "PBRT material graph fallback: " + *it ); - } - } - - std::string materialColorExpression( const ::pbrt::ParamSet& params, - const std::string& paramName, - const std::string& preferredValueType, - const std::string& defaultExpression ) - { - const std::string textureName{ params.FindTexture( paramName ) }; - if( textureName.empty() ) - { - return defaultExpression; - } - if( isFoldableTextureReference( textureName, preferredValueType ) ) - { - return defaultExpression; - } - return textureReference( textureName, preferredValueType ); - } - - std::string materialFloatExpression( const ::pbrt::ParamSet& params, - const std::string& paramName, - const std::string& preferredValueType, - const std::string& defaultExpression ) - { - const std::string textureName{ params.FindTexture( paramName ) }; - if( textureName.empty() ) - { - return defaultExpression; - } - if( isFoldableTextureReference( textureName, preferredValueType ) && defaultExpression != "0.0" ) - { - return defaultExpression; - } - m_usesTextureFloat = true; - return "pbrt_texture_float(" + textureReference( textureName, preferredValueType ) + ")"; - } - - std::string sourcePreamble() const - { - std::ostringstream out; - if( m_usesTextureFloat ) - { - out << "float pbrt_texture_float(color value) = ::math::luminance(value);\n"; - } - if( m_usesDemandTexture ) - { - out << "color pbrt_demand_texture_2d(int texture_id) = color(1.0, 1.0, 1.0);\n"; - } - if( m_usesCheckerboard ) - { - out << "color pbrt_checkerboard_2d(color tex1, color tex2) = (tex1 + tex2) * 0.5;\n"; - } - if( m_usesUnsupported ) - { - out << "color pbrt_unsupported_texture() = color(1.0, 0.0, 1.0);\n"; - } - if( !m_usesTextureFloat && !m_usesDemandTexture && !m_usesCheckerboard && !m_usesUnsupported ) - { - return std::string{}; - } - out << "\n"; - return out.str(); - } - - std::string functionDefinitions() const - { - std::ostringstream out; - for( std::vector::const_iterator it = m_functions.begin(); it != m_functions.end(); ++it ) - { - out << *it << "\n"; - } - return out.str(); - } - - private: - std::string textureReference( const std::string& textureName, const std::string& preferredValueType ) - { - const TextureLookup lookup{ findTexture( m_graph, textureName, preferredValueType ) }; - if( lookup.texture == nullptr ) - { - appendUnsupportedReason( m_result, "Missing PBRT texture '" + textureName + "'" ); - return unsupportedTextureExpression(); - } - if( contains( m_textureStack, lookup.graphKey ) ) - { - appendUnsupportedReason( m_result, "Recursive PBRT texture reference '" + textureName + "'" ); - return unsupportedTextureExpression(); - } - - std::map::const_iterator cached = m_textureFunctions.find( lookup.graphKey ); - if( cached != m_textureFunctions.end() ) - { - return cached->second + "()"; - } - - m_textureStack.push_back( lookup.graphKey ); - const std::string functionName{ defineTextureFunction( lookup.graphKey, *lookup.texture ) }; - m_textureStack.pop_back(); - m_textureFunctions.insert( std::make_pair( lookup.graphKey, functionName ) ); - return functionName + "()"; - } - - std::string defineTextureFunction( const std::string& /*graphKey*/, const otk::pbrt::PbrtTexture& texture ) - { - const std::string functionName{ "texture_" + std::to_string( m_nextTextureFunction++ ) }; - const std::string expression{ textureExpression( texture ) }; - - std::ostringstream out; - out << "// pbrt texture node: " << textureKind( texture ) << "\n"; - if( isDemandTexture( texture ) ) - { - out << "// demand texture parameter: texture_2d image_" << ( m_nextImageParameter - 1U ) << "\n"; - } - out << "color " << functionName << "() = " << expression << ";\n"; - m_functions.push_back( out.str() ); - return functionName; - } - - std::string textureExpression( const otk::pbrt::PbrtTexture& texture ) - { - if( texture.type == "imagemap" ) - { - return demandTextureExpression(); - } - if( texture.type == "constant" ) - { - return defaultColorExpression(); - } - if( texture.type == "scale" ) - { - const std::string tex1{ textureInputExpression( texture, "tex1", defaultColorExpression() ) }; - const std::string tex2{ textureInputExpression( texture, "tex2", defaultColorExpression() ) }; - return tex1 + " * " + tex2; - } - if( texture.type == "mix" ) - { - const std::string tex1{ textureInputExpression( texture, "tex1", defaultColorExpression() ) }; - const std::string tex2{ textureInputExpression( texture, "tex2", defaultColorExpression() ) }; - return tex1 + " * (1.0 - 0.5) + " + tex2 + " * 0.5"; - } - if( texture.type == "checkerboard" ) - { - if( !pbrtCheckerboardTextureKey( texture ).empty() ) - { - return demandTextureExpression(); - } - if( texture.params.FindOneString( "dimension", "2d" ) != "2d" ) - { - appendUnsupportedReason( m_result, "Unsupported PBRT checkerboard dimension in " + textureKind( texture ) ); - return unsupportedTextureExpression(); - } - m_usesCheckerboard = true; - const std::string tex1{ textureInputExpression( texture, "tex1", "color(1.0, 1.0, 1.0)" ) }; - const std::string tex2{ textureInputExpression( texture, "tex2", "color(0.0, 0.0, 0.0)" ) }; - return "pbrt_checkerboard_2d(" + tex1 + ", " + tex2 + ")"; - } - - if( isUnsupportedProceduralTexture( texture ) ) - { - appendUnsupportedReason( m_result, "Unsupported PBRT texture type " + textureKind( texture ) ); - return unsupportedTextureExpression(); - } - - appendUnsupportedReason( m_result, "Unsupported PBRT texture type " + textureKind( texture ) ); - return unsupportedTextureExpression(); - } - - std::string demandTextureExpression() - { - m_usesDemandTexture = true; - return "pbrt_demand_texture_2d(" + std::to_string( m_nextImageParameter++ ) + ")"; - } - - static bool isDemandTexture( const otk::pbrt::PbrtTexture& texture ) - { - return texture.type == "imagemap" || ( texture.type == "checkerboard" && !pbrtCheckerboardTextureKey( texture ).empty() ); - } - - bool isFoldableTextureReference( const std::string& textureName, const std::string& preferredValueType ) const - { - std::vector textureStack; - return isFoldableTextureReference( textureName, preferredValueType, textureStack ); - } - - bool isFoldableTextureReference( const std::string& textureName, - const std::string& preferredValueType, - std::vector& textureStack ) const - { - const TextureLookup lookup{ findTexture( m_graph, textureName, preferredValueType ) }; - if( lookup.texture == nullptr || contains( textureStack, lookup.graphKey ) ) - { - return false; - } - - textureStack.push_back( lookup.graphKey ); - const bool result{ isFoldableTexture( *lookup.texture, textureStack ) }; - textureStack.pop_back(); - return result; - } - - bool isFoldableTextureInput( const otk::pbrt::PbrtTexture& texture, const char* paramName, std::vector& textureStack ) const - { - const std::string textureName{ texture.params.FindTexture( paramName ) }; - return textureName.empty() || isFoldableTextureReference( textureName, texture.valueType, textureStack ); - } - - bool isFoldableTexture( const otk::pbrt::PbrtTexture& texture, std::vector& textureStack ) const - { - if( texture.type == "constant" ) - { - return true; - } - if( texture.type == "scale" ) - { - return isFoldableTextureInput( texture, "tex1", textureStack ) && isFoldableTextureInput( texture, "tex2", textureStack ); - } - if( texture.type == "mix" ) - { - return isFoldableTextureInput( texture, "tex1", textureStack ) && isFoldableTextureInput( texture, "tex2", textureStack ) - && isFoldableTextureInput( texture, "amount", textureStack ); - } - return false; - } - - std::string textureInputExpression( const otk::pbrt::PbrtTexture& texture, const std::string& paramName, const std::string& defaultExpression ) - { - const std::string textureName{ texture.params.FindTexture( paramName ) }; - if( textureName.empty() ) - { - return defaultExpression; - } - return textureReference( textureName, texture.valueType ); - } - - std::string unsupportedTextureExpression() - { - m_usesUnsupported = true; - return "pbrt_unsupported_texture()"; - } - - static std::string defaultColorExpression() { return "color(1.0, 1.0, 1.0)"; } - - const otk::pbrt::PbrtMaterialGraph& m_graph; - GeneratedMdlSource& m_result; - std::vector m_textureStack; - std::map m_textureFunctions; - std::vector m_functions; - unsigned int m_nextTextureFunction{}; - unsigned int m_nextImageParameter{}; - bool m_usesTextureFloat{}; - bool m_usesDemandTexture{}; - bool m_usesCheckerboard{}; - bool m_usesUnsupported{}; -}; - -struct MdlMaterialParameter -{ - std::string type; - std::string name; - std::string defaultValue; -}; - -struct MdlMaterialModel -{ - std::vector parameters; - std::vector comments; - std::string helperDefinitions; - std::string body; -}; - -std::string namedMaterialParameterName( unsigned int index, const std::string& paramName ); - -struct PbrtMaterialGapPolicy -{ - std::string type; - std::string policy; - std::string coverageReason; -}; - -struct BoundParameterSpec -{ - MdlBoundParameterType type; - const char* name; -}; - -struct FoldedColor -{ - float red{}; - float green{}; - float blue{}; -}; - -FoldedColor operator*( const FoldedColor& lhs, const FoldedColor& rhs ) -{ - return FoldedColor{ lhs.red * rhs.red, lhs.green * rhs.green, lhs.blue * rhs.blue }; -} - -FoldedColor mix( const FoldedColor& lhs, const FoldedColor& rhs, float amount ) -{ - return FoldedColor{ lhs.red * ( 1.0f - amount ) + rhs.red * amount, lhs.green * ( 1.0f - amount ) + rhs.green * amount, - lhs.blue * ( 1.0f - amount ) + rhs.blue * amount }; -} - -bool findConstantColor( const ::pbrt::ParamSet& params, const char* name, float& red, float& green, float& blue ) -{ - if( !params.FindTexture( name ).empty() ) - { - return false; - } - - int count{}; - const ::pbrt::Spectrum* values = params.FindSpectrum( name, &count ); - if( count <= 0 || values == nullptr ) - { - return false; - } - - float rgb[3]{}; - values[0].ToRGB( rgb ); - red = rgb[0]; - green = rgb[1]; - blue = rgb[2]; - return true; -} - -bool findConstantFloat( const ::pbrt::ParamSet& params, const char* name, float& value ) -{ - if( !params.FindTexture( name ).empty() ) - { - return false; - } - - int count{}; - const float* values = params.FindFloat( name, &count ); - if( count <= 0 || values == nullptr ) - { - return false; - } - - value = values[0]; - return true; -} - -bool scalarColorValue( const FoldedColor& color, float& value ) -{ - constexpr float epsilon{ 1.0e-6f }; - if( std::fabs( color.red - color.green ) > epsilon || std::fabs( color.red - color.blue ) > epsilon ) - { - return false; - } - value = color.red; - return true; -} - -bool promotesFloatToColorParameter( const char* name ) -{ - const std::string parameterName{ name }; - return parameterName == "opacity" || parameterName == "amount"; -} - -bool findTextureColorValue( const ::pbrt::ParamSet& params, const char* name, const FoldedColor& defaultValue, FoldedColor& value ) -{ - if( findConstantColor( params, name, value.red, value.green, value.blue ) ) - { - return true; - } - - float floatValue{}; - if( findConstantFloat( params, name, floatValue ) ) - { - value = FoldedColor{ floatValue, floatValue, floatValue }; - return true; - } - - value = defaultValue; - return true; -} - -bool findTextureFloatValue( const ::pbrt::ParamSet& params, const char* name, float defaultValue, float& value ) -{ - if( findConstantFloat( params, name, value ) ) - { - return true; - } - - FoldedColor color{}; - if( findConstantColor( params, name, color.red, color.green, color.blue ) ) - { - return scalarColorValue( color, value ); - } - - value = defaultValue; - return true; -} - -bool findFoldableTextureColor( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - const std::string& preferredValueType, - std::vector& textureStack, - FoldedColor& value ); - -bool findFoldableTextureFloat( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack, - float& value ); - -bool findTextureInputColor( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - const char* name, - const FoldedColor& defaultValue, - std::vector& textureStack, - FoldedColor& value ) -{ - const std::string inputTextureName{ texture.params.FindTexture( name ) }; - if( !inputTextureName.empty() ) - { - return findFoldableTextureColor( graph, inputTextureName, texture.valueType, textureStack, value ); - } - return findTextureColorValue( texture.params, name, defaultValue, value ); -} - -bool findTextureInputFloat( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - const char* name, - float defaultValue, - std::vector& textureStack, - float& value ) -{ - const std::string inputTextureName{ texture.params.FindTexture( name ) }; - if( !inputTextureName.empty() ) - { - return findFoldableTextureFloat( graph, inputTextureName, textureStack, value ); - } - return findTextureFloatValue( texture.params, name, defaultValue, value ); -} - -bool findFoldableTextureColor( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - const std::string& preferredValueType, - std::vector& textureStack, - FoldedColor& value ) -{ - const TextureLookup lookup{ findTexture( graph, textureName, preferredValueType ) }; - if( lookup.texture == nullptr || contains( textureStack, lookup.graphKey ) ) - { - return false; - } - - textureStack.push_back( lookup.graphKey ); - const otk::pbrt::PbrtTexture& texture{ *lookup.texture }; - bool folded{ false }; - if( texture.type == "constant" ) - { - folded = findTextureColorValue( texture.params, "value", FoldedColor{ 1.0f, 1.0f, 1.0f }, value ); - } - else if( texture.type == "scale" ) - { - FoldedColor tex1{}; - FoldedColor tex2{}; - folded = findTextureInputColor( graph, texture, "tex1", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex1 ) - && findTextureInputColor( graph, texture, "tex2", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex2 ); - if( folded ) - { - value = tex1 * tex2; - } - } - else if( texture.type == "mix" ) - { - FoldedColor tex1{}; - FoldedColor tex2{}; - float amount{}; - folded = findTextureInputColor( graph, texture, "tex1", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex1 ) - && findTextureInputColor( graph, texture, "tex2", FoldedColor{ 1.0f, 1.0f, 1.0f }, textureStack, tex2 ) - && findTextureInputFloat( graph, texture, "amount", 0.5f, textureStack, amount ); - if( folded ) - { - value = mix( tex1, tex2, amount ); - } - } - - textureStack.pop_back(); - return folded; -} - -bool findFoldableTextureFloat( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack, - float& value ) -{ - const TextureLookup lookup{ findTexture( graph, textureName, "float" ) }; - if( lookup.texture == nullptr || contains( textureStack, lookup.graphKey ) ) - { - return false; - } - - textureStack.push_back( lookup.graphKey ); - const otk::pbrt::PbrtTexture& texture{ *lookup.texture }; - bool folded{ false }; - if( texture.type == "constant" ) - { - folded = findTextureFloatValue( texture.params, "value", 1.0f, value ); - } - else if( texture.type == "scale" ) - { - float tex1{}; - float tex2{}; - folded = findTextureInputFloat( graph, texture, "tex1", 1.0f, textureStack, tex1 ) - && findTextureInputFloat( graph, texture, "tex2", 1.0f, textureStack, tex2 ); - if( folded ) - { - value = tex1 * tex2; - } - } - else if( texture.type == "mix" ) - { - float tex1{}; - float tex2{}; - float amount{}; - folded = findTextureInputFloat( graph, texture, "tex1", 1.0f, textureStack, tex1 ) - && findTextureInputFloat( graph, texture, "tex2", 1.0f, textureStack, tex2 ) - && findTextureInputFloat( graph, texture, "amount", 0.5f, textureStack, amount ); - if( folded ) - { - value = tex1 * ( 1.0f - amount ) + tex2 * amount; - } - } - - textureStack.pop_back(); - return folded; -} - -bool findFoldableTextureColor( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - const std::string& preferredValueType, - FoldedColor& value ) -{ - std::vector textureStack; - return findFoldableTextureColor( graph, textureName, preferredValueType, textureStack, value ); -} - -bool findFoldableTextureFloat( const otk::pbrt::PbrtMaterialGraph& graph, const std::string& textureName, float& value ) -{ - std::vector textureStack; - return findFoldableTextureFloat( graph, textureName, textureStack, value ); -} - -void appendBoundParameter( std::vector& result, const ::pbrt::ParamSet& params, const BoundParameterSpec& spec ) -{ - MdlBoundMaterialParameter parameter{}; - parameter.name = spec.name; - parameter.type = spec.type; - if( spec.type == MdlBoundParameterType::COLOR ) - { - if( findConstantColor( params, spec.name, parameter.red, parameter.green, parameter.blue ) ) - { - result.push_back( parameter ); - } - else if( promotesFloatToColorParameter( spec.name ) && findConstantFloat( params, spec.name, parameter.value ) ) - { - parameter.red = parameter.green = parameter.blue = parameter.value; - result.push_back( parameter ); - } - return; - } - - if( findConstantFloat( params, spec.name, parameter.value ) ) - { - result.push_back( parameter ); - return; - } -} - -void appendTextureBackedBoundParameter( std::vector& result, - const otk::pbrt::PbrtMaterial& material, - const BoundParameterSpec& spec ) -{ - const std::string textureName{ material.params.FindTexture( spec.name ) }; - if( textureName.empty() ) - { - return; - } - - MdlBoundMaterialParameter parameter{}; - parameter.name = spec.name; - parameter.type = spec.type; - if( spec.type == MdlBoundParameterType::COLOR ) - { - FoldedColor value{}; - if( findFoldableTextureColor( material.graph, textureName, "color", value ) ) - { - parameter.red = value.red; - parameter.green = value.green; - parameter.blue = value.blue; - result.push_back( parameter ); - } - return; - } - - if( findFoldableTextureFloat( material.graph, textureName, parameter.value ) ) - { - result.push_back( parameter ); - } -} - -void appendBoundParameters( std::vector& result, - const ::pbrt::ParamSet& params, - const BoundParameterSpec* begin, - const BoundParameterSpec* end ) -{ - for( const BoundParameterSpec* it = begin; it != end; ++it ) - { - appendBoundParameter( result, params, *it ); - } -} - -void appendMaterialBoundParameters( std::vector& result, - const otk::pbrt::PbrtMaterial& material, - const BoundParameterSpec* begin, - const BoundParameterSpec* end ) -{ - for( const BoundParameterSpec* it = begin; it != end; ++it ) - { - appendBoundParameter( result, material.params, *it ); - appendTextureBackedBoundParameter( result, material, *it ); - } -} - -void appendNamedBoundParameter( std::vector& result, - const ::pbrt::ParamSet& params, - unsigned int index, - const BoundParameterSpec& spec ) -{ - MdlBoundMaterialParameter parameter{}; - parameter.name = namedMaterialParameterName( index, spec.name ); - parameter.type = spec.type; - if( spec.type == MdlBoundParameterType::COLOR ) - { - if( findConstantColor( params, spec.name, parameter.red, parameter.green, parameter.blue ) ) - { - result.push_back( parameter ); - } - return; - } - - if( findConstantFloat( params, spec.name, parameter.value ) ) - { - result.push_back( parameter ); - } -} - -void appendNamedBoundParameters( std::vector& result, - const ::pbrt::ParamSet& params, - unsigned int index, - const BoundParameterSpec* begin, - const BoundParameterSpec* end ) -{ - for( const BoundParameterSpec* it = begin; it != end; ++it ) - { - appendNamedBoundParameter( result, params, index, *it ); - } -} - -void appendMaterialParameter( MdlMaterialModel& model, const std::string& type, const std::string& name, const std::string& defaultValue ) -{ - model.parameters.push_back( MdlMaterialParameter{ type, name, defaultValue } ); -} - -const PbrtMaterialGapPolicy* explicitMaterialGapPolicy( const std::string& type ) -{ - static const PbrtMaterialGapPolicy policies[] = { - { "fourier", "unsupported with visible fallback", - "PBRT Fourier tables are data-driven BSDF resources found in the corpus; DemandPbrtScene preserves the " - "resource metadata but does not yet evaluate the Fourier table on the GPU" }, - { "hair", "unsupported with visible fallback", - "low-frequency PBRT corpus material; no current target scene or reference fixture requires approximation" }, - { "measured", "unsupported with visible fallback", - "PBRT parity completeness gap; current corpus sample did not find a target scene requiring support" }, - }; - - for( const PbrtMaterialGapPolicy& policy : policies ) - { - if( policy.type == type ) - { - return &policy; - } - } - return nullptr; -} - -bool hasFourierBsdfFile( const otk::pbrt::PbrtMaterial& material ) -{ - return !material.params.FindOneString( "bsdffile", std::string{} ).empty(); -} - -void appendRoughnessGapComment( MdlMaterialModel& model ) -{ - model.comments.push_back( "pbrt material gap: PBRT-exact roughness/remapping behavior is approximated" ); -} - -std::string mdlParameterList( const std::vector& parameters ) -{ - if( parameters.empty() ) - { - return "()"; - } - - std::ostringstream out; - out << "(\n"; - for( std::vector::const_iterator it = parameters.begin(); it != parameters.end(); ++it ) - { - out << " " << it->type << " " << it->name << " = " << it->defaultValue; - if( it + 1 != parameters.end() ) - { - out << ","; - } - out << "\n"; - } - out << ")"; - return out.str(); -} - -std::string materialTextureCommentExpression( MdlTextureGraphGenerator& textureGraph, - const ::pbrt::ParamSet& params, - const std::string& paramName, - const std::string& preferredValueType ) -{ - if( params.FindTexture( paramName ).empty() ) - { - return "none"; - } - return textureGraph.materialColorExpression( params, paramName, preferredValueType, "none" ); -} - -std::string materialBumpmapExpression( MdlTextureGraphGenerator& textureGraph, const ::pbrt::ParamSet& params ) -{ - if( params.FindTexture( "bumpmap" ).empty() ) - { - return "none"; - } - return textureGraph.materialFloatExpression( params, "bumpmap", "float", "0.0" ); -} - -bool hasBumpmapExpression( const std::string& bumpmap ) -{ - return bumpmap != "none"; -} - -void appendBumpmapCommentsAndHelpers( MdlMaterialModel& model, const std::string& bumpmap ) -{ - model.comments.push_back( "pbrt material input bumpmap: " + bumpmap ); - if( !hasBumpmapExpression( bumpmap ) ) - { - return; - } - - model.comments.push_back( "pbrt material implementation: bumpmap is evaluated with runtime finite differences" ); -} - -std::string materialGeometryExpression( const std::string& cutoutOpacity, const std::string& bumpmap ) -{ - (void)bumpmap; - if( cutoutOpacity.empty() ) - { - return std::string{}; - } - - std::ostringstream out; - out << " geometry: material_geometry(\n"; - out << " cutout_opacity: " << cutoutOpacity << "\n"; - out << " )\n"; - return out.str(); -} - -std::string namedMaterialParameterName( unsigned int index, const std::string& paramName ) -{ - return "named_" + std::to_string( index ) + "_" + paramName; -} - -std::string namedMaterialColorExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index, - const std::string& paramName, - const std::string& defaultValue ) -{ - const std::string parameterName{ namedMaterialParameterName( index, paramName ) }; - appendMaterialParameter( model, "color", parameterName, defaultValue ); - return textureGraph.materialColorExpression( material.params, paramName, "color", parameterName ); -} - -std::string namedMaterialFloatExpression( MdlMaterialModel& model, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index, - const std::string& paramName, - const std::string& defaultValue ) -{ - const std::string parameterName{ namedMaterialParameterName( index, paramName ) }; - appendMaterialParameter( model, "float", parameterName, defaultValue ); - if( !material.params.FindTexture( paramName ).empty() ) - { - return defaultValue; - } - return parameterName; -} - -std::string namedMaterialType( const otk::pbrt::PbrtNamedMaterial& material ) -{ - if( !material.type.empty() ) - { - return material.type; - } - return material.params.FindOneString( "type", std::string{} ); -} - -std::string namedMaterialMatteBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string kd{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; - const std::string sigma{ namedMaterialFloatExpression( model, material, index, "sigma", "0.0" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input sigma: " + sigma ); - return "::df::diffuse_reflection_bsdf(\n" - " tint: " - + kd - + ",\n" - " roughness: pbrt_mix_matte_sigma_roughness(" - + sigma + "))"; -} - -std::string namedMaterialPlasticBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string kd{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; - const std::string ks{ - namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.0, 0.0, 0.0)" ) }; - const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); - return "::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: " - + kd - + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + ks - + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: " - + roughness - + ",\n" - " roughness_v: " - + roughness - + ",\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect))))"; -} - -std::string namedMaterialSubstrateBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string kd{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.5, 0.5, 0.5)" ) }; - const std::string ks{ - namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.5, 0.5, 0.5)" ) }; - const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; - const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "-1.0" ) }; - const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "-1.0" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); - return "::df::color_weighted_layer(\n" - " weight: " - + ks - + ",\n" - " layer: ::df::simple_glossy_bsdf(\n" - " roughness_u: pbrt_mix_resolved_roughness(" - + roughness + ", " + uroughness - + "),\n" - " roughness_v: pbrt_mix_resolved_roughness(" - + roughness + ", " + vroughness - + "),\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect),\n" - " base: ::df::diffuse_reflection_bsdf(\n" - " tint: " - + kd + "))"; -} - -std::string namedMaterialUberBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string kd{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; - const std::string ks{ - namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.0, 0.0, 0.0)" ) }; - const std::string kr{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kr", "color(0.0, 0.0, 0.0)" ) }; - const std::string kt{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kt", "color(0.0, 0.0, 0.0)" ) }; - const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; - const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "-1.0" ) }; - const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "-1.0" ) }; - const std::string alpha{ namedMaterialFloatExpression( model, material, index, "alpha", "1.0" ) }; - const std::string opacity{ namedMaterialFloatExpression( model, material, index, "opacity", "1.0" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kr: " + kr ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kt: " + kt ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input opacity: " + opacity ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input alpha: " + alpha - + "; cutout does not compose through mix" ); - return std::string{ "::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: " } - + "pbrt_mix_opacity_weight(" + opacity + ") * " + kd - + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + "pbrt_mix_opacity_weight(" + opacity + ") * " + ks - + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: pbrt_mix_resolved_roughness(" - + roughness + ", " + uroughness - + "),\n" - " roughness_v: pbrt_mix_resolved_roughness(" - + roughness + ", " + vroughness - + "),\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect)),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + "pbrt_mix_opacity_weight(" + opacity + ") * " + kr - + ",\n" - " component: ::df::specular_bsdf(\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect)),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + "pbrt_mix_opacity_weight(" + opacity + ") * " + kt - + ",\n" - " component: ::df::specular_bsdf(\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_transmit)),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_mix_transparency_weight(" - + opacity - + "),\n" - " component: ::df::specular_bsdf(\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_transmit))))"; -} - -std::string namedMaterialMirrorBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string kr{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kr", "color(1.0, 1.0, 1.0)" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kr: " + kr ); - return "::df::specular_bsdf(\n" - " tint: " - + kr - + ",\n" - " mode: ::df::scatter_reflect)"; -} - -std::string namedMaterialGlassBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string kr{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kr", "color(1.0, 1.0, 1.0)" ) }; - const std::string kt{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kt", "color(1.0, 1.0, 1.0)" ) }; - const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.0" ) }; - const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "0.0" ) }; - const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "0.0" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kr: " + kr ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kt: " + kt ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); - return "::df::tint(\n" - " " - + kr - + ",\n" - " " - + kt - + ",\n" - " ::df::microfacet_ggx_smith_bsdf(\n" - " roughness_u: pbrt_mix_resolved_roughness(" - + roughness + ", " + uroughness - + "),\n" - " roughness_v: pbrt_mix_resolved_roughness(" - + roughness + ", " + vroughness - + "),\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect_transmit))"; -} - -std::string namedMaterialMetalBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string eta{ - namedMaterialColorExpression( model, textureGraph, material, index, "eta", "color(0.2, 0.2, 0.2)" ) }; - const std::string k{ - namedMaterialColorExpression( model, textureGraph, material, index, "k", "color(3.0, 3.0, 3.0)" ) }; - const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; - const std::string uroughness{ namedMaterialFloatExpression( model, material, index, "uroughness", "-1.0" ) }; - const std::string vroughness{ namedMaterialFloatExpression( model, material, index, "vroughness", "-1.0" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input eta: " + eta ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input k: " + k ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input uroughness: " + uroughness ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input vroughness: " + vroughness ); - return "::df::microfacet_ggx_smith_bsdf(\n" - " roughness_u: pbrt_mix_resolved_roughness(" - + roughness + ", " + uroughness - + "),\n" - " roughness_v: pbrt_mix_resolved_roughness(" - + roughness + ", " + vroughness - + "),\n" - " tint: pbrt_mix_metal_conductor_tint(" - + eta + ", " + k - + "),\n" - " mode: ::df::scatter_reflect)"; -} - -std::string namedMaterialTranslucentBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string kd{ - namedMaterialColorExpression( model, textureGraph, material, index, "Kd", "color(0.8, 0.8, 0.8)" ) }; - const std::string ks{ - namedMaterialColorExpression( model, textureGraph, material, index, "Ks", "color(0.0, 0.0, 0.0)" ) }; - const std::string reflect{ - namedMaterialColorExpression( model, textureGraph, material, index, "reflect", "color(0.5, 0.5, 0.5)" ) }; - const std::string transmit{ - namedMaterialColorExpression( model, textureGraph, material, index, "transmit", "color(0.5, 0.5, 0.5)" ) }; - const std::string roughness{ namedMaterialFloatExpression( model, material, index, "roughness", "0.1" ) }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Kd: " + kd ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input Ks: " + ks ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input reflect: " + reflect ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input transmit: " + transmit ); - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " input roughness: " + roughness ); - return "::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: " - + kd + " * " + reflect - + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + kd + " * " + transmit - + ",\n" - " component: ::df::diffuse_transmission_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + ks + " * " + reflect - + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: " - + roughness - + ",\n" - " roughness_v: " - + roughness - + ",\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect)),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + ks + " * " + transmit - + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: " - + roughness - + ",\n" - " roughness_v: " - + roughness - + ",\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_transmit))))"; -} - -std::string unsupportedNamedMaterialBsdfExpression() -{ - return "::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 0.0, 1.0))"; -} - -std::string namedMaterialBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - GeneratedMdlSource& result, - const otk::pbrt::PbrtNamedMaterial& material, - unsigned int index ) -{ - const std::string type{ namedMaterialType( material ) }; - const std::string typeComment{ type.empty() ? std::string{ "" } : type }; - model.comments.push_back( "pbrt named material " + std::to_string( index ) + " model: " + typeComment ); - - if( type == "matte" ) - { - return namedMaterialMatteBsdfExpression( model, textureGraph, material, index ); - } - if( type == "plastic" ) - { - return namedMaterialPlasticBsdfExpression( model, textureGraph, material, index ); - } - if( type == "substrate" ) - { - return namedMaterialSubstrateBsdfExpression( model, textureGraph, material, index ); - } - if( type == "uber" ) - { - return namedMaterialUberBsdfExpression( model, textureGraph, material, index ); - } - if( type == "mirror" ) - { - return namedMaterialMirrorBsdfExpression( model, textureGraph, material, index ); - } - if( type == "glass" ) - { - return namedMaterialGlassBsdfExpression( model, textureGraph, material, index ); - } - if( type == "metal" ) - { - return namedMaterialMetalBsdfExpression( model, textureGraph, material, index ); - } - if( type == "translucent" ) - { - return namedMaterialTranslucentBsdfExpression( model, textureGraph, material, index ); - } - - appendUnsupportedReason( result, "Unsupported PBRT named material type " + typeComment ); - return unsupportedNamedMaterialBsdfExpression(); -} - -MdlMaterialModel makeMatteMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); - appendMaterialParameter( model, "float", "sigma", "0.0" ); - appendMaterialParameter( model, "float", "alpha", "1.0" ); - appendMaterialParameter( model, "float", "opacity", "1.0" ); - - const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; - const std::string alphaTexture{ - materialTextureCommentExpression( textureGraph, material.params, "alpha", "float" ) }; - const std::string shadowAlphaTexture{ - materialTextureCommentExpression( textureGraph, material.params, "shadowalpha", "float" ) }; - const std::string opacityTexture{ - materialTextureCommentExpression( textureGraph, material.params, "opacity", "float" ) }; - const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; - - model.comments.push_back( "pbrt material model: matte" ); - model.comments.push_back( "pbrt material input Kd: " + kd ); - model.comments.push_back( "pbrt material input sigma: sigma" ); - model.comments.push_back( "pbrt material approximation: sigma degrees map to MDL Oren-Nayar roughness sigma / 90" ); - model.comments.push_back( "pbrt material input alpha: alpha; texture=" + alphaTexture ); - model.comments.push_back( "pbrt material input shadowalpha: any-hit texture=" + shadowAlphaTexture ); - model.comments.push_back( "pbrt material input opacity: opacity; texture=" + opacityTexture ); - appendBumpmapCommentsAndHelpers( model, bumpmap ); - model.helperDefinitions = - "float pbrt_matte_sigma_roughness(float sigma_degrees) = ::math::clamp(sigma_degrees / 90.0, 0.0, 1.0);\n\n" - + model.helperDefinitions; - model.body = - " surface: material_surface(\n" - " scattering: ::df::diffuse_reflection_bsdf(\n" - " tint: " - + kd + ",\n" - " roughness: pbrt_matte_sigma_roughness(sigma))),\n" - + materialGeometryExpression( "alpha * opacity", bumpmap ); - return model; -} - -MdlMaterialModel makePlasticMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); - appendMaterialParameter( model, "color", "Ks", "color(0.0, 0.0, 0.0)" ); - appendMaterialParameter( model, "float", "roughness", "0.1" ); - - const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; - const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; - const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; - - model.comments.push_back( "pbrt material model: plastic" ); - model.comments.push_back( "pbrt material input Kd: " + kd ); - model.comments.push_back( "pbrt material input Ks: " + ks ); - model.comments.push_back( "pbrt material input roughness: roughness" ); - appendBumpmapCommentsAndHelpers( model, bumpmap ); - appendRoughnessGapComment( model ); - model.comments.push_back( - "pbrt material approximation: diffuse and glossy reflection use an MDL color-normalized mix" ); - model.body = - " surface: material_surface(\n" - " scattering: ::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: " + kd + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " + ks + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: roughness,\n" - " roughness_v: roughness,\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect)))))" - + "\n"; - return model; -} - -MdlMaterialModel makeUberMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); - appendMaterialParameter( model, "color", "Ks", "color(0.0, 0.0, 0.0)" ); - appendMaterialParameter( model, "color", "Kr", "color(0.0, 0.0, 0.0)" ); - appendMaterialParameter( model, "color", "Kt", "color(0.0, 0.0, 0.0)" ); - appendMaterialParameter( model, "float", "roughness", "0.1" ); - appendMaterialParameter( model, "float", "uroughness", "-1.0" ); - appendMaterialParameter( model, "float", "vroughness", "-1.0" ); - appendMaterialParameter( model, "float", "index", "1.5" ); - appendMaterialParameter( model, "float", "alpha", "1.0" ); - appendMaterialParameter( model, "color", "opacity", "color(1.0, 1.0, 1.0)" ); - - const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; - const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; - const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; - const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; - const std::string alphaTexture{ - materialTextureCommentExpression( textureGraph, material.params, "alpha", "float" ) }; - const std::string opacityTexture{ - materialTextureCommentExpression( textureGraph, material.params, "opacity", "float" ) }; - const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; - - model.comments.push_back( "pbrt material model: uber" ); - model.comments.push_back( "pbrt material input Kd: " + kd ); - model.comments.push_back( "pbrt material input Ks: " + ks ); - model.comments.push_back( "pbrt material input Kr: " + kr ); - model.comments.push_back( "pbrt material input Kt: " + kt ); - model.comments.push_back( "pbrt material input roughness: roughness" ); - model.comments.push_back( "pbrt material input uroughness: uroughness" ); - model.comments.push_back( "pbrt material input vroughness: vroughness" ); - model.comments.push_back( "pbrt material input index: index" ); - model.comments.push_back( "pbrt material input alpha: alpha; texture=" + alphaTexture ); - model.comments.push_back( "pbrt material input opacity: opacity; texture=" + opacityTexture ); - appendBumpmapCommentsAndHelpers( model, bumpmap ); - appendRoughnessGapComment( model ); - model.comments.push_back( "pbrt material approximation: PBRT uber lobes use an MDL color-normalized mix" ); - model.comments.push_back( - "pbrt material approximation: spectrum opacity weights BSDF lobes and adds transparent transmission; alpha " - "remains " - "cutout" ); - model.helperDefinitions = - "float pbrt_uber_resolved_roughness(float roughness, float axis_roughness) = " - "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" - "color pbrt_uber_clamped_opacity(color opacity) = " - "::math::clamp(opacity, color(0.0, 0.0, 0.0), color(1.0, 1.0, 1.0));\n\n" - "color pbrt_uber_opacity_weight(color opacity) = pbrt_uber_clamped_opacity(opacity);\n\n" - "color pbrt_uber_transparency_weight(color opacity) = " - "color(1.0, 1.0, 1.0) - pbrt_uber_clamped_opacity(opacity);\n\n" - + model.helperDefinitions; - model.body = std::string{ " ior: color(index, index, index),\n" - " surface: material_surface(\n" - " scattering: ::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: " } - + "pbrt_uber_opacity_weight(opacity) * " + kd - + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " - + "pbrt_uber_opacity_weight(opacity) * " + ks - + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: pbrt_uber_resolved_roughness(roughness, uroughness),\n" - " roughness_v: pbrt_uber_resolved_roughness(roughness, vroughness),\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect)),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_uber_opacity_weight(opacity) * " - + kr - + ",\n" - " component: ::df::specular_bsdf(\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect)),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_uber_opacity_weight(opacity) * " - + kt - + ",\n" - " component: ::df::specular_bsdf(\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_transmit)),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_uber_transparency_weight(opacity),\n" - " component: ::df::specular_bsdf(\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_transmit))))),\n" - + materialGeometryExpression( "alpha", bumpmap ); - return model; -} - -MdlMaterialModel makeMirrorMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); - - const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; - - model.comments.push_back( "pbrt material model: mirror" ); - model.comments.push_back( "pbrt material input Kr: " + kr ); - model.body = - " surface: material_surface(\n" - " scattering: ::df::specular_bsdf(\n" - " tint: " + kr + ",\n" - " mode: ::df::scatter_reflect))\n"; - return model; -} - -MdlMaterialModel makeGlassMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); - appendMaterialParameter( model, "color", "Kt", "color(1.0, 1.0, 1.0)" ); - appendMaterialParameter( model, "float", "index", "1.5" ); - appendMaterialParameter( model, "float", "roughness", "0.0" ); - appendMaterialParameter( model, "float", "uroughness", "0.0" ); - appendMaterialParameter( model, "float", "vroughness", "0.0" ); - - const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; - const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; - - model.comments.push_back( "pbrt material model: glass" ); - model.comments.push_back( "pbrt material input Kr: " + kr ); - model.comments.push_back( "pbrt material input Kt: " + kt ); - model.comments.push_back( "pbrt material input index/eta: index" ); - model.comments.push_back( "pbrt material input roughness: roughness" ); - model.comments.push_back( "pbrt material input uroughness: uroughness" ); - model.comments.push_back( "pbrt material input vroughness: vroughness" ); - model.comments.push_back( "pbrt material approximation: rough glass uses an MDL GGX microfacet dielectric lobe" ); - appendRoughnessGapComment( model ); - model.helperDefinitions = - "float pbrt_glass_resolved_roughness(float roughness, float axis_roughness) = " - "axis_roughness > 0.0 ? axis_roughness : roughness;\n\n"; - model.body = - " ior: color(index, index, index),\n" - " surface: material_surface(\n" - " scattering: ::df::tint(\n" - " " - + kr + ",\n" - " " + kt - + ",\n" - " ::df::microfacet_ggx_smith_bsdf(\n" - " roughness_u: pbrt_glass_resolved_roughness(roughness, uroughness),\n" - " roughness_v: pbrt_glass_resolved_roughness(roughness, vroughness),\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect_transmit)))\n"; - return model; -} - -MdlMaterialModel makeMetalMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "eta", "color(0.2, 0.2, 0.2)" ); - appendMaterialParameter( model, "color", "k", "color(3.0, 3.0, 3.0)" ); - appendMaterialParameter( model, "float", "roughness", "0.1" ); - appendMaterialParameter( model, "float", "uroughness", "-1.0" ); - appendMaterialParameter( model, "float", "vroughness", "-1.0" ); - - const std::string eta{ textureGraph.materialColorExpression( material.params, "eta", "color", "eta" ) }; - const std::string k{ textureGraph.materialColorExpression( material.params, "k", "color", "k" ) }; - - model.comments.push_back( "pbrt material model: metal" ); - model.comments.push_back( "pbrt material input eta: " + eta ); - model.comments.push_back( "pbrt material input k: " + k ); - model.comments.push_back( "pbrt material input roughness: roughness" ); - model.comments.push_back( "pbrt material input uroughness: uroughness" ); - model.comments.push_back( "pbrt material input vroughness: vroughness" ); - model.comments.push_back( "pbrt material gap: PBRT-exact spectral conductor behavior is approximated" ); - appendRoughnessGapComment( model ); - model.comments.push_back( - "pbrt material approximation: RGB eta/k maps to MDL microfacet tint using normal-incidence conductor " - "reflectance" ); - model.helperDefinitions = - "float pbrt_metal_resolved_roughness(float roughness, float axis_roughness) = " - "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" - "color pbrt_metal_conductor_tint(color eta, color k) =\n" - " ((eta - color(1.0, 1.0, 1.0)) * (eta - color(1.0, 1.0, 1.0)) + k * k) /\n" - " ((eta + color(1.0, 1.0, 1.0)) * (eta + color(1.0, 1.0, 1.0)) + k * k);\n\n"; - model.body = - " surface: material_surface(\n" - " scattering: ::df::microfacet_ggx_smith_bsdf(\n" - " roughness_u: pbrt_metal_resolved_roughness(roughness, uroughness),\n" - " roughness_v: pbrt_metal_resolved_roughness(roughness, vroughness),\n" - " tint: pbrt_metal_conductor_tint(" - + eta + ", " + k - + "),\n" - " mode: ::df::scatter_reflect))\n"; - return model; -} - -MdlMaterialModel makeSubstrateMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kd", "color(0.5, 0.5, 0.5)" ); - appendMaterialParameter( model, "color", "Ks", "color(0.5, 0.5, 0.5)" ); - appendMaterialParameter( model, "float", "roughness", "0.1" ); - appendMaterialParameter( model, "float", "uroughness", "-1.0" ); - appendMaterialParameter( model, "float", "vroughness", "-1.0" ); - - const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; - const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; - const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; - - model.comments.push_back( "pbrt material model: substrate" ); - model.comments.push_back( "pbrt material input Kd: " + kd ); - model.comments.push_back( "pbrt material input Ks: " + ks ); - model.comments.push_back( "pbrt material input roughness: roughness" ); - model.comments.push_back( "pbrt material input uroughness: uroughness" ); - model.comments.push_back( "pbrt material input vroughness: vroughness" ); - appendBumpmapCommentsAndHelpers( model, bumpmap ); - appendRoughnessGapComment( model ); - model.comments.push_back( - "pbrt material approximation: diffuse base and glossy layer use an MDL color-weighted layer" ); - model.helperDefinitions = - "float pbrt_substrate_resolved_roughness(float roughness, float axis_roughness) = " - "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" - + model.helperDefinitions; - model.body = - " surface: material_surface(\n" - " scattering: ::df::color_weighted_layer(\n" - " weight: " + ks + ",\n" - " layer: ::df::simple_glossy_bsdf(\n" - " roughness_u: pbrt_substrate_resolved_roughness(roughness, uroughness),\n" - " roughness_v: pbrt_substrate_resolved_roughness(roughness, vroughness),\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect),\n" - " base: ::df::diffuse_reflection_bsdf(\n" - " tint: " - + kd + ")))" - + "\n"; - return model; -} - -MdlMaterialModel makeTranslucentMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kd", "color(0.8, 0.8, 0.8)" ); - appendMaterialParameter( model, "color", "Ks", "color(0.0, 0.0, 0.0)" ); - appendMaterialParameter( model, "color", "reflect", "color(0.5, 0.5, 0.5)" ); - appendMaterialParameter( model, "color", "transmit", "color(0.5, 0.5, 0.5)" ); - appendMaterialParameter( model, "float", "roughness", "0.1" ); - appendMaterialParameter( model, "color", "opacity", "color(1.0, 1.0, 1.0)" ); - - const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; - const std::string ks{ textureGraph.materialColorExpression( material.params, "Ks", "color", "Ks" ) }; - const std::string reflect{ textureGraph.materialColorExpression( material.params, "reflect", "color", "reflect" ) }; - const std::string transmit{ - textureGraph.materialColorExpression( material.params, "transmit", "color", "transmit" ) }; - const std::string opacityTexture{ - materialTextureCommentExpression( textureGraph, material.params, "opacity", "float" ) }; - const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; - - model.comments.push_back( "pbrt material model: translucent" ); - model.comments.push_back( "pbrt material input Kd: " + kd ); - model.comments.push_back( "pbrt material input Ks: " + ks ); - model.comments.push_back( "pbrt material input reflect: " + reflect ); - model.comments.push_back( "pbrt material input transmit: " + transmit ); - model.comments.push_back( "pbrt material input roughness: roughness" ); - model.comments.push_back( "pbrt material input opacity: opacity; texture=" + opacityTexture ); - appendBumpmapCommentsAndHelpers( model, bumpmap ); - model.comments.push_back( "pbrt material input eta: fixed 1.5" ); - appendRoughnessGapComment( model ); - model.comments.push_back( - "pbrt material approximation: diffuse/glossy reflection and transmission use an MDL color-normalized mix" ); - model.comments.push_back( - "pbrt material approximation: spectrum opacity weights generated translucent lobes and adds transparent " - "transmission" ); - model.helperDefinitions = - "color pbrt_translucent_clamped_opacity(color opacity) = " - "::math::clamp(opacity, color(0.0, 0.0, 0.0), color(1.0, 1.0, 1.0));\n\n" - "color pbrt_translucent_opacity_weight(color opacity) = pbrt_translucent_clamped_opacity(opacity);\n\n" - "color pbrt_translucent_transparency_weight(color opacity) = " - "color(1.0, 1.0, 1.0) - pbrt_translucent_clamped_opacity(opacity);\n\n" - + model.helperDefinitions; - model.body = - " ior: color(1.5, 1.5, 1.5),\n" - " surface: material_surface(\n" - " scattering: ::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_translucent_opacity_weight(opacity) * " + kd + " * " + reflect + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_translucent_opacity_weight(opacity) * " + kd + " * " + transmit + ",\n" - " component: ::df::diffuse_transmission_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_translucent_opacity_weight(opacity) * " + ks + " * " + reflect + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: roughness,\n" - " roughness_v: roughness,\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_reflect)),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_translucent_opacity_weight(opacity) * " + ks + " * " + transmit + ",\n" - " component: ::df::simple_glossy_bsdf(\n" - " roughness_u: roughness,\n" - " roughness_v: roughness,\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_transmit)),\n" - " ::df::color_bsdf_component(\n" - " weight: pbrt_translucent_transparency_weight(opacity),\n" - " component: ::df::specular_bsdf(\n" - " tint: color(1.0, 1.0, 1.0),\n" - " mode: ::df::scatter_transmit)))))" - + ( hasBumpmapExpression( bumpmap ) ? std::string{ ",\n" } + materialGeometryExpression( "", bumpmap ) : "\n" ); - return model; -} - -MdlMaterialModel makeSubsurfaceMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); - appendMaterialParameter( model, "color", "Kt", "color(1.0, 1.0, 1.0)" ); - appendMaterialParameter( model, "color", "sigma_a", "color(0.0011, 0.0024, 0.014)" ); - appendMaterialParameter( model, "color", "sigma_s", "color(2.55, 3.21, 3.77)" ); - appendMaterialParameter( model, "float", "scale", "1.0" ); - appendMaterialParameter( model, "float", "g", "0.0" ); - appendMaterialParameter( model, "float", "eta", "1.33" ); - appendMaterialParameter( model, "float", "uroughness", "0.0" ); - appendMaterialParameter( model, "float", "vroughness", "0.0" ); - - const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; - const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; - const std::string sigmaA{ textureGraph.materialColorExpression( material.params, "sigma_a", "color", "sigma_a" ) }; - const std::string sigmaS{ textureGraph.materialColorExpression( material.params, "sigma_s", "color", "sigma_s" ) }; - const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; - const std::string albedo{ "pbrt_subsurface_albedo(" + sigmaA + ", " + sigmaS + ", scale)" }; - - model.comments.push_back( "pbrt material model: subsurface" ); - model.comments.push_back( "pbrt material input Kr: " + kr ); - model.comments.push_back( "pbrt material input Kt: " + kt ); - model.comments.push_back( "pbrt material input sigma_a: " + sigmaA ); - model.comments.push_back( "pbrt material input sigma_s: " + sigmaS ); - model.comments.push_back( "pbrt material input scale: scale" ); - model.comments.push_back( "pbrt material input g: g" ); - model.comments.push_back( "pbrt material input eta: eta" ); - model.comments.push_back( "pbrt material input uroughness: uroughness" ); - model.comments.push_back( "pbrt material input vroughness: vroughness" ); - model.comments.push_back( "pbrt material input name: named scattering database lookup is not modeled" ); - appendBumpmapCommentsAndHelpers( model, bumpmap ); - model.comments.push_back( - "pbrt material gap: full PBRT BSSRDF transport and named-medium scattering data are not evaluated" ); - model.comments.push_back( - "pbrt material approximation: sigma_a/sigma_s albedo drives diffuse reflection and transmission lobes" ); - model.helperDefinitions = - "color pbrt_subsurface_albedo(color sigma_a, color sigma_s, float scale) =\n" - " ::math::clamp((sigma_s * scale) / ((sigma_a + sigma_s) * scale + color(0.000001, 0.000001, 0.000001)), " - "color(0.0, 0.0, 0.0), color(1.0, 1.0, 1.0));\n\n" - + model.helperDefinitions; - model.body = - " ior: color(eta, eta, eta),\n" - " surface: material_surface(\n" - " scattering: ::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: " + kr + " * " + albedo + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " + kt + " * " + albedo + ",\n" - " component: ::df::diffuse_transmission_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))))))" - + ( hasBumpmapExpression( bumpmap ) ? std::string{ ",\n" } + materialGeometryExpression( "", bumpmap ) : "\n" ); - return model; -} - -MdlMaterialModel makeKdSubsurfaceMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "Kd", "color(0.5, 0.5, 0.5)" ); - appendMaterialParameter( model, "color", "Kr", "color(1.0, 1.0, 1.0)" ); - appendMaterialParameter( model, "color", "Kt", "color(1.0, 1.0, 1.0)" ); - appendMaterialParameter( model, "color", "mfp", "color(1.0, 1.0, 1.0)" ); - appendMaterialParameter( model, "float", "scale", "1.0" ); - appendMaterialParameter( model, "float", "g", "0.0" ); - appendMaterialParameter( model, "float", "eta", "1.33" ); - appendMaterialParameter( model, "float", "uroughness", "0.0" ); - appendMaterialParameter( model, "float", "vroughness", "0.0" ); - - const std::string kd{ textureGraph.materialColorExpression( material.params, "Kd", "color", "Kd" ) }; - const std::string kr{ textureGraph.materialColorExpression( material.params, "Kr", "color", "Kr" ) }; - const std::string kt{ textureGraph.materialColorExpression( material.params, "Kt", "color", "Kt" ) }; - const std::string mfp{ textureGraph.materialColorExpression( material.params, "mfp", "color", "mfp" ) }; - const std::string bumpmap{ materialBumpmapExpression( textureGraph, material.params ) }; - - model.comments.push_back( "pbrt material model: kdsubsurface" ); - model.comments.push_back( "pbrt material input Kd: " + kd ); - model.comments.push_back( "pbrt material input Kr: " + kr ); - model.comments.push_back( "pbrt material input Kt: " + kt ); - model.comments.push_back( "pbrt material input mfp: " + mfp ); - model.comments.push_back( "pbrt material input scale: scale" ); - model.comments.push_back( "pbrt material input g: g" ); - model.comments.push_back( "pbrt material input eta: eta" ); - model.comments.push_back( "pbrt material input uroughness: uroughness" ); - model.comments.push_back( "pbrt material input vroughness: vroughness" ); - appendBumpmapCommentsAndHelpers( model, bumpmap ); - model.comments.push_back( "pbrt material gap: full PBRT diffusion-profile BSSRDF transport is not evaluated" ); - model.comments.push_back( "pbrt material approximation: Kd drives diffuse reflection and transmission lobes" ); - model.body = - " ior: color(eta, eta, eta),\n" - " surface: material_surface(\n" - " scattering: ::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: " + kr + " * " + kd + ",\n" - " component: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))),\n" - " ::df::color_bsdf_component(\n" - " weight: " + kt + " * " + kd + ",\n" - " component: ::df::diffuse_transmission_bsdf(\n" - " tint: color(1.0, 1.0, 1.0))))))" - + ( hasBumpmapExpression( bumpmap ) ? std::string{ ",\n" } + materialGeometryExpression( "", bumpmap ) : "\n" ); - return model; -} - -std::string mixNamedMaterialBsdfExpression( MdlMaterialModel& model, - MdlTextureGraphGenerator& textureGraph, - GeneratedMdlSource& result, - const otk::pbrt::PbrtMaterial& material, - const std::string& paramName, - unsigned int index ) -{ - const std::string materialName{ material.params.FindOneString( paramName, std::string{} ) }; - if( materialName.empty() ) - { - model.comments.push_back( "pbrt material input " + paramName + ": missing" ); - appendUnsupportedReason( result, "Missing PBRT mix " + paramName ); - return unsupportedNamedMaterialBsdfExpression(); - } - - model.comments.push_back( "pbrt material input " + paramName + ": named material " + std::to_string( index ) ); - const otk::pbrt::PbrtNamedMaterialMap::const_iterator namedMaterial = material.graph.namedMaterials.find( materialName ); - if( namedMaterial == material.graph.namedMaterials.end() ) - { - appendUnsupportedReason( result, "Missing PBRT named material reference" ); - return unsupportedNamedMaterialBsdfExpression(); - } - - return namedMaterialBsdfExpression( model, textureGraph, result, namedMaterial->second, index ); -} - -MdlMaterialModel makeMixMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph, GeneratedMdlSource& result ) -{ - MdlMaterialModel model; - appendMaterialParameter( model, "color", "amount", "color(0.5, 0.5, 0.5)" ); - - model.comments.push_back( "pbrt material model: mix" ); - const std::string first{ mixNamedMaterialBsdfExpression( model, textureGraph, result, material, "namedmaterial1", 0U ) }; - const std::string second{ mixNamedMaterialBsdfExpression( model, textureGraph, result, material, "namedmaterial2", 1U ) }; - const std::string amountTexture{ - materialTextureCommentExpression( textureGraph, material.params, "amount", "color" ) }; - - model.comments.push_back( "pbrt material input amount: amount; texture=" + amountTexture ); - model.comments.push_back( "pbrt material weighting: namedmaterial1 uses amount; namedmaterial2 uses 1 - amount" ); - model.comments.push_back( "pbrt material approximation: mix composes supported named material MDL closures" ); - model.helperDefinitions = - "float pbrt_mix_matte_sigma_roughness(float sigma_degrees) = ::math::clamp(sigma_degrees / 90.0, 0.0, 1.0);\n\n" - "float pbrt_mix_resolved_roughness(float roughness, float axis_roughness) = " - "axis_roughness >= 0.0 ? axis_roughness : roughness;\n\n" - "float pbrt_mix_clamped_opacity(float opacity) = ::math::clamp(opacity, 0.0, 1.0);\n\n" - "color pbrt_mix_opacity_weight(float opacity) =\n" - " color(pbrt_mix_clamped_opacity(opacity), pbrt_mix_clamped_opacity(opacity), " - "pbrt_mix_clamped_opacity(opacity));\n\n" - "color pbrt_mix_transparency_weight(float opacity) =\n" - " color(1.0 - pbrt_mix_clamped_opacity(opacity), 1.0 - pbrt_mix_clamped_opacity(opacity), " - "1.0 - pbrt_mix_clamped_opacity(opacity));\n\n" - "color pbrt_mix_metal_conductor_tint(color eta, color k) =\n" - " ((eta - color(1.0, 1.0, 1.0)) * (eta - color(1.0, 1.0, 1.0)) + k * k) /\n" - " ((eta + color(1.0, 1.0, 1.0)) * (eta + color(1.0, 1.0, 1.0)) + k * k);\n\n"; - model.body = - " surface: material_surface(\n" - " scattering: ::df::color_normalized_mix(\n" - " components: ::df::color_bsdf_component[](\n" - " ::df::color_bsdf_component(\n" - " weight: amount,\n" - " component: " - + first - + "),\n" - " ::df::color_bsdf_component(\n" - " weight: color(1.0, 1.0, 1.0) - amount,\n" - " component: " - + second + "))))\n"; - return model; -} - -MdlMaterialModel makeUnsupportedMaterialModel( const otk::pbrt::PbrtMaterial& material, GeneratedMdlSource& result ) -{ - const std::string type{ material.type.empty() ? std::string{ "" } : material.type }; - - MdlMaterialModel model; - model.comments.push_back( "pbrt material model: " + type ); - const PbrtMaterialGapPolicy* const policy{ explicitMaterialGapPolicy( material.type ) }; - if( policy != nullptr ) - { - model.comments.push_back( "pbrt material gap policy: " + policy->policy ); - model.comments.push_back( "pbrt material gap coverage: " + policy->coverageReason ); - appendUnsupportedReason( result, "Explicit PBRT material gap " + type + ": " + policy->policy ); - if( material.type == "fourier" ) - { - if( hasFourierBsdfFile( material ) ) - { - model.comments.push_back( "pbrt fourier bsdffile: preserved as material metadata" ); - } - else - { - model.comments.push_back( "pbrt fourier bsdffile: missing" ); - appendUnsupportedReason( result, "PBRT Fourier material missing bsdffile" ); - } - } - } - else - { - model.comments.push_back( "pbrt material gap policy: unknown material type" ); - appendUnsupportedReason( result, "Unsupported PBRT material type " + type ); - } - model.body = - " surface: material_surface(\n" - " scattering: ::df::diffuse_reflection_bsdf(\n" - " tint: color(1.0, 0.0, 1.0)))\n"; - return model; -} - -MdlMaterialModel makeMaterialModel( const otk::pbrt::PbrtMaterial& material, MdlTextureGraphGenerator& textureGraph, GeneratedMdlSource& result ) -{ - if( material.type == "matte" ) - { - return makeMatteMaterialModel( material, textureGraph ); - } - if( material.type == "plastic" ) - { - return makePlasticMaterialModel( material, textureGraph ); - } - if( material.type == "uber" ) - { - return makeUberMaterialModel( material, textureGraph ); - } - if( material.type == "mirror" ) - { - return makeMirrorMaterialModel( material, textureGraph ); - } - if( material.type == "glass" ) - { - return makeGlassMaterialModel( material, textureGraph ); - } - if( material.type == "metal" ) - { - return makeMetalMaterialModel( material, textureGraph ); - } - if( material.type == "substrate" ) - { - return makeSubstrateMaterialModel( material, textureGraph ); - } - if( material.type == "translucent" ) - { - return makeTranslucentMaterialModel( material, textureGraph ); - } - if( material.type == "subsurface" ) - { - return makeSubsurfaceMaterialModel( material, textureGraph ); - } - if( material.type == "kdsubsurface" ) - { - return makeKdSubsurfaceMaterialModel( material, textureGraph ); - } - if( material.type == "mix" ) - { - return makeMixMaterialModel( material, textureGraph, result ); - } - return makeUnsupportedMaterialModel( material, result ); -} - -GeneratedMdlSource generateSource( const MdlShaderKey& key ) -{ - const std::string suffix{ stableHash( key.signature ) }; - - GeneratedMdlSource result; - result.moduleName = "::otk::demand_pbrt_scene::pbrt_" + suffix; - result.materialName = "material_" + suffix; - - std::ostringstream source; - source << "mdl 1.10;\n" - << "import ::df::*;\n" - << "import ::math::*;\n" - << "\n" - << "export material " << result.materialName << "() = material(\n" - << " surface: material_surface(\n" - << " scattering: ::df::diffuse_reflection_bsdf(\n" - << " tint: color(0.8, 0.8, 0.8))));\n"; - result.source = source.str(); - return result; -} - -} // namespace - -bool operator==( const MdlShaderKey& lhs, const MdlShaderKey& rhs ) -{ - return lhs.signature == rhs.signature; -} - -bool operator!=( const MdlShaderKey& lhs, const MdlShaderKey& rhs ) -{ - return !( lhs == rhs ); -} - -bool operator<( const MdlShaderKey& lhs, const MdlShaderKey& rhs ) -{ - return lhs.signature < rhs.signature; -} - -std::string toString( const MdlShaderKey& key ) -{ - return key.signature; -} - -MdlShaderKey makeMdlShaderKey( const otk::pbrt::PbrtMaterial& material ) -{ - std::ostringstream signature; - std::vector materialStack; - std::vector textureStack; - - signature << "pbrt-mdl-v1"; - if( !material.graph.fallbackReasons.empty() ) - { - signature << "|graph-fallback"; - } - appendMaterialSignature( signature, material.type, material.params, material.graph, materialStack, textureStack ); - - return MdlShaderKey{ signature.str() }; -} - -bool operator==( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ) -{ - return lhs.sourceKey == rhs.sourceKey && lhs.signature == rhs.signature - && lhs.sourceShapeProgramReusable == rhs.sourceShapeProgramReusable; -} - -bool operator!=( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ) -{ - return !( lhs == rhs ); -} - -bool operator<( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ) -{ - if( lhs.sourceKey != rhs.sourceKey ) - { - return lhs.sourceKey < rhs.sourceKey; - } - if( lhs.signature != rhs.signature ) - { - return lhs.signature < rhs.signature; - } - return lhs.sourceShapeProgramReusable < rhs.sourceShapeProgramReusable; -} - -std::string toString( const MdlMaterialInstanceKey& key ) -{ - return "source=" + toString( key.sourceKey ) + "|instance=" + key.signature - + ( key.sourceShapeProgramReusable ? "|source-shape-program=reusable" : "|source-shape-program=instance" ); -} - -MdlMaterialInstanceKey makeMdlMaterialInstanceKey( const otk::pbrt::PbrtMaterial& material ) -{ - MdlMaterialInstanceKey result; - result.sourceKey = makeMdlShaderKey( material ); - result.sourceShapeProgramReusable = true; - - std::ostringstream signature; - std::vector materialStack; - std::vector textureStack; - - signature << "pbrt-mdl-instance-v1"; - for( std::vector::const_iterator it = material.graph.fallbackReasons.begin(); - it != material.graph.fallbackReasons.end(); ++it ) - { - signature << "|graph-fallback=" << *it; - } - appendMaterialInstanceSignature( signature, material.type, material.params, material.graph, materialStack, textureStack ); - - result.signature = signature.str(); - return result; -} - -std::vector makeMdlBoundMaterialParameters( const otk::pbrt::PbrtMaterial& material ) -{ - static const BoundParameterSpec matteParams[] = { - { MdlBoundParameterType::COLOR, "Kd" }, - { MdlBoundParameterType::FLOAT, "sigma" }, - { MdlBoundParameterType::FLOAT, "alpha" }, - { MdlBoundParameterType::FLOAT, "opacity" }, - }; - static const BoundParameterSpec plasticParams[] = { - { MdlBoundParameterType::COLOR, "Kd" }, - { MdlBoundParameterType::COLOR, "Ks" }, - { MdlBoundParameterType::FLOAT, "roughness" }, - }; - static const BoundParameterSpec uberParams[] = { - { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, - { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, - { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, - { MdlBoundParameterType::FLOAT, "vroughness" }, { MdlBoundParameterType::FLOAT, "index" }, - { MdlBoundParameterType::FLOAT, "alpha" }, { MdlBoundParameterType::COLOR, "opacity" }, - }; - static const BoundParameterSpec namedUberParams[] = { - { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, - { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, - { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, - { MdlBoundParameterType::FLOAT, "vroughness" }, { MdlBoundParameterType::FLOAT, "alpha" }, - { MdlBoundParameterType::FLOAT, "opacity" }, - }; - static const BoundParameterSpec mirrorParams[] = { - { MdlBoundParameterType::COLOR, "Kr" }, - }; - static const BoundParameterSpec glassParams[] = { - { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, - { MdlBoundParameterType::FLOAT, "index" }, { MdlBoundParameterType::FLOAT, "roughness" }, - { MdlBoundParameterType::FLOAT, "uroughness" }, { MdlBoundParameterType::FLOAT, "vroughness" }, - }; - static const BoundParameterSpec metalParams[] = { - { MdlBoundParameterType::COLOR, "eta" }, { MdlBoundParameterType::COLOR, "k" }, - { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, - { MdlBoundParameterType::FLOAT, "vroughness" }, - }; - static const BoundParameterSpec substrateParams[] = { - { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, - { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::FLOAT, "uroughness" }, - { MdlBoundParameterType::FLOAT, "vroughness" }, - }; - static const BoundParameterSpec translucentParams[] = { - { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Ks" }, - { MdlBoundParameterType::COLOR, "reflect" }, { MdlBoundParameterType::COLOR, "transmit" }, - { MdlBoundParameterType::FLOAT, "roughness" }, { MdlBoundParameterType::COLOR, "opacity" }, - }; - static const BoundParameterSpec subsurfaceParams[] = { - { MdlBoundParameterType::COLOR, "Kr" }, { MdlBoundParameterType::COLOR, "Kt" }, - { MdlBoundParameterType::COLOR, "sigma_a" }, { MdlBoundParameterType::COLOR, "sigma_s" }, - { MdlBoundParameterType::FLOAT, "scale" }, { MdlBoundParameterType::FLOAT, "g" }, - { MdlBoundParameterType::FLOAT, "eta" }, { MdlBoundParameterType::FLOAT, "uroughness" }, - { MdlBoundParameterType::FLOAT, "vroughness" }, - }; - static const BoundParameterSpec kdSubsurfaceParams[] = { - { MdlBoundParameterType::COLOR, "Kd" }, { MdlBoundParameterType::COLOR, "Kr" }, - { MdlBoundParameterType::COLOR, "Kt" }, { MdlBoundParameterType::COLOR, "mfp" }, - { MdlBoundParameterType::FLOAT, "scale" }, { MdlBoundParameterType::FLOAT, "g" }, - { MdlBoundParameterType::FLOAT, "eta" }, { MdlBoundParameterType::FLOAT, "uroughness" }, - { MdlBoundParameterType::FLOAT, "vroughness" }, - }; - static const BoundParameterSpec mixParams[] = { - { MdlBoundParameterType::COLOR, "amount" }, - }; - - std::vector result; - const auto appendNamedMaterialParameters = [&]( const std::string& paramName, unsigned int index ) { - const std::string materialName{ material.params.FindOneString( paramName, std::string{} ) }; - if( materialName.empty() ) - { - return; - } - - const otk::pbrt::PbrtNamedMaterialMap::const_iterator namedMaterial = material.graph.namedMaterials.find( materialName ); - if( namedMaterial == material.graph.namedMaterials.end() ) - { - return; - } - - const std::string type{ namedMaterialType( namedMaterial->second ) }; - if( type == "matte" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( matteParams ), - std::end( matteParams ) ); - } - else if( type == "plastic" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( plasticParams ), - std::end( plasticParams ) ); - } - else if( type == "uber" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( namedUberParams ), - std::end( namedUberParams ) ); - } - else if( type == "mirror" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( mirrorParams ), - std::end( mirrorParams ) ); - } - else if( type == "glass" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( glassParams ), - std::end( glassParams ) ); - } - else if( type == "metal" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( metalParams ), - std::end( metalParams ) ); - } - else if( type == "substrate" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( substrateParams ), - std::end( substrateParams ) ); - } - else if( type == "translucent" ) - { - appendNamedBoundParameters( result, namedMaterial->second.params, index, std::begin( translucentParams ), - std::end( translucentParams ) ); - } - }; - - if( material.type == "matte" ) - { - appendMaterialBoundParameters( result, material, std::begin( matteParams ), std::end( matteParams ) ); - } - else if( material.type == "plastic" ) - { - appendMaterialBoundParameters( result, material, std::begin( plasticParams ), std::end( plasticParams ) ); - } - else if( material.type == "uber" ) - { - appendMaterialBoundParameters( result, material, std::begin( uberParams ), std::end( uberParams ) ); - } - else if( material.type == "mirror" ) - { - appendMaterialBoundParameters( result, material, std::begin( mirrorParams ), std::end( mirrorParams ) ); - } - else if( material.type == "glass" ) - { - appendMaterialBoundParameters( result, material, std::begin( glassParams ), std::end( glassParams ) ); - } - else if( material.type == "metal" ) - { - appendMaterialBoundParameters( result, material, std::begin( metalParams ), std::end( metalParams ) ); - } - else if( material.type == "substrate" ) - { - appendMaterialBoundParameters( result, material, std::begin( substrateParams ), std::end( substrateParams ) ); - } - else if( material.type == "translucent" ) - { - appendMaterialBoundParameters( result, material, std::begin( translucentParams ), std::end( translucentParams ) ); - } - else if( material.type == "subsurface" ) - { - appendMaterialBoundParameters( result, material, std::begin( subsurfaceParams ), std::end( subsurfaceParams ) ); - } - else if( material.type == "kdsubsurface" ) - { - appendMaterialBoundParameters( result, material, std::begin( kdSubsurfaceParams ), std::end( kdSubsurfaceParams ) ); - } - else if( material.type == "mix" ) - { - appendMaterialBoundParameters( result, material, std::begin( mixParams ), std::end( mixParams ) ); - appendNamedMaterialParameters( "namedmaterial1", 0U ); - appendNamedMaterialParameters( "namedmaterial2", 1U ); - } - return result; -} - -GeneratedMdlSource generateMdlSource( const otk::pbrt::PbrtMaterial& material ) -{ - const MdlShaderKey key{ makeMdlShaderKey( material ) }; - const std::string suffix{ stableHash( key.signature ) }; - - GeneratedMdlSource result; - result.moduleName = "::otk::demand_pbrt_scene::pbrt_" + suffix; - result.materialName = "material_" + suffix; - - MdlTextureGraphGenerator textureGraph{ material.graph, result }; - const MdlMaterialModel materialModel{ makeMaterialModel( material, textureGraph, result ) }; - - std::ostringstream source; - source << "mdl 1.10;\n" - << "import ::df::*;\n" - << "import ::math::*;\n" - << "import ::state::*;\n" - << "\n"; - for( std::vector::const_iterator it = materialModel.comments.begin(); it != materialModel.comments.end(); ++it ) - { - source << "// " << *it << "\n"; - } - if( !materialModel.comments.empty() ) - { - source << "\n"; - } - for( std::vector::const_iterator it = result.unsupportedReasons.begin(); - it != result.unsupportedReasons.end(); ++it ) - { - source << "// unsupported: " << *it << "\n"; - } - if( !result.unsupportedReasons.empty() ) - { - source << "\n"; - } - source << textureGraph.sourcePreamble() << materialModel.helperDefinitions << textureGraph.functionDefinitions() - << "export material " << result.materialName << mdlParameterList( materialModel.parameters ) << " = material(\n" - << materialModel.body << ");\n"; - result.source = source.str(); - return result; -} MdlShaderCompileRecord& MdlShaderCompileCache::getMutableRecord( const MdlMaterialInstanceKey& key ) { @@ -2804,7 +266,7 @@ const GeneratedMdlSource& MdlGeneratedSourceCache::getSource( const MdlShaderKey std::map::iterator it = m_sources.find( key ); if( it == m_sources.end() ) { - it = m_sources.insert( std::make_pair( key, generateSource( key ) ) ).first; + it = m_sources.insert( std::make_pair( key, generateMdlSource( key ) ) ).first; } return it->second; } diff --git a/examples/DemandLoading/DemandPbrtScene/MdlTextureGraphGenerator.cpp b/examples/DemandLoading/DemandPbrtScene/MdlTextureGraphGenerator.cpp new file mode 100644 index 00000000..2834c163 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/MdlTextureGraphGenerator.cpp @@ -0,0 +1,361 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "DemandPbrtScene/MdlTextureGraphGenerator.h" + +#ifdef OTK_USE_MDL + +#include "DemandPbrtScene/MdlMaterialModelBuilder.h" +#include "DemandPbrtScene/MdlShaderCache.h" +#include "DemandPbrtScene/PbrtCheckerboardImageSource.h" + +#include +#include +#include +#include +#include + +namespace demandPbrtScene { + +MdlTextureLookup findMdlTexture( const otk::pbrt::PbrtMaterialGraph& graph, const std::string& textureName, const std::string& preferredValueType ) +{ + MdlTextureLookup fallback{ std::string{}, nullptr }; + for( otk::pbrt::PbrtTextureMap::const_iterator it = graph.textures.begin(); it != graph.textures.end(); ++it ) + { + if( it->second.name != textureName ) + { + continue; + } + if( preferredValueType.empty() || it->second.valueType == preferredValueType ) + { + return MdlTextureLookup{ it->first, &it->second }; + } + if( fallback.texture == nullptr ) + { + fallback = MdlTextureLookup{ it->first, &it->second }; + } + } + return fallback; +} + +namespace { + +std::string textureKind( const otk::pbrt::PbrtTexture& texture ) +{ + return texture.valueType + ":" + texture.type; +} + +bool isUnsupportedProceduralTexture( const otk::pbrt::PbrtTexture& texture ) +{ + return texture.type == "marble" || texture.type == "fbm" || texture.type == "windy" || texture.type == "wrinkled"; +} + +} // namespace + +class MdlTextureGraphGenerator::Impl +{ + public: + Impl( const otk::pbrt::PbrtMaterialGraph& graph, GeneratedMdlSource& result ) + : m_graph( graph ) + , m_result( result ) + { + for( std::vector::const_iterator it = m_graph.fallbackReasons.begin(); + it != m_graph.fallbackReasons.end(); ++it ) + { + appendUnsupportedReason( m_result, "PBRT material graph fallback: " + *it ); + } + } + + std::string materialColorExpression( const ::pbrt::ParamSet& params, + const std::string& paramName, + const std::string& preferredValueType, + const std::string& defaultExpression ) + { + const std::string textureName{ params.FindTexture( paramName ) }; + if( textureName.empty() ) + { + return defaultExpression; + } + if( isFoldableTextureReference( textureName, preferredValueType ) ) + { + return defaultExpression; + } + return textureReference( textureName, preferredValueType ); + } + + std::string materialFloatExpression( const ::pbrt::ParamSet& params, + const std::string& paramName, + const std::string& preferredValueType, + const std::string& defaultExpression ) + { + const std::string textureName{ params.FindTexture( paramName ) }; + if( textureName.empty() ) + { + return defaultExpression; + } + if( isFoldableTextureReference( textureName, preferredValueType ) && defaultExpression != "0.0" ) + { + return defaultExpression; + } + m_usesTextureFloat = true; + return "pbrt_texture_float(" + textureReference( textureName, preferredValueType ) + ")"; + } + + std::string sourcePreamble() const + { + std::ostringstream out; + if( m_usesTextureFloat ) + { + out << "float pbrt_texture_float(color value) = ::math::luminance(value);\n"; + } + if( m_usesDemandTexture ) + { + out << "color pbrt_demand_texture_2d(int texture_id) = color(1.0, 1.0, 1.0);\n"; + } + if( m_usesCheckerboard ) + { + out << "color pbrt_checkerboard_2d(color tex1, color tex2) = (tex1 + tex2) * 0.5;\n"; + } + if( m_usesUnsupported ) + { + out << "color pbrt_unsupported_texture() = color(1.0, 0.0, 1.0);\n"; + } + if( !m_usesTextureFloat && !m_usesDemandTexture && !m_usesCheckerboard && !m_usesUnsupported ) + { + return std::string{}; + } + out << "\n"; + return out.str(); + } + + std::string functionDefinitions() const + { + std::ostringstream out; + for( std::vector::const_iterator it = m_functions.begin(); it != m_functions.end(); ++it ) + { + out << *it << "\n"; + } + return out.str(); + } + + private: + std::string textureReference( const std::string& textureName, const std::string& preferredValueType ) + { + const MdlTextureLookup lookup{ findMdlTexture( m_graph, textureName, preferredValueType ) }; + if( lookup.texture == nullptr ) + { + appendUnsupportedReason( m_result, "Missing PBRT texture '" + textureName + "'" ); + return unsupportedTextureExpression(); + } + if( std::find( m_textureStack.begin(), m_textureStack.end(), lookup.graphKey ) != m_textureStack.end() ) + { + appendUnsupportedReason( m_result, "Recursive PBRT texture reference '" + textureName + "'" ); + return unsupportedTextureExpression(); + } + + std::map::const_iterator cached = m_textureFunctions.find( lookup.graphKey ); + if( cached != m_textureFunctions.end() ) + { + return cached->second + "()"; + } + + m_textureStack.push_back( lookup.graphKey ); + const std::string functionName{ defineTextureFunction( lookup.graphKey, *lookup.texture ) }; + m_textureStack.pop_back(); + m_textureFunctions.insert( std::make_pair( lookup.graphKey, functionName ) ); + return functionName + "()"; + } + + std::string defineTextureFunction( const std::string& /*graphKey*/, const otk::pbrt::PbrtTexture& texture ) + { + const std::string functionName{ "texture_" + std::to_string( m_nextTextureFunction++ ) }; + const std::string expression{ textureExpression( texture ) }; + + std::ostringstream out; + out << "// pbrt texture node: " << textureKind( texture ) << "\n"; + if( isDemandTexture( texture ) ) + { + out << "// demand texture parameter: texture_2d image_" << ( m_nextImageParameter - 1U ) << "\n"; + } + out << "color " << functionName << "() = " << expression << ";\n"; + m_functions.push_back( out.str() ); + return functionName; + } + + std::string textureExpression( const otk::pbrt::PbrtTexture& texture ) + { + if( texture.type == "imagemap" ) + { + return demandTextureExpression(); + } + if( texture.type == "constant" ) + { + return defaultColorExpression(); + } + if( texture.type == "scale" ) + { + const std::string tex1{ textureInputExpression( texture, "tex1", defaultColorExpression() ) }; + const std::string tex2{ textureInputExpression( texture, "tex2", defaultColorExpression() ) }; + return tex1 + " * " + tex2; + } + if( texture.type == "mix" ) + { + const std::string tex1{ textureInputExpression( texture, "tex1", defaultColorExpression() ) }; + const std::string tex2{ textureInputExpression( texture, "tex2", defaultColorExpression() ) }; + return tex1 + " * (1.0 - 0.5) + " + tex2 + " * 0.5"; + } + if( texture.type == "checkerboard" ) + { + if( !pbrtCheckerboardTextureKey( texture ).empty() ) + { + return demandTextureExpression(); + } + if( texture.params.FindOneString( "dimension", "2d" ) != "2d" ) + { + appendUnsupportedReason( m_result, "Unsupported PBRT checkerboard dimension in " + textureKind( texture ) ); + return unsupportedTextureExpression(); + } + m_usesCheckerboard = true; + const std::string tex1{ textureInputExpression( texture, "tex1", "color(1.0, 1.0, 1.0)" ) }; + const std::string tex2{ textureInputExpression( texture, "tex2", "color(0.0, 0.0, 0.0)" ) }; + return "pbrt_checkerboard_2d(" + tex1 + ", " + tex2 + ")"; + } + + if( isUnsupportedProceduralTexture( texture ) ) + { + appendUnsupportedReason( m_result, "Unsupported PBRT texture type " + textureKind( texture ) ); + return unsupportedTextureExpression(); + } + + appendUnsupportedReason( m_result, "Unsupported PBRT texture type " + textureKind( texture ) ); + return unsupportedTextureExpression(); + } + + std::string demandTextureExpression() + { + m_usesDemandTexture = true; + return "pbrt_demand_texture_2d(" + std::to_string( m_nextImageParameter++ ) + ")"; + } + + static bool isDemandTexture( const otk::pbrt::PbrtTexture& texture ) + { + return texture.type == "imagemap" || ( texture.type == "checkerboard" && !pbrtCheckerboardTextureKey( texture ).empty() ); + } + + bool isFoldableTextureReference( const std::string& textureName, const std::string& preferredValueType ) const + { + std::vector textureStack; + return isFoldableTextureReference( textureName, preferredValueType, textureStack ); + } + + bool isFoldableTextureReference( const std::string& textureName, + const std::string& preferredValueType, + std::vector& textureStack ) const + { + const MdlTextureLookup lookup{ findMdlTexture( m_graph, textureName, preferredValueType ) }; + if( lookup.texture == nullptr + || std::find( textureStack.begin(), textureStack.end(), lookup.graphKey ) != textureStack.end() ) + { + return false; + } + + textureStack.push_back( lookup.graphKey ); + const bool result{ isFoldableTexture( *lookup.texture, textureStack ) }; + textureStack.pop_back(); + return result; + } + + bool isFoldableTextureInput( const otk::pbrt::PbrtTexture& texture, const char* paramName, std::vector& textureStack ) const + { + const std::string textureName{ texture.params.FindTexture( paramName ) }; + return textureName.empty() || isFoldableTextureReference( textureName, texture.valueType, textureStack ); + } + + bool isFoldableTexture( const otk::pbrt::PbrtTexture& texture, std::vector& textureStack ) const + { + if( texture.type == "constant" ) + { + return true; + } + if( texture.type == "scale" ) + { + return isFoldableTextureInput( texture, "tex1", textureStack ) && isFoldableTextureInput( texture, "tex2", textureStack ); + } + if( texture.type == "mix" ) + { + return isFoldableTextureInput( texture, "tex1", textureStack ) && isFoldableTextureInput( texture, "tex2", textureStack ) + && isFoldableTextureInput( texture, "amount", textureStack ); + } + return false; + } + + std::string textureInputExpression( const otk::pbrt::PbrtTexture& texture, const std::string& paramName, const std::string& defaultExpression ) + { + const std::string textureName{ texture.params.FindTexture( paramName ) }; + if( textureName.empty() ) + { + return defaultExpression; + } + return textureReference( textureName, texture.valueType ); + } + + std::string unsupportedTextureExpression() + { + m_usesUnsupported = true; + return "pbrt_unsupported_texture()"; + } + + static std::string defaultColorExpression() { return "color(1.0, 1.0, 1.0)"; } + + const otk::pbrt::PbrtMaterialGraph& m_graph; + GeneratedMdlSource& m_result; + std::vector m_textureStack; + std::map m_textureFunctions; + std::vector m_functions; + unsigned int m_nextTextureFunction{}; + unsigned int m_nextImageParameter{}; + bool m_usesTextureFloat{}; + bool m_usesDemandTexture{}; + bool m_usesCheckerboard{}; + bool m_usesUnsupported{}; +}; + +MdlTextureGraphGenerator::MdlTextureGraphGenerator( const otk::pbrt::PbrtMaterialGraph& graph, + GeneratedMdlSource& result ) + : m_impl{ std::make_unique( graph, result ) } +{ +} + +MdlTextureGraphGenerator::~MdlTextureGraphGenerator() = default; + +std::string MdlTextureGraphGenerator::materialColorExpression( const ::pbrt::ParamSet& params, + const std::string& paramName, + const std::string& preferredValueType, + const std::string& defaultExpression ) +{ + return m_impl->materialColorExpression( params, paramName, preferredValueType, defaultExpression ); +} + +std::string MdlTextureGraphGenerator::materialFloatExpression( const ::pbrt::ParamSet& params, + const std::string& paramName, + const std::string& preferredValueType, + const std::string& defaultExpression ) +{ + return m_impl->materialFloatExpression( params, paramName, preferredValueType, defaultExpression ); +} + +std::string MdlTextureGraphGenerator::sourcePreamble() const +{ + return m_impl->sourcePreamble(); +} + +std::string MdlTextureGraphGenerator::functionDefinitions() const +{ + return m_impl->functionDefinitions(); +} + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL + diff --git a/examples/DemandLoading/DemandPbrtScene/MdlUtils.cpp b/examples/DemandLoading/DemandPbrtScene/MdlUtils.cpp new file mode 100644 index 00000000..a025294c --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/MdlUtils.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "DemandPbrtScene/MdlUtils.h" + +#ifdef OTK_USE_MDL + +#include "DemandPbrtScene/MdlHandleTypes.h" + +#include +#include + +namespace demandPbrtScene { + +std::string describeMdlContextMessages( const mi::neuraylib::IMdl_execution_context* context ) +{ + if( !context ) + { + return {}; + } + + std::ostringstream out; + for( mi::Size i = 0; i < context->get_messages_count(); ++i ) + { + MessageHandle message( context->get_message( i ) ); + if( message.is_valid_interface() ) + { + out << message->get_string() << '\n'; + } + } + return out.str(); +} + +[[noreturn]] void failMdl( const std::string& message, const mi::neuraylib::IMdl_execution_context* context ) +{ + const std::string contextMessages{ describeMdlContextMessages( context ) }; + throw std::runtime_error( contextMessages.empty() ? message : message + ":\n" + contextMessages ); +} + +void requireMdl( bool condition, const std::string& message, const mi::neuraylib::IMdl_execution_context* context ) +{ + if( !condition ) + { + failMdl( message, context ); + } +} + +MdlTargetArgumentBlock captureMdlTargetArgumentBlock( const mi::neuraylib::ITarget_code* targetCode, + const mi::neuraylib::ICompiled_material* compiledMaterial, + mi::neuraylib::IMdl_execution_context* context ) +{ + requireMdl( targetCode != nullptr, "Cannot capture MDL argument block without target code", context ); + requireMdl( compiledMaterial != nullptr, "Cannot capture MDL argument block without a compiled material", context ); + requireMdl( targetCode->get_callable_function_count() > 0U, + "Cannot capture MDL argument block without callable functions", context ); + + const mi::Size argumentBlockIndex{ targetCode->get_callable_function_argument_block_index( 0U ) }; + if( argumentBlockIndex == ~mi::Size( 0 ) ) + { + return MdlTargetArgumentBlock{}; + } + + TargetArgumentBlockHandle argumentBlock( targetCode->get_argument_block( argumentBlockIndex ) ); + requireMdl( argumentBlock.is_valid_interface(), "MDL target code did not expose an argument block", context ); + + TargetValueLayoutHandle layout( targetCode->get_argument_block_layout( argumentBlockIndex ) ); + requireMdl( layout.is_valid_interface(), "MDL target code did not expose an argument block layout", context ); + + MdlTargetArgumentBlock result; + result.data.assign( argumentBlock->get_data(), argumentBlock->get_data() + argumentBlock->get_size() ); + + const mi::Size parameterCount{ compiledMaterial->get_parameter_count() }; + requireMdl( layout->get_num_elements() >= parameterCount, + "MDL argument block layout has fewer entries than the compiled material", context ); + for( mi::Size i = 0; i < parameterCount; ++i ) + { + const char* const name = compiledMaterial->get_parameter_name( i ); + requireMdl( name != nullptr, "MDL compiled material exposed a null parameter name", context ); + + const mi::neuraylib::Target_value_layout_state state{ layout->get_nested_state( i ) }; + requireMdl( state.m_state_offs != ~mi::Uint32( 0 ), + "MDL argument block layout did not expose parameter state for " + std::string{ name }, context ); + + mi::neuraylib::IValue::Kind kind{}; + mi::Size size{}; + const mi::Size offset{ layout->get_layout( kind, size, state ) }; + requireMdl( offset != ~mi::Size( 0 ), + "MDL argument block layout did not expose parameter offset for " + std::string{ name }, context ); + requireMdl( offset + size <= result.data.size(), + "MDL argument block layout parameter exceeds block size for " + std::string{ name }, context ); + + result.parameters.push_back( MdlTargetArgumentBlockParameter{ + name, static_cast( kind ), static_cast( offset ), static_cast( size ) } ); + } + return result; +} + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL diff --git a/examples/DemandLoading/DemandPbrtScene/PbrtMaterialKind.cpp b/examples/DemandLoading/DemandPbrtScene/PbrtMaterialKind.cpp new file mode 100644 index 00000000..a9283532 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/PbrtMaterialKind.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "DemandPbrtScene/PbrtMaterialKind.h" + +namespace demandPbrtScene { +namespace { + +constexpr PbrtMaterialCapability operator|( PbrtMaterialCapability lhs, PbrtMaterialCapability rhs ) +{ + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); +} + +constexpr PbrtMaterialDescriptor MATERIALS[] = { + { PbrtMaterialKind::UNKNOWN, "", PbrtMaterialCapability::NONE }, + { PbrtMaterialKind::MATTE, "matte", PbrtMaterialCapability::GENERATED_MDL | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::KD }, + { PbrtMaterialKind::PLASTIC, "plastic", PbrtMaterialCapability::GENERATED_MDL | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::KD | PbrtMaterialCapability::KS + | PbrtMaterialCapability::ROUGHNESS }, + { PbrtMaterialKind::UBER, "uber", PbrtMaterialCapability::GENERATED_MDL | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::KD | PbrtMaterialCapability::KS + | PbrtMaterialCapability::KR | PbrtMaterialCapability::KT + | PbrtMaterialCapability::ROUGHNESS + | PbrtMaterialCapability::AXIS_ROUGHNESS }, + { PbrtMaterialKind::MIRROR, "mirror", PbrtMaterialCapability::GENERATED_MDL | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::KR }, + { PbrtMaterialKind::GLASS, "glass", PbrtMaterialCapability::GENERATED_MDL | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::KR | PbrtMaterialCapability::KT }, + { PbrtMaterialKind::METAL, "metal", PbrtMaterialCapability::GENERATED_MDL | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::ROUGHNESS + | PbrtMaterialCapability::AXIS_ROUGHNESS }, + { PbrtMaterialKind::SUBSTRATE, "substrate", PbrtMaterialCapability::GENERATED_MDL + | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::KD | PbrtMaterialCapability::KS + | PbrtMaterialCapability::AXIS_ROUGHNESS }, + { PbrtMaterialKind::TRANSLUCENT, "translucent", PbrtMaterialCapability::GENERATED_MDL + | PbrtMaterialCapability::NAMED_MDL + | PbrtMaterialCapability::KD | PbrtMaterialCapability::KS + | PbrtMaterialCapability::ROUGHNESS }, + { PbrtMaterialKind::SUBSURFACE, "subsurface", PbrtMaterialCapability::GENERATED_MDL }, + { PbrtMaterialKind::KD_SUBSURFACE, "kdsubsurface", PbrtMaterialCapability::GENERATED_MDL + | PbrtMaterialCapability::KD }, + { PbrtMaterialKind::MIX, "mix", PbrtMaterialCapability::GENERATED_MDL }, + { PbrtMaterialKind::FOURIER, "fourier", PbrtMaterialCapability::NONE }, + { PbrtMaterialKind::HAIR, "hair", PbrtMaterialCapability::NONE }, + { PbrtMaterialKind::MEASURED, "measured", PbrtMaterialCapability::NONE }, +}; + +} // namespace + +bool PbrtMaterialDescriptor::has( PbrtMaterialCapability capability ) const +{ + return ( static_cast( capabilities ) & static_cast( capability ) ) != 0U; +} + +const PbrtMaterialDescriptor& pbrtMaterialDescriptor( std::string_view type ) +{ + for( const PbrtMaterialDescriptor& material : MATERIALS ) + { + if( material.type == type ) + { + return material; + } + } + return MATERIALS[0]; +} + +PbrtMaterialKind pbrtMaterialKind( std::string_view type ) +{ + return pbrtMaterialDescriptor( type ).kind; +} + +} // namespace demandPbrtScene diff --git a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp index b217c566..21c66ad0 100644 --- a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp +++ b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp @@ -12,7 +12,10 @@ #include "DemandPbrtScene/FourierBsdfTableResource.h" #include "DemandPbrtScene/MaterialAdapters.h" #include "DemandPbrtScene/MdlBsdfCompiler.h" +#include "DemandPbrtScene/MdlHandleTypes.h" +#include "DemandPbrtScene/MdlSdkSession.h" #include "DemandPbrtScene/MdlShaderCache.h" +#include "DemandPbrtScene/MdlUtils.h" #endif #include "DemandPbrtScene/Options.h" #include "DemandPbrtScene/Params.h" @@ -64,7 +67,6 @@ #include #include #endif -#include #include #include #ifdef OTK_USE_MDL @@ -75,12 +77,6 @@ #ifdef OTK_USE_MDL #include - -#ifdef _WIN32 -#include -#else -#include -#endif #endif #if OPTIX_VERSION < 70700 @@ -222,85 +218,6 @@ class MdlOptixModuleCache std::map m_modules; }; -std::string describeContextMessages( const mi::neuraylib::IMdl_execution_context* context ) -{ - if( !context ) - return {}; - - std::ostringstream out; - for( mi::Size i = 0; i < context->get_messages_count(); ++i ) - { - mi::base::Handle message( context->get_message( i ) ); - if( message.is_valid_interface() ) - out << message->get_string() << '\n'; - } - return out.str(); -} - -[[noreturn]] void failMdl( const std::string& message, const mi::neuraylib::IMdl_execution_context* context = nullptr ) -{ - const std::string contextMessages{ describeContextMessages( context ) }; - throw std::runtime_error( contextMessages.empty() ? message : message + ":\n" + contextMessages ); -} - -void requireMdl( bool condition, const std::string& message, const mi::neuraylib::IMdl_execution_context* context = nullptr ) -{ - if( !condition ) - { - failMdl( message, context ); - } -} - -MdlTargetArgumentBlock captureMdlTargetArgumentBlock( const mi::neuraylib::ITarget_code* targetCode, - const mi::neuraylib::ICompiled_material* compiledMaterial, - mi::neuraylib::IMdl_execution_context* context ) -{ - requireMdl( targetCode != nullptr, "Cannot capture MDL argument block without target code", context ); - requireMdl( compiledMaterial != nullptr, "Cannot capture MDL argument block without a compiled material", context ); - requireMdl( targetCode->get_callable_function_count() > 0U, - "Cannot capture MDL argument block without callable functions", context ); - - const mi::Size argumentBlockIndex{ targetCode->get_callable_function_argument_block_index( 0U ) }; - if( argumentBlockIndex == ~mi::Size( 0 ) ) - { - return MdlTargetArgumentBlock{}; - } - - mi::base::Handle argumentBlock( targetCode->get_argument_block( argumentBlockIndex ) ); - requireMdl( argumentBlock.is_valid_interface(), "MDL target code did not expose an argument block", context ); - - mi::base::Handle layout( targetCode->get_argument_block_layout( argumentBlockIndex ) ); - requireMdl( layout.is_valid_interface(), "MDL target code did not expose an argument block layout", context ); - - MdlTargetArgumentBlock result; - result.data.assign( argumentBlock->get_data(), argumentBlock->get_data() + argumentBlock->get_size() ); - - const mi::Size parameterCount{ compiledMaterial->get_parameter_count() }; - requireMdl( layout->get_num_elements() >= parameterCount, - "MDL argument block layout has fewer entries than the compiled material", context ); - for( mi::Size i = 0; i < parameterCount; ++i ) - { - const char* const name = compiledMaterial->get_parameter_name( i ); - requireMdl( name != nullptr, "MDL compiled material exposed a null parameter name", context ); - - const mi::neuraylib::Target_value_layout_state state{ layout->get_nested_state( i ) }; - requireMdl( state.m_state_offs != ~mi::Uint32( 0 ), - "MDL argument block layout did not expose parameter state for " + std::string{ name }, context ); - - mi::neuraylib::IValue::Kind kind{}; - mi::Size size{}; - const mi::Size offset{ layout->get_layout( kind, size, state ) }; - requireMdl( offset != ~mi::Size( 0 ), - "MDL argument block layout did not expose parameter offset for " + std::string{ name }, context ); - requireMdl( offset + size <= result.data.size(), - "MDL argument block layout parameter exceeds block size for " + std::string{ name }, context ); - - result.parameters.push_back( MdlTargetArgumentBlockParameter{ - name, static_cast( kind ), static_cast( offset ), static_cast( size ) } ); - } - return result; -} - bool isPtxIdentifierChar( char value ) { const unsigned char ch{ static_cast( value ) }; @@ -394,140 +311,44 @@ std::string generatedMdlMessage( const std::string& message, const GeneratedMdlS return message + " (" + generatedMdlContext( source, key ) + ")"; } -#ifdef _WIN32 - -using MdlLibraryHandle = HMODULE; - -std::string lastLibraryError() -{ - std::ostringstream out; - out << "Windows error " << GetLastError(); - return out.str(); -} - -MdlLibraryHandle loadMdlSdkLibrary( std::string& error ) -{ - const char* const libraryName = "libmdl_sdk" MI_BASE_DLL_FILE_EXT; - MdlLibraryHandle handle = LoadLibraryA( libraryName ); - if( handle ) - return handle; - - const std::string fallback = std::string( "../../../bin/" ) + libraryName; - handle = LoadLibraryA( fallback.c_str() ); - if( handle ) - return handle; - - error = "Failed to load " + std::string( libraryName ) + ": " + lastLibraryError(); - return nullptr; -} - -void* loadMdlFactorySymbol( MdlLibraryHandle handle, std::string& error ) -{ - void* symbol = GetProcAddress( handle, "mi_factory" ); - if( !symbol ) - error = "Failed to find mi_factory: " + lastLibraryError(); - return symbol; -} - -void unloadMdlSdkLibrary( MdlLibraryHandle handle ) -{ - if( handle ) - FreeLibrary( handle ); -} - -#else - -using MdlLibraryHandle = void*; - -MdlLibraryHandle loadMdlSdkLibrary( std::string& error ) -{ - const char* const libraryName = "libmdl_sdk" MI_BASE_DLL_FILE_EXT; - MdlLibraryHandle handle = dlopen( libraryName, RTLD_LAZY ); - if( !handle ) - error = dlerror(); - return handle; -} - -void* loadMdlFactorySymbol( MdlLibraryHandle handle, std::string& error ) -{ - void* symbol = dlsym( handle, "mi_factory" ); - if( !symbol ) - error = dlerror(); - return symbol; -} - -void unloadMdlSdkLibrary( MdlLibraryHandle handle ) -{ - if( handle ) - dlclose( handle ); -} - -#endif - -class MdlSdkSession +class MdlTransaction { public: - MdlSdkSession() - : m_library( loadMdlSdkLibrary( m_error ) ) + explicit MdlTransaction( const NeurayHandle& neuray ) + : m_database( neuray->get_api_component() ) { - if( !m_library ) - return; - - void* symbol = loadMdlFactorySymbol( m_library, m_error ); - if( !symbol ) - return; + requireMdl( m_database.is_valid_interface(), "Failed to get MDL database" ); + m_scope = m_database->get_global_scope(); + requireMdl( m_scope.is_valid_interface(), "Failed to get MDL global scope" ); + m_transaction = m_scope->create_transaction(); + requireMdl( m_transaction.is_valid_interface(), "Failed to create MDL transaction" ); + } - m_neuray = mi::neuraylib::mi_factory( symbol ); - if( !m_neuray.is_valid_interface() ) - { - mi::base::Handle version( mi::neuraylib::mi_factory( symbol ) ); - m_error = version.is_valid_interface() ? "MDL SDK library version does not match header version " - + std::string( MI_NEURAYLIB_PRODUCT_VERSION_STRING ) : - "MDL SDK library is incompatible with this header"; - return; - } + MdlTransaction( const MdlTransaction& ) = delete; + MdlTransaction& operator=( const MdlTransaction& ) = delete; - const mi::Sint32 startResult = m_neuray->start( true ); - if( startResult != 0 ) + ~MdlTransaction() + { + if( m_transaction.is_valid_interface() && m_transaction->is_open() ) { - std::ostringstream out; - out << "Failed to start MDL SDK: " << startResult; - m_error = out.str(); - return; + m_transaction->abort(); } - - m_started = true; } - ~MdlSdkSession() - { - shutdown(); - unloadMdlSdkLibrary( m_library ); - } - - bool isStarted() const { return m_started; } - - const std::string& error() const { return m_error; } - - mi::neuraylib::INeuray* neuray() const { return m_neuray.get(); } + const TransactionHandle& handle() const { return m_transaction; } - mi::Sint32 shutdown() + void commit() { - mi::Sint32 result = 0; - if( m_started ) - { - result = m_neuray->shutdown( true ); - m_started = false; - } - m_neuray.reset(); - return result; + requireMdl( m_transaction->commit() == 0, "Failed to commit MDL transaction" ); + m_transaction.reset(); + m_scope.reset(); + m_database.reset(); } private: - MdlLibraryHandle m_library{}; - mi::base::Handle m_neuray; - std::string m_error; - bool m_started{ false }; + DatabaseHandle m_database; + ScopeHandle m_scope; + TransactionHandle m_transaction; }; otk::pbrt::PbrtMaterial makeSyntheticMatteMaterial( const float3& kd ) @@ -550,12 +371,12 @@ void bindGeneratedColorParameter( mi::neuraylib::IFunction_call* materialCa const MdlShaderKey& key, const MdlBoundMaterialParameter& parameter ) { - mi::base::Handle value( + ColorValueHandle value( valueFactory->create_color( parameter.red, parameter.green, parameter.blue ) ); requireMdl( value.is_valid_interface(), generatedMdlMessage( "Failed to create generated MDL " + parameter.name + " value", source, key ) ); - mi::base::Handle expression( expressionFactory->create_constant( value.get() ) ); + ExpressionConstantHandle expression( expressionFactory->create_constant( value.get() ) ); requireMdl( expression.is_valid_interface(), generatedMdlMessage( "Failed to create generated MDL " + parameter.name + " expression", source, key ) ); @@ -571,11 +392,11 @@ void bindGeneratedFloatParameter( mi::neuraylib::IFunction_call* materialCa const MdlShaderKey& key, const MdlBoundMaterialParameter& parameter ) { - mi::base::Handle value( valueFactory->create_float( parameter.value ) ); + FloatValueHandle value( valueFactory->create_float( parameter.value ) ); requireMdl( value.is_valid_interface(), generatedMdlMessage( "Failed to create generated MDL " + parameter.name + " value", source, key ) ); - mi::base::Handle expression( expressionFactory->create_constant( value.get() ) ); + ExpressionConstantHandle expression( expressionFactory->create_constant( value.get() ) ); requireMdl( expression.is_valid_interface(), generatedMdlMessage( "Failed to create generated MDL " + parameter.name + " expression", source, key ) ); @@ -604,7 +425,7 @@ void bindGeneratedMaterialParameters( mi::neuraylib::IFunction_call* } } -mi::base::Handle compileGeneratedMaterial( mi::neuraylib::INeuray* neuray, +CompiledMaterialHandle compileGeneratedMaterial( mi::neuraylib::INeuray* neuray, mi::neuraylib::ITransaction* transaction, mi::neuraylib::IMdl_execution_context* context, const GeneratedMdlSource& source, @@ -612,16 +433,16 @@ mi::base::Handle compileGeneratedMaterial( mi const std::vector& parameters, mi::Uint32 compileFlags ) { - mi::base::Handle mdlFactory( neuray->get_api_component() ); + MdlFactoryHandle mdlFactory( neuray->get_api_component() ); requireMdl( mdlFactory.is_valid_interface(), "Failed to get MDL factory" ); - mi::base::Handle mdlImpexpApi( neuray->get_api_component() ); + MdlImpexpApiHandle mdlImpexpApi( neuray->get_api_component() ); requireMdl( mdlImpexpApi.is_valid_interface(), "Failed to get MDL import/export API" ); - mi::base::Handle moduleDbName( mdlFactory->get_db_module_name( source.moduleName.c_str() ) ); + ConstStringHandle moduleDbName( mdlFactory->get_db_module_name( source.moduleName.c_str() ) ); requireMdl( moduleDbName.is_valid_interface(), generatedMdlMessage( "Failed to get generated MDL module DB name", source, key ) ); - mi::base::Handle module( transaction->access( moduleDbName->get_c_str() ) ); + ModuleHandle module( transaction->access( moduleDbName->get_c_str() ) ); if( !module.is_valid_interface() ) { context->clear_messages(); @@ -637,34 +458,34 @@ mi::base::Handle compileGeneratedMaterial( mi const char* const materialDbName = module->get_material( 0 ); requireMdl( materialDbName != nullptr, generatedMdlMessage( "Generated MDL module had no material definition", source, key ) ); - mi::base::Handle materialDefinition( + FunctionDefinitionHandle materialDefinition( transaction->access( materialDbName ) ); requireMdl( materialDefinition.is_valid_interface(), generatedMdlMessage( "Failed to access generated MDL material definition", source, key ) ); mi::Sint32 callResult = 0; - mi::base::Handle materialCall( materialDefinition->create_function_call( nullptr, &callResult ) ); + FunctionCallHandle materialCall( materialDefinition->create_function_call( nullptr, &callResult ) ); requireMdl( callResult == 0 && materialCall.is_valid_interface(), generatedMdlMessage( "Failed to create generated MDL material call", source, key ) ); - mi::base::Handle valueFactory( mdlFactory->create_value_factory( transaction ) ); + ValueFactoryHandle valueFactory( mdlFactory->create_value_factory( transaction ) ); requireMdl( valueFactory.is_valid_interface(), generatedMdlMessage( "Failed to create MDL value factory", source, key ) ); - mi::base::Handle expressionFactory( mdlFactory->create_expression_factory( transaction ) ); + ExpressionFactoryHandle expressionFactory( mdlFactory->create_expression_factory( transaction ) ); requireMdl( expressionFactory.is_valid_interface(), generatedMdlMessage( "Failed to create MDL expression factory", source, key ) ); bindGeneratedMaterialParameters( materialCall.get(), valueFactory.get(), expressionFactory.get(), source, key, parameters ); - mi::base::Handle materialInstance( + MaterialInstanceHandle materialInstance( materialCall->get_interface() ); requireMdl( materialInstance.is_valid_interface(), generatedMdlMessage( "Failed to create generated MDL material instance", source, key ) ); - mi::base::Handle typeFactory( mdlFactory->create_type_factory( transaction ) ); + TypeFactoryHandle typeFactory( mdlFactory->create_type_factory( transaction ) ); requireMdl( typeFactory.is_valid_interface(), generatedMdlMessage( "Failed to create MDL type factory", source, key ) ); - mi::base::Handle standardMaterialType( + TypeHandle standardMaterialType( typeFactory->get_predefined_struct( mi::neuraylib::IType_struct::SID_MATERIAL ) ); requireMdl( standardMaterialType.is_valid_interface(), generatedMdlMessage( "Failed to get MDL material type", source, key ) ); @@ -672,7 +493,7 @@ mi::base::Handle compileGeneratedMaterial( mi const mi::Sint32 targetTypeResult = context->set_option( "target_type", standardMaterialType.get() ); requireMdl( targetTypeResult == 0, generatedMdlMessage( "Failed to set MDL target material type", source, key ), context ); - mi::base::Handle compiledMaterial( + CompiledMaterialHandle compiledMaterial( materialInstance->create_compiled_material( compileFlags, context ) ); requireMdl( compiledMaterial.is_valid_interface(), generatedMdlMessage( "Failed to compile generated MDL material", source, key ), context ); @@ -684,7 +505,7 @@ const char* findMdlPreviewColorExpressionPath( const mi::neuraylib::ICompiled_ma static const char* const paths[] = { "surface.scattering.tint", "ior" }; for( const char* path : paths ) { - mi::base::Handle expression( compiledMaterial->lookup_sub_expression( path ) ); + ConstExpressionHandle expression( compiledMaterial->lookup_sub_expression( path ) ); if( expression.is_valid_interface() ) { return path; @@ -693,79 +514,104 @@ const char* findMdlPreviewColorExpressionPath( const mi::neuraylib::ICompiled_ma failMdl( "Generated MDL material has no preview color expression" ); } -MdlMaterialTargetCode compileMdlMaterialTargetCode( const MaterialGroup& group, bool includeBsdfCallables ) +ExecutionContextHandle createMdlExecutionContext( const NeurayHandle& neuray ) { - MdlSdkSession session; - requireMdl( session.isStarted(), session.error() ); + MdlFactoryHandle mdlFactory( neuray->get_api_component() ); + requireMdl( mdlFactory.is_valid_interface(), "Failed to get MDL factory" ); - mi::base::Handle database( session.neuray()->get_api_component() ); - requireMdl( database.is_valid_interface(), "Failed to get MDL database" ); + ExecutionContextHandle context( mdlFactory->create_execution_context() ); + requireMdl( context.is_valid_interface(), "Failed to create MDL execution context" ); + return context; +} - mi::base::Handle scope( database->get_global_scope() ); - requireMdl( scope.is_valid_interface(), "Failed to get MDL global scope" ); +CompiledMaterialHandle compileMdlGroupMaterial( const NeurayHandle& neuray, + const TransactionHandle& transaction, + const ExecutionContextHandle& context, + const MaterialGroup& group ) +{ + const otk::pbrt::PbrtMaterial syntheticMaterial{ makeSyntheticMatteMaterial( group.material.Kd ) }; + const otk::pbrt::PbrtMaterial& material{ + group.pbrtMaterial && !group.pbrtMaterial->type.empty() ? *group.pbrtMaterial : syntheticMaterial }; + MdlGeneratedSourceCache sourceCache; + const MdlShaderKey key{ makeMdlShaderKey( material ) }; + const GeneratedMdlSource& source{ sourceCache.getSource( material ) }; + return compileGeneratedMaterial( neuray.get(), transaction.get(), context.get(), source, key, + std::vector{}, + mi::neuraylib::IMaterial_instance::CLASS_COMPILATION ); +} - mi::base::Handle transaction( scope->create_transaction() ); - requireMdl( transaction.is_valid_interface(), "Failed to create MDL transaction" ); +BackendHandle createMdlPtxBackend( const NeurayHandle& neuray ) +{ + BackendApiHandle backendApi( + neuray->get_api_component() ); + requireMdl( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); - MdlMaterialTargetCode targetCode; - { - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - requireMdl( mdlFactory.is_valid_interface(), "Failed to get MDL factory" ); + BackendHandle backend( backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); + requireMdl( backend.is_valid_interface(), "Failed to get MDL CUDA PTX backend" ); + requireMdl( backend->set_option( "sm_version", "50" ) == 0, "Failed to set MDL CUDA PTX target architecture" ); + requireMdl( backend->set_option( "visible_functions", MDL_MATERIAL_TINT_FUNCTION_NAME ) == 0, + "Failed to restrict MDL tint visible functions" ); + return backend; +} - mi::base::Handle context( mdlFactory->create_execution_context() ); - requireMdl( context.is_valid_interface(), "Failed to create MDL execution context" ); +TargetCodeHandle translateMdlTint( const TransactionHandle& transaction, const CompiledMaterialHandle& compiledMaterial, + const BackendHandle& backend, const ExecutionContextHandle& context ) +{ + const char* const expressionPath{ findMdlPreviewColorExpressionPath( compiledMaterial.get() ) }; + context->clear_messages(); + TargetCodeHandle targetCode( backend->translate_material_expression( + transaction.get(), compiledMaterial.get(), expressionPath, MDL_MATERIAL_TINT_FUNCTION_NAME, context.get() ) ); + requireMdl( targetCode.is_valid_interface(), "Failed to translate MDL tint expression to PTX", context.get() ); + requireMdl( targetCode->get_code_size() > 0U, "MDL generated empty PTX target code" ); + requireMdl( targetCode->get_callable_function_count() == 1U, "MDL generated unexpected callable function count" ); + requireMdl( std::string( targetCode->get_callable_function( 0 ) ) == MDL_MATERIAL_TINT_FUNCTION_NAME, + "MDL generated unexpected callable function name" ); + return targetCode; +} - const otk::pbrt::PbrtMaterial syntheticMaterial{ makeSyntheticMatteMaterial( group.material.Kd ) }; - const otk::pbrt::PbrtMaterial& pbrtMaterial{ - group.pbrtMaterial && !group.pbrtMaterial->type.empty() ? *group.pbrtMaterial : syntheticMaterial }; - MdlGeneratedSourceCache sourceCache; - const MdlShaderKey key{ makeMdlShaderKey( pbrtMaterial ) }; - const GeneratedMdlSource& source{ sourceCache.getSource( pbrtMaterial ) }; - mi::base::Handle compiledMaterial( compileGeneratedMaterial( - session.neuray(), transaction.get(), context.get(), source, key, std::vector{}, - mi::neuraylib::IMaterial_instance::CLASS_COMPILATION ) ); - const char* const previewColorExpressionPath{ findMdlPreviewColorExpressionPath( compiledMaterial.get() ) }; +MdlMaterialTargetCode captureMdlTint( const TargetCodeHandle& targetCode, const CompiledMaterialHandle& compiledMaterial, + const ExecutionContextHandle& context ) +{ + MdlMaterialTargetCode result; + result.tintPtx.assign( targetCode->get_code(), static_cast( targetCode->get_code_size() ) ); + result.tintArgumentBlock = captureMdlTargetArgumentBlock( targetCode.get(), compiledMaterial.get(), context.get() ); + return result; +} - mi::base::Handle backendApi( - session.neuray()->get_api_component() ); - requireMdl( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); +void appendMdlBsdfCallables( MdlMaterialTargetCode& result, const NeurayHandle& neuray, + const TransactionHandle& transaction, const CompiledMaterialHandle& compiledMaterial, + const ExecutionContextHandle& context ) +{ + result.bsdfPtx = compileMdlBsdfCallablesToPtx( neuray.get(), transaction.get(), compiledMaterial.get(), context.get(), + "surface.scattering", MDL_MATERIAL_BSDF_FUNCTION_NAME ); + result.hasBsdfCallables = true; +} - mi::base::Handle ptxBackend( - backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); - requireMdl( ptxBackend.is_valid_interface(), "Failed to get MDL CUDA PTX backend" ); - requireMdl( ptxBackend->set_option( "sm_version", "50" ) == 0, - "Failed to set MDL CUDA PTX target architecture" ); - requireMdl( ptxBackend->set_option( "visible_functions", MDL_MATERIAL_TINT_FUNCTION_NAME ) == 0, - "Failed to restrict MDL tint visible functions" ); +MdlMaterialTargetCode compileMdlMaterialTargetCode( const MaterialGroup& group, bool includeBsdfCallables ) +{ + MdlSdkSession session; + requireMdl( session.isStarted(), session.error() ); + const NeurayHandle& neuray{ session.handle() }; + MdlTransaction transaction{ neuray }; + const TransactionHandle& transactionHandle{ transaction.handle() }; + + MdlMaterialTargetCode result; + { + ExecutionContextHandle context{ createMdlExecutionContext( neuray ) }; + CompiledMaterialHandle compiledMaterial{ compileMdlGroupMaterial( neuray, transactionHandle, context, group ) }; + BackendHandle backend{ createMdlPtxBackend( neuray ) }; + TargetCodeHandle tintTargetCode{ translateMdlTint( transactionHandle, compiledMaterial, backend, context ) }; + result = captureMdlTint( tintTargetCode, compiledMaterial, context ); - context->clear_messages(); - mi::base::Handle tintTargetCode( - ptxBackend->translate_material_expression( transaction.get(), compiledMaterial.get(), previewColorExpressionPath, - MDL_MATERIAL_TINT_FUNCTION_NAME, context.get() ) ); - requireMdl( tintTargetCode.is_valid_interface(), "Failed to translate MDL tint expression to PTX", context.get() ); - requireMdl( tintTargetCode->get_code_size() > 0U, "MDL generated empty PTX target code" ); - requireMdl( tintTargetCode->get_callable_function_count() == 1U, - "MDL generated unexpected callable function count" ); - requireMdl( std::string( tintTargetCode->get_callable_function( 0 ) ) == MDL_MATERIAL_TINT_FUNCTION_NAME, - "MDL generated unexpected callable function name" ); - - targetCode.tintPtx.assign( tintTargetCode->get_code(), static_cast( tintTargetCode->get_code_size() ) ); - targetCode.tintArgumentBlock = - captureMdlTargetArgumentBlock( tintTargetCode.get(), compiledMaterial.get(), context.get() ); if( includeBsdfCallables ) { - targetCode.bsdfPtx = - compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), compiledMaterial.get(), - context.get(), "surface.scattering", MDL_MATERIAL_BSDF_FUNCTION_NAME ); - targetCode.hasBsdfCallables = true; + appendMdlBsdfCallables( result, neuray, transactionHandle, compiledMaterial, context ); } } - requireMdl( transaction->commit() == 0, "Failed to commit MDL transaction" ); - transaction.reset(); - scope.reset(); - database.reset(); - requireMdl( session.shutdown() == 0, "Failed to shut down MDL SDK" ); - return targetCode; + + transaction.commit(); + session.close(); + return result; } struct MdlMaterialBuildJob @@ -1141,7 +987,7 @@ MdlMaterialBuildResult buildMdlMaterialPipelineState( const MdlMaterialBuildJob& job.moduleCache->getOrCreate( job.optixContext, job.pipelineCompileOptions, targetCode.bsdfPtx.ptx ); } result.closestHitModule = createOptixModule( job.optixContext, job.pipelineCompileOptions, - MdlSmokeMaterialCudaText(), MdlSmokeMaterialCudaSize ); + MdlMaterialCudaText(), MdlMaterialCudaSize ); result.programGroups = job.programGroups; result.callableProgramGroups = job.callableProgramGroups; @@ -1611,7 +1457,7 @@ uint_t PbrtProgramGroups::getTriangleMdlMaterialSbtOffset( MaterialFlags flags ) const Stopwatch optixTimer; if( m_mdlMaterialClosestHitModule == nullptr ) { - m_mdlMaterialClosestHitModule = createModule( MdlSmokeMaterialCudaText(), MdlSmokeMaterialCudaSize ); + m_mdlMaterialClosestHitModule = createModule( MdlMaterialCudaText(), MdlMaterialCudaSize ); } OptixProgramGroupOptions options{}; diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/FourierBsdfEval.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/FourierBsdfEval.h index a62f58d2..c6c0cc48 100644 --- a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/FourierBsdfEval.h +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/FourierBsdfEval.h @@ -20,6 +20,9 @@ namespace demandPbrtScene { constexpr int FOURIER_BSDF_EVAL_MAX_ORDER{ 1599 }; constexpr int FOURIER_BSDF_EVAL_MAX_CHANNELS{ 3 }; +constexpr float FOURIER_PI{ 3.14159265358979323846f }; +constexpr float FOURIER_TWO_PI{ 2.0f * FOURIER_PI }; +constexpr float FOURIER_INV_TWO_PI{ 1.0f / FOURIER_TWO_PI }; enum class FourierBsdfTransportMode { @@ -375,16 +378,25 @@ DEMAND_PBRT_SCENE_FOURIER_HD int fourierFindCatmullRom2DInterval( const float* c return value > maxValue ? maxValue : value; } -DEMAND_PBRT_SCENE_FOURIER_HD bool fourierSampleCatmullRom2D( const FourierBsdfTableDeviceData& table, float alpha, float u, float& sample, float& pdf ) +struct FourierCatmullRomSegment { - int offset{}; - float weights[4]{}; - const float* mu{ fourierFloatData( table.mu ) }; - if( !fourierCatmullRomWeights( table.nMu, mu, alpha, offset, weights ) ) - { - return false; - } + float x0; + float width; + float f0; + float f1; + float d0; + float d1; + float target; + float maximum; +}; +DEMAND_PBRT_SCENE_FOURIER_HD bool fourierSelectCatmullRomSegment( const FourierBsdfTableDeviceData& table, + const float* nodes, + int offset, + const float weights[4], + float u, + FourierCatmullRomSegment& segment ) +{ const float* values{ fourierFloatData( table.zeroOrderCoefficients ) }; const float* cdf{ fourierFloatData( table.cdf ) }; const float maximum{ fourierInterpolateTableRow( cdf, table.nMu, table.nMu, offset, weights, table.nMu - 1 ) }; @@ -393,160 +405,230 @@ DEMAND_PBRT_SCENE_FOURIER_HD bool fourierSampleCatmullRom2D( const FourierBsdfTa return false; } - u *= maximum; - const int idx{ fourierFindCatmullRom2DInterval( cdf, table.nMu, table.nMu, offset, weights, u ) }; + const float target{ u * maximum }; + const int idx{ fourierFindCatmullRom2DInterval( cdf, table.nMu, table.nMu, offset, weights, target ) }; const float f0{ fourierInterpolateTableRow( values, table.nMu, table.nMu, offset, weights, idx ) }; const float f1{ fourierInterpolateTableRow( values, table.nMu, table.nMu, offset, weights, idx + 1 ) }; - const float x0{ mu[idx] }; - const float x1{ mu[idx + 1] }; + const float x0{ nodes[idx] }; + const float x1{ nodes[idx + 1] }; const float width{ x1 - x0 }; if( width == 0.0f ) { return false; } - u = ( u - fourierInterpolateTableRow( cdf, table.nMu, table.nMu, offset, weights, idx ) ) / width; - - const float d0{ idx > 0 ? width * ( f1 - fourierInterpolateTableRow( values, table.nMu, table.nMu, offset, weights, idx - 1 ) ) - / ( x1 - mu[idx - 1] ) : - f1 - f0 }; - const float d1{ idx + 2 < table.nMu ? - width * ( fourierInterpolateTableRow( values, table.nMu, table.nMu, offset, weights, idx + 2 ) - f0 ) - / ( mu[idx + 2] - x0 ) : - f1 - f0 }; + segment.x0 = x0; + segment.width = width; + segment.f0 = f0; + segment.f1 = f1; + segment.d0 = idx > 0 ? + width * ( f1 - fourierInterpolateTableRow( values, table.nMu, table.nMu, offset, weights, idx - 1 ) ) + / ( x1 - nodes[idx - 1] ) : + f1 - f0; + segment.d1 = idx + 2 < table.nMu ? + width * ( fourierInterpolateTableRow( values, table.nMu, table.nMu, offset, weights, idx + 2 ) - f0 ) + / ( nodes[idx + 2] - x0 ) : + f1 - f0; + segment.target = ( target - fourierInterpolateTableRow( cdf, table.nMu, table.nMu, offset, weights, idx ) ) / width; + segment.maximum = maximum; + return true; +} - float t{}; - if( f0 != f1 ) - { - t = ( f0 - fourierSqrt( fourierMax( 0.0f, f0 * f0 + 2.0f * u * ( f1 - f0 ) ) ) ) / ( f0 - f1 ); - } - else if( f0 != 0.0f ) +DEMAND_PBRT_SCENE_FOURIER_HD bool fourierInitialCatmullRomEstimate( const FourierCatmullRomSegment& segment, float& estimate ) +{ + if( segment.f0 != segment.f1 ) { - t = u / f0; + estimate = ( segment.f0 - fourierSqrt( fourierMax( 0.0f, segment.f0 * segment.f0 + + 2.0f * segment.target * ( segment.f1 - segment.f0 ) ) ) ) + / ( segment.f0 - segment.f1 ); + return true; } - else + if( segment.f0 != 0.0f ) { - return false; + estimate = segment.target / segment.f0; + return true; } + return false; +} + +DEMAND_PBRT_SCENE_FOURIER_HD void fourierEvaluateCatmullRomSegment( const FourierCatmullRomSegment& segment, + float t, + float& integral, + float& value ) +{ + integral = t * ( segment.f0 + + t * ( 0.5f * segment.d0 + + t * ( ( 1.0f / 3.0f ) * ( -2.0f * segment.d0 - segment.d1 ) + segment.f1 + - segment.f0 + + t * ( 0.25f * ( segment.d0 + segment.d1 ) + + 0.5f * ( segment.f0 - segment.f1 ) ) ) ) ); + value = segment.f0 + + t * ( segment.d0 + + t * ( -2.0f * segment.d0 - segment.d1 + 3.0f * ( segment.f1 - segment.f0 ) + + t * ( segment.d0 + segment.d1 + 2.0f * ( segment.f0 - segment.f1 ) ) ) ); +} + +DEMAND_PBRT_SCENE_FOURIER_HD bool fourierNewtonConverged( float residual, float low, float high ) +{ + return fourierAbs( residual ) < 1.0e-6f || high - low < 1.0e-6f; +} + +DEMAND_PBRT_SCENE_FOURIER_HD float fourierNewtonEstimate( float estimate, float residual, float derivative ) +{ + return derivative != 0.0f ? estimate - residual / derivative : estimate; +} - float a{ 0.0f }; - float b{ 1.0f }; - float fhat{}; - for( int iter = 0; iter < 32; ++iter ) +DEMAND_PBRT_SCENE_FOURIER_HD float fourierInvertCatmullRomSegment( const FourierCatmullRomSegment& segment, + float estimate, + float& value ) +{ + float low{ 0.0f }; + float high{ 1.0f }; + value = 0.0f; + for( int i = 0; i < 32; ++i ) { - if( !( t >= a && t <= b ) ) + if( !( estimate >= low && estimate <= high ) ) { - t = 0.5f * ( a + b ); + estimate = 0.5f * ( low + high ); } - const float fhatIntegral = - t * ( f0 + t * ( 0.5f * d0 + t * ( ( 1.0f / 3.0f ) * ( -2.0f * d0 - d1 ) + f1 - f0 + t * ( 0.25f * ( d0 + d1 ) + 0.5f * ( f0 - f1 ) ) ) ) ); - fhat = f0 + t * ( d0 + t * ( -2.0f * d0 - d1 + 3.0f * ( f1 - f0 ) + t * ( d0 + d1 + 2.0f * ( f0 - f1 ) ) ) ); - - if( fourierAbs( fhatIntegral - u ) < 1.0e-6f || b - a < 1.0e-6f ) + float integral{}; + fourierEvaluateCatmullRomSegment( segment, estimate, integral, value ); + const float residual{ integral - segment.target }; + if( fourierNewtonConverged( residual, low, high ) ) { break; } - if( fhatIntegral - u < 0.0f ) + if( residual < 0.0f ) { - a = t; + low = estimate; } else { - b = t; - } - - if( fhat != 0.0f ) - { - t -= ( fhatIntegral - u ) / fhat; + high = estimate; } + estimate = fourierNewtonEstimate( estimate, residual, value ); } - - sample = x0 + width * t; - pdf = fhat / maximum; - return pdf > 0.0f; + return estimate; } -DEMAND_PBRT_SCENE_FOURIER_HD bool fourierSampleFourier( const float* coefficients, int order, float u, float& value, float& pdf, float& phi ) +DEMAND_PBRT_SCENE_FOURIER_HD bool fourierSampleCatmullRom2D( const FourierBsdfTableDeviceData& table, float alpha, float u, float& sample, float& pdf ) { - constexpr float PI{ 3.14159265358979323846f }; - constexpr float TWO_PI{ 2.0f * PI }; - constexpr float INV_TWO_PI{ 1.0f / TWO_PI }; - if( coefficients == nullptr || order <= 0 || coefficients[0] <= 0.0f ) + int offset{}; + float weights[4]{}; + const float* nodes{ fourierFloatData( table.mu ) }; + if( !fourierCatmullRomWeights( table.nMu, nodes, alpha, offset, weights ) ) { return false; } - const bool flip{ u >= 0.5f }; - if( flip ) + FourierCatmullRomSegment segment{}; + if( !fourierSelectCatmullRomSegment( table, nodes, offset, weights, u, segment ) ) { - u = 1.0f - 2.0f * ( u - 0.5f ); + return false; } - else + + float estimate{}; + if( !fourierInitialCatmullRomEstimate( segment, estimate ) ) { - u *= 2.0f; + return false; } - float a{ 0.0f }; - float b{ PI }; - phi = 0.5f * PI; - float f{}; - for( int iter = 0; iter < 32; ++iter ) - { - const float cosPhi{ fourierCos( phi ) }; - const float sinPhi{ fourierSqrt( fourierMax( 0.0f, 1.0f - cosPhi * cosPhi ) ) }; - float cosPhiPrev{ cosPhi }; - float cosPhiCur{ 1.0f }; - float sinPhiPrev{ -sinPhi }; - float sinPhiCur{ 0.0f }; + float value{}; + estimate = fourierInvertCatmullRomSegment( segment, estimate, value ); + sample = segment.x0 + segment.width * estimate; + pdf = value / segment.maximum; + return pdf > 0.0f; +} - float cdf{ coefficients[0] * phi }; - f = coefficients[0]; - for( int k = 1; k < order; ++k ) - { - const float sinPhiNext{ 2.0f * cosPhi * sinPhiCur - sinPhiPrev }; - const float cosPhiNext{ 2.0f * cosPhi * cosPhiCur - cosPhiPrev }; - sinPhiPrev = sinPhiCur; - sinPhiCur = sinPhiNext; - cosPhiPrev = cosPhiCur; - cosPhiCur = cosPhiNext; - - cdf += coefficients[k] * ( 1.0f / static_cast( k ) ) * sinPhiNext; - f += coefficients[k] * cosPhiNext; - } - cdf -= u * coefficients[0] * PI; +struct FourierSeriesEvaluation +{ + float residual; + float value; +}; + +DEMAND_PBRT_SCENE_FOURIER_HD float fourierFoldSample( float u, bool& flip ) +{ + flip = u >= 0.5f; + return flip ? 1.0f - 2.0f * ( u - 0.5f ) : 2.0f * u; +} + +DEMAND_PBRT_SCENE_FOURIER_HD FourierSeriesEvaluation fourierEvaluateSeriesIntegral( const float* coefficients, + int order, + float phi, + float u ) +{ + const float cosPhi{ fourierCos( phi ) }; + const float sinPhi{ fourierSqrt( fourierMax( 0.0f, 1.0f - cosPhi * cosPhi ) ) }; + float cosPhiPrev{ cosPhi }; + float cosPhiCur{ 1.0f }; + float sinPhiPrev{ -sinPhi }; + float sinPhiCur{ 0.0f }; + float cdf{ coefficients[0] * phi }; + float value{ coefficients[0] }; + for( int k = 1; k < order; ++k ) + { + const float sinPhiNext{ 2.0f * cosPhi * sinPhiCur - sinPhiPrev }; + const float cosPhiNext{ 2.0f * cosPhi * cosPhiCur - cosPhiPrev }; + sinPhiPrev = sinPhiCur; + sinPhiCur = sinPhiNext; + cosPhiPrev = cosPhiCur; + cosPhiCur = cosPhiNext; + cdf += coefficients[k] * ( 1.0f / static_cast( k ) ) * sinPhiNext; + value += coefficients[k] * cosPhiNext; + } + return FourierSeriesEvaluation{ cdf - u * coefficients[0] * FOURIER_PI, value }; +} - if( cdf > 0.0f ) +DEMAND_PBRT_SCENE_FOURIER_HD float fourierInvertSeries( const float* coefficients, int order, float u, float& phi ) +{ + float low{ 0.0f }; + float high{ FOURIER_PI }; + phi = 0.5f * FOURIER_PI; + float value{}; + for( int i = 0; i < 32; ++i ) + { + const FourierSeriesEvaluation evaluation{ fourierEvaluateSeriesIntegral( coefficients, order, phi, u ) }; + value = evaluation.value; + if( evaluation.residual > 0.0f ) { - b = phi; + high = phi; } else { - a = phi; + low = phi; } - if( fourierAbs( cdf ) < 1.0e-6f || b - a < 1.0e-6f ) + if( fourierNewtonConverged( evaluation.residual, low, high ) ) { break; } - if( f != 0.0f ) - { - phi -= cdf / f; - } - if( !( phi > a && phi < b ) ) + phi = fourierNewtonEstimate( phi, evaluation.residual, value ); + if( !( phi > low && phi < high ) ) { - phi = 0.5f * ( a + b ); + phi = 0.5f * ( low + high ); } } + return value; +} + +DEMAND_PBRT_SCENE_FOURIER_HD bool fourierSampleFourier( const float* coefficients, int order, float u, float& value, float& pdf, float& phi ) +{ + if( coefficients == nullptr || order <= 0 || coefficients[0] <= 0.0f ) + { + return false; + } + bool flip{}; + u = fourierFoldSample( u, flip ); + value = fourierInvertSeries( coefficients, order, u, phi ); if( flip ) { - phi = TWO_PI - phi; + phi = FOURIER_TWO_PI - phi; } - value = f; - pdf = INV_TWO_PI * f / coefficients[0]; + pdf = FOURIER_INV_TWO_PI * value / coefficients[0]; return pdf > 0.0f; } diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MaterialAdapters.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MaterialAdapters.h index bfb72128..cb01368b 100644 --- a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MaterialAdapters.h +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MaterialAdapters.h @@ -219,110 +219,116 @@ inline bool findPbrtScalar( const ::pbrt::ParamSet& params, const char* name, fl return false; } -inline bool findPbrtTextureInputConstantColor( const ::pbrt::ParamSet& params, const char* name, const float3& defaultValue, float3& value ) +struct PbrtColorTextureTraits { - if( findPbrtConstantColor( params, name, value ) ) + using Value = float3; + + static Value one() { return pbrtTextureScale( 1.0f ); } + static bool foldsMix() { return false; } + + static const otk::pbrt::PbrtTexture* findTexture( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + std::string& graphKey ) { - return true; + return findPbrtGraphTexture( graph, textureName, { "spectrum", "color" }, graphKey ); } - float floatValue{}; - if( findPbrtConstantFloat( params, name, floatValue ) ) + static bool findConstant( const ::pbrt::ParamSet& params, + const char* name, + const Value& defaultValue, + Value& value ) { - value = pbrtTextureScale( floatValue ); - return true; - } + if( findPbrtConstantColor( params, name, value ) ) + { + return true; + } - value = defaultValue; - return true; -} + float scalar{}; + if( findPbrtConstantFloat( params, name, scalar ) ) + { + value = pbrtTextureScale( scalar ); + return true; + } -inline bool foldPbrtTextureConstantColor( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack, - float3& value ); + value = defaultValue; + return true; + } -inline bool foldPbrtTextureConstantFloat( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack, - float& value ); + static Value multiply( const Value& lhs, const Value& rhs ) { return multiplyPbrtTextureScale( lhs, rhs ); } -inline bool foldPbrtTextureInputConstantColor( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - const char* name, - const float3& defaultValue, - std::vector& textureStack, - float3& value ) -{ - const std::string inputTextureName{ texture.params.FindTexture( name ) }; - if( !inputTextureName.empty() ) + static Value mix( const Value& lhs, const Value& rhs, float amount ) { - return foldPbrtTextureConstantColor( graph, inputTextureName, textureStack, value ); + return addPbrtTextureColor( scalePbrtTextureColor( lhs, 1.0f - amount ), + scalePbrtTextureColor( rhs, amount ) ); } - return findPbrtTextureInputConstantColor( texture.params, name, defaultValue, value ); -} -inline bool foldPbrtTextureInputConstantFloat( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - const char* name, - float defaultValue, - std::vector& textureStack, - float& value ) + static float3 color( const Value& value ) { return value; } +}; + +struct PbrtFloatTextureTraits { - const std::string inputTextureName{ texture.params.FindTexture( name ) }; - if( !inputTextureName.empty() ) + using Value = float; + + static Value one() { return 1.0f; } + static bool foldsMix() { return true; } + + static const otk::pbrt::PbrtTexture* findTexture( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + std::string& graphKey ) { - return foldPbrtTextureConstantFloat( graph, inputTextureName, textureStack, value ); + return findPbrtGraphTexture( graph, textureName, { "float" }, graphKey ); } - if( findPbrtScalar( texture.params, name, value ) ) + + static bool findConstant( const ::pbrt::ParamSet& params, + const char* name, + Value defaultValue, + Value& value ) { + if( findPbrtScalar( params, name, value ) ) + { + return true; + } + value = defaultValue; return true; } - value = defaultValue; - return true; -} -inline bool foldPbrtTextureConstantColor( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, + static Value multiply( Value lhs, Value rhs ) { return lhs * rhs; } + static Value mix( Value lhs, Value rhs, float amount ) { return lhs * ( 1.0f - amount ) + rhs * amount; } + static float3 color( Value value ) { return pbrtTextureScale( value ); } +}; + +template +inline bool foldPbrtTextureConstant( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + std::vector& textureStack, + typename Traits::Value& value ); + +template +inline bool foldPbrtTextureInputConstant( const otk::pbrt::PbrtMaterialGraph& graph, + const otk::pbrt::PbrtTexture& texture, + const char* name, + const typename Traits::Value& defaultValue, std::vector& textureStack, - float3& value ) + typename Traits::Value& value ) { - std::string graphKey; - const otk::pbrt::PbrtTexture* texture{ findPbrtGraphTexture( graph, textureName, { "spectrum", "color" }, graphKey ) }; - if( texture == nullptr || pbrtTextureStackContains( textureStack, graphKey ) ) - { - return false; - } - - textureStack.push_back( graphKey ); - bool folded{ false }; - if( texture->type == "constant" ) + const std::string inputTextureName{ texture.params.FindTexture( name ) }; + if( !inputTextureName.empty() ) { - folded = findPbrtTextureInputConstantColor( texture->params, "value", pbrtTextureScale( 1.0f ), value ); + return foldPbrtTextureConstant( graph, inputTextureName, textureStack, value ); } - else if( texture->type == "scale" ) - { - float3 tex1{}; - float3 tex2{}; - folded = foldPbrtTextureInputConstantColor( graph, *texture, "tex1", pbrtTextureScale( 1.0f ), textureStack, tex1 ) - && foldPbrtTextureInputConstantColor( graph, *texture, "tex2", pbrtTextureScale( 1.0f ), textureStack, tex2 ); - if( folded ) - { - value = multiplyPbrtTextureScale( tex1, tex2 ); - } - } - - textureStack.pop_back(); - return folded; + return Traits::findConstant( texture.params, name, defaultValue, value ); } -inline bool foldPbrtTextureConstantFloat( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack, - float& value ) +template +inline bool foldPbrtTextureConstant( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + std::vector& textureStack, + typename Traits::Value& value ) { - std::string graphKey; - const otk::pbrt::PbrtTexture* texture{ findPbrtGraphTexture( graph, textureName, { "float" }, graphKey ) }; + using Value = typename Traits::Value; + + std::string graphKey; + const otk::pbrt::PbrtTexture* texture{ Traits::findTexture( graph, textureName, graphKey ) }; if( texture == nullptr || pbrtTextureStackContains( textureStack, graphKey ) ) { return false; @@ -332,30 +338,31 @@ inline bool foldPbrtTextureConstantFloat( const otk::pbrt::PbrtMaterialGraph& gr bool folded{ false }; if( texture->type == "constant" ) { - folded = foldPbrtTextureInputConstantFloat( graph, *texture, "value", 1.0f, textureStack, value ); + folded = Traits::findConstant( texture->params, "value", Traits::one(), value ); } else if( texture->type == "scale" ) { - float tex1{}; - float tex2{}; - folded = foldPbrtTextureInputConstantFloat( graph, *texture, "tex1", 1.0f, textureStack, tex1 ) - && foldPbrtTextureInputConstantFloat( graph, *texture, "tex2", 1.0f, textureStack, tex2 ); + Value tex1{}; + Value tex2{}; + folded = foldPbrtTextureInputConstant( graph, *texture, "tex1", Traits::one(), textureStack, tex1 ) + && foldPbrtTextureInputConstant( graph, *texture, "tex2", Traits::one(), textureStack, tex2 ); if( folded ) { - value = tex1 * tex2; + value = Traits::multiply( tex1, tex2 ); } } - else if( texture->type == "mix" ) + else if( texture->type == "mix" && Traits::foldsMix() ) { - float tex1{}; - float tex2{}; + Value tex1{}; + Value tex2{}; float amount{}; - folded = foldPbrtTextureInputConstantFloat( graph, *texture, "tex1", 1.0f, textureStack, tex1 ) - && foldPbrtTextureInputConstantFloat( graph, *texture, "tex2", 1.0f, textureStack, tex2 ) - && foldPbrtTextureInputConstantFloat( graph, *texture, "amount", 0.5f, textureStack, amount ); + folded = foldPbrtTextureInputConstant( graph, *texture, "tex1", Traits::one(), textureStack, tex1 ) + && foldPbrtTextureInputConstant( graph, *texture, "tex2", Traits::one(), textureStack, tex2 ) + && foldPbrtTextureInputConstant( graph, *texture, "amount", 0.5f, + textureStack, amount ); if( folded ) { - value = tex1 * ( 1.0f - amount ) + tex2 * amount; + value = Traits::mix( tex1, tex2, amount ); } } @@ -363,40 +370,25 @@ inline bool foldPbrtTextureConstantFloat( const otk::pbrt::PbrtMaterialGraph& gr return folded; } +enum class PbrtTextureTraversalPolicy +{ + SCALE_ONLY, + SCALE_AND_MIX, +}; + +template inline PbrtDemandTextureBinding findPbrtDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, const std::string& textureName, std::vector& textureStack, - bool allowMix ); - -inline PbrtDemandTextureBinding findPbrtFloatDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack, - bool allowMix ); + PbrtTextureTraversalPolicy policy ); +template inline PbrtDemandTextureBinding findPbrtDirectDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, const std::string& textureName, std::vector& textureStack ) { std::string graphKey; - const otk::pbrt::PbrtTexture* texture{ findPbrtGraphTexture( graph, textureName, { "spectrum", "color" }, graphKey ) }; - if( texture == nullptr || pbrtTextureStackContains( textureStack, graphKey ) ) - { - return pbrtDemandTextureBinding(); - } - if( texture->type != "imagemap" && texture->type != "checkerboard" ) - { - return pbrtDemandTextureBinding(); - } - return pbrtDemandTextureBinding( pbrtTextureMapName( texture ), pbrtTextureScale( 1.0f ), pbrtTextureBias( 0.0f ), - false, pbrtTextureGamma( texture ) ); -} - -inline PbrtDemandTextureBinding findPbrtDirectFloatDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack ) -{ - std::string graphKey; - const otk::pbrt::PbrtTexture* texture{ findPbrtGraphTexture( graph, textureName, { "float" }, graphKey ) }; + const otk::pbrt::PbrtTexture* texture{ Traits::findTexture( graph, textureName, graphKey ) }; if( texture == nullptr || pbrtTextureStackContains( textureStack, graphKey ) ) { return pbrtDemandTextureBinding(); @@ -409,53 +401,24 @@ inline PbrtDemandTextureBinding findPbrtDirectFloatDemandTextureBinding( const o false, pbrtTextureGamma( texture ) ); } +template inline bool findPbrtDemandTextureInputBinding( const otk::pbrt::PbrtMaterialGraph& graph, const otk::pbrt::PbrtTexture& texture, const char* name, std::vector& textureStack, PbrtDemandTextureBinding& binding, - bool allowMix ) + PbrtTextureTraversalPolicy policy ) { const std::string inputTextureName{ texture.params.FindTexture( name ) }; if( inputTextureName.empty() ) { return false; } - binding = findPbrtDemandTextureBinding( graph, inputTextureName, textureStack, allowMix ); - return hasPbrtDemandTextureBinding( binding ); -} - -inline bool findPbrtFloatDemandTextureInputBinding( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - const char* name, - std::vector& textureStack, - PbrtDemandTextureBinding& binding, - bool allowMix ) -{ - const std::string inputTextureName{ texture.params.FindTexture( name ) }; - if( inputTextureName.empty() ) - { - return false; - } - binding = findPbrtFloatDemandTextureBinding( graph, inputTextureName, textureStack, allowMix ); - return hasPbrtDemandTextureBinding( binding ); -} - -inline bool findPbrtDirectFloatDemandTextureInputBinding( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - const char* name, - std::vector& textureStack, - PbrtDemandTextureBinding& binding ) -{ - const std::string inputTextureName{ texture.params.FindTexture( name ) }; - if( inputTextureName.empty() ) - { - return false; - } - binding = findPbrtDirectFloatDemandTextureBinding( graph, inputTextureName, textureStack ); + binding = findPbrtDemandTextureBinding( graph, inputTextureName, textureStack, policy ); return hasPbrtDemandTextureBinding( binding ); } +template inline bool findPbrtDirectDemandTextureInputBinding( const otk::pbrt::PbrtMaterialGraph& graph, const otk::pbrt::PbrtTexture& texture, const char* name, @@ -467,51 +430,23 @@ inline bool findPbrtDirectDemandTextureInputBinding( const otk::pbrt::PbrtMateri { return false; } - binding = findPbrtDirectDemandTextureBinding( graph, inputTextureName, textureStack ); + binding = findPbrtDirectDemandTextureBinding( graph, inputTextureName, textureStack ); return hasPbrtDemandTextureBinding( binding ); } +template inline PbrtDemandTextureBinding findPbrtScaleDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, const otk::pbrt::PbrtTexture& texture, std::vector& textureStack ) { - PbrtDemandTextureBinding tex1Binding{}; - PbrtDemandTextureBinding tex2Binding{}; - const bool hasTex1Demand{ findPbrtDemandTextureInputBinding( graph, texture, "tex1", textureStack, tex1Binding, false ) }; - const bool hasTex2Demand{ findPbrtDemandTextureInputBinding( graph, texture, "tex2", textureStack, tex2Binding, false ) }; - if( hasTex1Demand == hasTex2Demand ) - { - return pbrtDemandTextureBinding(); - } + using Value = typename Traits::Value; - if( hasTex1Demand ) - { - float3 tex2Scale{}; - if( foldPbrtTextureInputConstantColor( graph, texture, "tex2", pbrtTextureScale( 1.0f ), textureStack, tex2Scale ) ) - { - return pbrtDemandTextureBinding( tex1Binding.fileName, multiplyPbrtTextureScale( tex1Binding.scale, tex2Scale ), - multiplyPbrtTextureScale( tex1Binding.bias, tex2Scale ), true, tex1Binding.gamma ); - } - return pbrtDemandTextureBinding(); - } - - float3 tex1Scale{}; - if( foldPbrtTextureInputConstantColor( graph, texture, "tex1", pbrtTextureScale( 1.0f ), textureStack, tex1Scale ) ) - { - return pbrtDemandTextureBinding( tex2Binding.fileName, multiplyPbrtTextureScale( tex2Binding.scale, tex1Scale ), - multiplyPbrtTextureScale( tex2Binding.bias, tex1Scale ), true, tex2Binding.gamma ); - } - return pbrtDemandTextureBinding(); -} - -inline PbrtDemandTextureBinding findPbrtScaleFloatDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - std::vector& textureStack ) -{ PbrtDemandTextureBinding tex1Binding{}; PbrtDemandTextureBinding tex2Binding{}; - const bool hasTex1Demand{ findPbrtFloatDemandTextureInputBinding( graph, texture, "tex1", textureStack, tex1Binding, false ) }; - const bool hasTex2Demand{ findPbrtFloatDemandTextureInputBinding( graph, texture, "tex2", textureStack, tex2Binding, false ) }; + const bool hasTex1Demand{ findPbrtDemandTextureInputBinding( + graph, texture, "tex1", textureStack, tex1Binding, PbrtTextureTraversalPolicy::SCALE_ONLY ) }; + const bool hasTex2Demand{ findPbrtDemandTextureInputBinding( + graph, texture, "tex2", textureStack, tex2Binding, PbrtTextureTraversalPolicy::SCALE_ONLY ) }; if( hasTex1Demand == hasTex2Demand ) { return pbrtDemandTextureBinding(); @@ -519,154 +454,84 @@ inline PbrtDemandTextureBinding findPbrtScaleFloatDemandTextureBinding( const ot if( hasTex1Demand ) { - float tex2Scale{}; - if( foldPbrtTextureInputConstantFloat( graph, texture, "tex2", 1.0f, textureStack, tex2Scale ) ) + Value tex2Scale{}; + if( foldPbrtTextureInputConstant( graph, texture, "tex2", Traits::one(), textureStack, tex2Scale ) ) { - const float3 scale{ pbrtTextureScale( tex2Scale ) }; + const float3 scale{ Traits::color( tex2Scale ) }; return pbrtDemandTextureBinding( tex1Binding.fileName, multiplyPbrtTextureScale( tex1Binding.scale, scale ), multiplyPbrtTextureScale( tex1Binding.bias, scale ), true, tex1Binding.gamma ); } return pbrtDemandTextureBinding(); } - float tex1Scale{}; - if( foldPbrtTextureInputConstantFloat( graph, texture, "tex1", 1.0f, textureStack, tex1Scale ) ) + Value tex1Scale{}; + if( foldPbrtTextureInputConstant( graph, texture, "tex1", Traits::one(), textureStack, tex1Scale ) ) { - const float3 scale{ pbrtTextureScale( tex1Scale ) }; + const float3 scale{ Traits::color( tex1Scale ) }; return pbrtDemandTextureBinding( tex2Binding.fileName, multiplyPbrtTextureScale( tex2Binding.scale, scale ), multiplyPbrtTextureScale( tex2Binding.bias, scale ), true, tex2Binding.gamma ); } return pbrtDemandTextureBinding(); } +template inline PbrtDemandTextureBinding findPbrtMixDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, const otk::pbrt::PbrtTexture& texture, std::vector& textureStack ) { - PbrtDemandTextureBinding tex1Binding{}; - PbrtDemandTextureBinding tex2Binding{}; - const bool hasTex1Demand{ findPbrtDirectDemandTextureInputBinding( graph, texture, "tex1", textureStack, tex1Binding ) }; - const bool hasTex2Demand{ findPbrtDirectDemandTextureInputBinding( graph, texture, "tex2", textureStack, tex2Binding ) }; - if( hasTex1Demand == hasTex2Demand ) - { - return pbrtDemandTextureBinding(); - } - - float amount{}; - if( !foldPbrtTextureInputConstantFloat( graph, texture, "amount", 0.5f, textureStack, amount ) ) - { - return pbrtDemandTextureBinding(); - } + using Value = typename Traits::Value; - if( hasTex1Demand ) - { - float3 tex2{}; - if( foldPbrtTextureInputConstantColor( graph, texture, "tex2", pbrtTextureScale( 1.0f ), textureStack, tex2 ) ) - { - return pbrtDemandTextureBinding( tex1Binding.fileName, scalePbrtTextureColor( tex1Binding.scale, 1.0f - amount ), - addPbrtTextureColor( scalePbrtTextureColor( tex1Binding.bias, 1.0f - amount ), - scalePbrtTextureColor( tex2, amount ) ), - true, tex1Binding.gamma ); - } - return pbrtDemandTextureBinding(); - } - - float3 tex1{}; - if( foldPbrtTextureInputConstantColor( graph, texture, "tex1", pbrtTextureScale( 1.0f ), textureStack, tex1 ) ) - { - return pbrtDemandTextureBinding( tex2Binding.fileName, scalePbrtTextureColor( tex2Binding.scale, amount ), - addPbrtTextureColor( scalePbrtTextureColor( tex1, 1.0f - amount ), - scalePbrtTextureColor( tex2Binding.bias, amount ) ), - true, tex2Binding.gamma ); - } - return pbrtDemandTextureBinding(); -} - -inline PbrtDemandTextureBinding findPbrtMixFloatDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, - const otk::pbrt::PbrtTexture& texture, - std::vector& textureStack ) -{ PbrtDemandTextureBinding tex1Binding{}; PbrtDemandTextureBinding tex2Binding{}; - const bool hasTex1Demand{ findPbrtDirectFloatDemandTextureInputBinding( graph, texture, "tex1", textureStack, tex1Binding ) }; - const bool hasTex2Demand{ findPbrtDirectFloatDemandTextureInputBinding( graph, texture, "tex2", textureStack, tex2Binding ) }; + const bool hasTex1Demand{ + findPbrtDirectDemandTextureInputBinding( graph, texture, "tex1", textureStack, tex1Binding ) }; + const bool hasTex2Demand{ + findPbrtDirectDemandTextureInputBinding( graph, texture, "tex2", textureStack, tex2Binding ) }; if( hasTex1Demand == hasTex2Demand ) { return pbrtDemandTextureBinding(); } float amount{}; - if( !foldPbrtTextureInputConstantFloat( graph, texture, "amount", 0.5f, textureStack, amount ) ) + if( !foldPbrtTextureInputConstant( graph, texture, "amount", 0.5f, textureStack, amount ) ) { return pbrtDemandTextureBinding(); } if( hasTex1Demand ) { - float tex2{}; - if( foldPbrtTextureInputConstantFloat( graph, texture, "tex2", 1.0f, textureStack, tex2 ) ) + Value tex2{}; + if( foldPbrtTextureInputConstant( graph, texture, "tex2", Traits::one(), textureStack, tex2 ) ) { - return pbrtDemandTextureBinding( tex1Binding.fileName, scalePbrtTextureColor( tex1Binding.scale, 1.0f - amount ), - addPbrtTextureColor( scalePbrtTextureColor( tex1Binding.bias, 1.0f - amount ), - pbrtTextureBias( tex2 * amount ) ), - true, tex1Binding.gamma ); + return pbrtDemandTextureBinding( + tex1Binding.fileName, scalePbrtTextureColor( tex1Binding.scale, 1.0f - amount ), + addPbrtTextureColor( scalePbrtTextureColor( tex1Binding.bias, 1.0f - amount ), + scalePbrtTextureColor( Traits::color( tex2 ), amount ) ), + true, tex1Binding.gamma ); } return pbrtDemandTextureBinding(); } - float tex1{}; - if( foldPbrtTextureInputConstantFloat( graph, texture, "tex1", 1.0f, textureStack, tex1 ) ) + Value tex1{}; + if( foldPbrtTextureInputConstant( graph, texture, "tex1", Traits::one(), textureStack, tex1 ) ) { - return pbrtDemandTextureBinding( tex2Binding.fileName, scalePbrtTextureColor( tex2Binding.scale, amount ), - addPbrtTextureColor( pbrtTextureBias( tex1 * ( 1.0f - amount ) ), - scalePbrtTextureColor( tex2Binding.bias, amount ) ), - true, tex2Binding.gamma ); + return pbrtDemandTextureBinding( + tex2Binding.fileName, scalePbrtTextureColor( tex2Binding.scale, amount ), + addPbrtTextureColor( scalePbrtTextureColor( Traits::color( tex1 ), 1.0f - amount ), + scalePbrtTextureColor( tex2Binding.bias, amount ) ), + true, tex2Binding.gamma ); } return pbrtDemandTextureBinding(); } +template inline PbrtDemandTextureBinding findPbrtDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, const std::string& textureName, std::vector& textureStack, - bool allowMix ) + PbrtTextureTraversalPolicy policy ) { std::string graphKey; - const otk::pbrt::PbrtTexture* texture{ findPbrtGraphTexture( graph, textureName, { "spectrum", "color" }, graphKey ) }; - if( texture == nullptr || pbrtTextureStackContains( textureStack, graphKey ) ) - { - return pbrtDemandTextureBinding(); - } - - textureStack.push_back( graphKey ); - PbrtDemandTextureBinding result{}; - if( texture->type == "imagemap" || texture->type == "checkerboard" ) - { - result = pbrtDemandTextureBinding( pbrtTextureMapName( texture ), pbrtTextureScale( 1.0f ), - pbrtTextureBias( 0.0f ), false, pbrtTextureGamma( texture ) ); - } - else if( texture->type == "scale" ) - { - result = findPbrtScaleDemandTextureBinding( graph, *texture, textureStack ); - } - else if( texture->type == "mix" && allowMix ) - { - result = findPbrtMixDemandTextureBinding( graph, *texture, textureStack ); - } - else - { - result = pbrtDemandTextureBinding(); - } - textureStack.pop_back(); - return result; -} - -inline PbrtDemandTextureBinding findPbrtFloatDemandTextureBinding( const otk::pbrt::PbrtMaterialGraph& graph, - const std::string& textureName, - std::vector& textureStack, - bool allowMix ) -{ - std::string graphKey; - const otk::pbrt::PbrtTexture* texture{ findPbrtGraphTexture( graph, textureName, { "float" }, graphKey ) }; + const otk::pbrt::PbrtTexture* texture{ Traits::findTexture( graph, textureName, graphKey ) }; if( texture == nullptr || pbrtTextureStackContains( textureStack, graphKey ) ) { return pbrtDemandTextureBinding(); @@ -681,11 +546,11 @@ inline PbrtDemandTextureBinding findPbrtFloatDemandTextureBinding( const otk::pb } else if( texture->type == "scale" ) { - result = findPbrtScaleFloatDemandTextureBinding( graph, *texture, textureStack ); + result = findPbrtScaleDemandTextureBinding( graph, *texture, textureStack ); } - else if( texture->type == "mix" && allowMix ) + else if( texture->type == "mix" && policy == PbrtTextureTraversalPolicy::SCALE_AND_MIX ) { - result = findPbrtMixFloatDemandTextureBinding( graph, *texture, textureStack ); + result = findPbrtMixDemandTextureBinding( graph, *texture, textureStack ); } else { @@ -704,7 +569,10 @@ inline PbrtDemandTextureBinding pbrtColorTextureBinding( const otk::pbrt::PbrtMa } std::vector textureStack; - return findPbrtDemandTextureBinding( material.graph, textureName, textureStack, std::string{ paramName } == "Kd" ); + const PbrtTextureTraversalPolicy policy{ std::string{ paramName } == "Kd" ? + PbrtTextureTraversalPolicy::SCALE_AND_MIX : + PbrtTextureTraversalPolicy::SCALE_ONLY }; + return findPbrtDemandTextureBinding( material.graph, textureName, textureStack, policy ); } inline PbrtDemandTextureBinding pbrtFloatTextureBinding( const otk::pbrt::PbrtMaterial& material, const char* paramName ) @@ -716,8 +584,10 @@ inline PbrtDemandTextureBinding pbrtFloatTextureBinding( const otk::pbrt::PbrtMa } std::vector textureStack; - return findPbrtFloatDemandTextureBinding( material.graph, textureName, textureStack, - std::string{ paramName } == "bumpmap" ); + const PbrtTextureTraversalPolicy policy{ std::string{ paramName } == "bumpmap" ? + PbrtTextureTraversalPolicy::SCALE_AND_MIX : + PbrtTextureTraversalPolicy::SCALE_ONLY }; + return findPbrtDemandTextureBinding( material.graph, textureName, textureStack, policy ); } inline std::string pbrtColorMapFileName( const otk::pbrt::PbrtMaterial& material, const char* paramName ) diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlBsdfCompiler.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlBsdfCompiler.h index c1330d69..a311b699 100644 --- a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlBsdfCompiler.h +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlBsdfCompiler.h @@ -8,9 +8,9 @@ #ifdef OTK_USE_MDL -#include +#include "DemandPbrtScene/MdlUtils.h" + #include -#include namespace mi { namespace neuraylib { @@ -25,20 +25,6 @@ class ITransaction; namespace demandPbrtScene { -struct MdlTargetArgumentBlockParameter -{ - std::string name; - unsigned int kind{}; - std::size_t offset{}; - std::size_t size{}; -}; - -struct MdlTargetArgumentBlock -{ - std::vector data; - std::vector parameters; -}; - struct MdlBsdfCallablePtx { std::string initFunctionName; diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlHandleTypes.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlHandleTypes.h new file mode 100644 index 00000000..76215def --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlHandleTypes.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include + +namespace demandPbrtScene { + +using BackendApiHandle = mi::base::Handle; +using BackendHandle = mi::base::Handle; +using BsdfMeasurementHandle = mi::base::Handle; +using ColorValueHandle = mi::base::Handle; +using CompiledMaterialHandle = mi::base::Handle; +using ConstColorValueHandle = mi::base::Handle; +using ConstExpressionConstantHandle = mi::base::Handle; +using ConstExpressionHandle = mi::base::Handle; +using ConstFloatValueHandle = mi::base::Handle; +using ConstStringHandle = mi::base::Handle; +using DatabaseHandle = mi::base::Handle; +using ExecutionContextHandle = mi::base::Handle; +using ExpressionConstantHandle = mi::base::Handle; +using ExpressionFactoryHandle = mi::base::Handle; +using FloatValueHandle = mi::base::Handle; +using FunctionCallHandle = mi::base::Handle; +using FunctionDefinitionHandle = mi::base::Handle; +using MaterialInstanceHandle = mi::base::Handle; +using MdlFactoryHandle = mi::base::Handle; +using MdlImpexpApiHandle = mi::base::Handle; +using MessageHandle = mi::base::Handle; +using ModuleHandle = mi::base::Handle; +using NeurayHandle = mi::base::Handle; +using ScopeHandle = mi::base::Handle; +using TargetArgumentBlockHandle = mi::base::Handle; +using TargetCodeHandle = mi::base::Handle; +using TargetValueLayoutHandle = mi::base::Handle; +using TransactionHandle = mi::base::Handle; +using TypeFactoryHandle = mi::base::Handle; +using TypeHandle = mi::base::Handle; +using ValueFactoryHandle = mi::base::Handle; +using VersionHandle = mi::base::Handle; + +} // namespace demandPbrtScene diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlKeyBuilder.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlKeyBuilder.h new file mode 100644 index 00000000..c7fa6535 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlKeyBuilder.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include "DemandPbrtScene/Config.h" + +#ifdef OTK_USE_MDL + +#include + +#include + +namespace demandPbrtScene { + +struct MdlShaderKey +{ + std::string signature; +}; + +bool operator==( const MdlShaderKey& lhs, const MdlShaderKey& rhs ); +bool operator!=( const MdlShaderKey& lhs, const MdlShaderKey& rhs ); +bool operator<( const MdlShaderKey& lhs, const MdlShaderKey& rhs ); + +std::string toString( const MdlShaderKey& key ); +MdlShaderKey makeMdlShaderKey( const otk::pbrt::PbrtMaterial& material ); + +struct MdlMaterialInstanceKey +{ + MdlShaderKey sourceKey; + std::string signature; + bool sourceShapeProgramReusable{}; +}; + +bool operator==( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ); +bool operator!=( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ); +bool operator<( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ); + +std::string toString( const MdlMaterialInstanceKey& key ); +MdlMaterialInstanceKey makeMdlMaterialInstanceKey( const otk::pbrt::PbrtMaterial& material ); + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlMaterialModelBuilder.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlMaterialModelBuilder.h new file mode 100644 index 00000000..23cc05e7 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlMaterialModelBuilder.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include "DemandPbrtScene/Config.h" + +#ifdef OTK_USE_MDL + +#include + +#include +#include + +namespace demandPbrtScene { + +struct MdlShaderKey; + +struct GeneratedMdlSource +{ + std::string moduleName; + std::string materialName; + std::string source; + std::vector unsupportedReasons; +}; + +void appendUnsupportedReason( GeneratedMdlSource& result, const std::string& reason ); + +GeneratedMdlSource generateMdlSource( const MdlShaderKey& key ); +GeneratedMdlSource generateMdlSource( const otk::pbrt::PbrtMaterial& material ); + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL + diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlParameterBinder.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlParameterBinder.h new file mode 100644 index 00000000..943fcfcc --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlParameterBinder.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include "DemandPbrtScene/Config.h" + +#ifdef OTK_USE_MDL + +#include + +#include +#include + +namespace demandPbrtScene { + +enum class MdlBoundParameterType +{ + COLOR, + FLOAT, +}; + +struct MdlBoundMaterialParameter +{ + std::string name; + MdlBoundParameterType type{}; + float red{}; + float green{}; + float blue{}; + float value{}; +}; + +std::string namedMaterialParameterName( unsigned int index, const std::string& paramName ); +std::string namedMaterialType( const otk::pbrt::PbrtNamedMaterial& material ); + +std::vector makeMdlBoundMaterialParameters( const otk::pbrt::PbrtMaterial& material ); + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL + diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h new file mode 100644 index 00000000..e1685269 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include "DemandPbrtScene/MdlHandleTypes.h" + +#include +#include + +namespace demandPbrtScene { + +class MdlSdkSession +{ + public: + MdlSdkSession(); + ~MdlSdkSession(); + + MdlSdkSession( const MdlSdkSession& ) = delete; + MdlSdkSession& operator=( const MdlSdkSession& ) = delete; + + bool isStarted() const; + + const std::string& error() const; + + const NeurayHandle& handle() const; + mi::neuraylib::INeuray* neuray() const; + + mi::Sint32 shutdown(); + void close(); + + private: + struct Impl; + std::unique_ptr m_impl; +}; + +} // namespace demandPbrtScene diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlShaderCache.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlShaderCache.h index d55a5152..2c571eb2 100644 --- a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlShaderCache.h +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlShaderCache.h @@ -7,71 +7,17 @@ #include "DemandPbrtScene/Config.h" #ifdef OTK_USE_MDL +#include "DemandPbrtScene/MdlKeyBuilder.h" +#include "DemandPbrtScene/MdlMaterialModelBuilder.h" +#include "DemandPbrtScene/MdlParameterBinder.h" #include "DemandPbrtScene/MdlShaderCompileCacheStatistics.h" -#include - #include #include #include -#include namespace demandPbrtScene { -struct MdlShaderKey -{ - std::string signature; -}; - -bool operator==( const MdlShaderKey& lhs, const MdlShaderKey& rhs ); -bool operator!=( const MdlShaderKey& lhs, const MdlShaderKey& rhs ); -bool operator<( const MdlShaderKey& lhs, const MdlShaderKey& rhs ); - -std::string toString( const MdlShaderKey& key ); -MdlShaderKey makeMdlShaderKey( const otk::pbrt::PbrtMaterial& material ); - -struct MdlMaterialInstanceKey -{ - MdlShaderKey sourceKey; - std::string signature; - bool sourceShapeProgramReusable{}; -}; - -bool operator==( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ); -bool operator!=( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ); -bool operator<( const MdlMaterialInstanceKey& lhs, const MdlMaterialInstanceKey& rhs ); - -std::string toString( const MdlMaterialInstanceKey& key ); -MdlMaterialInstanceKey makeMdlMaterialInstanceKey( const otk::pbrt::PbrtMaterial& material ); - -enum class MdlBoundParameterType -{ - COLOR, - FLOAT, -}; - -struct MdlBoundMaterialParameter -{ - std::string name; - MdlBoundParameterType type{}; - float red{}; - float green{}; - float blue{}; - float value{}; -}; - -std::vector makeMdlBoundMaterialParameters( const otk::pbrt::PbrtMaterial& material ); - -struct GeneratedMdlSource -{ - std::string moduleName; - std::string materialName; - std::string source; - std::vector unsupportedReasons; -}; - -GeneratedMdlSource generateMdlSource( const otk::pbrt::PbrtMaterial& material ); - enum class MdlShaderCompileState { MISSING, diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlTextureGraphGenerator.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlTextureGraphGenerator.h new file mode 100644 index 00000000..c2734039 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlTextureGraphGenerator.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include "DemandPbrtScene/Config.h" + +#ifdef OTK_USE_MDL + +#include + +#include +#include + +namespace demandPbrtScene { + +struct GeneratedMdlSource; + +struct MdlTextureLookup +{ + std::string graphKey; + const otk::pbrt::PbrtTexture* texture{}; +}; + +MdlTextureLookup findMdlTexture( const otk::pbrt::PbrtMaterialGraph& graph, + const std::string& textureName, + const std::string& preferredValueType ); + +class MdlTextureGraphGenerator +{ + public: + MdlTextureGraphGenerator( const otk::pbrt::PbrtMaterialGraph& graph, GeneratedMdlSource& result ); + ~MdlTextureGraphGenerator(); + + MdlTextureGraphGenerator( const MdlTextureGraphGenerator& ) = delete; + MdlTextureGraphGenerator& operator=( const MdlTextureGraphGenerator& ) = delete; + + std::string materialColorExpression( const ::pbrt::ParamSet& params, + const std::string& paramName, + const std::string& preferredValueType, + const std::string& defaultExpression ); + std::string materialFloatExpression( const ::pbrt::ParamSet& params, + const std::string& paramName, + const std::string& preferredValueType, + const std::string& defaultExpression ); + std::string sourcePreamble() const; + std::string functionDefinitions() const; + + private: + class Impl; + std::unique_ptr m_impl; +}; + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL + diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlUtils.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlUtils.h new file mode 100644 index 00000000..9c25540a --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlUtils.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include "DemandPbrtScene/Config.h" + +#ifdef OTK_USE_MDL + +#include +#include +#include + +namespace mi { +namespace neuraylib { + +class ICompiled_material; +class IMdl_execution_context; +class ITarget_code; + +} // namespace neuraylib +} // namespace mi + +namespace demandPbrtScene { + +struct MdlTargetArgumentBlockParameter +{ + std::string name; + unsigned int kind{}; + std::size_t offset{}; + std::size_t size{}; +}; + +struct MdlTargetArgumentBlock +{ + std::vector data; + std::vector parameters; +}; + +std::string describeMdlContextMessages( const mi::neuraylib::IMdl_execution_context* context ); + +[[noreturn]] void failMdl( const std::string& message, + const mi::neuraylib::IMdl_execution_context* context = nullptr ); + +void requireMdl( bool condition, const std::string& message, + const mi::neuraylib::IMdl_execution_context* context = nullptr ); + +MdlTargetArgumentBlock captureMdlTargetArgumentBlock( const mi::neuraylib::ITarget_code* targetCode, + const mi::neuraylib::ICompiled_material* compiledMaterial, + mi::neuraylib::IMdl_execution_context* context ); + +} // namespace demandPbrtScene + +#endif // OTK_USE_MDL diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/Params.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/Params.h index d11cefc1..854479cc 100644 --- a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/Params.h +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/Params.h @@ -112,9 +112,9 @@ inline uint_t operator+( HitGroupIndex value ) #ifdef OTK_USE_MDL struct MdlMaterialTextureBinding { - uint_t textureId; - float3 scale; - float3 bias; + uint_t textureId{ INVALID_TEXTURE_ID }; + float3 scale{ make_float3( 1.0f, 1.0f, 1.0f ) }; + float3 bias{ make_float3( 0.0f, 0.0f, 0.0f ) }; }; inline bool operator==( const MdlMaterialTextureBinding& lhs, const MdlMaterialTextureBinding& rhs ) @@ -129,7 +129,7 @@ inline bool operator!=( const MdlMaterialTextureBinding& lhs, const MdlMaterialT __host__ __device__ inline MdlMaterialTextureBinding invalidMdlMaterialTextureBinding() { - return MdlMaterialTextureBinding{ INVALID_TEXTURE_ID, make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) }; + return MdlMaterialTextureBinding{}; } #endif @@ -368,19 +368,19 @@ inline bool usesFallbackShader( const MaterialState& state ) #ifdef OTK_USE_MDL struct MdlMaterialShader { - uint_t callableBaseIndex; - uint_t callableCount; - CUdeviceptr tintArgumentBlock; - CUdeviceptr bsdfArgumentBlock; - uint_t bsdfArgumentBlockSize; - uint_t roughnessArgumentBlockOffset; - uint_t uRoughnessArgumentBlockOffset; - uint_t vRoughnessArgumentBlockOffset; - uint_t mixAmountArgumentBlockOffset; + uint_t callableBaseIndex{}; + uint_t callableCount{}; + CUdeviceptr tintArgumentBlock{}; + CUdeviceptr bsdfArgumentBlock{}; + uint_t bsdfArgumentBlockSize{}; + uint_t roughnessArgumentBlockOffset{ INVALID_MDL_ARGUMENT_BLOCK_OFFSET }; + uint_t uRoughnessArgumentBlockOffset{ INVALID_MDL_ARGUMENT_BLOCK_OFFSET }; + uint_t vRoughnessArgumentBlockOffset{ INVALID_MDL_ARGUMENT_BLOCK_OFFSET }; + uint_t mixAmountArgumentBlockOffset{ INVALID_MDL_ARGUMENT_BLOCK_OFFSET }; // Per-instance shader data lives here rather than in hitgroup SBT records. - uint_t textureBindingCount; - MdlMaterialTextureBinding textureBindings[MDL_MATERIAL_TEXTURE_BINDING_COUNT]; + uint_t textureBindingCount{}; + MdlMaterialTextureBinding textureBindings[MDL_MATERIAL_TEXTURE_BINDING_COUNT]{}; __host__ __device__ void clearTextureBindings() { @@ -405,90 +405,32 @@ struct MdlMaterialShader return true; } - __host__ __device__ MdlMaterialShader() - : callableBaseIndex( 0U ) - , callableCount( 0U ) - , tintArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlockSize( 0U ) - , roughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , uRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , vRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , mixAmountArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , textureBindingCount( 0U ) - { - clearTextureBindings(); - } + __host__ __device__ MdlMaterialShader() = default; - __host__ __device__ MdlMaterialShader( uint_t callableBaseIndex_, uint_t callableCount_ ) - : callableBaseIndex( callableBaseIndex_ ) - , callableCount( callableCount_ ) - , tintArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlockSize( 0U ) - , roughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , uRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , vRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , mixAmountArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , textureBindingCount( 0U ) + __host__ __device__ MdlMaterialShader( uint_t baseIndex, uint_t count ) + : callableBaseIndex( baseIndex ) + , callableCount( count ) { - clearTextureBindings(); } - __host__ __device__ MdlMaterialShader( uint_t callableBaseIndex_, uint_t callableCount_, const float3& diffuseTextureScale_ ) - : callableBaseIndex( callableBaseIndex_ ) - , callableCount( callableCount_ ) - , tintArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlockSize( 0U ) - , roughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , uRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , vRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , mixAmountArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , textureBindingCount( 0U ) + __host__ __device__ MdlMaterialShader( uint_t baseIndex, uint_t count, const float3& scale ) + : MdlMaterialShader( baseIndex, count ) { - clearTextureBindings(); - setTextureBinding( MDL_MATERIAL_DIFFUSE_TEXTURE_BINDING_INDEX, INVALID_TEXTURE_ID, diffuseTextureScale_, + setTextureBinding( MDL_MATERIAL_DIFFUSE_TEXTURE_BINDING_INDEX, INVALID_TEXTURE_ID, scale, make_float3( 0.0f, 0.0f, 0.0f ) ); } - __host__ __device__ MdlMaterialShader( uint_t callableBaseIndex_, - uint_t callableCount_, - const float3& diffuseTextureScale_, - const float3& diffuseTextureBias_ ) - : callableBaseIndex( callableBaseIndex_ ) - , callableCount( callableCount_ ) - , tintArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlockSize( 0U ) - , roughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , uRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , vRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , mixAmountArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , textureBindingCount( 0U ) + __host__ __device__ MdlMaterialShader( uint_t baseIndex, uint_t count, const float3& scale, const float3& bias ) + : MdlMaterialShader( baseIndex, count ) { - clearTextureBindings(); - setTextureBinding( MDL_MATERIAL_DIFFUSE_TEXTURE_BINDING_INDEX, INVALID_TEXTURE_ID, diffuseTextureScale_, diffuseTextureBias_ ); + setTextureBinding( MDL_MATERIAL_DIFFUSE_TEXTURE_BINDING_INDEX, INVALID_TEXTURE_ID, scale, bias ); } - __host__ __device__ MdlMaterialShader( uint_t callableBaseIndex_, - uint_t callableCount_, - uint_t diffuseTextureId_, - const float3& diffuseTextureScale_, - const float3& diffuseTextureBias_ ) - : callableBaseIndex( callableBaseIndex_ ) - , callableCount( callableCount_ ) - , tintArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlock( CUdeviceptr{} ) - , bsdfArgumentBlockSize( 0U ) - , roughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , uRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , vRoughnessArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , mixAmountArgumentBlockOffset( INVALID_MDL_ARGUMENT_BLOCK_OFFSET ) - , textureBindingCount( 0U ) + __host__ __device__ MdlMaterialShader( uint_t baseIndex, uint_t count, uint_t textureId, const float3& scale, + const float3& bias ) + : MdlMaterialShader( baseIndex, count ) { - clearTextureBindings(); - setTextureBinding( MDL_MATERIAL_DIFFUSE_TEXTURE_BINDING_INDEX, diffuseTextureId_, diffuseTextureScale_, diffuseTextureBias_ ); + setTextureBinding( MDL_MATERIAL_DIFFUSE_TEXTURE_BINDING_INDEX, textureId, scale, bias ); } }; diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/PbrtMaterialKind.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/PbrtMaterialKind.h new file mode 100644 index 00000000..fee9e3c9 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/PbrtMaterialKind.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include + +namespace demandPbrtScene { + +enum class PbrtMaterialKind +{ + UNKNOWN, + MATTE, + PLASTIC, + UBER, + MIRROR, + GLASS, + METAL, + SUBSTRATE, + TRANSLUCENT, + SUBSURFACE, + KD_SUBSURFACE, + MIX, + FOURIER, + HAIR, + MEASURED, +}; + +enum class PbrtMaterialCapability : unsigned int +{ + NONE = 0U, + GENERATED_MDL = 1U << 0, + NAMED_MDL = 1U << 1, + KD = 1U << 2, + KS = 1U << 3, + KR = 1U << 4, + KT = 1U << 5, + ROUGHNESS = 1U << 6, + AXIS_ROUGHNESS = 1U << 7, +}; + +struct PbrtMaterialDescriptor +{ + PbrtMaterialKind kind; + std::string_view type; + PbrtMaterialCapability capabilities; + + bool has( PbrtMaterialCapability capability ) const; +}; + +const PbrtMaterialDescriptor& pbrtMaterialDescriptor( std::string_view type ); +PbrtMaterialKind pbrtMaterialKind( std::string_view type ); + +} // namespace demandPbrtScene diff --git a/examples/DemandLoading/DemandPbrtScene/tests/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/tests/CMakeLists.txt index 8842af0b..13676ac0 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/tests/CMakeLists.txt @@ -7,6 +7,7 @@ include(GoogleTest) include(ImageTest) add_executable(TestDemandPbrtSceneImpl + include/DemandPbrtScene/Testing/FourierBsdfTableWriter.h include/DemandPbrtScene/Testing/GeometryInstancePrinter.h include/DemandPbrtScene/Testing/Matchers.h include/DemandPbrtScene/Testing/MockDemandTextureCache.h diff --git a/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp index bc9722b9..f1e0f734 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp @@ -4,21 +4,19 @@ #include +#include + #include #include -#include -#include #include #include -#include -using namespace demandPbrtScene; -using namespace testing; +using demandPbrtScene::testing::FourierBsdfTableWriter; namespace { -constexpr char SCATFUN_HEADER[8] = { 'S', 'C', 'A', 'T', 'F', 'U', 'N', '\x01' }; +using namespace demandPbrtScene; std::filesystem::path pbrtReferenceDir() { @@ -34,94 +32,6 @@ std::filesystem::path tempFourierTableFile( const std::string& name ) return file; } -void writeUint32( std::ostream& output, std::uint32_t value ) -{ - const unsigned char bytes[] = { - static_cast( value & 0xffU ), - static_cast( ( value >> 8 ) & 0xffU ), - static_cast( ( value >> 16 ) & 0xffU ), - static_cast( ( value >> 24 ) & 0xffU ), - }; - output.write( reinterpret_cast( bytes ), sizeof( bytes ) ); -} - -void writeInt32( std::ostream& output, int value ) -{ - writeUint32( output, static_cast( value ) ); -} - -void writeFloat( std::ostream& output, float value ) -{ - std::uint32_t bits{}; - std::memcpy( &bits, &value, sizeof( bits ) ); - writeUint32( output, bits ); -} - -void writeHeader( std::ostream& output ) -{ - output.write( SCATFUN_HEADER, sizeof( SCATFUN_HEADER ) ); -} - -void writeMetadata( std::ostream& output, int flags, int nMu, int nCoefficients, int maxOrder, int nChannels, int nBases ) -{ - writeInt32( output, flags ); - writeInt32( output, nMu ); - writeInt32( output, nCoefficients ); - writeInt32( output, maxOrder ); - writeInt32( output, nChannels ); - writeInt32( output, nBases ); - writeInt32( output, 0 ); - writeInt32( output, 0 ); - writeInt32( output, 0 ); - writeFloat( output, 1.0f ); - writeInt32( output, 0 ); - writeInt32( output, 0 ); - writeInt32( output, 0 ); - writeInt32( output, 0 ); -} - -void writeMinimalTable( const std::filesystem::path& fileName, int nCoefficients, int nChannels, int coefficientOffset, int coefficientCount ) -{ - std::ofstream output{ fileName, std::ios::binary }; - writeHeader( output ); - writeMetadata( output, 1, 1, nCoefficients, 1, nChannels, 1 ); - writeFloat( output, 1.0f ); - writeFloat( output, 1.0f ); - writeInt32( output, coefficientOffset ); - writeInt32( output, coefficientCount ); - for( int i = 0; i < nCoefficients; ++i ) - { - writeFloat( output, static_cast( i + 1 ) ); - } -} - -void writeFourierOrderShapeTable( const std::filesystem::path& fileName, int maxOrder ) -{ - constexpr int nMu{ 2 }; - constexpr int nChannels{ 3 }; - constexpr int gridSize{ nMu * nMu }; - const int nCoefficients{ gridSize * nChannels * maxOrder }; - - std::ofstream output{ fileName, std::ios::binary }; - writeHeader( output ); - writeMetadata( output, 1, nMu, nCoefficients, maxOrder, nChannels, 1 ); - writeFloat( output, -1.0f ); - writeFloat( output, 1.0f ); - writeFloat( output, 0.0f ); - writeFloat( output, 1.0f ); - writeFloat( output, 0.0f ); - writeFloat( output, 1.0f ); - for( int entry = 0; entry < gridSize; ++entry ) - { - writeInt32( output, entry * nChannels * maxOrder ); - writeInt32( output, maxOrder ); - } - for( int i = 0; i < nCoefficients; ++i ) - { - writeFloat( output, i % maxOrder == 0 ? 1.0f : 0.0f ); - } -} - void writeMalformedLargeSpanTable( const std::filesystem::path& fileName ) { constexpr int maxOrder{ 1599 }; @@ -129,28 +39,25 @@ void writeMalformedLargeSpanTable( const std::filesystem::path& fileName ) constexpr int nChannels{ 3 }; constexpr int gridSize{ nMu * nMu }; - std::ofstream output{ fileName, std::ios::binary }; - writeHeader( output ); - writeMetadata( output, 1, nMu, maxOrder, maxOrder, nChannels, 1 ); - writeFloat( output, -1.0f ); - writeFloat( output, 1.0f ); - writeFloat( output, 0.0f ); - writeFloat( output, 1.0f ); - writeFloat( output, 0.0f ); - writeFloat( output, 1.0f ); - for( int entry = 0; entry < gridSize; ++entry ) + FourierBsdfTableWriter output{ fileName }; + output.writeMetadata( 1, nMu, maxOrder, maxOrder, nChannels, 1 ); + output.writeFloat( -1.0f ); + output.writeFloat( 1.0f ); + output.writeFloat( 0.0f ); + output.writeFloat( 1.0f ); + output.writeFloat( 0.0f ); + output.writeFloat( 1.0f ); + for( int i = 0; i < gridSize; ++i ) { - writeInt32( output, 0 ); - writeInt32( output, maxOrder ); + output.writeInt32( 0 ); + output.writeInt32( maxOrder ); } for( int i = 0; i < maxOrder; ++i ) { - writeFloat( output, 1.0f ); + output.writeFloat( 1.0f ); } } -} // namespace - TEST( TestFourierBsdfTable, parsesFixtureTableMetadataAndCoefficientLayout ) { const std::filesystem::path fixture{ pbrtReferenceDir() / "bsdfs" / "roughgold_alpha_0.2.bsdf" }; @@ -190,7 +97,7 @@ TEST( TestFourierBsdfTable, parsesFixtureTableMetadataAndCoefficientLayout ) TEST( TestFourierBsdfTable, parsesCoatedCopperOrderShape ) { const std::filesystem::path fileName{ tempFourierTableFile( "coated-copper-order.bsdf" ) }; - writeFourierOrderShapeTable( fileName, 530 ); + FourierBsdfTableWriter::writeOrderShapeTable( fileName, 530 ); const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( fileName.string() ) }; @@ -201,14 +108,20 @@ TEST( TestFourierBsdfTable, parsesCoatedCopperOrderShape ) EXPECT_EQ( 3, table.nChannels ); EXPECT_EQ( 6360, table.nCoefficients ); EXPECT_EQ( 4U, table.coefficientOffsets.size() ); - EXPECT_THAT( table.coefficientCounts, Each( 530 ) ); - EXPECT_THAT( table.zeroOrderCoefficients, Each( 1.0f ) ); + for( int count : table.coefficientCounts ) + { + EXPECT_EQ( 530, count ); + } + for( float coefficient : table.zeroOrderCoefficients ) + { + EXPECT_FLOAT_EQ( 1.0f, coefficient ); + } } TEST( TestFourierBsdfTable, parsesCeramicOrderShape ) { const std::filesystem::path fileName{ tempFourierTableFile( "ceramic-order.bsdf" ) }; - writeFourierOrderShapeTable( fileName, 1599 ); + FourierBsdfTableWriter::writeOrderShapeTable( fileName, 1599 ); const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( fileName.string() ) }; @@ -219,8 +132,14 @@ TEST( TestFourierBsdfTable, parsesCeramicOrderShape ) EXPECT_EQ( 3, table.nChannels ); EXPECT_EQ( 19188, table.nCoefficients ); EXPECT_EQ( 4U, table.coefficientOffsets.size() ); - EXPECT_THAT( table.coefficientCounts, Each( 1599 ) ); - EXPECT_THAT( table.zeroOrderCoefficients, Each( 1.0f ) ); + for( int count : table.coefficientCounts ) + { + EXPECT_EQ( 1599, count ); + } + for( float coefficient : table.zeroOrderCoefficients ) + { + EXPECT_FLOAT_EQ( 1.0f, coefficient ); + } } TEST( TestFourierBsdfTable, reportsMissingTable ) @@ -228,7 +147,7 @@ TEST( TestFourierBsdfTable, reportsMissingTable ) const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( tempFourierTableFile( "missing.bsdf" ).string() ) }; EXPECT_EQ( FourierBsdfTableLoadStatus::FILE_NOT_FOUND, result.status ); - EXPECT_THAT( result.diagnostic, HasSubstr( "Unable to open Fourier BSDF table file" ) ); + EXPECT_THAT( result.diagnostic, ::testing::HasSubstr( "Unable to open Fourier BSDF table file" ) ); } TEST( TestFourierBsdfTable, rejectsInvalidHeader ) @@ -242,50 +161,50 @@ TEST( TestFourierBsdfTable, rejectsInvalidHeader ) const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( fileName.string() ) }; EXPECT_EQ( FourierBsdfTableLoadStatus::INVALID_HEADER, result.status ); - EXPECT_THAT( result.diagnostic, HasSubstr( "Invalid Fourier BSDF table header" ) ); + EXPECT_THAT( result.diagnostic, ::testing::HasSubstr( "Invalid Fourier BSDF table header" ) ); } TEST( TestFourierBsdfTable, rejectsTruncatedTable ) { const std::filesystem::path fileName{ tempFourierTableFile( "truncated.bsdf" ) }; { - std::ofstream output{ fileName, std::ios::binary }; - writeHeader( output ); - writeInt32( output, 1 ); + FourierBsdfTableWriter writer{ fileName }; + writer.writeInt32( 1 ); } const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( fileName.string() ) }; EXPECT_EQ( FourierBsdfTableLoadStatus::TRUNCATED, result.status ); - EXPECT_THAT( result.diagnostic, HasSubstr( "while reading metadata" ) ); + EXPECT_THAT( result.diagnostic, ::testing::HasSubstr( "while reading metadata" ) ); } TEST( TestFourierBsdfTable, rejectsUnsupportedMetadata ) { const std::filesystem::path fileName{ tempFourierTableFile( "unsupported.bsdf" ) }; { - std::ofstream output{ fileName, std::ios::binary }; - writeHeader( output ); - writeMetadata( output, 1, 1, 1, 1, 2, 1 ); + FourierBsdfTableWriter writer{ fileName }; + writer.writeMetadata( 1, 1, 1, 1, 2, 1 ); } const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( fileName.string() ) }; EXPECT_EQ( FourierBsdfTableLoadStatus::UNSUPPORTED, result.status ); - EXPECT_THAT( result.diagnostic, HasSubstr( "nChannels=2" ) ); + EXPECT_THAT( result.diagnostic, ::testing::HasSubstr( "nChannels=2" ) ); } TEST( TestFourierBsdfTable, rejectsMalformedCoefficientSpans ) { const std::filesystem::path fileName{ tempFourierTableFile( "malformed-span.bsdf" ) }; - writeMinimalTable( fileName, 1, 3, 0, 1 ); + FourierBsdfTableWriter::writeMinimalTable( fileName, { 1.0f }, 3, 0, 1 ); const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( fileName.string() ) }; EXPECT_EQ( FourierBsdfTableLoadStatus::MALFORMED, result.status ); - EXPECT_THAT( result.diagnostic, HasSubstr( "coefficient span exceeds coefficient data" ) ); + EXPECT_THAT( result.diagnostic, ::testing::HasSubstr( "coefficient span exceeds coefficient data" ) ); } +} // namespace + TEST( TestFourierBsdfTable, rejectsMalformedLargeCoefficientSpans ) { const std::filesystem::path fileName{ tempFourierTableFile( "malformed-large-span.bsdf" ) }; @@ -294,5 +213,5 @@ TEST( TestFourierBsdfTable, rejectsMalformedLargeCoefficientSpans ) const FourierBsdfTableLoadResult result{ loadFourierBsdfTable( fileName.string() ) }; EXPECT_EQ( FourierBsdfTableLoadStatus::MALFORMED, result.status ); - EXPECT_THAT( result.diagnostic, HasSubstr( "coefficient span exceeds coefficient data" ) ); + EXPECT_THAT( result.diagnostic, ::testing::HasSubstr( "coefficient span exceeds coefficient data" ) ); } diff --git a/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp index 9eb96371..36f6c1d7 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -34,8 +35,6 @@ #include #ifdef OTK_USE_MDL -#include -#include #include #include #endif @@ -939,107 +938,19 @@ std::filesystem::path makeFourierTestDirectory() return directory; } -void writeFourierUint32( std::ostream& output, std::uint32_t value ) -{ - const unsigned char bytes[] = { - static_cast( value & 0xffU ), - static_cast( ( value >> 8 ) & 0xffU ), - static_cast( ( value >> 16 ) & 0xffU ), - static_cast( ( value >> 24 ) & 0xffU ), - }; - output.write( reinterpret_cast( bytes ), sizeof( bytes ) ); -} - -void writeFourierInt32( std::ostream& output, int value ) -{ - writeFourierUint32( output, static_cast( value ) ); -} - -void writeFourierFloat( std::ostream& output, float value ) -{ - std::uint32_t bits{}; - std::memcpy( &bits, &value, sizeof( bits ) ); - writeFourierUint32( output, bits ); -} - void writeMinimalFourierBsdfTable( const std::filesystem::path& fileName ) { - constexpr char scatfunHeader[8] = { 'S', 'C', 'A', 'T', 'F', 'U', 'N', '\x01' }; - std::ofstream output{ fileName, std::ios::binary }; - output.write( scatfunHeader, sizeof( scatfunHeader ) ); - writeFourierInt32( output, 1 ); - writeFourierInt32( output, 1 ); - writeFourierInt32( output, 3 ); - writeFourierInt32( output, 1 ); - writeFourierInt32( output, 3 ); - writeFourierInt32( output, 1 ); - for( int i = 0; i < 3; ++i ) - { - writeFourierInt32( output, 0 ); - } - writeFourierFloat( output, 1.0f ); - for( int i = 0; i < 4; ++i ) - { - writeFourierInt32( output, 0 ); - } - writeFourierFloat( output, 1.0f ); - writeFourierFloat( output, 1.0f ); - writeFourierInt32( output, 0 ); - writeFourierInt32( output, 1 ); - writeFourierFloat( output, 0.1f ); - writeFourierFloat( output, 0.2f ); - writeFourierFloat( output, 0.3f ); -} - -void writeFourierBsdfOrderShapeTable( const std::filesystem::path& fileName, int maxOrder ) -{ - constexpr char scatfunHeader[8] = { 'S', 'C', 'A', 'T', 'F', 'U', 'N', '\x01' }; - constexpr int nMu{ 2 }; - constexpr int nChannels{ 3 }; - constexpr int gridSize{ nMu * nMu }; - const int nCoefficients{ gridSize * nChannels * maxOrder }; - std::ofstream output{ fileName, std::ios::binary }; - output.write( scatfunHeader, sizeof( scatfunHeader ) ); - writeFourierInt32( output, 1 ); - writeFourierInt32( output, nMu ); - writeFourierInt32( output, nCoefficients ); - writeFourierInt32( output, maxOrder ); - writeFourierInt32( output, nChannels ); - writeFourierInt32( output, 1 ); - for( int i = 0; i < 3; ++i ) - { - writeFourierInt32( output, 0 ); - } - writeFourierFloat( output, 1.0f ); - for( int i = 0; i < 4; ++i ) - { - writeFourierInt32( output, 0 ); - } - writeFourierFloat( output, -1.0f ); - writeFourierFloat( output, 1.0f ); - writeFourierFloat( output, 0.0f ); - writeFourierFloat( output, 1.0f ); - writeFourierFloat( output, 0.0f ); - writeFourierFloat( output, 1.0f ); - for( int entry = 0; entry < gridSize; ++entry ) - { - writeFourierInt32( output, entry * nChannels * maxOrder ); - writeFourierInt32( output, maxOrder ); - } - for( int i = 0; i < nCoefficients; ++i ) - { - writeFourierFloat( output, i % maxOrder == 0 ? 1.0f : 0.0f ); - } + FourierBsdfTableWriter::writeMinimalTable( fileName, { 0.1f, 0.2f, 0.3f }, 3, 0, 1 ); } void writeCoatedCopperOrderFourierBsdfTable( const std::filesystem::path& fileName ) { - writeFourierBsdfOrderShapeTable( fileName, 530 ); + FourierBsdfTableWriter::writeOrderShapeTable( fileName, 530 ); } void writeCeramicOrderFourierBsdfTable( const std::filesystem::path& fileName ) { - writeFourierBsdfOrderShapeTable( fileName, 1599 ); + FourierBsdfTableWriter::writeOrderShapeTable( fileName, 1599 ); } void writeInvalidFourierBsdfTable( const std::filesystem::path& fileName ) @@ -1933,48 +1844,27 @@ TEST_F( TestMaterialResolverRequestedProxyIds, requestedGeneratedLandscapeMixMat make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ) ); EXPECT_TRUE( setMdlMaterialTextureBinding( expectedShader, MDL_MATERIAL_MIX_NAMED_1_BUMPMAP_TEXTURE_BINDING_INDEX, backBumpTextureId, make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ) ); - EXPECT_CALL( *m_programGroups, - getMdlMaterialSbtOffset( hasGeometryInstance( hasAll( - hasMaterialFlags( MaterialFlags::ALPHA_MAP | MaterialFlags::ALPHA_MAP_ALLOCATED ), hasAlphaTextureId( frontAlphaCutoutTextureId ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KD_TEXTURE_BINDING_INDEX, frontDiffuseTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KS_TEXTURE_BINDING_INDEX, frontSpecularTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KR_TEXTURE_BINDING_INDEX, frontReflectanceTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_ALPHA_TEXTURE_BINDING_INDEX, frontAlphaTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_BUMPMAP_TEXTURE_BINDING_INDEX, frontBumpTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_KD_TEXTURE_BINDING_INDEX, backDiffuseTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_ALPHA_TEXTURE_BINDING_INDEX, backAlphaTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_BUMPMAP_TEXTURE_BINDING_INDEX, backBumpTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ) ) ) ) ) - .WillOnce( Return( stableSbtOffset ) ); - EXPECT_CALL( *m_programGroups, - realizeMdlMaterialShader( - hasGeometryInstance( hasAll( - hasMaterialFlags( MaterialFlags::ALPHA_MAP | MaterialFlags::ALPHA_MAP_ALLOCATED ), - hasAlphaTextureId( frontAlphaCutoutTextureId ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KD_TEXTURE_BINDING_INDEX, frontDiffuseTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KS_TEXTURE_BINDING_INDEX, frontSpecularTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KR_TEXTURE_BINDING_INDEX, frontReflectanceTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_ALPHA_TEXTURE_BINDING_INDEX, frontAlphaTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_BUMPMAP_TEXTURE_BINDING_INDEX, frontBumpTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_KD_TEXTURE_BINDING_INDEX, backDiffuseTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_ALPHA_TEXTURE_BINDING_INDEX, backAlphaTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), - hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_BUMPMAP_TEXTURE_BINDING_INDEX, backBumpTextureId, - make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ) ) ), - 1U ) ) + const auto hasExpectedMixBindings{ hasGeometryInstance( hasAll( + hasMaterialFlags( MaterialFlags::ALPHA_MAP | MaterialFlags::ALPHA_MAP_ALLOCATED ), + hasAlphaTextureId( frontAlphaCutoutTextureId ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KD_TEXTURE_BINDING_INDEX, frontDiffuseTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KS_TEXTURE_BINDING_INDEX, frontSpecularTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_KR_TEXTURE_BINDING_INDEX, frontReflectanceTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_ALPHA_TEXTURE_BINDING_INDEX, frontAlphaTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_0_BUMPMAP_TEXTURE_BINDING_INDEX, frontBumpTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_KD_TEXTURE_BINDING_INDEX, backDiffuseTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_ALPHA_TEXTURE_BINDING_INDEX, backAlphaTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ), + hasMdlTextureBinding( MDL_MATERIAL_MIX_NAMED_1_BUMPMAP_TEXTURE_BINDING_INDEX, backBumpTextureId, + make_float3( 1.0f, 1.0f, 1.0f ), make_float3( 0.0f, 0.0f, 0.0f ) ) ) ) }; + EXPECT_CALL( *m_programGroups, getMdlMaterialSbtOffset( hasExpectedMixBindings ) ).WillOnce( Return( stableSbtOffset ) ); + EXPECT_CALL( *m_programGroups, realizeMdlMaterialShader( hasExpectedMixBindings, 1U ) ) .WillOnce( Return( expectedShader ) ); EXPECT_CALL( *m_loader, remove( proxyMaterialId ) ).Times( 1 ); EXPECT_CALL( *m_loader, clearRequestedMaterialIds() ).Times( 1 ); diff --git a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp index 0848ab66..1f97c231 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp @@ -7,26 +7,53 @@ #include "DemandPbrtScene/FourierBsdfTable.h" #include "DemandPbrtScene/FourierMdlMeasuredBsdfCapability.h" #include "DemandPbrtScene/MdlBsdfCompiler.h" +#include "DemandPbrtScene/MdlHandleTypes.h" +#include "DemandPbrtScene/MdlSdkSession.h" #include "DemandPbrtScene/MdlShaderCache.h" +#include "DemandPbrtScene/MdlUtils.h" #include -#ifdef _WIN32 -#include -#else -#include -#endif - #include #include #include -#include #include #include #include namespace { +using demandPbrtScene::MdlSdkSession; +using demandPbrtScene::describeMdlContextMessages; + +using demandPbrtScene::BackendApiHandle; +using demandPbrtScene::BackendHandle; +using demandPbrtScene::BsdfMeasurementHandle; +using demandPbrtScene::ColorValueHandle; +using demandPbrtScene::CompiledMaterialHandle; +using demandPbrtScene::ConstColorValueHandle; +using demandPbrtScene::ConstExpressionConstantHandle; +using demandPbrtScene::ConstExpressionHandle; +using demandPbrtScene::ConstFloatValueHandle; +using demandPbrtScene::ConstStringHandle; +using demandPbrtScene::DatabaseHandle; +using demandPbrtScene::ExecutionContextHandle; +using demandPbrtScene::ExpressionConstantHandle; +using demandPbrtScene::ExpressionFactoryHandle; +using demandPbrtScene::FloatValueHandle; +using demandPbrtScene::FunctionCallHandle; +using demandPbrtScene::FunctionDefinitionHandle; +using demandPbrtScene::MaterialInstanceHandle; +using demandPbrtScene::MdlFactoryHandle; +using demandPbrtScene::MdlImpexpApiHandle; +using demandPbrtScene::ModuleHandle; +using demandPbrtScene::ScopeHandle; +using demandPbrtScene::TargetCodeHandle; +using demandPbrtScene::TransactionHandle; +using demandPbrtScene::TypeFactoryHandle; +using demandPbrtScene::TypeHandle; +using demandPbrtScene::ValueFactoryHandle; + constexpr mi::Float32 PBRT_KD_RED = 0.25f; constexpr mi::Float32 PBRT_KD_GREEN = 0.50f; constexpr mi::Float32 PBRT_KD_BLUE = 0.75f; @@ -59,157 +86,6 @@ BoundMdlColor conductorNormalReflectance( const BoundMdlColor& eta, const BoundM conductorNormalReflectance( eta.blue, k.blue ) }; } -std::string describeContextMessages( const mi::neuraylib::IMdl_execution_context* context ) -{ - if( !context ) - return {}; - - std::ostringstream out; - for( mi::Size i = 0; i < context->get_messages_count(); ++i ) - { - mi::base::Handle message( context->get_message( i ) ); - if( message.is_valid_interface() ) - out << message->get_string() << '\n'; - } - return out.str(); -} - -#ifdef _WIN32 - -using MdlLibraryHandle = HMODULE; - -std::string lastLibraryError() -{ - std::ostringstream out; - out << "Windows error " << GetLastError(); - return out.str(); -} - -MdlLibraryHandle loadMdlSdkLibrary( std::string& error ) -{ - const char* const libraryName = "libmdl_sdk" MI_BASE_DLL_FILE_EXT; - MdlLibraryHandle handle = LoadLibraryA( libraryName ); - if( handle ) - return handle; - - const std::string fallback = std::string( "../../../bin/" ) + libraryName; - handle = LoadLibraryA( fallback.c_str() ); - if( handle ) - return handle; - - error = "Failed to load " + std::string( libraryName ) + ": " + lastLibraryError(); - return nullptr; -} - -void* loadMdlFactorySymbol( MdlLibraryHandle handle, std::string& error ) -{ - void* symbol = GetProcAddress( handle, "mi_factory" ); - if( !symbol ) - error = "Failed to find mi_factory: " + lastLibraryError(); - return symbol; -} - -void unloadMdlSdkLibrary( MdlLibraryHandle handle ) -{ - if( handle ) - FreeLibrary( handle ); -} - -#else - -using MdlLibraryHandle = void*; - -MdlLibraryHandle loadMdlSdkLibrary( std::string& error ) -{ - const char* const libraryName = "libmdl_sdk" MI_BASE_DLL_FILE_EXT; - MdlLibraryHandle handle = dlopen( libraryName, RTLD_LAZY ); - if( !handle ) - error = dlerror(); - return handle; -} - -void* loadMdlFactorySymbol( MdlLibraryHandle handle, std::string& error ) -{ - void* symbol = dlsym( handle, "mi_factory" ); - if( !symbol ) - error = dlerror(); - return symbol; -} - -void unloadMdlSdkLibrary( MdlLibraryHandle handle ) -{ - if( handle ) - dlclose( handle ); -} - -#endif - -class MdlSdkSession -{ - public: - MdlSdkSession() - : m_library( loadMdlSdkLibrary( m_error ) ) - { - if( !m_library ) - return; - - void* symbol = loadMdlFactorySymbol( m_library, m_error ); - if( !symbol ) - return; - - m_neuray = mi::neuraylib::mi_factory( symbol ); - if( !m_neuray.is_valid_interface() ) - { - mi::base::Handle version( mi::neuraylib::mi_factory( symbol ) ); - m_error = version.is_valid_interface() ? "MDL SDK library version does not match header version " - + std::string( MI_NEURAYLIB_PRODUCT_VERSION_STRING ) : - "MDL SDK library is incompatible with this header"; - return; - } - - const mi::Sint32 startResult = m_neuray->start( true ); - if( startResult != 0 ) - { - std::ostringstream out; - out << "Failed to start MDL SDK: " << startResult; - m_error = out.str(); - return; - } - - m_started = true; - } - - ~MdlSdkSession() - { - shutdown(); - unloadMdlSdkLibrary( m_library ); - } - - bool isStarted() const { return m_started; } - - const std::string& error() const { return m_error; } - - mi::neuraylib::INeuray* neuray() const { return m_neuray.get(); } - - mi::Sint32 shutdown() - { - mi::Sint32 result = 0; - if( m_started ) - { - result = m_neuray->shutdown( true ); - m_started = false; - } - m_neuray.reset(); - return result; - } - - private: - MdlLibraryHandle m_library{}; - mi::base::Handle m_neuray; - std::string m_error; - bool m_started{ false }; -}; - void addRgbSpectrum( ::pbrt::ParamSet& params, const std::string& name, float red, float green, float blue ) { std::unique_ptr<::pbrt::Float[]> values{ new ::pbrt::Float[3] }; @@ -608,7 +484,7 @@ std::string describeGeneratedSource( const demandPbrtScene::GeneratedMdlSource& return "module=" + source.moduleName + ", material=" + source.materialName + ", key=" + demandPbrtScene::toString( key ); } -mi::base::Handle compileGeneratedMaterialWithBoundParameters( +CompiledMaterialHandle compileGeneratedMaterialWithBoundParameters( mi::neuraylib::INeuray* neuray, mi::neuraylib::ITransaction* transaction, mi::neuraylib::IMdl_execution_context* context, @@ -617,28 +493,28 @@ mi::base::Handle compileGeneratedMaterialWith const std::vector& parameters ) { const std::string sourceDescription{ describeGeneratedSource( source, key ) }; - mi::base::Handle mdlFactory( neuray->get_api_component() ); + MdlFactoryHandle mdlFactory( neuray->get_api_component() ); EXPECT_TRUE( mdlFactory.is_valid_interface() ) << sourceDescription; if( !mdlFactory.is_valid_interface() ) return {}; - mi::base::Handle mdlImpexpApi( neuray->get_api_component() ); + MdlImpexpApiHandle mdlImpexpApi( neuray->get_api_component() ); EXPECT_TRUE( mdlImpexpApi.is_valid_interface() ) << sourceDescription; if( !mdlImpexpApi.is_valid_interface() ) return {}; - mi::base::Handle moduleDbName( mdlFactory->get_db_module_name( source.moduleName.c_str() ) ); + ConstStringHandle moduleDbName( mdlFactory->get_db_module_name( source.moduleName.c_str() ) ); EXPECT_TRUE( moduleDbName.is_valid_interface() ) << sourceDescription; if( !moduleDbName.is_valid_interface() ) return {}; - mi::base::Handle module( transaction->access( moduleDbName->get_c_str() ) ); + ModuleHandle module( transaction->access( moduleDbName->get_c_str() ) ); if( !module.is_valid_interface() ) { context->clear_messages(); const mi::Sint32 loadResult = mdlImpexpApi->load_module_from_string( transaction, source.moduleName.c_str(), source.source.c_str(), context ); - EXPECT_EQ( 0, loadResult ) << sourceDescription << '\n' << describeContextMessages( context ); + EXPECT_EQ( 0, loadResult ) << sourceDescription << '\n' << describeMdlContextMessages( context ); if( loadResult != 0 ) return {}; @@ -654,25 +530,25 @@ mi::base::Handle compileGeneratedMaterialWith if( !materialDbName ) return {}; - mi::base::Handle materialDefinition( + FunctionDefinitionHandle materialDefinition( transaction->access( materialDbName ) ); EXPECT_TRUE( materialDefinition.is_valid_interface() ) << sourceDescription; if( !materialDefinition.is_valid_interface() ) return {}; mi::Sint32 callResult = 0; - mi::base::Handle materialCall( materialDefinition->create_function_call( nullptr, &callResult ) ); + FunctionCallHandle materialCall( materialDefinition->create_function_call( nullptr, &callResult ) ); EXPECT_EQ( 0, callResult ) << sourceDescription; EXPECT_TRUE( materialCall.is_valid_interface() ) << sourceDescription; if( !materialCall.is_valid_interface() ) return {}; - mi::base::Handle valueFactory( mdlFactory->create_value_factory( transaction ) ); + ValueFactoryHandle valueFactory( mdlFactory->create_value_factory( transaction ) ); EXPECT_TRUE( valueFactory.is_valid_interface() ) << sourceDescription; if( !valueFactory.is_valid_interface() ) return {}; - mi::base::Handle expressionFactory( mdlFactory->create_expression_factory( transaction ) ); + ExpressionFactoryHandle expressionFactory( mdlFactory->create_expression_factory( transaction ) ); EXPECT_TRUE( expressionFactory.is_valid_interface() ) << sourceDescription; if( !expressionFactory.is_valid_interface() ) return {}; @@ -681,13 +557,13 @@ mi::base::Handle compileGeneratedMaterialWith { if( parameter.type == demandPbrtScene::MdlBoundParameterType::COLOR ) { - mi::base::Handle value( + ColorValueHandle value( valueFactory->create_color( parameter.red, parameter.green, parameter.blue ) ); EXPECT_TRUE( value.is_valid_interface() ) << sourceDescription << ", parameter=" << parameter.name; if( !value.is_valid_interface() ) return {}; - mi::base::Handle expression( expressionFactory->create_constant( value.get() ) ); + ExpressionConstantHandle expression( expressionFactory->create_constant( value.get() ) ); EXPECT_TRUE( expression.is_valid_interface() ) << sourceDescription << ", parameter=" << parameter.name; if( !expression.is_valid_interface() ) return {}; @@ -697,12 +573,12 @@ mi::base::Handle compileGeneratedMaterialWith } else { - mi::base::Handle value( valueFactory->create_float( parameter.value ) ); + FloatValueHandle value( valueFactory->create_float( parameter.value ) ); EXPECT_TRUE( value.is_valid_interface() ) << sourceDescription << ", parameter=" << parameter.name; if( !value.is_valid_interface() ) return {}; - mi::base::Handle expression( expressionFactory->create_constant( value.get() ) ); + ExpressionConstantHandle expression( expressionFactory->create_constant( value.get() ) ); EXPECT_TRUE( expression.is_valid_interface() ) << sourceDescription << ", parameter=" << parameter.name; if( !expression.is_valid_interface() ) return {}; @@ -712,18 +588,18 @@ mi::base::Handle compileGeneratedMaterialWith } } - mi::base::Handle materialInstance( + MaterialInstanceHandle materialInstance( materialCall->get_interface() ); EXPECT_TRUE( materialInstance.is_valid_interface() ) << sourceDescription; if( !materialInstance.is_valid_interface() ) return {}; - mi::base::Handle typeFactory( mdlFactory->create_type_factory( transaction ) ); + TypeFactoryHandle typeFactory( mdlFactory->create_type_factory( transaction ) ); EXPECT_TRUE( typeFactory.is_valid_interface() ) << sourceDescription; if( !typeFactory.is_valid_interface() ) return {}; - mi::base::Handle standardMaterialType( + TypeHandle standardMaterialType( typeFactory->get_predefined_struct( mi::neuraylib::IType_struct::SID_MATERIAL ) ); EXPECT_TRUE( standardMaterialType.is_valid_interface() ) << sourceDescription; if( !standardMaterialType.is_valid_interface() ) @@ -731,14 +607,14 @@ mi::base::Handle compileGeneratedMaterialWith context->clear_messages(); const mi::Sint32 targetTypeResult = context->set_option( "target_type", standardMaterialType.get() ); - EXPECT_EQ( 0, targetTypeResult ) << sourceDescription << '\n' << describeContextMessages( context ); + EXPECT_EQ( 0, targetTypeResult ) << sourceDescription << '\n' << describeMdlContextMessages( context ); if( targetTypeResult != 0 ) return {}; - mi::base::Handle compiledMaterial( + CompiledMaterialHandle compiledMaterial( materialInstance->create_compiled_material( mi::neuraylib::IMaterial_instance::DEFAULT_OPTIONS, context ) ); EXPECT_TRUE( compiledMaterial.is_valid_interface() ) << sourceDescription << '\n' - << describeContextMessages( context ); + << describeMdlContextMessages( context ); return compiledMaterial; } @@ -746,20 +622,20 @@ void expectColorExpressionMatches( const mi::neuraylib::ICompiled_material* comp const char* expressionPath, const BoundMdlColor& expected ) { - mi::base::Handle tintExpression( compiledMaterial->lookup_sub_expression( expressionPath ) ); + ConstExpressionHandle tintExpression( compiledMaterial->lookup_sub_expression( expressionPath ) ); ASSERT_TRUE( tintExpression.is_valid_interface() ); ASSERT_EQ( mi::neuraylib::IExpression::EK_CONSTANT, tintExpression->get_kind() ); - mi::base::Handle tintConstant( + ConstExpressionConstantHandle tintConstant( tintExpression->get_interface() ); ASSERT_TRUE( tintConstant.is_valid_interface() ); - mi::base::Handle tintValue( tintConstant->get_value() ); + ConstColorValueHandle tintValue( tintConstant->get_value() ); ASSERT_TRUE( tintValue.is_valid_interface() ); - mi::base::Handle red( tintValue->get_value( 0 ) ); - mi::base::Handle green( tintValue->get_value( 1 ) ); - mi::base::Handle blue( tintValue->get_value( 2 ) ); + ConstFloatValueHandle red( tintValue->get_value( 0 ) ); + ConstFloatValueHandle green( tintValue->get_value( 1 ) ); + ConstFloatValueHandle blue( tintValue->get_value( 2 ) ); ASSERT_TRUE( red.is_valid_interface() ); ASSERT_TRUE( green.is_valid_interface() ); ASSERT_TRUE( blue.is_valid_interface() ); @@ -781,15 +657,15 @@ void expectTintMatchesPbrtKd( const mi::neuraylib::ICompiled_material* compiledM void expectFloatExpressionMatches( const mi::neuraylib::ICompiled_material* compiledMaterial, const char* expressionPath, float expected ) { - mi::base::Handle expression( compiledMaterial->lookup_sub_expression( expressionPath ) ); + ConstExpressionHandle expression( compiledMaterial->lookup_sub_expression( expressionPath ) ); ASSERT_TRUE( expression.is_valid_interface() ); ASSERT_EQ( mi::neuraylib::IExpression::EK_CONSTANT, expression->get_kind() ); - mi::base::Handle constant( + ConstExpressionConstantHandle constant( expression->get_interface() ); ASSERT_TRUE( constant.is_valid_interface() ); - mi::base::Handle value( constant->get_value() ); + ConstFloatValueHandle value( constant->get_value() ); ASSERT_TRUE( value.is_valid_interface() ); EXPECT_NEAR( expected, value->get_value(), 1.0e-6f ); @@ -805,7 +681,7 @@ const char* findPreviewColorExpressionPath( const mi::neuraylib::ICompiled_mater static const char* const paths[] = { "surface.scattering.tint", "ior" }; for( const char* path : paths ) { - mi::base::Handle expression( compiledMaterial->lookup_sub_expression( path ) ); + ConstExpressionHandle expression( compiledMaterial->lookup_sub_expression( path ) ); if( expression.is_valid_interface() ) { return path; @@ -820,12 +696,12 @@ std::string translateTintExpressionToPtx( mi::neuraylib::INeuray* const mi::neuraylib::ICompiled_material* compiledMaterial, mi::neuraylib::IMdl_execution_context* context ) { - mi::base::Handle backendApi( neuray->get_api_component() ); + BackendApiHandle backendApi( neuray->get_api_component() ); EXPECT_TRUE( backendApi.is_valid_interface() ); if( !backendApi.is_valid_interface() ) return {}; - mi::base::Handle ptxBackend( backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); + BackendHandle ptxBackend( backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); EXPECT_TRUE( ptxBackend.is_valid_interface() ); if( !ptxBackend.is_valid_interface() ) return {}; @@ -836,9 +712,9 @@ std::string translateTintExpressionToPtx( mi::neuraylib::INeuray* { return {}; } - mi::base::Handle targetCode( ptxBackend->translate_material_expression( + TargetCodeHandle targetCode( ptxBackend->translate_material_expression( transaction, compiledMaterial, previewColorExpressionPath, "evaluate_tint", context ) ); - EXPECT_TRUE( targetCode.is_valid_interface() ) << describeContextMessages( context ); + EXPECT_TRUE( targetCode.is_valid_interface() ) << describeMdlContextMessages( context ); if( !targetCode.is_valid_interface() ) return {}; EXPECT_GT( targetCode->get_code_size(), 0U ); @@ -854,20 +730,20 @@ std::string translateNormalExpressionToPtx( mi::neuraylib::INeuray* const mi::neuraylib::ICompiled_material* compiledMaterial, mi::neuraylib::IMdl_execution_context* context ) { - mi::base::Handle backendApi( neuray->get_api_component() ); + BackendApiHandle backendApi( neuray->get_api_component() ); EXPECT_TRUE( backendApi.is_valid_interface() ); if( !backendApi.is_valid_interface() ) return {}; - mi::base::Handle ptxBackend( backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); + BackendHandle ptxBackend( backendApi->get_backend( mi::neuraylib::IMdl_backend_api::MB_CUDA_PTX ) ); EXPECT_TRUE( ptxBackend.is_valid_interface() ); if( !ptxBackend.is_valid_interface() ) return {}; context->clear_messages(); - mi::base::Handle targetCode( + TargetCodeHandle targetCode( ptxBackend->translate_material_expression( transaction, compiledMaterial, "geometry.normal", "evaluate_normal", context ) ); - EXPECT_TRUE( targetCode.is_valid_interface() ) << describeContextMessages( context ); + EXPECT_TRUE( targetCode.is_valid_interface() ) << describeMdlContextMessages( context ); if( !targetCode.is_valid_interface() ) return {}; EXPECT_GT( targetCode->get_code_size(), 0U ); @@ -878,123 +754,133 @@ std::string translateNormalExpressionToPtx( mi::neuraylib::INeuray* return std::string{ targetCode->get_code(), static_cast( targetCode->get_code_size() ) }; } +class TestMdlSdk : public testing::Test +{ + protected: + void SetUp() override + { + ASSERT_TRUE( session.isStarted() ) << session.error(); + + database = session.neuray()->get_api_component(); + ASSERT_TRUE( database.is_valid_interface() ); + + scope = database->get_global_scope(); + ASSERT_TRUE( scope.is_valid_interface() ); + + transaction = scope->create_transaction(); + ASSERT_TRUE( transaction.is_valid_interface() ); + + mdlFactory = session.neuray()->get_api_component(); + ASSERT_TRUE( mdlFactory.is_valid_interface() ); + + context = mdlFactory->create_execution_context(); + ASSERT_TRUE( context.is_valid_interface() ); + } + + void TearDown() override + { + context.reset(); + mdlFactory.reset(); + if( transaction.is_valid_interface() ) + { + EXPECT_EQ( 0, transaction->commit() ); + transaction.reset(); + } + scope.reset(); + database.reset(); + if( session.isStarted() ) + { + EXPECT_EQ( 0, session.shutdown() ); + } + } + + CompiledMaterialHandle compileMaterial( + const demandPbrtScene::GeneratedMdlSource& source, + const demandPbrtScene::MdlShaderKey& key, + const std::vector& parameters ) + { + return compileGeneratedMaterialWithBoundParameters( session.neuray(), transaction.get(), context.get(), source, key, + parameters ); + } + + MdlSdkSession session; + DatabaseHandle database; + ScopeHandle scope; + TransactionHandle transaction; + MdlFactoryHandle mdlFactory; + ExecutionContextHandle context; +}; + } // namespace -TEST( TestMdlSdk, headerProvidesVersionMetadata ) +TEST( TestMdlSdkHeaders, headerProvidesVersionMetadata ) { EXPECT_GT( std::strlen( MI_NEURAYLIB_PRODUCT_VERSION_STRING ), 0U ); EXPECT_GT( MI_NEURAYLIB_API_VERSION, 0 ); } -TEST( TestMdlSdk, headerProvidesNeurayInterfaceId ) +TEST( TestMdlSdkHeaders, headerProvidesNeurayInterfaceId ) { const mi::base::Uuid id = mi::neuraylib::INeuray::IID(); EXPECT_NE( 0U, id.m_id1 | id.m_id2 | id.m_id3 | id.m_id4 ); } -TEST( TestMdlSdk, rejectsPbrtFourierFixtureAsMdlMeasuredBsdfResource ) +TEST_F( TestMdlSdk, rejectsPbrtFourierFixtureAsMdlMeasuredBsdfResource ) { const std::filesystem::path fixture{ pbrtReferenceDir() / "bsdfs" / "roughgold_alpha_0.2.bsdf" }; const demandPbrtScene::FourierBsdfTableLoadResult table{ demandPbrtScene::loadFourierBsdfTable( fixture.string() ) }; ASSERT_TRUE( table ) << table.diagnostic; + BsdfMeasurementHandle measurement( + transaction->create( "Bsdf_measurement" ) ); + ASSERT_TRUE( measurement.is_valid_interface() ); - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle measurement( - transaction->create( "Bsdf_measurement" ) ); - ASSERT_TRUE( measurement.is_valid_interface() ); - EXPECT_EQ( -3, measurement->reset_file( fixture.string().c_str() ) ); + const auto resetResult = measurement->reset_file( fixture.string().c_str() ); + const demandPbrtScene::FourierMdlMeasuredBsdfCapability capability{ demandPbrtScene::fourierMdlMeasuredBsdfCapability() }; - const demandPbrtScene::FourierMdlMeasuredBsdfCapability capability{ demandPbrtScene::fourierMdlMeasuredBsdfCapability() }; - EXPECT_FALSE( capability.acceptsPbrtBsdfTables ); - EXPECT_FALSE( capability.exposesSampleEvaluatePdfCallables ); - EXPECT_EQ( demandPbrtScene::FourierGpuEvaluationPath::PBRT_FOURIER_CALLABLE, capability.selectedPath ); - EXPECT_THAT( capability.reason, testing::HasSubstr( ".mbsdf" ) ); - EXPECT_THAT( capability.reason, testing::HasSubstr( ".bsdf" ) ); + EXPECT_EQ( -3, resetResult ); + EXPECT_FALSE( capability.acceptsPbrtBsdfTables ); + EXPECT_FALSE( capability.exposesSampleEvaluatePdfCallables ); + EXPECT_EQ( demandPbrtScene::FourierGpuEvaluationPath::PBRT_FOURIER_CALLABLE, capability.selectedPath ); + EXPECT_THAT( capability.reason, testing::HasSubstr( ".mbsdf" ) ); + EXPECT_THAT( capability.reason, testing::HasSubstr( ".bsdf" ) ); - measurement.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); + measurement.reset(); } -TEST( TestMdlSdk, compilesGeneratedMatteMaterialWithBoundKd ) +TEST_F( TestMdlSdk, compilesGeneratedMatteMaterialWithBoundKd ) { const otk::pbrt::PbrtMaterial sourceMaterial{ matteMaterial( PBRT_KD_RED, PBRT_KD_GREEN, PBRT_KD_BLUE ) }; const demandPbrtScene::MdlShaderKey key{ demandPbrtScene::makeMdlShaderKey( sourceMaterial ) }; demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( sourceMaterial ) }; - const std::string sourceDescription{ describeGeneratedSource( generated, key ) }; - EXPECT_THAT( sourceDescription, testing::HasSubstr( generated.moduleName ) ); - EXPECT_THAT( sourceDescription, testing::HasSubstr( generated.materialName ) ); - EXPECT_THAT( sourceDescription, testing::HasSubstr( demandPbrtScene::toString( key ) ) ); - EXPECT_THAT( sourceDescription, testing::Not( testing::HasSubstr( ":\\" ) ) ); - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const BoundMdlColor firstKd{ PBRT_KD_RED, PBRT_KD_GREEN, PBRT_KD_BLUE }; - const BoundMdlColor secondKd{ PBRT_KD_ALT_RED, PBRT_KD_ALT_GREEN, PBRT_KD_ALT_BLUE }; - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( sourceMaterial ) }; - const std::vector secondParameters{ demandPbrtScene::makeMdlBoundMaterialParameters( - matteMaterial( PBRT_KD_ALT_RED, PBRT_KD_ALT_GREEN, PBRT_KD_ALT_BLUE ) ) }; - mi::base::Handle compiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, firstParameters ) ); - ASSERT_TRUE( compiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - expectTintMatchesPbrtKd( compiledMaterial.get(), firstKd ); - expectTintMatchesPbrtKd( secondCompiledMaterial.get(), secondKd ); - const std::string firstPtx{ - translateTintExpressionToPtx( session.neuray(), transaction.get(), compiledMaterial.get(), context.get() ) }; - const std::string secondPtx{ translateTintExpressionToPtx( session.neuray(), transaction.get(), - secondCompiledMaterial.get(), context.get() ) }; - EXPECT_FALSE( firstPtx.empty() ); - EXPECT_FALSE( secondPtx.empty() ); - EXPECT_NE( firstPtx, secondPtx ); - - secondCompiledMaterial.reset(); - compiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedMatteMaterialWithFoldedKdTexture ) + const BoundMdlColor firstKd{ PBRT_KD_RED, PBRT_KD_GREEN, PBRT_KD_BLUE }; + const BoundMdlColor secondKd{ PBRT_KD_ALT_RED, PBRT_KD_ALT_GREEN, PBRT_KD_ALT_BLUE }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( sourceMaterial ) }; + const std::vector secondParameters{ demandPbrtScene::makeMdlBoundMaterialParameters( + matteMaterial( PBRT_KD_ALT_RED, PBRT_KD_ALT_GREEN, PBRT_KD_ALT_BLUE ) ) }; + + CompiledMaterialHandle compiledMaterial( compileMaterial( generated, key, firstParameters ) ); + ASSERT_TRUE( compiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, key, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + const std::string firstPtx{ + translateTintExpressionToPtx( session.neuray(), transaction.get(), compiledMaterial.get(), context.get() ) }; + const std::string secondPtx{ translateTintExpressionToPtx( session.neuray(), transaction.get(), + secondCompiledMaterial.get(), context.get() ) }; + + expectTintMatchesPbrtKd( compiledMaterial.get(), firstKd ); + expectTintMatchesPbrtKd( secondCompiledMaterial.get(), secondKd ); + EXPECT_FALSE( firstPtx.empty() ); + EXPECT_FALSE( secondPtx.empty() ); + EXPECT_NE( firstPtx, secondPtx ); + + secondCompiledMaterial.reset(); + compiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedMatteMaterialWithFoldedKdTexture ) { const BoundMdlColor firstKd{ PBRT_KD_RED, PBRT_KD_GREEN, PBRT_KD_BLUE }; const BoundMdlColor secondKd{ PBRT_KD_ALT_RED, PBRT_KD_ALT_GREEN, PBRT_KD_ALT_BLUE }; @@ -1003,103 +889,53 @@ TEST( TestMdlSdk, compilesGeneratedMatteMaterialWithFoldedKdTexture ) demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( sourceMaterial ) }; const demandPbrtScene::GeneratedMdlSource& secondGenerated{ sourceCache.getSource( matteMaterialWithKdTexture( secondKd ) ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( sourceMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( matteMaterialWithKdTexture( secondKd ) ) }; + + CompiledMaterialHandle compiledMaterial( compileMaterial( generated, key, firstParameters ) ); + ASSERT_TRUE( compiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, key, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + EXPECT_EQ( generated.moduleName, secondGenerated.moduleName ); EXPECT_EQ( generated.materialName, secondGenerated.materialName ); EXPECT_EQ( generated.source, secondGenerated.source ); EXPECT_THAT( generated.source, testing::HasSubstr( "// pbrt material input Kd: Kd" ) ); EXPECT_THAT( generated.source, testing::Not( testing::HasSubstr( "// pbrt texture node:" ) ) ); + expectTintMatchesPbrtKd( compiledMaterial.get(), firstKd ); + expectTintMatchesPbrtKd( secondCompiledMaterial.get(), secondKd ); - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( sourceMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( matteMaterialWithKdTexture( secondKd ) ) }; - mi::base::Handle compiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, firstParameters ) ); - ASSERT_TRUE( compiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - expectTintMatchesPbrtKd( compiledMaterial.get(), firstKd ); - expectTintMatchesPbrtKd( secondCompiledMaterial.get(), secondKd ); - - compiledMaterial.reset(); - secondCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); + compiledMaterial.reset(); + secondCompiledMaterial.reset(); } -TEST( TestMdlSdk, compilesGeneratedMaterialWithRuntimeBumpmap ) +TEST_F( TestMdlSdk, compilesGeneratedMaterialWithRuntimeBumpmap ) { const otk::pbrt::PbrtMaterial sourceMaterial{ matteMaterialWithBumpmap() }; const demandPbrtScene::MdlShaderKey key{ demandPbrtScene::makeMdlShaderKey( sourceMaterial ) }; demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( sourceMaterial ) }; - EXPECT_THAT( generated.source, testing::Not( testing::HasSubstr( "pbrt_bump_normal" ) ) ); - EXPECT_THAT( generated.source, - testing::HasSubstr( "// pbrt material implementation: bumpmap is evaluated with runtime finite differences" ) ); const std::string sourceDescription{ describeGeneratedSource( generated, key ) }; + const std::vector parameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( sourceMaterial ) }; - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); + CompiledMaterialHandle compiledMaterial( compileMaterial( generated, key, parameters ) ); + ASSERT_TRUE( compiledMaterial.is_valid_interface() ) << sourceDescription; + const std::string normalPtx{ translateNormalExpressionToPtx( session.neuray(), transaction.get(), + compiledMaterial.get(), context.get() ) }; - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector parameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( sourceMaterial ) }; - mi::base::Handle compiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, parameters ) ); - ASSERT_TRUE( compiledMaterial.is_valid_interface() ) << sourceDescription; - - const std::string normalPtx{ translateNormalExpressionToPtx( session.neuray(), transaction.get(), - compiledMaterial.get(), context.get() ) }; - EXPECT_FALSE( normalPtx.empty() ) << sourceDescription; - EXPECT_THAT( normalPtx, testing::HasSubstr( "evaluate_normal" ) ) << sourceDescription; - - compiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } + EXPECT_THAT( generated.source, testing::Not( testing::HasSubstr( "pbrt_bump_normal" ) ) ); + EXPECT_THAT( generated.source, + testing::HasSubstr( "// pbrt material implementation: bumpmap is evaluated with runtime finite differences" ) ); + EXPECT_FALSE( normalPtx.empty() ) << sourceDescription; + EXPECT_THAT( normalPtx, testing::HasSubstr( "evaluate_normal" ) ) << sourceDescription; - EXPECT_EQ( 0, session.shutdown() ); + compiledMaterial.reset(); } -TEST( TestMdlSdk, compilesGeneratedMatteBsdfCallablesWithBoundKd ) +TEST_F( TestMdlSdk, compilesGeneratedMatteBsdfCallablesWithBoundKd ) { const otk::pbrt::PbrtMaterial firstMaterial{ matteMaterial( PBRT_KD_RED, PBRT_KD_GREEN, PBRT_KD_BLUE ) }; const otk::pbrt::PbrtMaterial secondMaterial{ matteMaterial( PBRT_KD_ALT_RED, PBRT_KD_ALT_GREEN, PBRT_KD_ALT_BLUE ) }; @@ -1107,138 +943,82 @@ TEST( TestMdlSdk, compilesGeneratedMatteBsdfCallablesWithBoundKd ) const demandPbrtScene::MdlShaderKey firstKey{ demandPbrtScene::makeMdlShaderKey( firstMaterial ) }; const demandPbrtScene::MdlShaderKey secondKey{ demandPbrtScene::makeMdlShaderKey( secondMaterial ) }; const demandPbrtScene::MdlShaderKey roughKey{ demandPbrtScene::makeMdlShaderKey( roughMaterial ) }; - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); - demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector roughParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle roughCompiledMaterial( compileMaterial( generated, firstKey, roughParameters ) ); + ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_matte_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_matte_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_matte_bsdf" ) }; - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector roughParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle roughCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, roughParameters ) ); - ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_matte_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_matte_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_matte_bsdf" ) }; - - EXPECT_EQ( "pbrt_matte_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_matte_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_matte_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_matte_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( roughBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - roughCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedMirrorBsdfCallablesWithBoundKr ) + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); + EXPECT_EQ( "pbrt_matte_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_matte_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_matte_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_matte_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( roughBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + roughCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedMirrorBsdfCallablesWithBoundKr ) { const otk::pbrt::PbrtMaterial material{ mirrorMaterial() }; const demandPbrtScene::MdlShaderKey key{ demandPbrtScene::makeMdlShaderKey( material ) }; - demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( material ) }; - EXPECT_THAT( generated.source, testing::HasSubstr( "::df::specular_bsdf" ) ); const std::string sourceDescription{ describeGeneratedSource( generated, key ) }; + const std::vector parameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( material ) }; - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector parameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( material ) }; - mi::base::Handle compiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, parameters ) ); - ASSERT_TRUE( compiledMaterial.is_valid_interface() ); - - const demandPbrtScene::MdlBsdfCallablePtx bsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), compiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_mirror_bsdf" ) }; - - EXPECT_EQ( "pbrt_mirror_bsdf_init", bsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_mirror_bsdf_sample", bsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_mirror_bsdf_evaluate", bsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_mirror_bsdf_pdf", bsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.initFunctionName ) ); - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.sampleFunctionName ) ); - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.evaluateFunctionName ) ); - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.pdfFunctionName ) ); + CompiledMaterialHandle compiledMaterial( compileMaterial( generated, key, parameters ) ); + ASSERT_TRUE( compiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx bsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), compiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_mirror_bsdf" ) }; - compiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } + EXPECT_THAT( generated.source, testing::HasSubstr( "::df::specular_bsdf" ) ); + EXPECT_EQ( "pbrt_mirror_bsdf_init", bsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_mirror_bsdf_sample", bsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_mirror_bsdf_evaluate", bsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_mirror_bsdf_pdf", bsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.initFunctionName ) ); + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.sampleFunctionName ) ); + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.evaluateFunctionName ) ); + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.pdfFunctionName ) ); - EXPECT_EQ( 0, session.shutdown() ); + compiledMaterial.reset(); } -TEST( TestMdlSdk, compilesGeneratedPlasticBsdfCallablesWithBoundDiffuseAndGlossyInputs ) +TEST_F( TestMdlSdk, compilesGeneratedPlasticBsdfCallablesWithBoundDiffuseAndGlossyInputs ) { const BoundMdlColor firstKd{ 0.2f, 0.3f, 0.4f }; const BoundMdlColor firstKs{ 0.5f, 0.6f, 0.7f }; @@ -1250,86 +1030,56 @@ TEST( TestMdlSdk, compilesGeneratedPlasticBsdfCallablesWithBoundDiffuseAndGlossy const demandPbrtScene::MdlShaderKey firstKey{ demandPbrtScene::makeMdlShaderKey( firstMaterial ) }; const demandPbrtScene::MdlShaderKey secondKey{ demandPbrtScene::makeMdlShaderKey( secondMaterial ) }; const demandPbrtScene::MdlShaderKey roughKey{ demandPbrtScene::makeMdlShaderKey( roughMaterial ) }; - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); - demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector roughParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle roughCompiledMaterial( compileMaterial( generated, firstKey, roughParameters ) ); + ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_plastic_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_plastic_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_plastic_bsdf" ) }; + + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::color_normalized_mix" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::simple_glossy_bsdf" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "component: ::df::diffuse_reflection_bsdf" ) ); - const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector roughParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle roughCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, roughParameters ) ); - ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_plastic_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_plastic_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_plastic_bsdf" ) }; - - EXPECT_EQ( "pbrt_plastic_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_plastic_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_plastic_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_plastic_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( roughBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - roughCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedUberBsdfCallablesWithBoundDiffuseAndGlossyInputs ) + EXPECT_EQ( "pbrt_plastic_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_plastic_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_plastic_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_plastic_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( roughBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + roughCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedUberBsdfCallablesWithBoundDiffuseAndGlossyInputs ) { const BoundMdlColor firstKd{ 0.2f, 0.3f, 0.4f }; const BoundMdlColor firstKs{ 0.5f, 0.6f, 0.7f }; @@ -1353,14 +1103,55 @@ TEST( TestMdlSdk, compilesGeneratedUberBsdfCallablesWithBoundDiffuseAndGlossyInp const demandPbrtScene::MdlShaderKey opacityKey{ demandPbrtScene::makeMdlShaderKey( opacityMaterial ) }; const demandPbrtScene::MdlShaderKey spectrumOpacityKey{ demandPbrtScene::makeMdlShaderKey( spectrumOpacityMaterial ) }; const demandPbrtScene::MdlShaderKey alphaKey{ demandPbrtScene::makeMdlShaderKey( alphaMaterial ) }; + demandPbrtScene::MdlGeneratedSourceCache sourceCache; + const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector roughParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; + const std::vector opacityParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( opacityMaterial ) }; + const std::vector spectrumOpacityParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( spectrumOpacityMaterial ) }; + const std::vector alphaParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( alphaMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle roughCompiledMaterial( compileMaterial( generated, firstKey, roughParameters ) ); + ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle opacityCompiledMaterial( compileMaterial( generated, firstKey, opacityParameters ) ); + ASSERT_TRUE( opacityCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle spectrumOpacityCompiledMaterial( compileMaterial( generated, firstKey, spectrumOpacityParameters ) ); + ASSERT_TRUE( spectrumOpacityCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle alphaCompiledMaterial( compileMaterial( generated, firstKey, alphaParameters ) ); + ASSERT_TRUE( alphaCompiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx opacityBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), opacityCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx spectrumOpacityBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), spectrumOpacityCompiledMaterial.get(), context.get(), + "surface.scattering", "pbrt_uber_bsdf" ) }; + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( opacityKey ) ); EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( spectrumOpacityKey ) ); EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( alphaKey ) ); - - demandPbrtScene::MdlGeneratedSourceCache sourceCache; - const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; EXPECT_THAT( generated.source, testing::HasSubstr( "::df::color_normalized_mix" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::simple_glossy_bsdf" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::specular_bsdf" ) ); @@ -1371,114 +1162,36 @@ TEST( TestMdlSdk, compilesGeneratedUberBsdfCallablesWithBoundDiffuseAndGlossyInp EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_uber_opacity_weight" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_uber_transparency_weight" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "cutout_opacity: alpha" ) ); - const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector roughParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; - const std::vector opacityParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( opacityMaterial ) }; - const std::vector spectrumOpacityParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( spectrumOpacityMaterial ) }; - const std::vector alphaParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( alphaMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle roughCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, roughParameters ) ); - ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); - - mi::base::Handle opacityCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, opacityParameters ) ); - ASSERT_TRUE( opacityCompiledMaterial.is_valid_interface() ); - - mi::base::Handle spectrumOpacityCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, spectrumOpacityParameters ) ); - ASSERT_TRUE( spectrumOpacityCompiledMaterial.is_valid_interface() ); - - mi::base::Handle alphaCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, alphaParameters ) ); - ASSERT_TRUE( alphaCompiledMaterial.is_valid_interface() ); - - expectIorMatchesFloat( firstCompiledMaterial.get(), 1.4f ); - expectFloatExpressionMatches( firstCompiledMaterial.get(), "geometry.cutout_opacity", 0.8f ); - expectFloatExpressionMatches( opacityCompiledMaterial.get(), "geometry.cutout_opacity", 0.8f ); - expectFloatExpressionMatches( alphaCompiledMaterial.get(), "geometry.cutout_opacity", 0.35f ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx opacityBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), opacityCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_uber_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx spectrumOpacityBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), spectrumOpacityCompiledMaterial.get(), context.get(), - "surface.scattering", "pbrt_uber_bsdf" ) }; - - EXPECT_EQ( "pbrt_uber_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_uber_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_uber_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_uber_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( roughBsdf.ptx.empty() ); - EXPECT_FALSE( opacityBsdf.ptx.empty() ); - EXPECT_FALSE( spectrumOpacityBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, opacityBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, spectrumOpacityBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - roughCompiledMaterial.reset(); - opacityCompiledMaterial.reset(); - spectrumOpacityCompiledMaterial.reset(); - alphaCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedSubstrateBsdfCallablesWithBoundLayeredInputs ) + expectIorMatchesFloat( firstCompiledMaterial.get(), 1.4f ); + expectFloatExpressionMatches( firstCompiledMaterial.get(), "geometry.cutout_opacity", 0.8f ); + expectFloatExpressionMatches( opacityCompiledMaterial.get(), "geometry.cutout_opacity", 0.8f ); + expectFloatExpressionMatches( alphaCompiledMaterial.get(), "geometry.cutout_opacity", 0.35f ); + EXPECT_EQ( "pbrt_uber_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_uber_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_uber_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_uber_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( roughBsdf.ptx.empty() ); + EXPECT_FALSE( opacityBsdf.ptx.empty() ); + EXPECT_FALSE( spectrumOpacityBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, opacityBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, spectrumOpacityBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + roughCompiledMaterial.reset(); + opacityCompiledMaterial.reset(); + spectrumOpacityCompiledMaterial.reset(); + alphaCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedSubstrateBsdfCallablesWithBoundLayeredInputs ) { const BoundMdlColor firstKd{ 0.2f, 0.3f, 0.4f }; const BoundMdlColor firstKs{ 0.5f, 0.6f, 0.7f }; @@ -1490,87 +1203,57 @@ TEST( TestMdlSdk, compilesGeneratedSubstrateBsdfCallablesWithBoundLayeredInputs const demandPbrtScene::MdlShaderKey firstKey{ demandPbrtScene::makeMdlShaderKey( firstMaterial ) }; const demandPbrtScene::MdlShaderKey secondKey{ demandPbrtScene::makeMdlShaderKey( secondMaterial ) }; const demandPbrtScene::MdlShaderKey roughKey{ demandPbrtScene::makeMdlShaderKey( roughMaterial ) }; - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); - demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector roughParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle roughCompiledMaterial( compileMaterial( generated, firstKey, roughParameters ) ); + ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), firstCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_substrate_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), secondCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_substrate_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), roughCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_substrate_bsdf" ) }; + + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::color_weighted_layer" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::simple_glossy_bsdf" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "base: ::df::diffuse_reflection_bsdf" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_substrate_resolved_roughness" ) ); - const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector roughParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle roughCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, roughParameters ) ); - ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), firstCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_substrate_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), secondCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_substrate_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), roughCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_substrate_bsdf" ) }; - - EXPECT_EQ( "pbrt_substrate_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_substrate_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_substrate_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_substrate_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( roughBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - roughCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedGlassBsdfCallablesWithBoundDielectricInputs ) + EXPECT_EQ( "pbrt_substrate_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_substrate_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_substrate_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_substrate_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( roughBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + roughCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedGlassBsdfCallablesWithBoundDielectricInputs ) { const otk::pbrt::PbrtMaterial firstMaterial{ glassMaterial( 1.5f, 0.0f, 0.0f, 0.0f ) }; const otk::pbrt::PbrtMaterial secondMaterial{ glassMaterial( 1.1f, 0.0f, 0.0f, 0.0f ) }; @@ -1578,93 +1261,62 @@ TEST( TestMdlSdk, compilesGeneratedGlassBsdfCallablesWithBoundDielectricInputs ) const demandPbrtScene::MdlShaderKey firstKey{ demandPbrtScene::makeMdlShaderKey( firstMaterial ) }; const demandPbrtScene::MdlShaderKey secondKey{ demandPbrtScene::makeMdlShaderKey( secondMaterial ) }; const demandPbrtScene::MdlShaderKey roughKey{ demandPbrtScene::makeMdlShaderKey( roughMaterial ) }; - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); - demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector roughParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle roughCompiledMaterial( compileMaterial( generated, firstKey, roughParameters ) ); + ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); + const std::string tintPtx{ translateTintExpressionToPtx( session.neuray(), transaction.get(), + firstCompiledMaterial.get(), context.get() ) }; + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_glass_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_glass_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_glass_bsdf" ) }; + + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::tint" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::microfacet_ggx_smith_bsdf" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_glass_resolved_roughness" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::scatter_reflect_transmit" ) ); - const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector roughParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle roughCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, roughParameters ) ); - ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); - expectIorMatchesFloat( firstCompiledMaterial.get(), 1.5f ); - expectIorMatchesFloat( secondCompiledMaterial.get(), 1.1f ); - expectIorMatchesFloat( roughCompiledMaterial.get(), 1.5f ); - - const std::string tintPtx{ translateTintExpressionToPtx( session.neuray(), transaction.get(), - firstCompiledMaterial.get(), context.get() ) }; - EXPECT_FALSE( tintPtx.empty() ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_glass_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_glass_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_glass_bsdf" ) }; - - EXPECT_EQ( "pbrt_glass_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_glass_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_glass_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_glass_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( roughBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - roughCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedMetalBsdfCallablesWithBoundConductorInputs ) + expectIorMatchesFloat( firstCompiledMaterial.get(), 1.5f ); + expectIorMatchesFloat( secondCompiledMaterial.get(), 1.1f ); + expectIorMatchesFloat( roughCompiledMaterial.get(), 1.5f ); + EXPECT_FALSE( tintPtx.empty() ); + EXPECT_EQ( "pbrt_glass_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_glass_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_glass_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_glass_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( roughBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + roughCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedMetalBsdfCallablesWithBoundConductorInputs ) { const BoundMdlColor firstEta{ 0.2f, 0.3f, 0.45f }; const BoundMdlColor firstK{ 2.2f, 2.8f, 3.4f }; @@ -1676,90 +1328,59 @@ TEST( TestMdlSdk, compilesGeneratedMetalBsdfCallablesWithBoundConductorInputs ) const demandPbrtScene::MdlShaderKey firstKey{ demandPbrtScene::makeMdlShaderKey( firstMaterial ) }; const demandPbrtScene::MdlShaderKey secondKey{ demandPbrtScene::makeMdlShaderKey( secondMaterial ) }; const demandPbrtScene::MdlShaderKey roughKey{ demandPbrtScene::makeMdlShaderKey( roughMaterial ) }; - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); - demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector roughParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle roughCompiledMaterial( compileMaterial( generated, firstKey, roughParameters ) ); + ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_metal_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_metal_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_metal_bsdf" ) }; + + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::microfacet_ggx_smith_bsdf" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_metal_conductor_tint" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_metal_resolved_roughness" ) ); - const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector roughParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle roughCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, roughParameters ) ); - ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); - - expectTintMatchesColor( firstCompiledMaterial.get(), conductorNormalReflectance( firstEta, firstK ) ); - expectTintMatchesColor( secondCompiledMaterial.get(), conductorNormalReflectance( secondEta, secondK ) ); - expectTintMatchesColor( roughCompiledMaterial.get(), conductorNormalReflectance( firstEta, firstK ) ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_metal_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_metal_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), roughCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_metal_bsdf" ) }; - - EXPECT_EQ( "pbrt_metal_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_metal_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_metal_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_metal_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( roughBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - roughCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedTranslucentBsdfCallablesWithBoundReflectionTransmissionAndOpacity ) + expectTintMatchesColor( firstCompiledMaterial.get(), conductorNormalReflectance( firstEta, firstK ) ); + expectTintMatchesColor( secondCompiledMaterial.get(), conductorNormalReflectance( secondEta, secondK ) ); + expectTintMatchesColor( roughCompiledMaterial.get(), conductorNormalReflectance( firstEta, firstK ) ); + EXPECT_EQ( "pbrt_metal_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_metal_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_metal_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_metal_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( roughBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + roughCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedTranslucentBsdfCallablesWithBoundReflectionTransmissionAndOpacity ) { const BoundMdlColor firstKd{ 0.2f, 0.3f, 0.4f }; const BoundMdlColor firstKs{ 0.5f, 0.6f, 0.7f }; @@ -1782,13 +1403,50 @@ TEST( TestMdlSdk, compilesGeneratedTranslucentBsdfCallablesWithBoundReflectionTr const demandPbrtScene::MdlShaderKey roughKey{ demandPbrtScene::makeMdlShaderKey( roughMaterial ) }; const demandPbrtScene::MdlShaderKey opacityKey{ demandPbrtScene::makeMdlShaderKey( opacityMaterial ) }; const demandPbrtScene::MdlShaderKey spectrumOpacityKey{ demandPbrtScene::makeMdlShaderKey( spectrumOpacityMaterial ) }; + demandPbrtScene::MdlGeneratedSourceCache sourceCache; + const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector roughParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; + const std::vector opacityParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( opacityMaterial ) }; + const std::vector spectrumOpacityParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( spectrumOpacityMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle roughCompiledMaterial( compileMaterial( generated, firstKey, roughParameters ) ); + ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle opacityCompiledMaterial( compileMaterial( generated, firstKey, opacityParameters ) ); + ASSERT_TRUE( opacityCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle spectrumOpacityCompiledMaterial( compileMaterial( generated, firstKey, spectrumOpacityParameters ) ); + ASSERT_TRUE( spectrumOpacityCompiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), firstCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_translucent_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), secondCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_translucent_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), roughCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_translucent_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx opacityBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), opacityCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_translucent_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx spectrumOpacityBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), spectrumOpacityCompiledMaterial.get(), context.get(), + "surface.scattering", "pbrt_translucent_bsdf" ) }; + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( roughKey ) ); EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( opacityKey ) ); EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( spectrumOpacityKey ) ); - - demandPbrtScene::MdlGeneratedSourceCache sourceCache; - const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; EXPECT_THAT( generated.source, testing::HasSubstr( "::df::color_normalized_mix" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::diffuse_reflection_bsdf" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::diffuse_transmission_bsdf" ) ); @@ -1799,104 +1457,32 @@ TEST( TestMdlSdk, compilesGeneratedTranslucentBsdfCallablesWithBoundReflectionTr EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_translucent_opacity_weight" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "pbrt_translucent_transparency_weight" ) ); EXPECT_THAT( generated.source, testing::Not( testing::HasSubstr( "cutout_opacity: opacity" ) ) ); - const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector roughParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( roughMaterial ) }; - const std::vector opacityParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( opacityMaterial ) }; - const std::vector spectrumOpacityParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( spectrumOpacityMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle roughCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, roughParameters ) ); - ASSERT_TRUE( roughCompiledMaterial.is_valid_interface() ); - - mi::base::Handle opacityCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, opacityParameters ) ); - ASSERT_TRUE( opacityCompiledMaterial.is_valid_interface() ); - - mi::base::Handle spectrumOpacityCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, spectrumOpacityParameters ) ); - ASSERT_TRUE( spectrumOpacityCompiledMaterial.is_valid_interface() ); - - expectIorMatchesFloat( firstCompiledMaterial.get(), 1.5f ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), firstCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_translucent_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), secondCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_translucent_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx roughBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), roughCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_translucent_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx opacityBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), opacityCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_translucent_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx spectrumOpacityBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), spectrumOpacityCompiledMaterial.get(), context.get(), - "surface.scattering", "pbrt_translucent_bsdf" ) }; - - EXPECT_EQ( "pbrt_translucent_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_translucent_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_translucent_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_translucent_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( roughBsdf.ptx.empty() ); - EXPECT_FALSE( opacityBsdf.ptx.empty() ); - EXPECT_FALSE( spectrumOpacityBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, opacityBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, spectrumOpacityBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - roughCompiledMaterial.reset(); - opacityCompiledMaterial.reset(); - spectrumOpacityCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesGeneratedSubsurfaceApproximationBsdfCallables ) + expectIorMatchesFloat( firstCompiledMaterial.get(), 1.5f ); + EXPECT_EQ( "pbrt_translucent_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_translucent_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_translucent_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_translucent_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( roughBsdf.ptx.empty() ); + EXPECT_FALSE( opacityBsdf.ptx.empty() ); + EXPECT_FALSE( spectrumOpacityBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, roughBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, opacityBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, spectrumOpacityBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + roughCompiledMaterial.reset(); + opacityCompiledMaterial.reset(); + spectrumOpacityCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesGeneratedSubsurfaceApproximationBsdfCallables ) { const otk::pbrt::PbrtMaterial subsurface{ subsurfaceMaterial( BoundMdlColor{ 0.7f, 0.6f, 0.5f }, BoundMdlColor{ 0.2f, 0.3f, 0.4f }, @@ -1904,65 +1490,39 @@ TEST( TestMdlSdk, compilesGeneratedSubsurfaceApproximationBsdfCallables ) const otk::pbrt::PbrtMaterial kdSubsurface{ kdSubsurfaceMaterial( BoundMdlColor{ 0.2f, 0.3f, 0.4f }, BoundMdlColor{ 0.8f, 0.7f, 0.6f }, BoundMdlColor{ 0.4f, 0.5f, 0.6f }, BoundMdlColor{ 0.1f, 0.2f, 0.3f }, 1.5f, 1.33f ) }; + demandPbrtScene::MdlGeneratedSourceCache sourceCache; + const auto expectCompiledBsdf = [&]( const otk::pbrt::PbrtMaterial& material, const char* callablePrefix, float expectedEta ) { + const demandPbrtScene::MdlShaderKey key{ demandPbrtScene::makeMdlShaderKey( material ) }; + const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( material ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, key ) }; + const std::vector parameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( material ) }; - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - demandPbrtScene::MdlGeneratedSourceCache sourceCache; - const auto expectCompiledBsdf = [&]( const otk::pbrt::PbrtMaterial& material, const char* callablePrefix, float expectedEta ) { - const demandPbrtScene::MdlShaderKey key{ demandPbrtScene::makeMdlShaderKey( material ) }; - const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( material ) }; - const std::string sourceDescription{ describeGeneratedSource( generated, key ) }; - EXPECT_TRUE( generated.unsupportedReasons.empty() ) << sourceDescription; - EXPECT_THAT( generated.source, testing::HasSubstr( "::df::diffuse_reflection_bsdf" ) ); - EXPECT_THAT( generated.source, testing::HasSubstr( "::df::diffuse_transmission_bsdf" ) ); - - const std::vector parameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( material ) }; - mi::base::Handle compiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, parameters ) ); - ASSERT_TRUE( compiledMaterial.is_valid_interface() ) << sourceDescription; - expectIorMatchesFloat( compiledMaterial.get(), expectedEta ); - - const demandPbrtScene::MdlBsdfCallablePtx bsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), compiledMaterial.get(), - context.get(), "surface.scattering", callablePrefix ) }; - EXPECT_EQ( std::string{ callablePrefix } + "_init", bsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( std::string{ callablePrefix } + "_sample", bsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( std::string{ callablePrefix } + "_evaluate", bsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( std::string{ callablePrefix } + "_pdf", bsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.initFunctionName ) ); - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.sampleFunctionName ) ); - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.evaluateFunctionName ) ); - EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.pdfFunctionName ) ); - }; - - expectCompiledBsdf( subsurface, "pbrt_subsurface_bsdf", 1.4f ); - expectCompiledBsdf( kdSubsurface, "pbrt_kdsubsurface_bsdf", 1.33f ); - - EXPECT_EQ( 0, transaction->commit() ); - } + CompiledMaterialHandle compiledMaterial( compileMaterial( generated, key, parameters ) ); + ASSERT_TRUE( compiledMaterial.is_valid_interface() ) << sourceDescription; + const demandPbrtScene::MdlBsdfCallablePtx bsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), compiledMaterial.get(), + context.get(), "surface.scattering", callablePrefix ) }; + + EXPECT_TRUE( generated.unsupportedReasons.empty() ) << sourceDescription; + EXPECT_THAT( generated.source, testing::HasSubstr( "::df::diffuse_reflection_bsdf" ) ); + EXPECT_THAT( generated.source, testing::HasSubstr( "::df::diffuse_transmission_bsdf" ) ); + expectIorMatchesFloat( compiledMaterial.get(), expectedEta ); + EXPECT_EQ( std::string{ callablePrefix } + "_init", bsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( std::string{ callablePrefix } + "_sample", bsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( std::string{ callablePrefix } + "_evaluate", bsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( std::string{ callablePrefix } + "_pdf", bsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.initFunctionName ) ); + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.sampleFunctionName ) ); + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.evaluateFunctionName ) ); + EXPECT_THAT( bsdf.ptx, testing::HasSubstr( bsdf.pdfFunctionName ) ); + }; - EXPECT_EQ( 0, session.shutdown() ); + expectCompiledBsdf( subsurface, "pbrt_subsurface_bsdf", 1.4f ); + expectCompiledBsdf( kdSubsurface, "pbrt_kdsubsurface_bsdf", 1.33f ); } -TEST( TestMdlSdk, compilesGeneratedMixBsdfCallablesWithBoundNamedMaterialClosures ) +TEST_F( TestMdlSdk, compilesGeneratedMixBsdfCallablesWithBoundNamedMaterialClosures ) { const BoundMdlColor firstFront{ 0.8f, 0.2f, 0.1f }; const BoundMdlColor firstBack{ 0.1f, 0.3f, 0.9f }; @@ -1980,14 +1540,59 @@ TEST( TestMdlSdk, compilesGeneratedMixBsdfCallablesWithBoundNamedMaterialClosure const demandPbrtScene::MdlShaderKey rgbAmountKey{ demandPbrtScene::makeMdlShaderKey( rgbAmountMaterial ) }; const demandPbrtScene::MdlShaderKey amountTextureKey{ demandPbrtScene::makeMdlShaderKey( amountTextureMaterial ) }; const demandPbrtScene::MdlShaderKey namedUberKey{ demandPbrtScene::makeMdlShaderKey( namedUberMaterial ) }; - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( amountKey ) ); - EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( rgbAmountKey ) ); - demandPbrtScene::MdlGeneratedSourceCache sourceCache; const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( firstMaterial ) }; const demandPbrtScene::GeneratedMdlSource& amountTextureGenerated{ sourceCache.getSource( amountTextureMaterial ) }; const demandPbrtScene::GeneratedMdlSource& namedUberGenerated{ sourceCache.getSource( namedUberMaterial ) }; + const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; + const std::vector firstParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; + const std::vector secondParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; + const std::vector amountParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( amountMaterial ) }; + const std::vector rgbAmountParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( rgbAmountMaterial ) }; + const std::vector amountTextureParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( amountTextureMaterial ) }; + const std::vector namedUberParameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( namedUberMaterial ) }; + + CompiledMaterialHandle firstCompiledMaterial( compileMaterial( generated, firstKey, firstParameters ) ); + ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle secondCompiledMaterial( compileMaterial( generated, firstKey, secondParameters ) ); + ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle amountCompiledMaterial( compileMaterial( generated, firstKey, amountParameters ) ); + ASSERT_TRUE( amountCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle rgbAmountCompiledMaterial( compileMaterial( generated, firstKey, rgbAmountParameters ) ); + ASSERT_TRUE( rgbAmountCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle amountTextureCompiledMaterial( + compileMaterial( amountTextureGenerated, amountTextureKey, amountTextureParameters ) ); + ASSERT_TRUE( amountTextureCompiledMaterial.is_valid_interface() ); + CompiledMaterialHandle namedUberCompiledMaterial( compileMaterial( namedUberGenerated, namedUberKey, namedUberParameters ) ); + ASSERT_TRUE( namedUberCompiledMaterial.is_valid_interface() ); + const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_mix_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_mix_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx amountBsdf{ + demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), amountCompiledMaterial.get(), + context.get(), "surface.scattering", "pbrt_mix_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx rgbAmountBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), rgbAmountCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_mix_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx amountTextureBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), amountTextureCompiledMaterial.get(), context.get(), + "surface.scattering", "pbrt_mix_bsdf" ) }; + const demandPbrtScene::MdlBsdfCallablePtx namedUberBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( + session.neuray(), transaction.get(), namedUberCompiledMaterial.get(), context.get(), "surface.scattering", + "pbrt_mix_uber_bsdf" ) }; + + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( secondKey ) ); + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( amountKey ) ); + EXPECT_EQ( demandPbrtScene::toString( firstKey ), demandPbrtScene::toString( rgbAmountKey ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::color_normalized_mix" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "::df::color_bsdf_component[]" ) ); EXPECT_THAT( generated.source, testing::HasSubstr( "weight: amount" ) ); @@ -1995,148 +1600,45 @@ TEST( TestMdlSdk, compilesGeneratedMixBsdfCallablesWithBoundNamedMaterialClosure EXPECT_THAT( generated.source, testing::HasSubstr( "component: ::df::diffuse_reflection_bsdf" ) ); EXPECT_THAT( generated.source, testing::Not( testing::HasSubstr( "pbrt_mix_approximation_tint" ) ) ); EXPECT_THAT( namedUberGenerated.source, testing::Not( testing::HasSubstr( "named_0_index" ) ) ); - const std::string sourceDescription{ describeGeneratedSource( generated, firstKey ) }; - - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); - - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); - - const std::vector firstParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( firstMaterial ) }; - const std::vector secondParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( secondMaterial ) }; - const std::vector amountParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( amountMaterial ) }; - const std::vector rgbAmountParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( rgbAmountMaterial ) }; - const std::vector amountTextureParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( amountTextureMaterial ) }; - const std::vector namedUberParameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( namedUberMaterial ) }; - mi::base::Handle firstCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, firstParameters ) ); - ASSERT_TRUE( firstCompiledMaterial.is_valid_interface() ); - - mi::base::Handle secondCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, secondParameters ) ); - ASSERT_TRUE( secondCompiledMaterial.is_valid_interface() ); - - mi::base::Handle amountCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, amountParameters ) ); - ASSERT_TRUE( amountCompiledMaterial.is_valid_interface() ); - - mi::base::Handle rgbAmountCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, firstKey, rgbAmountParameters ) ); - ASSERT_TRUE( rgbAmountCompiledMaterial.is_valid_interface() ); - - mi::base::Handle amountTextureCompiledMaterial( - compileGeneratedMaterialWithBoundParameters( session.neuray(), transaction.get(), context.get(), - amountTextureGenerated, amountTextureKey, amountTextureParameters ) ); - ASSERT_TRUE( amountTextureCompiledMaterial.is_valid_interface() ); - mi::base::Handle namedUberCompiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), namedUberGenerated, namedUberKey, namedUberParameters ) ); - ASSERT_TRUE( namedUberCompiledMaterial.is_valid_interface() ); - - const demandPbrtScene::MdlBsdfCallablePtx firstBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), firstCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_mix_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx secondBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), secondCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_mix_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx amountBsdf{ - demandPbrtScene::compileMdlBsdfCallablesToPtx( session.neuray(), transaction.get(), amountCompiledMaterial.get(), - context.get(), "surface.scattering", "pbrt_mix_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx rgbAmountBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), rgbAmountCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_mix_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx amountTextureBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), amountTextureCompiledMaterial.get(), context.get(), - "surface.scattering", "pbrt_mix_bsdf" ) }; - const demandPbrtScene::MdlBsdfCallablePtx namedUberBsdf{ demandPbrtScene::compileMdlBsdfCallablesToPtx( - session.neuray(), transaction.get(), namedUberCompiledMaterial.get(), context.get(), "surface.scattering", - "pbrt_mix_uber_bsdf" ) }; - - EXPECT_EQ( "pbrt_mix_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_mix_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_mix_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; - EXPECT_EQ( "pbrt_mix_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); - EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); - EXPECT_FALSE( secondBsdf.ptx.empty() ); - EXPECT_FALSE( amountBsdf.ptx.empty() ); - EXPECT_FALSE( rgbAmountBsdf.ptx.empty() ); - EXPECT_FALSE( amountTextureBsdf.ptx.empty() ); - EXPECT_FALSE( namedUberBsdf.ptx.empty() ); - EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, amountBsdf.ptx ); - EXPECT_NE( firstBsdf.ptx, rgbAmountBsdf.ptx ); - - firstCompiledMaterial.reset(); - secondCompiledMaterial.reset(); - amountCompiledMaterial.reset(); - rgbAmountCompiledMaterial.reset(); - amountTextureCompiledMaterial.reset(); - namedUberCompiledMaterial.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } - - EXPECT_EQ( 0, session.shutdown() ); -} - -TEST( TestMdlSdk, compilesOpaqueGeneratedMaterialsWithBoundConstants ) -{ - MdlSdkSession session; - ASSERT_TRUE( session.isStarted() ) << session.error(); - - { - mi::base::Handle database( session.neuray()->get_api_component() ); - ASSERT_TRUE( database.is_valid_interface() ); - - mi::base::Handle scope( database->get_global_scope() ); - ASSERT_TRUE( scope.is_valid_interface() ); - - mi::base::Handle transaction( scope->create_transaction() ); - ASSERT_TRUE( transaction.is_valid_interface() ); - - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - ASSERT_TRUE( mdlFactory.is_valid_interface() ); + EXPECT_EQ( "pbrt_mix_bsdf_init", firstBsdf.initFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_mix_bsdf_sample", firstBsdf.sampleFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_mix_bsdf_evaluate", firstBsdf.evaluateFunctionName ) << sourceDescription; + EXPECT_EQ( "pbrt_mix_bsdf_pdf", firstBsdf.pdfFunctionName ) << sourceDescription; + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.initFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.sampleFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.evaluateFunctionName ) ); + EXPECT_THAT( firstBsdf.ptx, testing::HasSubstr( firstBsdf.pdfFunctionName ) ); + EXPECT_FALSE( secondBsdf.ptx.empty() ); + EXPECT_FALSE( amountBsdf.ptx.empty() ); + EXPECT_FALSE( rgbAmountBsdf.ptx.empty() ); + EXPECT_FALSE( amountTextureBsdf.ptx.empty() ); + EXPECT_FALSE( namedUberBsdf.ptx.empty() ); + EXPECT_NE( firstBsdf.ptx, secondBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, amountBsdf.ptx ); + EXPECT_NE( firstBsdf.ptx, rgbAmountBsdf.ptx ); + + firstCompiledMaterial.reset(); + secondCompiledMaterial.reset(); + amountCompiledMaterial.reset(); + rgbAmountCompiledMaterial.reset(); + amountTextureCompiledMaterial.reset(); + namedUberCompiledMaterial.reset(); +} + +TEST_F( TestMdlSdk, compilesOpaqueGeneratedMaterialsWithBoundConstants ) +{ + demandPbrtScene::MdlGeneratedSourceCache sourceCache; + const auto expectCompiledTint = [&]( const otk::pbrt::PbrtMaterial& material, const BoundMdlColor& expected ) { + const demandPbrtScene::MdlShaderKey key{ demandPbrtScene::makeMdlShaderKey( material ) }; + const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( material ) }; + const std::vector parameters{ + demandPbrtScene::makeMdlBoundMaterialParameters( material ) }; - mi::base::Handle context( mdlFactory->create_execution_context() ); - ASSERT_TRUE( context.is_valid_interface() ); + CompiledMaterialHandle compiledMaterial( compileMaterial( generated, key, parameters ) ); + ASSERT_TRUE( compiledMaterial.is_valid_interface() ); - demandPbrtScene::MdlGeneratedSourceCache sourceCache; - const auto expectCompiledTint = [&]( const otk::pbrt::PbrtMaterial& material, const BoundMdlColor& expected ) { - const demandPbrtScene::MdlShaderKey key{ demandPbrtScene::makeMdlShaderKey( material ) }; - const demandPbrtScene::GeneratedMdlSource& generated{ sourceCache.getSource( material ) }; - const std::vector parameters{ - demandPbrtScene::makeMdlBoundMaterialParameters( material ) }; - mi::base::Handle compiledMaterial( compileGeneratedMaterialWithBoundParameters( - session.neuray(), transaction.get(), context.get(), generated, key, parameters ) ); - ASSERT_TRUE( compiledMaterial.is_valid_interface() ); - expectTintMatchesColor( compiledMaterial.get(), expected ); - }; - - expectCompiledTint( mirrorMaterial(), BoundMdlColor{ 0.2f, 0.3f, 0.4f } ); - - EXPECT_EQ( 0, transaction->commit() ); - } + expectTintMatchesColor( compiledMaterial.get(), expected ); + }; - EXPECT_EQ( 0, session.shutdown() ); + expectCompiledTint( mirrorMaterial(), BoundMdlColor{ 0.2f, 0.3f, 0.4f } ); } diff --git a/examples/DemandLoading/DemandPbrtScene/tests/include/DemandPbrtScene/Testing/FourierBsdfTableWriter.h b/examples/DemandLoading/DemandPbrtScene/tests/include/DemandPbrtScene/Testing/FourierBsdfTableWriter.h new file mode 100644 index 00000000..4a2f6781 --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/tests/include/DemandPbrtScene/Testing/FourierBsdfTableWriter.h @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace demandPbrtScene { +namespace testing { + +class FourierBsdfTableWriter +{ + public: + explicit FourierBsdfTableWriter( const std::filesystem::path& fileName ) + : m_output{ fileName, std::ios::binary } + { + constexpr char header[8] = { 'S', 'C', 'A', 'T', 'F', 'U', 'N', '\x01' }; + m_output.write( header, sizeof( header ) ); + } + + void writeInt32( int value ) { writeUint32( static_cast( value ) ); } + + void writeFloat( float value ) + { + std::uint32_t bits{}; + std::memcpy( &bits, &value, sizeof( bits ) ); + writeUint32( bits ); + } + + void writeMetadata( int flags, int nMu, int nCoefficients, int maxOrder, int nChannels, int nBases ) + { + writeInt32( flags ); + writeInt32( nMu ); + writeInt32( nCoefficients ); + writeInt32( maxOrder ); + writeInt32( nChannels ); + writeInt32( nBases ); + writeInt32( 0 ); + writeInt32( 0 ); + writeInt32( 0 ); + writeFloat( 1.0f ); + writeInt32( 0 ); + writeInt32( 0 ); + writeInt32( 0 ); + writeInt32( 0 ); + } + + static void writeMinimalTable( const std::filesystem::path& fileName, + const std::vector& coefficients, + int nChannels, + int coefficientOffset, + int coefficientCount ) + { + FourierBsdfTableWriter output{ fileName }; + output.writeMetadata( 1, 1, static_cast( coefficients.size() ), 1, nChannels, 1 ); + output.writeFloat( 1.0f ); + output.writeFloat( 1.0f ); + output.writeInt32( coefficientOffset ); + output.writeInt32( coefficientCount ); + for( float coefficient : coefficients ) + { + output.writeFloat( coefficient ); + } + } + + static void writeOrderShapeTable( const std::filesystem::path& fileName, int maxOrder ) + { + constexpr int nMu{ 2 }; + constexpr int nChannels{ 3 }; + constexpr int gridSize{ nMu * nMu }; + const int nCoefficients{ gridSize * nChannels * maxOrder }; + + FourierBsdfTableWriter output{ fileName }; + output.writeMetadata( 1, nMu, nCoefficients, maxOrder, nChannels, 1 ); + output.writeFloat( -1.0f ); + output.writeFloat( 1.0f ); + output.writeFloat( 0.0f ); + output.writeFloat( 1.0f ); + output.writeFloat( 0.0f ); + output.writeFloat( 1.0f ); + for( int i = 0; i < gridSize; ++i ) + { + output.writeInt32( i * nChannels * maxOrder ); + output.writeInt32( maxOrder ); + } + for( int i = 0; i < nCoefficients; ++i ) + { + output.writeFloat( i % maxOrder == 0 ? 1.0f : 0.0f ); + } + } + + private: + void writeUint32( std::uint32_t value ) + { + const unsigned char bytes[] = { + static_cast( value & 0xffU ), + static_cast( ( value >> 8 ) & 0xffU ), + static_cast( ( value >> 16 ) & 0xffU ), + static_cast( ( value >> 24 ) & 0xffU ), + }; + m_output.write( reinterpret_cast( bytes ), sizeof( bytes ) ); + } + + std::ofstream m_output; +}; + +} // namespace testing +} // namespace demandPbrtScene diff --git a/examples/PbrtSceneLoader/PbrtApiImpl.cpp b/examples/PbrtSceneLoader/PbrtApiImpl.cpp index 4debbb7f..9bbd843e 100644 --- a/examples/PbrtSceneLoader/PbrtApiImpl.cpp +++ b/examples/PbrtSceneLoader/PbrtApiImpl.cpp @@ -248,7 +248,7 @@ void PbrtApiImpl::film( const std::string& type, const ParamSet& params ) unsupportedParams.EraseInt( "yresolution" ); if( !unsupportedParams.ToString().empty() ) { - PBRT_WARNING( "Film 'image' parameters other than xresolution and yresolution are not implemented." ); + PBRT_WARNING( "Film 'image' parameters are ignored; resolution is controlled by the window." ); } } else @@ -735,18 +735,15 @@ PbrtMaterialGraph PbrtApiImpl::getShapePbrtMaterialGraph( const PbrtMaterial& ma { collectNamedMaterialGraph( material.namedMaterialName, graph, materialStack, textureStack ); } - collectMaterialGraphReferences( material.type, material.params, graph, materialStack, textureStack ); + collectMaterialGraphReferences( material.params, graph, materialStack, textureStack ); return graph; } -void PbrtApiImpl::collectMaterialGraphReferences( const std::string& type, - const ::pbrt::ParamSet& params, - PbrtMaterialGraph& graph, +void PbrtApiImpl::collectMaterialGraphReferences( const ::pbrt::ParamSet& params, + PbrtMaterialGraph& graph, std::vector& materialStack, std::vector& textureStack ) const { - static_cast( type ); - for( const char* paramName : MATERIAL_TEXTURE_PARAMS ) { const std::string textureName{ params.FindTexture( paramName ) }; @@ -793,7 +790,7 @@ void PbrtApiImpl::collectNamedMaterialGraph( const std::string& name, } materialStack.push_back( name ); - collectMaterialGraphReferences( type, it->second.params, graph, materialStack, textureStack ); + collectMaterialGraphReferences( it->second.params, graph, materialStack, textureStack ); materialStack.pop_back(); } diff --git a/examples/PbrtSceneLoader/PbrtApiImpl.h b/examples/PbrtSceneLoader/PbrtApiImpl.h index 437a9c45..7d2e7a48 100644 --- a/examples/PbrtSceneLoader/PbrtApiImpl.h +++ b/examples/PbrtSceneLoader/PbrtApiImpl.h @@ -124,9 +124,10 @@ class PbrtApiImpl : public Api PlasticMaterial getShapeMaterial( const ::pbrt::ParamSet& params ) const; PbrtMaterial getShapePbrtMaterial() const; PbrtMaterialGraph getShapePbrtMaterialGraph( const PbrtMaterial& material ) const; - void collectMaterialGraphReferences( const std::string& type, const ::pbrt::ParamSet& params, - PbrtMaterialGraph& graph, std::vector& materialStack, - std::vector& textureStack ) const; + void collectMaterialGraphReferences( const ::pbrt::ParamSet& params, + PbrtMaterialGraph& graph, + std::vector& materialStack, + std::vector& textureStack ) const; void collectNamedMaterialGraph( const std::string& name, PbrtMaterialGraph& graph, std::vector& materialStack, std::vector& textureStack ) const;