diff --git a/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs b/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs index 25a0749..dd86fcb 100644 --- a/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs +++ b/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs @@ -19,7 +19,7 @@ public static void BakeTextBackendMeshAndMaterial(this IBaker baker, Renderer re { var mesh = Resources.Load(kTextBackendMeshResource); - RenderingBakingTools.GetLOD(baker, renderer, out var lodGroupEntity, out var lodMask); + RenderingBakingTools.GetLOD(baker, renderer, out var lodSettings); var rendererSettings = new MeshRendererBakeSettings { @@ -30,8 +30,7 @@ public static void BakeTextBackendMeshAndMaterial(this IBaker baker, Renderer re useLightmapsIfPossible = true, lightmapIndex = renderer.lightmapIndex, lightmapScaleOffset = renderer.lightmapScaleOffset, - lodGroupEntity = lodGroupEntity, - lodGroupMask = lodMask, + lodSettings = lodSettings, isStatic = baker.IsStatic(), localBounds = default, }; diff --git a/Calligraphics/Fonts/LiberationSans SDF.asset b/Calligraphics/Fonts/LiberationSans SDF.asset index b126ae4..9b535ee 100644 --- a/Calligraphics/Fonts/LiberationSans SDF.asset +++ b/Calligraphics/Fonts/LiberationSans SDF.asset @@ -12,14 +12,18 @@ Material: type: 3} m_Parent: {fileID: 0} m_ModifiedSerializedProperties: 0 - m_ValidKeywords: [] + m_ValidKeywords: + - _ALPHATEST_ON + - _SURFACE_TYPE_TRANSPARENT m_InvalidKeywords: [] m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - DepthOnly m_LockedProperties: m_SavedProperties: serializedVersion: 3 @@ -54,6 +58,8 @@ Material: m_Offset: {x: 0, y: 0} m_Ints: [] m_Floats: + - _AlphaClip: 1 + - _AlphaToMask: 0 - _Ambient: 0.3 - _BevelAmount: 0 - _BevelClamp: 0 @@ -61,9 +67,13 @@ Material: - _BevelRoundness: 0 - _BevelType: 0 - _BevelWidth: 0 + - _Blend: 0 + - _CastShadows: 1 - _ColorMask: 15 + - _Cull: 0 - _CullMode: 0 - _Diffuse: 0.3 + - _DstBlend: 10 - _FaceDilate: 0 - _GradientScale: 10 - _LightAngle: 0 @@ -87,11 +97,13 @@ Material: - _ShaderFlags: 0 - _Sharpness: 0 - _SpecularPower: 0 + - _SrcBlend: 5 - _Stencil: 0 - _StencilComp: 8 - _StencilOp: 0 - _StencilReadMask: 255 - _StencilWriteMask: 255 + - _Surface: 1 - _TextureHeight: 1024 - _TextureWidth: 1024 - _UnderlayDilate: 0 @@ -102,6 +114,10 @@ Material: - _VertexOffsetY: 0 - _WeightBold: 0.75 - _WeightNormal: 0 + - _ZTest: 4 + - _ZWrite: 0 + - _ZWriteControl: 0 + - _latiosTextGlyphMaskBase: 0 m_Colors: - _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767} - _FaceColor: {r: 1, g: 1, b: 1, a: 1} @@ -137,6 +153,24 @@ MonoBehaviour: m_Version: 1.1.0 m_Material: {fileID: -658778759613031741} m_SourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75 + m_fontAssetCreationEditorSettings: + sourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75 + faceIndex: 0 + pointSizeSamplingMode: 0 + pointSize: 84 + padding: 9 + paddingMode: 2 + packingMode: 0 + atlasWidth: 1024 + atlasHeight: 1024 + characterSetSelectionMode: 1 + characterSequence: 32 - 126, 160 - 255, 8192 - 8303, 8364, 8482, 9633 + referencedFontAssetGUID: b773c04e36205284fa9d7b06cf720e42 + referencedTextAssetGUID: + fontStyle: 0 + fontStyleModifier: 0 + renderMode: 4165 + includeFontFeatures: 1 m_SourceFontFile: {fileID: 0} m_SourceFontFilePath: m_AtlasPopulationMode: 0 @@ -8161,24 +8195,6 @@ MonoBehaviour: m_MarkToBaseAdjustmentRecords: [] m_MarkToMarkAdjustmentRecords: [] m_FallbackFontAssetTable: [] - m_fontAssetCreationEditorSettings: - sourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75 - faceIndex: 0 - pointSizeSamplingMode: 0 - pointSize: 84 - padding: 9 - paddingMode: 2 - packingMode: 0 - atlasWidth: 1024 - atlasHeight: 1024 - characterSetSelectionMode: 1 - characterSequence: 32 - 126, 160 - 255, 8192 - 8303, 8364, 8482, 9633 - referencedFontAssetGUID: b773c04e36205284fa9d7b06cf720e42 - referencedTextAssetGUID: - fontStyle: 0 - fontStyleModifier: 0 - renderMode: 4165 - includeFontFeatures: 1 m_FontWeightTable: - regularTypeface: {fileID: 0} italicTypeface: {fileID: 0} diff --git a/Calligraphics/Internal/RichText/RichTextParser.cs b/Calligraphics/Internal/RichText/RichTextParser.cs index 0e4a771..1d401a5 100644 --- a/Calligraphics/Internal/RichText/RichTextParser.cs +++ b/Calligraphics/Internal/RichText/RichTextParser.cs @@ -839,36 +839,36 @@ internal static bool ValidateHtmlTag( // } // } // return true; - case 275917: // - case 186285: // - switch (firstTagIndentifier.valueHashCode) - { - case 3774683: // - textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Left; - textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); - return true; - case 136703040: // - textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Right; - textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); - return true; - case -458210101: // - textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Center; - textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); - return true; - case -523808257: // - textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Justified; - textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); - return true; - case 122383428: // - textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Flush; - textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); - return true; - } - return false; - case 1065846: // - case 976214: // - textConfiguration.m_lineJustification = textConfiguration.m_lineJustificationStack.RemoveExceptRoot(); - return true; + //case 275917: // + //case 186285: // + // switch (firstTagIndentifier.valueHashCode) + // { + // case 3774683: // + // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Left; + // textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); + // return true; + // case 136703040: // + // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Right; + // textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); + // return true; + // case -458210101: // + // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Center; + // textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); + // return true; + // case -523808257: // + // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Justified; + // textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); + // return true; + // case 122383428: // + // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Flush; + // textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); + // return true; + // } + // return false; + //case 1065846: // + //case 976214: // + // textConfiguration.m_lineJustification = textConfiguration.m_lineJustificationStack.RemoveExceptRoot(); + // return true; case 327550: // case 237918: // calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); @@ -1526,8 +1526,8 @@ internal static bool ValidateHtmlTag( // m_actionStack.Remove(); // return true; - case 315682: // - case 226050: // + case 315682: // + case 226050: // calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) @@ -1536,8 +1536,8 @@ internal static bool ValidateHtmlTag( textConfiguration.m_FXScale = new Vector3(value, 1, 1); return true; - case 1105611: // - case 1015979: // + case 1105611: // + case 1015979: // textConfiguration.m_FXScale = 1; return true; case 2227963: // diff --git a/Calligraphics/Shaders/Calligraphics-URP.shadergraph b/Calligraphics/Shaders/Calligraphics-URP.shadergraph index 64f414e..629af92 100644 --- a/Calligraphics/Shaders/Calligraphics-URP.shadergraph +++ b/Calligraphics/Shaders/Calligraphics-URP.shadergraph @@ -812,7 +812,6 @@ "m_OutputNode": { "m_Id": "" }, - "m_SubDatas": [], "m_ActiveTargets": [ { "m_Id": "94300469581b4924ac7dda496811d45d" @@ -3762,7 +3761,7 @@ "m_ActiveSubTarget": { "m_Id": "0eeb5490760e492f8c9691086fa00929" }, - "m_AllowMaterialOverride": false, + "m_AllowMaterialOverride": true, "m_SurfaceType": 1, "m_ZTestMode": 4, "m_ZWriteControl": 0, diff --git a/Kinemation/Authoring/BakingSystems/LatiosFrozenStaticRendererSystem.cs b/Kinemation/Authoring/BakingSystems/LatiosFrozenStaticRendererSystem.cs index aaa62d4..960edac 100644 --- a/Kinemation/Authoring/BakingSystems/LatiosFrozenStaticRendererSystem.cs +++ b/Kinemation/Authoring/BakingSystems/LatiosFrozenStaticRendererSystem.cs @@ -1,5 +1,3 @@ -using Latios; -using Latios.Transforms; using Latios.Transforms.Abstract; using Unity.Burst; using Unity.Collections; @@ -8,6 +6,8 @@ using Unity.Mathematics; using Unity.Rendering; +// Todo: This is a dead system due to the query for RenderMesh. Unity hasn't updated the query either. + namespace Latios.Kinemation.Authoring { [WorldSystemFilter(WorldSystemFilterFlags.EntitySceneOptimizations)] diff --git a/Kinemation/Authoring/KinemationBakingBootstrap.cs b/Kinemation/Authoring/KinemationBakingBootstrap.cs index 284561e..9be5871 100644 --- a/Kinemation/Authoring/KinemationBakingBootstrap.cs +++ b/Kinemation/Authoring/KinemationBakingBootstrap.cs @@ -37,7 +37,6 @@ public static void InstallKinemation(ref CustomBakingBootstrapContext context) context.optimizationSystemTypesToInject.Add(TypeManager.GetSystemTypeIndex()); context.optimizationSystemTypesToDisable.Add(TypeManager.GetSystemTypeIndex()); - context.optimizationSystemTypesToInject.Add(TypeManager.GetSystemTypeIndex()); context.optimizationSystemTypesToInject.Add(TypeManager.GetSystemTypeIndex()); context.optimizationSystemTypesToInject.Add(TypeManager.GetSystemTypeIndex()); } diff --git a/Kinemation/Authoring/LatiosMeshRendererBakingUtility.cs b/Kinemation/Authoring/LatiosMeshRendererBakingUtility.cs index c6d785a..bfab6a8 100644 --- a/Kinemation/Authoring/LatiosMeshRendererBakingUtility.cs +++ b/Kinemation/Authoring/LatiosMeshRendererBakingUtility.cs @@ -128,10 +128,45 @@ internal static void Convert(IBaker baker, if (renderer.isDeforming) requiredPropertiesForReference |= classification; - if (renderer.lodGroupEntity != Entity.Null && renderer.lodGroupMask != -1) + if (!renderer.lodSettings.Equals(default(LodSettings))) { - var lodComponent = new MeshLODComponent { Group = renderer.lodGroupEntity, LODMask = renderer.lodGroupMask }; - baker.AddComponent(renderer.targetEntity, lodComponent); + bool negHeight = (renderer.lodSettings.lowestResLodLevel & 0x1) == 1; + bool negMin = (renderer.lodSettings.lowestResLodLevel & 0x2) == 2; + bool negMax = (renderer.lodSettings.lowestResLodLevel & 0x4) == 4; + if (negHeight) + renderer.lodSettings.localHeight *= -1; + if (negMin) + renderer.lodSettings.minScreenHeightPercent *= new half(-1f); + if (negMax) + renderer.lodSettings.maxScreenHeightPercent *= new half(-1f); + + if (renderer.lodSettings.minScreenHeightPercentAtCrossfadeEdge > 0f || renderer.lodSettings.maxScreenHeightPercentAtCrossfadeEdge > 0f) + { + if (renderer.lodSettings.isSpeedTree) + baker.AddComponent(renderer.targetEntity); + + if (renderer.lodSettings.maxScreenHeightPercentAtCrossfadeEdge < 0f) + renderer.lodSettings.maxScreenHeightPercentAtCrossfadeEdge = half.MaxValueAsHalf; + + baker.AddComponent(renderer.targetEntity); + baker.AddComponent( renderer.targetEntity, new LodHeightPercentagesWithCrossfadeMargins + { + localSpaceHeight = renderer.lodSettings.localHeight, + maxCrossFadeEdge = renderer.lodSettings.maxScreenHeightPercentAtCrossfadeEdge, + maxPercent = renderer.lodSettings.maxScreenHeightPercent, + minCrossFadeEdge = renderer.lodSettings.minScreenHeightPercentAtCrossfadeEdge, + minPercent = renderer.lodSettings.minScreenHeightPercent + }); + } + else + { + baker.AddComponent(renderer.targetEntity, new LodHeightPercentages + { + localSpaceHeight = renderer.lodSettings.localHeight, + maxPercent = renderer.lodSettings.maxScreenHeightPercent, + minPercent = renderer.lodSettings.minScreenHeightPercent + }); + } } if (renderer.useLightmapsIfPossible) diff --git a/Kinemation/Authoring/OverrideMeshRendererBaker.cs b/Kinemation/Authoring/OverrideMeshRendererBaker.cs index 241d080..2d289ce 100644 --- a/Kinemation/Authoring/OverrideMeshRendererBaker.cs +++ b/Kinemation/Authoring/OverrideMeshRendererBaker.cs @@ -95,19 +95,67 @@ public struct MeshRendererBakeSettings /// public float4 lightmapScaleOffset; /// - /// The entity that contains the LOD group components + /// The LOD settings for this entity, typically extracted from a belonging LOD group /// - public Entity lodGroupEntity; - /// - /// A bitmask where each bit represents a LOD level in the LOD group this entity belongs to - /// - public int lodGroupMask; + public LodSettings lodSettings; /// /// True if the entity should be treated as if it were marked static /// public bool isStatic; } + /// + /// A struct which describes the membership within a LOD group. + /// + public struct LodSettings : IEquatable + { + /// + /// The local height as dictated by the LOD Group + /// + public float localHeight; + /// + /// The smallest percentage of the screen height this entity should consume to be drawn + /// + public half minScreenHeightPercent; + /// + /// The largest percentage of the screen height this entity should consume to be drawn + /// + public half maxScreenHeightPercent; + /// + /// The percentage of the screen height at which the entity begins crossfading to the LOD that + /// shares the minScreenHeightPercent. (The adjacent LOD is higher indexed and lower-res or complete fade-out.) + /// Zero or negative if crossfade is unused for this edge. + /// + public half minScreenHeightPercentAtCrossfadeEdge; + /// + /// The percentage of the screen height at which the entity begins crossfading to the LOD that + /// shares the maxScreenHeightPercent. (The adjacent LOD is lower indexed and higher res.) + /// Zero or negative if crossfade is unused for this edge. + /// + public half maxScreenHeightPercentAtCrossfadeEdge; + + /// + /// The LOD level used to compare against QualitySettings.maximumLODLevel + /// + public byte lowestResLodLevel; + + /// + /// True if the crossfade mode uses speedtree + /// + public bool isSpeedTree; + + public bool Equals(LodSettings other) + { + return localHeight.Equals(other.localHeight) && + minScreenHeightPercent.Equals(other.minScreenHeightPercent) && + maxScreenHeightPercent.Equals(other.maxScreenHeightPercent) && + minScreenHeightPercentAtCrossfadeEdge.Equals(other.minScreenHeightPercentAtCrossfadeEdge) && + maxScreenHeightPercentAtCrossfadeEdge.Equals(other.maxScreenHeightPercentAtCrossfadeEdge) && + lowestResLodLevel.Equals(other.lowestResLodLevel) && + isSpeedTree.Equals(other.isSpeedTree); + } + } + /// /// A combination of mesh, material, and submesh that can be rendered /// @@ -186,38 +234,79 @@ public static int GroupByDepthSorting(Span meshMate } /// - /// Acquires the LOD Group entity and mask that the passed in renderer belongs to + /// Acquires the LOD Group the renderer belongs to and extracts LOD settings for the renderer. /// /// The baker used to search for the LOD Group in the hierarchy /// The renderer to find within the LOD Group - /// The entity which represents the LOD Group - /// The bitmask that represents the levels in the LOD Group the renderer belongs to - public static void GetLOD(IBaker baker, Renderer renderer, out Entity lodGroupEntity, out int lodMask) + /// The LOD settings for this renderer inside the LOD Group + public static void GetLOD(IBaker baker, Renderer renderer, out LodSettings lodSettings) { - var group = baker.GetComponentInParent(); - lodGroupEntity = baker.GetEntity(group, TransformUsageFlags.Renderable); + var group = baker.GetComponentInParent(); + lodSettings = default; if (group != null) { - lodMask = 0; - var lodGroupLODs = group.GetLODs(); + lodSettings.localHeight = group.size; + lodSettings.isSpeedTree = group.fadeMode == LODFadeMode.SpeedTree; + var lodGroupLODs = group.GetLODs(); - int lodGroupMask = 0; + float previousMargin = -1f; + float previousTransition = 1f; + bool foundFirst = false; // Find the renderer inside the LODGroup for (int i = 0; i < lodGroupLODs.Length; ++i) { + float currentMargin = group.fadeMode != LODFadeMode.None ? math.lerp(lodGroupLODs[i].screenRelativeTransitionHeight, + previousTransition, + lodGroupLODs[i].fadeTransitionWidth) : -1f; foreach (var candidate in lodGroupLODs[i].renderers) { if (renderer == candidate) { - lodGroupMask |= (1 << i); + if (!foundFirst) + { + if (previousMargin > 0f) + { + // For non-animated cross-fade, the fade starts before the transition and ends at the transition. + lodSettings.maxScreenHeightPercent = (half)previousMargin; + lodSettings.maxScreenHeightPercentAtCrossfadeEdge = (half)previousTransition; + } + else + { + lodSettings.maxScreenHeightPercent = (half)previousTransition; + lodSettings.maxScreenHeightPercentAtCrossfadeEdge = new half(-1f); + } + foundFirst = true; + } + if (currentMargin > 0f) + { + lodSettings.minScreenHeightPercentAtCrossfadeEdge = (half)currentMargin; + } + lodSettings.minScreenHeightPercent = (half)lodGroupLODs[i].screenRelativeTransitionHeight; + lodSettings.lowestResLodLevel = (byte)i; + break; } } + previousMargin = currentMargin; + previousTransition = lodGroupLODs[i].screenRelativeTransitionHeight; + } + if (lodSettings.maxScreenHeightPercent <= 0f) + { + lodSettings = default; + return; } - lodMask = lodGroupMask > 0 ? lodGroupMask : -1; + + // We have found valid LODs. But we should validate that the screenHeight is useful or if an offset would be required. + var rendererTransform = baker.GetComponent(renderer); + var groupTransform = baker.GetComponent(group); + var relativeTransform = Transforms.Authoring.Abstract.AbstractBakingUtilities.ExtractTransformRelativeTo(rendererTransform, groupTransform); + if (math.lengthsq(relativeTransform.position) > math.EPSILON) + { + Debug.LogWarning( + $"LOD renderer {renderer.gameObject.name} has a different world position than the LOD Group {group.gameObject.name} it belongs to. This is currently not supported and artifacts may occur. If you are seeing this message, please report it to the Latios Framework developers so that we can better understand your use case."); + } + lodSettings.localHeight /= math.cmax(relativeTransform.scale * relativeTransform.stretch); } - else - lodMask = -1; } } @@ -254,7 +343,7 @@ public override void Bake(MeshRenderer authoring) RenderingBakingTools.ExtractMeshMaterialSubmeshes(mms, mesh, m_materialsCache); var opaqueMaterialCount = RenderingBakingTools.GroupByDepthSorting(mms); - RenderingBakingTools.GetLOD(this, authoring, out var lodGroupEntity, out var lodMask); + RenderingBakingTools.GetLOD(this, authoring, out var lodSettings); var rendererSettings = new MeshRendererBakeSettings { @@ -265,8 +354,7 @@ public override void Bake(MeshRenderer authoring) useLightmapsIfPossible = true, lightmapIndex = authoring.lightmapIndex, lightmapScaleOffset = authoring.lightmapScaleOffset, - lodGroupEntity = lodGroupEntity, - lodGroupMask = lodMask, + lodSettings = lodSettings, isStatic = IsStatic(), localBounds = mesh != null ? mesh.bounds : default, }; diff --git a/Kinemation/Authoring/SkinnedMeshBaker.cs b/Kinemation/Authoring/SkinnedMeshBaker.cs index 6462908..10f8737 100644 --- a/Kinemation/Authoring/SkinnedMeshBaker.cs +++ b/Kinemation/Authoring/SkinnedMeshBaker.cs @@ -174,7 +174,7 @@ public override void Bake(SkinnedMeshRenderer authoring) RenderingBakingTools.ExtractMeshMaterialSubmeshes(mms, sharedMesh, m_materialsCache); var opaqueMaterialCount = RenderingBakingTools.GroupByDepthSorting(mms); - RenderingBakingTools.GetLOD(this, authoring, out var lodGroupEntity, out var lodMask); + RenderingBakingTools.GetLOD(this, authoring, out var lodSettings); var rendererSettings = new MeshRendererBakeSettings { @@ -185,8 +185,7 @@ public override void Bake(SkinnedMeshRenderer authoring) useLightmapsIfPossible = true, lightmapIndex = authoring.lightmapIndex, lightmapScaleOffset = authoring.lightmapScaleOffset, - lodGroupEntity = lodGroupEntity, - lodGroupMask = lodMask, + lodSettings = lodSettings, isStatic = IsStatic(), localBounds = sharedMesh != null ? sharedMesh.bounds : default, }; diff --git a/Kinemation/Components/MeshComponents.cs b/Kinemation/Components/MeshComponents.cs index 315e283..040bbc3 100644 --- a/Kinemation/Components/MeshComponents.cs +++ b/Kinemation/Components/MeshComponents.cs @@ -8,19 +8,19 @@ namespace Latios.Kinemation { #region All Meshes - public struct LodCrossfade : IComponentData + public struct LodCrossfade : IComponentData, IEnableableComponent { public byte raw; - public void SetOpacity(float opacity, bool isComplementary) + public void SetHiResOpacity(float opacity, bool isLowRes) { int snorm = (int)math.round(opacity * 127f); - snorm = math.select(snorm, -snorm, isComplementary); + snorm = math.select(snorm, -snorm, isLowRes); snorm &= 0xff; raw = (byte)snorm; } - public float opacity + public float hiResOpacity { get { @@ -31,6 +31,28 @@ public float opacity } } + // Note: You might think it would be better to cache the world-space heights before the culling callbacks. + // However, we still need the positions for distance calculations. + public struct LodHeightPercentages : IComponentData + { + // Signs represent the LOD index + public float localSpaceHeight; + public half minPercent; + public half maxPercent; + } + + public struct LodHeightPercentagesWithCrossfadeMargins : IComponentData + { + // Signs of first three fields represent the LOD index + public float localSpaceHeight; + public half minPercent; + public half maxPercent; + public half minCrossFadeEdge; // if negative, then disable crossfade + public half maxCrossFadeEdge; // if negative, then disable crossfade + } + + public struct SpeedTreeCrossfadeTag : IComponentData { } + /// /// An optional component that when present will be enabled for the duration of the frame /// following a frame it was rendered by some view (including shadows), and disabled otherwise. diff --git a/Kinemation/ShaderLibrary/URP14-LOD_Crossfade_Fix_Node.shadersubgraph b/Kinemation/ShaderLibrary/URP14-LOD_Crossfade_Fix_Node.shadersubgraph new file mode 100644 index 0000000..a58e134 --- /dev/null +++ b/Kinemation/ShaderLibrary/URP14-LOD_Crossfade_Fix_Node.shadersubgraph @@ -0,0 +1,342 @@ +{ + "m_SGVersion": 3, + "m_Type": "UnityEditor.ShaderGraph.GraphData", + "m_ObjectId": "9f231329493c44708e077f712ddb8630", + "m_Properties": [ + { + "m_Id": "0721b204640d4b438573b1bfc38dfb7e" + } + ], + "m_Keywords": [], + "m_Dropdowns": [], + "m_CategoryData": [ + { + "m_Id": "7b7b6c7f34fa449689edd844fdebd5ec" + } + ], + "m_Nodes": [ + { + "m_Id": "676cda1a11a449e39c5114cdad34796d" + }, + { + "m_Id": "4917051f90634e10a316653421e9349d" + }, + { + "m_Id": "6550f4bc2d53489d83f87395a9199fd5" + } + ], + "m_GroupDatas": [], + "m_StickyNoteDatas": [], + "m_Edges": [ + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "4917051f90634e10a316653421e9349d" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "676cda1a11a449e39c5114cdad34796d" + }, + "m_SlotId": 1 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "6550f4bc2d53489d83f87395a9199fd5" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "4917051f90634e10a316653421e9349d" + }, + "m_SlotId": 1 + } + } + ], + "m_VertexContext": { + "m_Position": { + "x": 0.0, + "y": 0.0 + }, + "m_Blocks": [] + }, + "m_FragmentContext": { + "m_Position": { + "x": 0.0, + "y": 0.0 + }, + "m_Blocks": [] + }, + "m_PreviewData": { + "serializedMesh": { + "m_SerializedMesh": "{\"mesh\":{\"instanceID\":0}}", + "m_Guid": "" + }, + "preventRotation": false + }, + "m_Path": "Sub Graphs", + "m_GraphPrecision": 1, + "m_PreviewMode": 2, + "m_OutputNode": { + "m_Id": "676cda1a11a449e39c5114cdad34796d" + }, + "m_ActiveTargets": [] +} + +{ + "m_SGVersion": 1, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_ObjectId": "0721b204640d4b438573b1bfc38dfb7e", + "m_Guid": { + "m_GuidSerialized": "b8135b0f-b19d-4850-a9c4-9c40b136df41" + }, + "m_Name": "In_Value", + "m_DefaultRefNameVersion": 1, + "m_RefNameGeneratedByDisplayName": "In_Value", + "m_DefaultReferenceName": "_In_Value", + "m_OverrideReferenceName": "", + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 2, + "m_Hidden": false, + "m_Value": { + "x": 1.0, + "y": 1.0, + "z": 1.0, + "w": 1.0 + } +} + +{ + "m_SGVersion": 1, + "m_Type": "UnityEditor.ShaderGraph.CustomFunctionNode", + "m_ObjectId": "4917051f90634e10a316653421e9349d", + "m_Group": { + "m_Id": "" + }, + "m_Name": "LOD_Crossfade_Fix_Passthrough (Custom Function)", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 26.250091552734376, + "y": -101.25001525878906, + "width": 212.99998474121095, + "height": 278.0 + } + }, + "m_Slots": [ + { + "m_Id": "99496caa8af84c9fb7d261cb6b32e61a" + }, + { + "m_Id": "81cf5c8bd8b9456f96ab39e94d3d1c64" + } + ], + "synonyms": [ + "code", + "HLSL" + ], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SourceType": 1, + "m_FunctionName": "LOD_Crossfade_Fix_Passthrough", + "m_FunctionSource": "", + "m_FunctionBody": "ColorOut = ColorIn;\n\n#ifdef UNITY_DOTS_INSTANCING_ENABLED\n}\n\n#define LODFadeCrossFade LODFadeCrossFadeFixed\n\nvoid LODFadeCrossFadeFixed(float4 positionCS)\n{\n half2 uv = positionCS.xy * _DitheringTextureInvSize;\n\n half d = SAMPLE_TEXTURE2D(_DitheringTexture, sampler_PointRepeat, uv).a;\n\n half fade = LoadDOTSInstancedData_LODFade();\n d = fade - CopySign(d, fade);\n\n clip(d);\n\n//#define unity_LODFade LoadDOTSInstancedData_LODFade()\n//unity_LODFade = LoadDOTSInstancedData_LODFade();\n#endif" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.PropertyNode", + "m_ObjectId": "6550f4bc2d53489d83f87395a9199fd5", + "m_Group": { + "m_Id": "" + }, + "m_Name": "Property", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": -166.0, + "y": -68.0000228881836, + "width": 119.9999771118164, + "height": 34.00001907348633 + } + }, + "m_Slots": [ + { + "m_Id": "97ce8da8ae1245638cbf9e1efdc5a4c4" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_Property": { + "m_Id": "0721b204640d4b438573b1bfc38dfb7e" + } +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.SubGraphOutputNode", + "m_ObjectId": "676cda1a11a449e39c5114cdad34796d", + "m_Group": { + "m_Id": "" + }, + "m_Name": "Output", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 406.0, + "y": -50.99998474121094, + "width": 121.0, + "height": 76.99998474121094 + } + }, + "m_Slots": [ + { + "m_Id": "90c15112d9ae468082e923974a7dd447" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "IsFirstSlotValid": true +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.CategoryData", + "m_ObjectId": "7b7b6c7f34fa449689edd844fdebd5ec", + "m_Name": "", + "m_ChildObjectList": [ + { + "m_Id": "0721b204640d4b438573b1bfc38dfb7e" + } + ] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", + "m_ObjectId": "81cf5c8bd8b9456f96ab39e94d3d1c64", + "m_Id": 0, + "m_DisplayName": "ColorOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "ColorOut", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", + "m_ObjectId": "90c15112d9ae468082e923974a7dd447", + "m_Id": 1, + "m_DisplayName": "Out_Value", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Out_Value", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", + "m_ObjectId": "97ce8da8ae1245638cbf9e1efdc5a4c4", + "m_Id": 0, + "m_DisplayName": "In_Value", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "Out", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", + "m_ObjectId": "99496caa8af84c9fb7d261cb6b32e61a", + "m_Id": 1, + "m_DisplayName": "ColorIn", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "ColorIn", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_Labels": [] +} + diff --git a/Kinemation/ShaderLibrary/URP14-LOD_Crossfade_Fix_Node.shadersubgraph.meta b/Kinemation/ShaderLibrary/URP14-LOD_Crossfade_Fix_Node.shadersubgraph.meta new file mode 100644 index 0000000..57850a8 --- /dev/null +++ b/Kinemation/ShaderLibrary/URP14-LOD_Crossfade_Fix_Node.shadersubgraph.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 8144d79aa857c8e4c9219eeae1d7dec2 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 60072b568d64c40a485e0fc55012dc9f, type: 3} diff --git a/Kinemation/Systems/Culling/CullLodsSystem.cs b/Kinemation/Systems/Culling/CullLodsSystem.cs new file mode 100644 index 0000000..36f44ca --- /dev/null +++ b/Kinemation/Systems/Culling/CullLodsSystem.cs @@ -0,0 +1,333 @@ +using Latios.Transforms; +using Latios.Transforms.Abstract; +using Unity.Burst; +using Unity.Burst.Intrinsics; +using Unity.Collections; +using Unity.Entities; +using Unity.Entities.Exposed; +using Unity.Jobs; +using Unity.Mathematics; +using Unity.Rendering; + +using static Unity.Entities.SystemAPI; + +// Unity's implementation of LODs in ECS and GPU Resident Drawer prefer to encode +// LODs as distances. The problem with this is that LODs are really defined as +// percentages of screen heights. And such percentages are dependent on the scale +// values of the objects. We want to derive from Unity's formulas a test that permits +// a dynamic worldSpaceSize. +// Unity defines a LODRange distance as follows: +// lodRangeDistance = worldSpaceSize / lodGroupLodScreenHeightPercent +// Unity also defines a distanceScale which is one of two values depending on whether +// or not the camera is orthographic, and is constant per job: +// distanceScale = 2 * orthoSize / lodBias [orthographic] +// distanceScale = 2 * tan(fov / 2) / lodBias [perspective] +// Next, there's distance defined as: +// distance = vectorLength(cameraPosition - lodReferencePoint) +// Lastly, we'll define the complex comparison query operation with the symbol <=> +// The queries Unity performs in the culling job are defined like this: +// lodRangeDistance <=> distanceScale [orthographic] +// lodRangeDistance <=> distance * distanceScale [perspective] +// +// We can perform any algebraic operation across the <=> operator, at the risk of +// changing which type of comparison we end up needing (> vs <). But we can reason +// about the correct operator via intuition when we are done. Expanding lodRangeDistance +// into its formulation, we can perform the following sequence to arrive at a desirable +// comparison for orthographic +// worldSpaceSize / lodGroupLodScreenHeightPercent <=> distanceScale +// worldSpaceSize <=> distanceScale * lodGroupLodScreenHeightPercent +// worldSpaceSize / distanceScale <=> lodGroupLodScreenHeightPercent +// Now we have a compact formula that relates our dynamic value and a constant to +// bake-able values defined in the LOD Group. +// We can further optimize by using the reciprocal of the distanceScale to convert +// our division into a multiplication at runtime. +// +// For perspective, we can perform a similar sequence: +// worldSpaceSize / lodGroupLodScreenHeightPercent <=> distance * distanceScale +// worldSpaceSize <=> distance * distanceScale * lodGroupLodScreenHeightPercent +// worldSpaceSize / distanceScale <=> distance * lodGroupLodScreenHeightPercent +// We could move distance to the left to get a cleaner formulation, however, as +// distance is always positive, the comparison directions won't change based on which +// side it is on, and having the multiplication on the right instead of the division +// on the left is more optimal. We can still intuitively reason about the directionality +// of comparisons. +// +// Because lodGroupLodScreenHeightPercent is now in our formulation instead of the +// full lodRangeDistance, we can do even more optimizations. This value is loosely +// authored using sliders in the editor for a limited range of [0, 1]. Half precision +// should be more than sufficient to represent these values, of which there are multiple. +// Thus, we can save precious chunk memory at the cost of converting these to higher +// precision in the job. +// +// Additionally, the common case for LODs is to have identity transforms relative to +// their LOD Group (except for maybe scale, which we can encode into the exponent +// part of our lodGroupLodScreenHeightPercent). Because of this, we make the assumption +// that the worldTransform of the LOD Entity is also the lodReferencePoint. + +namespace Latios.Kinemation.Systems +{ + [DisableAutoCreation] + [BurstCompile] + public unsafe partial struct CullLodsSystem : ISystem, ISystemShouldUpdate + { + EntityQuery m_query; + EntityQuery m_metaQuery; + + LatiosWorldUnmanaged latiosWorld; + + WorldTransformReadOnlyAspect.TypeHandle m_worldTransformHandle; + + float3 m_previousCameraPosition; + float m_previousCameraHeightMultiplier; + int m_previousMaxResolutionLodLevel; + bool m_previousWasPerspective; + int m_maximumLODLevel; + float m_lodBias; + + public void OnCreate(ref SystemState state) + { + latiosWorld = state.GetLatiosWorldUnmanaged(); + m_query = state.Fluent().WithAnyEnabled(true).WithWorldTransformReadOnly() + .With(false, true).Build(); + m_metaQuery = state.Fluent().With(true).With(false).With(false).Build(); + + m_worldTransformHandle = new WorldTransformReadOnlyAspect.TypeHandle(ref state); + + state.RequireForUpdate(m_query); + } + + public bool ShouldUpdateSystem(ref SystemState state) + { + m_maximumLODLevel = UnityEngine.QualitySettings.maximumLODLevel; + m_lodBias = UnityEngine.QualitySettings.lodBias; + return true; + } + + [BurstCompile] + public void OnUpdate(ref SystemState state) + { + var context = latiosWorld.worldBlackboardEntity.GetComponentData(); + + var cameraPosition = context.lodParameters.cameraPosition; + var isPerspective = !context.lodParameters.isOrthographic; + float cameraMultiplier; + if (isPerspective) + { + cameraMultiplier = m_lodBias / (2f * math.tan(math.radians(context.lodParameters.fieldOfView) / 2f)); + } + else + { + cameraMultiplier = m_lodBias / (2f * context.lodParameters.orthoSize); + } + + var needsCulling = context.cullIndexThisFrame == 0; + needsCulling |= cameraMultiplier != m_previousCameraHeightMultiplier; + needsCulling |= !cameraPosition.Equals(m_previousCameraPosition); + needsCulling |= m_maximumLODLevel != m_previousMaxResolutionLodLevel; + needsCulling |= isPerspective != m_previousWasPerspective; + + if (needsCulling) + { + m_worldTransformHandle.Update(ref state); + state.Dependency = new CullLodsJob + { + worldTransformHandle = m_worldTransformHandle, + lodHeightPercentagesHandle = GetComponentTypeHandle(true), + lodHeightPercentagesWithCrossfadeMarginsHandle = GetComponentTypeHandle(true), + speedTreeTagHandle = GetComponentTypeHandle(true), + chunkInfoHandle = GetComponentTypeHandle(false), + crossfadeHandle = GetComponentTypeHandle(false), + cameraPosition = cameraPosition, + cameraHeightMultiplier = cameraMultiplier, + maxResolutionLodLevel = m_maximumLODLevel, + isPerspective = isPerspective, + }.ScheduleParallel(m_query, state.Dependency); + } + + state.Dependency = new CopyLodsToPerCameraVisisbilitiesJob + { + chunkInfoHandle = GetComponentTypeHandle(true), + perCameraMaskHandle = GetComponentTypeHandle(false) + }.ScheduleParallel(m_metaQuery, state.Dependency); + } + + [BurstCompile] + unsafe struct CullLodsJob : IJobChunk + { + [ReadOnly] public WorldTransformReadOnlyAspect.TypeHandle worldTransformHandle; + [ReadOnly] public ComponentTypeHandle lodHeightPercentagesHandle; + [ReadOnly] public ComponentTypeHandle lodHeightPercentagesWithCrossfadeMarginsHandle; + [ReadOnly] public ComponentTypeHandle speedTreeTagHandle; + + public ComponentTypeHandle chunkInfoHandle; + public ComponentTypeHandle crossfadeHandle; + + public float3 cameraPosition; + public float cameraHeightMultiplier; + public int maxResolutionLodLevel; + public bool isPerspective; + + public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + ref var chunkInfo = ref chunk.GetChunkComponentRefRW(ref chunkInfoHandle); + chunkInfo.CullingData.InstanceLodEnableds = default; + + var transforms = worldTransformHandle.Resolve(chunk); + var percentsWithMargins = chunk.GetNativeArray(ref lodHeightPercentagesWithCrossfadeMarginsHandle); + NativeArray percents = default; + NativeArray crossfades = default; + bool hasCrossfades = percentsWithMargins.Length > 0; + if (hasCrossfades) + { + crossfades = chunk.GetNativeArray(ref crossfadeHandle); + var crossfadeEnableds = chunk.GetEnabledMask(ref crossfadeHandle); + bool isSpeedTree = chunk.Has(ref speedTreeTagHandle); + + for (int i = 0; i < chunk.Count; i++) + { + crossfadeEnableds[i] = false; + var lodValues = percentsWithMargins[i]; + var transform = transforms[i].worldTransformQvvs; + if (!TestInRange(lodValues.localSpaceHeight, lodValues.minPercent, lodValues.maxPercent, in transform, out var computedParams)) + continue; + + float maxMargin = lodValues.maxCrossFadeEdge; + if (isPerspective) + maxMargin *= computedParams.distance; + if (!computedParams.isPromotedLod && computedParams.worldHeight > maxMargin) + { + // We are crossfading with a higher resolution LOD + if (isSpeedTree) + { + // For speedTree, the lower-res LOD is unused as the higher-res LOD is deformed towards the lower-res LOD surface + continue; + } + + LodCrossfade newCrossfade = default; + float opacity = math.unlerp(maxMargin, computedParams.maxPercent, computedParams.worldHeight); + newCrossfade.SetHiResOpacity(opacity, true); + crossfades[i] = newCrossfade; + crossfadeEnableds[i] = true; + } + else + { + float minMargin = lodValues.minCrossFadeEdge; + if (isPerspective) + minMargin *= computedParams.distance; + if (computedParams.worldHeight < minMargin) + { + // We are crossfading with a lower resolution LOD + // The fromula is the same for both SpeedTree and dithered, meaning SpeedTree only uses half the snorm space + LodCrossfade newCrossfade = default; + float opacity = math.unlerp(computedParams.minPercent, minMargin, computedParams.worldHeight); + newCrossfade.SetHiResOpacity(opacity, false); + crossfades[i] = newCrossfade; + crossfadeEnableds[i] = true; + } + } + + // We have a hit. + if (i < 64) + chunkInfo.CullingData.InstanceLodEnableds.Enabled[0] |= 1ul << i; + else + chunkInfo.CullingData.InstanceLodEnableds.Enabled[0] |= 1ul << (i - 64); + } + } + else + { + percents = chunk.GetNativeArray(ref lodHeightPercentagesHandle); + + for (int i = 0; i < chunk.Count; i++) + { + var lodValues = percents[i]; + var transform = transforms[i].worldTransformQvvs; + + if (!TestInRange(lodValues.localSpaceHeight, lodValues.minPercent, lodValues.maxPercent, in transform, out var computedParams)) + continue; + + // We have a hit. + if (i < 64) + chunkInfo.CullingData.InstanceLodEnableds.Enabled[0] |= 1ul << i; + else + chunkInfo.CullingData.InstanceLodEnableds.Enabled[0] |= 1ul << (i - 64); + } + } + } + + struct ComputedRangeParams + { + public float worldHeight; + public float distance; + public float minPercent; + public float maxPercent; + public bool isPromotedLod; + } + + bool TestInRange(float localHeight, half minPercent, half maxPercent, in TransformQvvs transform, out ComputedRangeParams computedParams) + { + computedParams = default; + + if (maxResolutionLodLevel > 0) + { + bool bit0 = localHeight < 0f; + bool bit1 = minPercent < 0f; + bool bit2 = maxPercent < 0f; + var lowLod = math.bitmask(new bool4(bit0, bit1, bit2, false)); + if (lowLod < maxResolutionLodLevel) + return false; + computedParams.isPromotedLod = lowLod == maxResolutionLodLevel; + } + else + computedParams.isPromotedLod = false; + + computedParams.worldHeight = math.abs(localHeight) * math.abs(transform.scale) * math.cmax(math.abs(transform.stretch)); + computedParams.worldHeight *= cameraHeightMultiplier; + computedParams.minPercent = math.abs(minPercent); + if (isPerspective) + { + computedParams.distance = math.distance(transform.position, cameraPosition); + computedParams.minPercent *= computedParams.distance; + } + if (computedParams.worldHeight < computedParams.minPercent) + return false; + computedParams.maxPercent = math.abs(maxPercent); + if (isPerspective) + computedParams.maxPercent *= computedParams.distance; + if (!computedParams.isPromotedLod && computedParams.worldHeight >= computedParams.maxPercent) + return false; + + return true; + } + } + + [BurstCompile] + struct CopyLodsToPerCameraVisisbilitiesJob : IJobChunk + { + [ReadOnly] public ComponentTypeHandle chunkInfoHandle; + public ComponentTypeHandle perCameraMaskHandle; + + public unsafe void Execute(in ArchetypeChunk metaChunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + var chunkInfoArray = metaChunk.GetNativeArray(ref chunkInfoHandle); + var maskArray = metaChunk.GetNativeArray(ref perCameraMaskHandle); + + for (int i = 0; i < metaChunk.Count; i++) + { + var mask = maskArray[i]; + if ((mask.lower.Value | mask.upper.Value) == 0) + continue; + + var cullingData = chunkInfoArray[i].CullingData; + if ((cullingData.Flags & EntitiesGraphicsChunkCullingData.kFlagHasLodData) != EntitiesGraphicsChunkCullingData.kFlagHasLodData) + continue; + + var lods = cullingData.InstanceLodEnableds; + mask.lower.Value &= lods.Enabled[0]; + mask.upper.Value &= lods.Enabled[1]; + maskArray[i] = mask; + } + } + } + } +} + diff --git a/Kinemation/Systems/Culling/UpdateLODsSystem.cs.meta b/Kinemation/Systems/Culling/CullLodsSystem.cs.meta similarity index 100% rename from Kinemation/Systems/Culling/UpdateLODsSystem.cs.meta rename to Kinemation/Systems/Culling/CullLodsSystem.cs.meta diff --git a/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs b/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs index 2e20f6a..20b01bb 100644 --- a/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs +++ b/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs @@ -98,13 +98,14 @@ public unsafe void OnUpdate(ref SystemState state) #elif !LATIOS_TRANSFORMS_UNCACHED_QVVS && LATIOS_TRANSFORMS_UNITY WorldTransform = GetComponentTypeHandle(true), #endif - PostProcessMatrix = GetComponentTypeHandle(true), - MaterialMeshInfo = GetComponentTypeHandle(true), - ProfilerEmitChunk = m_profilerEmitChunk, - RenderFilterSettings = GetSharedComponentTypeHandle(), - RenderMeshArray = ManagedAPI.GetSharedComponentTypeHandle(), - SceneCullingMask = cullingContext.sceneCullingMask, - splitsAreValid = cullingContext.viewType == BatchCullingViewType.Light, + PostProcessMatrix = GetComponentTypeHandle(true), + MaterialMeshInfo = GetComponentTypeHandle(true), + ProfilerEmitChunk = m_profilerEmitChunk, + RenderFilterSettings = GetSharedComponentTypeHandle(), + RenderMeshArray = ManagedAPI.GetSharedComponentTypeHandle(), + SceneCullingMask = cullingContext.sceneCullingMask, + speedTreeCrossfadeTagHandle = GetComponentTypeHandle(true), + splitsAreValid = cullingContext.viewType == BatchCullingViewType.Light, }; var allocateWorkItemsJob = new AllocateWorkItemsJob @@ -246,6 +247,7 @@ unsafe struct EmitDrawCommandsJob : IJobParallelForDefer [ReadOnly] public ComponentTypeHandle chunkPerCameraCullingMaskHandle; [ReadOnly] public ComponentTypeHandle chunkPerCameraCullingSplitsMaskHandle; [ReadOnly] public ComponentTypeHandle lodCrossfadeHandle; + [ReadOnly] public ComponentTypeHandle speedTreeCrossfadeTagHandle; [ReadOnly] public EntityQueryMask motionVectorDeformQueryMask; public bool splitsAreValid; @@ -336,8 +338,10 @@ void Execute(in ArchetypeChunk chunk) int chunkStartIndex = entitiesGraphicsChunkInfo.CullingData.ChunkOffsetInBatch; - var mask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingMaskHandle); - var splitsMask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingSplitsMaskHandle); + var mask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingMaskHandle); + var splitsMask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingSplitsMaskHandle); + var crossFadeEnableds = hasLodCrossfade ? chunk.GetEnabledMask(ref lodCrossfadeHandle) : default; + var isSpeedTree = hasLodCrossfade && chunk.Has(ref speedTreeCrossfadeTagHandle); TransformQvvs* depthSortingTransformsPtr = null; if (isDepthSorted && hasPostProcess) @@ -384,8 +388,14 @@ void Execute(in ArchetypeChunk chunk) if (isLightMapped) drawCommandFlags |= BatchDrawCommandFlags.IsLightMapped; - if (hasLodCrossfade) + if (hasLodCrossfade && crossFadeEnableds[entityIndex]) + { + // Todo: Remove the LODCrossFade line and replace with the commented out lines in Unity 6. drawCommandFlags |= BatchDrawCommandFlags.LODCrossFade; + //drawCommandFlags |= BatchDrawCommandFlags.LODCrossFadeValuePacked; + //if (!isSpeedTree) + // drawCommandFlags |= BatchDrawCommandFlags.LODCrossFadeKeyword; + } // Depth sorted draws are emitted with access to entity transforms, // so they can also be written out for sorting diff --git a/Kinemation/Systems/Culling/InitializeAndFilterPerCameraSystem.cs b/Kinemation/Systems/Culling/InitializeAndFilterPerCameraSystem.cs index a8efbb2..bea5057 100644 --- a/Kinemation/Systems/Culling/InitializeAndFilterPerCameraSystem.cs +++ b/Kinemation/Systems/Culling/InitializeAndFilterPerCameraSystem.cs @@ -100,13 +100,11 @@ struct Job : IJobChunk public unsafe void Execute(in ArchetypeChunk metaChunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { - var ptr = metaChunk.GetComponentDataPtrRW(ref maskHandle); - UnsafeUtility.MemClear(ptr, sizeof(ChunkPerCameraCullingMask) * metaChunk.Count); + var chunkPerCameraMasks = metaChunk.GetComponentDataPtrRW(ref maskHandle); + UnsafeUtility.MemClear(chunkPerCameraMasks, sizeof(ChunkPerCameraCullingMask) * metaChunk.Count); - var chunkPerCameraMasks = (ChunkPerCameraCullingMask*)ptr; - - var chunkHeaders = (ChunkHeader*)metaChunk.GetComponentDataPtrRO(ref headerHandle); - var chunkInfos = (EntitiesGraphicsChunkInfo*)metaChunk.GetComponentDataPtrRO(ref chunkInfoHandle); + var chunkHeaders = metaChunk.GetComponentDataPtrRO(ref headerHandle); + var chunkInfos = metaChunk.GetComponentDataPtrRO(ref chunkInfoHandle); for (int i = 0; i < metaChunk.Count; i++) { chunkPerCameraMasks[i] = default; @@ -156,8 +154,8 @@ struct EditorJob : IJobChunk public unsafe void Execute(in ArchetypeChunk metaChunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { - var chunkPerCameraMasks = (ChunkPerCameraCullingMask*)metaChunk.GetComponentDataPtrRW(ref maskHandle); - var chunkHeaders = (ChunkHeader*)metaChunk.GetComponentDataPtrRO(ref headerHandle); + var chunkPerCameraMasks = metaChunk.GetComponentDataPtrRW(ref maskHandle); + var chunkHeaders = metaChunk.GetComponentDataPtrRO(ref headerHandle); for (int i = 0; i < metaChunk.Count; i++) { if (chunkPerCameraMasks[i].lower.Value == 0) diff --git a/Kinemation/Systems/Culling/UpdateLODsSystem.cs b/Kinemation/Systems/Culling/UpdateLODsSystem.cs deleted file mode 100644 index d441133..0000000 --- a/Kinemation/Systems/Culling/UpdateLODsSystem.cs +++ /dev/null @@ -1,297 +0,0 @@ -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Entities; -using Unity.Jobs; -using Unity.Mathematics; -using Unity.Rendering; - -// Note: The following comment pertains to Entities 0.51. The behavior has not been compared to 1.0 yet. -// Unity's LOD system is currently a bit of a mess. The behavior of this version has been modified for more -// predictable behavior at the cost of performance. Some of the things that make no sense in Unity's original -// implementation which have been modified in this version include: -// 1) ForceLowLOD is created with cleared memory and then assigned to the SelectLodEnabled job as [ReadOnly]. -// It is not touched anywhere else in HR V2. -// 2) The algorithm tries to cache LOD levels by camera. This doesn't make much sense since there is typically -// more than one camera rendering (shadows). However, since the checks are cheap and there's a chance that -// two cameras have similar LOD requirements, I'm leaving that logic in. -// 3) There's a ResetLod function which is public API, but is only called at the beginning of each render by -// EntitiesGraphicsV2RenderSystem. I suspect this is to cover structural changes. But since we have a real system, we -// can do that a smarter way using order versions. -// 4) CullingStats gets cleared immediately after scheduling a job using it every time there's a LOD update. -// I think this is actually supposed to be cleared every frame. - -namespace Latios.Kinemation.Systems -{ - [RequireMatchingQueriesForUpdate] - [DisableAutoCreation] - [BurstCompile] - public unsafe partial struct UpdateLODsSystem : ISystem, ISystemShouldUpdate - { - LODGroupExtensions.LODParams m_PrevLODParams; - float3 m_PrevCameraPos; - float m_PrevLodDistanceScale; - - EntityQuery m_query; - - int m_lastLodRangeOrderVersion; - int m_lastChunkInfoOrderVersion; - - SelectLodEnabledJob m_job; - CopyLodsToPerCameraVisisbilitiesJob m_copyJob; - int m_maximumLODLevel; - - LatiosWorldUnmanaged latiosWorld; - - public void OnCreate(ref SystemState state) - { - latiosWorld = state.GetLatiosWorldUnmanaged(); - m_query = state.Fluent().With(true).With(false).With(false).Build(); - - m_job = new SelectLodEnabledJob - { - RootLODRanges = state.GetComponentTypeHandle(true), - RootLODReferencePoints = state.GetComponentTypeHandle(true), - LODRanges = state.GetComponentTypeHandle(true), - LODReferencePoints = state.GetComponentTypeHandle(true), - EntitiesGraphicsChunkInfo = state.GetComponentTypeHandle(), - ChunkHeader = state.GetComponentTypeHandle(), - }; - - m_copyJob = new CopyLodsToPerCameraVisisbilitiesJob - { - chunkInfoHandle = state.GetComponentTypeHandle(true), - perCameraMaskHandle = state.GetComponentTypeHandle() - }; - } - - public bool ShouldUpdateSystem(ref SystemState state) - { - m_maximumLODLevel = UnityEngine.QualitySettings.maximumLODLevel; - return true; - } - - [BurstCompile] - public void OnUpdate(ref SystemState state) - { - var context = latiosWorld.worldBlackboardEntity.GetComponentData(); - var firstRunThisFrame = context.cullIndexThisFrame == 0; - var lodParams = LODGroupExtensions.CalculateLODParams(context.lodParameters); - - bool lodParamsMatchPrev = lodParams.Equals(m_PrevLODParams); - var resetLod = !lodParamsMatchPrev; - resetLod |= firstRunThisFrame; - resetLod |= (state.EntityManager.GetComponentOrderVersion() - m_lastLodRangeOrderVersion) > 0; - resetLod |= (state.EntityManager.GetComponentOrderVersion() - m_lastChunkInfoOrderVersion) > 0; - - if (resetLod) - { - float cameraMoveDistance = math.length(m_PrevCameraPos - lodParams.cameraPos); - var lodDistanceScaleChanged = lodParams.distanceScale != m_PrevLodDistanceScale; - - m_job.RootLODRanges.Update(ref state); - m_job.RootLODReferencePoints.Update(ref state); - m_job.LODRanges.Update(ref state); - m_job.LODReferencePoints.Update(ref state); - m_job.EntitiesGraphicsChunkInfo.Update(ref state); - m_job.ChunkHeader.Update(ref state); - - m_job.lodParamsMatchPrev = lodParamsMatchPrev; - m_job.lastSystemVersion = state.LastSystemVersion; - - m_job.LODParams = lodParams; - m_job.CameraMoveDistanceFixed16 = Fixed16CamDistance.FromFloatCeil(cameraMoveDistance * lodParams.distanceScale); - m_job.DistanceScale = lodParams.distanceScale; - m_job.DistanceScaleChanged = lodDistanceScaleChanged; - m_job.MaximumLODLevelMask = 1 << m_maximumLODLevel; - - state.Dependency = m_job.ScheduleParallelByRef(m_query, state.Dependency); - - m_PrevLODParams = lodParams; - m_PrevLodDistanceScale = lodParams.distanceScale; - m_PrevCameraPos = lodParams.cameraPos; - } - m_lastLodRangeOrderVersion = state.EntityManager.GetComponentOrderVersion(); - m_lastChunkInfoOrderVersion = state.EntityManager.GetComponentOrderVersion(); - - m_copyJob.perCameraMaskHandle.Update(ref state); - m_copyJob.chunkInfoHandle.Update(ref state); - state.Dependency = m_copyJob.ScheduleParallelByRef(m_query, state.Dependency); - } - - [BurstCompile] - public void OnDestroy(ref SystemState state) - { - } - - [BurstCompile] - unsafe struct SelectLodEnabledJob : IJobChunk - { - public bool lodParamsMatchPrev; - public uint lastSystemVersion; - - [ReadOnly] public LODGroupExtensions.LODParams LODParams; - [ReadOnly] public ComponentTypeHandle RootLODRanges; - [ReadOnly] public ComponentTypeHandle RootLODReferencePoints; - [ReadOnly] public ComponentTypeHandle LODRanges; - [ReadOnly] public ComponentTypeHandle LODReferencePoints; - public ushort CameraMoveDistanceFixed16; - public float DistanceScale; - public bool DistanceScaleChanged; - public int MaximumLODLevelMask; - - public ComponentTypeHandle EntitiesGraphicsChunkInfo; - [ReadOnly] public ComponentTypeHandle ChunkHeader; - - public void Execute(in ArchetypeChunk metaChunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) - { - var entitiesGraphicsChunkInfoArray = metaChunk.GetNativeArray(ref EntitiesGraphicsChunkInfo); - var chunkHeaderArray = metaChunk.GetNativeArray(ref ChunkHeader); - - for (var entityIndex = 0; entityIndex < metaChunk.Count; entityIndex++) - { - var entitiesGraphicsChunkInfo = entitiesGraphicsChunkInfoArray[entityIndex]; - if (!entitiesGraphicsChunkInfo.Valid) - continue; - - var chunkHeader = chunkHeaderArray[entityIndex]; - var chunk = chunkHeader.ArchetypeChunk; - - var chunkOrderChanged = chunk.DidOrderChange(lastSystemVersion); - - var batchIndex = entitiesGraphicsChunkInfo.BatchIndex; - var chunkInstanceCount = chunk.Count; - var isOrtho = LODParams.isOrtho; - - ref var chunkCullingData = ref entitiesGraphicsChunkInfo.CullingData; - ChunkInstanceLodEnabled chunkEntityLodEnabled = chunkCullingData.InstanceLodEnableds; - - if (0 == (chunkCullingData.Flags & EntitiesGraphicsChunkCullingData.kFlagHasLodData)) - { - chunkEntityLodEnabled.Enabled[0] = 0; - chunkEntityLodEnabled.Enabled[1] = 0; - for (int i = 0; i < chunkInstanceCount; ++i) - { - int wordIndex = i >> 6; - int bitIndex = i & 63; - chunkEntityLodEnabled.Enabled[wordIndex] |= 1ul << bitIndex; - } - } - else - { - int diff = (int)chunkCullingData.MovementGraceFixed16 - CameraMoveDistanceFixed16; - chunkCullingData.MovementGraceFixed16 = (ushort)math.max(0, diff); - - var graceExpired = chunkCullingData.MovementGraceFixed16 == 0; - if (graceExpired || DistanceScaleChanged || chunkOrderChanged) - { - chunkEntityLodEnabled.Enabled[0] = 0; - chunkEntityLodEnabled.Enabled[1] = 0; - - var rootLODRanges = chunk.GetNativeArray(ref RootLODRanges); - var rootLODReferencePoints = chunk.GetNativeArray(ref RootLODReferencePoints); - var lodRanges = chunk.GetNativeArray(ref LODRanges); - var lodReferencePoints = chunk.GetNativeArray(ref LODReferencePoints); - - float graceDistance = float.MaxValue; - - for (int i = 0; i < chunkInstanceCount; i++) - { - var rootLODRange = rootLODRanges[i]; - var rootLODReferencePoint = rootLODReferencePoints[i]; - - var rootLodDistance = - math.select( - DistanceScale * - math.length(LODParams.cameraPos - rootLODReferencePoint.Value), - DistanceScale, isOrtho); - - float rootMinDist = rootLODRange.LOD.MinDist; - float rootMaxDist = rootLODRange.LOD.MaxDist; - - graceDistance = math.min(math.abs(rootLodDistance - rootMinDist), graceDistance); - graceDistance = math.min(math.abs(rootLodDistance - rootMaxDist), graceDistance); - - var rootLodIntersect = (rootLodDistance < rootMaxDist) && (rootLodDistance >= rootMinDist); - - if (rootLodIntersect) - { - var lodRange = lodRanges[i]; - if (lodRange.LODMask < MaximumLODLevelMask) - { - continue; - } - if (lodRange.LODMask == MaximumLODLevelMask) - { - // Expand maximum LOD range to cover all higher LODs - lodRange.MinDist = 0.0f; - } - var lodReferencePoint = lodReferencePoints[i]; - - var instanceDistance = - math.select( - DistanceScale * - math.length(LODParams.cameraPos - - lodReferencePoint.Value), DistanceScale, - isOrtho); - - var instanceLodIntersect = - (instanceDistance < lodRange.MaxDist) && - (instanceDistance >= lodRange.MinDist); - - graceDistance = math.min(math.abs(instanceDistance - lodRange.MinDist), - graceDistance); - graceDistance = math.min(math.abs(instanceDistance - lodRange.MaxDist), - graceDistance); - - if (instanceLodIntersect) - { - var index = i; - var wordIndex = index >> 6; - var bitIndex = index & 0x3f; - var lodWord = chunkEntityLodEnabled.Enabled[wordIndex]; - - lodWord |= 1UL << bitIndex; - chunkEntityLodEnabled.Enabled[wordIndex] = lodWord; - } - } - } - - chunkCullingData.MovementGraceFixed16 = Fixed16CamDistance.FromFloatFloor(graceDistance); - } - } - - chunkCullingData.InstanceLodEnableds = chunkEntityLodEnabled; - entitiesGraphicsChunkInfoArray[entityIndex] = entitiesGraphicsChunkInfo; - } - } - } - - [BurstCompile] - struct CopyLodsToPerCameraVisisbilitiesJob : IJobChunk - { - [ReadOnly] public ComponentTypeHandle chunkInfoHandle; - public ComponentTypeHandle perCameraMaskHandle; - - public unsafe void Execute(in ArchetypeChunk metaChunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) - { - var chunkInfoArray = metaChunk.GetNativeArray(ref chunkInfoHandle); - var maskArray = metaChunk.GetNativeArray(ref perCameraMaskHandle); - - for (int i = 0; i < metaChunk.Count; i++) - { - if (maskArray[i].lower.Value == 0) - continue; - - var lods = chunkInfoArray[i].CullingData.InstanceLodEnableds; - - var mask = maskArray[i]; - mask.lower.Value &= lods.Enabled[0]; - mask.upper.Value &= lods.Enabled[1]; - maskArray[i] = mask; - } - } - } - } -} - diff --git a/Kinemation/Systems/KinemationSuperSystems.cs b/Kinemation/Systems/KinemationSuperSystems.cs index fb16619..8b97886 100644 --- a/Kinemation/Systems/KinemationSuperSystems.cs +++ b/Kinemation/Systems/KinemationSuperSystems.cs @@ -22,7 +22,7 @@ protected override void CreateSystems() GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddUnmanagedSystem(); - GetOrCreateAndAddUnmanagedSystem(); + GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddUnmanagedSystem(); @@ -113,7 +113,6 @@ protected override void CreateSystems() GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddUnmanagedSystem(); - GetOrCreateAndAddUnmanagedSystem(); GetOrCreateAndAddManagedSystem(); } } diff --git a/Kinemation/Systems/LatiosEntitiesGraphicsSystem.cs b/Kinemation/Systems/LatiosEntitiesGraphicsSystem.cs index 7196f0a..4619ae7 100644 --- a/Kinemation/Systems/LatiosEntitiesGraphicsSystem.cs +++ b/Kinemation/Systems/LatiosEntitiesGraphicsSystem.cs @@ -902,8 +902,6 @@ private JobHandle UpdateAllBatches(JobHandle inputDependencies, out int totalChu var entitiesGraphicsRenderedChunkType = GetComponentTypeHandle(false); var entitiesGraphicsRenderedChunkTypeRO = GetComponentTypeHandle(true); var chunkHeadersRO = GetComponentTypeHandle(true); - var lodRangesRO = GetComponentTypeHandle(true); - var rootLodRangesRO = GetComponentTypeHandle(true); var materialMeshInfosRO = GetComponentTypeHandle(true); var renderMeshArrays = GetSharedComponentTypeHandle(); @@ -991,8 +989,6 @@ private JobHandle UpdateAllBatches(JobHandle inputDependencies, out int totalChu EntitiesGraphicsChunkInfo = entitiesGraphicsRenderedChunkType, ChunkHeader = chunkHeadersRO, WorldTransform = GetDynamicComponentTypeHandle(QueryExtensions.GetAbstractWorldTransformROComponentType()), - LodRange = lodRangesRO, - RootLodRange = rootLodRangesRO, MaterialMeshInfo = materialMeshInfosRO, EntitiesGraphicsChunkUpdater = entitiesGraphicsChunkUpdater, }; @@ -1184,7 +1180,12 @@ private int AddNewChunks(NativeArray newChunks) Assert.IsTrue(newChunks.Length > 0, "Attempted to add new chunks, but list of new chunks was empty"); - var batchCreationTypeHandles = new BatchCreationTypeHandles(this); + var batchCreationTypeHandles = new BatchCreationTypeHandles + { + perInstanceCullingHandle = SystemAPI.GetComponentTypeHandle(true), + lodHeightPercentagesHandle = SystemAPI.GetComponentTypeHandle(true), + lodHeightPercentagesWithCrossfadeMarginsHandle = SystemAPI.GetComponentTypeHandle(true), + }; // Sort new chunks by RenderMesh so we can put // all compatible chunks inside one batch. @@ -1405,6 +1406,13 @@ struct SetBatchChunkDataArgs public NativeArray OverrideStreamBegin; } + internal struct BatchCreationTypeHandles + { + public ComponentTypeHandle lodHeightPercentagesHandle; + public ComponentTypeHandle lodHeightPercentagesWithCrossfadeMarginsHandle; + public ComponentTypeHandle perInstanceCullingHandle; + } + [BurstCompile] static void SetBatchChunkData(ref SetBatchChunkDataArgs args, ref UnsafeList overrides) { @@ -1456,13 +1464,12 @@ static void SetBatchChunkData(ref SetBatchChunkDataArgs args, ref UnsafeList EntitiesGraphicsChunkInfo; [ReadOnly] public ComponentTypeHandle ChunkHeader; [ReadOnly] public DynamicComponentTypeHandle WorldTransform; - [ReadOnly] public ComponentTypeHandle LodRange; - [ReadOnly] public ComponentTypeHandle RootLodRange; [ReadOnly] public ComponentTypeHandle MaterialMeshInfo; public EntitiesGraphicsChunkUpdater EntitiesGraphicsChunkUpdater; @@ -1845,16 +1850,16 @@ public void Execute(in ArchetypeChunk metaChunk, int unfilteredChunkIndex, bool continue; // When LOD ranges change, we must reset the movement grace to avoid using stale data - bool lodRangeChange = - chunkHeader.ArchetypeChunk.DidOrderChange(EntitiesGraphicsChunkUpdater.lastSystemVersion) | - chunkHeader.ArchetypeChunk.DidChange(ref LodRange, EntitiesGraphicsChunkUpdater.lastSystemVersion) | - chunkHeader.ArchetypeChunk.DidChange(ref RootLodRange, EntitiesGraphicsChunkUpdater.lastSystemVersion); - - if (lodRangeChange) - { - chunkInfo.CullingData.MovementGraceFixed16 = 0; - entitiesGraphicsChunkInfos[i] = chunkInfo; - } + //bool lodRangeChange = + // chunkHeader.ArchetypeChunk.DidOrderChange(EntitiesGraphicsChunkUpdater.lastSystemVersion) | + // chunkHeader.ArchetypeChunk.DidChange(ref LodRange, EntitiesGraphicsChunkUpdater.lastSystemVersion) | + // chunkHeader.ArchetypeChunk.DidChange(ref RootLodRange, EntitiesGraphicsChunkUpdater.lastSystemVersion); + // + //if (lodRangeChange) + //{ + // chunkInfo.CullingData.MovementGraceFixed16 = 0; + // entitiesGraphicsChunkInfos[i] = chunkInfo; + //} EntitiesGraphicsChunkUpdater.ProcessChunk(in chunkInfo, in chunk); } diff --git a/Kinemation/Systems/PostBatching/PrepareLODsSystem.cs b/Kinemation/Systems/PostBatching/PrepareLODsSystem.cs index dace9dd..2601562 100644 --- a/Kinemation/Systems/PostBatching/PrepareLODsSystem.cs +++ b/Kinemation/Systems/PostBatching/PrepareLODsSystem.cs @@ -36,9 +36,7 @@ public void OnDestroy(ref SystemState state) [BurstCompile] public void OnUpdate(ref SystemState state) { - // Todo? Batches not accounted for in hashmap? - var crossfadePtrMap = new NativeHashMap( + var crossfadePtrMap = new NativeHashMap( 1, state.WorldUpdateAllocator); latiosWorld.worldBlackboardEntity.SetCollectionComponentAndDisposeOld(new LODCrossfadePtrMap { chunkIdentifierToPtrMap = crossfadePtrMap }); diff --git a/Kinemation/Systems/UnityReplacements/LatiosLODRequirementsUpdateSystem.cs b/Kinemation/Systems/UnityReplacements/LatiosLODRequirementsUpdateSystem.cs deleted file mode 100644 index 4ba4f83..0000000 --- a/Kinemation/Systems/UnityReplacements/LatiosLODRequirementsUpdateSystem.cs +++ /dev/null @@ -1,233 +0,0 @@ -using System.Diagnostics; -using Latios.Transforms; -using Latios.Transforms.Abstract; -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Entities; -using Unity.Jobs; -using Unity.Mathematics; -using Unity.Rendering; - -using static Unity.Entities.SystemAPI; - -namespace Latios.Kinemation -{ - [RequireMatchingQueriesForUpdate] - [UpdateBefore(typeof(FreezeStaticLODObjects))] // FreezeStaticLODObjects system has an UpdateAfter dependency on this - [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.EntitySceneOptimizations | WorldSystemFilterFlags.Editor)] - [DisableAutoCreation] - [BurstCompile] - public partial struct LatiosLODRequirementsUpdateSystem : ISystem - { - EntityQuery m_UpdatedLODRanges; - EntityQuery m_LODReferencePoints; - EntityQuery m_LODGroupReferencePoints; - - WorldTransformReadOnlyAspect.TypeHandle m_worldTransformHandle; - - [BurstCompile] - public void OnCreate(ref SystemState state) - { - // Change filter: LODGroupConversion add MeshLODComponent for all LOD children. When the MeshLODComponent is added/changed, we recalculate LOD ranges. - m_UpdatedLODRanges = state.Fluent().WithWorldTransformReadOnly().With().With().With().Build(); - m_UpdatedLODRanges.SetChangedVersionFilter(ComponentType.ReadWrite()); - - m_LODReferencePoints = - state.Fluent().WithWorldTransformReadOnly().With(true).With().With().Build(); - - // Change filter: LOD Group world reference points only change when MeshLODGroupComponent or LocalToWorld change - m_LODGroupReferencePoints = state.Fluent().With(true).WithWorldTransformReadOnly().With().Build(); - m_LODGroupReferencePoints.AddChangedVersionFilter(ComponentType.ReadWrite()); - m_LODGroupReferencePoints.AddWorldTranformChangeFilter(); - - m_worldTransformHandle = new WorldTransformReadOnlyAspect.TypeHandle(ref state); - } - - [BurstCompile] - public void OnDestroy(ref SystemState state) - { - } - - [BurstCompile] - public void OnUpdate(ref SystemState state) - { - m_worldTransformHandle.Update(ref state); - - var updateLODRangesJob = new UpdateLODRangesJob - { - MeshLODGroupComponent = GetComponentLookup(true), - MeshLODComponent = GetComponentTypeHandle(), - RootLODRange = GetComponentTypeHandle(), - LODRange = GetComponentTypeHandle(), - }; - - var updateGroupReferencePointJob = new UpdateLODGroupWorldReferencePointsJob - { - MeshLODGroupComponent = GetComponentTypeHandle(true), - WorldTransform = m_worldTransformHandle, - LODGroupWorldReferencePoint = GetComponentTypeHandle(), - }; - - var updateReferencePointJob = new UpdateLODWorldReferencePointsJob - { - //MeshLODGroupComponent = GetComponentLookup(true), - MeshLODComponent = GetComponentTypeHandle(true), - LODGroupWorldReferencePoint = GetComponentLookup(true), - RootLODWorldReferencePoint = GetComponentTypeHandle(), - LODWorldReferencePoint = GetComponentTypeHandle(), - }; - - var depLODRanges = updateLODRangesJob.ScheduleParallelByRef(m_UpdatedLODRanges, state.Dependency); - var depGroupReferencePoints = updateGroupReferencePointJob.ScheduleParallelByRef(m_LODGroupReferencePoints, state.Dependency); - var depCombined = JobHandle.CombineDependencies(depLODRanges, depGroupReferencePoints); - var depReferencePoints = updateReferencePointJob.ScheduleParallelByRef(m_LODReferencePoints, depCombined); - - state.Dependency = JobHandle.CombineDependencies(depReferencePoints, depReferencePoints); - } - - [BurstCompile] - struct UpdateLODRangesJob : IJobChunk - { - [ReadOnly] public ComponentLookup MeshLODGroupComponent; - - public ComponentTypeHandle MeshLODComponent; - public ComponentTypeHandle RootLODRange; - public ComponentTypeHandle LODRange; - - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] - private static void CheckDeepHLODSupport(Entity entity) - { - if (entity != Entity.Null) - throw new System.NotImplementedException("Deep HLOD is not supported yet"); - } - - public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) - { - // This job is not written to support queries with enableable component types. - Unity.Assertions.Assert.IsFalse(useEnabledMask); - - var rootLODRange = chunk.GetNativeArray(ref RootLODRange); - var lodRange = chunk.GetNativeArray(ref LODRange); - var meshLods = chunk.GetNativeArray(ref MeshLODComponent); - var instanceCount = chunk.Count; - - for (int i = 0; i < instanceCount; i++) - { - var meshLod = meshLods[i]; - var lodGroupEntity = meshLod.Group; - var lodMask = meshLod.LODMask; - var lodGroup = MeshLODGroupComponent[lodGroupEntity]; - - lodRange[i] = new LODRange(lodGroup, lodMask); - } - - for (int i = 0; i < instanceCount; i++) - { - var meshLod = meshLods[i]; - var lodGroupEntity = meshLod.Group; - var lodGroup = MeshLODGroupComponent[lodGroupEntity]; - var parentMask = lodGroup.ParentMask; - var parentGroupEntity = lodGroup.ParentGroup; - - // Store LOD parent group in MeshLODComponent to avoid double indirection for every entity - meshLod.ParentGroup = parentGroupEntity; - meshLods[i] = meshLod; - - RootLODRange rootLod; - - if (parentGroupEntity == Entity.Null) - { - rootLod.LOD.MinDist = 0; - rootLod.LOD.MaxDist = 1048576.0f; - rootLod.LOD.LODMask = 0; - } - else - { - var parentLodGroup = MeshLODGroupComponent[parentGroupEntity]; - rootLod.LOD = new LODRange(parentLodGroup, parentMask); - CheckDeepHLODSupport(parentLodGroup.ParentGroup); - } - - rootLODRange[i] = rootLod; - } - } - } - - [BurstCompile] - struct UpdateLODGroupWorldReferencePointsJob : IJobChunk - { - [ReadOnly] public ComponentTypeHandle MeshLODGroupComponent; - [ReadOnly] public WorldTransformReadOnlyAspect.TypeHandle WorldTransform; - public ComponentTypeHandle LODGroupWorldReferencePoint; - - public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) - { - // This job is not written to support queries with enableable component types. - Unity.Assertions.Assert.IsFalse(useEnabledMask); - - var meshLODGroupComponent = chunk.GetNativeArray(ref MeshLODGroupComponent); - var worldTransform = WorldTransform.Resolve(chunk); - var lodGroupWorldReferencePoint = chunk.GetNativeArray(ref LODGroupWorldReferencePoint); - var instanceCount = chunk.Count; - - for (int i = 0; i < instanceCount; i++) - { - lodGroupWorldReferencePoint[i] = new LODGroupWorldReferencePoint { - Value = qvvs.TransformPoint(worldTransform[i].worldTransformQvvs, meshLODGroupComponent[i].LocalReferencePoint) - }; - } - } - } - - [BurstCompile] - struct UpdateLODWorldReferencePointsJob : IJobChunk - { - [ReadOnly] public ComponentTypeHandle MeshLODComponent; - [ReadOnly] public ComponentLookup LODGroupWorldReferencePoint; - public ComponentTypeHandle RootLODWorldReferencePoint; - public ComponentTypeHandle LODWorldReferencePoint; - - public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) - { - // This job is not written to support queries with enableable component types. - Unity.Assertions.Assert.IsFalse(useEnabledMask); - - var rootLODWorldReferencePoint = chunk.GetNativeArray(ref RootLODWorldReferencePoint); - var lodWorldReferencePoint = chunk.GetNativeArray(ref LODWorldReferencePoint); - var meshLods = chunk.GetNativeArray(ref MeshLODComponent); - var instanceCount = chunk.Count; - - for (int i = 0; i < instanceCount; i++) - { - var meshLod = meshLods[i]; - var lodGroupEntity = meshLod.Group; - var lodGroupWorldReferencePoint = LODGroupWorldReferencePoint[lodGroupEntity].Value; - - lodWorldReferencePoint[i] = new LODWorldReferencePoint { Value = lodGroupWorldReferencePoint }; - } - - for (int i = 0; i < instanceCount; i++) - { - var meshLod = meshLods[i]; - var parentGroupEntity = meshLod.ParentGroup; - - RootLODWorldReferencePoint rootPoint; - - if (parentGroupEntity == Entity.Null) - { - rootPoint.Value = new float3(0, 0, 0); - } - else - { - var parentGroupWorldReferencePoint = LODGroupWorldReferencePoint[parentGroupEntity].Value; - rootPoint.Value = parentGroupWorldReferencePoint; - } - - rootLODWorldReferencePoint[i] = rootPoint; - } - } - } - } -} - diff --git a/Kinemation/Systems/UnityReplacements/LatiosLODRequirementsUpdateSystem.cs.meta b/Kinemation/Systems/UnityReplacements/LatiosLODRequirementsUpdateSystem.cs.meta deleted file mode 100644 index ea55624..0000000 --- a/Kinemation/Systems/UnityReplacements/LatiosLODRequirementsUpdateSystem.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5799871a0dcb6814bb882fc22b6a0338 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Kinemation/Utilities/KinemationBootstrap.cs b/Kinemation/Utilities/KinemationBootstrap.cs index 8f27d29..7f78afa 100644 --- a/Kinemation/Utilities/KinemationBootstrap.cs +++ b/Kinemation/Utilities/KinemationBootstrap.cs @@ -47,7 +47,6 @@ public static void InstallKinemation(World world) BootstrapTools.InjectSystem(TypeManager.GetSystemTypeIndex(), world); BootstrapTools.InjectSystem(TypeManager.GetSystemTypeIndex(), world); BootstrapTools.InjectSystem(TypeManager.GetSystemTypeIndex(), world); - BootstrapTools.InjectSystem(TypeManager.GetSystemTypeIndex(), world); BootstrapTools.InjectSystem(TypeManager.GetSystemTypeIndex(), world); BootstrapTools.InjectSystem(TypeManager.GetSystemTypeIndex(), world); diff --git a/MachAxle.meta b/MachAxle.meta new file mode 100644 index 0000000..ae56813 --- /dev/null +++ b/MachAxle.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 146e1d41e0d5fdc4b819ed6c8ae57a72 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PsyshockPhysics/Physics/Spatial/Builders/Physics.BuildCollisionLayer.cs b/PsyshockPhysics/Physics/Spatial/Builders/Physics.BuildCollisionLayer.cs index 1c00f63..b980e53 100644 --- a/PsyshockPhysics/Physics/Spatial/Builders/Physics.BuildCollisionLayer.cs +++ b/PsyshockPhysics/Physics/Spatial/Builders/Physics.BuildCollisionLayer.cs @@ -503,7 +503,10 @@ public static JobHandle ScheduleParallel(this BuildCollisionLayerConfig config, NativeList xMinMaxs; NativeList aos; - JobHandle filteredCacheDisposeHandle = default; + // A Unity bug causes some platforms to not recognize the != operator for JobHandle. + // As a workaround, we use a secondary bool. + JobHandle filteredCacheDisposeHandle = default; + bool hasFilteredCacheDisposeHandle = false; if (config.query.HasFilter() || config.query.UsesEnabledFiltering()) { @@ -536,7 +539,8 @@ public static JobHandle ScheduleParallel(this BuildCollisionLayerConfig config, xMinMaxs = xMinMaxs.AsDeferredJobArray() }.Schedule(filteredChunkCache, 1, jh); - filteredCacheDisposeHandle = filteredChunkCache.Dispose(jh); + filteredCacheDisposeHandle = filteredChunkCache.Dispose(jh); + hasFilteredCacheDisposeHandle = true; } else { @@ -567,7 +571,8 @@ public static JobHandle ScheduleParallel(this BuildCollisionLayerConfig config, layerIndices = layerIndices.AsDeferredJobArray() }.Schedule(jh); - if (filteredCacheDisposeHandle != default) + //if (filteredCacheDisposeHandle != default) + if (hasFilteredCacheDisposeHandle) jh = JobHandle.CombineDependencies(filteredCacheDisposeHandle, jh); jh = new BuildCollisionLayerInternal.Part3Job diff --git a/README.md b/README.md index 77685e0..719f7bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![](https://github.com/Dreaming381/Latios-Framework-Documentation/blob/554a583e217bfe5bf38ece0ed65b22c33711afc6/media/bf2cb606139bb3ca01fe1c4c9f92cdf7.png) -# Latios Framework for Unity ECS – [0.10.0-alpha.3] +# Latios Framework for Unity ECS – [0.10.0-alpha.4] **This is a prerelease version of the Latios Framework version 0.10 which is still under development. Changelogs and Documentation, including the remainder diff --git a/package.json b/package.json index 7a925ed..032a5b0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.latios.latiosframework", "displayName": "Latios Framework for ECS", - "version": "0.10.0-alpha.3", + "version": "0.10.0-alpha.4", "unity": "2022.3", "description": "Latios Framework for ECS is a collection of tools, algorithms, and API extensions developed by a hardcore hobbyist game developer.\n\nThis package includes all of the following modules:\n\u25aa Core\n\u25aa QVVS Transforms\n\u25aa Psyshock Physics\n\u25aa Myri Audio\n\u25aa Kinemation Animation and Rendering\nu25aa Caligraphics\nu25aa Mimic\n\nExamples: \n\u25aa Latios Space Shooter Sample - https://github.com/Dreaming381/lsss-wip \n\u25aa Mini Demos - https://github.com/Dreaming381/LatiosFrameworkMiniDemos \n\u25aa Free Parking - https://github.com/Dreaming381/Free-Parking", "documentationURL": "https://github.com/Dreaming381/Latios-Framework-Documentation",