using System;
using System.Collections.Generic;
using UnityEngine.PathTracing.Core;
using UnityEngine.Rendering;
using UnityEngine.LightTransport;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.PathTracing.Lightmapping;
using UnityEngine.PathTracing.Integration;
using UnityEngine.Rendering.Sampling;
using UnityEngine.Rendering.UnifiedRayTracing;
namespace UnityEditor.PathTracing.LightBakerBridge
{
using MaterialHandle = Handle<MaterialPool.MaterialDescriptor>;
using LightHandle = Handle<World.LightDescriptor>;
internal static class BakeInputToWorldConversion
{
private static Mesh MeshDataToMesh(in MeshData meshData)
{
ref readonly VertexData vertexData = ref meshData.vertexData;
var outMesh = new Mesh();
var outRawMeshArray = Mesh.AllocateWritableMeshData(1);
var outRawMesh = outRawMeshArray[0];
int vertexCount = (int)vertexData.vertexCount;
List<VertexAttributeDescriptor> vertexLayout = new();
if (vertexData.meshShaderChannelMask.HasFlag(MeshShaderChannelMask.Vertex))
vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
if (vertexData.meshShaderChannelMask.HasFlag(MeshShaderChannelMask.Normal))
vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3));
if (vertexData.meshShaderChannelMask.HasFlag(MeshShaderChannelMask.TexCoord0))
vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2));
if (vertexData.meshShaderChannelMask.HasFlag(MeshShaderChannelMask.TexCoord1))
vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.TexCoord1, VertexAttributeFormat.Float32, 2));
outRawMesh.SetVertexBufferParams(vertexCount, vertexLayout.ToArray());
outRawMesh.GetVertexData<byte>().CopyFrom(vertexData.data);
outRawMesh.SetIndexBufferParams(meshData.indexBuffer.Length, IndexFormat.UInt32);
outRawMesh.GetIndexData<uint>().CopyFrom(meshData.indexBuffer);
int subMeshCount = meshData.subMeshAABB.Length;
outRawMesh.subMeshCount = subMeshCount;
for (int sm = 0; sm < outRawMesh.subMeshCount; sm++)
{
var smd = new SubMeshDescriptor((int)meshData.subMeshIndexOffset[sm], (int)meshData.subMeshIndexCount[sm]);
outRawMesh.SetSubMesh(sm, smd);
}
Mesh.ApplyAndDisposeWritableMeshData(outRawMeshArray, outMesh);
outMesh.RecalculateBounds();
// MeshData from LightBaker contains UVs that are scaled to perfectly fit in the [0, 1] range.
// The scale and offset used to achieve that are stored in the uvScaleOffset field.
// Here we undo the scaling to get the original UVs of the input mesh.
Vector2[] uv2 = outMesh.uv2;
float4 uvScaleOffset = meshData.uvScaleOffset;
Vector2 uvScale = new Vector2(uvScaleOffset.x, uvScaleOffset.y);
Vector2 uvOffset = new Vector2(uvScaleOffset.z, uvScaleOffset.w);
for (int i = 0; i < uv2.Length; i++)
{
uv2[i] = (uv2[i] - uvOffset) / uvScale;
}
outMesh.uv2 = uv2;
return outMesh;
}
private static Texture2D CreateTexture2DFromTextureData(in TextureData textureData, string name = "CreateTexture2DFromTextureData")
{
Texture2D texture = new Texture2D((int)textureData.width, (int)textureData.height, TextureFormat.RGBAFloat, false, linear: true) { name = name };
texture.SetPixelData(textureData.data, 0);
texture.Apply(false, false);
texture.wrapMode = TextureWrapMode.Clamp;
return texture;
}
private static Texture CreateTextureFromCookieData(in CookieData textureData)
{
if (textureData.slices == 1)
{
Texture2D texture = new Texture2D((int)textureData.width, (int)textureData.height, TextureFormat.RGBA32, false, linear: true);
texture.SetPixelData(textureData.textureData, 0);
texture.Apply(false, false);
return texture;
}
else
{
Cubemap texture = new Cubemap((int)textureData.width, TextureFormat.RGBA32, false);
uint faceStride = textureData.width * textureData.width * textureData.pixelStride;
for (int faceIndex = 0; faceIndex < textureData.slices; faceIndex++)
texture.SetPixelData(textureData.textureData, 0, (CubemapFace)faceIndex, faceIndex * (int)faceStride);
texture.Apply(false, false);
return texture;
}
}
private static UnityEngine.LightType LightBakerLightTypeToUnityLightType(LightBakerBridge.LightType type)
{
switch (type)
{
case LightBakerBridge.LightType.Directional: return UnityEngine.LightType.Directional;
case LightBakerBridge.LightType.Point: return UnityEngine.LightType.Point;
case LightBakerBridge.LightType.Spot: return UnityEngine.LightType.Spot;
case LightBakerBridge.LightType.Rectangle: return UnityEngine.LightType.Rectangle;
case LightBakerBridge.LightType.Disc: return UnityEngine.LightType.Disc;
case LightBakerBridge.LightType.SpotBoxShape: return UnityEngine.LightType.Box;
default: throw new ArgumentException("Unknown light type");
}
}
private static UnityEngine.Experimental.GlobalIllumination.FalloffType LightBakerFalloffTypeToUnityFalloffType(FalloffType falloff)
{
switch (falloff)
{
case FalloffType.InverseSquared:
return UnityEngine.Experimental.GlobalIllumination.FalloffType.InverseSquared;
case FalloffType.InverseSquaredNoRangeAttenuation:
return UnityEngine.Experimental.GlobalIllumination.FalloffType.InverseSquaredNoRangeAttenuation;
case FalloffType.Linear:
return UnityEngine.Experimental.GlobalIllumination.FalloffType.Linear;
case FalloffType.Legacy:
return UnityEngine.Experimental.GlobalIllumination.FalloffType.Legacy;
case FalloffType.None:
return UnityEngine.Experimental.GlobalIllumination.FalloffType.Undefined;
default:
Debug.Assert(false, $"Unknown falloff type: {falloff}");
return UnityEngine.Experimental.GlobalIllumination.FalloffType.Undefined;
}
}
internal static void InjectAnalyticalLights(
World world,
bool autoEstimateLUTRange,
in BakeInput bakeInput,
out LightHandle[] lightHandles,
List<UnityEngine.Object> allocatedObjects)
{
// Extract lights
var lights = new World.LightDescriptor[bakeInput.lightData.Length];
for (int i = 0; i < bakeInput.lightData.Length; i++)
{
ref readonly LightData lightData = ref bakeInput.lightData[i];
// TODO(pema.malling): The following transform is only correct for linear color space :( https://jira.unity3d.com/browse/LIGHT-1763
float maxColor = Mathf.Max(lightData.color.x, Mathf.Max(lightData.color.y, lightData.color.z));
float maxIndirectColor = Mathf.Max(lightData.indirectColor.x, Mathf.Max(lightData.indirectColor.y, lightData.indirectColor.z));
float bounceIntensity = maxColor <= 0 ? 0 : maxIndirectColor / maxColor;
World.LightDescriptor lightDescriptor;
lightDescriptor.Type = LightBakerLightTypeToUnityLightType(lightData.type);
// We multiply intensity by PI, since LightBaker produces radiance estimates that are too bright by a factor of PI,
// for light coming from punctual light sources. This isn't correct, but we need to match LightBaker's output.
// Instead of adding incorrect code to the baker itself, we do the multiplication on the outside.
float3 linearColor = lightData.color;
lightDescriptor.LinearLightColor = linearColor;
lightDescriptor.Shadows = lightData.castsShadows ? LightShadows.Hard : LightShadows.None;
lightDescriptor.Transform = Matrix4x4.TRS(lightData.position, lightData.orientation, Vector3.one);
lightDescriptor.ColorTemperature = 0;
lightDescriptor.LightmapBakeType = lightData.mode == LightMode.Mixed ? LightmapBakeType.Mixed : LightmapBakeType.Baked;
lightDescriptor.AreaSize = Vector2.one;
lightDescriptor.SpotAngle = 0;
lightDescriptor.InnerSpotAngle = 0;
lightDescriptor.CullingMask = uint.MaxValue;
lightDescriptor.BounceIntensity = bounceIntensity;
lightDescriptor.Range = lightData.range;
lightDescriptor.ShadowMaskChannel = (lightData.shadowMaskChannel < 4) ? (int)lightData.shadowMaskChannel : -1;
lightDescriptor.UseColorTemperature = false;
lightDescriptor.FalloffType = LightBakerFalloffTypeToUnityFalloffType(lightData.falloff);
lightDescriptor.ShadowRadius = Util.IsPunctualLightType(lightDescriptor.Type) ? lightData.shape0 : 0.0f;
lightDescriptor.CookieSize = lightData.cookieScale;
lightDescriptor.CookieTexture = Util.IsCookieValid(lightData.cookieTextureIndex) ? CreateTextureFromCookieData(in bakeInput.cookieData[lightData.cookieTextureIndex]) : null;
if (lightDescriptor.CookieTexture != null)
allocatedObjects.Add(lightDescriptor.CookieTexture);
switch (lightDescriptor.Type)
{
case UnityEngine.LightType.Box:
case UnityEngine.LightType.Rectangle:
lightDescriptor.AreaSize = new Vector2(lightData.shape0, lightData.shape1);
break;
case UnityEngine.LightType.Disc:
lightDescriptor.AreaSize = new Vector2(lightData.shape0, lightData.shape0);
break;
case UnityEngine.LightType.Spot:
lightDescriptor.SpotAngle = Mathf.Rad2Deg * lightData.coneAngle;
// TODO(pema.malling): This isn't quite correct, but very close. I couldn't figure out the math. See ExtractInnerCone(). https://jira.unity3d.com/browse/LIGHT-1727
lightDescriptor.InnerSpotAngle = Mathf.Rad2Deg * lightData.innerConeAngle;
break;
case UnityEngine.LightType.Directional:
lightDescriptor.AreaSize = new Vector2(lightData.coneAngle, lightData.innerConeAngle);
break;
}
lights[i] = lightDescriptor;
}
world.lightPickingMethod = LightPickingMethod.LightGrid;
lightHandles = world.AddLights(lights, false, autoEstimateLUTRange, bakeInput.lightingSettings.mixedLightingMode);
}
internal static void InjectEnvironmentLight(
World world,
in BakeInput bakeInput,
List<UnityEngine.Object> allocatedObjects)
{
// Setup environment light
int envCubemapResolution = (int)bakeInput.environmentData.cubeResolution;
var envCubemap = new Cubemap(envCubemapResolution, TextureFormat.RGBAFloat, false);
bool isEmptyCubemap = envCubemapResolution == 1;
for (int i = 0; i < 6; i++)
{
envCubemap.SetPixelData(bakeInput.environmentData.cubeData, 0, (CubemapFace)i, envCubemapResolution * envCubemapResolution * i);
isEmptyCubemap &= math.all(bakeInput.environmentData.cubeData[i].xyz == float3.zero);
}
envCubemap.Apply();
allocatedObjects.Add(envCubemap);
// If we have no cubemap (i.e. the 1x1x1 black texture), don't set it - we don't want to waste samples directly sampling it.
if (!isEmptyCubemap)
{
var envCubemapMaterial = new Material(Shader.Find("Hidden/PassthroughSkybox"));
envCubemapMaterial.SetTexture("_Tex", envCubemap);
world.SetEnvironmentMaterial(envCubemapMaterial);
allocatedObjects.Add(envCubemapMaterial);
}
}
internal static void InjectMaterials(
World world,
in BakeInput bakeInput,
out MaterialHandle[][] perInstanceSubMeshMaterials,
out bool[][] perInstanceSubMeshVisibility,
List<UnityEngine.Object> allocatedObjects)
{
int allocationCount = allocatedObjects.Count;
// Create albedo and emission textures from materials
var perTexturePairMaterials = new MaterialPool.MaterialDescriptor[bakeInput.albedoData.Length];
Debug.Assert(bakeInput.albedoData.Length == bakeInput.emissiveData.Length);
for (int i = 0; i < bakeInput.albedoData.Length; i++)
{
ref var material = ref perTexturePairMaterials[i];
var baseTexture = CreateTexture2DFromTextureData(in bakeInput.albedoData[i], $"World (albedo) {i}");
allocatedObjects.Add(baseTexture);
var emissiveTexture = CreateTexture2DFromTextureData(in bakeInput.emissiveData[i], $"World (emissive) {i}");
allocatedObjects.Add(emissiveTexture);
material.Albedo = baseTexture;
material.Emission = emissiveTexture;
// Only mark emissive if it isn't the default black texture
bool isEmissiveSinglePixel = bakeInput.emissiveData[i].data.Length == 1;
bool isEmissiveBlack = math.all(bakeInput.emissiveData[i].data[0].xyz == float3.zero);
if (isEmissiveSinglePixel && isEmissiveBlack)
{
material.EmissionType = UnityEngine.PathTracing.Core.MaterialPropertyType.None;
material.EmissionColor = Vector3.zero;
}
else
{
material.EmissionType = UnityEngine.PathTracing.Core.MaterialPropertyType.Texture;
material.EmissionColor = Vector3.one;
}
perTexturePairMaterials[i] = material;
}
// Create all the unique transmission textures in bakeInput.transmissionData.
Texture2D[] transmissiveTextures = new Texture2D[bakeInput.transmissionData.Length];
for (int i = 0; i < bakeInput.transmissionData.Length; i++)
{
ref readonly TextureData transmissionData = ref bakeInput.transmissionData[i];
ref readonly TextureProperties transmissionDataProperties = ref bakeInput.transmissionDataProperties[i];
Texture2D transmissiveTexture = CreateTexture2DFromTextureData(in transmissionData, $"World (transmission) {i}");
transmissiveTexture.wrapModeU = transmissionDataProperties.wrapModeU;
transmissiveTexture.wrapModeV = transmissionDataProperties.wrapModeV;
transmissiveTexture.filterMode = transmissionDataProperties.filterMode;
allocatedObjects.Add(transmissiveTexture);
transmissiveTextures[i] = transmissiveTexture;
}
// Certain material properties we can only determine by looking at individual submeshes of each instance.
// Therefore, we must make a copy of the base material for each submesh. We create these materials here.
perInstanceSubMeshMaterials = new MaterialHandle[bakeInput.instanceData.Length][];
perInstanceSubMeshVisibility = new bool[bakeInput.instanceData.Length][];
// To avoid needlessly creating duplicate materials, we also cache the materials we've already created:
// Hashing texturePairIdx handles deduplicating by source mesh, scale and the set of source materials. We hash materialIdx
// to identify the specific source material in the set associated with the texture pair (in case there are submeshes).
Dictionary<(uint texturePairIdx, int materialIdx), MaterialHandle> materialCache = new();
for (int instanceIdx = 0; instanceIdx < bakeInput.instanceData.Length; instanceIdx++)
{
// Get base (per-instance) material
ref readonly InstanceData instanceData = ref bakeInput.instanceData[instanceIdx];
uint texturePairIdx = bakeInput.instanceToTextureDataIndex[instanceIdx];
ref readonly MaterialPool.MaterialDescriptor baseMaterial = ref perTexturePairMaterials[texturePairIdx];
// Make space for per-submesh materials and visibility
perInstanceSubMeshMaterials[instanceIdx] = new MaterialHandle[instanceData.subMeshMaterialIndices.Length];
perInstanceSubMeshVisibility[instanceIdx] = new bool[instanceData.subMeshMaterialIndices.Length];
// Extract per-subMesh materials
for (int subMeshIdx = 0; subMeshIdx < instanceData.subMeshMaterialIndices.Length; subMeshIdx++)
{
int subMeshMaterialIdx = instanceData.subMeshMaterialIndices[subMeshIdx];
// If we've already created this material, use it
if (materialCache.TryGetValue((texturePairIdx, subMeshMaterialIdx), out MaterialHandle existingHandle))
{
perInstanceSubMeshMaterials[instanceIdx][subMeshIdx] = existingHandle;
perInstanceSubMeshVisibility[instanceIdx][subMeshIdx] = true;
continue;
}
// Copy the base material
MaterialPool.MaterialDescriptor subMeshMaterial = baseMaterial;
// Get per-subMesh material properties, set them on the copy
if (-1 != subMeshMaterialIdx)
{
ref readonly MaterialData materialData = ref bakeInput.materialData[subMeshMaterialIdx];
subMeshMaterial.DoubleSidedGI = materialData.doubleSidedGI;
// Set transmission texture, if any
int transmissionDataIndex = bakeInput.materialToTransmissionDataIndex[subMeshMaterialIdx];
if (-1 != transmissionDataIndex)
{
subMeshMaterial.Transmission = transmissiveTextures[transmissionDataIndex];
ref readonly TextureProperties transmissionDataProperties = ref bakeInput.transmissionDataProperties[transmissionDataIndex];
subMeshMaterial.TransmissionScale = transmissionDataProperties.transmissionTextureST.scale;
subMeshMaterial.TransmissionOffset = transmissionDataProperties.transmissionTextureST.offset;
subMeshMaterial.TransmissionChannels = UnityEngine.PathTracing.Core.TransmissionChannels.RGB;
subMeshMaterial.PointSampleTransmission = transmissionDataProperties.filterMode == FilterMode.Point;
}
// Apply the stretching operation that LightBaker applies - ensures that the UV layout fills the entire UV space
if (instanceData.meshIndex >= 0)
{
Vector4 uvScaleOffset = bakeInput.meshData[instanceData.meshIndex].uvScaleOffset;
Vector2 uvScale = new Vector2(uvScaleOffset.x, uvScaleOffset.y);
Vector2 uvOffset = new Vector2(uvScaleOffset.z, uvScaleOffset.w);
subMeshMaterial.AlbedoScale = uvScale;
subMeshMaterial.AlbedoOffset = uvOffset;
subMeshMaterial.EmissionScale = uvScale;
subMeshMaterial.EmissionOffset = uvOffset;
}
else
{
subMeshMaterial.AlbedoScale = Vector2.one;
subMeshMaterial.AlbedoOffset = Vector2.zero;
subMeshMaterial.EmissionScale = Vector2.one;
subMeshMaterial.EmissionOffset = Vector2.zero;
}
}
MaterialHandle addedHandle = world.AddMaterial(in subMeshMaterial, UVChannel.UV1);
materialCache.Add((texturePairIdx, subMeshMaterialIdx), addedHandle);
perInstanceSubMeshMaterials[instanceIdx][subMeshIdx] = addedHandle;
perInstanceSubMeshVisibility[instanceIdx][subMeshIdx] = subMeshMaterialIdx != -1;
}
}
Debug.Assert(allocatedObjects.Count == allocationCount + bakeInput.albedoData.Length * 2 + bakeInput.transmissionData.Length, "InjectMaterials allocated objects incorrectly");
}
internal static Mesh TerrainDataToMesh(in TerrainData terrainData, in HeightmapData heightmapData, in TerrainHoleData holeData)
{
var outMesh = TerrainToMesh.Convert(heightmapData.resolution, heightmapData.resolution, heightmapData.data, terrainData.heightmapScale, holeData.resolution, holeData.resolution, holeData.data);
return outMesh;
}
internal static void ConvertInstancesAndMeshes(
World world,
in BakeInput bakeInput,
in MaterialHandle[][] perInstanceSubMeshMaterials,
in bool[][] perInstanceSubMeshVisibility,
out Bounds sceneBounds,
out Mesh[] meshes,
out FatInstance[] fatInstances,
List<UnityEngine.Object> allocatedObjects,
uint renderingObjectLayer)
{
sceneBounds = new Bounds();
// Extract meshes
meshes = new Mesh[bakeInput.meshData.Length];
int meshIndex = 0;
for (int i = 0; i < bakeInput.meshData.Length; i++)
{
meshes[meshIndex] = MeshDataToMesh(in bakeInput.meshData[meshIndex]);
meshes[meshIndex].name = $"{meshIndex}";
meshIndex++;
}
// Extract terrains
int terrainMeshOffset = meshIndex; // remember where the terrains start
for (int i = 0; i < bakeInput.terrainData.Length; i++)
{
var heightMap = bakeInput.heightMapData[bakeInput.terrainData[i].heightMapIndex];
var holeMap = bakeInput.terrainData[i].terrainHoleIndex >= 0 ? bakeInput.terrainHoleData[bakeInput.terrainData[i].terrainHoleIndex] : new TerrainHoleData();
meshes[meshIndex] = TerrainDataToMesh(in bakeInput.terrainData[i], in heightMap, in holeMap);
meshIndex++;
}
// Compute the tight UV scale and offset for each mesh.
Vector2[] uvBoundsSizes = new Vector2[meshes.Length];
Vector2[] uvBoundsOffsets = new Vector2[meshes.Length];
for (int i = 0; i < meshes.Length; ++i)
{
if (meshes[i].uv2.Length == 0)
LightmapIntegrationHelpers.ComputeUVBounds(meshes[i].uv, out uvBoundsSizes[i], out uvBoundsOffsets[i]);
else
LightmapIntegrationHelpers.ComputeUVBounds(meshes[i].uv2, out uvBoundsSizes[i], out uvBoundsOffsets[i]);
}
Debug.Assert(meshes.Length == bakeInput.meshData.Length);
// Baking specific settings
RenderedGameObjectsFilter filter = RenderedGameObjectsFilter.OnlyStatic;
const bool isStatic = true;
// Extract instances
List<FatInstance> fatInstanceList = new();
for (int i = 0; i < bakeInput.instanceData.Length; i++)
{
// Get materials
ref readonly InstanceData instanceData = ref bakeInput.instanceData[i];
var materials = perInstanceSubMeshMaterials[i];
var visibility = perInstanceSubMeshVisibility[i];
// Get other instance data
float4x4 localToWorldFloat4x4 = instanceData.transform;
Matrix4x4 localToWorldMatrix4x4 = new Matrix4x4(localToWorldFloat4x4.c0, localToWorldFloat4x4.c1, localToWorldFloat4x4.c2, localToWorldFloat4x4.c3);
ShadowCastingMode shadowCastingMode = instanceData.castShadows ? ShadowCastingMode.On : ShadowCastingMode.Off;
int globalMeshIndex = instanceData.meshIndex >= 0 ? instanceData.meshIndex : terrainMeshOffset + instanceData.terrainIndex; // the mesh array is a concatenation of the meshes and terrain meshes - figure out the right index
Debug.Assert(globalMeshIndex >= 0 && globalMeshIndex < meshes.Length);
Mesh mesh = meshes[globalMeshIndex];
Vector2 uvBoundsSize = uvBoundsSizes[globalMeshIndex];
Vector2 uvBoundsOffset = uvBoundsOffsets[globalMeshIndex];
// Calculate bounds
var bounds = new Bounds();
foreach (Vector3 vert in mesh.vertices)
{
bounds.Encapsulate(localToWorldMatrix4x4.MultiplyPoint(vert)); // TODO: transform the bounding box instead of looping verts (https://jira.unity3d.com/browse/GFXFEAT-667)
}
// Keep track of scene bounds as we go
if (i == 0)
sceneBounds = bounds;
else
sceneBounds.Encapsulate(bounds);
// Get masks
uint[] subMeshMasks = new uint[mesh.subMeshCount];
for (int s = 0; s < mesh.subMeshCount; ++s)
{
subMeshMasks[s] = visibility[s] ? World.GetInstanceMask(shadowCastingMode, isStatic, filter) : 0u;
}
// add instance
var boundingSphere = new BoundingSphere();
boundingSphere.position = localToWorldMatrix4x4.MultiplyPoint(mesh.bounds.center);
boundingSphere.radius = (localToWorldMatrix4x4.MultiplyPoint(mesh.bounds.extents) - boundingSphere.position).magnitude;
var lodIdentifier = new LodIdentifier(instanceData.lodGroup, instanceData.lodMask, instanceData.contributingLodLevel);
var fatInstance = new FatInstance
{
BoundingSphere = boundingSphere,
Mesh = mesh,
UVBoundsSize = uvBoundsSize,
UVBoundsOffset = uvBoundsOffset,
Materials = materials,
SubMeshMasks = subMeshMasks,
LocalToWorldMatrix = localToWorldMatrix4x4,
Bounds = bounds,
IsStatic = isStatic,
LodIdentifier = lodIdentifier,
ReceiveShadows = instanceData.receiveShadows,
Filter = filter,
RenderingObjectLayer = renderingObjectLayer,
EnableEmissiveSampling = true
};
fatInstanceList.Add(fatInstance);
}
fatInstances = fatInstanceList.ToArray();
Debug.Assert(fatInstances.Length == bakeInput.instanceData.Length);
}
internal static void PopulateWorld(InputExtraction.BakeInput input, UnityComputeWorld world, SamplingResources samplingResources, CommandBuffer cmd, bool autoEstimateLUTRange)
{
FatInstance[] fatInstances;
BakeInputToWorldConversion.DeserializeAndInjectBakeInputData(world.PathTracingWorld, autoEstimateLUTRange, in input,
out UnityEngine.Bounds sceneBounds, out world.Meshes, out fatInstances, out world.LightHandles,
world.TemporaryObjects, UnityComputeWorld.RenderingObjectLayer);
// Add instances to world
Dictionary<int, List<LodInstanceBuildData>> lodInstances;
Dictionary<Int32, List<ContributorLodInfo>> lodgroupToContributorInstances;
WorldHelpers.AddContributingInstancesToWorld(world.PathTracingWorld, in fatInstances, out lodInstances, out lodgroupToContributorInstances);
world.PathTracingWorld.Build(sceneBounds, cmd, ref world.ScratchBuffer, samplingResources, true);
}
internal static void DeserializeAndInjectBakeInputData(
World world,
bool autoEstimateLUTRange,
in InputExtraction.BakeInput bakeInput,
out Bounds sceneBounds,
out Mesh[] meshes,
out FatInstance[] fatInstances,
out LightHandle[] lightHandles,
List<UnityEngine.Object> allocatedObjects,
uint renderingObjectLayer)
{
string bakeInputPath = $"Temp/TempNative.bakeInput";
bool serializeSucceeded = LightBaking.LightBaker.Serialize(bakeInputPath, bakeInput.bakeInput);
Debug.Assert(serializeSucceeded, $"Failed to serialize input to '{bakeInputPath}'.");
LightBakerBridge.BakeInput conversionBakeInput;
bool deserializeSucceeded = BakeInputSerialization.Deserialize(bakeInputPath, out conversionBakeInput);
System.IO.File.Delete(bakeInputPath);
Debug.Assert(deserializeSucceeded, $"Failed to deserialize input from '{bakeInputPath}'.");
InjectBakeInputData(world, autoEstimateLUTRange, conversionBakeInput, out sceneBounds, out meshes, out fatInstances, out lightHandles, allocatedObjects, renderingObjectLayer);
}
internal static void InjectBakeInputData(
World world,
bool autoEstimateLUTRange,
in BakeInput bakeInput,
out Bounds sceneBounds,
out Mesh[] meshes,
out FatInstance[] fatInstances,
out LightHandle[] lightHandles,
List<UnityEngine.Object> allocatedObjects,
uint renderingObjectLayer)
{
InjectAnalyticalLights(world, autoEstimateLUTRange, bakeInput, out lightHandles, allocatedObjects);
InjectEnvironmentLight(world, bakeInput, allocatedObjects);
InjectMaterials(world, bakeInput, out var perInstanceSubMeshMaterials, out var perInstanceSubMeshVisibility, allocatedObjects);
ConvertInstancesAndMeshes(world, bakeInput, perInstanceSubMeshMaterials, perInstanceSubMeshVisibility, out sceneBounds, out meshes, out fatInstances, allocatedObjects, renderingObjectLayer);
}
}
}