/*============================================================================= LocalVertexFactory.hlsl: Local vertex factory shader code. Copyright 1998-2010 Epic Games, Inc. All Rights Reserved. =============================================================================*/ #include "DecalFactoryCommon.usf" // In SM5, Vertex color can coexist with TANGENTTOWORLD0 and TANGENTTOWORLD2 // Other platforms don't have enough interpolators #define ALLOW_VERTEX_COLOR (!WORLD_COORDS || SM5_PROFILE) float4x4 LocalToWorld; float4x4 PreviousLocalToWorld; float LocalToWorldRotDeterminantFlip; float3x3 WorldToLocal; float4 LightmapCoordinateScaleBias; float4 ShadowmapCoordinateScaleBias; #if USE_INSTANCING float4 InstancedViewTranslation; #endif #if USE_SPLINEDEFORM float4 SplineStartPos; float4 SplineStartTangent; float SplineStartRoll; float2 SplineStartScale; float2 SplineStartOffset; float4 SplineEndPos; float4 SplineEndTangent; float SplineEndRoll; float2 SplineEndScale; float2 SplineEndOffset; float4 SplineXDir; float SmoothInterpRollScale; // Should be a bool, but bool params don't work in vertex shaders yet (TTP 125134) float MeshMinZ; float MeshRangeZ; #endif #if XBOXINSTANCING // Instanced rendering parameters (x=NumVerticesPerInstance, y=1/NumVerticesPerInstance, z=undefined, w=undefined) float4 InstancingParameters; #endif // ME3START - Nov-01-2011 - Peter Woytiuk - Corrected user-specified vertex colours #if NEEDS_LIGHTMAP_COORDINATE #if MATERIAL_USES_VERTEX_COLOR && WORLD_COORDS && !PER_PIXEL_CAMERA_VECTOR // TEXCOORD2 #if NEEDS_BASEPASS_FOGGING #if (ENABLE_DIRECTIONAL_LIGHT || ENABLE_MULTITYPE_BASEPASS_LIGHT) && !MATERIAL_LIGHTINGMODEL_UNLIT // TEXCOORD3 #if NUM_MATERIAL_TEXCOORDS > 4 #error "[UV Error 1] Too many texcoords in use for the shader options enabled" #endif #else #if NUM_MATERIAL_TEXCOORDS > 4 #error "[UV Error 2] Too many texcoords in use for the shader options enabled" #endif #endif #endif #endif #endif #if ( VERTEX_LIGHTMAP || SIMPLE_VERTEX_LIGHTMAP ) // TEXCOORD2 #if MATERIAL_USES_VERTEX_COLOR && WORLD_COORDS && !PER_PIXEL_CAMERA_VECTOR // TEXCOORD1 #if NUM_MATERIAL_TEXCOORDS > 4 #error "[UV Error 3] Too many texcoords in use for the shader options enabled" #endif #else #if NUM_MATERIAL_TEXCOORDS > 4 #error "[UV Error 4] Too many texcoords in use for the shader options enabled" #endif #endif #endif // ME3END - Nov-01-2011 - Peter Woytiuk - Corrected user-specified vertex colours struct FVertexFactoryInput { #if XBOXINSTANCING int VertexIndex : INDEX; #else float4 Position : POSITION; half3 TangentX : TANGENT; // TangentZ.w contains sign of tangent basis determinant half4 TangentZ : NORMAL; // Normally COLOR1 is used to store user-defined per-vertex color and alpha // When instancing is enabled, we use COLOR1 for InstanceShadowMapBias (see below.) #if !USE_INSTANCING half4 Color : COLOR1; #endif #if NUM_MATERIAL_TEXCOORDS && !(DECAL_FACTORY && MATERIAL_DECAL) float2 TexCoords[NUM_MATERIAL_TEXCOORDS] : TEXCOORD0; #endif #if USE_INSTANCING float4 InstanceShadowMapBias : COLOR1; float4 InstanceTransform1 : TEXCOORD4; float4 InstanceTransform2 : TEXCOORD5; float4 InstanceTransform3 : TEXCOORD6; float4 InstanceInverseTransform1 : TEXCOORD7; float4 InstanceInverseTransform2 : BLENDINDICES0; float4 InstanceInverseTransform3 : BINORMAL; // note, the per-instance lightmap bias is stored in the W of InverseTransform1 and 2 // BIOSTART - August 25, 2010 - Wihlidal, Graham - PS3 Fragment and Vertex Shader Optimizations #elif NEEDS_VERTEX_LIGHTMAP half4 LightMapA : TEXCOORD5; half4 LightMapB : TEXCOORD6; #elif NEEDS_SIMPLE_VERTEX_LIGHTMAP half4 LightMapA : TEXCOORD5; #endif // BIOEND #if NEEDS_LIGHTMAP_COORDINATE float2 LightMapCoordinate : COLOR; #endif #endif }; struct FPositionOnlyVertexFactoryInput { #if XBOXINSTANCING int VertexIndex : INDEX; #else float4 Position : POSITION; #if USE_INSTANCING float4 InstanceTransform1 : TEXCOORD4; float4 InstanceTransform2 : TEXCOORD5; float4 InstanceTransform3 : TEXCOORD6; #endif #endif }; struct FVertexFactoryInterpolants { #if WORLD_COORDS // First row of the tangent to world matrix float3 TangentToWorld0 : TANGENTTOWORLD0; // Last row of the tangent to world matrix in xyz float4 TangentToWorld2 : TANGENTTOWORLD2; #endif #if ALLOW_VERTEX_COLOR // NOTE: When USE_INSTANCING is 1, the Color interpolant is filled with PerInstanceRandom instead of VertexColor float4 Color : COLOR0; #endif #if NEEDS_LIGHTMAP_COORDINATE // ME3START - Nov-01-2011 - Peter Woytiuk - Corrected user-specified vertex colours #if ( USE_INSTANCING && NEEDS_SHADOWMAP_COORDINATE ) float4 LightMapCoordinate : TEXCOORD0; #else float2 LightMapCoordinate : TEXCOORD0; #endif #if NUM_MATERIAL_TEXCOORDS // #if MATERIAL_USES_VERTEX_COLOR && WORLD_COORDS && !PER_PIXEL_CAMERA_VECTOR // float4 Color : TEXCOORD1; // float4 TexCoords[(NUM_MATERIAL_TEXCOORDS+1)/2] : TEXCOORD2; // #else float4 TexCoords[(NUM_MATERIAL_TEXCOORDS+1)/2] : TEXCOORD1; // #endif // #else // #if MATERIAL_USES_VERTEX_COLOR && WORLD_COORDS && !PER_PIXEL_CAMERA_VECTOR // float4 Color : TEXCOORD1; // #endif #endif #else #if NUM_MATERIAL_TEXCOORDS // #if MATERIAL_USES_VERTEX_COLOR && WORLD_COORDS && !PER_PIXEL_CAMERA_VECTOR // float4 Color : TEXCOORD0; // float4 TexCoords[(NUM_MATERIAL_TEXCOORDS+1)/2] : TEXCOORD1; // #else float4 TexCoords[(NUM_MATERIAL_TEXCOORDS+1)/2] : TEXCOORD0; // #endif // #else // #if MATERIAL_USES_VERTEX_COLOR && WORLD_COORDS && !PER_PIXEL_CAMERA_VECTOR // float4 Color : TEXCOORD0; // #endif #endif // ME3END #endif #if !COMPILER_SUPPORTS_EMPTY_STRUCTS && !WORLD_COORDS && !NEEDS_LIGHTMAP_COORDINATE && !NUM_MATERIAL_TEXCOORDS float4 Dummy : TEXCOORD0; #endif }; struct FVertexFactoryIntermediates { #if XBOXINSTANCING float4 Position; half3 TangentX; // TangentZ.w contains sign of tangent basis determinant half4 TangentZ; #if NUM_MATERIAL_TEXCOORDS && !(DECAL_FACTORY && MATERIAL_DECAL) float2 TexCoords[NUM_MATERIAL_TEXCOORDS]; #endif float4 InstanceShadowMapBias; float4 InstanceTransform1; float4 InstanceTransform2; float4 InstanceTransform3; float4 InstanceInverseTransform1; float4 InstanceInverseTransform2; float4 InstanceInverseTransform3; // note, the per-instance lightmap bias is stored in the W of InverseTransform1 and 2 #if NEEDS_LIGHTMAP_COORDINATE float2 LightMapCoordinate; #endif #else float Dummy; #endif }; /** Converts from vertex factory specific interpolants to a FMaterialPixelParameters, which is used by material inputs. */ FMaterialPixelParameters GetMaterialPixelParameters(FVertexFactoryInterpolants Interpolants) { FMaterialPixelParameters Result; #if NUM_MATERIAL_TEXCOORDS UNROLL for(int CoordinateIndex = 0;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex += 2) { Result.TexCoords[CoordinateIndex] = Interpolants.TexCoords[CoordinateIndex/2].xy; if(CoordinateIndex + 1 < NUM_MATERIAL_TEXCOORDS) { Result.TexCoords[CoordinateIndex + 1] = Interpolants.TexCoords[CoordinateIndex/2].wz; } } #endif #if WORLD_COORDS half3 TangentToWorld0 = UnpackColor3( Interpolants.TangentToWorld0 ); half4 TangentToWorld2 = UnpackColor4( Interpolants.TangentToWorld2 ); Result.UnMirrored = TangentToWorld2.w; #endif #if ALLOW_VERTEX_COLOR // NOTE: When USE_INSTANCING is 1, the Color.a interpolant is filled with the per-instance fade factor instead of VertexColor alpha Result.VertexColor = Interpolants.Color; #else Result.VertexColor = 1; #endif Result.TangentNormal = 0; Result.TangentCameraVector = 0; Result.TangentReflectionVector = 0; Result.ScreenPosition = 0; Result.TangentLightVector = 0; #if WORLD_COORDS // BW_START - Leonardo Benaducci - December 15, 2020 - Fix Decal normals #if DECAL_FACTORY Result.TangentToWorld = CalcInvTangentBasisDecal( TangentToWorld0 ); #else Result.TangentToWorld = CalcTangentToWorld( TangentToWorld0, TangentToWorld2 ); #endif // BW_END #endif #if MATERIAL_DECAL #if DECAL_FACTORY && NUM_MATERIAL_TEXCOORDS > 1 Result.DecalAttenuation = Interpolants.TexCoords[0].z; Result.DecalPlaneDistance = Interpolants.TexCoords[0].w; #else Result.DecalAttenuation = 1; Result.DecalPlaneDistance = 0; #endif #endif #if LIGHTMAP_UV_ACCESS #if NEEDS_LIGHTMAP_COORDINATE //TEXTURE_LIGHTMAP || SIMPLE_TEXTURE_LIGHTMAP Result.LightmapUVs = Interpolants.LightMapCoordinate.xy; #else Result.LightmapUVs = float2(0, 0); #endif #endif Result.TwoSidedSign = 1; // ME3START, MAK, January 24/11, Implement wounds #if NUM_WOUNDS >= 1 // Wounds are only supported on skelmeshes. // Provide a dummy value so local vertex factory will compile. Result.WoundParameters0 = float4(1,0,0,1); #if NUM_WOUNDS >= 2 Result.WoundParameters1 = float4(1,0,0,1); #endif #endif // ME3END return Result; } // When we're using instancing on Xbox, the vertex attributes are in the FVertexFactoryIntermediates, rather than the VertexFactoryInput struct. #if XBOXINSTANCING #define VertexAttributes Intermediates #else #define VertexAttributes Input #endif float4 VertexFactoryGetVertexColor(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { // NOTE: When USE_INSTANCING is 1, the Color interpolant is filled with PerInstanceRandom instead of VertexColor #if USE_INSTANCING return VertexAttributes.InstanceShadowMapBias.zzzz; #else return VertexAttributes.Color FCOLOR_COMPONENT_SWIZZLE; #endif } /** Converts from vertex factory specific input to a FMaterialVertexParameters, which is used by vertex shader material inputs. */ FMaterialVertexParameters GetMaterialVertexParameters( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 WorldPosition, float3x3 TangentBasis ) { FMaterialVertexParameters Result; Result.WorldPosition = WorldPosition - PreViewTranslation.xyz; Result.WorldNormal = TangentBasis[2]; Result.VertexColor = VertexFactoryGetVertexColor(Input, Intermediates); return Result; } #if NEEDS_LIGHTMAP_COORDINATE float2 GetLightMapCoordinate(FVertexFactoryInterpolants Interpolants) { return Interpolants.LightMapCoordinate.xy; } #if NEEDS_SHADOWMAP_COORDINATE float2 GetShadowMapCoordinate(FVertexFactoryInterpolants Interpolants) { #if USE_INSTANCING // When using instancing and rendering a shadow map along with a light map in a single pass, the shadow // map coordinate is stored in the .zw components of the LightMapCoordinate interpolator return Interpolants.LightMapCoordinate.zw; #else return Interpolants.LightMapCoordinate.xy * ShadowmapCoordinateScaleBias.xy + ShadowmapCoordinateScaleBias.zw; #endif } #endif #endif #if NEEDS_VERTEX_LIGHTMAP // BIOSTART - August 25, 2010 - Wihlidal, Graham - PS3 Fragment and Vertex Shader Optimizations void VertexFactoryGetVertexLightMap(FVertexFactoryInput Input,out half4 LightMapA,out half4 LightMapB) // BIOEND { LightMapA = Input.LightMapA; LightMapB = Input.LightMapB; } #elif NEEDS_SIMPLE_VERTEX_LIGHTMAP // BIOSTART - August 25, 2010 - Wihlidal, Graham - PS3 Fragment and Vertex Shader Optimizations void VertexFactoryGetSimpleVertexLightMap(FVertexFactoryInput Input,out half4 LightMapA) // BIOEND { LightMapA = Input.LightMapA; } #endif #if USE_SPLINEDEFORM float3 SplineEvalPos(float3 StartPos, float3 StartTangent, float3 EndPos, float3 EndTangent, float A) { float A2 = A * A; float A3 = A2 * A; return (((2*A3)-(3*A2)+1) * StartPos) + ((A3-(2*A2)+A) * StartTangent) + ((A3-A2) * EndTangent) + (((-2*A3)+(3*A2)) * EndPos); } float3 SplineEvalDir(float3 StartPos, float3 StartTangent, float3 EndPos, float3 EndTangent, float A) { float3 C = (6*StartPos) + (3*StartTangent) + (3*EndTangent) - (6*EndPos); float3 D = (-6*StartPos) - (4*StartTangent) - (2*EndTangent) + (6*EndPos); float3 E = StartTangent; float A2 = A * A; return normalize((C * A2) + (D * A) + E); } #endif #if USE_SPLINEDEFORM /** Calculate full transform that defines frame along spline, given the Z of a vertex. */ float4x4 CalcSliceTransform(float ZPos) { // Find how far 'along' mesh we are float Alpha = (ZPos - MeshMinZ)/MeshRangeZ; // Apply hermite interp to Alpha if desired float HermiteAlpha = lerp(Alpha, smoothstep(0.0, 1.0, Alpha), SmoothInterpRollScale); // Then find the point and direction of the spline at this point along float3 SplinePos = SplineEvalPos( SplineStartPos.xyz, SplineStartTangent.xyz, SplineEndPos.xyz, SplineEndTangent.xyz, Alpha ); float3 SplineDir = SplineEvalDir( SplineStartPos.xyz, SplineStartTangent.xyz, SplineEndPos.xyz, SplineEndTangent.xyz, Alpha ); // Find base frenet frame float3 BaseYVec = normalize( cross(SplineDir, SplineXDir) ); float3 BaseXVec = normalize( cross(BaseYVec, SplineDir) ); // Offset from the spline, using the frenet frame float2 SliceOffset = lerp(SplineStartOffset, SplineEndOffset, HermiteAlpha); SplinePos += SliceOffset.x * BaseXVec; SplinePos += SliceOffset.y * BaseYVec; // Apply roll to frame around spline float UseRoll = lerp(SplineStartRoll, SplineEndRoll, HermiteAlpha); float CosAng = cos(UseRoll); float SinAng = sin(UseRoll); float3 XVec = (CosAng * BaseXVec) - (SinAng * BaseYVec); float3 YVec = (CosAng * BaseYVec) + (SinAng * BaseXVec); // Find scale at this point along spline float2 UseScale = lerp(SplineStartScale, SplineEndScale, HermiteAlpha); // Build overall transform float4x4 SliceTransform = float4x4(UseScale.x*float4(XVec,0), UseScale.y*float4(YVec,0), float4(SplineDir,0), float4(SplinePos,1)); return SliceTransform; } /** Calculate rotation matrix that defines frame along spline, given the Z of a vertex. */ float3x3 CalcSliceRot(float ZPos) { // Find how far 'along' mesh we are float Alpha = (ZPos - MeshMinZ)/MeshRangeZ; // Apply hermite interp to Alpha if desired float HermiteAlpha = lerp(Alpha, smoothstep(0.0, 1.0, Alpha), SmoothInterpRollScale); // Then find the point and direction of the spline at this point along float3 SplineDir = SplineEvalDir( SplineStartPos.xyz, SplineStartTangent.xyz, SplineEndPos.xyz, SplineEndTangent.xyz, Alpha ); // Find base frenet frame float3 BaseYVec = normalize( cross(SplineDir, SplineXDir) ); float3 BaseXVec = normalize( cross(BaseYVec, SplineDir) ); // Apply roll to frame around spline float UseRoll = lerp(SplineStartRoll, SplineEndRoll, HermiteAlpha); float CosAng = cos(UseRoll); float SinAng = sin(UseRoll); float3 XVec = (CosAng * BaseXVec) - (SinAng * BaseYVec); float3 YVec = (CosAng * BaseYVec) + (SinAng * BaseXVec); // Build rotation transform float3x3 SliceTransform = float3x3(XVec, YVec, SplineDir); return SliceTransform; } #endif #if USE_INSTANCING float4 CalcWorldPosition(float4 Position, float4 InstanceTransform1, float4 InstanceTransform2, float4 InstanceTransform3) #else float4 CalcWorldPosition(float4 Position) #endif { #if USE_INSTANCING float4x4 InstanceTransform = float4x4(InstanceTransform1, InstanceTransform2, InstanceTransform3, float4(0, 0, 0, 1)); return mul(InstanceTransform, Position) + InstancedViewTranslation; #elif USE_SPLINEDEFORM // Make transform for this point along spline float4x4 SliceTransform = CalcSliceTransform(Position.z); // Remove Z (transform will move us along spline) float4 SlicePos = Position; SlicePos.z = 0; // Transform into mesh space float4 LocalPos = mul(SlicePos, SliceTransform); // Transform from mesh to world space return MulMatrix(LocalToWorld, LocalPos); #else return MulMatrix(LocalToWorld,Position); #endif } #define VFETCH_TEXCOORD( Index ) \ asm \ { \ vfetch TexCoord, VertexIndex, texcoord##Index \ }; \ Intermediates.TexCoords[##Index] = TexCoord.xy; FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input) { FVertexFactoryIntermediates Intermediates; #if XBOXINSTANCING // Add 0.5 to the vertex index to avoid rounding errors. int InstanceIndex = (Input.VertexIndex + 0.5f) * InstancingParameters.y; int VertexIndex = Input.VertexIndex - InstancingParameters.x * InstanceIndex; float4 Pos, TangentX, TangentZ; float4 InstanceShadowMapBias; float4 InstanceTransform1, InstanceTransform2, InstanceTransform3; float4 InstanceInverseTransform1, InstanceInverseTransform2, InstanceInverseTransform3; asm { vfetch Pos, VertexIndex, position vfetch TangentX, VertexIndex, tangent vfetch TangentZ, VertexIndex, normal vfetch InstanceShadowMapBias, InstanceIndex, color1 vfetch InstanceTransform1, InstanceIndex, texcoord4 vfetch InstanceTransform2, InstanceIndex, texcoord5 vfetch InstanceTransform3, InstanceIndex, texcoord6 vfetch InstanceInverseTransform1, InstanceIndex, texcoord7 vfetch InstanceInverseTransform2, InstanceIndex, blendindices0 vfetch InstanceInverseTransform3, InstanceIndex, binormal }; #if NEEDS_LIGHTMAP_COORDINATE float4 LightMapCoordinate; asm { vfetch LightMapCoordinate, VertexIndex, color0 }; Intermediates.LightMapCoordinate = LightMapCoordinate.xy; #endif float4 TexCoord; #if !(DECAL_FACTORY && MATERIAL_DECAL) && NUM_MATERIAL_TEXCOORDS > 0 VFETCH_TEXCOORD( 0 ) #endif #if !(DECAL_FACTORY && MATERIAL_DECAL) && NUM_MATERIAL_TEXCOORDS > 1 VFETCH_TEXCOORD( 1 ) #endif #if !(DECAL_FACTORY && MATERIAL_DECAL) && NUM_MATERIAL_TEXCOORDS > 2 VFETCH_TEXCOORD( 2 ) #endif #if !(DECAL_FACTORY && MATERIAL_DECAL) && NUM_MATERIAL_TEXCOORDS > 3 VFETCH_TEXCOORD( 3 ) #endif #if !(DECAL_FACTORY && MATERIAL_DECAL) && NUM_MATERIAL_TEXCOORDS > 4 VFETCH_TEXCOORD( 4 ) #endif Intermediates.Position = Pos; Intermediates.TangentX = TangentX; Intermediates.TangentZ = TangentZ; Intermediates.InstanceShadowMapBias = InstanceShadowMapBias; Intermediates.InstanceTransform1 = InstanceTransform1; Intermediates.InstanceTransform2 = InstanceTransform2; Intermediates.InstanceTransform3 = InstanceTransform3; Intermediates.InstanceInverseTransform1 = InstanceInverseTransform1; Intermediates.InstanceInverseTransform2 = InstanceInverseTransform2; Intermediates.InstanceInverseTransform3 = InstanceInverseTransform3; #else Intermediates.Dummy = 0; #endif return Intermediates; } /** * Get the 3x3 tangent basis vectors for this vertex factory * this vertex factory will calculate the binormal on-the-fly * * @param Input - vertex input stream structure * @return 3x3 matrix */ float3x3 VertexFactoryGetTangentBasis( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates ) { float3x3 Result=0; float4 TangentZ = TangentBias(VertexAttributes.TangentZ); #if DECAL_FACTORY Result = ProjectDecalTangents(float3(TangentZ.x,TangentZ.y,TangentZ.z)); #else #if USE_SPLINEDEFORM // Make slice rotation matrix, and use that to transform tangents float3x3 SliceRot = CalcSliceRot(Input.Position.z); Result[0] = mul(TangentBias(Input.TangentX), SliceRot); Result[2] = mul(float3(TangentZ.x,TangentZ.y,TangentZ.z), SliceRot); #else // pass-thru the tangent Result[0] = TangentBias(VertexAttributes.TangentX); // pass-thru the normal Result[2] = float3(TangentZ.x,TangentZ.y,TangentZ.z); #endif // derive the binormal by getting the cross product of the normal and tangent Result[1] = cross(Result[2], Result[0]) * TangentZ.w; // Recalculate TangentX off of the other two vectors // This corrects quantization error since TangentX was passed in as a quantized vertex input // The error shows up most in specular off of a mesh with a smoothed UV seam (normal is smooth, but tangents vary across the seam) Result[0] = cross(Result[1], Result[2]) * TangentZ.w; #endif return Result; } float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { #if USE_INSTANCING return CalcWorldPosition(VertexAttributes.Position, VertexAttributes.InstanceTransform1, VertexAttributes.InstanceTransform2, VertexAttributes.InstanceTransform3); #else return CalcWorldPosition(Input.Position); #endif } FVertexFactoryInterpolants VertexFactoryGetInterpolants(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { FVertexFactoryInterpolants Interpolants; // BW_START - Yefima, Matthew - 2020/6/6 - Initialize the interpolant color to all 0s since the pixel shader expects to use // the value in some cases that don't match up with the defines that grab it below, and XB1 renders with garbage values if // we don't fill every semantic output slot for the next shader stage. This value is filled with all 0s rather than using // a 1 for alpha as this was the behavior in RenderDoc for DX11 PC captures that did not manually assign anything to // the COLOR0 output slot in the DXBC assembly #if ALLOW_VERTEX_COLOR Interpolants.Color = float4(0,0,0,0); #endif // BW_END #if NUM_MATERIAL_TEXCOORDS // Ensure the unused components of the last packed texture coordinate are initialized. Interpolants.TexCoords[(NUM_MATERIAL_TEXCOORDS + 1) / 2 - 1] = 0; #if MATERIAL_DECAL #if DECAL_FACTORY // first uv pair are the projected decal coords // BW_START - Yefima, Matthew - 2020/1/7 - Orbis thinks this function is a Geometry Shader //Interpolants.TexCoords[0].xy = ComputeDecalTexCoord(VertexAttributes.Position); float2 DecalTexCoord = MulMatrix(DecalMatrix, VertexAttributes.Position-float4(DecalLocation,1)).xy; Interpolants.TexCoords[0].xy = float2(-DecalTexCoord.x+0.5+DecalOffset.x, -DecalTexCoord.y+0.5+DecalOffset.y ); // BW_END // next uv pair stores: z=decal attenutation w=distance of vertex to decal origin Interpolants.TexCoords[0].z = CalcDecalAttenuation(TangentBias(Input.TangentZ).xyz); Interpolants.TexCoords[0].w = CalcDistanceToDecalOrigin(VertexAttributes.Position); #else Interpolants.TexCoords[0].xy = VertexAttributes.TexCoords[0]; #endif #else UNROLL for(int CoordinateIndex = 0;CoordinateIndex < NUM_MATERIAL_TEXCOORDS;CoordinateIndex += 2) { Interpolants.TexCoords[CoordinateIndex / 2].xy = VertexAttributes.TexCoords[CoordinateIndex]; if(CoordinateIndex + 1 < NUM_MATERIAL_TEXCOORDS) { Interpolants.TexCoords[CoordinateIndex / 2].wz = VertexAttributes.TexCoords[CoordinateIndex + 1]; } } #endif #endif #if NEEDS_LIGHTMAP_COORDINATE #if USE_INSTANCING float2 InstanceLightMapBias = float2(VertexAttributes.InstanceInverseTransform1.w, VertexAttributes.InstanceInverseTransform2.w); // Are we using shadow maps? #if STATICLIGHTING_TEXTUREMASK // Drawing light+shadow in a single pass? (bass pass) #if NEEDS_SHADOWMAP_COORDINATE // The vertex shader copies this instance's shadow map coordinate bias into the zw component of LightMapCoordinate. // We can't use the ShadowmapCoordinateScaleBias parameter's shadow map bias as this isn't instance-aware. // Single-pass shadow mapping needs both light map and shadow map coordinates float2 InstanceShadowMapBias = VertexAttributes.InstanceShadowMapBias.xy; Interpolants.LightMapCoordinate.xy = VertexAttributes.LightMapCoordinate * LightmapCoordinateScaleBias.xy + InstanceLightMapBias; Interpolants.LightMapCoordinate.zw = VertexAttributes.LightMapCoordinate * ShadowmapCoordinateScaleBias.xy + InstanceShadowMapBias; // Drawing a lighting pass only? #else // Shadowed lighting passes pass shadow map scale/bias in the light map coordinate and parameters, but we still need // the per-instance shadow map bias from the vertex float2 InstanceShadowMapBias = VertexAttributes.InstanceShadowMapBias.xy; Interpolants.LightMapCoordinate.xy = VertexAttributes.LightMapCoordinate * LightmapCoordinateScaleBias.xy + InstanceShadowMapBias; #endif #else // Regular light map pass Interpolants.LightMapCoordinate.xy = VertexAttributes.LightMapCoordinate * LightmapCoordinateScaleBias.xy + InstanceLightMapBias; #endif #else Interpolants.LightMapCoordinate.xy = VertexAttributes.LightMapCoordinate * LightmapCoordinateScaleBias.xy + LightmapCoordinateScaleBias.wz; #endif #endif #if WORLD_COORDS #if DECAL_FACTORY // pass 2x2 matrix for transforming normals as interpolant float3 TangentXBiased = TangentBias(VertexAttributes.TangentX); float4 TangentZBiased = TangentBias(VertexAttributes.TangentZ); // pass through the vertex normal (note the misnomer) Interpolants.TangentToWorld0 = TangentZBiased.xyz; Interpolants.TangentToWorld2 = CalcDecal2x2NormalTransform(TangentXBiased, TangentZBiased.xyz, TangentZBiased.w); #else #if SM2_PROFILE // In SM2 these vectors will store the tangent basis. PackColorN scales them to [0..1] to fit in the COLOR0 and COLOR1 interpolators. // We cannot combine the tangent to local and local to world due to insufficient temporary registers in some vertex factories (GPU skin) Interpolants.TangentToWorld0 = TangentNorm(VertexAttributes.TangentX)*2.0f - 1.0f; Interpolants.TangentToWorld2 = TangentNorm(VertexAttributes.TangentZ)*2.0f - 1.0f; #else float3x3 LocalToTangent = VertexFactoryGetTangentBasis(Input, Intermediates); // The inverse of LocalToTangent is just the transpose because it is an orthonormal basis float3x3 TangentToLocal = transpose(LocalToTangent); #if PS3 float3x3 TangentToWorld = mul(transpose((float3x3)LocalToWorld), TangentToLocal); #else #if USE_INSTANCING float3x3 InstanceTransform = transpose(float3x3(VertexAttributes.InstanceInverseTransform1.xyz, VertexAttributes.InstanceInverseTransform2.xyz, VertexAttributes.InstanceInverseTransform3.xyz)); float3x3 TangentToWorld = mul(InstanceTransform, TangentToLocal); #else float3x3 TangentToWorld = mul((float3x3)LocalToWorld, TangentToLocal); #endif #endif Interpolants.TangentToWorld0 = TangentToWorld[0]; Interpolants.TangentToWorld2 = float4(TangentToWorld[2], TangentBias(VertexAttributes.TangentZ.w) * LocalToWorldRotDeterminantFlip); #endif #endif Interpolants.TangentToWorld0 = PackColor3( Interpolants.TangentToWorld0 ); Interpolants.TangentToWorld2 = PackColor4( Interpolants.TangentToWorld2 ); // ME3START - Nov-01-2011 - Peter Woytiuk - Corrected user-specified vertex colours #if MATERIAL_USES_VERTEX_COLOR && !PER_PIXEL_CAMERA_VECTOR Interpolants.Color = VertexFactoryGetVertexColor(Input, Intermediates); #endif // ME3END #else Interpolants.Color = VertexFactoryGetVertexColor(Input, Intermediates); #endif #if !COMPILER_SUPPORTS_EMPTY_STRUCTS && !WORLD_COORDS && !NEEDS_LIGHTMAP_COORDINATE && !NUM_MATERIAL_TEXCOORDS Interpolants.Dummy = float4(0,0,0,0); #endif return Interpolants; } /** for depth-only pass */ float4 VertexFactoryGetWorldPosition(FPositionOnlyVertexFactoryInput Input) { #if XBOXINSTANCING // Add 0.5 to the vertex index to avoid rounding errors. int InstanceIndex = (Input.VertexIndex + 0.5f) * InstancingParameters.y; int VertexIndex = Input.VertexIndex - InstancingParameters.x * InstanceIndex; float4 Pos; float4 InstanceTransform1, InstanceTransform2, InstanceTransform3; asm { vfetch Pos, VertexIndex, position vfetch InstanceTransform1, InstanceIndex, texcoord4 vfetch InstanceTransform2, InstanceIndex, texcoord5 vfetch InstanceTransform3, InstanceIndex, texcoord6 }; return CalcWorldPosition( Pos, InstanceTransform1, InstanceTransform2, InstanceTransform3 ); #elif USE_INSTANCING return CalcWorldPosition(Input.Position, Input.InstanceTransform1, Input.InstanceTransform2, Input.InstanceTransform3); #else return CalcWorldPosition(Input.Position); #endif } float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { #if USE_INSTANCING // @todo: THIS IS SO VERY WRONG, WE DON'T HAVE PRECALCED PreviousLocalToWorld for instance sigh float4x4 InstanceTransform = float4x4(VertexAttributes.InstanceTransform1, VertexAttributes.InstanceTransform2, VertexAttributes.InstanceTransform3, float4(0, 0, 0, 1)); return mul(InstanceTransform, VertexAttributes.Position); #elif USE_SPLINEDEFORM // Just like CalcWorldPosition... float4x4 SliceTransform = CalcSliceTransform(Input.Position.z); float4 SlicePos = Input.Position; SlicePos.z = 0; float4 LocalPos = mul(SlicePos, SliceTransform); return MulMatrix(PreviousLocalToWorld, LocalPos); #else return MulMatrix(PreviousLocalToWorld,Input.Position); #endif } /** * Transform a vector from world space to tangent space * * @param Input - vertex input stream structure * @param TangentBasis - 3x3 matrix to transform to tangent space * @param WorldVector - vector in world space to transform * @return vector in tangent space */ float3 VertexFactoryWorldToTangentSpace( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3x3 TangentBasis, float3 WorldVector ) { // we use a straight mul here because we are generating the matrix, so we don't worry about column major vs row major (which is what MulMatrix manages per-platform) #if USE_INSTANCING float3x3 InverseTransform = float3x3(VertexAttributes.InstanceInverseTransform1.xyz, VertexAttributes.InstanceInverseTransform2.xyz, VertexAttributes.InstanceInverseTransform3.xyz); return mul(TangentBasis, mul(InverseTransform, WorldVector)); #else return mul(TangentBasis, MulMatrix(WorldToLocal,WorldVector)); #endif } half3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { half4 Normal = TangentBias(VertexAttributes.TangentZ); Normal.w = 0; #if USE_INSTANCING float4x4 InstanceTransform = float4x4(VertexAttributes.InstanceTransform1, VertexAttributes.InstanceTransform2, VertexAttributes.InstanceTransform3, float4(0, 0, 0, 1)); half4 WorldNormal = mul(InstanceTransform, Normal); #elif USE_SPLINEDEFORM // Make slice rotation matrix, and use that to transform the normal float3x3 SliceRot = CalcSliceRot(Input.Position.z); float3 LocalNormal = mul(Normal.xyz, SliceRot); half4 WorldNormal = MulMatrix( LocalToWorld, float4(LocalNormal,0) ); #else half4 WorldNormal = MulMatrix( LocalToWorld, Normal ); #endif return WorldNormal.xyz; }