// SPDX-FileCopyrightText: 2024 Unity Technologies and the glTFast authors // SPDX-License-Identifier: Apache-2.0 using GLTFast.Schema; using UnityEngine; #if USING_HDRP using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; #endif namespace GLTFast.Export { /// /// Provides conversion from Unity light components to glTF lights. /// public static class KhrLightsPunctual { /// /// Converts a Unity light component to a glTF light. /// /// Unity light component. /// glTF light. public static LightPunctual ConvertToLight(Light uLight) { var light = new LightPunctual { name = uLight.name }; var renderPipeline = RenderPipelineUtils.RenderPipeline; var lightType = uLight.type; #if USING_HDRP HDAdditionalLightData lightHd = null; if (renderPipeline == RenderPipeline.HighDefinition) { lightHd = uLight.gameObject.GetComponent(); #if !UNITY_2023_2_OR_NEWER lightType = lightHd != null ? lightHd.TryGetLightType() ?? lightType : lightType; #endif } #endif switch (lightType) { case LightType.Spot: light.SetLightType(LightPunctual.Type.Spot); light.spot = new SpotLight { outerConeAngle = uLight.spotAngle * Mathf.Deg2Rad * .5f, }; #if USING_HDRP && !UNITY_6000_3_OR_NEWER if (renderPipeline == RenderPipeline.HighDefinition) { // Up until Unity 6.2/HDRP 17.2 lightHd.innerSpotPercent was used // instead of uLight.innerSpotAngle. light.spot.innerConeAngle = lightHd != null ? uLight.spotAngle * Mathf.Deg2Rad * .5f * lightHd.innerSpotPercent01 : 0; } else #endif { light.spot.innerConeAngle = uLight.innerSpotAngle * Mathf.Deg2Rad * .5f; } break; case LightType.Directional: light.SetLightType(LightPunctual.Type.Directional); break; case LightType.Point: light.SetLightType(LightPunctual.Type.Point); break; case LightType.Rectangle: case LightType.Disc: default: light.SetLightType(LightPunctual.Type.Spot); light.spot = new SpotLight { outerConeAngle = 45 * Mathf.Deg2Rad * .5f, innerConeAngle = 35 * Mathf.Deg2Rad * .5f }; break; } light.LightColor = uLight.color.linear; light.range = GetLightRange(uLight, lightType); // Set Light intensity switch (renderPipeline) { case RenderPipeline.BuiltIn: light.intensity = uLight.intensity * Mathf.PI; break; case RenderPipeline.Universal: light.intensity = uLight.intensity; break; #if USING_HDRP case RenderPipeline.HighDefinition: if (lightHd == null) { light.intensity = uLight.intensity; } else { #if UNITY_2023_2_OR_NEWER switch (lightType) { case LightType.Spot: case LightType.Point: #if RENDER_PIPELINES_CORE_17_OR_NEWER light.intensity = LightUnitUtils.ConvertIntensity(uLight, uLight.intensity, uLight.lightUnit, LightUnit.Candela); #else light.intensity = GetIntensity(LightUnit.Candela); #endif break; case LightType.Directional: #if RENDER_PIPELINES_CORE_17_OR_NEWER light.intensity = LightUnitUtils.ConvertIntensity(uLight, uLight.intensity, uLight.lightUnit, LightUnit.Lux); #else light.intensity = GetIntensity(LightUnit.Lux); #endif break; case LightType.Rectangle: default: light.intensity = uLight.intensity; break; } #else switch (lightHd.type) { case HDLightType.Spot: case HDLightType.Point: light.intensity = GetIntensity(LightUnit.Candela); break; case HDLightType.Directional: light.intensity = GetIntensity(LightUnit.Lux); break; case HDLightType.Area: default: light.intensity = uLight.intensity; break; } #endif } break; #if !RENDER_PIPELINES_CORE_17_OR_NEWER float GetIntensity(LightUnit unit) { #if UNITY_2023_3_OR_NEWER if (uLight.lightUnit == unit) { return uLight.intensity; } // Workaround to get intensity in candela var oldUnit = uLight.lightUnit; uLight.lightUnit = unit; var result = uLight.intensity; uLight.lightUnit = oldUnit; return result; #else if (lightHd.lightUnit == unit) { return lightHd.intensity; } // Workaround to get intensity in candela var oldUnit = lightHd.lightUnit; lightHd.lightUnit = unit; var result = lightHd.intensity; lightHd.lightUnit = oldUnit; return result; #endif } #endif // !RENDER_PIPELINES_CORE_17_OR_NEWER #endif // USING_HDRP default: light.intensity = uLight.intensity; break; } return light; } /// /// Retrieves the light's range. /// In Unity 2023.1 and older `Light.range` would return what now is , which is /// the range extended by a certain area light size factor. This method removes that addition in that case to /// get the original value that's shown in the inspector. /// /// Unity Light /// Actual light type (might differ from light.type in HDRP). /// The light's range. static float GetLightRange(Light light, LightType lightType) { #if UNITY_2023_2_OR_NEWER return light.range; #else var range = light.range; switch (lightType) { #if !USING_HDRP // And, of course, it behaves correctly in the particular case HDRP+Rectangle. case LightType.Rectangle: #if UNITY_EDITOR var longestSide = light.areaSize.magnitude; #else // At runtime, assume default magnitude(vec2(1,1)) var longestSide = 1.4142135624f; #endif range -= longestSide * .5f; break; #endif // !USING_HDRP case LightType.Disc: #if UNITY_EDITOR var radius = light.areaSize.x; #else // At runtime, assume default const float radius = 1f; #endif range -= radius * .5f; break; } return range; #endif } } }