diff --git a/package/com.unity.formats.usd/Dependencies/USD.NET.Unity/Geometry/LightSample.cs b/package/com.unity.formats.usd/Dependencies/USD.NET.Unity/Geometry/LightSample.cs new file mode 100644 index 000000000..5ef05a2cd --- /dev/null +++ b/package/com.unity.formats.usd/Dependencies/USD.NET.Unity/Geometry/LightSample.cs @@ -0,0 +1,153 @@ +using UnityEngine; + +namespace USD.NET.Unity +{ + [System.Serializable] + public class LightSampleBase : XformSample + { + public LightSampleBase() + { + } + + public virtual void CopyFromLight(UnityEngine.Light light, bool convertTransformToUsd = true) + { + var tr = light.transform; + transform = UnityEngine.Matrix4x4.TRS(tr.localPosition, + tr.localRotation, + tr.localScale); + if (convertTransformToUsd) + { + ConvertTransform(); + } + } + + public virtual void CopyToLight(UnityEngine.Light light, bool setTransform) + { + if (setTransform) + { + var tr = light.transform; + var xf = transform; + UnityTypeConverter.SetTransform(xf, tr); + } + } + } + + [System.Serializable] + [UsdSchema("DistantLight")] + public class DistantLightSample : LightSampleBase + { + // Core Light parameters + public float angle; + public float intensity; + + public DistantLightSample() + { + } + + public DistantLightSample(UnityEngine.Light fromLight) + { + CopyFromLight(fromLight); + } + + override public void CopyFromLight(UnityEngine.Light light, bool convertTransformToUsd = true) + { + intensity = light.intensity; + base.CopyFromLight(light, convertTransformToUsd); + } + + override public void CopyToLight(UnityEngine.Light light, bool setTransform) + { + light.type = LightType.Directional; + light.intensity = intensity; + base.CopyToLight(light, setTransform); + } + } + + [System.Serializable] + [UsdSchema("SphereLight")] + public class SphereLightSample : LightSampleBase + { + // Core Light parameters + public bool treatAsPoint; + public float radius; + + [UsdNamespace("shaping:cone")] + public float angle; + + public SphereLightSample() + { + } + + public SphereLightSample(UnityEngine.Light fromLight) + { + CopyFromLight(fromLight); + } + + override public void CopyFromLight(UnityEngine.Light light, bool convertTransformToUsd = true) + { + treatAsPoint = true; + radius = light.range; + if (light.spotAngle > 0) + { + angle = light.spotAngle; + } + base.CopyFromLight(light, convertTransformToUsd); + } + + override public void CopyToLight(UnityEngine.Light light, bool setTransform) + { + if (angle > 0) + { + light.type = LightType.Spot; + light.spotAngle = angle; + } + else + { + light.type = LightType.Point; + } + + light.range = radius; + base.CopyToLight(light, setTransform); + } + } + + [System.Serializable] + [UsdSchema("RectLight")] + public class RectLightSample : LightSampleBase + { + public RectLightSample() + { + } + + public RectLightSample(UnityEngine.Light fromLight) + { + base.CopyFromLight(fromLight); + } + + override public void CopyToLight(UnityEngine.Light light, bool setTransform) + { + light.type = LightType.Rectangle; + base.CopyToLight(light, setTransform); + } + } + + [System.Serializable] + [UsdSchema("DiskLight")] + public class DiskLightSample : LightSampleBase + { + public DiskLightSample() + { + } + + public DiskLightSample(UnityEngine.Light fromLight) + { + base.CopyFromLight(fromLight); + } + + override public void CopyToLight(UnityEngine.Light light, bool setTransform) + { + light.type = LightType.Disc; + base.CopyToLight(light, setTransform); + } + } +} \ No newline at end of file diff --git a/package/com.unity.formats.usd/Runtime/Scripts/Behaviors/UsdAsset.cs b/package/com.unity.formats.usd/Runtime/Scripts/Behaviors/UsdAsset.cs index 73f633e8c..39003b7c3 100644 --- a/package/com.unity.formats.usd/Runtime/Scripts/Behaviors/UsdAsset.cs +++ b/package/com.unity.formats.usd/Runtime/Scripts/Behaviors/UsdAsset.cs @@ -160,6 +160,7 @@ public string usdFullPath #if false [Header("Export Settings")] public bool m_exportCameras = true; + public bool m_exportLights = true; public bool m_exportMeshes = true; public bool m_exportSkinning = true; public bool m_exportTransforms = true; diff --git a/package/com.unity.formats.usd/Runtime/Scripts/IO/Geometry/LightExporter.cs b/package/com.unity.formats.usd/Runtime/Scripts/IO/Geometry/LightExporter.cs new file mode 100644 index 000000000..5b0d45df6 --- /dev/null +++ b/package/com.unity.formats.usd/Runtime/Scripts/IO/Geometry/LightExporter.cs @@ -0,0 +1,50 @@ + + + +using UnityEngine; +using USD.NET; +using USD.NET.Unity; + +namespace Unity.Formats.USD +{ + public static class LightExporter + { + public static void ExportLight(ObjectContext objContext, ExportContext exportContext) where T : LightSampleBase, new() + { + UnityEngine.Profiling.Profiler.BeginSample("USD: Light Conversion"); + + T sample = (T)objContext.sample; + Light light = objContext.gameObject.GetComponent(); + var path = objContext.path; + var scene = exportContext.scene; + bool fastConvert = exportContext.basisTransform == BasisTransformation.FastWithNegativeScale; + + sample.CopyFromLight(light, convertTransformToUsd: !fastConvert); + + if (fastConvert) + { + // Partial change of basis. + var basisChange = Matrix4x4.identity; + // Invert the forward vector. + basisChange[2, 2] = -1; + // Full change of basis would be b*t*b-1, but here we're placing only a single inversion + // at the root of the hierarchy, so all we need to do is get the camera into the same + // space. + sample.transform = sample.transform * basisChange; + + // Is this also a root path? + // If so the partial basis conversion must be completed on the camera itself. + if (path.LastIndexOf("/") == 0) + { + sample.transform = basisChange * sample.transform; + } + } + + UnityEngine.Profiling.Profiler.EndSample(); + + UnityEngine.Profiling.Profiler.BeginSample("USD: Light Write"); + scene.Write(path, sample); + UnityEngine.Profiling.Profiler.EndSample(); + } + } +} \ No newline at end of file diff --git a/package/com.unity.formats.usd/Runtime/Scripts/IO/Geometry/LightImporter.cs b/package/com.unity.formats.usd/Runtime/Scripts/IO/Geometry/LightImporter.cs new file mode 100644 index 000000000..93de91a3b --- /dev/null +++ b/package/com.unity.formats.usd/Runtime/Scripts/IO/Geometry/LightImporter.cs @@ -0,0 +1,18 @@ + + +using UnityEngine; +using USD.NET.Unity; + +namespace Unity.Formats.USD +{ + public static class LightImporter where T : LightSampleBase + { + public static void BuildLight(T usdLight, + GameObject go, + SceneImportOptions options) + { + var light = ImporterBase.GetOrAddComponent(go); + usdLight.CopyToLight(light, setTransform: false); + } + } +} \ No newline at end of file diff --git a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/HierarchyBuilder.cs b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/HierarchyBuilder.cs index 13250da71..f3ab622e9 100644 --- a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/HierarchyBuilder.cs +++ b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/HierarchyBuilder.cs @@ -133,8 +133,8 @@ static JobHandle BeginReading(Scene scene, { FindPathsJob.usdRoot = usdRoot; FindPathsJob.scene = scene; - FindPathsJob.results = new SdfPath[9][]; - FindPathsJob.queries = new FindPathsJob.IQuery[9]; + FindPathsJob.results = new SdfPath[13][]; + FindPathsJob.queries = new FindPathsJob.IQuery[13]; if (options.ShouldBindMaterials) { @@ -167,6 +167,15 @@ static JobHandle BeginReading(Scene scene, FindPathsJob.queries[8] = (FindPathsJob.IQuery) new FindPathsJob.Query(); + if (options.importLights) + { + FindPathsJob.queries[9] = (FindPathsJob.IQuery) new FindPathsJob.Query(); + FindPathsJob.queries[10] = (FindPathsJob.IQuery) new FindPathsJob.Query(); + FindPathsJob.queries[11] = (FindPathsJob.IQuery) new FindPathsJob.Query(); + FindPathsJob.queries[12] = (FindPathsJob.IQuery) new FindPathsJob.Query(); + + } + var findPathsJob = new FindPathsJob(); var findHandle = findPathsJob.Schedule(FindPathsJob.queries.Length, 1); findHandle.Complete(); @@ -185,6 +194,10 @@ static JobHandle BeginReading(Scene scene, map.SkelRoots = FindPathsJob.results[5]; map.Skeletons = FindPathsJob.results[6]; map.Xforms = FindPathsJob.results[7]; + map.DirectionalLights = FindPathsJob.results[9]; + map.SphereLights = FindPathsJob.results[10]; + map.RectLights = FindPathsJob.results[11]; + map.DiscLights = FindPathsJob.results[12]; ReadHierJob.paths = FindPathsJob.results.Where(i => i != null).SelectMany(i => i).ToArray(); ReadHierJob.result = new HierInfo[ReadHierJob.paths.Length]; diff --git a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/PrimMap.cs b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/PrimMap.cs index 8466ad37e..8873d8ecb 100644 --- a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/PrimMap.cs +++ b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/PrimMap.cs @@ -47,6 +47,10 @@ public struct InstanceRoot public SdfPath[] SkelRoots { get; set; } public SdfPath[] Skeletons { get; set; } public SdfPath[] Materials { get; set; } + public SdfPath[] DirectionalLights { get; set; } + public SdfPath[] SphereLights { get; set; } + public SdfPath[] RectLights { get; set; } + public SdfPath[] DiscLights { get; set; } // Normal objects in the hierarchy. private Dictionary m_prims = new Dictionary(); @@ -166,6 +170,10 @@ public void Clear() SkelRoots = null; Skeletons = null; Materials = null; + DirectionalLights = null; + SphereLights = null; + RectLights = null; + DiscLights = null; } } } diff --git a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneExporter.cs b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneExporter.cs index 5906d6848..32bc61c00 100644 --- a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneExporter.cs +++ b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneExporter.cs @@ -541,6 +541,7 @@ static void InitExportableObjects(GameObject go, var mr = go.GetComponent(); var mf = go.GetComponent(); var cam = go.GetComponent(); + var lig = go.GetComponent(); Transform expRoot = context.exportRoot; var tmpPath = new pxr.SdfPath(UnityTypeConverter.GetPath(go.transform, expRoot)); @@ -623,6 +624,22 @@ static void InitExportableObjects(GameObject go, CreateExportPlan(go, CreateSample(context), NativeExporter.ExportObject, context, insertFirst: false); } + else if (lig != null) + { + if (lig.type == LightType.Directional) + CreateExportPlan(go, CreateSample(context), LightExporter.ExportLight, context); + else if (lig.type == LightType.Spot) + CreateExportPlan(go, CreateSample(context), LightExporter.ExportLight, context); + else if (lig.type == LightType.Point) + CreateExportPlan(go, CreateSample(context), LightExporter.ExportLight, context); + else if (lig.type == LightType.Rectangle) + CreateExportPlan(go, CreateSample(context), LightExporter.ExportLight, context); + else if (lig.type == LightType.Disc) + CreateExportPlan(go, CreateSample(context), LightExporter.ExportLight, context); + + CreateExportPlan(go, CreateSample(context), NativeExporter.ExportObject, context, + insertFirst: false); + } } static Transform MergeBonesBelowAnimator(Transform animator, ExportContext context) diff --git a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImportOptions.cs b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImportOptions.cs index 339c99cab..c976d47bf 100644 --- a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImportOptions.cs +++ b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImportOptions.cs @@ -100,6 +100,7 @@ public class SceneImportOptions public bool importHierarchy = true; public bool importCameras = true; + public bool importLights = true; public bool importMeshes = true; public bool importSkinning = true; public bool importSkinWeights = true; diff --git a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImporter.cs b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImporter.cs index c68b1ed68..d54b11856 100644 --- a/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImporter.cs +++ b/package/com.unity.formats.usd/Runtime/Scripts/IO/Scene/SceneImporter.cs @@ -751,6 +751,42 @@ public static IEnumerator BuildScene(Scene scene, Profiler.EndSample(); } + // Lights. + if (importOptions.importLights) + { + Profiler.BeginSample("USD: Lights"); + + void importLights(pxr.SdfPath[] dest) where T : LightSampleBase, new() + { + foreach (var pathAndSample in scene.ReadAll(dest)) + { + try + { + GameObject go = primMap[pathAndSample.path]; + NativeImporter.ImportObject(scene, go, scene.GetPrimAtPath(pathAndSample.path), importOptions); + XformImporter.BuildXform(pathAndSample.path, pathAndSample.sample, go, importOptions, scene); + + if (scene.AccessMask == null || scene.IsPopulatingAccessMask) + { + LightImporter.BuildLight(pathAndSample.sample, go, importOptions); + } + } + catch (System.Exception ex) + { + Debug.LogException( + new ImportException("Error processing light <" + pathAndSample.path + ">", ex)); + } + } + } + + importLights(primMap.DirectionalLights); + importLights(primMap.SphereLights); + importLights(primMap.RectLights); + importLights(primMap.DiscLights); + + Profiler.EndSample(); + } + // Build out masters for instancing. Profiler.BeginSample("USD: Build Instances"); foreach (var masterRootPath in primMap.GetMasterRootPaths())