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; using LightHandle = Handle; 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 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().CopyFrom(vertexData.data); outRawMesh.SetIndexBufferParams(meshData.indexBuffer.Length, IndexFormat.UInt32); outRawMesh.GetIndexData().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 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 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 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 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 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> lodInstances; Dictionary> 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 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 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); } } }