From 1d7c02570adc2a55eb4d7de1101135c9bd5bb14d Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 10:55:02 -0600 Subject: [PATCH 01/21] Remove unused material graph type parameter Drop the unused material type parameter from material graph reference collection. Update direct and named-material callers to use the simplified interface. --- examples/PbrtSceneLoader/PbrtApiImpl.cpp | 11 ++++------- examples/PbrtSceneLoader/PbrtApiImpl.h | 7 ++++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/PbrtSceneLoader/PbrtApiImpl.cpp b/examples/PbrtSceneLoader/PbrtApiImpl.cpp index 4debbb7f..2faf2cbb 100644 --- a/examples/PbrtSceneLoader/PbrtApiImpl.cpp +++ b/examples/PbrtSceneLoader/PbrtApiImpl.cpp @@ -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; From 68481fbc50decae54c69a8618d039ecac9e3825d Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 11:09:33 -0600 Subject: [PATCH 02/21] Compose Method on MDL closest-hit shading from named steps Extract hit loading, payload initialization, texture-coordinate setup, tinting, bump mapping, BSDF shading, and fallback handling into focused device helpers. Keep the closest-hit program as a concise description of the shading pipeline. --- .../DemandPbrtScene/MdlSmokeMaterial.cu | 361 +++++++++++------- 1 file changed, 224 insertions(+), 137 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu b/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu index 8bd42198..9ddfb4a1 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu +++ b/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu @@ -847,185 +847,272 @@ __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; } - if( !hasAllocatedDiffuseMap( material ) ) + 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; } - setMdlMaterialDiffuseTexturePayload( params, prd, material, shadingNormal, vertices, instanceId, rayT ); + setMdlDirectColor( prd, material, shading.state.normal, hit.rayDirection ); + if( shadeWithMdl( params, shader, material, hit, textureSamples, shading, prd ) ) + { + return; + } + + if( hasAllocatedDiffuseMap( material ) ) + { + setMdlMaterialDiffuseTexturePayload( params, &prd, material, shading.state.normal, hit.vertices, hit.instanceId, + hit.rayT ); + } } } // namespace demandPbrtScene From 522d56693ad1f43ea46ab13b7d6bfc7f282c5317 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 11:27:02 -0600 Subject: [PATCH 03/21] Compose Method on MDL target code compilation from named steps Extract context creation, material compilation, backend setup, tint translation, result capture, and BSDF generation into focused helpers. Manage session and transaction lifetimes with checked RAII, and pass aliased MDL handles by const reference to keep the orchestration concise. --- .../DemandPbrtScene/ProgramGroups.cpp | 202 ++++++++++++------ 1 file changed, 138 insertions(+), 64 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp index b217c566..7a79d21a 100644 --- a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp +++ b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp @@ -464,6 +464,13 @@ void unloadMdlSdkLibrary( MdlLibraryHandle handle ) #endif +using NeurayHandle = mi::base::Handle; +using TransactionHandle = mi::base::Handle; +using ExecutionContextHandle = mi::base::Handle; +using CompiledMaterialHandle = mi::base::Handle; +using BackendHandle = mi::base::Handle; +using TargetCodeHandle = mi::base::Handle; + class MdlSdkSession { public: @@ -509,8 +516,11 @@ class MdlSdkSession const std::string& error() const { return m_error; } - mi::neuraylib::INeuray* neuray() const { return m_neuray.get(); } + const NeurayHandle& neuray() const { return m_neuray; } + + void close() { requireMdl( shutdown() == 0, "Failed to shut down MDL SDK" ); } + private: mi::Sint32 shutdown() { mi::Sint32 result = 0; @@ -523,11 +533,50 @@ class MdlSdkSession return result; } + MdlLibraryHandle m_library{}; + NeurayHandle m_neuray; + std::string m_error; + bool m_started{ false }; +}; + +class MdlTransaction +{ + public: + explicit MdlTransaction( const NeurayHandle& neuray ) + : m_database( neuray->get_api_component() ) + { + 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" ); + } + + MdlTransaction( const MdlTransaction& ) = delete; + MdlTransaction& operator=( const MdlTransaction& ) = delete; + + ~MdlTransaction() + { + if( m_transaction.is_valid_interface() && m_transaction->is_open() ) + { + m_transaction->abort(); + } + } + + const TransactionHandle& handle() const { return m_transaction; } + + void commit() + { + 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 }; + mi::base::Handle m_database; + mi::base::Handle m_scope; + TransactionHandle m_transaction; }; otk::pbrt::PbrtMaterial makeSyntheticMatteMaterial( const float3& kd ) @@ -693,79 +742,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() ); + mi::base::Handle mdlFactory( neuray->get_api_component() ); + requireMdl( mdlFactory.is_valid_interface(), "Failed to get MDL factory" ); + + ExecutionContextHandle context( mdlFactory->create_execution_context() ); + requireMdl( context.is_valid_interface(), "Failed to create MDL execution context" ); + return context; +} - mi::base::Handle database( session.neuray()->get_api_component() ); - requireMdl( database.is_valid_interface(), "Failed to get MDL database" ); +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 scope( database->get_global_scope() ); - requireMdl( scope.is_valid_interface(), "Failed to get MDL global scope" ); +BackendHandle createMdlPtxBackend( const NeurayHandle& neuray ) +{ + mi::base::Handle backendApi( + neuray->get_api_component() ); + requireMdl( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); - mi::base::Handle transaction( scope->create_transaction() ); - requireMdl( transaction.is_valid_interface(), "Failed to create MDL transaction" ); + 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; +} - MdlMaterialTargetCode targetCode; - { - mi::base::Handle mdlFactory( session.neuray()->get_api_component() ); - requireMdl( mdlFactory.is_valid_interface(), "Failed to get MDL factory" ); +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; +} - mi::base::Handle context( mdlFactory->create_execution_context() ); - requireMdl( context.is_valid_interface(), "Failed to create MDL execution context" ); +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; +} - 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() ) }; +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 backendApi( - session.neuray()->get_api_component() ); - requireMdl( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); +MdlMaterialTargetCode compileMdlMaterialTargetCode( const MaterialGroup& group, bool includeBsdfCallables ) +{ + MdlSdkSession session; + requireMdl( session.isStarted(), session.error() ); + const NeurayHandle& neuray{ session.neuray() }; + MdlTransaction transaction{ neuray }; + const TransactionHandle& transactionHandle{ transaction.handle() }; - 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 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 From e1260b479fa2920be0a57dbd32487f6798c12dfc Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 11:39:07 -0600 Subject: [PATCH 04/21] Extract MDL SDK session to eliminate duplication Extract platform library loading, factory lookup, SDK startup, shutdown, and error reporting into a production MdlSdkSession component. Use it from ProgramGroups and the MDL SDK tests to replace parallel implementations. --- .../DemandPbrtScene/CMakeLists.txt | 2 + .../DemandPbrtScene/MdlSdkSession.cpp | 178 ++++++++++++++++++ .../DemandPbrtScene/ProgramGroups.cpp | 148 +-------------- .../include/DemandPbrtScene/MdlSdkSession.h | 40 ++++ .../DemandPbrtScene/tests/TestMdlSdk.cpp | 145 +------------- 5 files changed, 225 insertions(+), 288 deletions(-) create mode 100644 examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h diff --git a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt index 0467d350..e084ddf7 100644 --- a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt @@ -35,6 +35,7 @@ if(OTK_USE_MDL) FourierBsdfTable.cpp FourierBsdfTableResource.cpp MdlBsdfCompiler.cpp + MdlSdkSession.cpp MdlShaderCache.cpp include/DemandPbrtScene/FourierBsdfEval.h include/DemandPbrtScene/FourierBsdfTable.h @@ -42,6 +43,7 @@ if(OTK_USE_MDL) include/DemandPbrtScene/FourierMdlMeasuredBsdfCapability.h include/DemandPbrtScene/MdlBumpMap.h include/DemandPbrtScene/MdlBsdfCompiler.h + include/DemandPbrtScene/MdlSdkSession.h include/DemandPbrtScene/MdlShaderCache.h include/DemandPbrtScene/MdlShaderCompileCacheStatistics.h ) diff --git a/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp b/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp new file mode 100644 index 00000000..3af67c0c --- /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() ) + { + mi::base::Handle 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/ProgramGroups.cpp b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp index 7a79d21a..3fbf34a1 100644 --- a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp +++ b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp @@ -12,6 +12,7 @@ #include "DemandPbrtScene/FourierBsdfTableResource.h" #include "DemandPbrtScene/MaterialAdapters.h" #include "DemandPbrtScene/MdlBsdfCompiler.h" +#include "DemandPbrtScene/MdlSdkSession.h" #include "DemandPbrtScene/MdlShaderCache.h" #endif #include "DemandPbrtScene/Options.h" @@ -75,12 +76,6 @@ #ifdef OTK_USE_MDL #include - -#ifdef _WIN32 -#include -#else -#include -#endif #endif #if OPTIX_VERSION < 70700 @@ -394,151 +389,12 @@ 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 - -using NeurayHandle = mi::base::Handle; using TransactionHandle = mi::base::Handle; using ExecutionContextHandle = mi::base::Handle; using CompiledMaterialHandle = mi::base::Handle; using BackendHandle = mi::base::Handle; using TargetCodeHandle = mi::base::Handle; -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; } - - const NeurayHandle& neuray() const { return m_neuray; } - - void close() { requireMdl( shutdown() == 0, "Failed to shut down MDL SDK" ); } - - private: - mi::Sint32 shutdown() - { - mi::Sint32 result = 0; - if( m_started ) - { - result = m_neuray->shutdown( true ); - m_started = false; - } - m_neuray.reset(); - return result; - } - - MdlLibraryHandle m_library{}; - NeurayHandle m_neuray; - std::string m_error; - bool m_started{ false }; -}; - class MdlTransaction { public: @@ -819,7 +675,7 @@ MdlMaterialTargetCode compileMdlMaterialTargetCode( const MaterialGroup& group, { MdlSdkSession session; requireMdl( session.isStarted(), session.error() ); - const NeurayHandle& neuray{ session.neuray() }; + const NeurayHandle& neuray{ session.handle() }; MdlTransaction transaction{ neuray }; const TransactionHandle& transactionHandle{ transaction.handle() }; diff --git a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h new file mode 100644 index 00000000..7baf411d --- /dev/null +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// + +#pragma once + +#include + +#include +#include + +namespace demandPbrtScene { + +using NeurayHandle = mi::base::Handle; + +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/tests/TestMdlSdk.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp index 0848ab66..54da7f67 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp @@ -7,16 +7,11 @@ #include "DemandPbrtScene/FourierBsdfTable.h" #include "DemandPbrtScene/FourierMdlMeasuredBsdfCapability.h" #include "DemandPbrtScene/MdlBsdfCompiler.h" +#include "DemandPbrtScene/MdlSdkSession.h" #include "DemandPbrtScene/MdlShaderCache.h" #include -#ifdef _WIN32 -#include -#else -#include -#endif - #include #include #include @@ -27,6 +22,8 @@ namespace { +using demandPbrtScene::MdlSdkSession; + constexpr mi::Float32 PBRT_KD_RED = 0.25f; constexpr mi::Float32 PBRT_KD_GREEN = 0.50f; constexpr mi::Float32 PBRT_KD_BLUE = 0.75f; @@ -74,142 +71,6 @@ std::string describeContextMessages( const mi::neuraylib::IMdl_execution_context 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] }; From 3abbd3146cc072136b298c3b20102b71c3775dc6 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 12:30:03 -0600 Subject: [PATCH 05/21] Extract common MDL signature graph traversal Replace parallel source-key and instance-key recursion with one templated material and texture graph traversal. Parameterize node formatting with stateless source and instance emitters while preserving each key's fields. --- .../DemandPbrtScene/MdlShaderCache.cpp | 287 ++++++++---------- 1 file changed, 127 insertions(+), 160 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp index 3da9e39d..6a8fe375 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp @@ -77,153 +77,89 @@ void appendUnsupportedReason( GeneratedMdlSource& result, const std::string& rea } } -void appendTextureSignature( std::ostringstream& out, - const std::string& graphKey, - const otk::pbrt::PbrtTexture& texture, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ); +std::string paramSetToString( const ::pbrt::ParamSet& params ) +{ + ::pbrt::ParamSet copy{ params }; + return copy.ToString(); +} -void appendTextureReference( std::ostringstream& out, - const std::string& paramName, - const std::string& textureName, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) +struct SourceSignatureEmitter { - out << "|texture-ref(" << paramName << ")="; - bool found{ false }; - for( otk::pbrt::PbrtTextureMap::const_iterator it = graph.textures.begin(); it != graph.textures.end(); ++it ) + void appendTextureReference( std::ostringstream& out, const std::string& paramName, const std::string& ) const { - if( it->second.name == textureName ) - { - if( found ) - out << ","; - appendTextureSignature( out, it->first, it->second, graph, textureStack ); - found = true; - } + out << "|texture-ref(" << paramName << ")="; } - if( !found ) + + void appendMaterialReference( std::ostringstream& out, const std::string& paramName, const std::string& ) const { - out << "missing"; + out << "|material-ref(" << paramName << ")="; } -} -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 ) + void appendRecursiveTexture( std::ostringstream& out, const std::string&, const otk::pbrt::PbrtTexture& texture ) const { - const std::string textureName{ params.FindTexture( *it ) }; - if( !textureName.empty() ) - { - appendTextureReference( out, *it, textureName, graph, textureStack ); - } + out << "texture(" << texture.valueType << ":" << texture.type << ";recursive)"; } -} -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 ) ) + void appendTexture( std::ostringstream& out, const std::string&, const otk::pbrt::PbrtTexture& texture ) const { - out << "recursive"; - return; + out << "texture(" << texture.valueType << ":" << texture.type; } - const otk::pbrt::PbrtNamedMaterialMap::const_iterator material = graph.namedMaterials.find( materialName ); - if( material == graph.namedMaterials.end() ) + void appendMaterial( std::ostringstream& out, const std::string& type, const ::pbrt::ParamSet& ) const { - out << "missing"; - return; + out << "material(" << type; } +}; - 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 ) +struct InstanceSignatureEmitter { - const std::vector& paramNames{ namedMaterialParamNames() }; - for( std::vector::const_iterator it = paramNames.begin(); it != paramNames.end(); ++it ) + void appendTextureReference( std::ostringstream& out, const std::string& paramName, const std::string& textureName ) const { - const std::string materialName{ params.FindOneString( *it, std::string{} ) }; - if( !materialName.empty() ) - { - appendMaterialReference( out, *it, materialName, graph, materialStack, textureStack ); - } + out << "|texture-ref(" << paramName << ")=" << textureName << ':'; } -} -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 ) ) + void appendMaterialReference( std::ostringstream& out, const std::string& paramName, const std::string& materialName ) const { - out << "texture(" << texture.valueType << ":" << texture.type << ";recursive)"; - return; + out << "|material-ref(" << paramName << ")=" << materialName << ':'; } - textureStack.push_back( graphKey ); - out << "texture(" << texture.valueType << ":" << texture.type; - appendTextureReferences( out, texture.params, textureTextureParamNames(), graph, textureStack ); - out << ")"; - textureStack.pop_back(); -} + 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 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 << ")"; -} + 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 ); + } -std::string paramSetToString( const ::pbrt::ParamSet& params ) -{ - ::pbrt::ParamSet copy{ params }; - return copy.ToString(); -} + void appendMaterial( std::ostringstream& out, const std::string& type, const ::pbrt::ParamSet& params ) const + { + out << "material(" << type << "|params=" << paramSetToString( params ); + } +}; -void appendTextureInstanceSignature( std::ostringstream& out, - const std::string& graphKey, - const otk::pbrt::PbrtTexture& texture, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ); +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 ); -void appendTextureInstanceReference( std::ostringstream& out, - const std::string& paramName, - const std::string& textureName, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) +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 ) { - out << "|texture-ref(" << paramName << ")=" << textureName << ':'; + emitter.appendTextureReference( out, paramName, textureName ); bool found{ false }; for( otk::pbrt::PbrtTextureMap::const_iterator it = graph.textures.begin(); it != graph.textures.end(); ++it ) { @@ -231,7 +167,7 @@ void appendTextureInstanceReference( std::ostringstream& out, { if( found ) out << ","; - appendTextureInstanceSignature( out, it->first, it->second, graph, textureStack ); + appendTextureGraphSignature( out, it->first, it->second, graph, textureStack, emitter ); found = true; } } @@ -241,37 +177,43 @@ void appendTextureInstanceReference( std::ostringstream& out, } } -void appendTextureInstanceReferences( std::ostringstream& out, - const ::pbrt::ParamSet& params, - const std::vector& paramNames, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) +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() ) { - appendTextureInstanceReference( out, *it, textureName, graph, textureStack ); + appendTextureReference( out, *it, textureName, graph, textureStack, emitter ); } } } -void appendMaterialInstanceSignature( std::ostringstream& out, - const std::string& type, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ); +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 ); -void appendMaterialInstanceReference( std::ostringstream& out, - const std::string& paramName, - const std::string& materialName, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) +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 ) { - out << "|material-ref(" << paramName << ")=" << materialName << ':'; + emitter.appendMaterialReference( out, paramName, materialName ); if( contains( materialStack, materialName ) ) { out << "recursive"; @@ -286,15 +228,18 @@ void appendMaterialInstanceReference( std::ostringstream& out, } materialStack.push_back( materialName ); - appendMaterialInstanceSignature( out, material->second.type, material->second.params, graph, materialStack, textureStack ); + appendMaterialGraphSignature( out, material->second.type, material->second.params, graph, materialStack, textureStack, + emitter ); materialStack.pop_back(); } -void appendMaterialInstanceReferences( std::ostringstream& out, - const ::pbrt::ParamSet& params, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& materialStack, - std::vector& textureStack ) +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 ) @@ -302,32 +247,57 @@ void appendMaterialInstanceReferences( std::ostringstream& out, const std::string materialName{ params.FindOneString( *it, std::string{} ) }; if( !materialName.empty() ) { - appendMaterialInstanceReference( out, *it, materialName, graph, materialStack, textureStack ); + appendMaterialReference( out, *it, materialName, graph, materialStack, textureStack, emitter ); } } } -void appendTextureInstanceSignature( std::ostringstream& out, - const std::string& graphKey, - const otk::pbrt::PbrtTexture& texture, - const otk::pbrt::PbrtMaterialGraph& graph, - std::vector& textureStack ) +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 ) ) { - out << "texture(key=" << graphKey << ",name=" << texture.name << ",kind=" << texture.valueType << ':' - << texture.type << ";recursive)"; + emitter.appendRecursiveTexture( out, graphKey, texture ); 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 ); + 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, @@ -335,10 +305,7 @@ void appendMaterialInstanceSignature( std::ostringstream& out, 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 << ")"; + appendMaterialGraphSignature( out, type, params, graph, materialStack, textureStack, InstanceSignatureEmitter{} ); } std::string stableHash( const std::string& text ) From 830f92e269d676db46814e9cd0e2d6fb895f99ca Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 13:17:20 -0600 Subject: [PATCH 06/21] Extract MDL SDK test fixture Extract the repeated SDK session, database, scope, transaction, factory, and execution-context lifecycle into a fixture. Add a fixture helper for compiling generated materials and convert SDK-dependent tests to use it. Introduce aliases for MDL handle types, remove redundant scopes, and organize each test into setup, execute, verify, and teardown phases. Keep header-only checks in a separate suite and drop assertions that exercised only the test-only source-description formatter. --- .../DemandPbrtScene/tests/TestMdlSdk.cpp | 1737 +++++++---------- 1 file changed, 696 insertions(+), 1041 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp index 54da7f67..37225a7a 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp @@ -24,6 +24,35 @@ namespace { using demandPbrtScene::MdlSdkSession; +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 ScopeHandle = mi::base::Handle; +using TargetCodeHandle = mi::base::Handle; +using TransactionHandle = mi::base::Handle; +using TypeFactoryHandle = mi::base::Handle; +using TypeHandle = mi::base::Handle; +using ValueFactoryHandle = mi::base::Handle; + constexpr mi::Float32 PBRT_KD_RED = 0.25f; constexpr mi::Float32 PBRT_KD_GREEN = 0.50f; constexpr mi::Float32 PBRT_KD_BLUE = 0.75f; @@ -64,7 +93,7 @@ std::string describeContextMessages( const mi::neuraylib::IMdl_execution_context std::ostringstream out; for( mi::Size i = 0; i < context->get_messages_count(); ++i ) { - mi::base::Handle message( context->get_message( i ) ); + MessageHandle message( context->get_message( i ) ); if( message.is_valid_interface() ) out << message->get_string() << '\n'; } @@ -469,7 +498,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, @@ -478,22 +507,22 @@ 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(); @@ -515,25 +544,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 {}; @@ -542,13 +571,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 {}; @@ -558,12 +587,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 {}; @@ -573,18 +602,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() ) @@ -596,7 +625,7 @@ mi::base::Handle compileGeneratedMaterialWith 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 ); @@ -607,20 +636,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() ); @@ -642,15 +671,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 ); @@ -666,7 +695,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; @@ -681,12 +710,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 {}; @@ -697,7 +726,7 @@ 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 ); if( !targetCode.is_valid_interface() ) @@ -715,18 +744,18 @@ 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 ); if( !targetCode.is_valid_interface() ) @@ -739,123 +768,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 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" ) ); + const auto resetResult = measurement->reset_file( fixture.string().c_str() ); + const demandPbrtScene::FourierMdlMeasuredBsdfCapability capability{ demandPbrtScene::fourierMdlMeasuredBsdfCapability() }; - measurement.reset(); - EXPECT_EQ( 0, transaction->commit() ); - } + 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" ) ); - 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 }; @@ -864,103 +903,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() ); - - 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; + 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() ) }; - 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 ) }; @@ -968,138 +957,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 }; @@ -1111,86 +1044,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 }; @@ -1214,14 +1117,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" ) ); @@ -1232,114 +1176,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 }; @@ -1351,87 +1217,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 ) }; @@ -1439,93 +1275,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 }; @@ -1537,90 +1342,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 }; @@ -1643,13 +1417,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" ) ); @@ -1660,104 +1471,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 }, @@ -1765,65 +1504,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 }; @@ -1841,14 +1554,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" ) ); @@ -1856,148 +1614,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 } ); } From a26f3033d83b8965ca332eb022c7394565584270 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 13:29:39 -0600 Subject: [PATCH 07/21] Extract MDL handle type aliases Add MdlHandleTypes.h as the single declaration site for all 32 MDL SDK handle wrappers used by DemandPbrtScene. Move NeurayHandle from MdlSdkSession and replace local aliases and raw Handle specializations in production code and tests with the shared names. Register the header with the MDL source set. --- .../DemandPbrtScene/CMakeLists.txt | 1 + .../DemandPbrtScene/MdlBsdfCompiler.cpp | 14 ++--- .../DemandPbrtScene/MdlSdkSession.cpp | 2 +- .../DemandPbrtScene/ProgramGroups.cpp | 59 +++++++++---------- .../include/DemandPbrtScene/MdlHandleTypes.h | 44 ++++++++++++++ .../include/DemandPbrtScene/MdlSdkSession.h | 4 +- .../DemandPbrtScene/tests/TestMdlSdk.cpp | 57 +++++++++--------- 7 files changed, 110 insertions(+), 71 deletions(-) create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlHandleTypes.h diff --git a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt index e084ddf7..e4db8a0a 100644 --- a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt @@ -43,6 +43,7 @@ if(OTK_USE_MDL) include/DemandPbrtScene/FourierMdlMeasuredBsdfCapability.h include/DemandPbrtScene/MdlBumpMap.h include/DemandPbrtScene/MdlBsdfCompiler.h + include/DemandPbrtScene/MdlHandleTypes.h include/DemandPbrtScene/MdlSdkSession.h include/DemandPbrtScene/MdlShaderCache.h include/DemandPbrtScene/MdlShaderCompileCacheStatistics.h diff --git a/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp b/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp index 3aec3f3f..b0ad9f1e 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp @@ -6,7 +6,7 @@ #ifdef OTK_USE_MDL -#include +#include "DemandPbrtScene/MdlHandleTypes.h" #include #include @@ -22,7 +22,7 @@ std::string describeContextMessages( const mi::neuraylib::IMdl_execution_context std::ostringstream out; for( mi::Size i = 0; i < context->get_messages_count(); ++i ) { - mi::base::Handle message( context->get_message( i ) ); + MessageHandle message( context->get_message( i ) ); if( message.is_valid_interface() ) out << message->get_string() << '\n'; } @@ -90,10 +90,10 @@ MdlTargetArgumentBlock captureTargetArgumentBlock( const mi::neuraylib::ITarget_ return MdlTargetArgumentBlock{}; } - mi::base::Handle argumentBlock( targetCode->get_argument_block( argumentBlockIndex ) ); + TargetArgumentBlockHandle 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 ) ); + TargetValueLayoutHandle 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; @@ -144,10 +144,10 @@ MdlBsdfCallablePtx compileMdlBsdfCallablesToPtx( mi::neuraylib::INeuray* "Cannot compile MDL BSDF callables without a base function " "name" ); - mi::base::Handle backendApi( neuray->get_api_component() ); + BackendApiHandle 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 ) ); + BackendHandle 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" ); @@ -158,7 +158,7 @@ MdlBsdfCallablePtx compileMdlBsdfCallablesToPtx( mi::neuraylib::INeuray* "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" ); diff --git a/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp b/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp index 3af67c0c..425290c9 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlSdkSession.cpp @@ -110,7 +110,7 @@ MdlSdkSession::MdlSdkSession() m_impl->neuray = mi::neuraylib::mi_factory( symbol ); if( !m_impl->neuray.is_valid_interface() ) { - mi::base::Handle version( mi::neuraylib::mi_factory( symbol ) ); + 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"; diff --git a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp index 3fbf34a1..e4dc6f88 100644 --- a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp +++ b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp @@ -12,6 +12,7 @@ #include "DemandPbrtScene/FourierBsdfTableResource.h" #include "DemandPbrtScene/MaterialAdapters.h" #include "DemandPbrtScene/MdlBsdfCompiler.h" +#include "DemandPbrtScene/MdlHandleTypes.h" #include "DemandPbrtScene/MdlSdkSession.h" #include "DemandPbrtScene/MdlShaderCache.h" #endif @@ -225,7 +226,7 @@ std::string describeContextMessages( const mi::neuraylib::IMdl_execution_context std::ostringstream out; for( mi::Size i = 0; i < context->get_messages_count(); ++i ) { - mi::base::Handle message( context->get_message( i ) ); + MessageHandle message( context->get_message( i ) ); if( message.is_valid_interface() ) out << message->get_string() << '\n'; } @@ -261,10 +262,10 @@ MdlTargetArgumentBlock captureMdlTargetArgumentBlock( const mi::neuraylib::ITarg return MdlTargetArgumentBlock{}; } - mi::base::Handle argumentBlock( targetCode->get_argument_block( argumentBlockIndex ) ); + TargetArgumentBlockHandle 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 ) ); + 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; @@ -389,12 +390,6 @@ std::string generatedMdlMessage( const std::string& message, const GeneratedMdlS return message + " (" + generatedMdlContext( source, key ) + ")"; } -using TransactionHandle = mi::base::Handle; -using ExecutionContextHandle = mi::base::Handle; -using CompiledMaterialHandle = mi::base::Handle; -using BackendHandle = mi::base::Handle; -using TargetCodeHandle = mi::base::Handle; - class MdlTransaction { public: @@ -430,9 +425,9 @@ class MdlTransaction } private: - mi::base::Handle m_database; - mi::base::Handle m_scope; - TransactionHandle m_transaction; + DatabaseHandle m_database; + ScopeHandle m_scope; + TransactionHandle m_transaction; }; otk::pbrt::PbrtMaterial makeSyntheticMatteMaterial( const float3& kd ) @@ -455,12 +450,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 ) ); @@ -476,11 +471,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 ) ); @@ -509,7 +504,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, @@ -517,16 +512,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(); @@ -542,34 +537,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 ) ); @@ -577,7 +572,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 ); @@ -589,7 +584,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; @@ -600,7 +595,7 @@ const char* findMdlPreviewColorExpressionPath( const mi::neuraylib::ICompiled_ma ExecutionContextHandle createMdlExecutionContext( const NeurayHandle& neuray ) { - mi::base::Handle mdlFactory( neuray->get_api_component() ); + MdlFactoryHandle mdlFactory( neuray->get_api_component() ); requireMdl( mdlFactory.is_valid_interface(), "Failed to get MDL factory" ); ExecutionContextHandle context( mdlFactory->create_execution_context() ); @@ -626,7 +621,7 @@ CompiledMaterialHandle compileMdlGroupMaterial( const NeurayHandle& neu BackendHandle createMdlPtxBackend( const NeurayHandle& neuray ) { - mi::base::Handle backendApi( + BackendApiHandle backendApi( neuray->get_api_component() ); requireMdl( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); 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/MdlSdkSession.h b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h index 7baf411d..e1685269 100644 --- a/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h +++ b/examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlSdkSession.h @@ -4,15 +4,13 @@ #pragma once -#include +#include "DemandPbrtScene/MdlHandleTypes.h" #include #include namespace demandPbrtScene { -using NeurayHandle = mi::base::Handle; - class MdlSdkSession { public: diff --git a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp index 37225a7a..02e8f603 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp @@ -7,6 +7,7 @@ #include "DemandPbrtScene/FourierBsdfTable.h" #include "DemandPbrtScene/FourierMdlMeasuredBsdfCapability.h" #include "DemandPbrtScene/MdlBsdfCompiler.h" +#include "DemandPbrtScene/MdlHandleTypes.h" #include "DemandPbrtScene/MdlSdkSession.h" #include "DemandPbrtScene/MdlShaderCache.h" @@ -24,34 +25,34 @@ namespace { using demandPbrtScene::MdlSdkSession; -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 ScopeHandle = mi::base::Handle; -using TargetCodeHandle = mi::base::Handle; -using TransactionHandle = mi::base::Handle; -using TypeFactoryHandle = mi::base::Handle; -using TypeHandle = mi::base::Handle; -using ValueFactoryHandle = mi::base::Handle; +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::MessageHandle; +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; From cfa9eae7342ce5b840f593e411bba2f893f0be55 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 13:42:30 -0600 Subject: [PATCH 08/21] Compose Method on MDL bound material parameter generation Move material parameter specifications into a table used by both root and named materials. Extract the root and named-material binding steps so makeMdlBoundMaterialParameters reads as their ordered composition. --- .../DemandPbrtScene/MdlShaderCache.cpp | 328 +++++++++--------- 1 file changed, 160 insertions(+), 168 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp index 6a8fe375..47c1579e 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp @@ -1185,6 +1185,164 @@ std::string namedMaterialType( const otk::pbrt::PbrtNamedMaterial& material ) return material.params.FindOneString( "type", std::string{} ); } +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 +{ + const char* type; + BoundParameterSpecs root; + BoundParameterSpecs named; +}; + +constexpr MaterialBoundParameterSpecs materialBoundParameterSpecs[] = { + { "matte", makeBoundParameterSpecs( matteParams ), makeBoundParameterSpecs( matteParams ) }, + { "plastic", makeBoundParameterSpecs( plasticParams ), makeBoundParameterSpecs( plasticParams ) }, + { "uber", makeBoundParameterSpecs( uberParams ), makeBoundParameterSpecs( namedUberParams ) }, + { "mirror", makeBoundParameterSpecs( mirrorParams ), makeBoundParameterSpecs( mirrorParams ) }, + { "glass", makeBoundParameterSpecs( glassParams ), makeBoundParameterSpecs( glassParams ) }, + { "metal", makeBoundParameterSpecs( metalParams ), makeBoundParameterSpecs( metalParams ) }, + { "substrate", makeBoundParameterSpecs( substrateParams ), makeBoundParameterSpecs( substrateParams ) }, + { "translucent", makeBoundParameterSpecs( translucentParams ), makeBoundParameterSpecs( translucentParams ) }, + { "subsurface", makeBoundParameterSpecs( subsurfaceParams ), {} }, + { "kdsubsurface", makeBoundParameterSpecs( kdSubsurfaceParams ), {} }, + { "mix", makeBoundParameterSpecs( mixParams ), {} }, +}; + +BoundParameterSpecs boundParameterSpecs( const std::string& type, BoundMaterialKind kind ) +{ + for( const MaterialBoundParameterSpecs& specs : materialBoundParameterSpecs ) + { + if( type == specs.type ) + { + return kind == BoundMaterialKind::ROOT ? specs.root : specs.named; + } + } + return {}; +} + +void appendRootMaterialBoundParameters( std::vector& result, + const otk::pbrt::PbrtMaterial& material ) +{ + const BoundParameterSpecs specs{ boundParameterSpecs( material.type, 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 std::string type{ namedMaterialType( namedMaterial->second ) }; + const BoundParameterSpecs specs{ boundParameterSpecs( type, BoundMaterialKind::NAMED ) }; + appendNamedBoundParameters( result, namedMaterial->second.params, index, specs.begin, specs.end ); +} + +void appendNamedMaterialBoundParameters( std::vector& result, + const otk::pbrt::PbrtMaterial& material ) +{ + if( material.type != "mix" ) + { + return; + } + + appendNamedMaterialBoundParameters( result, material, "namedmaterial1", 0U ); + appendNamedMaterialBoundParameters( result, material, "namedmaterial2", 1U ); +} + std::string namedMaterialMatteBsdfExpression( MdlMaterialModel& model, MdlTextureGraphGenerator& textureGraph, const otk::pbrt::PbrtNamedMaterial& material, @@ -2304,175 +2462,9 @@ MdlMaterialInstanceKey makeMdlMaterialInstanceKey( const otk::pbrt::PbrtMaterial 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 ); - } + appendRootMaterialBoundParameters( result, material ); + appendNamedMaterialBoundParameters( result, material ); return result; } From 6cbbada926f12b658afdf71594cf7d63290ec536 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 13:53:35 -0600 Subject: [PATCH 09/21] Extract common MDL compilation utilities Centralize execution-context diagnostics, failure handling, and target argument-block capture in MdlUtils. Use the same implementations from material compilation, BSDF compilation, and the MDL SDK tests. --- .../DemandPbrtScene/CMakeLists.txt | 2 + .../DemandPbrtScene/MdlBsdfCompiler.cpp | 130 ++++-------------- .../DemandPbrtScene/MdlUtils.cpp | 101 ++++++++++++++ .../DemandPbrtScene/ProgramGroups.cpp | 81 +---------- .../include/DemandPbrtScene/MdlBsdfCompiler.h | 18 +-- .../include/DemandPbrtScene/MdlUtils.h | 55 ++++++++ .../DemandPbrtScene/tests/TestMdlSdk.cpp | 29 +--- 7 files changed, 191 insertions(+), 225 deletions(-) create mode 100644 examples/DemandLoading/DemandPbrtScene/MdlUtils.cpp create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlUtils.h diff --git a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt index e4db8a0a..e56e54dd 100644 --- a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt @@ -37,6 +37,7 @@ if(OTK_USE_MDL) MdlBsdfCompiler.cpp MdlSdkSession.cpp MdlShaderCache.cpp + MdlUtils.cpp include/DemandPbrtScene/FourierBsdfEval.h include/DemandPbrtScene/FourierBsdfTable.h include/DemandPbrtScene/FourierBsdfTableResource.h @@ -47,6 +48,7 @@ if(OTK_USE_MDL) include/DemandPbrtScene/MdlSdkSession.h include/DemandPbrtScene/MdlShaderCache.h include/DemandPbrtScene/MdlShaderCompileCacheStatistics.h + include/DemandPbrtScene/MdlUtils.h ) endif() diff --git a/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp b/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp index b0ad9f1e..7f586b37 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlBsdfCompiler.cpp @@ -8,41 +8,11 @@ #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 ) - { - MessageHandle 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{}; - } - - TargetArgumentBlockHandle argumentBlock( targetCode->get_argument_block( argumentBlockIndex ) ); - requireMdlBsdfCompile( argumentBlock.is_valid_interface(), "MDL target code did not expose an argument block", context ); - - TargetValueLayoutHandle 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" ); + 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() ); - requireMdlBsdfCompile( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); + requireMdl( backendApi.is_valid_interface(), "Failed to get MDL backend API" ); BackendHandle 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( 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(); 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/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/ProgramGroups.cpp b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp index e4dc6f88..1aa90caf 100644 --- a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp +++ b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp @@ -15,6 +15,7 @@ #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" @@ -66,7 +67,6 @@ #include #include #endif -#include #include #include #ifdef OTK_USE_MDL @@ -218,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 ) - { - 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 = 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{}; - } - - 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; -} - bool isPtxIdentifierChar( char value ) { const unsigned char ch{ static_cast( value ) }; 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/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/tests/TestMdlSdk.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp index 02e8f603..1f97c231 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestMdlSdk.cpp @@ -10,13 +10,13 @@ #include "DemandPbrtScene/MdlHandleTypes.h" #include "DemandPbrtScene/MdlSdkSession.h" #include "DemandPbrtScene/MdlShaderCache.h" +#include "DemandPbrtScene/MdlUtils.h" #include #include #include #include -#include #include #include #include @@ -24,6 +24,7 @@ namespace { using demandPbrtScene::MdlSdkSession; +using demandPbrtScene::describeMdlContextMessages; using demandPbrtScene::BackendApiHandle; using demandPbrtScene::BackendHandle; @@ -45,7 +46,6 @@ using demandPbrtScene::FunctionDefinitionHandle; using demandPbrtScene::MaterialInstanceHandle; using demandPbrtScene::MdlFactoryHandle; using demandPbrtScene::MdlImpexpApiHandle; -using demandPbrtScene::MessageHandle; using demandPbrtScene::ModuleHandle; using demandPbrtScene::ScopeHandle; using demandPbrtScene::TargetCodeHandle; @@ -86,21 +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 ) - { - MessageHandle message( context->get_message( i ) ); - if( message.is_valid_interface() ) - out << message->get_string() << '\n'; - } - return out.str(); -} - 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] }; @@ -529,7 +514,7 @@ CompiledMaterialHandle compileGeneratedMaterialWithBoundParameters( 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 {}; @@ -622,14 +607,14 @@ CompiledMaterialHandle compileGeneratedMaterialWithBoundParameters( 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 {}; 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; } @@ -729,7 +714,7 @@ std::string translateTintExpressionToPtx( mi::neuraylib::INeuray* } 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 ); @@ -758,7 +743,7 @@ std::string translateNormalExpressionToPtx( mi::neuraylib::INeuray* context->clear_messages(); 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 ); From c169cdfffdc44fe8736ebfadbd5c91f614ad986d Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 14:07:51 -0600 Subject: [PATCH 10/21] Use field initializers for MDL shader state Define shader and texture-binding defaults at their declarations. Delegate specialized constructors from the base callable constructor, eliminating repeated member initialization and texture-binding cleanup. --- .../include/DemandPbrtScene/Params.h | 116 +++++------------- 1 file changed, 29 insertions(+), 87 deletions(-) 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 ); } }; From 5440a9f0ce1ca63bee661a8a9472235ea7f3fe10 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 14:21:55 -0600 Subject: [PATCH 11/21] Centralize generated MDL texture policy Describe generated MDL texture parameters in one table used by support detection and runtime binding. Compose support from map discovery, foldability, runtime binding, exceptions, and flag validation so the two paths cannot drift. --- .../DemandPbrtScene/MaterialResolver.cpp | 391 ++++++++++-------- 1 file changed, 216 insertions(+), 175 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp b/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp index 0766f36b..9ae50d09 100644 --- a/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp @@ -208,6 +208,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,19 +319,11 @@ 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 ) +bool hasGeneratedMdlFoldableTextureParameter( const otk::pbrt::PbrtMaterial& material, + const MdlTexturePolicy& policy ) { - return paramName == "bumpmap" || paramName == "roughness" || paramName == "uroughness" || paramName == "vroughness"; + return hasGeneratedMdlFoldableTextureParameter( material, policy.name, MdlBoundParameterType::COLOR ) + || hasGeneratedMdlFoldableTextureParameter( material, policy.name, MdlBoundParameterType::FLOAT ); } bool usesGeneratedMdlNamedMaterialKd( const std::string& type ) @@ -316,74 +387,76 @@ bool isDirectGeneratedMdlDemandTexture( const PbrtDemandTextureBinding& binding PbrtDemandTextureBinding generatedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const std::string& paramName ) + 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 && usesGeneratedMdlNamedMaterialKd( type ) ) { return binding; } - if( paramName == "Ks" && usesGeneratedMdlNamedMaterialKs( type ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::SPECULAR && usesGeneratedMdlNamedMaterialKs( type ) + && isDirectGeneratedMdlDemandTexture( binding ) ) { return binding; } - if( paramName == "Kr" && usesGeneratedMdlNamedMaterialKr( type ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::REFLECTION && usesGeneratedMdlNamedMaterialKr( type ) + && isDirectGeneratedMdlDemandTexture( binding ) ) { return binding; } - if( paramName == "bumpmap" || isGeneratedMdlNamedMaterialAlphaTextureParam( paramName ) ) + if( policy.use == MdlTextureUse::BUMP || policy.use == MdlTextureUse::ALPHA ) { return binding; } return pbrtDemandTextureBinding(); } +PbrtDemandTextureBinding generatedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, + const otk::pbrt::PbrtNamedMaterial& namedMaterial, + const std::string& paramName ) +{ + const MdlTexturePolicy* const policy{ mdlTexturePolicy( paramName ) }; + return policy ? generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, *policy ) : + pbrtDemandTextureBinding(); +} + bool hasGeneratedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const std::string& paramName ) + const MdlTexturePolicy& policy ) { - return hasPbrtDemandTextureBinding( generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, paramName ) ); + return hasPbrtDemandTextureBinding( generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, policy ) ); } bool supportsGeneratedMdlNamedMaterialTextureReference( const otk::pbrt::PbrtMaterial& parent, const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const std::string& paramName ) + const MdlTexturePolicy& policy ) { - if( namedMaterial.params.FindTexture( paramName ).empty() ) + if( namedMaterial.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( parent, namedMaterial, policy ); } bool supportsGeneratedMdlNamedMaterialTextureReferences( 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 ) + for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) { - if( !supportsGeneratedMdlNamedMaterialTextureReference( parent, namedMaterial, param ) ) + if( !supportsGeneratedMdlNamedMaterialTextureReference( parent, namedMaterial, policy ) ) { return false; } @@ -423,193 +496,161 @@ 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 otk::pbrt::PbrtMaterial& 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 ) ) + if( policy.use == MdlTextureUse::DIFFUSE ) { - return pbrtDemandTextureBinding(); - } - - if( paramName == "Kd" ) - { - 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.type == "mirror" ) { - return binding; + return flagSet( group.material.flags, MaterialFlags::DIFFUSE_MAP ) + && binding.fileName == group.diffuseMapFileName; } - return pbrtDemandTextureBinding(); + return material.type == "uber" && isDirectGeneratedMdlDemandTexture( binding ); } - if( paramName == "Ks" && usesGeneratedMdlKs( material.type ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::SPECULAR ) { - return binding; + return usesGeneratedMdlKs( material.type ) && isDirectGeneratedMdlDemandTexture( binding ); } - if( material.type == "uber" && paramName == "Kr" && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::TRANSMISSION ) { - return binding; - } - if( paramName == "Kt" && usesGeneratedMdlKt( material.type ) && isDirectGeneratedMdlDemandTexture( binding ) ) - { - return binding; + return usesGeneratedMdlKt( material.type ) && isDirectGeneratedMdlDemandTexture( binding ); } - if( usesGeneratedMdlRoughnessTextureParam( material.type, paramName ) && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::ROUGHNESS ) { - return binding; + return usesGeneratedMdlRoughnessTextureParam( material.type, policy.name ) + && isDirectGeneratedMdlDemandTexture( binding ); } - if( material.type == "mix" && paramName == "amount" && isDirectGeneratedMdlDemandTexture( binding ) ) + if( policy.use == MdlTextureUse::AMOUNT ) { - return binding; + return material.type == "mix" && isDirectGeneratedMdlDemandTexture( binding ); } - if( paramName == "bumpmap" ) + return policy.use == MdlTextureUse::BUMP; +} + +PbrtDemandTextureBinding generatedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const MaterialGroup& group, + const MdlTexturePolicy& policy ) +{ + const PbrtDemandTextureBinding binding{ generatedMdlTextureBinding( material, policy ) }; + if( hasPbrtDemandTextureBinding( binding ) + && acceptsGeneratedMdlRuntimeTextureBinding( material, 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 MaterialGroup& group, + const std::string& paramName ) +{ + const MdlTexturePolicy* const policy{ mdlTexturePolicy( paramName ) }; + return policy ? generatedMdlRuntimeTextureBinding( material, group, *policy ) : pbrtDemandTextureBinding(); +} + +bool hasGeneratedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const MaterialGroup& group, + const MdlTexturePolicy& policy ) { - return hasPbrtDemandTextureBinding( generatedMdlRuntimeTextureBinding( material, group, paramName ) ); + return hasPbrtDemandTextureBinding( generatedMdlRuntimeTextureBinding( material, group, policy ) ); } -bool supportsGeneratedMdlTextureReferences( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group ) +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 otk::pbrt::PbrtMaterial& material, const MdlTexturePolicy& policy ) +{ + return policy.use == MdlTextureUse::DIFFUSE + || ( policy.use == MdlTextureUse::REFLECTION && material.type == "mirror" ); +} - for( const char* const param : textureParams ) +bool supportsGeneratedMdlTextureReference( const otk::pbrt::PbrtMaterial& material, + 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, 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( material, 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, 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 supportsGeneratedMdlTextureReferences( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group ) +{ + const MdlTextureMaps maps{ generatedMdlTextureMaps( group ) }; + for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) + { + if( !supportsGeneratedMdlTextureReference( material, group, policy, maps ) ) + { + return false; + } + } + return supportsGeneratedMdlTextureFlags( group, maps ); +} + bool hasGeneratedMdlUnsupportedTextureReference( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group ) { if( !supportsGeneratedMdlTextureReferences( material, group ) ) From d057c948259427fa6cb16fffce290c72228a4a21 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 14:32:41 -0600 Subject: [PATCH 12/21] Unify PBRT texture graph traversal Extract value and folding traits for color and float texture graphs. Use one implementation for constant folding, direct binding, scale composition, mix composition, and recursive dispatch. Replace the Boolean mix flag with an explicit traversal policy. --- .../DemandPbrtScene/MaterialAdapters.h | 450 +++++++----------- 1 file changed, 160 insertions(+), 290 deletions(-) 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 ) From 295ab08c97077948ff79f579ea050a4bcfedf884 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 14:49:28 -0600 Subject: [PATCH 13/21] Shorten named-material texture helper names Rename file-local named-material runtime binding and texture support helpers to concise names that rely on their narrow module context. Update all local callers. --- .../DemandPbrtScene/MaterialResolver.cpp | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp b/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp index 9ae50d09..f10d55fb 100644 --- a/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp @@ -385,9 +385,9 @@ bool isDirectGeneratedMdlDemandTexture( const PbrtDemandTextureBinding& binding return hasPbrtDemandTextureBinding( binding ) && !binding.transformed; } -PbrtDemandTextureBinding generatedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const MdlTexturePolicy& policy ) +PbrtDemandTextureBinding namedRuntimeBinding( const otk::pbrt::PbrtMaterial& parent, + const otk::pbrt::PbrtNamedMaterial& namedMaterial, + const MdlTexturePolicy& policy ) { const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; const PbrtDemandTextureBinding binding{ generatedMdlTextureBinding( material, policy ) }; @@ -418,20 +418,19 @@ PbrtDemandTextureBinding generatedMdlNamedMaterialRuntimeTextureBinding( const o return pbrtDemandTextureBinding(); } -PbrtDemandTextureBinding generatedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const std::string& paramName ) +PbrtDemandTextureBinding namedRuntimeBinding( const otk::pbrt::PbrtMaterial& parent, + const otk::pbrt::PbrtNamedMaterial& namedMaterial, + const std::string& paramName ) { const MdlTexturePolicy* const policy{ mdlTexturePolicy( paramName ) }; - return policy ? generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, *policy ) : - pbrtDemandTextureBinding(); + return policy ? namedRuntimeBinding( parent, namedMaterial, *policy ) : pbrtDemandTextureBinding(); } bool hasGeneratedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, const otk::pbrt::PbrtNamedMaterial& namedMaterial, const MdlTexturePolicy& policy ) { - return hasPbrtDemandTextureBinding( generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, policy ) ); + return hasPbrtDemandTextureBinding( namedRuntimeBinding( parent, namedMaterial, policy ) ); } bool supportsGeneratedMdlNamedMaterialTextureReference( const otk::pbrt::PbrtMaterial& parent, @@ -451,8 +450,7 @@ bool supportsGeneratedMdlNamedMaterialTextureReference( const otk::pbrt::PbrtMat return hasGeneratedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, 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 ) { for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) { @@ -479,7 +477,7 @@ bool supportsGeneratedMdlNamedMaterialReference( const otk::pbrt::PbrtMaterial& } return supportsGeneratedMdlNamedMaterialType( generatedMdlNamedMaterialType( namedMaterial->second ) ) - && supportsGeneratedMdlNamedMaterialTextureReferences( material, namedMaterial->second ); + && supportsNamedTextures( material, namedMaterial->second ); } bool supportsGeneratedMdlNamedMaterialReferences( const otk::pbrt::PbrtMaterial& material ) @@ -760,7 +758,7 @@ PbrtDemandTextureBinding generatedMdlNamedMaterialAlphaCutoutBinding( const otk: { for( const char* const paramName : { "alpha", "shadowalpha", "opacity" } ) { - const PbrtDemandTextureBinding binding{ generatedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, paramName ) }; + const PbrtDemandTextureBinding binding{ namedRuntimeBinding( parent, namedMaterial, paramName ) }; if( hasPbrtDemandTextureBinding( binding ) ) { return binding; @@ -807,7 +805,7 @@ void createGeneratedMdlNamedMaterialTextureBinding( MaterialGroup& uint_t offset ) { createGeneratedMdlTextureBinding( group, sync, demandTextureCache, - generatedMdlNamedMaterialRuntimeTextureBinding( *group.pbrtMaterial, namedMaterial, paramName ), + namedRuntimeBinding( *group.pbrtMaterial, namedMaterial, paramName ), generatedMdlMixNamedMaterialTextureBindingIndex( namedMaterialIndex, offset ) ); } From 03790782eb43a4a0b52752b8020cd0da1c98ba0c Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 15:00:09 -0600 Subject: [PATCH 14/21] Extract repeated mix binding matcher Capture the complete expected mix-material geometry matcher once and reuse it for SBT-offset lookup and shader-realization expectations. This keeps both mock calls aligned as the binding set changes. --- .../tests/TestMaterialResolver.cpp | 63 +++++++------------ 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp index 9eb96371..a2fb06a2 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestMaterialResolver.cpp @@ -1933,48 +1933,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 ); From f520f6ba9e81a572de50429b9571a57ede4a7daf Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 15:25:23 -0600 Subject: [PATCH 15/21] Compose Method on Fourier BSDF table loading Extract header reading, metadata validation, array loading, span validation, and finalization into focused free functions. Combine file opening, header decoding, and metadata reading as one preamble step. Return load status directly from every step. --- .../DemandPbrtScene/FourierBsdfTable.cpp | 110 +++++++++++++++--- 1 file changed, 94 insertions(+), 16 deletions(-) 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 From b9c7ffc2686b6ac1e5c1c725076b1ddad6a3c85c Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 15:44:44 -0600 Subject: [PATCH 16/21] Extract Fourier BSDF table test writer Extract the little-endian Fourier table writer and common minimal and order-shape builders into a reusable test helper. Use it from the Fourier table and material resolver tests to remove duplicate binary serialization code. Qualify Google Mock matchers to distinguish them from the project testing namespace. --- .../DemandPbrtScene/tests/CMakeLists.txt | 1 + .../tests/TestFourierBsdfTable.cpp | 158 ++++-------------- .../tests/TestMaterialResolver.cpp | 97 +---------- .../Testing/FourierBsdfTableWriter.h | 113 +++++++++++++ 4 files changed, 150 insertions(+), 219 deletions(-) create mode 100644 examples/DemandLoading/DemandPbrtScene/tests/include/DemandPbrtScene/Testing/FourierBsdfTableWriter.h 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..042e4b99 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp @@ -4,22 +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' }; - std::filesystem::path pbrtReferenceDir() { return std::filesystem::path{ DEMAND_PBRT_SCENE_TEST_SOURCE_DIR } / "pbrt-reference"; @@ -34,94 +31,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,23 +38,22 @@ 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 ); } } @@ -190,7 +98,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 +109,14 @@ 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 ) ); + EXPECT_THAT( table.coefficientCounts, ::testing::Each( 530 ) ); + EXPECT_THAT( table.zeroOrderCoefficients, ::testing::Each( 1.0f ) ); } 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 +127,8 @@ 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 ) ); + EXPECT_THAT( table.coefficientCounts, ::testing::Each( 1599 ) ); + EXPECT_THAT( table.zeroOrderCoefficients, ::testing::Each( 1.0f ) ); } TEST( TestFourierBsdfTable, reportsMissingTable ) @@ -228,7 +136,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,48 +150,46 @@ 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" ) ); } TEST( TestFourierBsdfTable, rejectsMalformedLargeCoefficientSpans ) @@ -294,5 +200,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 a2fb06a2..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 ) 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 From ded46e854addfa3bcbc346bd6fe0de750f9360a9 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 16:01:42 -0600 Subject: [PATCH 17/21] Compose Method on Fourier BSDF sampling Extract Catmull-Rom segment selection and integration, Fourier recurrence evaluation, and safeguarded Newton iteration into device helpers. Share convergence and Newton-update logic between the sampling paths. Scope the DemandPbrtScene namespace import to the Fourier table tests and avoid an MSVC lookup collision in Google Mock's Each matcher. --- .../include/DemandPbrtScene/FourierBsdfEval.h | 288 +++++++++++------- .../tests/TestFourierBsdfTable.cpp | 27 +- 2 files changed, 205 insertions(+), 110 deletions(-) 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/tests/TestFourierBsdfTable.cpp b/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp index 042e4b99..f1e0f734 100644 --- a/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp +++ b/examples/DemandLoading/DemandPbrtScene/tests/TestFourierBsdfTable.cpp @@ -12,11 +12,12 @@ #include #include -using namespace demandPbrtScene; using demandPbrtScene::testing::FourierBsdfTableWriter; namespace { +using namespace demandPbrtScene; + std::filesystem::path pbrtReferenceDir() { return std::filesystem::path{ DEMAND_PBRT_SCENE_TEST_SOURCE_DIR } / "pbrt-reference"; @@ -57,8 +58,6 @@ void writeMalformedLargeSpanTable( const std::filesystem::path& fileName ) } } -} // namespace - TEST( TestFourierBsdfTable, parsesFixtureTableMetadataAndCoefficientLayout ) { const std::filesystem::path fixture{ pbrtReferenceDir() / "bsdfs" / "roughgold_alpha_0.2.bsdf" }; @@ -109,8 +108,14 @@ TEST( TestFourierBsdfTable, parsesCoatedCopperOrderShape ) EXPECT_EQ( 3, table.nChannels ); EXPECT_EQ( 6360, table.nCoefficients ); EXPECT_EQ( 4U, table.coefficientOffsets.size() ); - EXPECT_THAT( table.coefficientCounts, ::testing::Each( 530 ) ); - EXPECT_THAT( table.zeroOrderCoefficients, ::testing::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 ) @@ -127,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, ::testing::Each( 1599 ) ); - EXPECT_THAT( table.zeroOrderCoefficients, ::testing::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 ) @@ -192,6 +203,8 @@ TEST( TestFourierBsdfTable, rejectsMalformedCoefficientSpans ) 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" ) }; From 4f108c4e5d1834e0ba35edc238b5ff4eabc35118 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 16:19:20 -0600 Subject: [PATCH 18/21] Replace PBRT material type strings with descriptors Parse material type names into a centralized kind and capability descriptor. Dispatch MDL source generation, support checks, runtime texture binding, parameter selection, and gap policy by material kind. This removes repeated string comparisons and localizes material behavior. --- .../DemandPbrtScene/CMakeLists.txt | 2 + .../DemandPbrtScene/MaterialResolver.cpp | 308 +++++++++--------- .../DemandPbrtScene/MdlShaderCache.cpp | 206 ++++++------ .../DemandPbrtScene/PbrtMaterialKind.cpp | 75 +++++ .../DemandPbrtScene/PbrtMaterialKind.h | 55 ++++ 5 files changed, 382 insertions(+), 264 deletions(-) create mode 100644 examples/DemandLoading/DemandPbrtScene/PbrtMaterialKind.cpp create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/PbrtMaterialKind.h diff --git a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt index e56e54dd..c5d269a6 100644 --- a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt @@ -38,6 +38,7 @@ if(OTK_USE_MDL) MdlSdkSession.cpp MdlShaderCache.cpp MdlUtils.cpp + PbrtMaterialKind.cpp include/DemandPbrtScene/FourierBsdfEval.h include/DemandPbrtScene/FourierBsdfTable.h include/DemandPbrtScene/FourierBsdfTableResource.h @@ -49,6 +50,7 @@ if(OTK_USE_MDL) include/DemandPbrtScene/MdlShaderCache.h include/DemandPbrtScene/MdlShaderCompileCacheStatistics.h include/DemandPbrtScene/MdlUtils.h + include/DemandPbrtScene/PbrtMaterialKind.h ) endif() diff --git a/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp b/examples/DemandLoading/DemandPbrtScene/MaterialResolver.cpp index f10d55fb..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 ) @@ -326,51 +324,15 @@ bool hasGeneratedMdlFoldableTextureParameter( const otk::pbrt::PbrtMaterial& mat || hasGeneratedMdlFoldableTextureParameter( material, policy.name, MdlBoundParameterType::FLOAT ); } -bool usesGeneratedMdlNamedMaterialKd( const std::string& type ) -{ - return type == "matte" || type == "plastic" || type == "uber" || type == "substrate" || type == "translucent" - || type == "kdsubsurface"; -} - -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; } @@ -385,28 +347,26 @@ bool isDirectGeneratedMdlDemandTexture( const PbrtDemandTextureBinding& binding return hasPbrtDemandTextureBinding( binding ) && !binding.transformed; } -PbrtDemandTextureBinding namedRuntimeBinding( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const MdlTexturePolicy& policy ) +PbrtDemandTextureBinding namedRuntimeBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MdlTexturePolicy& policy ) { - const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; const PbrtDemandTextureBinding binding{ generatedMdlTextureBinding( material, policy ) }; if( !hasPbrtDemandTextureBinding( binding ) ) { return pbrtDemandTextureBinding(); } - const std::string type{ material.type }; - if( policy.use == MdlTextureUse::DIFFUSE && usesGeneratedMdlNamedMaterialKd( type ) ) + if( policy.use == MdlTextureUse::DIFFUSE && descriptor.has( PbrtMaterialCapability::KD ) ) { return binding; } - if( policy.use == MdlTextureUse::SPECULAR && usesGeneratedMdlNamedMaterialKs( type ) + if( policy.use == MdlTextureUse::SPECULAR && descriptor.has( PbrtMaterialCapability::KS ) && isDirectGeneratedMdlDemandTexture( binding ) ) { return binding; } - if( policy.use == MdlTextureUse::REFLECTION && usesGeneratedMdlNamedMaterialKr( type ) + if( policy.use == MdlTextureUse::REFLECTION && descriptor.has( PbrtMaterialCapability::KR ) && isDirectGeneratedMdlDemandTexture( binding ) ) { return binding; @@ -418,43 +378,48 @@ PbrtDemandTextureBinding namedRuntimeBinding( const otk::pbrt::PbrtMaterial& return pbrtDemandTextureBinding(); } -PbrtDemandTextureBinding namedRuntimeBinding( 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( parent, namedMaterial, *policy ) : pbrtDemandTextureBinding(); + return policy ? namedRuntimeBinding( material, descriptor, *policy ) : pbrtDemandTextureBinding(); } -bool hasGeneratedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const MdlTexturePolicy& policy ) +bool hasGeneratedMdlNamedMaterialRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MdlTexturePolicy& policy ) { - return hasPbrtDemandTextureBinding( namedRuntimeBinding( parent, namedMaterial, policy ) ); + return hasPbrtDemandTextureBinding( namedRuntimeBinding( material, descriptor, policy ) ); } -bool supportsGeneratedMdlNamedMaterialTextureReference( const otk::pbrt::PbrtMaterial& parent, - const otk::pbrt::PbrtNamedMaterial& namedMaterial, - const MdlTexturePolicy& policy ) +bool supportsGeneratedMdlNamedMaterialTextureReference( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MdlTexturePolicy& policy ) { - if( namedMaterial.params.FindTexture( policy.name ).empty() ) + if( material.params.FindTexture( policy.name ).empty() ) { return true; } - const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; if( hasGeneratedMdlFoldableTextureParameter( material, policy ) ) { return true; } - return hasGeneratedMdlNamedMaterialRuntimeTextureBinding( parent, namedMaterial, policy ); + return hasGeneratedMdlNamedMaterialRuntimeTextureBinding( material, descriptor, policy ); } bool supportsNamedTextures( const otk::pbrt::PbrtMaterial& parent, const otk::pbrt::PbrtNamedMaterial& namedMaterial ) { + const otk::pbrt::PbrtMaterial material{ generatedMdlMaterialForNamedMaterial( parent, namedMaterial ) }; + const PbrtMaterialDescriptor& descriptor{ pbrtMaterialDescriptor( material.type ) }; + if( !supportsGeneratedMdlNamedMaterial( descriptor ) ) + { + return false; + } for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) { - if( !supportsGeneratedMdlNamedMaterialTextureReference( parent, namedMaterial, policy ) ) + if( !supportsGeneratedMdlNamedMaterialTextureReference( material, descriptor, policy ) ) { return false; } @@ -476,17 +441,17 @@ bool supportsGeneratedMdlNamedMaterialReference( const otk::pbrt::PbrtMaterial& return false; } - return supportsGeneratedMdlNamedMaterialType( generatedMdlNamedMaterialType( namedMaterial->second ) ) - && supportsNamedTextures( 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; } @@ -494,7 +459,7 @@ bool supportsGeneratedMdlNamedMaterialReferences( const otk::pbrt::PbrtMaterial& && supportsGeneratedMdlNamedMaterialReference( material, "namedmaterial2" ); } -bool acceptsGeneratedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, +bool acceptsGeneratedMdlRuntimeTextureBinding( const PbrtMaterialDescriptor& material, const MaterialGroup& group, const MdlTexturePolicy& policy, const PbrtDemandTextureBinding& binding ) @@ -505,40 +470,41 @@ bool acceptsGeneratedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& ma } if( policy.use == MdlTextureUse::REFLECTION ) { - if( material.type == "mirror" ) + if( material.kind == PbrtMaterialKind::MIRROR ) { return flagSet( group.material.flags, MaterialFlags::DIFFUSE_MAP ) && binding.fileName == group.diffuseMapFileName; } - return material.type == "uber" && isDirectGeneratedMdlDemandTexture( binding ); + return material.kind == PbrtMaterialKind::UBER && isDirectGeneratedMdlDemandTexture( binding ); } if( policy.use == MdlTextureUse::SPECULAR ) { - return usesGeneratedMdlKs( material.type ) && isDirectGeneratedMdlDemandTexture( binding ); + return material.has( PbrtMaterialCapability::KS ) && isDirectGeneratedMdlDemandTexture( binding ); } if( policy.use == MdlTextureUse::TRANSMISSION ) { - return usesGeneratedMdlKt( material.type ) && isDirectGeneratedMdlDemandTexture( binding ); + return material.has( PbrtMaterialCapability::KT ) && isDirectGeneratedMdlDemandTexture( binding ); } if( policy.use == MdlTextureUse::ROUGHNESS ) { - return usesGeneratedMdlRoughnessTextureParam( material.type, policy.name ) + return usesGeneratedMdlRoughnessTextureParam( material, policy.name ) && isDirectGeneratedMdlDemandTexture( binding ); } if( policy.use == MdlTextureUse::AMOUNT ) { - return material.type == "mix" && isDirectGeneratedMdlDemandTexture( binding ); + return material.kind == PbrtMaterialKind::MIX && isDirectGeneratedMdlDemandTexture( binding ); } return policy.use == MdlTextureUse::BUMP; } PbrtDemandTextureBinding generatedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, - const MaterialGroup& group, - const MdlTexturePolicy& policy ) + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group, + const MdlTexturePolicy& policy ) { const PbrtDemandTextureBinding binding{ generatedMdlTextureBinding( material, policy ) }; if( hasPbrtDemandTextureBinding( binding ) - && acceptsGeneratedMdlRuntimeTextureBinding( material, group, policy, binding ) ) + && acceptsGeneratedMdlRuntimeTextureBinding( descriptor, group, policy, binding ) ) { return binding; } @@ -546,18 +512,20 @@ PbrtDemandTextureBinding generatedMdlRuntimeTextureBinding( const otk::pbrt::Pbr } PbrtDemandTextureBinding generatedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, - const MaterialGroup& group, - const std::string& paramName ) + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group, + const std::string& paramName ) { const MdlTexturePolicy* const policy{ mdlTexturePolicy( paramName ) }; - return policy ? generatedMdlRuntimeTextureBinding( material, group, *policy ) : pbrtDemandTextureBinding(); + return policy ? generatedMdlRuntimeTextureBinding( material, descriptor, group, *policy ) : pbrtDemandTextureBinding(); } bool hasGeneratedMdlRuntimeTextureBinding( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, const MaterialGroup& group, const MdlTexturePolicy& policy ) { - return hasPbrtDemandTextureBinding( generatedMdlRuntimeTextureBinding( material, group, policy ) ); + return hasPbrtDemandTextureBinding( generatedMdlRuntimeTextureBinding( material, descriptor, group, policy ) ); } struct MdlTextureMaps @@ -577,13 +545,14 @@ MdlTextureMaps generatedMdlTextureMaps( const MaterialGroup& group ) }; } -bool needsGeneratedMdlDiffuseMap( const otk::pbrt::PbrtMaterial& material, const MdlTexturePolicy& policy ) +bool needsGeneratedMdlDiffuseMap( const PbrtMaterialDescriptor& material, const MdlTexturePolicy& policy ) { return policy.use == MdlTextureUse::DIFFUSE - || ( policy.use == MdlTextureUse::REFLECTION && material.type == "mirror" ); + || ( policy.use == MdlTextureUse::REFLECTION && material.kind == PbrtMaterialKind::MIRROR ); } bool supportsGeneratedMdlTextureReference( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, const MaterialGroup& group, const MdlTexturePolicy& policy, const MdlTextureMaps& maps ) @@ -595,7 +564,7 @@ bool supportsGeneratedMdlTextureReference( const otk::pbrt::PbrtMaterial& materi } if( policy.use == MdlTextureUse::BUMP ) { - return hasGeneratedMdlRuntimeTextureBinding( material, group, policy ); + return hasGeneratedMdlRuntimeTextureBinding( material, descriptor, group, policy ); } if( hasGeneratedMdlFoldableTextureParameter( material, policy ) ) { @@ -609,11 +578,11 @@ bool supportsGeneratedMdlTextureReference( const otk::pbrt::PbrtMaterial& materi { return true; } - if( needsGeneratedMdlDiffuseMap( material, policy ) && !maps.diffuse ) + if( needsGeneratedMdlDiffuseMap( descriptor, policy ) && !maps.diffuse ) { return false; } - return hasGeneratedMdlRuntimeTextureBinding( material, group, policy ); + return hasGeneratedMdlRuntimeTextureBinding( material, descriptor, group, policy ); } bool supportsGeneratedMdlTextureFlags( const MaterialGroup& group, const MdlTextureMaps& maps ) @@ -636,12 +605,14 @@ bool supportsGeneratedMdlTextureFlags( const MaterialGroup& group, const MdlText return true; } -bool supportsGeneratedMdlTextureReferences( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group ) +bool supportsGeneratedMdlTextureReferences( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group ) { const MdlTextureMaps maps{ generatedMdlTextureMaps( group ) }; for( const MdlTexturePolicy& policy : MDL_TEXTURE_POLICIES ) { - if( !supportsGeneratedMdlTextureReference( material, group, policy, maps ) ) + if( !supportsGeneratedMdlTextureReference( material, descriptor, group, policy, maps ) ) { return false; } @@ -649,9 +620,11 @@ bool supportsGeneratedMdlTextureReferences( const otk::pbrt::PbrtMaterial& mater return supportsGeneratedMdlTextureFlags( group, maps ); } -bool hasGeneratedMdlUnsupportedTextureReference( const otk::pbrt::PbrtMaterial& material, const MaterialGroup& group ) +bool hasGeneratedMdlUnsupportedTextureReference( const otk::pbrt::PbrtMaterial& material, + const PbrtMaterialDescriptor& descriptor, + const MaterialGroup& group ) { - if( !supportsGeneratedMdlTextureReferences( material, group ) ) + if( !supportsGeneratedMdlTextureReferences( material, descriptor, group ) ) { return true; } @@ -681,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 ) }; @@ -715,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 ) @@ -754,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{ namedRuntimeBinding( parent, namedMaterial, paramName ) }; + const PbrtDemandTextureBinding binding{ namedRuntimeBinding( material, descriptor, paramName ) }; if( hasPbrtDemandTextureBinding( binding ) ) { return binding; @@ -769,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; } @@ -796,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, - namedRuntimeBinding( *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; @@ -837,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 ); } @@ -862,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 ) @@ -1144,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/MdlShaderCache.cpp b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp index 47c1579e..07d631f9 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp @@ -5,6 +5,7 @@ #include "DemandPbrtScene/MdlShaderCache.h" #ifdef OTK_USE_MDL +#include "DemandPbrtScene/PbrtMaterialKind.h" #include "DemandPbrtScene/PbrtCheckerboardImageSource.h" #include @@ -645,9 +646,9 @@ std::string namedMaterialParameterName( unsigned int index, const std::string& p struct PbrtMaterialGapPolicy { - std::string type; - std::string policy; - std::string coverageReason; + PbrtMaterialKind kind; + std::string policy; + std::string coverageReason; }; struct BoundParameterSpec @@ -1038,21 +1039,21 @@ void appendMaterialParameter( MdlMaterialModel& model, const std::string& type, model.parameters.push_back( MdlMaterialParameter{ type, name, defaultValue } ); } -const PbrtMaterialGapPolicy* explicitMaterialGapPolicy( const std::string& type ) +const PbrtMaterialGapPolicy* explicitMaterialGapPolicy( PbrtMaterialKind kind ) { static const PbrtMaterialGapPolicy policies[] = { - { "fourier", "unsupported with visible fallback", + { 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" }, - { "hair", "unsupported with visible fallback", + { PbrtMaterialKind::HAIR, "unsupported with visible fallback", "low-frequency PBRT corpus material; no current target scene or reference fixture requires approximation" }, - { "measured", "unsupported with visible fallback", + { 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.type == type ) + if( policy.kind == kind ) { return &policy; } @@ -1271,41 +1272,43 @@ enum class BoundMaterialKind struct MaterialBoundParameterSpecs { - const char* type; + PbrtMaterialKind kind; BoundParameterSpecs root; BoundParameterSpecs named; }; constexpr MaterialBoundParameterSpecs materialBoundParameterSpecs[] = { - { "matte", makeBoundParameterSpecs( matteParams ), makeBoundParameterSpecs( matteParams ) }, - { "plastic", makeBoundParameterSpecs( plasticParams ), makeBoundParameterSpecs( plasticParams ) }, - { "uber", makeBoundParameterSpecs( uberParams ), makeBoundParameterSpecs( namedUberParams ) }, - { "mirror", makeBoundParameterSpecs( mirrorParams ), makeBoundParameterSpecs( mirrorParams ) }, - { "glass", makeBoundParameterSpecs( glassParams ), makeBoundParameterSpecs( glassParams ) }, - { "metal", makeBoundParameterSpecs( metalParams ), makeBoundParameterSpecs( metalParams ) }, - { "substrate", makeBoundParameterSpecs( substrateParams ), makeBoundParameterSpecs( substrateParams ) }, - { "translucent", makeBoundParameterSpecs( translucentParams ), makeBoundParameterSpecs( translucentParams ) }, - { "subsurface", makeBoundParameterSpecs( subsurfaceParams ), {} }, - { "kdsubsurface", makeBoundParameterSpecs( kdSubsurfaceParams ), {} }, - { "mix", makeBoundParameterSpecs( mixParams ), {} }, + { 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( const std::string& type, BoundMaterialKind kind ) +BoundParameterSpecs boundParameterSpecs( PbrtMaterialKind materialKind, BoundMaterialKind boundKind ) { for( const MaterialBoundParameterSpecs& specs : materialBoundParameterSpecs ) { - if( type == specs.type ) + if( materialKind == specs.kind ) { - return kind == BoundMaterialKind::ROOT ? specs.root : specs.named; + return boundKind == BoundMaterialKind::ROOT ? specs.root : specs.named; } } return {}; } void appendRootMaterialBoundParameters( std::vector& result, - const otk::pbrt::PbrtMaterial& material ) + const otk::pbrt::PbrtMaterial& material, + PbrtMaterialKind kind ) { - const BoundParameterSpecs specs{ boundParameterSpecs( material.type, BoundMaterialKind::ROOT ) }; + const BoundParameterSpecs specs{ boundParameterSpecs( kind, BoundMaterialKind::ROOT ) }; appendMaterialBoundParameters( result, material, specs.begin, specs.end ); } @@ -1326,15 +1329,16 @@ void appendNamedMaterialBoundParameters( std::vector& return; } - const std::string type{ namedMaterialType( namedMaterial->second ) }; - const BoundParameterSpecs specs{ boundParameterSpecs( type, BoundMaterialKind::NAMED ) }; + 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 ) + const otk::pbrt::PbrtMaterial& material, + PbrtMaterialKind kind ) { - if( material.type != "mix" ) + if( kind != PbrtMaterialKind::MIX ) { return; } @@ -1657,45 +1661,33 @@ std::string namedMaterialBsdfExpression( MdlMaterialModel& mod const otk::pbrt::PbrtNamedMaterial& material, unsigned int index ) { - const std::string type{ namedMaterialType( material ) }; - const std::string typeComment{ type.empty() ? std::string{ "" } : type }; + 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 ); - 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 ); + 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(); } - 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 ) @@ -2263,19 +2255,21 @@ MdlMaterialModel makeMixMaterialModel( const otk::pbrt::PbrtMaterial& material, return model; } -MdlMaterialModel makeUnsupportedMaterialModel( const otk::pbrt::PbrtMaterial& material, GeneratedMdlSource& result ) +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( material.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( material.type == "fourier" ) + if( kind == PbrtMaterialKind::FOURIER ) { if( hasFourierBsdfFile( material ) ) { @@ -2302,51 +2296,34 @@ MdlMaterialModel makeUnsupportedMaterialModel( const otk::pbrt::PbrtMaterial& ma 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 ); + 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 ); } - return makeUnsupportedMaterialModel( material, result ); } GeneratedMdlSource generateSource( const MdlShaderKey& key ) @@ -2463,8 +2440,9 @@ MdlMaterialInstanceKey makeMdlMaterialInstanceKey( const otk::pbrt::PbrtMaterial std::vector makeMdlBoundMaterialParameters( const otk::pbrt::PbrtMaterial& material ) { std::vector result; - appendRootMaterialBoundParameters( result, material ); - appendNamedMaterialBoundParameters( result, material ); + const PbrtMaterialKind kind{ pbrtMaterialKind( material.type ) }; + appendRootMaterialBoundParameters( result, material, kind ); + appendNamedMaterialBoundParameters( result, material, kind ); return result; } 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/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 From b0b71339c1605ef75b3ca4e7229f86ab9c637e93 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Wed, 2 Sep 2026 16:36:03 -0600 Subject: [PATCH 19/21] Decompose MDL shader cache translation unit Extract key construction, material-model generation, parameter binding, and texture-graph generation into focused components. Leave MdlShaderCache.cpp responsible only for cache orchestration. Move MdlTextureGraphGenerator behind a private implementation and place each component interface in its corresponding header. --- .../DemandPbrtScene/CMakeLists.txt | 8 + .../DemandPbrtScene/MdlKeyBuilder.cpp | 390 +++ .../MdlMaterialModelBuilder.cpp | 1255 +++++++++ .../DemandPbrtScene/MdlParameterBinder.cpp | 594 ++++ .../DemandPbrtScene/MdlShaderCache.cpp | 2481 +---------------- .../MdlTextureGraphGenerator.cpp | 361 +++ .../include/DemandPbrtScene/MdlKeyBuilder.h | 45 + .../DemandPbrtScene/MdlMaterialModelBuilder.h | 36 + .../DemandPbrtScene/MdlParameterBinder.h | 42 + .../include/DemandPbrtScene/MdlShaderCache.h | 60 +- .../MdlTextureGraphGenerator.h | 58 + 11 files changed, 2795 insertions(+), 2535 deletions(-) create mode 100644 examples/DemandLoading/DemandPbrtScene/MdlKeyBuilder.cpp create mode 100644 examples/DemandLoading/DemandPbrtScene/MdlMaterialModelBuilder.cpp create mode 100644 examples/DemandLoading/DemandPbrtScene/MdlParameterBinder.cpp create mode 100644 examples/DemandLoading/DemandPbrtScene/MdlTextureGraphGenerator.cpp create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlKeyBuilder.h create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlMaterialModelBuilder.h create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlParameterBinder.h create mode 100644 examples/DemandLoading/DemandPbrtScene/include/DemandPbrtScene/MdlTextureGraphGenerator.h diff --git a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt index c5d269a6..219581b2 100644 --- a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt @@ -35,8 +35,12 @@ if(OTK_USE_MDL) 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 @@ -46,9 +50,13 @@ if(OTK_USE_MDL) 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 ) 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/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/MdlShaderCache.cpp b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp index 07d631f9..9b4adaab 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp +++ b/examples/DemandLoading/DemandPbrtScene/MdlShaderCache.cpp @@ -5,2488 +5,13 @@ #include "DemandPbrtScene/MdlShaderCache.h" #ifdef OTK_USE_MDL -#include "DemandPbrtScene/PbrtMaterialKind.h" -#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 ); - } -} - -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{} ); -} - -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 -{ - PbrtMaterialKind kind; - 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( 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 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{} ); -} - -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 ); -} - -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 ); - } -} - -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 ) -{ - std::vector result; - const PbrtMaterialKind kind{ pbrtMaterialKind( material.type ) }; - appendRootMaterialBoundParameters( result, material, kind ); - appendNamedMaterialBoundParameters( result, material, kind ); - 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 ) { @@ -2741,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/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/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 + From 78ce0b0d64678d734fada6d76483cd64cd44a081 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Thu, 3 Sep 2026 13:23:34 -0600 Subject: [PATCH 20/21] Rename MDL material device source Rename MdlSmokeMaterial.cu to MdlMaterial.cu now that it contains the production MDL closest-hit implementation. Update CUDA embedding and generated symbol references to match. --- examples/DemandLoading/DemandPbrtScene/CMakeLists.txt | 2 +- .../DemandPbrtScene/{MdlSmokeMaterial.cu => MdlMaterial.cu} | 1 + examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) rename examples/DemandLoading/DemandPbrtScene/{MdlSmokeMaterial.cu => MdlMaterial.cu} (99%) diff --git a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt index 219581b2..f7731843 100644 --- a/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt +++ b/examples/DemandLoading/DemandPbrtScene/CMakeLists.txt @@ -28,7 +28,7 @@ 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 diff --git a/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu b/examples/DemandLoading/DemandPbrtScene/MdlMaterial.cu similarity index 99% rename from examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu rename to examples/DemandLoading/DemandPbrtScene/MdlMaterial.cu index 9ddfb4a1..1783abd2 100644 --- a/examples/DemandLoading/DemandPbrtScene/MdlSmokeMaterial.cu +++ b/examples/DemandLoading/DemandPbrtScene/MdlMaterial.cu @@ -1116,3 +1116,4 @@ extern "C" __global__ void __closesthit__mdlMesh() } } // namespace demandPbrtScene + diff --git a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp index 1aa90caf..21c66ad0 100644 --- a/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp +++ b/examples/DemandLoading/DemandPbrtScene/ProgramGroups.cpp @@ -987,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; @@ -1457,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{}; From 5e41488ebd1143ca677d1be60260f3c935853206 Mon Sep 17 00:00:00 2001 From: Richard Thomson Date: Thu, 3 Sep 2026 15:59:15 -0600 Subject: [PATCH 21/21] Clarify PBRT film parameter warning State that image-film parameters are ignored and rendering resolution is controlled by the application window. Avoid implying that parsed xresolution and yresolution values affect output. --- examples/PbrtSceneLoader/PbrtApiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/PbrtSceneLoader/PbrtApiImpl.cpp b/examples/PbrtSceneLoader/PbrtApiImpl.cpp index 2faf2cbb..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