diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal index 507fa655b5..37ea7d293b 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal @@ -71,6 +71,7 @@ using namespace metal; constant const bool perPixelLighting [[ function_constant(FunctionConstantPerPixelLighting) ]]; constant const bool perVertexLighting = !perPixelLighting; +constant const bool bumpMap [[ function_constant(FunctionConstantPerPixelBumpMap) ]]; constant const uint8_t sourceType1 [[ function_constant(FunctionConstantSources + 0) ]]; constant const uint8_t sourceType2 [[ function_constant(FunctionConstantSources + 1) ]]; @@ -167,6 +168,7 @@ typedef struct float3 texCoord6 [[ function_constant(hasLayer6) ]]; float3 texCoord7 [[ function_constant(hasLayer7) ]]; float3 texCoord8 [[ function_constant(hasLayer8) ]]; + float3 T, B [[ function_constant(bumpMap) ]]; half4 vtxColor [[ centroid_perspective ]]; half4 fogColor; } ColorInOut; @@ -182,7 +184,8 @@ half4 calcLitMaterialColor(constant plMetalLights & lights, const half4 materialColor, constant plMaterialLightingDescriptor & materialLighting, const float4 position, - const float3 normal) + const float3 normal + ) { half3 LAmbient = half3(0.h, 0.h, 0.h); half3 LDiffuse = half3(0.h, 0.h, 0.h); @@ -277,6 +280,9 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], (&out.texCoord1)[layer] = uniforms.sampleLocation(layer, &in.texCoord1, normal, vCamPosition); } + out.T = normalize( uniforms.localToWorldMatrix * float4(in.texCoord2, 0.f)). xyz; + out.B = normalize( uniforms.localToWorldMatrix * float4(in.texCoord2, 0.f)). xyz; + out.position = vCamPosition * uniforms.projectionMatrix; return out; @@ -442,9 +448,33 @@ half4 FragmentShaderArguments::sampleLayer(const size_t index, const half4 verte fragment half4 pipelineFragmentShader(ColorInOut in [[stage_in]], const FragmentShaderArguments fragmentShaderArgs, constant plMetalLights & lights [[ buffer(FragmentShaderArgumentLights), function_constant(perPixelLighting) ]], - constant plMaterialLightingDescriptor & materialLighting [[ buffer(FragmentShaderArgumentMaterialLighting), function_constant(perPixelLighting) ]]) + constant plMaterialLightingDescriptor & materialLighting [[ buffer(FragmentShaderArgumentMaterialLighting), function_constant(perPixelLighting) ]], + texture2d bumpTexture [[ texture(FragmentShaderArgumentAttributeBumpMapTexture), function_constant(bumpMap) ]]) { - const half4 lightingContributionColor = perPixelLighting ? calcLitMaterialColor(lights, in.vtxColor, materialLighting, in.worldPos, in.normal) : in.vtxColor; + half4 lightingContributionColor = half4(0.h); + + constexpr bool bumpMapIsAdditive = false; + const bool performBaseLighting = bumpMapIsAdditive || !bumpMap; + + if(performBaseLighting) { + lightingContributionColor = perPixelLighting ? calcLitMaterialColor(lights, in.vtxColor, materialLighting, in.worldPos, in.normal) : in.vtxColor; + } + + if(bumpMap) { + float3 sampleCoord = in.texCoord1; + half3 bumpNormal = bumpTexture.sample(fragmentShaderArgs.samplers, sampleCoord.xy).rgb; + + bumpNormal -= 0.5f; + bumpNormal *= 2.f; + + float3x3 TBN = float3x3(in.T, in.B, in.normal); + bumpNormal = half3(normalize(TBN * float3(bumpNormal))); + + if(performBaseLighting) { + bumpNormal.z = 0.f; + } + lightingContributionColor += perPixelLighting ? calcLitMaterialColor(lights, in.vtxColor, materialLighting, in.worldPos, float3(bumpNormal)) : in.vtxColor; + } half4 currentColor = lightingContributionColor; /* @@ -602,6 +632,7 @@ constexpr void blend(half4 srcSample, thread half4 &destSample, const uint32_t b } } + vertex ShadowCasterInOut shadowVertexShader(Vertex in [[stage_in]], constant VertexUniforms & uniforms [[ buffer( VertexShaderArgumentFixedFunctionUniforms) ]]) { diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h index f3eb238ca9..7e49f4022f 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h @@ -115,6 +115,7 @@ enum plMetalFunctionConstant FunctionConstantNumWeights = 26, /// Per pixel lighting enable flag FunctionConstantPerPixelLighting = 27, + FunctionConstantPerPixelBumpMap = 28, }; enum plMetalLayerPassType: uint8_t @@ -144,7 +145,10 @@ enum plMetalFragmentShaderTextures { FragmentShaderArgumentAttributeTextures = 0, FragmentShaderArgumentAttributeCubicTextures = 8, - FragmentShaderArgumentAttributeUniforms = 32 + FragmentShaderArgumentAttributeUniforms = 32, + // A bump map pass can't use all 8 texture passes + // Re-use the last texture for bump. + FragmentShaderArgumentAttributeBumpMapTexture = 7 }; struct plMetalShaderLightSource diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.cpp index 725eb9eb74..62621de723 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.cpp @@ -198,6 +198,15 @@ void plMetalMaterialShaderRef::EncodeArguments(MTL::RenderCommandEncoder* encode return postEncodeTransform(layer, index); } ); + + if (fBumps[pass].has_value()) + { + plLayerInterface *bumpLayer = fMaterial->GetLayer(fMaterial->GetNumLayers()-1); + passDescription->fUsePerPixelLighting = true; + passDescription->fHasBumpMap = true; + auto texture = (plMetalTextureRef*)bumpLayer->GetTexture()->GetDeviceRef(); + encoder->setFragmentTexture(texture->fTexture, 7); + } encoder->setFragmentBytes(&uniforms, sizeof(plMetalFragmentShaderArgumentBuffer), FragmentShaderArgumentUniforms); } @@ -293,6 +302,9 @@ void plMetalMaterialShaderRef::ILoopOverLayers() fPassIndices.push_back(currLayer); fPassLengths.push_back(j - currLayer); + + fBumps.push_back(IEatBumpmapLayers(j)); + fNumPasses++; #if 0 @@ -301,6 +313,46 @@ void plMetalMaterialShaderRef::ILoopOverLayers() } } +std::optional plMetalMaterialShaderRef::IEatBumpmapLayers( uint32_t& layerIdx ) +{ + + std::optional bumpMapping; + + // If there aren't enough layers left to support a bump map, return + if (!(layerIdx + 3 < fMaterial->GetNumLayers())) + { + return bumpMapping; + } + + // Does the next layer imply a bump map? + if (!(fMaterial->GetLayer(layerIdx)->GetMiscFlags() & hsGMatState::kMiscBumpChans)) + { + return bumpMapping; + } + + printf("Bump map layer found!\n"); + bumpMapping = plMetalBumpMapping(); + // We have a bump map and it should occupy the next four layers + for (size_t bumpLayer = 0; bumpLayer < 4; bumpLayer++) + { + plLayerInterface* layer = fMaterial->GetLayer(layerIdx + bumpLayer); + uint32_t miscFlags = layer->GetMiscFlags(); + switch( miscFlags & hsGMatState::kMiscBumpChans ) + { + case hsGMatState::kMiscBumpDu: + bumpMapping.value().dTangentUIndex = layer->GetUVWSrc(); + case hsGMatState::kMiscBumpDv: + bumpMapping.value().dTangentVIndex = layer->GetUVWSrc(); + case hsGMatState::kMiscBumpDw: + default: + break; + } + } + + layerIdx+=4; + return bumpMapping; +} + const hsGMatState plMetalMaterialShaderRef::ICompositeLayerState(const plLayerInterface* layer) const { hsGMatState state; @@ -323,13 +375,24 @@ void plMetalMaterialShaderRef::IBuildLayerTexture(MTL::RenderCommandEncoder* enc // FIXME: Better way to address missing textures than null pointers encoder->setFragmentTexture(nullptr, FragmentShaderArgumentAttributeCubicTextures + offsetFromRootLayer); encoder->setFragmentTexture(nullptr, FragmentShaderArgumentAttributeTextures + offsetFromRootLayer); + fPipeline->fState.fTextures[FragmentShaderArgumentAttributeTextures + offsetFromRootLayer] = nullptr; + fPipeline->fState.fTextures[FragmentShaderArgumentAttributeCubicTextures + offsetFromRootLayer] = nullptr; + return; } hsAssert(offsetFromRootLayer <= 8, "Too many layers requested"); if (plCubicEnvironmap::ConvertNoRef(texture) != nullptr || plCubicRenderTarget::ConvertNoRef(texture) != nullptr) { - encoder->setFragmentTexture(deviceTexture->fTexture, FragmentShaderArgumentAttributeCubicTextures + offsetFromRootLayer); + MTL::Texture* currentTexture = fPipeline->fState.fTextures[FragmentShaderArgumentAttributeCubicTextures + offsetFromRootLayer]; + if (currentTexture != deviceTexture->fTexture) { + encoder->setFragmentTexture(deviceTexture->fTexture, FragmentShaderArgumentAttributeCubicTextures + offsetFromRootLayer); + fPipeline->fState.fTextures[FragmentShaderArgumentAttributeCubicTextures + offsetFromRootLayer] = deviceTexture->fTexture; + } } else if (plMipmap::ConvertNoRef(texture) != nullptr || plRenderTarget::ConvertNoRef(texture) != nullptr) { - encoder->setFragmentTexture(deviceTexture->fTexture, FragmentShaderArgumentAttributeTextures + offsetFromRootLayer); + MTL::Texture* currentTexture = fPipeline->fState.fTextures[FragmentShaderArgumentAttributeTextures + offsetFromRootLayer]; + if (currentTexture != deviceTexture->fTexture) { + encoder->setFragmentTexture(deviceTexture->fTexture, FragmentShaderArgumentAttributeTextures + offsetFromRootLayer); + fPipeline->fState.fTextures[FragmentShaderArgumentAttributeTextures + offsetFromRootLayer] = deviceTexture->fTexture; + } } if (fPipeline->fState.layerStates[offsetFromRootLayer].clampFlag != layer->GetClampFlags()) { diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.h index ae643e56cb..2c21b3aff2 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalMaterialShaderRef.h @@ -118,10 +118,13 @@ class plMetalMaterialShaderRef : public plMetalDeviceRef const std::function& postEncodeTransform); bool ICanEatLayer(plLayerInterface* lay); uint32_t ILayersAtOnce(uint32_t which); + + std::optional IEatBumpmapLayers( uint32_t& layer ); void IBuildLayerTexture(MTL::RenderCommandEncoder* encoder, const uint32_t offsetFromRootLayer, plLayerInterface* layer); void EncodeTransform(const plLayerInterface* layer, UVOutDescriptor *transform); std::vector> fPasses; + std::vector> fBumps; std::vector fFragmentShaderDescriptions; }; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index 77cccab029..ff3bca7def 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -1639,11 +1639,11 @@ bool plMetalPipeline::IHandleMaterialPass(hsGMaterial* material, uint32_t pass, lay = IPopOverAllLayer(lay); lay = IPopOverBaseLayer(lay); - if (numActivePiggyBacks == 0 && fOverBaseLayer == nullptr && fOverAllLayer == nullptr) { - mRef->FastEncodeArguments(fDevice.CurrentRenderCommandEncoder(), fCurrentRenderPassUniforms, pass); + //if (numActivePiggyBacks == 0 && fOverBaseLayer == nullptr && fOverAllLayer == nullptr) { + // mRef->FastEncodeArguments(fDevice.CurrentRenderCommandEncoder(), fCurrentRenderPassUniforms, pass); - fragmentShaderDescription = mRef->GetFragmentShaderDescription(pass); - } else { + // fragmentShaderDescription = mRef->GetFragmentShaderDescription(pass); + //} else { // Plasma pulls piggybacks from the rear first, pull the number of active piggybacks auto firstPiggyback = fPiggyBackStack.end() - numActivePiggyBacks; auto lastPiggyback = fPiggyBackStack.end(); @@ -1673,9 +1673,14 @@ bool plMetalPipeline::IHandleMaterialPass(hsGMaterial* material, uint32_t pass, &subPiggybacks, preEncodeTransform, postEncodeTransform); + //} + + if(!fragmentShaderDescription.fUsePerPixelLighting) + { + fragmentShaderDescription.fUsePerPixelLighting = PLASMA_FORCE_PER_PIXEL_LIGHTING; } - fLightingPerPixel = fragmentShaderDescription.fUsePerPixelLighting = PLASMA_FORCE_PER_PIXEL_LIGHTING; + fLightingPerPixel = fragmentShaderDescription.fUsePerPixelLighting; plMetalDevice::plMetalLinkedPipeline* linkedPipeline = plMetalMaterialPassPipelineState(&fDevice, vRef, fragmentShaderDescription).GetRenderPipelineState(); const MTL::RenderPipelineState* pipelineState = linkedPipeline->pipelineState; @@ -1694,8 +1699,8 @@ void plMetalPipeline::IBindLights() size_t lightSize = offsetof(plMetalLights, lampSources) + (sizeof(plMetalShaderLightSource) * fLights.count); // FIXME: These states should support dirtying instead of expense memcmps - if ( !(fState.fBoundLights.has_value() && fState.fBoundLights == fLights) ) { - fState.fBoundLights = fLights; + if ( !(fState.fBoundLights.has_value() && fState.fBoundLights == fLights) || fLightingPerPixel ) { + fState.fBoundLights.reset(); if (fLightingPerPixel) { fDevice.CurrentRenderCommandEncoder()->setFragmentBytes(&fLights, sizeof(plMetalLights), FragmentShaderArgumentLights); } else { @@ -1703,8 +1708,8 @@ void plMetalPipeline::IBindLights() } } - if ( !(fState.fBoundMaterialProperties.has_value() && fState.fBoundMaterialProperties == fCurrentRenderPassMaterialLighting) ) { - fState.fBoundMaterialProperties = fCurrentRenderPassMaterialLighting; + if ( !(fState.fBoundMaterialProperties.has_value() && fState.fBoundMaterialProperties == fCurrentRenderPassMaterialLighting) || fLightingPerPixel ) { + fState.fBoundMaterialProperties.reset(); fDevice.CurrentRenderCommandEncoder()->setVertexBytes(&fDevice.fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), VertexShaderArgumentMaterialLighting); if (fLightingPerPixel) { fDevice.CurrentRenderCommandEncoder()->setFragmentBytes(&fDevice.fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), FragmentShaderArgumentMaterialLighting); @@ -3938,6 +3943,8 @@ void plMetalPipeline::ISetShadowLightState(hsGMaterial* mat) fCurrentRenderPassMaterialLighting.specularSrc = 0.0f; fCurrentRenderPassMaterialLighting.ambientSrc = 0.0f; fCurrentRenderPassMaterialLighting.globalAmb = 0.0f; + + IBindLights(); } // IDisableLightsForShadow /////////////////////////////////////////////////////////// @@ -4426,4 +4433,6 @@ void plMetalPipeline::plMetalPipelineCurrentState::Reset() for (auto& layer : layerStates) { layer.clampFlag = hsGMatState::hsGMatClampFlags(-1); } + + memset(fTextures, 0, sizeof(fTextures)); } diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h index 99051c1296..cdfced618f 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h @@ -48,6 +48,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "ShaderTypes.h" #include "plMetalDevice.h" +#include "pfMetalPipeline/plMetalPipelineState.h" #include "plPipeline/hsG3DDeviceSelector.h" #include "plPipeline/pl3DPipeline.h" @@ -293,6 +294,7 @@ class plMetalPipeline : public pl3DPipeline std::optional fBoundLights; std::optional fBoundMaterialProperties; std::optional fCurrentVertexUniforms; + MTL::Texture* fTextures[16]; void Reset(); diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp index 037a50ff66..a4e99b7afb 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp @@ -112,6 +112,7 @@ void plMetalMaterialPassPipelineState::GetFunctionConstants(MTL::FunctionConstan constants->setConstantValues(&fFragmentShaderDescription.fBlendModes, MTL::DataTypeUInt, NS::Range(FunctionConstantBlendModes, 8)); constants->setConstantValues(&fFragmentShaderDescription.fMiscFlags, MTL::DataTypeUInt, NS::Range(FunctionConstantLayerFlags, 8)); constants->setConstantValue(&fFragmentShaderDescription.fUsePerPixelLighting, MTL::DataTypeBool, FunctionConstantPerPixelLighting); + constants->setConstantValue(&fFragmentShaderDescription.fHasBumpMap, MTL::DataTypeBool, FunctionConstantPerPixelBumpMap); } size_t plMetalMaterialPassPipelineState::GetHash() const diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h index 61ce9d7a51..1b344290af 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h @@ -111,6 +111,13 @@ class plMetalPipelineState //MARK: Abstract FVF vertex shader program parent type +struct plMetalBumpMapping +{ + uint8_t dTangentUIndex; + uint8_t dTangentVIndex; + uint8_t bumpMapTextureIndex; +}; + class plMetalRenderSpanPipelineState : public plMetalPipelineState { public: @@ -152,12 +159,13 @@ struct plMetalFragmentShaderDescription uint32_t fMiscFlags[8]; uint8_t fNumLayers; bool fUsePerPixelLighting; + bool fHasBumpMap; size_t hash; bool operator==(const plMetalFragmentShaderDescription& p) const { - bool match = fNumLayers == p.fNumLayers && memcmp(fPassTypes, p.fPassTypes, sizeof(fPassTypes)) == 0 && memcmp(fBlendModes, p.fBlendModes, sizeof(fBlendModes)) == 0 && memcmp(fMiscFlags, p.fMiscFlags, sizeof(fMiscFlags)) == 0 && fUsePerPixelLighting == p.fUsePerPixelLighting; + bool match = fNumLayers == p.fNumLayers && memcmp(fPassTypes, p.fPassTypes, sizeof(fPassTypes)) == 0 && memcmp(fBlendModes, p.fBlendModes, sizeof(fBlendModes)) == 0 && memcmp(fMiscFlags, p.fMiscFlags, sizeof(fMiscFlags)) == 0 && fUsePerPixelLighting == p.fUsePerPixelLighting && fHasBumpMap == p.fHasBumpMap; return match; } @@ -176,6 +184,7 @@ struct plMetalFragmentShaderDescription value ^= std::hash()(fNumLayers); value ^= std::hash()(fUsePerPixelLighting); + value ^= std::hash()(fHasBumpMap); for (int i = 0; i < 8; i++) { value ^= std::hash()(fBlendModes[i]);