RawGPUIndexBuffer.h:
// By penguin21 and backported from UE3
#pragma once
#include "CoreMinimal.h"
/**
* FRawGPUIndexBuffer represents a basic index buffer GPU resource with
* no CPU-side data.
* The member functions are meant to be called from the renderthread only.
*/
class FRawGPUIndexBuffer : public FIndexBuffer
{
public:
/**
* Default constructor
*/
FRawGPUIndexBuffer();
/**
* Setup constructor
* @param InNumIndices - Number of indices to allocate space for
* @param InIsDynamic - TRUE if the index buffer should be dynamic
* @param InStride - Number of bytes per index
*/
FRawGPUIndexBuffer(uint32 InNumIndices, bool InIsDynamic=false, uint32 InStride=sizeof(uint16));
/**
* Sets up the index buffer, if the default constructor was used.
* @param InNumIndices - Number of indices to allocate space for
* @param InIsDynamic - TRUE if the index buffer should be dynamic
* @param InStride - Number of bytes per index
*/
void Setup(uint32 InNumIndices, bool InIsDynamic=false, uint32 InStride=sizeof(uint16));
/**
* Returns TRUE if the index buffer hasn't been filled in with data yet,
* or if it's a dynamic resource that has been re-created due to Device Lost.
* Calling Lock + Unlock will make the index buffer non-empty again.
*/
bool IsEmpty() const
{
return bIsEmpty;
}
/**
* Returns the number of indices that are allocated in the buffer.
*/
int GetNumIndices() const
{
return NumIndices;
}
/**
* Create an empty index buffer RHI resource.
*/
virtual void InitRHI(FRHICommandListBase& RHICmdList) override;
/**
* Releases a index buffer RHI resource.
* Called when the resource is released, or when reseting all RHI resources.
*/
virtual void ReleaseRHI() override;
/**
* Locks the index buffer and returns a pointer to the first index in the locked region.
*
* @param FirstIndex - First index in the locked region. Defaults to the first index in the buffer.
* @param NumIndices - Number of indices to lock. Defaults to the remainder of the buffer.
*/
void* Lock(FRHICommandListBase& RHICmdList, uint32 FirstIndex=0, uint32 NumIndices=0);
/**
* Unlocks the index buffer.
*/
void Unlock(FRHICommandListBase& RHICmdList);
protected:
uint32 NumIndices;
uint32 Stride;
uint8 bIsDynamic:1;
uint8 bIsEmpty:1;
};
RawGPUIndexBuffer.cpp:
// By penguin21 and backported from UE3
#include "RawGPUIndexBuffer.h"
/*-----------------------------------------------------------------------------
FRawGPUIndexBuffer
-----------------------------------------------------------------------------*/
/**
* Default constructor
*/
FRawGPUIndexBuffer::FRawGPUIndexBuffer()
: NumIndices(0)
, Stride(sizeof(uint16))
, bIsDynamic(false)
, bIsEmpty(true)
{
}
/**
* Setup constructor
* @param InNumIndices - Number of indices to allocate space for
* @param InIsDynamic - TRUE if the index buffer should be dynamic
* @param InStride - Number of bytes per index
*/
FRawGPUIndexBuffer::FRawGPUIndexBuffer(uint32 InNumIndices, bool InIsDynamic, uint32 InStride)
: NumIndices(InNumIndices)
, Stride(InStride)
, bIsDynamic(InIsDynamic)
, bIsEmpty(true)
{
}
/**
* Sets up the index buffer, if the default constructor was used.
* @param InNumIndices - Number of indices to allocate space for
* @param InIsDynamic - TRUE if the index buffer should be dynamic
* @param InStride - Number of bytes per index
*/
void FRawGPUIndexBuffer::Setup(uint32 InNumIndices, bool InIsDynamic, uint32 InStride)
{
check( bIsEmpty );
NumIndices = InNumIndices;
Stride = InStride;
bIsDynamic = InIsDynamic;
bIsEmpty = true;
}
/**
* Renderthread API.
* Create the index buffer RHI resource and initialize its data.
*/
void FRawGPUIndexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
{
FRHIResourceCreateInfo CreateInfo(TEXT("FRawGPUIndexBuffer"));
IndexBufferRHI = RHICmdList.CreateIndexBuffer(Stride, NumIndices * Stride, bIsDynamic ? BUF_Dynamic : BUF_Static, CreateInfo);
bIsEmpty = true;
}
/**
* Renderthread API.
* Releases a index buffer RHI resource.
* Called when the resource is released, or when reseting all RHI resources.
*/
void FRawGPUIndexBuffer::ReleaseRHI()
{
IndexBufferRHI.SafeRelease();
bIsEmpty = true;
}
/**
* Renderthread API.
* Locks the index buffer and returns a pointer to the first index in the locked region.
*
* @param FirstIndex - First index in the locked region. Defaults to the first index in the buffer.
* @param InNumIndices - Number of indices to lock. Defaults to the remainder of the buffer.
*/
void* FRawGPUIndexBuffer::Lock(FRHICommandListBase& RHICmdList, uint32 FirstIndex, uint32 InNumIndices)
{
if ( InNumIndices == 0 )
{
InNumIndices = NumIndices - FirstIndex;
}
return RHICmdList.LockBuffer(IndexBufferRHI, FirstIndex * Stride, InNumIndices * Stride, RLM_WriteOnly);
}
/**
* Renderthread API.
* Unlocks the index buffer.
*/
void FRawGPUIndexBuffer::Unlock(FRHICommandListBase& RHICmdList)
{
RHICmdList.UnlockBuffer(IndexBufferRHI);
bIsEmpty = false;
}
Here is the ShaderCode:
FluidVertexFactory.ush:
/*=============================================================================
FluidVertexFactory.ush: Local vertex factory shader code.
=============================================================================*/
#include "/Engine/Private/Common.ush"
#include "/Engine/Private/VertexFactoryCommon.ush"
#define USE_FLUIDSURFACE 1
#define ENABLE_XBOX_LEFTOVER 0
//float4x4 FluidLocalToWorld;
//float3x3 FluidWorldToLocal;
float4 GridSize; // X=TotalWidth, Y=TotalHeight, Z=TweakScale (for tangents)
float4 LightmapCoordinateScaleBias;
float4 ShadowmapCoordinateScaleBias;
#if ENABLE_XBOX_LEFTOVER
Texture2D Heightmap;
SamplerState HeightmapSampler;
float4 TessellationParameters; // X = HeightScale (for vertex height)
// Y = NumQuadsX (number of quads along X in the control mesh)
// Z = 1/NumQuadsX
// W = 1/NumQuadsY
// The following are used to tessellate the grid in 8 different direction,
// for back-to-front sorting purposes.
float4 TessellationFactors1;
float4 TessellationFactors2;
float4 TexcoordScaleBias; // Scale and bias to convert from heightmap UV to fluid UV
float4 SplineParameters;
static const float HeightScale = TessellationParameters.x;
static const float NumQuadsX = TessellationParameters.y;
static const float InvNumQuadsX = TessellationParameters.z;
#endif // ENABLE_XBOX_LEFTOVER
#define FluidSize GridSize.xy
#define TweakScale GridSize.z
struct FVertexFactoryInput
{
float Height : ATTRIBUTE0;
float2 TexCoord : ATTRIBUTE1;
float2 HeightDelta : ATTRIBUTE2;
VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK()
};
struct FVertexFactoryInterpolantsVSToPS
{
TANGENTTOWORLD_INTERPOLATOR_BLOCK
float4 TexCoord : TEXCOORD0;
float3 TangentX : TEXCOORD1;
float3 TangentY : TANGENTTOWORLD0;
float3 TangentZ : TANGENTTOWORLD2;
#if INSTANCED_STEREO
nointerpolation uint EyeIndex : PACKED_EYE_INDEX;
#endif
};
struct FVertexFactoryIntermediates
{
float Dummy;
//float3 InvNonUniformScale;
//float DeterminantSign;
FDFMatrix LocalToWorld;
FDFInverseMatrix WorldToLocal;
FDFMatrix PrevLocalToWorld;
/** Cached primitive and instance data */
FSceneDataIntermediates SceneData;
};
FSceneDataIntermediates GetSceneDataIntermediates(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.SceneData;
}
uint VertexFactoryGetViewIndex(FVertexFactoryIntermediates Intermediates)
{
return GetSceneDataIntermediates(Intermediates).ViewIndex;
}
uint VertexFactoryGetInstanceIdLoadIndex(FVertexFactoryIntermediates Intermediates)
{
return GetSceneDataIntermediates(Intermediates).InstanceIdLoadIndex;
}
FInstanceSceneData GetInstanceData(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.SceneData.InstanceData;
}
FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.SceneData.Primitive;
}
uint GetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
return Interpolants.PrimitiveId;
#else
return 0;
#endif
}
void SetPrimitiveId(inout FVertexFactoryInterpolantsVSToPS Interpolants, uint PrimitiveId)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
Interpolants.PrimitiveId = PrimitiveId;
#endif
}
#if NEEDS_LIGHTMAP_COORDINATE
float2 GetLightMapCoordinate(FVertexFactoryInterpolantsVSToPS Interpolants)
{
return Interpolants.TexCoord.zw;
}
#endif
/** Converts from vertex factory specific interpolants to a FMaterialPixelParameters, which is used by material inputs. */
FMaterialPixelParameters GetMaterialPixelParameters(FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition)
{
// GetMaterialPixelParameters is responsible for fully initializing the result
FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
#if NUM_MATERIAL_TEXCOORDS
UNROLL
for (int CoordinateIndex=0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
{
Result.TexCoords[CoordinateIndex] = Interpolants.TexCoord.xy;
}
#endif
Result.VertexColor = 1;
half3 TangentY = Interpolants.TangentY;
half3 TangentZ = Interpolants.TangentZ;
Result.TangentToWorld = transpose(float3x3(Interpolants.TangentX, TangentY, TangentZ));
Result.UnMirrored = 1;
#if LIGHTMAP_UV_ACCESS
#if NEEDS_LIGHTMAP_COORDINATE //TEXTURE_LIGHTMAP || SIMPLE_TEXTURE_LIGHTMAP
Result.LightmapUVs = GetLightMapCoordinate(Interpolants);
#else
Result.LightmapUVs = float2(0, 0);
#endif
#endif
Result.TwoSidedSign = 1;
Result.PrimitiveId = GetPrimitiveId(Interpolants);
return Result;
}
float4 GetLocalPosFluid(FVertexFactoryInput Vertex)
{
float4 LocalPos;
LocalPos.xy = (Vertex.TexCoord.xy - 0.5f) * FluidSize;
LocalPos.zw = float2( Vertex.Height, 1.0f );
return LocalPos;
}
float4 CalcWorldPosition( FVertexFactoryInput Vertex, FVertexFactoryIntermediates Intermediates )
{
return mul( DFHackToFloat(Intermediates.LocalToWorld), GetLocalPosFluid(Vertex) );
}
float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 InWorldPosition)
{
return InWorldPosition;
}
float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition)
{
return TranslatedWorldPosition;
}
FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
{
FVertexFactoryIntermediates Intermediates = (FVertexFactoryIntermediates)0;
Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input);
Intermediates.Dummy = 0;
// Pre calculate
//Intermediates.InvNonUniformScale = GetInstanceData(Intermediates).InvNonUniformScale;
//Intermediates.DeterminantSign = GetInstanceData(Intermediates).DeterminantSign;
Intermediates.LocalToWorld = GetInstanceData(Intermediates).LocalToWorld;
Intermediates.WorldToLocal = GetInstanceData(Intermediates).WorldToLocal;
Intermediates.PrevLocalToWorld = GetInstanceData(Intermediates).PrevLocalToWorld;
return Intermediates;
}
// @return translated world position
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return CalcWorldPosition(Input, Intermediates);
}
FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
{
// Initialize the whole struct to 0
// Really only the last two components of the packed UVs have the opportunity to be uninitialized
FVertexFactoryInterpolantsVSToPS Interpolants = (FVertexFactoryInterpolantsVSToPS)0;
Interpolants.TexCoord.xy = Input.TexCoord;
Interpolants.TexCoord.zw = 0;
float3 VX = float3( 6.0f, 0.0f, Input.HeightDelta.x*TweakScale );
float3 VY = float3( 0.0f, 6.0f, Input.HeightDelta.y*TweakScale );
float3 Normal = cross( VX, VY );
Interpolants.TangentX = normalize(VX);
Interpolants.TangentZ = normalize(Normal);
Interpolants.TangentY = cross(Interpolants.TangentZ, Interpolants.TangentX);
SetPrimitiveId(Interpolants, Intermediates.SceneData.PrimitiveId);
return Interpolants;
}
float4 TransformTranslatedWorldToLocal(float4 TranslatedWorldPosition, FDFInverseMatrix WorldToLocal)
{
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(WorldToLocal, ResolvedView.PreViewTranslation);
float4 LocalPosition = mul(TranslatedWorldPosition, TranslatedWorldToLocal);
return LocalPosition;
}
// local position relative to instance
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
float4 PosPrimSpace = TransformTranslatedWorldToLocal(VertexFactoryGetWorldPosition(Input, Intermediates), Intermediates.WorldToLocal);
return PosPrimSpace.xyz; // No support for instancing, so instance == primitive
}
// local position relative to instance
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return VertexFactoryGetInstanceSpacePosition(Input, Intermediates);
}
float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants)
{
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(GetPrimitiveId(Interpolants));
return float4(DFFastToTranslatedWorld(PrimitiveData.ObjectWorldPosition, ResolvedView.PreViewTranslation), PrimitiveData.ObjectRadius);
}
// @return previous translated world position
float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return VertexFactoryGetWorldPosition(Input, 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
*/
half3x3 VertexFactoryGetTangentToLocal( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
{
float3x3 Basis;
const half3 VX = half3( 6.0f, 0.0f, Input.HeightDelta.x*TweakScale );
const half3 VY = half3( 0.0f, 6.0f, Input.HeightDelta.y*TweakScale );
const half3 Normal = cross( VX, VY );
Basis[0] = normalize( VX );
Basis[2] = normalize( Normal );
Basis[1] = cross( Basis[2], Basis[0] );
return Basis;
}
/** 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,
half3x3 TangentToLocal,
bool bIsPreviousFrame = false)
{
FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
Result.SceneData = Intermediates.SceneData;
Result.PrimitiveId = Intermediates.SceneData.PrimitiveId;
Result.WorldPosition = WorldPosition;
Result.VertexColor = 1;
if (bIsPreviousFrame)
{
Result.PositionInstanceSpace = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates);
}
else
{
Result.PositionInstanceSpace = VertexFactoryGetInstanceSpacePosition(Input, Intermediates);
}
Result.PositionPrimitiveSpace = Result.PositionInstanceSpace; // No support for instancing, so instance == primitive
float3x3 TangentToWorld = mul(transpose((float3x3)DFHackToFloat(Intermediates.LocalToWorld)), TangentToLocal);
// Previous frame not handled deliberately. Lacks necessary information and
// primitives using this VF are usually transparent and hence don't output velocity
Result.TangentToWorld = TangentToWorld;
Result.PreSkinnedPosition = GetLocalPosFluid(Input); //WorldPosition - DFHackToFloat(ResolvedView.PreViewTranslation).xyz; // Relative Pos
Result.PreSkinnedNormal = float3(0,0,1); // Relative Pos Normal
Result.PrevFrameLocalToWorld = Intermediates.PrevLocalToWorld;
#if NUM_MATERIAL_TEXCOORDS_VERTEX
for(int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS_VERTEX; CoordinateIndex++)
{
// TexCoords for the vertex shader for this VertexFactory are not implemented.
Result.TexCoords[CoordinateIndex] = 0.0f;
}
#endif
Result.LWCData = MakeMaterialLWCData(Result);
return Result;
}
float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
const float3 VX = float3( 6.0f, 0.0f, Input.HeightDelta.x*TweakScale );
const float3 VY = float3( 0.0f, 6.0f, Input.HeightDelta.y*TweakScale );
const float3 TangentZ = cross( VX, VY );
float3 WorldNormal = mul( DFHackToFloat(GetPrimitiveData(Intermediates).LocalToWorld),TangentZ );
return WorldNormal.xyz;
}