using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Unity.Mathematics;
using UnityEngine;
using Unity.Collections.LowLevel.Unsafe;
// The types defined in this file should match the types defined in BakeInput.h.
namespace UnityEditor.PathTracing.LightBakerBridge
{
internal interface IBakeInputVisitable
{
void Transfer(IBakeInputVisitor visitor);
}
internal delegate void TransferFunction<T>(IBakeInputVisitor visitor, ref T result);
internal interface IBakeInputVisitor
{
// Booleans and strings need special handling as they are not blittable types.
public void TransferBoolean(ref bool result);
public void TransferString(ref string result);
public void TransferArray<T>(ref T[] array, TransferFunction<T> transfer);
public void TransferBlittable<T>(ref T result)
where T : unmanaged;
public void TransferBlittableArray<T>(ref T[] array)
where T : unmanaged;
public void TransferDictionary<TKey, TValue>(ref Dictionary<TKey, TValue> dict, TransferFunction<TValue> valueTransfer)
where TKey : unmanaged;
}
internal static class BakeInputVisitorExtensions
{
public static void Transfer<T>(this IBakeInputVisitor self, ref T result)
where T : IBakeInputVisitable => result.Transfer(self);
public static void TransferArray<T>(this IBakeInputVisitor self, ref T[] array) where T : IBakeInputVisitable
=> self.TransferArray(ref array, (IBakeInputVisitor visitor, ref T result) => visitor.Transfer(ref result));
public static void TransferBlittableDictionary<TKey, TValue>(this IBakeInputVisitor self, ref Dictionary<TKey, TValue> dict)
where TKey : unmanaged
where TValue : unmanaged
{
self.TransferDictionary(ref dict, (IBakeInputVisitor visitor, ref TValue result) => visitor.TransferBlittable(ref result));
}
}
internal class BakeInputReader : IBakeInputVisitor
{
private int _position;
private byte[] _bytes;
public BakeInputReader(byte[] bytes)
{
_position = 0;
_bytes = bytes;
}
public void TransferBoolean(ref bool result)
{
result = _bytes[_position] != 0;
_position += sizeof(byte);
}
public void TransferString(ref string result)
{
byte[] raw = null;
this.TransferBlittableArray(ref raw);
result = System.Text.Encoding.ASCII.GetString(raw);
}
public void TransferArray<T>(ref T[] array, TransferFunction<T> transfer)
{
UInt64 length = 0;
TransferBlittable(ref length);
array = new T[length];
for (int i = 0; i < (int)length; i++)
{
transfer(this, ref array[i]);
}
}
public unsafe void TransferBlittable<T>(ref T result)
where T : unmanaged
{
var size = sizeof(T);
fixed (byte* ptr = &_bytes[_position])
{
UnsafeUtility.CopyPtrToStructure(ptr, out result);
_position += size;
}
}
public unsafe void TransferBlittableArray<T>(ref T[] array)
where T : unmanaged
{
UInt64 length = 0;
TransferBlittable(ref length);
array = new T[length];
if (0 == length) // This avoids going out-of-bounds below when we are at the end of data.
return;
// Pin the managed arrays while we copy data over
int byteLength = (int)length * sizeof(T);
fixed (byte* ptr = &_bytes[_position])
{
fixed (T* arrayPtr = array)
{
UnsafeUtility.MemCpy(arrayPtr, ptr, byteLength);
_position += byteLength;
}
}
}
public void TransferDictionary<TKey, TValue>(ref Dictionary<TKey, TValue> dict, TransferFunction<TValue> valueTransfer)
where TKey : unmanaged
{
UInt64 length = 0;
TransferBlittable(ref length);
dict = new Dictionary<TKey, TValue>((int)length);
for (int i = 0; i < (int)length; i++)
{
TKey key = default;
TransferBlittable(ref key);
TValue value = default;
valueTransfer(this, ref value);
dict.Add(key, value);
}
}
}
internal class BakeInputWriter : IBakeInputVisitor
{
private List<byte> _outBytes;
public BakeInputWriter(List<byte> outBytes)
{
_outBytes = outBytes;
}
public void TransferBoolean(ref bool result) => _outBytes.Add(result ? (byte)1 : (byte)0);
public void TransferString(ref string result)
{
byte[] raw = System.Text.Encoding.ASCII.GetBytes(result);
this.TransferBlittableArray(ref raw);
}
public void TransferArray<T>(ref T[] array, TransferFunction<T> transfer)
{
UInt64 length = (UInt64)array.Length;
TransferBlittable(ref length);
for (int i = 0; i < (int)length; i++)
{
transfer(this, ref array[i]);
}
}
public unsafe void TransferBlittable<T>(ref T result)
where T : unmanaged
{
var size = sizeof(T);
byte[] bytes = new byte[size];
fixed (byte* ptr = &bytes[0])
{
UnsafeUtility.CopyStructureToPtr(ref result, ptr);
}
_outBytes.AddRange(bytes);
}
public void TransferBlittableArray<T>(ref T[] array)
where T : unmanaged => TransferArray(ref array, (IBakeInputVisitor visitor, ref T result) => visitor.TransferBlittable(ref result));
public void TransferDictionary<TKey, TValue>(ref Dictionary<TKey, TValue> dict, TransferFunction<TValue> valueTransfer)
where TKey : unmanaged
{
UInt64 length = (UInt64)dict.Count;
TransferBlittable(ref length);
foreach (var (key, value) in dict)
{
TKey keyCopy = key;
TransferBlittable(ref keyCopy);
TValue valueCopy = value;
valueTransfer(this, ref valueCopy);
}
}
}
internal enum Backend
{
CPU = 0,
GPU,
UnityComputeGPU,
}
internal enum TransmissionType
{
Opacity = 0,
Transparency,
None
}
internal enum TransmissionChannels
{
Red = 0,
Alpha,
AlphaCutout,
RGB,
None
}
internal enum LightmapBakeMode
{
NonDirectional = 0,
CombinedDirectional
}
[StructLayout(LayoutKind.Sequential)]
internal struct SampleCount : IBakeInputVisitable
{
public UInt32 directSampleCount;
public UInt32 indirectSampleCount;
public UInt32 environmentSampleCount;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref directSampleCount);
visitor.TransferBlittable(ref indirectSampleCount);
visitor.TransferBlittable(ref environmentSampleCount);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct LightingSettings : IBakeInputVisitable
{
public SampleCount lightmapSampleCounts;
public SampleCount probeSampleCounts;
public UInt32 minBounces;
public UInt32 maxBounces;
public LightmapBakeMode lightmapBakeMode;
public MixedLightingMode mixedLightingMode;
public bool aoEnabled;
public float aoDistance;
public bool useHardwareRayTracing;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.Transfer(ref lightmapSampleCounts);
visitor.Transfer(ref probeSampleCounts);
visitor.TransferBlittable(ref minBounces);
visitor.TransferBlittable(ref maxBounces);
visitor.TransferBlittable(ref lightmapBakeMode);
visitor.TransferBlittable(ref mixedLightingMode);
visitor.TransferBoolean(ref aoEnabled);
visitor.TransferBlittable(ref aoDistance);
visitor.TransferBlittable(ref useHardwareRayTracing);
}
}
internal enum MeshShaderChannel
{
None = -1,
Vertex = 0,
Normal = 1,
TexCoord0 = 2,
TexCoord1 = 3,
Count = 4
}
[Flags]
internal enum MeshShaderChannelMask
{
Invalid = -1,
Empty = 0,
Vertex = 1 << MeshShaderChannel.Vertex,
Normal = 1 << MeshShaderChannel.Normal,
TexCoord0 = 1 << MeshShaderChannel.TexCoord0,
TexCoord1 = 1 << MeshShaderChannel.TexCoord1,
MaskAll = (1 << MeshShaderChannel.Count) - 1
}
[StructLayout(LayoutKind.Sequential)]
internal struct VertexData : IBakeInputVisitable
{
public UInt32 vertexCount;
public MeshShaderChannelMask meshShaderChannelMask;
public UInt32[] dimensions; // number of float comprising the channel item
public UInt32[] offsets; // offset to channel item in bytes
public UInt32[] stride; // stride between channel items in bytes
public byte[] data;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref vertexCount);
visitor.TransferBlittable(ref meshShaderChannelMask);
visitor.TransferBlittableArray(ref dimensions);
visitor.TransferBlittableArray(ref offsets);
visitor.TransferBlittableArray(ref stride);
visitor.TransferBlittableArray(ref data);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct MeshData : IBakeInputVisitable
{
public VertexData vertexData;
public UInt32[] indexBuffer;
public UInt32[] subMeshIndexOffset;
public UInt32[] subMeshIndexCount;
public Bounds[] subMeshAABB;
public float4 uvScaleOffset;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.Transfer(ref vertexData);
visitor.TransferBlittableArray(ref indexBuffer);
visitor.TransferBlittableArray(ref subMeshIndexOffset);
visitor.TransferBlittableArray(ref subMeshIndexCount);
visitor.TransferBlittableArray(ref subMeshAABB);
visitor.TransferBlittable(ref uvScaleOffset);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct MaterialData : IBakeInputVisitable
{
public bool doubleSidedGI;
public TransmissionType transmissionType;
public TransmissionChannels transmissionChannels;
public float alphaCutoff;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBoolean(ref doubleSidedGI);
visitor.TransferBlittable(ref transmissionType);
visitor.TransferBlittable(ref transmissionChannels);
visitor.TransferBlittable(ref alphaCutoff);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct HeightmapData : IBakeInputVisitable
{
public Int16[] data;
public UInt16 resolution;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittableArray(ref data);
visitor.TransferBlittable(ref resolution);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct TerrainHoleData : IBakeInputVisitable
{
public byte[] data;
public UInt16 resolution;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittableArray(ref data);
visitor.TransferBlittable(ref resolution);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct TerrainData : IBakeInputVisitable
{
public UInt32 heightMapIndex; // index into BakeInput::m_HeightmapData
public Int32 terrainHoleIndex; // index into BakeInput::m_TerrainHoleData -1 means no hole data
public float outputResolution;
public float3 heightmapScale;
public float4 uvBounds;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref heightMapIndex);
visitor.TransferBlittable(ref terrainHoleIndex);
visitor.TransferBlittable(ref outputResolution);
visitor.TransferBlittable(ref heightmapScale);
visitor.TransferBlittable(ref uvBounds);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct InstanceData : IBakeInputVisitable
{
public Int32 meshIndex; // index into BakeInput::m_MeshData, -1 for Terrain
public Int32 terrainIndex; // index into BakeInput::m_TerrainData, -1 for MeshRenderer
public float4x4 transform;
public bool oddNegativeScale;
public bool castShadows;
public bool receiveShadows;
public Int32 lodGroup;
public byte lodMask;
public Int32 contributingLodLevel;
public Int32[] subMeshMaterialIndices; // -1 is no material for a given subMesh entry
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref meshIndex);
visitor.TransferBlittable(ref terrainIndex);
visitor.TransferBlittable(ref transform);
visitor.TransferBoolean(ref oddNegativeScale);
visitor.TransferBoolean(ref castShadows);
visitor.TransferBoolean(ref receiveShadows);
visitor.TransferBlittable(ref lodGroup);
visitor.TransferBlittable(ref lodMask);
visitor.TransferBlittable(ref contributingLodLevel);
visitor.TransferBlittableArray(ref subMeshMaterialIndices);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct TextureData : IBakeInputVisitable
{
public UInt32 width;
public UInt32 height;
public float4[] data;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref width);
visitor.TransferBlittable(ref height);
visitor.TransferBlittableArray(ref data);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct TextureTransformData : IBakeInputVisitable
{
public float2 scale;
public float2 offset;
public void Transfer(IBakeInputVisitor visitor)
{
float4 data = default;
visitor.TransferBlittable(ref data);
scale = data.xy;
offset = data.zw;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct TextureProperties : IBakeInputVisitable
{
public TextureWrapMode wrapModeU;
public TextureWrapMode wrapModeV;
public FilterMode filterMode;
public TextureTransformData transmissionTextureST;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref wrapModeU);
visitor.TransferBlittable(ref wrapModeV);
visitor.TransferBlittable(ref filterMode);
visitor.Transfer(ref transmissionTextureST);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct EnvironmentData : IBakeInputVisitable
{
public UInt32 cubeResolution;
public float4[] cubeData;
public UInt32 importanceSampleCount;
public float importanceIntegratedMetric;
public float4[] importanceDirections;
public float4[] importanceWeightedIntensities;
public float4[] importanceIntensities;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref cubeResolution);
visitor.TransferBlittableArray(ref cubeData);
visitor.TransferBlittable(ref importanceSampleCount);
visitor.TransferBlittable(ref importanceIntegratedMetric);
visitor.TransferBlittableArray(ref importanceDirections);
visitor.TransferBlittableArray(ref importanceWeightedIntensities);
visitor.TransferBlittableArray(ref importanceIntensities);
}
}
internal enum LightType : byte
{
Directional = 0,
Point,
Spot,
Rectangle,
Disc,
SpotPyramidShape,
SpotBoxShape
}
internal enum FalloffType : byte
{
InverseSquared = 0,
InverseSquaredNoRangeAttenuation,
Linear,
Legacy,
None
}
internal enum AngularFalloffType : byte
{
LUT = 0,
AnalyticAndInnerAngle
}
internal enum LightMode : byte
{
Realtime = 0,
Mixed,
Baked
}
[StructLayout(LayoutKind.Sequential)]
internal struct LightData : IBakeInputVisitable
{
// shared
public float3 color;
public float3 indirectColor;
public Quaternion orientation;
public float3 position;
public float range;
// cookie
public UInt32 cookieTextureIndex;
public float cookieScale;
// spot light only or cookieSize for directional lights
public float coneAngle;
public float innerConeAngle;
// area light parameters (interpretation depends on the type)
public float shape0;
public float shape1;
public LightType type;
public LightMode mode;
public FalloffType falloff;
public AngularFalloffType angularFalloff;
public bool castsShadows;
public UInt32 shadowMaskChannel;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref color);
visitor.TransferBlittable(ref indirectColor);
visitor.TransferBlittable(ref orientation);
visitor.TransferBlittable(ref position);
visitor.TransferBlittable(ref range);
visitor.TransferBlittable(ref cookieTextureIndex);
visitor.TransferBlittable(ref cookieScale);
visitor.TransferBlittable(ref coneAngle);
visitor.TransferBlittable(ref innerConeAngle);
visitor.TransferBlittable(ref shape0);
visitor.TransferBlittable(ref shape1);
visitor.TransferBlittable(ref type);
visitor.TransferBlittable(ref mode);
visitor.TransferBlittable(ref falloff);
visitor.TransferBlittable(ref angularFalloff);
visitor.TransferBoolean(ref castsShadows);
visitor.TransferBlittable(ref shadowMaskChannel);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct CookieData : IBakeInputVisitable
{
public UInt32 width;
public UInt32 height;
public UInt32 pixelStride;
public UInt32 slices; // 1 for single, 6 for cubes
public bool repeat; // texture addressing mode
public byte[] textureData;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref width);
visitor.TransferBlittable(ref height);
visitor.TransferBlittable(ref pixelStride);
visitor.TransferBlittable(ref slices);
visitor.TransferBoolean(ref repeat);
visitor.TransferBlittableArray(ref textureData);
}
}
[Flags]
internal enum ProbeRequestOutputType : uint
{
RadianceDirect = 1 << 0,
RadianceIndirect = 1 << 1,
Validity = 1 << 2,
MixedLightOcclusion = 1 << 3,
LightProbeOcclusion = 1 << 4,
EnvironmentOcclusion = 1 << 5,
Depth = 1 << 6,
All = 0xFFFFFFFF
}
[StructLayout(LayoutKind.Sequential)]
internal struct ProbeRequest : IBakeInputVisitable
{
public ProbeRequestOutputType outputTypeMask;
public UInt64 positionOffset;
public UInt64 count;
public float pushoff;
public string outputFolderPath;
public UInt64 integrationRadiusOffset;
public UInt32 environmentOcclusionSampleCount;
public bool ignoreDirectEnvironment;
public bool ignoreIndirectEnvironment;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref outputTypeMask);
visitor.TransferBlittable(ref positionOffset);
visitor.TransferBlittable(ref count);
visitor.TransferBlittable(ref pushoff);
visitor.TransferString(ref outputFolderPath);
visitor.TransferBlittable(ref integrationRadiusOffset);
visitor.TransferBlittable(ref environmentOcclusionSampleCount);
visitor.TransferBoolean(ref ignoreDirectEnvironment);
visitor.TransferBoolean(ref ignoreIndirectEnvironment);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct ProbeRequestData : IBakeInputVisitable
{
public float3[] positions;
public int[] occlusionLightIndices; // 4 entries per probe, index into BakeInput.lightData
public float[] integrationRadii;
public ProbeRequest[] requests;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittableArray(ref positions);
visitor.TransferBlittableArray(ref occlusionLightIndices);
visitor.TransferBlittableArray(ref integrationRadii);
visitor.TransferArray(ref requests);
}
}
[Flags]
internal enum LightmapRequestOutputType : uint
{
IrradianceIndirect = 1 << 0,
IrradianceDirect = 1 << 1,
IrradianceEnvironment = 1 << 2,
Occupancy = 1 << 3,
Validity = 1 << 4,
DirectionalityIndirect = 1 << 5,
DirectionalityDirect = 1 << 6,
AmbientOcclusion = 1 << 7,
Shadowmask = 1 << 8,
Normal = 1 << 9,
ChartIndex = 1 << 10,
OverlapPixelIndex = 1 << 11,
All = 0xFFFFFFFF
}
internal enum TilingMode : byte
{ // Assuming a 4k lightmap (16M texels), the tiling will yield the following chunk sizes:
None = 0, // 4k * 4k = 16M texels
Quarter = 1, // 2k * 2k = 4M texels
Sixteenth = 2, // 1k * 1k = 1M texels
Sixtyfourth = 3, // 512 * 512 = 262k texels
TwoHundredFiftySixth = 4, // 256 * 256 = 65k texels
Max = TwoHundredFiftySixth,
Error = 5 // Error. We don't want to go lower (GPU occupancy will start to be a problem for smaller atlas sizes).
}
[StructLayout(LayoutKind.Sequential)]
internal struct LightmapRequest : IBakeInputVisitable
{
public LightmapRequestOutputType outputTypeMask;
public UInt32 lightmapOffset;
public UInt32 lightmapCount;
public TilingMode tilingMode;
public string outputFolderPath;
public float pushoff;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref outputTypeMask);
visitor.TransferBlittable(ref lightmapOffset);
visitor.TransferBlittable(ref lightmapCount);
visitor.TransferBlittable(ref tilingMode);
visitor.TransferString(ref outputFolderPath);
visitor.TransferBlittable(ref pushoff);
}
public static UInt64 TilingModeToLightmapExpandedBufferSize(TilingMode tilingMode)
{
UInt64 kMinBufferSize = 64;
UInt64 bufferSize = 0;
// TODO: We need to change the naming of the entries in the enum see: https://jira.unity3d.com/browse/GFXFEAT-728
switch (tilingMode)
{
case TilingMode.None: bufferSize = 1048576; break; // UI: Highest Performance
case TilingMode.Quarter: bufferSize = 524288; break; // UI: High Performance
case TilingMode.Sixteenth: bufferSize = 262144; break; // UI: Automatic (but it is not automatic)
case TilingMode.Sixtyfourth: bufferSize = 131072; break; // UI: Low Memory Usage
case TilingMode.TwoHundredFiftySixth: bufferSize = 65536; break; // UI: Lowest Memory Usage
default: Debug.Assert(false, "Unknown tiling mode."); break;
}
return math.max(bufferSize, kMinBufferSize);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct ProgressiveBakeParametersStruct
{
public enum SupersamplingMultiplier
{
kSupersamplingDisabled = 1,
kSupersamplingx2 = 2,
kSupersamplingx4 = 4
}
public float backfaceTolerance;
public SupersamplingMultiplier supersamplingMultiplier;
public float pushOff;
public int bakedLightmapTag;
public int maxLightmapCount;
}
// This struct has the same binary layout as Hash128, but represents how we use 'fake' hashes
// to store indices in LightBaker.
[StructLayout(LayoutKind.Sequential)]
internal struct IndexHash128 : IEquatable<IndexHash128>
{
internal ulong _u64First;
internal ulong _u64Second;
public ulong Index => _u64First;
public override int GetHashCode() => HashCode.Combine(_u64First, _u64Second);
public bool Equals(IndexHash128 other) => _u64First == other._u64First && _u64Second == other._u64Second;
public override bool Equals(object obj) => obj is IndexHash128 other && Equals(other);
public static bool operator ==(IndexHash128 a, IndexHash128 b) => a.Equals(b);
public static bool operator !=(IndexHash128 a, IndexHash128 b) => !a.Equals(b);
}
[StructLayout(LayoutKind.Sequential)]
internal struct PVRAtlasData : IBakeInputVisitable
{
public IndexHash128 m_AtlasHash; // The hash of this atlas.
public IndexHash128 m_SceneGUID; // The scene identifier, used for multi-scene bakes.
public Int32 m_AtlasId; // The atlasId.
public ProgressiveBakeParametersStruct m_BakeParameters;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref m_AtlasHash);
visitor.TransferBlittable(ref m_SceneGUID);
visitor.TransferBlittable(ref m_AtlasId);
visitor.TransferBlittable(ref m_BakeParameters);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct GBufferInstanceData : IBakeInputVisitable
{
public IndexHash128 objectIDHash;
public IndexHash128 geometryHashPVR;
public float4 st;
public int transformIndex;
public Rect atlasViewport;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref objectIDHash);
visitor.TransferBlittable(ref geometryHashPVR);
visitor.TransferBlittable(ref st);
visitor.TransferBlittable(ref transformIndex);
visitor.TransferBlittable(ref atlasViewport);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct GBufferInstances : IBakeInputVisitable
{
public GBufferInstanceData[] gbufferInstanceDataArray;
public int atlasId;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferArray(ref gbufferInstanceDataArray);
visitor.TransferBlittable(ref atlasId);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct AtlassedInstanceData : IBakeInputVisitable
{
public int m_AtlasId; // The 0-based atlas index of the atlas (used by the renderers to get the lightmap).
public int m_InstanceIndex; // Instance index in atlas.
public float4 m_LightmapST; // The atlas UV scale and translate.
public Rect m_Viewport;
public float m_Width;
public float m_Height;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferBlittable(ref m_AtlasId);
visitor.TransferBlittable(ref m_InstanceIndex);
visitor.TransferBlittable(ref m_LightmapST);
visitor.TransferBlittable(ref m_Viewport);
visitor.TransferBlittable(ref m_Width);
visitor.TransferBlittable(ref m_Height);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct PVRAtlassingData : IBakeInputVisitable
{
public PVRAtlasData[] m_AtlasIdToAtlasHash;
public PVRAtlasData[] m_AtlasIdToAtlasHashLightmapped;
public (int width, int height)[] m_AtlasSizes;
public Dictionary<IndexHash128, int> m_AtlasHashToAtlasId;
public Dictionary<IndexHash128, IndexHash128> m_AtlasHashToGBufferHash;
public IndexHash128[] m_GBufferHashes;
public Dictionary<IndexHash128, IndexHash128[]> m_AtlasHashToObjectIDHashes;
public Dictionary<IndexHash128, float> m_AtlasHashToAtlasWeight;
public Dictionary<IndexHash128, GBufferInstances> m_GBufferHashToGBufferInstances;
public Dictionary<IndexHash128, AtlassedInstanceData> m_InstanceAtlassingData;
public int[] m_AtlasOffsets;
public double m_EstimatedTexelCount;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.TransferArray(ref m_AtlasIdToAtlasHash);
visitor.TransferArray(ref m_AtlasIdToAtlasHashLightmapped);
visitor.TransferBlittableArray(ref m_AtlasSizes);
visitor.TransferBlittableDictionary(ref m_AtlasHashToAtlasId);
visitor.TransferBlittableDictionary(ref m_AtlasHashToGBufferHash);
visitor.TransferBlittableArray(ref m_GBufferHashes);
visitor.TransferDictionary(ref m_AtlasHashToObjectIDHashes, (IBakeInputVisitor dictionaryVisitor, ref IndexHash128[] result) => dictionaryVisitor.TransferBlittableArray(ref result));
visitor.TransferBlittableDictionary(ref m_AtlasHashToAtlasWeight);
visitor.TransferDictionary(ref m_GBufferHashToGBufferInstances, (IBakeInputVisitor dictionaryVisitor, ref GBufferInstances result) => dictionaryVisitor.Transfer(ref result));
visitor.TransferBlittableDictionary(ref m_InstanceAtlassingData);
visitor.TransferBlittableArray(ref m_AtlasOffsets);
visitor.TransferBlittable(ref m_EstimatedTexelCount);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct LightmapRequestData : IBakeInputVisitable
{
public PVRAtlassingData atlassing;
public LightmapRequest[] requests;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.Transfer(ref atlassing);
visitor.TransferArray(ref requests);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct BakeInput : IBakeInputVisitable
{
// Global settings
public LightingSettings lightingSettings;
// Mesh data
public MeshData[] meshData;
public TerrainData[] terrainData;
public TerrainHoleData[] terrainHoleData;
public HeightmapData[] heightMapData;
// Material data
public MaterialData[] materialData;
// Instance data
public InstanceData[] instanceData;
// Texture data
public UInt32[] instanceToTextureDataIndex; // Index into albedoData and emissiveData for each instance
public Int32[] materialToTransmissionDataIndex; // Index into transmissionData and transmissionDataProperties for each material
public TextureData[] albedoData;
public TextureData[] emissiveData;
public TextureData[] transmissionData; // Same size as transmissionDataProperties
public TextureProperties[] transmissionDataProperties; // Same size as transmissionData
// Cookie data
public CookieData[] cookieData;
public LightData[] lightData;
// Environment data
public EnvironmentData environmentData;
public void Transfer(IBakeInputVisitor visitor)
{
visitor.Transfer(ref lightingSettings);
visitor.TransferArray(ref meshData);
visitor.TransferArray(ref terrainData);
visitor.TransferArray(ref terrainHoleData);
visitor.TransferArray(ref heightMapData);
visitor.TransferArray(ref materialData);
visitor.TransferArray(ref instanceData);
visitor.TransferBlittableArray(ref instanceToTextureDataIndex);
visitor.TransferBlittableArray(ref materialToTransmissionDataIndex);
visitor.TransferArray(ref albedoData);
visitor.TransferArray(ref emissiveData);
visitor.TransferArray(ref transmissionData);
visitor.TransferArray(ref transmissionDataProperties);
visitor.TransferArray(ref cookieData);
visitor.TransferArray(ref lightData);
visitor.Transfer(ref environmentData);
}
}
internal static class BakeInputSerialization
{
// Should match BakeInputSerialization::kCurrentFileVersion in BakeInputSerialization.h.
// If these are out of sync, the implementation in this file probably needs to be updated.
const UInt64 CurrentFileVersion = 202509021;
public static bool Deserialize(string path, out BakeInput bakeInput)
{
BakeInputReader reader = new(File.ReadAllBytes(path));
return Deserialize(reader, out bakeInput);
}
public static bool Deserialize(string path, out LightmapRequestData lightmapRequestData)
{
BakeInputReader reader = new(File.ReadAllBytes(path));
return Deserialize(reader, out lightmapRequestData);
}
public static bool Deserialize(string path, out ProbeRequestData probeRequestData)
{
BakeInputReader reader = new(File.ReadAllBytes(path));
return Deserialize(reader, out probeRequestData);
}
public static bool Deserialize(byte[] memory, out BakeInput bakeInput)
{
BakeInputReader reader = new(memory);
return Deserialize(reader, out bakeInput);
}
private static bool Deserialize(BakeInputReader visitor, out BakeInput bakeInput)
{
bakeInput = default;
UInt64 fileVersion = 0;
visitor.TransferBlittable(ref fileVersion);
Debug.Assert(fileVersion == CurrentFileVersion, "Version number did not match the current implementation of BakeInput deserialization.");
if (fileVersion != CurrentFileVersion)
return false;
visitor.Transfer(ref bakeInput);
return true;
}
private static bool Deserialize(BakeInputReader visitor, out LightmapRequestData lightmapRequestData)
{
lightmapRequestData = default;
UInt64 fileVersion = 0;
visitor.TransferBlittable(ref fileVersion);
Debug.Assert(fileVersion == CurrentFileVersion, "Version number did not match the current implementation of LightmapRequestData deserialization.");
if (fileVersion != CurrentFileVersion)
return false;
visitor.Transfer(ref lightmapRequestData);
return true;
}
private static bool Deserialize(BakeInputReader visitor, out ProbeRequestData lightProbeRequestData)
{
lightProbeRequestData = default;
UInt64 fileVersion = 0;
visitor.TransferBlittable(ref fileVersion);
Debug.Assert(fileVersion == CurrentFileVersion, "Version number did not match the current implementation of LightProbeRequestData deserialization.");
if (fileVersion != CurrentFileVersion)
return false;
visitor.Transfer(ref lightProbeRequestData);
return true;
}
public static byte[] Serialize(ref BakeInput bakeInput)
{
var bytes = new List<byte>();
BakeInputWriter writer = new(bytes);
UInt64 fileVersion = CurrentFileVersion;
writer.TransferBlittable(ref fileVersion);
writer.Transfer(ref bakeInput);
return bytes.ToArray();
}
public static byte[] Serialize(ref LightmapRequestData lightmapRequestData)
{
var bytes = new List<byte>();
BakeInputWriter writer = new(bytes);
UInt64 fileVersion = CurrentFileVersion;
writer.TransferBlittable(ref fileVersion);
writer.Transfer(ref lightmapRequestData);
return bytes.ToArray();
}
public static byte[] Serialize(ref ProbeRequestData probeRequestData)
{
var bytes = new List<byte>();
BakeInputWriter writer = new(bytes);
UInt64 fileVersion = CurrentFileVersion;
writer.TransferBlittable(ref fileVersion);
writer.Transfer(ref probeRequestData);
return bytes.ToArray();
}
public static void Serialize(string path, ref BakeInput bakeInput)
{
File.WriteAllBytes(path, Serialize(ref bakeInput));
}
public static void Serialize(string path, ref LightmapRequestData lightmapRequestData)
{
File.WriteAllBytes(path, Serialize(ref lightmapRequestData));
}
public static void Serialize(string path, ref ProbeRequestData probeRequestData)
{
File.WriteAllBytes(path, Serialize(ref probeRequestData));
}
}
}