// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "NaniteDefinitions.h" #include "Math/Bounds.h" namespace Nanite { class FCluster; struct FPageSections { uint32 Cluster = 0; uint32 ClusterBoneInfluence = 0; uint32 VoxelBoneInfluence = 0; uint32 MaterialTable = 0; uint32 VertReuseBatchInfo = 0; uint32 BoneInfluence = 0; uint32 BrickData = 0; uint32 ExtendedData = 0; uint32 DecodeInfo = 0; uint32 Index = 0; uint32 Position = 0; uint32 Attribute = 0; uint32 GetClusterBoneInfluenceSize() const { return Align(ClusterBoneInfluence, 16); } uint32 GetVoxelBoneInfluenceSize() const { return Align(VoxelBoneInfluence, 16); } uint32 GetMaterialTableSize() const { return Align(MaterialTable, 16); } uint32 GetVertReuseBatchInfoSize() const { return Align(VertReuseBatchInfo, 16); } uint32 GetBoneInfluenceSize() const { return Align(BoneInfluence, 16); } uint32 GetBrickDataSize() const { return Align(BrickData, 16); } uint32 GetExtendedDataSize() const { return Align(ExtendedData, 16); } uint32 GetDecodeInfoSize() const { return Align(DecodeInfo, 16); } uint32 GetClusterOffset() const { return NANITE_GPU_PAGE_HEADER_SIZE; } uint32 GetClusterBoneInfluenceOffset() const{ return GetClusterOffset() + Cluster; } uint32 GetVoxelBoneInfluenceOffset() const { return GetClusterBoneInfluenceOffset() + GetClusterBoneInfluenceSize(); } uint32 GetMaterialTableOffset() const { return GetVoxelBoneInfluenceOffset() + GetVoxelBoneInfluenceSize(); } uint32 GetVertReuseBatchInfoOffset() const { return GetMaterialTableOffset() + GetMaterialTableSize(); } uint32 GetBoneInfluenceOffset() const { return GetVertReuseBatchInfoOffset() + GetVertReuseBatchInfoSize(); } uint32 GetBrickDataOffset() const { return GetBoneInfluenceOffset() + GetBoneInfluenceSize(); } uint32 GetExtendedDataOffset() const { return GetBrickDataOffset() + GetBrickDataSize(); } uint32 GetDecodeInfoOffset() const { return GetExtendedDataOffset() + GetExtendedDataSize(); } uint32 GetIndexOffset() const { return GetDecodeInfoOffset() + GetDecodeInfoSize(); } uint32 GetPositionOffset() const { return GetIndexOffset() + Index; } uint32 GetAttributeOffset() const { return GetPositionOffset() + Position; } uint32 GetTotal() const { return GetAttributeOffset() + Attribute; } FPageSections GetOffsets() const { return FPageSections { GetClusterOffset(), GetClusterBoneInfluenceOffset(), GetVoxelBoneInfluenceOffset(), GetMaterialTableOffset(), GetVertReuseBatchInfoOffset(), GetBoneInfluenceOffset(), GetBrickDataOffset(), GetExtendedDataOffset(), GetDecodeInfoOffset(), GetIndexOffset(), GetPositionOffset(), GetAttributeOffset() }; } void operator+=(const FPageSections& Other) { Cluster += Other.Cluster; ClusterBoneInfluence+= Other.ClusterBoneInfluence; VoxelBoneInfluence += Other.VoxelBoneInfluence; MaterialTable += Other.MaterialTable; VertReuseBatchInfo += Other.VertReuseBatchInfo; BoneInfluence += Other.BoneInfluence; BrickData += Other.BrickData; ExtendedData += Other.ExtendedData; DecodeInfo += Other.DecodeInfo; Index += Other.Index; Position += Other.Position; Attribute += Other.Attribute; } }; struct FPageStreams { TArray StripBitmask; TArray PageClusterPair; TArray VertexRefBitmask; TArray VertexRef; TArray Index; TArray Attribute; TArray BoneInfluence; TArray Brick; TArray Extended; TArray MaterialRange; TArray VertReuseBatchInfo; TArray LowByte; TArray MidByte; TArray HighByte; }; struct FPage { uint32 PartsStartIndex = 0; uint32 PartsNum = 0; uint32 NumClusters = 0; uint32 MaxHierarchyPartDepth = 0; // Max depth of referenced parts uint32 MaxHierarchyDepth = 0; // Depth to guarantee next level can also be reached uint32 MaxClusterBoneInfluences = 0; uint32 MaxVoxelBoneInfluences = 0; bool bRelativeEncoding = false; FPageSections GpuSizes; }; struct FHierarchyNodeRef { uint32 NodeIndex = MAX_uint32; uint32 ChildIndex = MAX_uint32; }; struct FClusterGroupPart { TArray Clusters; FBounds3f Bounds; float MinLODError = MAX_flt; uint32 PageIndex = MAX_uint32; uint32 GroupIndex = MAX_uint32; uint32 PageClusterOffset = MAX_uint32; TArray HierarchyNodeRefs; }; struct FUVInfo { FUintVector2 Min = FUintVector2::ZeroValue; FUintVector2 NumBits = FUintVector2::ZeroValue; }; struct FPackedUVHeader { FUintVector2 Data; }; struct FClusterBoneInfluence { uint32 BoneIndex; }; struct FPackedVoxelBoneInfluence { uint32 Weight_BoneIndex; // Weight: 8, BoneIndex: 24 }; struct FBoneInfluenceInfo { uint32 DataOffset = 0; uint32 NumVertexBoneInfluences = 0; uint32 NumVertexBoneIndexBits = 0; uint32 NumVertexBoneWeightBits = 0; TArray BrickBoneIndices; TArray ClusterBoneInfluences; TArray VoxelBoneInfluences; }; struct FPackedBrick { uint32 VoxelMask[2]; uint32 PositionAndBrickMax[2]; // MaxX: 2, MaxY: 2, MaxZ: 2, PosX: 19, PosY: 19, PosZ: 19 uint32 VertOffset_BoneIndex; }; struct FEncodingInfo { uint32 BitsPerIndex = 0; uint32 BitsPerAttribute = 0; uint32 NormalPrecision = 0; uint32 TangentPrecision = 0; uint32 ColorMode = 0; FIntVector4 ColorMin = FIntVector4(0, 0, 0, 0); FIntVector4 ColorBits = FIntVector4(0, 0, 0, 0); FUVInfo UVs[NANITE_MAX_UVS]; FBoneInfluenceInfo BoneInfluence; FPageSections GpuSizes; }; class FBitWriter { public: FBitWriter(TArray& Buffer) : Buffer(Buffer), PendingBits(0ull), NumPendingBits(0) { } void PutBits(uint32 Bits, uint32 NumBits) { check((uint64)Bits < (1ull << NumBits)); PendingBits |= (uint64)Bits << NumPendingBits; NumPendingBits += NumBits; while (NumPendingBits >= 8) { Buffer.Add((uint8)PendingBits); PendingBits >>= 8; NumPendingBits -= 8; } } void Flush(uint32 Alignment=1) { if (NumPendingBits > 0) Buffer.Add((uint8)PendingBits); while (Buffer.Num() % Alignment != 0) Buffer.Add(0); PendingBits = 0; NumPendingBits = 0; } private: TArray& Buffer; uint64 PendingBits; int32 NumPendingBits; }; template void ProcessPageClusters(const FPage& Page, const TArray& Parts, TLambda&& Lambda) { uint32 LocalClusterIndex = 0; for (uint32 PartIndex = 0; PartIndex < Page.PartsNum; PartIndex++) { const FClusterGroupPart& Part = Parts[Page.PartsStartIndex + PartIndex]; for (uint32 i = 0; i < (uint32)Part.Clusters.Num(); i++) { Lambda(LocalClusterIndex, Part.Clusters[i]); LocalClusterIndex++; } } check(LocalClusterIndex == Page.NumClusters); } }