Skip to content

Commit

Permalink
Initial version of Metal bump mapping
Browse files Browse the repository at this point in the history
Full of hacks
  • Loading branch information
colincornaby committed Dec 2, 2024
1 parent b13fba8 commit 379ca20
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) ]];
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<half> 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;

/*
Expand Down Expand Up @@ -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) ]])
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ enum plMetalFunctionConstant
FunctionConstantNumWeights = 26,
/// Per pixel lighting enable flag
FunctionConstantPerPixelLighting = 27,
FunctionConstantPerPixelBumpMap = 28,
};

enum plMetalLayerPassType: uint8_t
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -293,6 +302,9 @@ void plMetalMaterialShaderRef::ILoopOverLayers()

fPassIndices.push_back(currLayer);
fPassLengths.push_back(j - currLayer);

fBumps.push_back(IEatBumpmapLayers(j));

fNumPasses++;

#if 0
Expand All @@ -301,6 +313,46 @@ void plMetalMaterialShaderRef::ILoopOverLayers()
}
}

std::optional<plMetalBumpMapping> plMetalMaterialShaderRef::IEatBumpmapLayers( uint32_t& layerIdx )
{

std::optional<plMetalBumpMapping> 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;
Expand All @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,13 @@ class plMetalMaterialShaderRef : public plMetalDeviceRef
const std::function<plLayerInterface* (plLayerInterface*, uint32_t)>& postEncodeTransform);
bool ICanEatLayer(plLayerInterface* lay);
uint32_t ILayersAtOnce(uint32_t which);

std::optional<plMetalBumpMapping> IEatBumpmapLayers( uint32_t& layer );

void IBuildLayerTexture(MTL::RenderCommandEncoder* encoder, const uint32_t offsetFromRootLayer, plLayerInterface* layer);
void EncodeTransform(const plLayerInterface* layer, UVOutDescriptor *transform);
std::vector<const std::vector<plLayerInterface*>> fPasses;
std::vector<const std::optional<plMetalBumpMapping>> fBumps;
std::vector<struct plMetalFragmentShaderDescription> fFragmentShaderDescriptions;
};

Expand Down
27 changes: 18 additions & 9 deletions Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand All @@ -1694,17 +1699,17 @@ 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 {
fDevice.CurrentRenderCommandEncoder()->setVertexBytes(&fLights, sizeof(plMetalLights), VertexShaderArgumentLights);
}
}

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);
Expand Down Expand Up @@ -3938,6 +3943,8 @@ void plMetalPipeline::ISetShadowLightState(hsGMaterial* mat)
fCurrentRenderPassMaterialLighting.specularSrc = 0.0f;
fCurrentRenderPassMaterialLighting.ambientSrc = 0.0f;
fCurrentRenderPassMaterialLighting.globalAmb = 0.0f;

IBindLights();
}

// IDisableLightsForShadow ///////////////////////////////////////////////////////////
Expand Down Expand Up @@ -4426,4 +4433,6 @@ void plMetalPipeline::plMetalPipelineCurrentState::Reset()
for (auto& layer : layerStates) {
layer.clampFlag = hsGMatState::hsGMatClampFlags(-1);
}

memset(fTextures, 0, sizeof(fTextures));
}
2 changes: 2 additions & 0 deletions Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ You can contact Cyan Worlds, Inc. by email [email protected]

#include "ShaderTypes.h"
#include "plMetalDevice.h"
#include "pfMetalPipeline/plMetalPipelineState.h"
#include "plPipeline/hsG3DDeviceSelector.h"
#include "plPipeline/pl3DPipeline.h"

Expand Down Expand Up @@ -293,6 +294,7 @@ class plMetalPipeline : public pl3DPipeline<plMetalDevice>
std::optional<plMetalLights> fBoundLights;
std::optional<plMaterialLightingDescriptor> fBoundMaterialProperties;
std::optional<VertexUniforms> fCurrentVertexUniforms;
MTL::Texture* fTextures[16];

void Reset();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}

Expand All @@ -176,6 +184,7 @@ struct plMetalFragmentShaderDescription
value ^= std::hash<uint8_t>()(fNumLayers);

value ^= std::hash<bool>()(fUsePerPixelLighting);
value ^= std::hash<bool>()(fHasBumpMap);

for (int i = 0; i < 8; i++) {
value ^= std::hash<uint32_t>()(fBlendModes[i]);
Expand Down

0 comments on commit 379ca20

Please sign in to comment.