Browse Source

Merge branch 'ww1dev/hdrp17/svn' into ww1dev/hdrp17/staging

ww1dev/hdrp17/staging
Nico de Poel 2 months ago
parent
commit
2e727d1daa
  1. 2
      Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbePlacement.cs
  2. 24
      Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs
  3. 60
      Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs
  4. 10
      Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs
  5. 2
      Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersBatchersContext.cs
  6. 5
      Packages/com.unity.render-pipelines.core/Runtime/Utilities/CoreUtils.cs
  7. 4
      Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeComponent.cs
  8. 7
      Packages/com.unity.render-pipelines.high-definition/Editor/Material/Canvas/ShaderGraph/HDCanvasSubTarget.cs
  9. 5
      Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/HDSubShaderUtilities.cs
  10. 2
      Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/SurfaceOptionPropertyBlock.cs
  11. 9
      Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/TargetData/SystemData.cs
  12. 8
      Packages/com.unity.render-pipelines.high-definition/Editor/Material/UIBlocks/SurfaceOptionUIBlock.cs
  13. 14
      Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/BaseLitAPI.cs
  14. 3
      Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/LitAPI.cs
  15. 3
      Packages/com.unity.render-pipelines.high-definition/Runtime/Material/ShaderGraphAPI.cs
  16. 3
      Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Unlit/UnlitAPI.cs
  17. 35
      Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/AlphaUpscale.compute
  18. 4
      Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/DLSSBiasColorMask.shader
  19. 2
      Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs
  20. 2
      Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs
  21. 4
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructAdapter.cs
  22. 24
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/TerrainToMesh.cs
  23. BIN
      Packages/com.unity.splines/.attestation.p7m
  24. 1
      Packages/com.unity.splines/.signature
  25. 553
      Packages/com.unity.splines/CHANGELOG.md
  26. 7
      Packages/com.unity.splines/CHANGELOG.md.meta
  27. 8
      Packages/com.unity.splines/Editor.meta
  28. 5
      Packages/com.unity.splines/Editor/AssemblyInfo.cs
  29. 11
      Packages/com.unity.splines/Editor/AssemblyInfo.cs.meta
  30. 8
      Packages/com.unity.splines/Editor/Components.meta
  31. 355
      Packages/com.unity.splines/Editor/Components/SplineAnimateEditor.cs
  32. 11
      Packages/com.unity.splines/Editor/Components/SplineAnimateEditor.cs.meta
  33. 42
      Packages/com.unity.splines/Editor/Components/SplineComponentEditor.cs
  34. 11
      Packages/com.unity.splines/Editor/Components/SplineComponentEditor.cs.meta
  35. 104
      Packages/com.unity.splines/Editor/Components/SplineContainerEditor.cs
  36. 11
      Packages/com.unity.splines/Editor/Components/SplineContainerEditor.cs.meta
  37. 305
      Packages/com.unity.splines/Editor/Components/SplineExtrudeEditor.cs
  38. 3
      Packages/com.unity.splines/Editor/Components/SplineExtrudeEditor.cs.meta
  39. 665
      Packages/com.unity.splines/Editor/Components/SplineInstantiateEditor.cs
  40. 11
      Packages/com.unity.splines/Editor/Components/SplineInstantiateEditor.cs.meta
  41. 8
      Packages/com.unity.splines/Editor/Controls.meta
  42. 295
      Packages/com.unity.splines/Editor/Controls/CurveHandles.cs
  43. 11
      Packages/com.unity.splines/Editor/Controls/CurveHandles.cs.meta
  44. 201
      Packages/com.unity.splines/Editor/Controls/DirectManipulation.cs
  45. 11
      Packages/com.unity.splines/Editor/Controls/DirectManipulation.cs.meta
  46. 322
      Packages/com.unity.splines/Editor/Controls/KnotHandles.cs
  47. 11
      Packages/com.unity.splines/Editor/Controls/KnotHandles.cs.meta
  48. 228
      Packages/com.unity.splines/Editor/Controls/SplineElementRectSelector.cs
  49. 11
      Packages/com.unity.splines/Editor/Controls/SplineElementRectSelector.cs.meta
  50. 68
      Packages/com.unity.splines/Editor/Controls/SplineHandleSettings.cs
  51. 11
      Packages/com.unity.splines/Editor/Controls/SplineHandleSettings.cs.meta
  52. 464
      Packages/com.unity.splines/Editor/Controls/SplineHandles.cs
  53. 11
      Packages/com.unity.splines/Editor/Controls/SplineHandles.cs.meta
  54. 188
      Packages/com.unity.splines/Editor/Controls/SplineMeshHandle.cs
  55. 3
      Packages/com.unity.splines/Editor/Controls/SplineMeshHandle.cs.meta
  56. 238
      Packages/com.unity.splines/Editor/Controls/TangentHandles.cs
  57. 11
      Packages/com.unity.splines/Editor/Controls/TangentHandles.cs.meta
  58. 8
      Packages/com.unity.splines/Editor/Core.meta
  59. 302
      Packages/com.unity.splines/Editor/Core/CopyPaste.cs
  60. 3
      Packages/com.unity.splines/Editor/Core/CopyPaste.cs.meta
  61. 41
      Packages/com.unity.splines/Editor/Core/PathSettings.cs
  62. 11
      Packages/com.unity.splines/Editor/Core/PathSettings.cs.meta
  63. 45
      Packages/com.unity.splines/Editor/Core/SelectionContext.cs
  64. 11
      Packages/com.unity.splines/Editor/Core/SelectionContext.cs.meta
  65. 385
      Packages/com.unity.splines/Editor/Core/SplineElement.cs
  66. 11
      Packages/com.unity.splines/Editor/Core/SplineElement.cs.meta
  67. 610
      Packages/com.unity.splines/Editor/Core/SplineSelection.cs
  68. 11
      Packages/com.unity.splines/Editor/Core/SplineSelection.cs.meta
  69. 8
      Packages/com.unity.splines/Editor/Editor Resources.meta
  70. 8
      Packages/com.unity.splines/Editor/Editor Resources/Icons.meta
  71. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On.png
  72. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On.png.meta
  73. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On@2x.png
  74. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On@2x.png.meta
  75. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot.png
  76. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot.png.meta
  77. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot@2x.png
  78. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot@2x.png.meta
  79. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool.png
  80. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool.png.meta
  81. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool@2x.png
  82. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool@2x.png.meta
  83. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent.png
  84. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent.png.meta
  85. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent@2x.png
  86. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent@2x.png.meta
  87. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext.png
  88. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext.png.meta
  89. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext@2x.png
  90. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext@2x.png.meta
  91. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info.png
  92. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info.png.meta
  93. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info@2x.png
  94. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info@2x.png.meta
  95. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On.png
  96. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On.png.meta
  97. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On@2x.png
  98. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On@2x.png.meta
  99. BIN
      Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous.png
  100. 146
      Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous.png.meta

2
Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbePlacement.cs

@ -506,6 +506,8 @@ namespace UnityEngine.Rendering
props.SetInt(_AxisSwizzle, 2);
cmd.DrawProcedural(transform, voxelizeMaterial, shaderPass: 0, MeshTopology.Quads, 4 * terrainTileCount, 1, props);
//WW1MOD - add if-case, comon unity
if (terrain.drawTreesAndFoliage)
foreach (var prototype in kp.treePrototypes)
{
if (prototype.component == null || prototype.instances.Count == 0)

24
Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs

@ -2312,6 +2312,7 @@ namespace UnityEngine.Rendering
public InstanceOcclusionTestSubviewSettings subviewSettings;
public OccluderHandles occluderHandles;
public IndirectBufferContextHandles bufferHandles;
public OcclusionCullingCommonShaderVariables commonShaderVariables;
}
public void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSettings settings, ReadOnlySpan<SubviewOcclusionTest> subviewOcclusionTests, RenderersBatchersContext batchersContext)
@ -2335,6 +2336,25 @@ namespace UnityEngine.Rendering
passData.bufferHandles.UseForOcclusionTest(builder);
passData.occluderHandles.UseForOcclusionTest(builder);
// Resolve the correct occlusion culling common data now as the depth buffer dimensions may be different when we execute,
// especially as we are dealing with the previous frame's occluders.
// (e.g. dynamic resolution)
int indirectContextIndex = m_IndirectStorage.TryGetContextIndex(settings.viewInstanceID);
if (indirectContextIndex >= 0)
{
var debugStats = GPUResidentDrawer.GetDebugStats();
passData.commonShaderVariables = new OcclusionCullingCommonShaderVariables(
in occluderCtx,
passData.subviewSettings,
debugStats?.occlusionOverlayCountVisible ?? false,
debugStats?.overrideOcclusionTestToAlwaysPass ?? false);
}
else
{
passData.commonShaderVariables = default;
}
builder.SetRenderFunc((InstanceOcclusionTestPassData data, ComputeGraphContext context) =>
{
var batcher = GPUResidentDrawer.instance.batcher;
@ -2344,6 +2364,7 @@ namespace UnityEngine.Rendering
data.subviewSettings,
data.bufferHandles,
data.occluderHandles,
data.commonShaderVariables,
batcher.batchersContext);
});
}
@ -2403,6 +2424,7 @@ namespace UnityEngine.Rendering
in InstanceOcclusionTestSubviewSettings subviewSettings,
in IndirectBufferContextHandles bufferHandles,
in OccluderHandles occluderHandles,
in OcclusionCullingCommonShaderVariables commonShaderVariables,
RenderersBatchersContext batchersContext)
{
var occlusionCullingCommon = batchersContext.occlusionCullingCommon;
@ -2534,7 +2556,7 @@ namespace UnityEngine.Rendering
cmd.SetBufferData(m_ConstantBuffer, m_ShaderVariables);
cmd.SetComputeConstantBufferParam(cs, ShaderIDs.InstanceOcclusionCullerShaderVariables, m_ConstantBuffer, 0, m_ConstantBuffer.stride);
occlusionCullingCommon.PrepareCulling(cmd, in occluderCtx, settings, subviewSettings, m_OcclusionTestShader, occlusionDebug);
occlusionCullingCommon.PrepareCulling(cmd, in commonShaderVariables, settings, m_OcclusionTestShader, occlusionDebug);
if (doCopyInstances)
{

60
Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs

@ -332,14 +332,18 @@ namespace UnityEngine.Rendering
var transformUpdateDataQueue = new NativeArray<TransformUpdatePacket>(initialize ? instances.Length * 2 : instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var boundingSpheresUpdateDataQueue = new NativeArray<float4>(m_EnableBoundingSpheres ? instances.Length : 0, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeInstanceQueue = new NativeArray<InstanceHandle>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var compactTetrahedronCache = new NativeArray<int>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeQueryPosition = new NativeArray<Vector3>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeUpdateDataQueue = new NativeArray<SphericalHarmonicsL2>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeOcclusionUpdateDataQueue = new NativeArray<Vector4>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
//WW1MOD THIS FUNCTION's COMMENTED OUT LINES
// Quite ugly, sorry, need to remove everything related to ProbesUpdateJob as we dont use lightprobes and this is wasted performance
// Figure this is easier to merge then to remove all offending lines?
//var probeInstanceQueue = new NativeArray<InstanceHandle>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
//var compactTetrahedronCache = new NativeArray<int>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
//var probeQueryPosition = new NativeArray<Vector3>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
//var probeUpdateDataQueue = new NativeArray<SphericalHarmonicsL2>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
//var probeOcclusionUpdateDataQueue = new NativeArray<Vector4>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var transformQueueCount = 0;
int probesQueueCount = 0;
//int probesQueueCount = 0;
var transformJob = new TransformUpdateJob()
{
@ -356,28 +360,28 @@ namespace UnityEngine.Rendering
boundingSpheresDataQueue = boundingSpheresUpdateDataQueue,
};
var probesJob = new ProbesUpdateJob()
{
instances = instances,
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount),
probeInstanceQueue = probeInstanceQueue,
compactTetrahedronCache = compactTetrahedronCache,
probeQueryPosition = probeQueryPosition
};
//var probesJob = new ProbesUpdateJob()
//{
// instances = instances,
// instanceData = m_InstanceData,
// sharedInstanceData = m_SharedInstanceData,
// atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount),
// probeInstanceQueue = probeInstanceQueue,
// compactTetrahedronCache = compactTetrahedronCache,
// probeQueryPosition = probeQueryPosition
//};
JobHandle jobHandle = transformJob.ScheduleBatch(instances.Length, TransformUpdateJob.k_BatchSize);
jobHandle = probesJob.ScheduleBatch(instances.Length, ProbesUpdateJob.k_BatchSize, jobHandle);
//jobHandle = probesJob.ScheduleBatch(instances.Length, ProbesUpdateJob.k_BatchSize, jobHandle);
jobHandle.Complete();
if (probesQueueCount > 0)
{
ScheduleInterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, probeQueryPosition,
probeUpdateDataQueue, probeOcclusionUpdateDataQueue).Complete();
//if (probesQueueCount > 0)
//{
// ScheduleInterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, probeQueryPosition,
// probeUpdateDataQueue, probeOcclusionUpdateDataQueue).Complete();
DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue, renderersParameters, outputBuffer);
}
// DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue, renderersParameters, outputBuffer);
//}
if (transformQueueCount > 0)
{
@ -389,11 +393,11 @@ namespace UnityEngine.Rendering
transformUpdateDataQueue.Dispose();
boundingSpheresUpdateDataQueue.Dispose();
probeInstanceQueue.Dispose();
compactTetrahedronCache.Dispose();
probeQueryPosition.Dispose();
probeUpdateDataQueue.Dispose();
probeOcclusionUpdateDataQueue.Dispose();
//probeInstanceQueue.Dispose();
//compactTetrahedronCache.Dispose();
//probeQueryPosition.Dispose();
//probeUpdateDataQueue.Dispose();
//probeOcclusionUpdateDataQueue.Dispose();
}
private unsafe void UpdateInstanceProbesData(NativeArray<InstanceHandle> instances, in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)

10
Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs

@ -223,17 +223,11 @@ namespace UnityEngine.Rendering
return occluderCtx.occlusionDebugOverlaySize != 0;
}
internal void PrepareCulling(ComputeCommandBuffer cmd, in OccluderContext occluderCtx, in OcclusionCullingSettings settings, in InstanceOcclusionTestSubviewSettings subviewSettings, in OcclusionTestComputeShader shader, bool useOcclusionDebug)
internal void PrepareCulling(ComputeCommandBuffer cmd, in OcclusionCullingCommonShaderVariables commonShaderVariables, in OcclusionCullingSettings settings, in OcclusionTestComputeShader shader, bool useOcclusionDebug)
{
OccluderContext.SetKeyword(cmd, shader.cs, shader.occlusionDebugKeyword, useOcclusionDebug);
var debugStats = GPUResidentDrawer.GetDebugStats();
m_CommonShaderVariables[0] = new OcclusionCullingCommonShaderVariables(
in occluderCtx,
subviewSettings,
debugStats?.occlusionOverlayCountVisible ?? false,
debugStats?.overrideOcclusionTestToAlwaysPass ?? false);
m_CommonShaderVariables[0] = commonShaderVariables;
cmd.SetBufferData(m_CommonConstantBuffer, m_CommonShaderVariables);
cmd.SetComputeConstantBufferParam(shader.cs, ShaderIDs.OcclusionCullingCommonShaderVariables, m_CommonConstantBuffer, 0, m_CommonConstantBuffer.stride);

2
Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersBatchersContext.cs

@ -311,6 +311,8 @@ namespace UnityEngine.Rendering
public void UpdateAmbientProbeAndGpuBuffer(bool forceUpdate)
{
return; //WW1MOD JUST NO! We dont use lightprobes, why are you calculating this, its SUPER EXPENSIVE
if (forceUpdate || m_CachedAmbientProbe != RenderSettings.ambientProbe)
{
m_CachedAmbientProbe = RenderSettings.ambientProbe;

5
Packages/com.unity.render-pipelines.core/Runtime/Utilities/CoreUtils.cs

@ -610,7 +610,6 @@ namespace UnityEngine.Rendering
SetRenderTarget(cmd, colorBuffer, colorLoadAction, colorStoreAction, depthBuffer, depthLoadAction, depthStoreAction, clearFlag, Color.clear);
}
// WW1MOD make standalone builds run the same code as the editor, as partially clearing the render target can cause uninitialized garbage pixels to remain on dynamically scaled RTs.
private static void SetViewportAndClear(CommandBuffer cmd, RTHandle buffer, ClearFlag clearFlag, Color clearColor)
{
// Clearing a partial viewport currently does not go through the hardware clear.
@ -619,11 +618,11 @@ namespace UnityEngine.Rendering
// That's why in the editor we don't set the viewport before clearing (it's set to full screen by the previous SetRenderTarget) but AFTER so that we benefit from un-bugged hardware clear.
// We consider that the small loss in performance is acceptable in the editor.
// A refactor of wireframe is needed before we can fix this properly (with not doing anything!)
#if false //!UNITY_EDITOR
#if !UNITY_EDITOR
SetViewport(cmd, buffer);
#endif
CoreUtils.ClearRenderTarget(cmd, clearFlag, clearColor);
#if true //UNITY_EDITOR
#if UNITY_EDITOR
SetViewport(cmd, buffer);
#endif
}

4
Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeComponent.cs

@ -232,6 +232,10 @@ namespace UnityEngine.Rendering
/// </summary>
protected virtual void OnDisable()
{
// WW1MOD: prevent nullrefs when loading prefab contents in Editor automation scripts
if (parameterList == null)
return;
foreach (var parameter in parameterList)
{
if (parameter != null)

7
Packages/com.unity.render-pipelines.high-definition/Editor/Material/Canvas/ShaderGraph/HDCanvasSubTarget.cs

@ -121,7 +121,12 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph
pass.renderStates = new RenderStateCollection
{
{RenderState.Cull(Cull.Off)},
{RenderState.ZWrite(ZWrite.On)},
// WW1MOD - Unity recommended workaround for Canvas ShaderGraphs not working correctly in SceneView.
// Note: Won't be fixed officially as it causes MotionVector issues with Text in WorldSpace UI. Not an issue for us.
// https://support.unity.com/hc/en-us/requests/2392321?page=1
//{RenderState.ZWrite(ZWrite.On)},
{RenderState.ZWrite(ZWrite.Off)},
// WW1MOD - END
{RenderState.ZTest(CanvasUniforms.ZTest)},
{RenderState.Blend(Blend.One, Blend.OneMinusSrcAlpha)},
{RenderState.ColorMask(CanvasUniforms.ColorMask)},

5
Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/HDSubShaderUtilities.cs

@ -123,6 +123,10 @@ namespace UnityEditor.Rendering.HighDefinition
bool excludeFromTUAndAA = systemData?.excludeFromTUAndAA ?? false;
collector.AddToggleProperty(kExcludeFromTUAndAA, excludeFromTUAndAA);
// WW1MOD: added composition mask on stencil user bit 0
bool compositionMask = systemData?.compositionMask ?? false;
collector.AddToggleProperty(kCompositionMask, compositionMask);
// Configure render state
BaseLitAPI.ComputeStencilProperties(
receivesLighting,
@ -131,6 +135,7 @@ namespace UnityEditor.Rendering.HighDefinition
splitLighting,
false,
excludeFromTUAndAA,
compositionMask, // WW1MOD
out int stencilRef,
out int stencilWriteMask,
out int stencilRefDepth,

2
Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/SurfaceOptionPropertyBlock.cs

@ -126,6 +126,8 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph
AddProperty(customVelocityText, () => systemData.customVelocity, (newValue) => systemData.customVelocity = newValue);
AddProperty(excludeFromTUAndAAText, () => systemData.excludeFromTUAndAA, (newValue) => systemData.excludeFromTUAndAA = newValue);
// WW1MOD: added composition mask on stencil user bit 0
AddProperty(compositionMaskText, () => systemData.compositionMask, (newValue) => systemData.compositionMask = newValue);
AddProperty(tessellationEnableText, () => systemData.tessellation, (newValue) => systemData.tessellation = newValue);
if (systemData.tessellation)

9
Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/TargetData/SystemData.cs

@ -98,6 +98,15 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph
set => m_ExcludeFromTUAndAA = value;
}
// WW1MOD: added composition mask on stencil user bit 0
[SerializeField]
bool m_CompositionMask = false;
public bool compositionMask
{
get => m_CompositionMask;
set => m_CompositionMask = value;
}
[SerializeField, Obsolete("Keep for migration")]
internal bool m_TransparentDepthPrepass;

8
Packages/com.unity.render-pipelines.high-definition/Editor/Material/UIBlocks/SurfaceOptionUIBlock.cs

@ -141,6 +141,8 @@ namespace UnityEditor.Rendering.HighDefinition
public static GUIContent specularAAThresholdText = new GUIContent("Threshold", "Controls the effect of Specular AA reduction. A values of 0 does not apply reduction, higher values allow higher reduction.");
public static GUIContent excludeFromTUAndAAText = new GUIContent("Exclude From Temporal Upscalers and Anti Aliasing", "When enabled, the current material wont be temporaly sampled during TAA and will have reduced ghosting on upscalers.");
// WW1MOD: added composition mask on stencil user bit 0
public static GUIContent compositionMaskText = new GUIContent("Composition Mask", "When enabled, the current material will be composited directly into the final image without temporal reconstruction.");
// SSR
public static GUIContent receivesSSRText = new GUIContent("Receive SSR", "When enabled, this Material can receive screen space reflections.");
@ -193,6 +195,7 @@ namespace UnityEditor.Rendering.HighDefinition
MaterialProperty transmissionEnable = null;
MaterialProperty clearCoatEnabled = null;
MaterialProperty excludeFromTUAndAA = null;
MaterialProperty compositionMask = null; // WW1MOD: added composition mask on stencil user bit 0
// Per pixel displacement params
MaterialProperty ppdMinSamples = null;
@ -327,6 +330,7 @@ namespace UnityEditor.Rendering.HighDefinition
clearCoatEnabled = FindProperty(kClearCoatEnabled);
excludeFromTUAndAA = FindProperty(kExcludeFromTUAndAA);
compositionMask = FindProperty(kCompositionMask); // WW1MOD: added composition mask on stencil user bit 0
if ((m_Features & Features.DoubleSidedNormalMode) != 0)
{
@ -816,6 +820,10 @@ namespace UnityEditor.Rendering.HighDefinition
if (excludeFromTUAndAA != null && BaseLitAPI.CompatibleWithExcludeFromTUAndAA(surfaceTypeValue, renderQueue))
materialEditor.ShaderProperty(excludeFromTUAndAA, Styles.excludeFromTUAndAAText);
// WW1MOD: added composition mask on stencil user bit 0
if (compositionMask != null && BaseLitAPI.CompatibleWithExcludeFromTUAndAA(surfaceTypeValue, renderQueue))
materialEditor.ShaderProperty(compositionMask, Styles.compositionMaskText);
if (enableGeometricSpecularAA != null)
{
materialEditor.ShaderProperty(enableGeometricSpecularAA, Styles.enableGeometricSpecularAAText);

14
Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/BaseLitAPI.cs

@ -119,7 +119,8 @@ namespace UnityEngine.Rendering.HighDefinition
return CompatibleWithExcludeFromTUAndAA(material.GetSurfaceType(), material.renderQueue) && material.HasProperty(kExcludeFromTUAndAA);
}
static public void SetupStencil(Material material, bool receivesLighting, bool receivesSSR, bool useSplitLighting, bool excludeFromTUAndAA = false)
// WW1MOD: added composition mask on stencil user bit 0
static public void SetupStencil(Material material, bool receivesLighting, bool receivesSSR, bool useSplitLighting, bool excludeFromTUAndAA = false, bool compositionMask = false)
{
// To determine if the shader is forward only, we can't rely on the presence of GBuffer pass because that depends on the active subshader, which
// depends on the active render pipeline, giving an inconsistent result. The properties of a shader are always the same so it's ok to check them
@ -133,6 +134,7 @@ namespace UnityEngine.Rendering.HighDefinition
useSplitLighting,
hasRefraction,
excludeFromTUAndAA,
compositionMask, // WW1MOD
out int stencilRef,
out int stencilWriteMask,
out int stencilRefDepth,
@ -177,6 +179,7 @@ namespace UnityEngine.Rendering.HighDefinition
bool useSplitLighting,
bool hasRefraction,
bool excludeFromTUAndAA,
bool compositionMask, // WW1MOD
out int stencilRef,
out int stencilWriteMask,
out int stencilRefDepth,
@ -252,6 +255,15 @@ namespace UnityEngine.Rendering.HighDefinition
stencilWriteMaskDepth |= (int)StencilUsage.ExcludeFromTUAndAA;
}
// WW1MOD: added composition mask on stencil user bit 0
if (compositionMask)
{
stencilRefDepth |= (int)StencilUsage.UserBit0;
stencilRef |= (int)StencilUsage.UserBit0;
stencilWriteMask |= (int)StencilUsage.UserBit0;
stencilWriteMaskDepth |= (int)StencilUsage.UserBit0;
}
stencilWriteMaskDepth |= (int)StencilUsage.IsUnlit;
stencilWriteMaskGBuffer |= (int)StencilUsage.IsUnlit;
stencilWriteMaskMV |= (int)StencilUsage.IsUnlit;

3
Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/LitAPI.cs

@ -14,7 +14,8 @@ namespace UnityEngine.Rendering.HighDefinition
bool receiveSSR = material.GetSurfaceType() == SurfaceType.Opaque ? (material.HasProperty(kReceivesSSR) ? material.GetInt(kReceivesSSR) != 0 : false)
: (material.HasProperty(kReceivesSSRTransparent) ? material.GetInt(kReceivesSSRTransparent) != 0 : false);
bool excludeFromTUAndAA = BaseLitAPI.CompatibleWithExcludeFromTUAndAA(material) && material.GetInt(kExcludeFromTUAndAA) != 0;
BaseLitAPI.SetupStencil(material, receivesLighting: true, receiveSSR, material.GetMaterialType() == MaterialId.LitSSS, excludeFromTUAndAA: excludeFromTUAndAA);
bool compositionMask = BaseLitAPI.CompatibleWithExcludeFromTUAndAA(material) && material.GetInt(kCompositionMask) != 0; // WW1MOD: added composition mask on stencil user bit 0
BaseLitAPI.SetupStencil(material, receivesLighting: true, receiveSSR, material.GetMaterialType() == MaterialId.LitSSS, excludeFromTUAndAA: excludeFromTUAndAA, compositionMask: compositionMask);
BaseLitAPI.SetupDisplacement(material);
if (material.HasProperty(kNormalMapSpace))

3
Packages/com.unity.render-pipelines.high-definition/Runtime/Material/ShaderGraphAPI.cs

@ -81,7 +81,8 @@ namespace UnityEngine.Rendering.HighDefinition
CoreUtils.SetKeyword(material, "_MATERIAL_FEATURE_CLEAR_COAT", material.GetFloat(kClearCoatEnabled) > 0.0);
bool excludeFromTUAndAA = BaseLitAPI.CompatibleWithExcludeFromTUAndAA(material) && material.GetInt(kExcludeFromTUAndAA) != 0;
BaseLitAPI.SetupStencil(material, receivesLighting: true, receiveSSR, useSplitLighting, excludeFromTUAndAA);
bool compositionMask = BaseLitAPI.CompatibleWithExcludeFromTUAndAA(material) && material.GetInt(kCompositionMask) != 0; // WW1MOD: added composition mask on stencil user bit 0
BaseLitAPI.SetupStencil(material, receivesLighting: true, receiveSSR, useSplitLighting, excludeFromTUAndAA, compositionMask);
}
public static void ValidateDecalMaterial(Material material)

3
Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Unlit/UnlitAPI.cs

@ -20,7 +20,8 @@ namespace UnityEngine.Rendering.HighDefinition
// All the bits exclusively related to lit are ignored inside the BaseLitGUI function.
bool receivesLighting = (material.HasProperty(kShadowMatteFilter) && material.GetFloat(kShadowMatteFilter) != 0);
bool excludeFromTUAndAA = BaseLitAPI.CompatibleWithExcludeFromTUAndAA(material) && material.GetInt(kExcludeFromTUAndAA) != 0;
BaseLitAPI.SetupStencil(material, receivesLighting: receivesLighting, receivesSSR: false, useSplitLighting: false, excludeFromTUAndAA: excludeFromTUAndAA);
bool compositionMask = BaseLitAPI.CompatibleWithExcludeFromTUAndAA(material) && material.GetInt(kCompositionMask) != 0; // WW1MOD: added composition mask on stencil user bit 0
BaseLitAPI.SetupStencil(material, receivesLighting: receivesLighting, receivesSSR: false, useSplitLighting: false, excludeFromTUAndAA: excludeFromTUAndAA, compositionMask: compositionMask);
}
}
}

35
Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/AlphaUpscale.compute

@ -4,6 +4,7 @@
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
TEXTURE2D_X(_InputTexture); // Input alpha source at (lower) render resolution
@ -106,6 +107,23 @@ void KMain(uint3 dispatchThreadId : SV_DispatchThreadID)
_OutputTexture[COORD_TEXTURE2D_X(InputPos)] = float4(fColor, fAlphaAccumulate);
}
static const float3 fKeyColor = float3(0, 1, 0); // Green screen chroma key, this needs to match the camera's background color
static const float fToleranceA = 0.4f;
static const float fToleranceB = 0.49f;
// Derived from http://gc-films.com/chromakey.html, using the faster YCoCg color space instead of YCbCr
float ChromaKey(float3 fColor)
{
const float3 fColorYCoCg = RGBToYCoCg(fColor);
const float3 fKeyYCoCg = RGBToYCoCg(fKeyColor);
const float3 fDelta = fKeyYCoCg - fColorYCoCg;
const float fDist = sqrt(fDelta.y * fDelta.y + fDelta.z * fDelta.z);
const float fMask = fDist < fToleranceA ? 0.0f : fDist < fToleranceB ? (fDist - fToleranceA) / (fToleranceB - fToleranceA) : 1.0f;
return fMask;
}
[numthreads(8, 8, 1)]
void KUpscaleFromDepth(uint3 dispatchThreadId : SV_DispatchThreadID)
{
@ -128,18 +146,19 @@ void KUpscaleFromDepth(uint3 dispatchThreadId : SV_DispatchThreadID)
const float2 fClampedLocation = max(0.5f, min(fSampleLocation, RenderSize - 0.5f));
const float2 fLrUv_HwSampler = fClampedLocation * InvInputResourceSize;
// De-jittered and bilinear upscaled alpha from depth
const float fAlpha = Linear01Depth(SAMPLE_TEXTURE2D_X_LOD(_CameraDepthTexture, s_linear_clamp_sampler, fLrUv_HwSampler, 0).r, _ZBufferParams) < 1 ? 1.0 : 0.0;
float3 fColor = _InputTexture[COORD_TEXTURE2D_X(InputPos)].rgb;
// Derive alpha value from green screen chroma keying
const float fAlpha = ChromaKey(fColor);
// Temporally reproject alpha from the previous frame and blend it with the current frame's upscaled alpha
const DepthExtents depthExtents = FindDepthExtents(fHrUvhistory, RenderSize); // Dilate depth so we don't end up grabbing motion vectors from the background
const float2 fMotionVector = LOAD_TEXTURE2D_X(_CameraMotionVectorsTexture, depthExtents.fNearestCoord).xy;
const float fReprojectedAlphaHistory = SAMPLE_TEXTURE2D_X_LOD(_InputHistoryTexture, s_linear_clamp_sampler, fHrUvhistory - fMotionVector, 0).r; // Sample reprojected history //TODO WRONG UV
const float fVelocityFactor = saturate(length(fMotionVector * DisplaySize) / 2.0f); // Adjust the amount of temporal blending based on the amount of motion
const float2 fDilatedMotionVector = LOAD_TEXTURE2D_X(_CameraMotionVectorsTexture, depthExtents.fNearestCoord).xy;
const float fReprojectedAlphaHistory = SAMPLE_TEXTURE2D_X_LOD(_InputHistoryTexture, s_linear_clamp_sampler, fHrUvhistory - fDilatedMotionVector, 0).r; // Sample reprojected history //TODO WRONG UV
const float fVelocityFactor = saturate(length(fDilatedMotionVector * DisplaySize) / 2.0f); // Adjust the amount of temporal blending based on the amount of motion
const float fBlend = depthExtents.fNearest > FLT_EPS && _TaaFrameInfo.z > 0 ? fVelocityFactor * 0.5f + 0.2f : 1.0f; // Depth clip to eliminate after-images
const float fAlphaAccumulate = lerp(fReprojectedAlphaHistory, fAlpha, fBlend);
_OutputHistoryTexture[COORD_TEXTURE2D_X(InputPos)] = fAlphaAccumulate;
const float3 fColor = _InputTexture[COORD_TEXTURE2D_X(InputPos)].rgb;
_OutputTexture2D[(InputPos)] = float4(fColor, fReprojectedAlphaHistory);
_OutputHistoryTexture[COORD_TEXTURE2D_X(InputPos)] = fAlphaAccumulate;
_OutputTexture2D[(InputPos)] = float4(fColor - (1.0f - fAlpha) * fKeyColor, fAlphaAccumulate); // Remove green spill from the edges
}

4
Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/DLSSBiasColorMask.shader

@ -55,13 +55,13 @@ Shader "Hidden/HDRP/DLSSBiasColorMask"
}
// Half/half reactive pixels, e.g. transparency effects like particles
if (stencilVal & STENCILUSAGE_USER_BIT0)
if (stencilVal & STENCILUSAGE_EXCLUDE_FROM_TUAND_AA)
{
reactiveValue = max(reactiveValue, 0.5f);
}
// Fully reactive pixels, e.g. composition effects like scopes
if (stencilVal & STENCILUSAGE_EXCLUDE_FROM_TUAND_AA)
if (stencilVal & STENCILUSAGE_USER_BIT0)
{
reactiveValue = max(reactiveValue, 0.95f);
}

2
Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs

@ -853,7 +853,7 @@ namespace UnityEngine.Rendering.HighDefinition
clearColor = Color.black,
name = "Upscaler Color Mask"
}), 0);
builder.UseDepthBuffer(inputDepth, DepthAccess.Read);
builder.ReadTexture(stencilBuffer);
passData.colorMaskMaterial = m_UpscalerBiasColorMaskMaterial;

2
Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs

@ -1319,6 +1319,8 @@ namespace UnityEngine.Rendering.HighDefinition
public const string kAffectEmission = "_AffectEmission";
/// <summary>Exclude from temporal upsamplers and anti aliasing.</summary>
public const string kExcludeFromTUAndAA = "_ExcludeFromTUAndAA";
/// <summary>WW1MOD: Composite pixels directly into the final image, without temporal reconstruction.</summary>
public const string kCompositionMask = "_CompositionMask";
/// <summary>Enable Receive Shadows Off (six-way only.) </summary>
public const string kReceiveShadows = "_ReceiveShadows";

4
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructAdapter.cs

@ -129,6 +129,10 @@ namespace UnityEngine.Rendering.UnifiedRayTracing
void AddTrees(TerrainDesc terrainDesc, ref List<InstanceIDs> instanceHandles)
{
//WW1MOD why doesnt unity handle this case?
if (!terrainDesc.terrain.drawTreesAndFoliage)
return;
TerrainData terrainData = terrainDesc.terrain.terrainData;
float4x4 terrainLocalToWorld = terrainDesc.localToWorldMatrix;
float3 positionScale = new float3((float)terrainData.heightmapResolution, 1.0f, (float)terrainData.heightmapResolution) * terrainData.heightmapScale;

24
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/TerrainToMesh.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
@ -8,6 +9,8 @@ namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal static class TerrainToMesh
{
static Dictionary<Terrain, Mesh> cache = new(); //WW1MOD Ok we will keep these, i dont care, its still faster and less crappy then re-doing this all the time for multiple bakes in a row
static private AsyncTerrainToMeshRequest MakeAsyncTerrainToMeshRequest(int width, int height, Vector3 heightmapScale, float[,] heightmap, bool[,] holes)
{
int vertexCount = width * height;
@ -70,6 +73,8 @@ namespace UnityEngine.Rendering.UnifiedRayTracing
JobHandle jobHandle = job.Schedule(vertexCount, math.max(width, 128));
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); //WW1MOD force GC as ive seen in log ends up with like 12GBs of it at this point...
return new AsyncTerrainToMeshRequest(job, jobHandle);
}
@ -85,7 +90,7 @@ namespace UnityEngine.Rendering.UnifiedRayTracing
static public AsyncTerrainToMeshRequest ConvertAsync(int heightmapWidth, int heightmapHeight, short[] heightmapData, Vector3 heightmapScale, int holeWidth, int holeHeight, byte[] holedata)
{
float[,] heightmap = new float[heightmapWidth,heightmapHeight];
float[,] heightmap = new float[heightmapWidth, heightmapHeight];
for (int y = 0; y < heightmapHeight; ++y)
for (int x = 0; x < heightmapWidth; ++x)
heightmap[y, x] = (float)heightmapData[y * heightmapWidth + x] / (float)32766;
@ -105,11 +110,26 @@ namespace UnityEngine.Rendering.UnifiedRayTracing
}
return MakeAsyncTerrainToMeshRequest(heightmapWidth, heightmapHeight, heightmapScale, heightmap, holes);
}
//WW1MOD this function, Add cache and mesh-name to output
static public Mesh Convert(Terrain terrain)
{
if (cache.TryGetValue(terrain, out Mesh mesh) && mesh != null && mesh.vertexCount > 0)
return mesh;
var request = ConvertAsync(terrain);
request.WaitForCompletion();
return request.GetMesh();
mesh = request.GetMesh();
mesh.name = terrain.name + "_lightbake";
cache[terrain] = mesh;
return mesh;
//Original
//var request = ConvertAsync(terrain);
//request.WaitForCompletion();
//return request.GetMesh();
}
static public Mesh Convert(int heightmapWidth, int heightmapHeight, short[] heightmapData, Vector3 heightmapScale, int holeWidth, int holeHeight, byte[] holedata)

BIN
Packages/com.unity.splines/.attestation.p7m

1
Packages/com.unity.splines/.signature

@ -0,0 +1 @@
{"timestamp":1757970422,"signature":"nMNeEND7In9XeSIqssS7g/agD+T9TkMOKwTl2kwp/0UhLtE6bBuuiqG8RStLM3qz4m5wRZLrGWs36ZTR1J2WFuUjC2as6ZSE7ItDsusnX5zhAQADU/ol5thot5fpwQm1G2nJUVUZOfHPCOc1uLIlruZ7bFwX9PwTZlDVk9GCNIe7N7cZ0CS5FdCyqQeuQjl99YC1PoHLifo+C1lPbqy0V1dEy6M9SacrAWSPWTmNaqLzfo46ircTPWS9zpx9dk3BQakBCHiF4cHFGIPpG5VTGpJqkR+SjsCbwjdPyI03BDOsZeCsUyIgvr2gSquBjDzARCqrzxKHNgLnb5WH/bnlrEIqslfMA0Ij/JdD1AYtycGlAjPd+Zb7EeF4utKbsYsv+E6jgkXVrwlCDH0JEgx2KhY1Iqy0E0k2rqPn8NJw41GV7G8KV63aVwK6pN8WcGZ+zi5oha+6UdySjnshSVAnBgBH/P4bxZVFpTMj5PqGuJLvDT7KQ8WeLm8ultfM5bIJ","publicKey":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUFzdUhXYUhsZ0I1cVF4ZEJjTlJKSAordHR4SmoxcVY1NTdvMlZaRE1XaXhYRVBkRTBEMVFkT1JIRXNSS1RscmplUXlERU83ZlNQS0ZwZ1A3MU5TTnJCCkFHM2NFSU45aHNQVDhOVmllZmdWem5QTkVMenFkVmdEbFhpb2VpUnV6OERKWFgvblpmU1JWKytwbk9ySTRibG4KS0twelJlNW14OTc1SjhxZ1FvRktKT0NNRlpHdkJMR2MxSzZZaEIzOHJFODZCZzgzbUovWjBEYkVmQjBxZm13cgo2ZDVFUXFsd0E5Y3JZT1YyV1VpWXprSnBLNmJZNzRZNmM1TmpBcEFKeGNiaTFOaDlRVEhUcU44N0ZtMDF0R1ZwCjVNd1pXSWZuYVRUemEvTGZLelR5U0pka0tldEZMVGdkYXpMYlpzUEE2aHBSK0FJRTJhc0tLTi84UUk1N3UzU2cKL2xyMnZKS1IvU2l5eEN1Q20vQWJkYnJMbXk0WjlSdm1jMGdpclA4T0lLQWxBRWZ2TzV5Z2hSKy8vd1RpTFlzUQp1SllDM0V2UE16ZGdKUzdGR2FscnFLZzlPTCsxVzROY05yNWdveVdSUUJ0cktKaWlTZEJVWmVxb0RvSUY5NHpCCndGbzJJT1JFdXFqcU51M3diMWZIM3p1dGdtalFra3IxVjJhd3hmcExLWlROQWdNQkFBRT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg"}

553
Packages/com.unity.splines/CHANGELOG.md

@ -0,0 +1,553 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [2.8.2] - 2025-09-18
### Fixed
- [DOCATT-4646], [DOCATT-5603], [DOCATT-7045], [DOCATT-5044], [DOCATT-6376], [DOCATT-6626] Clarified API documentation following user feedback.
- [SPLB-360] Fixed a bug when selecting the clear component option, resulting in out of range exception
- [SPLB-361] Fixed a bug where the spline would not animate with Scene/Domain reload disabled upon entering playmode
- [SPLB-356] Fixed a bug where a null reference exception would occur after baking a SplineInstantiate component and then pressing Undo.
## [2.8.1] - 2025-03-28
### Fixed
- [SPLB-345] Fixed a bug which was causing null reference exceptions during shutdown in IL2CPP builds.
- [SPLB-343] Fixed a bug where `JoinSplinesOnKnots` would throw a null reference exception when one of the splines was linked with another spline.
- [SPLB-341] Fixed a bug where changing the tangent mode on a knot on a prefab would not persist when entering play mode.
### Changed
- Cleaned up internal code to align with release standards.
## [2.8.0] - 2025-03-13
### Fixed
- [SPLB-322] Fixed a bug where `SplineExtrude` wouldn't update when multiple splines where modified.
- [SPLB-323] Fixed a bug where splines displayed in the Scene view would not match serialization changes.
- [SPLB-324] Re-added a dependency to the IMGUI module that was removed along with the UGUI package.
- [SPLB-332] Fixed a bug where `Element Inspector` fields get pushed and squashed.
- [SPLB-329] Fixed a bug where `SplineExtrude` could affect the mesh of other `SplineExtrude`.
- [SPLB-336] Fixed a bug where `SplineExtrude` would break with a range value of 0.
- [SPLB-333] Fixed a bug where `Create Spline` tool would sometimes throw an exception when the Esc or Enter keys were pressed.
### Added
- [SPLB-329] Added the ability to target a mesh in the library with a `SplineExtrude` component
## [2.7.2] - 2024-11-08
### Fixed
- [SPLB-315] Fixed a bug where the `DrawSplineTool` icon was missing in the Tools overlay.
- [STO-3434] Fixed a bug where the `DrawSplineTool` prevented the use of the View tool.
- [STO-3435] Fixed a bug where the `DrawSplineTool` did not display the Tool settings overlay.
- [SPLB-296] Fixed a bug where a log message appeared in the console when two knots are at the same position with 0-length tangents.
- [SPLB-304] Fixed a bug where an exception would be thrown when using `SplineInstantiate` with linear distance instantiate method and multiple splines.
- Fixed some failures related to the inline API documentation.
- [SPLB-302] Fixed a bug where an exception would be thrown when adding a knot to a closed spline from the Inspector.
- [SPLB-311] Fixed a bug where the `SplineAnimate` component would not function correctly with non-uniform scale transforms.
- Fixed compilation errors in builds.
- [SPLB-310] Fixed a bug where mesh extrude would not save when a change was applied to a mesh asset.
- [SPLB-303] Fixed a bug where spline section foldout labels (shape extrude, geometry, advanced) did not unfold the sections when clicked.
- [SPLB-294] Fixed a bug where adding new splines by using the `SplineContainer` inspector would dirty all open scenes.
## [2.7.1] - 2024-10-17
### Added
- [STO-3204] Added a **Create Spline** button to enter spline creation when you do not have the Spline tool context activated.
- New `SplineExtrude` and `SplineMesh` option enables new extrusion profiles, including `Circle`, `Square`, `Road`, and `Spline`.
- Added a section in the documentation for the `Source Spline Container` in the `SplineExtrude` component.
### Fixed
- [SPLB-292] Removed the dependency on the UGUI package.
- Removed the toggle to override the target spline in the `SplineExtrude` inspector.
- [SPLB-288] Fixed a bug where a prefab `SplineContainer` would unexpectedly restore splines deleted before entering and exiting playmode.
- [SPLB-284] Fixed a bug that prevented baking instances of multiple `SplineInstantiate` components when multi-selecting.
- [SPLB-277] Fixed a bug where instantiating a prefab with `SplineAnimate` component would throw messages in the console.
- [SPLB-279] Fixed a bug where closing a Spline would not update the auto-smooth knots at the extremities.
## [2.6.1] - 2024-05-23
### Fixed
- [SPLB-275] Fixed a bug where the `SplineContainer` component would throw warning when entering playmode.
- [SPLB-266] Fixed a bug where instantiating a `SplineExtrude` component at runtime would throw errors.
- [SPLB-269] Fixed a bug where instantiating a `SplineAnimate` component at runtime would throw errors.
- [SPLB-260] Fixed a bug where playing a `SplineAnimate` from a start method would not work.
- [SPLB-267] Fixed a bug where undoing a spline insertion in a container would throw errors from the `SplineInstantiate` component.
- [SPLB-261] Fixed a bug where the knot placement tool would not work when multiple scene views were opened.
- [SPLB-253] Fixed a bug where using the knot placement tool in 2D mode would not work if the grids are turned off.
- [SPLB-258] Fixed a bug where destroying a component would throw errors in the console.
## [2.6.0] - 2024-04-15
### Added
- Added new APIs that enable users to directly provide a collection of `float3` knot positions to a `Spline` constructor, or to the new `Add`, `AddRange`, `Insert`, and `InsertRange` methods, instead of using `BezierKnots`.
- Added an event to the `SplineAnimate` component that is triggered when the animation or loop is completed.
### Changed
- Changed supported version in the documentation dropdown selector to 2022.3 and later.
### Fixed
- [SPLB-246] Fixed performance and garbage collection (GC) memory allocation issues when evaluating splines using `SplineContainer` API.
- [STO-3176] Fixed an issue where `FitSplineToPoints` utility would not fit splines correctly.
- [SPLB-249] Fixed a bug where setting a spline game object position was overridden by the knot placement tool.
- Fixed a bug where using the knot placement tool on scaled objects would not create the correct tangents.
- Fixed a bug where changing the scale of a spline game object would not update the scene view handles.
- [SPLB-239] Fixed a bug where reverting overrides of a spline prefab instance would not properly update its visuals.
- [SPLB-238] Fixed a bug where `SplineAnimate` component would continuously output messages to the Console when in playmode and animated object was selected.
- [SPLB-245] Fixed a bug where `SplineAnimate` component would rotate the animated object on playmode exit.
## [2.5.2] - 2023-12-22
### Added
- [SPLB-222] Added safety checks to spline changed events.
### Changed
- [SPLB-233] Removed transform handles while dragging a knot using direct manipulation.
- [SPLB-234] Removed unnecessary spline metadata which was saved on disk.
### Fixed
- [SPLB-230] Fixed a bug where modifying the tangent mode on the last knot would change the spline shape unpredictably.
- [SPLB-225] Fixed a bug where closing a Spline with the `Draw Splines Tool` had inconsistent behaviour.
- [SPLB-236] Fixed a bug where the spline instantiate inspector was throwing an exception when the selection contained objects without that component.
- [SPLB-235] Fixed a bug where hovering a knot was create highlights when dragging tangents.
- [SPLB-234] Fixed a bug where it was impossible to add a knot on the latest added knot.
- [SPLB-214] Fixed an exception that would when adding a spline via the inspector if no other spline exists.
- [SPLB-226] Fixed broken preview when adding knots to a closed spline
## [2.5.1] - 2023-10-17
### Fixed
- [SPLB-208] Fixed a bug where `SplineToolContext` would attempt to draw empty Splines and flood the console with errors.
## [2.5.0] - 2023-10-16
### Added
- Exposed public API for `SplineInstantiate.Seed`.
### Fixed
- [SPLB-201] Fixed error messages showing in edit mode with spline components.
- [SPLB-203] Fixed a bug where baking `SplineInstantiate` instances broke prefab connections.
- [SPLB-196] Fixed a bug where the `Random` state was not restored after an update of the instances in `SplineInstantiate`.
- Fixed a bug where the `SplineCacheUtility` would send null reference exceptions.
- [SPLB-183] Fixed a bug where duplicating the `SplineExtrude` component was not updating the mesh asset reference.
- [SPLB-178] Fixed a bug where linking knots would not trigger a `Spline.changed` event.
- [SPLB-185] Fixed a bug where the Up vector evaluation would return NaN values when normals and curve tangents are parallel.
### Changed
- [SPLB-187] Fixed a bug where `Spline.EvaluatePosition` was allocating memory when called.
- [SPLB-181] Fixed a bug where the Auto tangent mode computed incorrect knot rotations.
- Changed the `SplineComponent` display name in the Inspector to `Spline Container` as a response to user feedback.
## [2.4.0] - 2023-07-26
### Added
- Exposed public APIs for `SplineUtility.ReverseFlow`, `SplineUtility.JoinSplinesOnKnots`, and `SplineUtility.SplitSplineOnKnot`.
### Changed
- For Editor versions of 2023.2 or newer, added clipboard operations for Splines to the Scene view context menu.
- Changed `Splines/Cycle Active Spline` shortcut to Shift-S to remove a conflict with Unity default shortcuts.
- [SPLB-163] Improved performance of spline handles by caching spline data.
- [SPLB-167] Changed the evaluation of up vectors to use a cache instead of re-evaluate the values on every single request.
- In Editor versions of 2023.2 or newer, moved the following actions from the Element Inspector to the Scene view context menu: Link, Unlink, Split, Join, and Reverse Spline Flow.
### Fixed
- [SPLB-176] Fixed a regression where the up vector would not evaluate correctly for some curves.
- Fixed the setter for SplineAnimate.Normalized time to handle ping-pong direction correctly when set.
- Fixed a bug where setting the time value to 0 after reaching the end of the spline animation was not resetting the object position.
- [SPLB-169] Fixed a bug where `CurveUtility.GetNearestPoint` would return incorrect values for the interpolation.
## [2.3.0] - 2023-05-31
### Added
- Exposed public APIs for `SplineTool` and `SplineHandles`.
- Added a method to *bake* SplineInstantiate objects in the scene so the users can modify the resulting GameObjects.
### Fixed
- [SPLB-165] Fixed a bug that prevented actions from being retained with prefab instances that contained splines when entering playmode.
- [SPLB-162] Fixed a bug where the start offset in `SplineAnimate` did not work in Play mode.
- [SPLB-152] Fixed objects created from `SplineInstantiate` not inheriting static flags from parent `GameObject`.
- [SPLB-160] Fixed potential 'look rotation vector is zero' in SplineInstantiate.
- [SPLB-156] Fixed possible null reference exception when accessing curve length.
## [2.2.1] - 2023-04-21
### Added
- [SPLB-100] Added a **Resolution** setting for spline meshes in Splines preference.
### Changed
- [SPLB-100] Improved `SplineMeshHandles` rendering.
### Fixed
- Fixed a bug where spline selection would be incorrect after a spline was deleted.
- [SPLB-146] Fixed a bug that prevented actions from being undone with prefab instances that contained splines .
- [SPLB-148] Fixed a bug in the `LoftRoadBehaviour` component where Undo calls prevented the project from building.
### Changed
- Changed `Knot Placement` tool behaviour regarding selection. Element selection is now empty when you exit the tool.
## [2.2.0] - 2023-03-22
### Added
- [SPLB-72] Added a public version of `SplineDataHandles.DataPointHandles` which requires an additional parameter, `splineID`.
- [SPLB-72] Added the `SplineContainer.SplineAdded`, `SplineContainer.SplineRemoved` and `SplineContainer.SplineReordered` public events and removed those same events from `ISplineContainer`
- [SPLB-72] Added the `SplineUtility.ReorderSpline` public method.
- [SPLB-72] Added an example to the **Loft Road** sample that supports spline data for multiple splines.
- Improved UI for editing points in the Inspector.
- Added preference to disable tangent dragging feature in `Knot Placement` tool.
- Added `ISplineContainer.AddSpline(Spline)` function overload.
- Added new functions on `Spline` to get and set `SplineData`.
- Added new `EmbeddedSplineData` type to easily create and reference `SplineData` stored on arbitrary `Spline` objects.
### Changed
- [SPLB-127] Changes made to a spline in the Inspector no longer invoke the `Spline.Changed` event with the `SplineModification.Default` type. The `Spline.Changed` event is still invoked, but with specific modification types like `SplineModification.KnotModified`.
- [SPLB-118] Changed the documentation of `SplineMath.PointLineNearestPoint` and `SplineMath.RayLineNearestPoint` to explain the returned values more precisely.
- [SPLB-96] Changed the default tension of tangents in **Auto** mode so they are smoother. **Auto** mode is now closer to the Catmull-Rom behavior of Splines 1.0.
- [SPLB-72] Removed the `WidthSplineData` and `MultipleRoadBehaviour` components from the samples.
- Reduced heap allocations when modifying Spline properties.
- [SPLB-92] Modified the Inspector to use `SplineModification.KnotRemoved` events to remove knots and pass the index for each removed knot.
- [SPLB-92] Modified the `Spline.SetTangentMode` and `Spline.SetAutoSmoothTension` methods to invoke `SplineModification.KnotModified` events for each modified knot and pass the index.
- Increased the threshold for tangent dragging when placing knots with the `Knot Placement` tool.
### Fixed
- [SPLB-141] Fixed a bug where `SplineExtrude` component would overwrite existing Mesh assets.
- [SPLB-129] Fixed a bug where `CurveUtility.EvaluateUpVector` caused burst errors.
- [SPLB-138] Fixed a bug where `SplineJobs.EvaluatePositionTangentNormal` returned incorrect values.
- [SPLB-136] Fixed a bug where undoing actions on a spline was not marking the spline as dirty.
- [SPLB-123] Fixed an inconsistency between inserting a `BezierKnot` using Editor tools and runtime API.
- [SPLB-130] Fixed a bug that would crash the Editor if the `SplineInstantiate` component had its parent GameObject in the list of items to instantiate.
- [SPLB-90] Fixed an issue where `Spline.SetKnotNoNotify` triggered change callbacks.
- [SPLB-77] Fixed a null reference exception when spline animate didn't have a spline referenced.
- [SPLB-125] Fixed issue where the spline would have different state during the `Spline.Changed` callback depending if it was modified using the API or inspector.
- [SPLB-74] Fixed a bug that caused an error when knots were deleted from the Inspector.
- [SPLB-121] Fixed a bug where spline data changes done using the Inspector would not undo correctly.
- [SPLB-92] Fixed an issue where adding knot using the inspector would trigger `SplineModification.KnotInserted` event twice.
- [SPLB-107] Fixed a bug where a duplicates of a spline with the Spline Instantiate component would have visible child GameObjects in the Hierarchy window.
- [SPLB-65] Fixed an issue where it wasn't possible to add a knot to a closed spline that is empty or contains a single knot.
- [SPLB-104] Fixed an issue where duplicating a spline using the Inspector would not copy knot link data.
- [SPLB-82] Fixed an issue where it would not be possible to undo adding a knot from the Inspector.
- [SPLB-84] Fixed an issue where the obsolete Spline Edit Mode would show in the Debug Inspector.
- [SPLB-135] Fixed an issue where the Auto Smooth tangent mode would not smooth a spline's curves correctly.
- [SPLB-104] Fixed an issue where shared knots would lose selection when a spline is selected using the Inspector.
- [SPLB-115] Fixed an issue where using the Width tool on MacOS (Metal) would render visual artifacts outside of the Scene view.
- [SPLB-98] Fixed an issue where `LoftRoadBehaviour` would create incorrect mesh geometry normals.
- [STO-2791] Fixed an issue where spline data's reorderable list would break if the data was not the field of a `MonoBehaviour`.
- [SPLB-86] Fixed a bug that caused a spline to change its shape when **Reverse Spline Flow** action was used.
- [SPLB-103] Fixed an issue where the spline tool settings' Visuals popup would display toggle checkboxes on the wrong size and be clipped in some editor versions.
- [SPLB-87] Fixed an issue where the cached Spline length could be incorrect after an undo action.
- Fixed an issue where modifying knot values or order in the Inspector could affect unassociated knot data.
- Fixed use of obsolete `Objects.FindObjectsOfType` API in Unity 2023.1 and newer.
- Fixed incorrect icon import settings.
- Fixed a bug that would prevent Spline Instantiate from instantiating when it was loaded from an addressable build in the Editor.
- Fixed a bug where calling `SplineDataHandles.DataPointHandles` without a `splineID` argument would crash.
- Fixed a bug that would cause `SplineData` handles to interfere with scene navigation.
- Fixed SplineData handles not setting `GUI.changed` correctly.
- Fixed an issue where `SplineSelection` errors could put the Scene view into an unrecoverable broken state.
- Added a new property drawer for `int` interpreted as a spline index, `SplineIndexAttribute`.
- Fixed a bug where modifying and undoing changes to `Spline` values in the Inspector resulted in an incorrect `Spline` length calculation.
- Added `SplineData.OnSplineModified` function to attempt to preserve knot indices when knots are inserted or removed. This is called automatically for all embedded `SplineData` collections.
## [2.1.0] - 2022-10-18
### Added
### Changed
### Fixed
- [STO-2745] Fixed an issue where the Speed Tilt tool's arrow handle cap would flicker when the View tool toggled on or off.
- [STO-2744] Fixed a bug where tangents would have the wrong orientation.
## [2.1.0-pre.1] - 2022-10-12
### Added
- New `SplineUtility.EvaluateNurbs` function to evaluate positions on NURBS curves.
- New `SplineUtility.FitSplineToPoints` function to fit a spline to a provided set of curve points.
- Added a new public static class, `InterpolatorUtility`, to improve the discoverability of `IInterpolator` implementations.
- Added the functionality to offset the starting position of a `SplineAnimate` component's animation.
- Added copy, paste, and duplicate support to Spline tools.
- Exposed API to draw spline and curve handles.
- Added settings to generate a 3D mesh around spline handles to better visualize depth.
- Added the functionality to disable specific Spline tool handles.
- Added the functionality to tweak knot position without being in the position tool.
- Added spline index to the Element Inspector when a spline element is selected.
- Updated public API documentation.
- Updated built-in Spline components to support spline containers with multiple splines.
- New `SplineUtility.ReducePoints` function to remove redundant points on a line and still retain the original shape.
- New `Spline.SetAutoSmoothTangentTension` function to specify the tension used when tangents are calculated.
- Added `Spline Filter` to the Spline tools settings.
- Added spline selection from the Spline Inspector.
- Added support to show knot indices in the Scene View.
- New `Paint` example shows how to create a spline from a list of points.
- Updated the `SplineUtility.GetAutoSmoothTangent` function to use centripetal parameterization to calculate the tangents in **Auto** tangent mode.
- New `SplineUtility.GetAutoSmoothTangent` function takes the current knot position and then uses centripetal parameterization to calculate the tangents in **Auto** tangent mode.
- Added new built-in Spline shapes: Rounded Square, Polygon, Helix, and Star.
- Added the functionality to to delete tangents.
- Removed the disc that displayed around selected knots.
- Added a disc that displays when a user has the Tweak tool enabled and hovers over a tangent.
- Added transparency to the disc that displays when a knot or tangent is hovered on if the disc occludes an object in the scene.
- Reduced the transparency of the Tweak tool handles when they occlude an object in the scene.
- New `EditorSplineUtility.SetKnotPlacementTool` function to set the active context to `SplineToolContext` and the active tool to `KnotPlacementTool`.
### Changed
- [STO-2666] Disabled the Visual dropdown button when using `KnotPlacementTool`.
- Modified spline element handles to use `Element Color`.
- [STO-2708] Modified spline deletion to remove empty splines.
- [STO-2682] Unified `Draw Splines Tool` naming across menus and documentation.
- [STO-2681] Attenuated the color of the tangents and the curve highlights when they are behind objects.
- [STO-2700] Modified Spline Instantiate so it no longer serializes instances in the Scene or prefabs.
- [STO-2682] Unified `Draw Splines Tool` naming across menus and docs.
- [STO-2728] Changed the label of the `SplineAnimate` component's `World` alignment mode to `World Space` in the Inspector.
- Modified the `Draw Splines Tool` to clear any Spline element selection when it activates.
- [STO-2490] Made active element selection consistent with standard Editor behavior for GameObjects. Now you can hold Shift and click a knot to set it as the active element.
- Spline element handles now use the `Element Selection` and `Element Preselection` colors.
- Changed tangent's shapes to diamonds.
- Modified the `Knot Placement Tool` to have a live preview for segments with auto-smoothed knots.
- Dependency on Unity Physics Module is now optional.
- Modified `SplinePath` to support the `Closed` property of Splines.
- Removed and merged redundant Sample scenes.
- Reduced the size of the flow indicator handle.
- Changed default colors and thickness for spline elements and curves.
- Improved the line visibility of handles and segments.
- Changed Burst from required dependency to optional.
- Burst compile `SplineJobs.EvaluationPosition` when Burst package is available.
### Fixed
- [STO-2746] Fixed an issue where it was impossible to delete knots on mac.
- [STO-2743] Fixed an issue where a curve's highlight would flicker when hovering near a knot.
- [STO-2729] Fixed an issue where reordering knots would break knot links until moved.
- [STO-2730] Fixed the curve highlight color when using the Tweak tool.
- [STO-2693] Fixed a bug that prevented users from adding and reordering knots in the Inspector when the spline comes from a class that inherits from `ISplineContainer`.
- [STO-2725] Fixed a bug where knots, discs, and the normal line of knots would use incorrect colors.
- [STO-2726] Fixed a bug where knots handles were drawn under curves handles.
- [STO-2727] Corrected a typo in the Loop Mode of the `SplineAnimate`.
- [STO-2653] Fixed a bug where the Tweak tool's guide plane would flicker when drawn directly on a surface.
- [STO-2731] Fixed View Tool not working when Spline Context was active.
- [STO-2700] Fixed spline instantiate having a delay before regenerating the instances.
- [STO-2679] Fixed the segmentation of the curve highlight.
- [STO-2665] Fixed sample scenes not rendering correctly when URP or HDRP was used.
- [STO-2702] Removed the **Dist** label in the Inspector when the `SplineInstantiate` component is set to `Exact`.
- [STO-2656] Fixed a bug where hovering on linked knots would display discs on each linked knot.
- [STO-2686] Fixed an issue where inserting a knot on the closing curve would result in other knots moving around.
- [STO-2685] Fixed a bug where `LoftRoadBehavior `would throw exceptions with knots that had linear tangents.
- [STO-2657] Fixed a bug where spline gizmos would appear at unexpected moments.
- [STO-2655] Fixed a bug that caused knots to highlight with the wrong color.
- [STO-2687] Fixed an error that would occur when deleting the last knot of a spline.
- [STO-2684] Fixed a bug that prevented Select All from selecting elements.
- [STO-2680] Fixed a bug where `SplineMesh.Extrude` would create twisted mesh geometry.
- [STO-2701] Fixed a bug where `LoftRoadBehavior` would either throw an exception or extrude incorrectly when the spline was in Linear tangent mode or if it was shorter than 1 unit length.
- [STO-2705] Fixed a bug where `SplineInstantiate` was not instantiating correctly when the instantiation method was set to `Method.InstanceCount`.
- [STO-2668] Fixed a bug where Spline element inspector values would not update when a knot is modified.
- [STO-2706] Fixed a bug where selecting a knot from the inspector was desynchronizing the tool selection.
- [STO-2696] Fixed a bug where clearing knot selection was not updating in the inspector.
- [STO-2689] Fixed the behavior of the spline inspector selection when clicking on a selected element.
- [STO-2688] Fixed a delay in the scene view update after changing selection from the spline inspector.
- [STO-2669] Fixed a bug where modifying a spline in the Inspector would not invoke the `Changed` callback.
- [STO-2695] Fixed a bug that would throw an exception when the `SplineAnimate` component was destroyed.
- [STO-2658] Fixed a bug that would delay the color change when you hover over a segment.
- [STO-2692] Fixed an issue where deleting a spline would result in errors.
- [STO-2716] Added missing tooltips to the Element Inspector.
- [STO-2636] Fixed an issue that prevented spline framing if the active tool context was not set to Spline.
- [STO-2714] Fixed transformation and direct manipulation tools not working correctly when spline has non-uniform scale.
- [STO-2698] Fixed a bug that could cause a knot link to desync if a linked knot was modified in the Inspector.
- [SPLB-54] Fixed a bug where flow arrows and curve highlights would not be centered on a spline's segments between knots.
- [SPLB-44] Fixed a bug where tangent selection would remain after changing to a tangent mode without modifiable tangents.
- [STO-2632] Fixed Spline Selection Undo when selecting a single element.
- Fixed a bug where the spline inspector was not working if the `Spline` object was not stored in a ISplineContainer.
- [SPLB-40] Fixed a bug where a tangent's Magnitude field in the `Element Inspector` created NaN values.
- [SPLB-39] Fixed a bug where knots from separate splines would link to the wrong knot.
- [SPLB-38] Fixed incorrect auto-smooth knots on reversed splines.
- Fixed a bug where the SplineContainer reorderable list broke the LinkKnots collection.
- Fixed a bug that caused the Inspector to display incorrect spline indexes.
- Fixed spline selection intercepting scene view navigation shortcuts.
- Fixed a bug where setting the Spline Instantiate component's instantiation items with the Inspector would have no effect.
- Fixed a potential exception that occurred when opening scenes with splines created in the 1.0 version of this package.
- Fixed tangent and knot handles incorrectly highlighting while a tool is engaged.
- Fixed a bug where deleting some element selections would result in an exception being thrown.
- Fixed a bug where undoing after deleting a selection would not re-select the restored elements.
- [STO-2690] Fixed a bug that prevented data points from being added to a spline when the spline was clicked on.
- [STO-2691] Fixed a bug where moving a data point along a spline behaved incorrectly.
- Fixed compile errors in sample scenes when building player.
- Added an ellipsis to the Draw Spline Tool menu item label.
- Fixed `Spline Tool Context` not working with `ISplineContainer` implementations that define a valid `KnotCollection`.
## [2.0.0-pre.2] - 2022-05-11
### Added
- Added the ability to edit multiple spline elements in the element inspector.
- Added functionality to join knots from different splines.
- Added functionality to reverse the flow of a spline.
- Added `SplinePath`, `SplineSlice`, and `SplineRange` types. These types enable interpolation and evaluation of partial or complete sections of discrete splines.
### Changed
- Modified rounding to be based on camera distance.
- [STO-2704] Changed `SplineUtility.GetBounds` to account for tangent positions when calculating bounds.
- Updated the design of the tangent mode fields in the Element Inspector.
- Added a dropdown menu to select tangent modes to the Element Inspector.
- Updated the `Draw Splines Tool` to display only one tangent when a new knot is created.
- Simplified tangents in the `Draw Splines Tool` by removing the interactable handle .
- Renamed `Knot Placement Tool` to `Draw Splines Tool`.
- Modified the `Draw Splines Tool` to account for multiple splines.
### Fixed
- Fixed SplineInspector knot removal not keeping metadata consistent (KnotLinks).
- Fixed an issue that caused auto-smoothed tangents to show in the `Draw Splines Tool` and be selectable by rect selection.
- Added `ReadOnly` to knot's and length's `NativeArray` to fix IndexOutOfRangeException on `NativeSpline`.
- Fixed tangents when closing the spline to keep user-defined values.
- Fixed index error in the `Spline.SendSizeChangeEvent` method.
- Fixed a case where inserting a knot would not update adjacent knots with "auto-smooth" tangent mode.
## [2.0.0-pre.1] - 2022-04-19
### Added
- Added structs and utility methods that use the [Job System](https://docs.unity3d.com/Manual/JobSystem.html) to evaluate splines.
### Changed
- Separated tangent and bezier modes in the Element Inspector.
- Added a feature to split splines at a knot from the Element Inspector.
- Added tool settings to change the default knot type.
- Added ON icons for tangent modes.
- Moved Spline creation menu items to `GameObject/Spline`.
- Modified the Spline Inspector to be reactive to spline element selections in the Scene View.
- New icons set for Spline-related items.
- Hiding knot handles if the EditorTool is not a SplineTool
- Tweaked the spline property drawer to make it a bit more clean.
- Changed the knot rotation property in the inspector to a Vector3Field instead of a QuaternionField.
- Added a new editor API to change the tangent mode of knots.
- Deprecated `Spline.EditType`. Tangent modes are now stored per knot. Use `Spline.GetTangentMode` and `Spline.SetTangentMode` to get and set tangent modes.
- Added ability to link and unlink knots using Element Inspector.
### Fixed
- [1411976] Fixed undo crash in SplineInstantiate component.
- Fixed scale offset in SplineInstantiate component.
- [1410919] Fixed SplineData Inspector PathIndexUnit when updating unit.
- Fixed issues with spline editor tools changes sometimes being overwritten
- Fixed `SplineUtility.Evaluate` incorrectly evaluating the up vector.
- [1403359] Fixed issue where `SplineExtrude` component would not update mesh after an undo operation.
- [1403386] Fixing SplineData Inspector triggering to SplineData.changed events.
- Fixing console InvalidOperationException when creating a Spline with a locked Inspector.
- [1384457] Fix for an exception being sometimes thrown when drawing a spline and rotating the scene view.
- [1384448] Fixed incorrect Rect Selection when using Shift or CTRL/CMD modifiers.
- [1413605] Fixed Linear to Bezier Edit Type conversion incorrectly leaving tangents set to zero length.
- [1413603] Spline creation menu items now respect the preference to place new objects at world origin.
- `SplineFactory.CreateSquare` now respects the `radius` argument.
## [1.0.0] - 2022-02-25
### Changed
- New icons set for Spline-related items.
- `SplineContainer` inspector is now more user-friendly.
- Fixed issue where Spline Inspector fields would not accept negative values.
- Fixed issue where the X shortcut would only cycle through World/Local handle orientations and ignore Parent/Element.
- Fixed samples compatibility issues on 2021.2.
- Spline Inspector no longer shows 2 editable tangent fields for Knots that only have one tangent.
- Fixed poor performance when manipulating long continuous tangents.
- `SplineUtility.ConvertIndexUnit` now wraps when returning normalized interpolations.
- Fixed issue where Knot rotation would not properly align to the surface the Knot is placed on.
- Fixed Spline length serialization issue that would result in incorrect Spline evaluations and length calculations.
- Updated Knot and Tangent handle design.
## [1.0.0-pre.9] - 2022-01-26
### Changed
- Adding new API to interact with SplineData Handles
- Adding a `SplineInstantiate` component and updating associated samples.
- Added a `SplineAnimate` component and sample scene.
### Fixed
- [1395734] Fixing SplineUtility errors with Spline made of 1 knot.
- Fixing Tangent Out when switching from Broken Tangents to Continuous Tangents Mode.
- Fixing Preview Curve for Linear and Catcall Rom when Closing Spline.
## [1.0.0-pre.8] - 2021-12-21
### Changed
- Added a `SplineExtrude` component and an accompanying ExtrudeSpline sample scene.
- When using a spline transform tool, CTRL/CMD + A now selects all spline elements.
- Improving Spline Inspector overlay.
- `SplineUtility.CalculateLength` now accepts `T : ISpline` instead of `Spline`.
### Fixed
- [1384451] Fixing knot handles size being too large.
- [1386704] Fixing SplineData Inspector not being displayed.
- Fixing wrong Spline length when editing spline using the inspector.
- [1384455] Fix single element selections breaking the undo stack.
- [1384448] Fix for CTRL/CMD + Drag not performing a multi selection.
- [1384457] Fix for an exception being sometimes thrown when drawing a spline and rotating the scene view.
- [1384520] Fixing stack overflow when entering playmode.
- Fixing SplineData conversion being wrong with KnotIndex.
## [1.0.0-pre.7] - 2021-11-17
### Changed
- Disable unstable GC alloc tests.
## [1.0.0-pre.6] - 2021-11-15
### Changed
- Replace references to 'time' with 'interpolation ratio' or 't'.
- Move distance to interpolation caching and lookup methods to `CurveUtility`, and document their use.
- Fix compile errors when opened in Unity 2021.2.
- Removed `Spline.ToNativeSpline`, use `new NativeSpline(ISpline)` instead.
- Removed `Spline.ToNativeArray`.
### Fixed
- Fixed issue where hidden start/end knot tangents would be selectable.
- Fixed active tangentOut incorrectly mirroring against tangentIn when changing tangent mode via shortcut.
- Fixed Knot Placement tool preview curve disappearing when cursor hovers over first knot.
- Fixed issue where knot would not align to tangents when switching from broken to mirrored or continuous modes.
- Fixed issue where hovering first knot while placing tangents would hide the last placed knot, its tangents and the preview curve.
## [1.0.0-pre.5] - 2021-11-02
- Initial release

7
Packages/com.unity.splines/CHANGELOG.md.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 06ba237cdfbe2264f89158773bd6268b
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.splines/Editor.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7be002d6127770a45b33b37bd3ba98ac
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

5
Packages/com.unity.splines/Editor/AssemblyInfo.cs

@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.Splines.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Splines.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Splines.Editor.Debug")]

11
Packages/com.unity.splines/Editor/AssemblyInfo.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c854ca5f1d7eed04aa92565d35431e96
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.splines/Editor/Components.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e88477d7eedfb4ca8b5d8912575efa76
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

355
Packages/com.unity.splines/Editor/Components/SplineAnimateEditor.cs

@ -0,0 +1,355 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Splines;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
[CustomEditor(typeof(SplineAnimate))]
[CanEditMultipleObjects]
class SplineAnimateEditor : UnityEditor.Editor
{
List<VisualElement> m_Roots = new ();
List<Slider> m_ProgressSliders = new ();
List<FloatField> m_ElapsedTimeFields = new ();
List<EnumField> m_ObjectForwardFields = new ();
List<EnumField> m_ObjectUpFields = new ();
SerializedProperty m_MethodProperty;
SerializedProperty m_ObjectForwardProperty;
SerializedProperty m_ObjectUpProperty;
SerializedProperty m_StartOffsetProperty;
SerializedObject m_TransformSO;
SplineAnimate m_SplineAnimate;
const string k_UxmlPath = "Packages/com.unity.splines/Editor/Editor Resources/UI/UXML/splineanimate-inspector.uxml";
static VisualTreeAsset s_TreeAsset;
static StyleSheet s_ThemeStyleSheet;
SplineAnimate[] m_Components;
void OnEnable()
{
m_SplineAnimate = target as SplineAnimate;
if (m_SplineAnimate == null)
return;
m_SplineAnimate.Updated += OnSplineAnimateUpdated;
try {
m_MethodProperty = serializedObject.FindProperty("m_Method");
m_ObjectForwardProperty = serializedObject.FindProperty("m_ObjectForwardAxis");
m_ObjectUpProperty = serializedObject.FindProperty("m_ObjectUpAxis");
m_StartOffsetProperty = serializedObject.FindProperty("m_StartOffset");
}
catch (Exception)
{
return;
}
m_TransformSO = new SerializedObject(m_SplineAnimate.transform);
m_Components = targets.Select(x => x as SplineAnimate).Where(y => y != null).ToArray();
foreach (var animate in m_Components)
{
if (animate.Container != null)
animate.RecalculateAnimationParameters();
}
m_Roots.Clear();
m_ObjectForwardFields.Clear();
m_ObjectUpFields.Clear();
m_ProgressSliders.Clear();
m_ElapsedTimeFields.Clear();
EditorApplication.update += OnEditorUpdate;
Spline.Changed += OnSplineChange;
SplineContainer.SplineAdded += OnContainerSplineSetModified;
SplineContainer.SplineRemoved += OnContainerSplineSetModified;
}
void OnDisable()
{
if(m_SplineAnimate != null)
m_SplineAnimate.Updated -= OnSplineAnimateUpdated;
if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
{
if (m_Components != null)
{
foreach (var animate in m_Components)
{
if (animate != null && animate.Container != null)
{
animate.RecalculateAnimationParameters();
animate.Restart(false);
}
}
}
}
EditorApplication.update -= OnEditorUpdate;
Spline.Changed -= OnSplineChange;
SplineContainer.SplineAdded -= OnContainerSplineSetModified;
SplineContainer.SplineRemoved -= OnContainerSplineSetModified;
}
void OnEditorUpdate()
{
if (!EditorApplication.isPlaying)
{
if (m_SplineAnimate.Container != null && m_SplineAnimate.IsPlaying)
{
m_SplineAnimate.Update();
RefreshProgressFields();
}
}
else if(m_SplineAnimate.IsPlaying)
RefreshProgressFields();
}
void OnSplineChange(Spline spline, int knotIndex, SplineModification modificationType)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var animate in m_Components)
{
if (animate.Container != null && animate.Container.Splines.Contains(spline))
animate.RecalculateAnimationParameters();
}
}
void OnContainerSplineSetModified(SplineContainer container, int spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var animate in m_Components)
{
if (animate.Container == container)
animate.RecalculateAnimationParameters();
}
}
public override VisualElement CreateInspectorGUI()
{
var root = new VisualElement();
if (s_TreeAsset == null)
s_TreeAsset = (VisualTreeAsset)AssetDatabase.LoadAssetAtPath(k_UxmlPath, typeof(VisualTreeAsset));
s_TreeAsset.CloneTree(root);
if (s_ThemeStyleSheet == null)
s_ThemeStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>($"Packages/com.unity.splines/Editor/Stylesheets/SplineAnimateInspector{(EditorGUIUtility.isProSkin ? "Dark" : "Light")}.uss");
root.styleSheets.Add(s_ThemeStyleSheet);
var methodField = root.Q<PropertyField>("method");
methodField.RegisterValueChangeCallback((_) => { RefreshMethodParamFields((SplineAnimate.Method)m_MethodProperty.enumValueIndex); });
RefreshMethodParamFields((SplineAnimate.Method)m_MethodProperty.enumValueIndex);
var objectForwardField = root.Q<EnumField>("object-forward");
objectForwardField.RegisterValueChangedCallback((evt) => OnObjectAxisFieldChange(evt, m_ObjectForwardProperty, m_ObjectUpProperty));
var objectUpField = root.Q<EnumField>("object-up");
objectUpField.RegisterValueChangedCallback((evt) => OnObjectAxisFieldChange(evt, m_ObjectUpProperty, m_ObjectForwardProperty));
var playButton = root.Q<Button>("play");
playButton.SetEnabled(!EditorApplication.isPlaying);
playButton.clicked += OnPlayClicked;
var pauseButton = root.Q<Button>("pause");
pauseButton.SetEnabled(!EditorApplication.isPlaying);
pauseButton.clicked += OnPauseClicked;
var resetButton = root.Q<Button>("reset");
resetButton.SetEnabled(!EditorApplication.isPlaying);
resetButton.clicked += OnResetClicked;
var progressSlider = root.Q<Slider>("normalized-progress");
progressSlider.SetEnabled(!EditorApplication.isPlaying);
progressSlider.RegisterValueChangedCallback((evt) => OnProgressSliderChange(evt.newValue));
var elapsedTimeField = root.Q<FloatField>("elapsed-time");
elapsedTimeField.SetEnabled(!EditorApplication.isPlaying);
elapsedTimeField.RegisterValueChangedCallback((evt) => OnElapsedTimeFieldChange(evt.newValue));
var startOffsetField = root.Q<PropertyField>("start-offset");
startOffsetField.RegisterValueChangeCallback((evt) =>
{
m_SplineAnimate.StartOffset = m_StartOffsetProperty.floatValue;
if (!EditorApplication.isPlayingOrWillChangePlaymode)
{
m_SplineAnimate.Restart(false);
OnElapsedTimeFieldChange(elapsedTimeField.value);
}
});
m_Roots.Add(root);
m_ProgressSliders.Add(progressSlider);
m_ElapsedTimeFields.Add(elapsedTimeField);
m_ObjectForwardFields.Add(objectForwardField);
m_ObjectUpFields.Add(objectUpField);
return root;
}
void RefreshMethodParamFields(SplineAnimate.Method method)
{
foreach (var root in m_Roots)
{
var durationField = root.Q<PropertyField>("duration");
var maxSpeedField = root.Q<PropertyField>("max-speed");
if (method == (int)SplineAnimate.Method.Time)
{
durationField.style.display = DisplayStyle.Flex;
maxSpeedField.style.display = DisplayStyle.None;
}
else
{
durationField.style.display = DisplayStyle.None;
maxSpeedField.style.display = DisplayStyle.Flex;
}
}
}
void RefreshProgressFields()
{
for (int i = 0; i < m_ProgressSliders.Count && i < m_ElapsedTimeFields.Count; ++i)
{
var progressSlider = m_ProgressSliders[i];
var elapsedTimeField = m_ElapsedTimeFields[i];
if (progressSlider == null || elapsedTimeField == null)
continue;
progressSlider.SetValueWithoutNotify(m_SplineAnimate.GetLoopInterpolation(false));
elapsedTimeField.SetValueWithoutNotify(m_SplineAnimate.ElapsedTime);
}
}
void OnProgressSliderChange(float progress)
{
m_SplineAnimate.Pause();
m_SplineAnimate.NormalizedTime = progress;
RefreshProgressFields();
}
void OnElapsedTimeFieldChange(float elapsedTime)
{
m_SplineAnimate.Pause();
m_SplineAnimate.ElapsedTime = elapsedTime;
RefreshProgressFields();
}
void OnObjectAxisFieldChange(ChangeEvent<Enum> changeEvent, SerializedProperty axisProp, SerializedProperty otherAxisProp)
{
if (changeEvent.newValue == null)
return;
var newValue = (SplineAnimate.AlignAxis)changeEvent.newValue;
var previousValue = (SplineAnimate.AlignAxis)changeEvent.previousValue;
// Swap axes if the picked value matches that of the other axis field
if (newValue == (SplineAnimate.AlignAxis)otherAxisProp.enumValueIndex)
{
otherAxisProp.enumValueIndex = (int)previousValue;
serializedObject.ApplyModifiedProperties();
}
// Prevent the user from configuring object's forward and up as opposite axes
if (((int) newValue) % 3 == otherAxisProp.enumValueIndex % 3)
{
axisProp.enumValueIndex = (int)previousValue;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
foreach (var objectForwardField in m_ObjectForwardFields)
objectForwardField.SetValueWithoutNotify((SplineComponent.AlignAxis)m_ObjectForwardProperty.enumValueIndex);
foreach (var objectUpField in m_ObjectUpFields)
objectUpField.SetValueWithoutNotify((SplineComponent.AlignAxis)m_ObjectUpProperty.enumValueIndex);
}
void OnPlayClicked()
{
if (!m_SplineAnimate.IsPlaying)
{
m_SplineAnimate.RecalculateAnimationParameters();
if (m_SplineAnimate.NormalizedTime == 1f)
m_SplineAnimate.Restart(true);
else
m_SplineAnimate.Play();
}
}
void OnPauseClicked()
{
m_SplineAnimate.Pause();
}
void OnResetClicked()
{
m_SplineAnimate.RecalculateAnimationParameters();
m_SplineAnimate.Restart(false);
RefreshProgressFields();
}
void OnSplineAnimateUpdated(Vector3 position, Quaternion rotation)
{
if (m_SplineAnimate == null)
return;
if (!EditorApplication.isPlaying)
{
m_TransformSO.Update();
var localPosition = position;
var localRotation = rotation;
if (m_SplineAnimate.transform.parent != null)
{
localPosition = m_SplineAnimate.transform.parent.worldToLocalMatrix.MultiplyPoint3x4(position);
localRotation = Quaternion.Inverse(m_SplineAnimate.transform.parent.rotation) * localRotation;
}
m_TransformSO.FindProperty("m_LocalPosition").vector3Value = localPosition;
m_TransformSO.FindProperty("m_LocalRotation").quaternionValue = localRotation;
m_TransformSO.ApplyModifiedProperties();
}
}
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void DrawSplineAnimateGizmos(SplineAnimate splineAnimate, GizmoType gizmoType)
{
if ((!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode) || splineAnimate.Container == null)
return;
const float k_OffsetGizmoSize = 0.15f;
splineAnimate.Container.Evaluate(splineAnimate.StartOffsetT, out var offsetPos, out var forward, out var up);
#if UNITY_2022_2_OR_NEWER
using (new Handles.DrawingScope(Handles.elementColor))
#else
using (new Handles.DrawingScope(SplineHandleUtility.knotColor))
#endif
if (Vector3.Magnitude(forward) <= Mathf.Epsilon)
{
if (splineAnimate.StartOffsetT < 1f)
forward = splineAnimate.Container.EvaluateTangent(Mathf.Min(1f, splineAnimate.StartOffsetT + 0.01f));
else
forward = splineAnimate.Container.EvaluateTangent(splineAnimate.StartOffsetT - 0.01f);
}
Handles.ConeHandleCap(-1, offsetPos, Quaternion.LookRotation(Vector3.Normalize(forward), up), k_OffsetGizmoSize * HandleUtility.GetHandleSize(offsetPos), EventType.Repaint);
}
}
}

11
Packages/com.unity.splines/Editor/Components/SplineAnimateEditor.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7292ebc78c5d44eaa80048f8fd7d5fe6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

42
Packages/com.unity.splines/Editor/Components/SplineComponentEditor.cs

@ -0,0 +1,42 @@
using System;
using UnityEngine;
using UnityEditor;
class SplineComponentEditor : Editor
{
static GUIStyle s_FoldoutStyle;
internal static readonly string k_Helpbox = L10n.Tr("Instantiated Objects need a SplineContainer target to be created.");
protected bool Foldout(bool foldout, GUIContent content)
{
return Foldout(foldout, content, false);
}
public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick)
{
if (s_FoldoutStyle == null)
{
s_FoldoutStyle = new GUIStyle(EditorStyles.foldout);
s_FoldoutStyle.fontStyle = FontStyle.Bold;
}
return EditorGUILayout.Foldout(foldout, content, toggleOnLabelClick, s_FoldoutStyle);
}
internal struct LabelWidthScope : IDisposable
{
float previousWidth;
public LabelWidthScope(float width)
{
previousWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = width;
}
public void Dispose()
{
EditorGUIUtility.labelWidth = previousWidth;
}
}
}

11
Packages/com.unity.splines/Editor/Components/SplineComponentEditor.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9103eb7dcd041455886438d75625d19c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

104
Packages/com.unity.splines/Editor/Components/SplineContainerEditor.cs

@ -0,0 +1,104 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
// Multi-object selection is not supported
[CustomEditor(typeof(SplineContainer))]
class SplineContainerEditor : UnityEditor.Editor
{
SerializedProperty m_SplineProperty;
SerializedProperty splinesProperty => m_SplineProperty ??= serializedObject.FindProperty("m_Splines");
static GUIStyle s_HelpLabelStyle;
static GUIStyle HelpLabelStyle
{
get
{
if (s_HelpLabelStyle == null)
{
s_HelpLabelStyle = new GUIStyle(EditorStyles.helpBox);
s_HelpLabelStyle.padding = new RectOffset(2, 2, 2, 2);
}
return s_HelpLabelStyle;
}
}
static GUIContent m_HelpLabelContent;
const string k_HelpBoxIconPath = "SplineEditMode-Info";
static GUIContent m_HelpLabelContentIcon;
const string k_ComponentMessage = "Use the Spline Edit Mode in the Scene Tools Overlay to edit this Spline.";
public void OnEnable()
{
m_HelpLabelContent = EditorGUIUtility.TrTextContent(k_ComponentMessage);
m_HelpLabelContentIcon = new GUIContent(PathIcons.GetIcon(k_HelpBoxIconPath));
Undo.undoRedoPerformed += UndoRedoPerformed;
}
public void OnDisable()
{
Undo.undoRedoPerformed -= UndoRedoPerformed;
}
void UndoRedoPerformed()
{
foreach (var t in targets)
{
var container = t as SplineContainer;
if (container != null)
{
container.ClearCaches();
foreach (var spline in container.Splines)
spline.SetDirty(SplineModification.Default);
}
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// [SPLB-132] Reverting to custom helpbox as the default helpbox style as a trouble to handle custom icons
// when using a screen with PixelPerPoints different than 1. This is done in trunk by setting the
// Texture2d.pixelsPerPoints which is an internal property than cannot be access from here.
EditorGUILayout.BeginHorizontal(HelpLabelStyle);
EditorGUIUtility.SetIconSize(new Vector2(32f, 32f));
EditorGUILayout.LabelField(m_HelpLabelContentIcon,
GUILayout.Width(34), GUILayout.MinHeight(34), GUILayout.ExpandHeight(true));
EditorGUIUtility.SetIconSize(Vector2.zero);
EditorGUILayout.LabelField(m_HelpLabelContent,
new GUIStyle(EditorStyles.label){wordWrap = HelpLabelStyle.wordWrap, fontSize = HelpLabelStyle.fontSize, padding = new RectOffset(-2, 0, 0, 0)},
GUILayout.ExpandHeight(true));
EditorGUILayout.EndHorizontal();
SplineReorderableList.Get(splinesProperty).DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
bool HasFrameBounds()
{
foreach (var o in targets)
{
var target = (SplineContainer) o;
foreach (var spline in target.Splines)
if (spline.Count > 0)
return true;
}
return false;
}
Bounds OnGetFrameBounds()
{
List<SplineInfo> splines = new List<SplineInfo>();
EditorSplineUtility.GetSplinesFromTargets(targets, splines);
return EditorSplineUtility.GetBounds(splines);
}
}
}

11
Packages/com.unity.splines/Editor/Components/SplineContainerEditor.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b17d23c06da64c139386c53a0b59281
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

305
Packages/com.unity.splines/Editor/Components/SplineExtrudeEditor.cs

@ -0,0 +1,305 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Splines;
using UnityEngine.Splines.ExtrusionShapes;
namespace UnityEditor.Splines
{
[CustomEditor(typeof(SplineExtrude))]
[CanEditMultipleObjects]
class SplineExtrudeEditor : SplineComponentEditor
{
SerializedProperty m_Container;
SerializedProperty m_RebuildOnSplineChange;
SerializedProperty m_RebuildFrequency;
SerializedProperty m_SegmentsPerUnit;
SerializedProperty m_Capped;
SerializedProperty m_Radius;
SerializedProperty m_Range;
SerializedProperty m_Shape;
SerializedProperty m_UpdateColliders;
SerializedProperty m_FlipNormals;
SerializedProperty m_TargetMesh;
static readonly GUIContent k_RangeContent = new GUIContent(L10n.Tr("Range"), L10n.Tr("The section of the Spline to extrude."));
static readonly GUIContent k_AdvancedContent = new GUIContent(L10n.Tr("Advanced"), L10n.Tr("Advanced Spline Extrude settings."));
static readonly GUIContent k_PercentageContent = new GUIContent(L10n.Tr("Percentage"), L10n.Tr("The section of the Spline to extrude in percentages."));
static readonly GUIContent k_ShapeContent = new GUIContent(L10n.Tr("Shape Extrude"), L10n.Tr("Shape Extrude settings."));
static readonly GUIContent k_ShapeSettings = EditorGUIUtility.TrTextContent("Settings");
static readonly GUIContent k_GeometryContent = new GUIContent(L10n.Tr("Geometry"), L10n.Tr("Mesh Geometry settings."));
static readonly GUIContent k_MeshTargetContent = new GUIContent(L10n.Tr("Target Mesh Asset"));
static readonly GUIContent k_CreateMeshTargetContent = new GUIContent(L10n.Tr("Create Mesh Asset"));
static readonly string k_SourceSplineContainer = L10n.Tr("Source Spline Container");
static readonly string k_CapEnds = L10n.Tr("Cap Ends");
static readonly string k_AutoRefreshGeneration = L10n.Tr("Auto Refresh Generation");
static readonly string k_To = L10n.Tr("to");
static readonly string k_From = L10n.Tr("from");
SplineExtrude[] m_Components;
protected void OnEnable()
{
m_Container = serializedObject.FindProperty("m_Container");
m_RebuildOnSplineChange = serializedObject.FindProperty("m_RebuildOnSplineChange");
m_RebuildFrequency = serializedObject.FindProperty("m_RebuildFrequency");
m_SegmentsPerUnit = serializedObject.FindProperty("m_SegmentsPerUnit");
m_Capped = serializedObject.FindProperty("m_Capped");
m_Radius = serializedObject.FindProperty("m_Radius");
m_Range = serializedObject.FindProperty("m_Range");
m_UpdateColliders = serializedObject.FindProperty("m_UpdateColliders");
m_Shape = serializedObject.FindProperty("m_Shape");
m_TargetMesh = serializedObject.FindProperty("m_TargetMesh");
m_FlipNormals = serializedObject.FindProperty("m_FlipNormals");
m_Components = targets.Select(x => x as SplineExtrude).Where(y => y != null).ToArray();
EditorSplineUtility.AfterSplineWasModified += OnSplineModified;
SplineContainer.SplineAdded += OnContainerSplineSetModified;
SplineContainer.SplineRemoved += OnContainerSplineSetModified;
}
void OnDisable()
{
EditorSplineUtility.AfterSplineWasModified -= OnSplineModified;
SplineContainer.SplineAdded -= OnContainerSplineSetModified;
SplineContainer.SplineRemoved -= OnContainerSplineSetModified;
}
void OnSplineModified(Spline spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var extrude in m_Components)
{
if (extrude.Container != null && extrude.Splines.Contains(spline))
extrude.Rebuild();
}
}
void OnContainerSplineSetModified(SplineContainer container, int spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var extrude in m_Components)
{
if (extrude.Container == container)
extrude.Rebuild();
}
}
void SetShapeType(ShapeType type)
{
foreach (var extrude in m_Components)
{
if (ShapeTypeUtility.GetShapeType(extrude.Shape) == type)
continue;
Undo.RecordObject(extrude, "Set Extrude Shape");
extrude.Shape = ShapeTypeUtility.CreateShape(type);
m_Shape.isExpanded = true;
}
}
bool CanCapEnds()
{
foreach (var extrude in m_Components)
{
if (!extrude.CanCapEnds)
return false;
}
return true;
}
void SetRebuildOnSplineChange(bool value)
{
foreach (var extrude in m_Components)
{
Undo.RecordObject(extrude, "Set Rebuild on Spline Change.");
extrude.RebuildOnSplineChange = value;
}
}
bool HasEmptyTargetMeshAssets(SplineExtrude[] components)
{
foreach (var extrude in components)
if (extrude.targetMesh == null)
return true;
return false;
}
void Rebuild()
{
foreach (var extrude in m_Components)
extrude.Rebuild();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Container, new GUIContent(k_SourceSplineContainer, m_Container.tooltip));
if (m_Container.objectReferenceValue == null)
EditorGUILayout.HelpBox(k_Helpbox, MessageType.Warning);
// shape section
m_Shape.isExpanded = Foldout(m_Shape.isExpanded, k_ShapeContent, true);
if (m_Shape.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUI.showMixedValue = m_Shape.hasMultipleDifferentValues;
EditorGUI.BeginChangeCheck();
var shapeType = ShapeTypeUtility.GetShapeType(m_Shape.managedReferenceValue);
shapeType = (ShapeType)EditorGUILayout.EnumPopup(L10n.Tr("Type"), shapeType);
if (EditorGUI.EndChangeCheck())
SetShapeType(shapeType);
EditorGUI.showMixedValue = false;
if (m_Shape.hasVisibleChildren)
EditorGUILayout.PropertyField(m_Shape, k_ShapeSettings, true);
EditorGUI.indentLevel--;
}
// https://unityeditordesignsystem.unity.com/patterns/content-organization recommends 8px spacing for
// vertical groups. padding already adds 4 so just nudge that up for a total of 8
EditorGUILayout.Space(4);
// geometry section
m_Radius.isExpanded = Foldout(m_Radius.isExpanded, k_GeometryContent, true);
if (m_Radius.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_RebuildOnSplineChange, new GUIContent(k_AutoRefreshGeneration, m_RebuildOnSplineChange.tooltip));
if (m_RebuildOnSplineChange.boolValue)
{
EditorGUI.BeginDisabledGroup(!m_RebuildOnSplineChange.boolValue);
using (new LabelWidthScope(80f))
EditorGUILayout.PropertyField(m_RebuildFrequency, new GUIContent() { text = L10n.Tr("Frequency") });
EditorGUI.EndDisabledGroup();
}
else
{
if (GUILayout.Button(new GUIContent(L10n.Tr("Regenerate"))))
Rebuild();
}
if (EditorGUI.EndChangeCheck() && !m_RebuildOnSplineChange.boolValue)
{
// This is needed to set m_RebuildRequested to the appropriate value.
SetRebuildOnSplineChange(m_RebuildOnSplineChange.boolValue);
}
EditorGUILayout.EndHorizontal();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Radius);
if (EditorGUI.EndChangeCheck())
m_Radius.floatValue = Mathf.Clamp(m_Radius.floatValue, .00001f, 1000f);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_SegmentsPerUnit);
if (EditorGUI.EndChangeCheck())
m_SegmentsPerUnit.floatValue = Mathf.Clamp(m_SegmentsPerUnit.floatValue, .00001f, 4096f);
var canCapEnds = CanCapEnds();
using (new EditorGUI.DisabledScope(!canCapEnds))
{
EditorGUILayout.PropertyField(m_Capped, new GUIContent(k_CapEnds, m_Capped.tooltip));
if (m_Capped.boolValue && !canCapEnds)
m_Capped.boolValue = false;
}
EditorGUILayout.PropertyField(m_FlipNormals);
// Range
EditorGUI.showMixedValue = m_Range.hasMultipleDifferentValues;
var range = m_Range.vector2Value;
EditorGUI.BeginChangeCheck();
EditorGUILayout.MinMaxSlider(k_RangeContent, ref range.x, ref range.y, 0f, 1f);
if (EditorGUI.EndChangeCheck())
m_Range.vector2Value = range;
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(k_PercentageContent);
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
EditorGUI.BeginChangeCheck();
var newRange = new Vector2(range.x, range.y);
using (new LabelWidthScope(30f))
newRange.x = EditorGUILayout.FloatField(k_From, range.x * 100f) / 100f;
using (new LabelWidthScope(15f))
newRange.y = EditorGUILayout.FloatField(k_To, range.y * 100f) / 100f;
if (EditorGUI.EndChangeCheck())
{
newRange.x = Mathf.Min(Mathf.Clamp(newRange.x, 0f, 1f), range.y);
newRange.y = Mathf.Max(newRange.x, Mathf.Clamp(newRange.y, 0f, 1f));
m_Range.vector2Value = newRange;
}
EditorGUILayout.EndHorizontal();
EditorGUI.showMixedValue = false;
}
// advanced section
EditorGUILayout.Space(4);
m_UpdateColliders.isExpanded = Foldout(m_UpdateColliders.isExpanded, k_AdvancedContent, true);
if (m_UpdateColliders.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_UpdateColliders);
EditorGUILayout.PropertyField(m_TargetMesh, k_MeshTargetContent);
EditorGUILayout.BeginHorizontal();
GUILayout.Space(18);
if (HasEmptyTargetMeshAssets(m_Components) && GUILayout.Button(k_CreateMeshTargetContent))
{
Undo.RecordObjects(m_Components, $"Modified {m_TargetMesh.displayName} in GameObject");
foreach (var extrude in m_Components)
{
if (extrude.targetMesh == null)
extrude.targetMesh = extrude.CreateMeshAsset();
}
}
EditorGUILayout.EndHorizontal();
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
foreach (var extrude in m_Components)
extrude.Rebuild();
}
}
}
}

3
Packages/com.unity.splines/Editor/Components/SplineExtrudeEditor.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8ae5aa24d97048439e76e9392765c364
timeCreated: 1637689699

665
Packages/com.unity.splines/Editor/Components/SplineInstantiateEditor.cs

@ -0,0 +1,665 @@
using System.Linq;
using UnityEditor;
using UnityEditor.Splines;
using UnityEngine;
using UnityEngine.Splines;
class SplineInstantiateGizmoDrawer
{
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void DrawSplineInstantiateGizmos(SplineInstantiate scr, GizmoType gizmoType)
{
var instances = scr.instances;
foreach(var instance in instances)
{
var pos = instance.transform.position;
Handles.color = Color.red;
Handles.DrawAAPolyLine(3f,new []{ pos, pos + 0.25f * instance.transform.right });
Handles.color = Color.green;
Handles.DrawAAPolyLine(3f,new []{pos, pos + 0.25f * instance.transform.up});
Handles.color = Color.blue;
Handles.DrawAAPolyLine(3f,new []{pos, pos + 0.25f * instance.transform.forward});
}
}
}
[CustomPropertyDrawer (typeof(SplineInstantiate.InstantiableItem))]
class InstantiableItemDrawer : PropertyDrawer
{
static readonly string k_ProbabilityTooltip = L10n.Tr("Probability for that element to appear.");
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
var prefabProperty = property.FindPropertyRelative(nameof(SplineInstantiate.InstantiableItem.Prefab));
var probaProperty = property.FindPropertyRelative(nameof(SplineInstantiate.InstantiableItem.Probability));
var headerLine = ReserveSpace(EditorGUIUtility.singleLineHeight, ref rect);
using(new SplineInstantiateEditor.LabelWidthScope(0f))
EditorGUI.ObjectField(ReserveLineSpace(headerLine.width - 100, ref headerLine), prefabProperty, new GUIContent(""));
ReserveLineSpace(10, ref headerLine);
EditorGUI.LabelField(ReserveLineSpace(15, ref headerLine), new GUIContent("%", k_ProbabilityTooltip));
probaProperty.floatValue = EditorGUI.FloatField(ReserveLineSpace(60, ref headerLine), probaProperty.floatValue);
}
static Rect ReserveSpace(float height, ref Rect total)
{
Rect current = total;
current.height = height;
total.y += height;
return current;
}
static Rect ReserveLineSpace(float width, ref Rect total)
{
Rect current = total;
current.width = width;
total.x += width;
return current;
}
}
[CustomPropertyDrawer (typeof(SplineInstantiate.AlignAxis))]
class ItemAxisDrawer : PropertyDrawer
{
static int s_LastUpAxis;
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
var enumValue = property.intValue;
if(property.name == "m_Up")
{
property.intValue = (int)( (SplineInstantiate.AlignAxis)EditorGUI.EnumPopup(rect, label, (SplineInstantiate.AlignAxis)enumValue));
s_LastUpAxis = property.intValue;
}
else
{
property.intValue = (int)((SplineInstantiate.AlignAxis)EditorGUI.EnumPopup(rect, label, (SplineInstantiate.AlignAxis)enumValue,
(item) =>
{
int axisItem = (int)(SplineInstantiate.AlignAxis)item;
return !(axisItem == s_LastUpAxis || axisItem == (s_LastUpAxis + 3) % 6);
}));
}
}
}
[CustomEditor(typeof(SplineInstantiate),false)]
[CanEditMultipleObjects]
class SplineInstantiateEditor : SplineComponentEditor
{
enum SpawnType
{
Exact,
Random
}
SerializedProperty m_SplineContainer;
SerializedProperty m_ItemsToInstantiate;
SerializedProperty m_InstantiateMethod;
SerializedProperty m_Seed;
SerializedProperty m_Space;
SerializedProperty m_UpAxis;
SerializedProperty m_ForwardAxis;
SerializedProperty m_Spacing;
SerializedProperty m_PositionOffset;
SerializedProperty m_RotationOffset;
SerializedProperty m_ScaleOffset;
SerializedProperty m_AutoRefresh;
static readonly string[] k_SpacingTypesLabels = new []
{
L10n.Tr("Count"),
L10n.Tr("Spacing (Spline)"),
L10n.Tr("Spacing (Linear)")
};
//Setup Section
static readonly string k_Setup = L10n.Tr("Instantiated Object Setup");
static readonly string k_ObjectUp = L10n.Tr("Up Axis");
static readonly string k_ObjectUpTooltip = L10n.Tr("Object axis to use as Up Direction when instantiating on the Spline (default is Y).");
static readonly string k_ObjectForward = L10n.Tr("Forward Axis");
static readonly string k_ObjectForwardTooltip = L10n.Tr("Object axis to use as Forward Direction when instantiating on the Spline (default is Z).");
static readonly string k_AlignTo = L10n.Tr("Align To");
static readonly string k_AlignToTooltip = L10n.Tr("Define the space to use to orientate the instantiated object.");
static readonly string k_Instantiation = L10n.Tr("Instantiation");
static readonly string k_Method = L10n.Tr("Instantiate Method");
static readonly string k_MethodTooltip = L10n.Tr("How instances are generated along the spline.");
static readonly string k_Max = L10n.Tr("Max");
static readonly string k_Min = L10n.Tr("Min");
SpawnType m_SpacingType;
//Offsets
static readonly string k_PositionOffset = L10n.Tr("Position Offset");
static readonly string k_PositionOffsetTooltip = L10n.Tr("Whether or not to use a position offset.");
static readonly string k_RotationOffset = L10n.Tr("Rotation Offset");
static readonly string k_RotationOffsetTooltip = L10n.Tr("Whether or not to use a rotation offset.");
static readonly string k_ScaleOffset = L10n.Tr("Scale Offset");
static readonly string k_ScaleOffsetTooltip = L10n.Tr("Whether or not to use a scale offset.");
//Generation
static readonly string k_Generation = L10n.Tr("Generation");
static readonly string k_AutoRefresh = L10n.Tr("Auto Refresh Generation");
static readonly string k_AutoRefreshTooltip = L10n.Tr("Automatically refresh the instances when the spline or the values are changed.");
static readonly string k_Seed = L10n.Tr("Randomization Seed");
static readonly string k_SeedTooltip = L10n.Tr("Value used to initialize the pseudorandom number generator of the instances.");
static readonly string k_Randomize = L10n.Tr("Randomize");
static readonly string k_RandomizeTooltip = L10n.Tr("Compute a new randomization of the instances along the spline.");
static readonly string k_Regenerate = L10n.Tr("Regenerate");
static readonly string k_RegenerateTooltip = L10n.Tr("Regenerate the instances along the spline.");
static readonly string k_Clear = L10n.Tr("Clear");
static readonly string k_ClearTooltip = L10n.Tr("Clear the instances along the spline.");
static readonly string k_Bake = L10n.Tr("Bake Instances");
static readonly string k_BakeTooltip = L10n.Tr("Bake the instances in the SceneView for custom edition and destroy that SplineInstantiate component.");
bool m_PositionFoldout;
bool m_RotationFoldout;
bool m_ScaleFoldout;
enum OffsetType
{
Exact,
Random
};
SplineInstantiate[] m_Components;
SplineInstantiate[] components
{
get
{
//in case of multiple selection where some objects do not have a SplineInstantiate component, m_Components might be null
if (m_Components == null)
m_Components = targets.Select(x => x as SplineInstantiate).Where(y => y != null).ToArray();
return m_Components;
}
}
protected void OnEnable()
{
Spline.Changed += OnSplineChanged;
EditorSplineUtility.AfterSplineWasModified += OnSplineModified;
SplineContainer.SplineAdded += OnContainerSplineSetModified;
SplineContainer.SplineRemoved += OnContainerSplineSetModified;
}
bool Initialize()
{
if (m_SplineContainer != null && m_Components != null && m_Components.Length > 0)
return true;
m_SplineContainer = serializedObject.FindProperty("m_Container");
m_ItemsToInstantiate = serializedObject.FindProperty("m_ItemsToInstantiate");
m_InstantiateMethod = serializedObject.FindProperty("m_Method");
m_Space = serializedObject.FindProperty("m_Space");
m_UpAxis = serializedObject.FindProperty("m_Up");
m_ForwardAxis = serializedObject.FindProperty("m_Forward");
m_Spacing = serializedObject.FindProperty("m_Spacing");
m_PositionOffset = serializedObject.FindProperty("m_PositionOffset");
m_RotationOffset = serializedObject.FindProperty("m_RotationOffset");
m_ScaleOffset = serializedObject.FindProperty("m_ScaleOffset");
m_Seed = serializedObject.FindProperty("m_Seed");
m_AutoRefresh = serializedObject.FindProperty("m_AutoRefresh");
if (m_Spacing != null)
m_SpacingType = Mathf.Approximately(m_Spacing.vector2Value.x, m_Spacing.vector2Value.y) ? SpawnType.Exact : SpawnType.Random;
else
m_SpacingType = SpawnType.Exact;
m_Components = targets.Select(x => x as SplineInstantiate).Where(y => y != null).ToArray();
return m_Components != null && m_Components.Length > 0;
}
void OnDisable()
{
m_Components = null;
Spline.Changed -= OnSplineChanged;
EditorSplineUtility.AfterSplineWasModified -= OnSplineModified;
SplineContainer.SplineAdded -= OnContainerSplineSetModified;
SplineContainer.SplineRemoved -= OnContainerSplineSetModified;
}
void OnSplineModified(Spline spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var instantiate in components)
{
if(instantiate == null)
continue;
if (instantiate.Container != null && instantiate.Container.Splines.Contains(spline))
instantiate.SetSplineDirty(spline);
}
}
void OnSplineChanged(Spline spline, int knotIndex, SplineModification modification)
{
OnSplineModified(spline);
}
void OnContainerSplineSetModified(SplineContainer container, int spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var instantiate in components)
{
if (instantiate.Container == container)
instantiate.UpdateInstances();
}
}
public override void OnInspectorGUI()
{
if(!Initialize())
return;
serializedObject.Update();
var dirtyInstances = false;
var updateInstances = false;
EditorGUILayout.PropertyField(m_SplineContainer);
if(m_SplineContainer.objectReferenceValue == null)
EditorGUILayout.HelpBox(k_Helpbox, MessageType.Warning);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_ItemsToInstantiate);
dirtyInstances = EditorGUI.EndChangeCheck();
DoSetupSection();
dirtyInstances |= DoInstantiateSection();
updateInstances |= DisplayOffsets();
EditorGUILayout.LabelField(k_Generation, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Seed, new GUIContent(k_Seed, k_SeedTooltip));
var newSeed = EditorGUI.EndChangeCheck();
dirtyInstances |= newSeed;
updateInstances |= newSeed;
EditorGUILayout.PropertyField(m_AutoRefresh, new GUIContent(k_AutoRefresh, k_AutoRefreshTooltip));
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
EditorGUILayout.Separator();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
if (GUILayout.Button(new GUIContent(k_Randomize, k_RandomizeTooltip), GUILayout.MaxWidth(100f)))
{
Undo.SetCurrentGroupName("Change SplineInstantiate Seed");
var group = Undo.GetCurrentGroup();
foreach (var splineInstantiate in m_Components)
{
Undo.RecordObject(splineInstantiate, $"Change SplineInstantiate Seed for {splineInstantiate.gameObject.name}");
splineInstantiate.Randomize();
}
Undo.CollapseUndoOperations(group);
updateInstances = true;
}
var isInstancesCountGreaterThanZero = false;
foreach (var splineInstantiate in m_Components)
{
if (splineInstantiate.instances.Count > 0)
{
isInstancesCountGreaterThanZero = true;
break;
}
}
if (GUILayout.Button(new GUIContent(k_Regenerate, k_RegenerateTooltip), GUILayout.MaxWidth(100f)))
updateInstances = true;
GUI.enabled = isInstancesCountGreaterThanZero;
if (GUILayout.Button(new GUIContent(k_Clear, k_ClearTooltip), GUILayout.MaxWidth(100f)))
{
Undo.SetCurrentGroupName("Clear SplineInstantiate");
var group = Undo.GetCurrentGroup();
foreach (var splineInstantiate in m_Components)
{
Undo.RecordObject(splineInstantiate, $"Clear SplineInstantiate for {splineInstantiate.gameObject.name}");
splineInstantiate.Clear();
}
Undo.CollapseUndoOperations(group);
}
if (GUILayout.Button(new GUIContent(k_Bake, k_BakeTooltip), GUILayout.MaxWidth(100f)))
{
foreach (var splineInstantiate in m_Components)
BakeInstances(splineInstantiate);
}
GUI.enabled = true;
EditorGUILayout.Space();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
// m_Components may have been made null as a result of the Bake operation (via OnDisable)
if (m_Components != null)
{
foreach (var splineInstantiate in m_Components)
{
if (dirtyInstances)
splineInstantiate.SetDirty();
if (updateInstances)
splineInstantiate.UpdateInstances();
}
}
if (dirtyInstances || updateInstances)
SceneView.RepaintAll();
}
void DoSetupSection()
{
EditorGUILayout.LabelField(k_Setup, EditorStyles.boldLabel);
GUILayout.Space(5f);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_UpAxis, new GUIContent(k_ObjectUp, k_ObjectUpTooltip));
EditorGUILayout.PropertyField(m_ForwardAxis, new GUIContent(k_ObjectForward, k_ObjectForwardTooltip));
if(EditorGUI.EndChangeCheck())
{
//Insuring axis integrity
if(m_ForwardAxis.intValue == m_UpAxis.intValue || m_ForwardAxis.intValue == ( m_UpAxis.intValue + 3 ) % 6)
m_ForwardAxis.intValue = ( m_ForwardAxis.intValue + 1 ) % 6;
}
EditorGUILayout.PropertyField(m_Space, new GUIContent(k_AlignTo, k_AlignToTooltip));
EditorGUI.indentLevel--;
}
bool DoInstantiateSection()
{
var dirty = false;
Vector2 spacingV2 = m_Spacing.vector2Value;
EditorGUILayout.LabelField(k_Instantiation, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_InstantiateMethod, new GUIContent(k_Method, k_MethodTooltip), EditorStyles.boldFont );
if(EditorGUI.EndChangeCheck())
{
if(m_SpacingType == SpawnType.Random && m_InstantiateMethod.intValue == (int)SplineInstantiate.Method.LinearDistance)
m_Spacing.vector2Value = new Vector2(spacingV2.x, float.NaN);
dirty = true;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(new GUIContent(k_SpacingTypesLabels[m_InstantiateMethod.intValue]));
EditorGUI.indentLevel--;
GUILayout.Space(2f);
EditorGUI.BeginChangeCheck();
float spacingX = m_Spacing.vector2Value.x;
var isExact = m_SpacingType == SpawnType.Exact;
if(isExact || m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
{
using(new LabelWidthScope(30f))
spacingX = (SplineInstantiate.Method)m_InstantiateMethod.intValue == SplineInstantiate.Method.InstanceCount ?
EditorGUILayout.IntField(new GUIContent(isExact ? string.Empty : k_Min), (int)m_Spacing.vector2Value.x, GUILayout.MinWidth(50f)) :
EditorGUILayout.FloatField(new GUIContent(isExact ? L10n.Tr("Dist") : k_Min), m_Spacing.vector2Value.x, GUILayout.MinWidth(50f));
}
if(isExact)
{
spacingV2 = new Vector2(spacingX, spacingX);
}
else if(m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
{
using(new LabelWidthScope(30f))
{
var spacingY = (SplineInstantiate.Method)m_InstantiateMethod.intValue == SplineInstantiate.Method.InstanceCount ?
EditorGUILayout.IntField(new GUIContent(k_Max), (int)m_Spacing.vector2Value.y, GUILayout.MinWidth(50f)) :
EditorGUILayout.FloatField(new GUIContent(k_Max), m_Spacing.vector2Value.y, GUILayout.MinWidth(50f));
if(spacingX > m_Spacing.vector2Value.y)
spacingY = spacingX;
else if(spacingY < m_Spacing.vector2Value.x)
spacingX = spacingY;
spacingV2 = new Vector2(spacingX, spacingY);
}
}
if(EditorGUI.EndChangeCheck())
m_Spacing.vector2Value = spacingV2;
EditorGUI.BeginChangeCheck();
if(m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
m_SpacingType = (SpawnType)EditorGUILayout.EnumPopup(m_SpacingType, GUILayout.MinWidth(30f));
else
m_SpacingType = (SpawnType)EditorGUILayout.Popup(m_SpacingType == SpawnType.Exact ? 0 : 1,
new []{"Exact", "Auto"}, GUILayout.MinWidth(30f));
if(EditorGUI.EndChangeCheck())
{
if(m_SpacingType == SpawnType.Exact)
m_Spacing.vector2Value = new Vector2(spacingV2.x, spacingV2.x);
else if(m_InstantiateMethod.intValue == (int)SplineInstantiate.Method.LinearDistance)
m_Spacing.vector2Value = new Vector2(spacingV2.x, float.NaN);
dirty = true;
}
EditorGUILayout.EndHorizontal();
return dirty;
}
bool DoOffsetProperties(
SerializedProperty offsetProperty, GUIContent content, bool foldoutValue, out bool newFoldoutValue)
{
bool changed = false;
newFoldoutValue = foldoutValue;
EditorGUILayout.BeginHorizontal();
using(new LabelWidthScope(0f))
{
var setupProperty = offsetProperty.FindPropertyRelative("setup");
var setup = (SplineInstantiate.Vector3Offset.Setup)setupProperty.intValue;
var hasOffset = ( setup & SplineInstantiate.Vector3Offset.Setup.HasOffset ) != 0;
EditorGUI.BeginChangeCheck();
hasOffset = EditorGUILayout.Toggle(hasOffset, GUILayout.MaxWidth(20f));
if(EditorGUI.EndChangeCheck())
{
if(hasOffset)
setup |= SplineInstantiate.Vector3Offset.Setup.HasOffset;
else
setup &= ~SplineInstantiate.Vector3Offset.Setup.HasOffset;
setupProperty.intValue = (int)setup;
changed = true;
}
EditorGUILayout.Space(10f);
using(new EditorGUI.DisabledScope(!hasOffset))
{
newFoldoutValue = Foldout(foldoutValue, content, hasOffset) && hasOffset;
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
if(newFoldoutValue)
{
EditorGUILayout.BeginHorizontal();
var hasCustomSpace = ( setup & SplineInstantiate.Vector3Offset.Setup.HasCustomSpace ) != 0;
EditorGUI.BeginChangeCheck();
var space = m_Space.intValue < 1 ? "Spline Element" : m_Space.intValue == 1 ? "Spline Object" : "World";
hasCustomSpace = EditorGUILayout.Toggle(new GUIContent("Override space", L10n.Tr("Override current space (" + space + ")")), hasCustomSpace);
if(EditorGUI.EndChangeCheck())
{
if(hasCustomSpace)
setup |= SplineInstantiate.Vector3Offset.Setup.HasCustomSpace;
else
setup &= ~SplineInstantiate.Vector3Offset.Setup.HasCustomSpace;
setupProperty.intValue = (int)setup;
changed = true;
}
var spaceProperty = offsetProperty.FindPropertyRelative("space");
using(new EditorGUI.DisabledScope(!hasCustomSpace))
{
var type = (SplineInstantiate.OffsetSpace)spaceProperty.intValue;
EditorGUI.BeginChangeCheck();
type = (SplineInstantiate.OffsetSpace)EditorGUILayout.EnumPopup(type);
if(EditorGUI.EndChangeCheck())
{
spaceProperty.intValue = (int)type;
changed = true;
}
}
EditorGUILayout.EndHorizontal();
var minProperty = offsetProperty.FindPropertyRelative("min");
var maxProperty = offsetProperty.FindPropertyRelative("max");
var minPropertyValue = minProperty.vector3Value;
var maxPropertyValue = maxProperty.vector3Value;
float min, max;
SerializedProperty randomProperty;
for(int i = 0; i < 3; i++)
{
string label = i == 0 ? "X" : i == 1 ? "Y" : "Z";
EditorGUILayout.BeginHorizontal();
using(new LabelWidthScope(30f))
EditorGUILayout.LabelField(label);
randomProperty = offsetProperty.FindPropertyRelative("random"+label);
GUILayout.FlexibleSpace();
if(randomProperty.boolValue)
{
EditorGUI.BeginChangeCheck();
using(new LabelWidthScope(30f))
{
min = EditorGUILayout.FloatField("from", minPropertyValue[i], GUILayout.MinWidth(95f), GUILayout.MaxWidth(95f));
max = EditorGUILayout.FloatField(" to", maxPropertyValue[i], GUILayout.MinWidth(95f), GUILayout.MaxWidth(95f));
}
if(EditorGUI.EndChangeCheck())
{
if(min > maxPropertyValue[i])
maxPropertyValue[i] = min;
if(max < minPropertyValue[i])
minPropertyValue[i] = max;
minPropertyValue[i] = min;
maxPropertyValue[i] = max;
minProperty.vector3Value = minPropertyValue;
maxProperty.vector3Value = maxPropertyValue;
changed = true;
}
}
else
{
EditorGUI.BeginChangeCheck();
using(new LabelWidthScope(30f))
min = EditorGUILayout.FloatField("is ", minPropertyValue[i], GUILayout.MinWidth(193f), GUILayout.MaxWidth(193f));
if(EditorGUI.EndChangeCheck())
{
minPropertyValue[i] = min;
if(min > maxPropertyValue[i])
maxPropertyValue[i] = min;
minProperty.vector3Value = minPropertyValue;
maxProperty.vector3Value = maxPropertyValue;
changed = true;
}
}
EditorGUI.BeginChangeCheck();
var isOffsetRandom = randomProperty.boolValue ? OffsetType.Random : OffsetType.Exact;
using(new LabelWidthScope(0f))
isOffsetRandom = (OffsetType)EditorGUILayout.EnumPopup(isOffsetRandom,GUILayout.MinWidth(100f), GUILayout.MaxWidth(200f));
if(EditorGUI.EndChangeCheck())
{
randomProperty.boolValue = isOffsetRandom == OffsetType.Random;
changed = true;
}
EditorGUILayout.EndHorizontal();
}
}
}
}
return changed;
}
bool DisplayOffsets()
{
var updateNeeded = DoOffsetProperties(m_PositionOffset, new GUIContent(k_PositionOffset, k_PositionOffsetTooltip), m_PositionFoldout, out m_PositionFoldout);
updateNeeded |= DoOffsetProperties(m_RotationOffset, new GUIContent(k_RotationOffset, k_RotationOffsetTooltip), m_RotationFoldout, out m_RotationFoldout);
updateNeeded |= DoOffsetProperties(m_ScaleOffset, new GUIContent(k_ScaleOffset, k_ScaleOffsetTooltip), m_ScaleFoldout, out m_ScaleFoldout);
return updateNeeded;
}
/// <summary>
/// Bake the instances into the scene and destroy this SplineInstantiate component.
/// Making changes to the spline after baking will not affect the instances anymore.
/// </summary>
void BakeInstances(SplineInstantiate splineInstantiate)
{
Undo.SetCurrentGroupName("Baking SplineInstantiate instances");
var group = Undo.GetCurrentGroup();
splineInstantiate.UpdateInstances();
for (int i = 0; i < splineInstantiate.instances.Count; ++i)
{
var newInstance = splineInstantiate.instances[i];
newInstance.name = "Instance-" + i;
newInstance.hideFlags = HideFlags.None;
newInstance.transform.SetParent(splineInstantiate.gameObject.transform, true);
Undo.RegisterCreatedObjectUndo(newInstance, "Baking instance");
}
splineInstantiate.instances.Clear();
if(splineInstantiate.InstancesRoot != null)
Undo.DestroyObjectImmediate(splineInstantiate.InstancesRoot);
Undo.DestroyObjectImmediate(splineInstantiate);
Undo.CollapseUndoOperations(group);
}
}

11
Packages/com.unity.splines/Editor/Components/SplineInstantiateEditor.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c574a2f698921d1458f6dfba4b5e1229
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.splines/Editor/Controls.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6042ea3ccf1fb5c45b533a9d67e956b7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

295
Packages/com.unity.splines/Editor/Controls/CurveHandles.cs

@ -0,0 +1,295 @@
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
static class CurveHandles
{
const float k_CurveLineWidth = 4f;
const float k_PreviewCurveOpacity = 0.5f;
static readonly Vector3[] s_CurveDrawingBuffer = new Vector3[SplineCacheUtility.CurveDrawResolution + 1];
static readonly Vector3[] s_FlowTriangleVertices = new Vector3[3];
/// <summary>
/// Creates handles for a BezierCurve.
/// </summary>
/// <param name="controlID">The controlID of the curve to create highlights for.</param>
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
public static void Draw(int controlID, BezierCurve curve)
{
if(Event.current.type == EventType.Repaint)
Draw(controlID, curve, false, true);
}
/// <summary>
/// Creates handles for a BezierCurve.
/// </summary>
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
/// <param name="activeSpline">Whether the curve is part of the active spline.</param>
internal static void Draw(BezierCurve curve, bool activeSpline)
{
if(Event.current.type == EventType.Repaint)
Draw(0, curve, false, activeSpline);
}
/// <summary>
/// Creates highlights for a BezierCurve to make it easier to select.
/// </summary>
/// <param name="controlID">The controlID of the curve to create highlights for.</param>
/// <param name="curve">The <see cref="BezierCurve"/> to create highlights for.</param>
/// <param name="spline">The <see cref="ISpline"/> (if any) that the curve belongs to.</param>
/// <param name="curveIndex">The curve's index if it belongs to a spline - otherwise -1.</param>
/// <param name="knotA">The knot at the start of the curve.</param>
/// <param name="knotB">The knot at the end of the curve.</param>
/// <param name="activeSpline">Whether the curve is part of the active spline.</param>
internal static void DrawWithHighlight(
int controlID,
ISpline spline,
int curveIndex,
float4x4 localToWorld,
SelectableKnot knotA,
SelectableKnot knotB,
bool activeSpline)
{
var evt = Event.current;
switch(evt.GetTypeForControl(controlID))
{
case EventType.Layout:
case EventType.MouseMove:
if (!SplineHandles.ViewToolActive() && activeSpline)
{
var curve = spline.GetCurve(curveIndex).Transform(localToWorld);
var dist = DistanceToCurve(curve);
HandleUtility.AddControl(controlID, Mathf.Max(0, dist - SplineHandleUtility.pickingDistance));
//Trigger repaint on MouseMove to update highlight visuals from SplineHandles
if (evt.type == EventType.MouseMove || controlID == HandleUtility.nearestControl)
{
SplineHandleUtility.GetNearestPointOnCurve(curve, out _, out var t);
var curveMidT = EditorSplineUtility.GetCurveMiddleInterpolation(curve, spline, curveIndex);
var hoveredKnot = t <= curveMidT ? knotA : knotB;
if (!(SplineHandleUtility.lastHoveredElement is SelectableKnot knot) || !knot.Equals(hoveredKnot))
{
if (GUIUtility.hotControl == 0 && HandleUtility.nearestControl == controlID)
{
SplineHandleUtility.SetLastHoveredElement(hoveredKnot, controlID);
SceneView.RepaintAll();
}
}
}
}
break;
case EventType.MouseDown:
var curveMD = spline.GetCurve(curveIndex).Transform(localToWorld);
if (!SplineHandles.ViewToolActive() && HandleUtility.nearestControl == controlID)
{
//Clicking a knot selects it
if (evt.button != 0)
break;
GUIUtility.hotControl = controlID;
evt.Use();
SplineHandleUtility.GetNearestPointOnCurve(curveMD, out _, out var t);
SplineSelectionUtility.HandleSelection(t <= .5f ? knotA : knotB, false);
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == controlID)
{
GUIUtility.hotControl = 0;
evt.Use();
}
break;
}
}
/// <summary>
/// Draws flow on a BezierCurve to indicate the direction.
/// </summary>
/// <param name="curve">The <see cref="BezierCurve"/> to create highlights for.</param>
/// <param name="spline">The <see cref="ISpline"/> (if any) that the curve belongs to.</param>
/// <param name="curveIndex">The curve's index if it belongs to a spline - otherwise -1.</param>
internal static void DrawFlow(BezierCurve curve, ISpline spline, int curveIndex)
{
if(Event.current.type != EventType.Repaint)
return;
var arrow = SplineCacheUtility.GetCurveArrow(spline, curveIndex, curve);
s_FlowTriangleVertices[0] = arrow.positions[0];
s_FlowTriangleVertices[1] = arrow.positions[1];
s_FlowTriangleVertices[2] = arrow.positions[2];
using (new Handles.DrawingScope(SplineHandleUtility.lineColor, arrow.trs))
{
using (new ZTestScope(CompareFunction.Less))
Handles.DrawAAConvexPolygon(s_FlowTriangleVertices);
}
using (new Handles.DrawingScope(SplineHandleUtility.lineBehindColor, arrow.trs))
{
using (new ZTestScope(CompareFunction.Greater))
Handles.DrawAAConvexPolygon(s_FlowTriangleVertices);
}
}
static void Draw(int controlID, BezierCurve curve, bool preview, bool activeSpline)
{
var evt = Event.current;
switch (evt.type)
{
case EventType.Layout:
case EventType.MouseMove:
if (!SplineHandles.ViewToolActive() && activeSpline)
{
var dist = DistanceToCurve(curve);
HandleUtility.AddControl(controlID, Mathf.Max(0, dist - SplineHandleUtility.pickingDistance));
}
break;
case EventType.Repaint:
var prevColor = Handles.color;
FillCurveDrawingBuffer(curve);
var color = SplineHandleUtility.lineColor;
if (preview)
color.a *= k_PreviewCurveOpacity;
Handles.color = color;
using (new ZTestScope(CompareFunction.Less))
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_CurveLineWidth, s_CurveDrawingBuffer);
color = SplineHandleUtility.lineBehindColor;
if (preview)
color.a *= k_PreviewCurveOpacity;
Handles.color = color;
using (new ZTestScope(CompareFunction.Greater))
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_CurveLineWidth, s_CurveDrawingBuffer);
Handles.color = prevColor;
break;
}
}
static void FillCurveDrawingBuffer(BezierCurve curve)
{
SplineCacheUtility.GetCurvePositions(curve, s_CurveDrawingBuffer);
}
internal static float DistanceToCurve(BezierCurve curve)
{
FillCurveDrawingBuffer(curve);
return DistanceToCurve();
}
static float DistanceToCurve()
{
float dist = float.MaxValue;
for (var i = 0; i < s_CurveDrawingBuffer.Length - 1; ++i)
{
var a = s_CurveDrawingBuffer[i];
var b = s_CurveDrawingBuffer[i + 1];
dist = Mathf.Min(HandleUtility.DistanceToLine(a, b), dist);
}
return dist;
}
internal static void DoCurveHighlightCap(SelectableKnot knot)
{
if(Event.current.type != EventType.Repaint)
return;
if(knot.IsValid())
{
var spline = knot.SplineInfo.Spline;
var localToWorld = knot.SplineInfo.LocalToWorld;
if(knot.KnotIndex > 0 || spline.Closed)
{
var curve = spline.GetCurve(spline.PreviousIndex(knot.KnotIndex)).Transform(localToWorld);
var curveMiddleT = EditorSplineUtility.GetCurveMiddleInterpolation(curve, spline, spline.PreviousIndex(knot.KnotIndex));
DrawCurveHighlight(curve, 1f, curveMiddleT);
}
if(knot.KnotIndex < spline.Count - 1 || spline.Closed)
{
var curve = spline.GetCurve(knot.KnotIndex).Transform(localToWorld);
var curveMiddleT = EditorSplineUtility.GetCurveMiddleInterpolation(curve, spline, knot.KnotIndex);
DrawCurveHighlight(curve, 0f, curveMiddleT);
}
}
}
static void DrawCurveHighlight(BezierCurve curve, float startT, float endT)
{
FillCurveDrawingBuffer(curve);
var growing = startT <= endT;
var color = Handles.color;
color.a = growing ? 1f : 0f;
using (new ZTestScope(CompareFunction.Less))
using (new Handles.DrawingScope(color))
DrawAAPolyLineForCurveHighlight(color, startT, endT, 1f, growing);
using (new ZTestScope(CompareFunction.Greater))
using (new Handles.DrawingScope(color))
DrawAAPolyLineForCurveHighlight(color, startT, endT, 0.3f, growing);
}
static void DrawAAPolyLineForCurveHighlight(Color color, float startT, float endT, float colorAlpha, bool growing)
{
for (int i = 1; i <= SplineCacheUtility.CurveDrawResolution; ++i)
{
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_CurveLineWidth, new[] { s_CurveDrawingBuffer[i - 1], s_CurveDrawingBuffer[i] });
var current = ((float)i / (float)SplineCacheUtility.CurveDrawResolution);
if (growing)
{
if (current > endT)
color.a = 0f;
else if (current > startT)
color.a = (1f - (current - startT) / (endT - startT)) * colorAlpha;
}
else
{
if (current < endT)
color.a = 0f;
else if (current > endT && current < startT)
color.a = (current - endT) / (startT - endT) * colorAlpha;
}
Handles.color = color;
}
}
/// <summary>
/// Creates the set of control points that make up a curve.
/// </summary>
/// <param name="curve">The <see cref="BezierCurve"/> to create control points for.</param>
public static void DrawControlNet(BezierCurve curve)
{
Handles.color = Color.green;
Handles.DotHandleCap(-1, curve.P0, Quaternion.identity, HandleUtility.GetHandleSize(curve.P0) * .04f, Event.current.type);
Handles.color = Color.red;
Handles.DotHandleCap(-1, curve.P1, Quaternion.identity, HandleUtility.GetHandleSize(curve.P1) * .04f, Event.current.type);
Handles.color = Color.yellow;
Handles.DotHandleCap(-1, curve.P2, Quaternion.identity, HandleUtility.GetHandleSize(curve.P2) * .04f, Event.current.type);
Handles.color = Color.blue;
Handles.DotHandleCap(-1, curve.P3, Quaternion.identity, HandleUtility.GetHandleSize(curve.P3) * .04f, Event.current.type);
Handles.color = Color.gray;
Handles.DrawDottedLine(curve.P0, curve.P1, 2f);
Handles.DrawDottedLine(curve.P1, curve.P2, 2f);
Handles.DrawDottedLine(curve.P2, curve.P3, 2f);
}
}
}

11
Packages/com.unity.splines/Editor/Controls/CurveHandles.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77221e48184d50742984ffe3b4192f2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

201
Packages/com.unity.splines/Editor/Controls/DirectManipulation.cs

@ -0,0 +1,201 @@
using Unity.Mathematics;
using UnityEditor.SettingsManagement;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.Splines
{
static class DirectManipulation
{
[UserSetting("Tweak Mode", "Plane Color")]
static readonly Pref<Color> s_GuidePlaneColor = new Pref<Color>("Handles.DirectManipulation.PlaneColor", new Color(1f, 1f, 1f, 5f/255f));
[UserSetting("Tweak Mode", "Snap to Guide Enabled")]
static readonly Pref<bool> s_SnapToGuide = new Pref<bool>("Handles.DirectManipulation.SnapToGuide", true);
[UserSetting("Tweak Mode", "Snap to Guide Distance")]
static readonly Pref<float> s_SnapToGuideDistance = new Pref<float>("Handles.DirectManipulation.SnapToGuideDistance", 7f);
static readonly Vector3[] s_VertexBuffer = new Vector3[4];
public static bool IsDragging => s_IsDragging;
static readonly Vector3 k_GuidePlaneZTestOffset = new Vector3(0.001f, 0.001f, 0.001f);
static Vector3 s_InitialPosition;
static Quaternion s_InitialRotation;
static Vector2 s_InitialMousePosition;
static bool s_IsDragging;
#if UNITY_2022_2_OR_NEWER
static bool IncrementalSnapActive => EditorSnapSettings.incrementalSnapActive;
#else
static bool IncrementalSnapActive => false;
#endif
const float k_HandleColorAlphaFactor = 0.3f;
static bool ShouldMoveOnNormal(int controlId) => GUIUtility.hotControl == controlId && Event.current.alt;
public static void BeginDrag(Vector3 position, Quaternion rotation)
{
s_InitialPosition = position;
s_InitialRotation = rotation;
s_InitialMousePosition = Event.current.mousePosition;
}
public static Vector3 UpdateDrag(int controlId)
{
var position = ShouldMoveOnNormal(controlId)
? MoveOnNormal(Event.current.mousePosition, s_InitialPosition, s_InitialRotation)
: MoveOnPlane(Event.current.mousePosition, s_InitialPosition, s_InitialRotation, IncrementalSnapActive);
s_IsDragging = true;
return position;
}
public static void EndDrag()
{
s_IsDragging = false;
}
public static void DrawHandles(int controlId, Vector3 position)
{
if (GUIUtility.hotControl != controlId || !s_IsDragging)
return;
EditorGUIUtility.AddCursorRect(new Rect(0, 0, 100000, 10000), MouseCursor.MoveArrow);
if (ShouldMoveOnNormal(controlId))
{
var yDir = s_InitialRotation * Vector3.up;
DrawGuideAxis(s_InitialPosition, yDir, Handles.yAxisColor);
}
else
{
var zDir = s_InitialRotation * Vector3.forward;
var xDir = s_InitialRotation * Vector3.right;
DrawGuidePlane(s_InitialPosition, xDir, zDir, position);
DrawGuideDottedLine(s_InitialPosition, zDir, position);
DrawGuideDottedLine(s_InitialPosition, xDir, position);
DrawGuideAxis(s_InitialPosition, zDir, Handles.zAxisColor);
DrawGuideAxis(s_InitialPosition, xDir, Handles.xAxisColor);
}
}
static (Vector3 projection, float distance) GetSnapToGuideData(Vector3 current, Vector3 origin, Vector3 axis)
{
var projection = Vector3.Project(current - origin, axis);
var screenPos = HandleUtility.WorldToGUIPoint(origin + projection);
var distance = Vector2.Distance(screenPos, Event.current.mousePosition);
return (projection, distance);
}
static Vector3 MoveOnPlane(Vector2 mousePosition, Vector3 origin, Quaternion rotation, bool snapping)
{
var ray = HandleUtility.GUIPointToWorldRay(mousePosition);
var manipPlane = new Plane(rotation * Vector3.up, origin);
var position = manipPlane.Raycast(ray, out float distance)
? ray.origin + ray.direction * distance
: origin;
var dir = position - origin;
var forward = GetSnapToGuideData(position, origin, rotation * Vector3.forward);
var right = GetSnapToGuideData(position, origin, rotation * Vector3.right);
if (!snapping && s_SnapToGuide)
{
if (forward.distance < s_SnapToGuideDistance || right.distance < s_SnapToGuideDistance)
{
var snapToForward = forward.distance < right.distance;
var axis = (snapToForward ? forward : right).projection;
return origin + axis;
}
}
if(Mathf.Approximately(dir.magnitude, 0f))
dir = Vector3.forward;
var translation = Handles.SnapValue(Quaternion.Inverse(rotation) * dir, new Vector3(EditorSnapSettings.move.x, 0, EditorSnapSettings.move.z));
return origin + rotation * translation;
}
static Vector3 MoveOnNormal(Vector2 mousePosition, Vector3 origin, Quaternion rotation)
{
var upAxis = rotation * Vector3.up;
var translation = upAxis * Handles.SnapValue(HandleUtility.CalcLineTranslation(s_InitialMousePosition, mousePosition, origin, upAxis), EditorSnapSettings.move.y);
return origin + translation;
}
static void DrawGuideAxis(Vector3 origin, Vector3 axis, Color color)
{
var start = origin - axis.normalized * 10000f;
var end = origin + axis.normalized * 10000f;
using (new ZTestScope(CompareFunction.Less))
using (new Handles.DrawingScope(color))
{
Handles.DrawLine(origin, start, 0f);
Handles.DrawLine(origin, end, 0f);
}
color = new Color(color.r, color.g, color.b, color.a * k_HandleColorAlphaFactor);
using (new ZTestScope(CompareFunction.Greater))
using (new Handles.DrawingScope(color))
{
Handles.DrawLine(origin, start, 0f);
Handles.DrawLine(origin, end, 0f);
}
}
static void DrawGuidePlane(Vector3 origin, Vector3 axisX, Vector3 axisZ, Vector3 position)
{
var xAxisProjection = Vector3.Project(position - origin, axisX);
var zAxisProjection = Vector3.Project(position - origin, axisZ);
var cross = math.cross(xAxisProjection, zAxisProjection);
var normal = math.normalizesafe(cross);
var scaledOffset = k_GuidePlaneZTestOffset * HandleUtility.GetHandleSize(origin);
var calculatedOffset = new Vector3(scaledOffset.x * normal.x, scaledOffset.y * normal.y, scaledOffset.z * normal.z);
position += calculatedOffset;
origin += calculatedOffset;
s_VertexBuffer[0] = origin;
s_VertexBuffer[1] = origin + Vector3.Project(position - origin, axisX);
s_VertexBuffer[2] = position;
s_VertexBuffer[3] = origin + Vector3.Project(position - origin, axisZ);
DrawGuidePlane(Matrix4x4.identity);
}
static void DrawGuidePlane(Matrix4x4 matrix)
{
var color = s_GuidePlaneColor.value;
using (new ZTestScope(CompareFunction.Less))
using (new Handles.DrawingScope(matrix))
Handles.DrawSolidRectangleWithOutline(s_VertexBuffer, color, Color.clear);
color = new Color(s_GuidePlaneColor.value.r, s_GuidePlaneColor.value.g, s_GuidePlaneColor.value.b,
s_GuidePlaneColor.value.a * k_HandleColorAlphaFactor);
using (new ZTestScope(CompareFunction.Greater))
using (new Handles.DrawingScope(matrix))
Handles.DrawSolidRectangleWithOutline(s_VertexBuffer, color, Color.clear);
}
static void DrawGuideDottedLine(Vector3 origin, Vector3 axis, Vector3 position)
{
using (new ZTestScope(CompareFunction.Less))
Handles.DrawDottedLine(origin + Vector3.Project(position - origin, axis), position, 3f);
var color = new Color(Handles.color.r, Handles.color.g, Handles.color.b, Handles.color.a * k_HandleColorAlphaFactor);
using (new ZTestScope(CompareFunction.Greater))
using (new Handles.DrawingScope(color))
Handles.DrawDottedLine(origin + Vector3.Project(position - origin, axis), position, 3f);
}
}
}

11
Packages/com.unity.splines/Editor/Controls/DirectManipulation.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 485daebcb9b4a264ba4f878081549056
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

322
Packages/com.unity.splines/Editor/Controls/KnotHandles.cs

@ -0,0 +1,322 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.Splines
{
static class KnotHandles
{
const float k_ColorAlphaFactor = 0.3f;
const float k_KnotRotDiscRadius = 0.18f;
const float k_KnotRotDiscWidthDefault = 1.5f;
const float k_KnotRotDiscWidthHover = 3f;
const float k_KnotHandleWidth = 2f;
static readonly List<SelectableKnot> k_KnotBuffer = new List<SelectableKnot>();
static readonly Vector3[] k_HandlePoints = new Vector3[11];
static List<(SelectableKnot knot, bool selected, bool hovered, Color knotColor, Color discColor, bool linkedKnot)> s_Knots = new ();
internal static void Do(int controlId, SelectableKnot knot, bool selected = false, bool hovered = false)
{
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
return;
//Hovered might not be available if a TRS tool is in use
hovered &= SplineHandleUtility.IsHoverAvailableForSplineElement();
var knotColor = SplineHandleUtility.elementColor;
var rotationDiscColor = SplineHandleUtility.elementPreselectionColor;
if (hovered)
knotColor = SplineHandleUtility.elementPreselectionColor;
else if (selected)
knotColor = SplineHandleUtility.elementSelectionColor;
Draw(knot.Position, knot.Rotation, knotColor, selected, hovered, rotationDiscColor, k_KnotRotDiscWidthHover);
DrawKnotIndices(knot);
}
internal static void Draw(int controlId, SelectableKnot knot)
{
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
return;
if(!knot.IsValid())
return;
var selected = SplineSelection.Contains(knot);
var knotHovered = SplineHandleUtility.IsElementHovered(controlId);
//Retrieving linked knots
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
var drawLinkedKnotHandle = k_KnotBuffer.Count != 1;
var mainKnot = knot;
SelectableKnot lastHovered = new SelectableKnot();
// Retrieving the last hovered element
// SplineHandleUtility.lastHoveredElement is pointing either to:
// - the hovered Knot and the ID is pointing to the controlID of that knot in that case
// - if a curve is hovered, the element is the knot closest to the hovered part of the curve (start or end knot depending)
// and the controlID is the one of the curve
var lastHoveredElementIsKnot = SplineHandleUtility.lastHoveredElement is SelectableKnot;
if (lastHoveredElementIsKnot)
lastHovered = (SelectableKnot)SplineHandleUtility.lastHoveredElement;
var isCurveId = SplineHandles.IsCurveId(SplineHandleUtility.lastHoveredElementId);
var curveIsHovered = lastHoveredElementIsKnot &&
k_KnotBuffer.Contains(lastHovered) &&
isCurveId;
var hovered = knotHovered || (curveIsHovered && knot.Equals(lastHovered));
if (drawLinkedKnotHandle)
{
if (curveIsHovered)
{
drawLinkedKnotHandle = false;
if (!knot.Equals(lastHovered))
{
if (!SplineSelection.Contains(knot))
return;
hovered = false;
mainKnot = lastHovered;
}
}
else
{
foreach (var linkedKnot in k_KnotBuffer)
{
if (!hovered)
{
var kSelected = SplineSelection.Contains(linkedKnot);
// If the current knot in not selected but other linked knots are, skip rendering
if (!selected && kSelected)
return;
// If current knot is selected but not k, don't consider k as a potential knot
if (selected && !kSelected)
{
drawLinkedKnotHandle = false;
continue;
}
}
//Main knot is the older one, the one on the spline of lowest range and the knot of lowest index
if ((!SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(linkedKnot.SplineInfo)) &&
(linkedKnot.SplineInfo.Index < mainKnot.SplineInfo.Index ||
linkedKnot.SplineInfo.Index == mainKnot.SplineInfo.Index && linkedKnot.KnotIndex < mainKnot.KnotIndex))
mainKnot = linkedKnot;
}
}
}
//Hovered might not be available if a TRS tool is in use
hovered &= SplineHandleUtility.IsHoverAvailableForSplineElement();
var knotColor = SplineHandleUtility.elementColor;
var highlightColor = SplineHandleUtility.elementPreselectionColor;
var rotationDiscColor = SplineHandleUtility.elementPreselectionColor;
if (hovered)
{
knotColor = SplineHandleUtility.elementPreselectionColor;
highlightColor = SplineHandleUtility.elementPreselectionColor;
}
else if (selected)
{
knotColor = SplineHandleUtility.elementSelectionColor;
highlightColor = SplineHandleUtility.elementSelectionColor;
}
if (SplineHandleUtility.canDrawOnCurves && (hovered || selected))
{
using (new Handles.DrawingScope(highlightColor))
CurveHandles.DoCurveHighlightCap(knot);
}
if (knot.Equals(mainKnot))
{
s_Knots.Add((knot, selected, hovered, knotColor, rotationDiscColor, drawLinkedKnotHandle));
DrawKnotIndices(knot);
}
}
internal static void ClearVisibleKnots()
{
if (Event.current.type != EventType.Repaint)
return;
s_Knots.Clear();
}
internal static void DrawVisibleKnots()
{
if (Event.current.type != EventType.Repaint)
return;
foreach (var knotInfo in s_Knots)
Draw(knotInfo.knot.Position, knotInfo.knot.Rotation, knotInfo.knotColor, knotInfo.selected, knotInfo.hovered, knotInfo.discColor, k_KnotRotDiscWidthHover, knotInfo.linkedKnot);
}
static void DrawKnotIndices(SelectableKnot knot)
{
if (!SplineHandleSettings.ShowKnotIndices)
return;
var hasLinkedKnots = !(k_KnotBuffer.Count == 1 && k_KnotBuffer.Contains(knot));
if (k_KnotBuffer != null && k_KnotBuffer.Count > 0 && hasLinkedKnots)
{
var stringBuilder = new System.Text.StringBuilder("[");
for (var i = 0; i < k_KnotBuffer.Count; i++)
{
stringBuilder.Append($"({k_KnotBuffer[i].SplineInfo.Index},{k_KnotBuffer[i].KnotIndex})");
if (i != k_KnotBuffer.Count - 1)
stringBuilder.Append(", ");
}
stringBuilder.Append("]");
Handles.Label(knot.Position, stringBuilder.ToString());
}
else
{
Handles.Label(knot.Position, $"[{knot.KnotIndex}]");
}
}
internal static void Draw(SelectableKnot knot, Color knotColor, bool selected, bool hovered)
{
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
var mainKnot = knot;
if(k_KnotBuffer.Count != 1)
{
foreach(var k in k_KnotBuffer)
{
//Main knot is the older one, the one on the spline of lowest range and the knot of lowest index
if(k.SplineInfo.Index < mainKnot.SplineInfo.Index ||
k.SplineInfo.Index == mainKnot.SplineInfo.Index && k.KnotIndex < mainKnot.KnotIndex)
mainKnot = k;
}
}
if(!mainKnot.Equals(knot))
return;
Draw(knot.Position, knot.Rotation, knotColor, selected, hovered, knotColor, k_KnotRotDiscWidthDefault, k_KnotBuffer.Count != 1);
}
internal static void Draw(Vector3 position, Quaternion rotation, Color knotColor, bool selected, bool hovered)
{
Draw(position, rotation, knotColor, selected, hovered, knotColor, k_KnotRotDiscWidthDefault);
}
static void UpdateHandlePoints(float size)
{
var startIndex = 5;
k_HandlePoints[startIndex] = Vector3.forward * k_KnotRotDiscRadius * size;
var r = Vector3.right * SplineHandleUtility.knotDiscRadiusFactorDefault * size;
//The first and last element should be in the middle of the points list to get a better visual
for(int i = 0; i < 9; i++)
{
var index = ( i + startIndex + 1 ) % k_HandlePoints.Length;
var pos = Quaternion.Euler(0, ( 1f - i / 8f ) * 180f, 0) * r;
k_HandlePoints[index] = pos;
if(index == k_HandlePoints.Length - 1)
{
startIndex += 1;
k_HandlePoints[0] = pos;
}
}
}
internal static void DrawInformativeKnot(SelectableKnot knot, float sizeFactor = 0.5f)
{
if (Event.current.type != EventType.Repaint)
return;
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
var drawLinkedKnotHandle = k_KnotBuffer.Count != 1;
if(drawLinkedKnotHandle)
{
foreach(var k in k_KnotBuffer)
{
//If the current knot in not selected but other linked knots are, skip rendering
if(SplineSelection.Contains(k))
return;
}
}
DrawInformativeKnotVisual(knot, SplineHandleUtility.lineColor, sizeFactor);
}
static void DrawInformativeKnotVisual(SelectableKnot knot, Color knotColor, float sizeFactor = 0.5f)
{
var position = knot.Position;
var size = HandleUtility.GetHandleSize(position);
using(new Handles.DrawingScope(knotColor, Matrix4x4.TRS(position, knot.Rotation, Vector3.one)))
{
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, size * SplineHandleUtility.knotDiscRadiusFactorSelected * sizeFactor);
}
}
static void Draw(Vector3 position, Quaternion rotation, Color knotColor, bool selected, bool hovered, Color discColor, float rotationDiscWidth, bool linkedKnots = false)
{
var size = HandleUtility.GetHandleSize(position);
using (new ZTestScope(CompareFunction.Less))
{
using (new Handles.DrawingScope(knotColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
DrawKnotShape(size, selected, linkedKnots);
if (hovered)
{
using (new Handles.DrawingScope(discColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * size, rotationDiscWidth);
}
}
using (new ZTestScope(CompareFunction.Greater))
{
var newKnotColor = new Color(knotColor.r, knotColor.g, knotColor.b, knotColor.a * k_ColorAlphaFactor);
using (new Handles.DrawingScope(newKnotColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
DrawKnotShape(size, selected, linkedKnots);
if (hovered)
{
var newDiscColor = new Color(discColor.r, discColor.g,
discColor.b, discColor.a * k_ColorAlphaFactor);
using (new Handles.DrawingScope(newDiscColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * size, rotationDiscWidth);
}
}
}
static void DrawKnotShape(float size, bool selected, bool linkedKnots)
{
if (!linkedKnots)
{
UpdateHandlePoints(size);
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_KnotHandleWidth, k_HandlePoints);
if (selected)
Handles.DrawAAConvexPolygon(k_HandlePoints);
}
else
{
// Knot disc
if (selected)
{
var radius = selected ? SplineHandleUtility.knotDiscRadiusFactorSelected : SplineHandleUtility.knotDiscRadiusFactorHover;
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, radius * size);
}
else
Handles.DrawWireDisc(Vector3.zero, Vector3.up, SplineHandleUtility.knotDiscRadiusFactorDefault * size, SplineHandleUtility.handleWidth * SplineHandleUtility.aliasedLineSizeMultiplier);
}
Handles.DrawAAPolyLine(Vector3.zero, Vector3.up * 2f * SplineHandleUtility.sizeFactor * size);
}
}
}

11
Packages/com.unity.splines/Editor/Controls/KnotHandles.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b96c3a8372a8f9945bc2e2b906ac79a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

228
Packages/com.unity.splines/Editor/Controls/SplineElementRectSelector.cs

@ -0,0 +1,228 @@
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
class SplineElementRectSelector
{
enum Mode
{
None,
Replace,
Add,
Subtract
}
static class Styles
{
public static readonly GUIStyle selectionRect = GUI.skin.FindStyle("selectionRect");
}
Rect m_Rect;
Vector2 m_StartPos;
Mode m_Mode;
Mode m_InitialMode;
static readonly HashSet<ISelectableElement> s_SplineElementsCompareSet = new HashSet<ISelectableElement>();
static readonly List<ISelectableElement> s_SplineElementsBuffer = new List<ISelectableElement>();
static readonly HashSet<ISelectableElement> s_PreRectSelectionElements = new HashSet<ISelectableElement>();
public void OnGUI(IReadOnlyList<SplineInfo> splines)
{
int id = GUIUtility.GetControlID(FocusType.Passive);
Event evt = Event.current;
switch (evt.GetTypeForControl(id))
{
case EventType.Layout:
case EventType.MouseMove:
HandleUtility.AddDefaultControl(id);
if (m_Mode != Mode.None)
{
// If we've started rect select in Add or Subtract modes, then if we were in a Replace
// mode just before (i.e. the shift or action has been released temporarily),
// we need to bring back the pre rect selection elements into current selection.
if (m_InitialMode != Mode.Replace && RefreshSelectionMode())
{
SplineSelection.Clear();
s_SplineElementsCompareSet.Clear();
if (m_Mode != Mode.Replace)
{
foreach (var element in s_PreRectSelectionElements)
SplineSelection.Add(element);
}
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
UpdateSelection(m_Rect, splines);
}
}
break;
case EventType.Repaint:
if (GUIUtility.hotControl == id && m_Rect.size != Vector2.zero)
{
Handles.BeginGUI();
Styles.selectionRect.Draw(m_Rect, GUIContent.none, false, false, false, false);
Handles.EndGUI();
}
break;
case EventType.MouseDown:
if (SplineHandles.ViewToolActive())
return;
if (HandleUtility.nearestControl == id && evt.button == 0)
{
m_StartPos = evt.mousePosition;
m_Rect = new Rect(Vector3.zero, Vector2.zero);
BeginSelection(splines);
GUIUtility.hotControl = id;
evt.Use();
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
evt.Use();
UpdateSelection(m_Rect, splines);
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
GUIUtility.hotControl = 0;
evt.Use();
EndSelection(m_Rect, splines);
}
break;
}
}
void BeginSelection(IReadOnlyList<SplineInfo> splines)
{
RefreshSelectionMode();
m_InitialMode = m_Mode;
s_SplineElementsCompareSet.Clear();
s_SplineElementsBuffer.Clear();
if (m_Mode == Mode.Replace)
{
SplineSelection.Clear();
s_PreRectSelectionElements.Clear();
}
else
SplineSelection.GetElements(splines, s_PreRectSelectionElements);
}
void UpdateSelection(Rect rect, IReadOnlyList<SplineInfo> splines)
{
//Get all elements in rect
s_SplineElementsBuffer.Clear();
for (int i = 0; i < splines.Count; ++i)
{
var splineData = splines[i];
for (int j = 0; j < splineData.Spline.Count; ++j)
if(!SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(splineData))
GetElementSelection(rect, splineData, j, s_SplineElementsBuffer);
}
foreach (var splineElement in s_SplineElementsBuffer)
{
//Compare current frame buffer with last frame's to find new additions/removals
var wasInRectLastFrame = s_SplineElementsCompareSet.Remove(splineElement);
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
{
var canAdd = m_Mode == Mode.Replace ? true : !s_PreRectSelectionElements.Contains(splineElement);
if (!wasInRectLastFrame && canAdd)
SplineSelection.Add(splineElement);
}
else if (m_Mode == Mode.Subtract && !wasInRectLastFrame)
{
SplineSelection.Remove(splineElement);
}
}
//Remaining spline elements from last frame are removed from selection (or added if mode is subtract)
foreach (var splineElement in s_SplineElementsCompareSet)
{
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
{
// If we're in Add mode, don't remove elements that were in select prior to rect selection
if (m_Mode == Mode.Add && s_PreRectSelectionElements.Contains(splineElement))
continue;
SplineSelection.Remove(splineElement);
}
else if (m_Mode == Mode.Subtract && s_PreRectSelectionElements.Contains(splineElement))
SplineSelection.Add(splineElement);
}
//Move current elements buffer to hash set for next frame compare
s_SplineElementsCompareSet.Clear();
foreach (var splineElement in s_SplineElementsBuffer)
s_SplineElementsCompareSet.Add(splineElement);
}
bool RefreshSelectionMode()
{
var modeBefore = m_Mode;
if (Event.current.shift)
m_Mode = Mode.Add;
else if (EditorGUI.actionKey)
m_Mode = Mode.Subtract;
else
m_Mode = Mode.Replace;
// Return true if the mode has changed
return m_Mode != modeBefore;
}
void GetElementSelection(Rect rect, SplineInfo splineInfo, int index, List<ISelectableElement> results)
{
var knot = splineInfo.Spline[index];
var localToWorld = splineInfo.LocalToWorld;
var worldKnot = knot.Transform(localToWorld);
Vector3 screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position);
if (screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(new SelectableKnot(splineInfo, index));
var tangentIn = new SelectableTangent(splineInfo, index, BezierTangent.In);
if (SplineSelectionUtility.IsSelectable(tangentIn))
{
screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position + math.rotate(worldKnot.Rotation, worldKnot.TangentIn));
if (screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(tangentIn);
}
var tangentOut = new SelectableTangent(splineInfo, index, BezierTangent.Out);
if (SplineSelectionUtility.IsSelectable(tangentOut))
{
screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position + math.rotate(worldKnot.Rotation, worldKnot.TangentOut));
if (screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(tangentOut);
}
}
void EndSelection(Rect rect, IReadOnlyList<SplineInfo> splines)
{
m_Mode = m_InitialMode = Mode.None;
}
static Rect GetRectFromPoints(Vector2 a, Vector2 b)
{
Vector2 min = new Vector2(Mathf.Min(a.x, b.x), Mathf.Min(a.y, b.y));
Vector2 max = new Vector2(Mathf.Max(a.x, b.x), Mathf.Max(a.y, b.y));
return new Rect(min, max - min);
}
}
}

11
Packages/com.unity.splines/Editor/Controls/SplineElementRectSelector.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d68b08563e248f4a02b78fb710d36bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

68
Packages/com.unity.splines/Editor/Controls/SplineHandleSettings.cs

@ -0,0 +1,68 @@
using System;
using UnityEngine;
using UnityEditor.SettingsManagement;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
static class SplineHandleSettings
{
[UserSetting]
static readonly Pref<bool> s_FlowDirectionEnabled = new Pref<bool>("Handles.FlowDirectionEnabled", true);
[UserSetting]
static readonly Pref<bool> s_ShowAllTangents = new Pref<bool>("Handles.ShowAllTangents", true);
static readonly Pref<bool> s_ShowKnotIndices = new Pref<bool>("Handles.ShowKnotIndices", false);
[UserSetting]
static UserSetting<bool> s_ShowMesh = new UserSetting<bool>(PathSettings.instance,"Handles.Debug.ShowMesh", false, SettingsScope.User);
[UserSetting]
static UserSetting<Color> s_MeshColor = new UserSetting<Color>(PathSettings.instance, "Handles.Debug.MeshColor", Color.white, SettingsScope.User);
[UserSetting]
static UserSetting<float> s_MeshSize = new UserSetting<float>(PathSettings.instance, "Handles.Debug.MeshSize", 0.1f, SettingsScope.User);
[UserSetting]
static UserSetting<int> s_MeshResolution = new UserSetting<int>(PathSettings.instance, "Handles.Debug.MeshResolution", SplineUtility.DrawResolutionDefault, SettingsScope.User);
[UserSettingBlock("Spline Mesh")]
static void HandleDebugPreferences(string searchContext)
{
EditorGUI.BeginChangeCheck();
s_MeshColor.value = SettingsGUILayout.SettingsColorField("Color", s_MeshColor, searchContext);
s_MeshSize.value = SettingsGUILayout.SettingsSlider("Size", s_MeshSize, 0.01f, 1f, searchContext);
s_MeshResolution.value = SettingsGUILayout.SettingsSlider("Resolution", s_MeshResolution, 4, 100, searchContext);
if(EditorGUI.EndChangeCheck())
SceneView.RepaintAll();
}
public static bool FlowDirectionEnabled
{
get => s_FlowDirectionEnabled;
set => s_FlowDirectionEnabled.SetValue(value);
}
public static bool ShowAllTangents
{
get => s_ShowAllTangents;
set => s_ShowAllTangents.SetValue(value);
}
public static bool ShowKnotIndices
{
get => s_ShowKnotIndices;
set => s_ShowKnotIndices.SetValue(value);
}
public static bool ShowMesh
{
get => s_ShowMesh;
set => s_ShowMesh.SetValue(value);
}
public static Color SplineMeshColor => s_MeshColor;
public static float SplineMeshSize => s_MeshSize;
public static int SplineMeshResolution => s_MeshResolution;
}
}

11
Packages/com.unity.splines/Editor/Controls/SplineHandleSettings.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bff491709f26ca4d924ad2c5f505fb0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

464
Packages/com.unity.splines/Editor/Controls/SplineHandles.cs

@ -0,0 +1,464 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
/// <summary>
/// This class provides the ability to draw a handle for a spline.
/// </summary>
public static class SplineHandles
{
/// <summary>
/// The scope used to draw a spline. This is managing several purposes when using SplineHandles.DrawSomething().
/// This ensure selection is working properly, and that hovering an element is highlighting the correct related
/// elements (for instance hovering a tangent highlights the opposite one when needed and the knot as well).
/// </summary>
public class SplineHandleScope : IDisposable
{
int m_NearestControl;
/// <summary>
/// Defines a new scope to draw spline elements in.
/// </summary>
public SplineHandleScope()
{
m_NearestControl = HandleUtility.nearestControl;
Clear();
SplineHandleUtility.minElementId = GUIUtility.GetControlID(FocusType.Passive);
}
/// <summary>
/// Called automatically when the `SplineHandleScope` is disposed.
/// </summary>
public void Dispose()
{
SplineHandleUtility.maxElementId = GUIUtility.GetControlID(FocusType.Passive);
var evtType = Event.current.type;
if ( (evtType == EventType.MouseMove || evtType == EventType.Layout)
&& HandleUtility.nearestControl == m_NearestControl)
SplineHandleUtility.ResetLastHoveredElement();
}
}
/// <summary>
/// The color of sections of spline curve handles that are behind objects in the Scene view.
/// </summary>
public static Color lineBehindColor => SplineHandleUtility.lineBehindColor;
/// <summary>
/// The color of sections of spline curves handles that are in front of objects in the Scene view.
/// </summary>
public static Color lineColor => SplineHandleUtility.lineColor;
/// <summary>
/// The color of tangent handles for a spline.
/// </summary>
public static Color tangentColor => SplineHandleUtility.tangentColor;
/// <summary>
/// The distance to pick a spline knot, tangent, or curve handle at.
/// </summary>
public static float pickingDistance => SplineHandleUtility.pickingDistance;
static List<int> s_ControlIDs = new();
static List<int> s_CurveIDs = new();
static readonly List<SelectableKnot> k_KnotBuffer = new ();
static Dictionary<SelectableKnot, int> s_KnotsIDs = new ();
// todo Tools.viewToolActive should be handling the modifier check, but 2022.2 broke this
internal static bool ViewToolActive()
{
return Tools.viewToolActive || Tools.current == Tool.View || (Event.current.modifiers & EventModifiers.Alt) == EventModifiers.Alt;
}
static void Clear()
{
s_CurveIDs.Clear();
s_KnotsIDs.Clear();
}
/// <summary>
/// Creates handles for a set of splines. These handles display the knots, tangents, and segments of a spline.
/// These handles support selection and the direct manipulation of spline elements.
/// </summary>
/// <param name="splines">The set of splines to draw handles for.</param>
public static void DoHandles(IReadOnlyList<SplineInfo> splines)
{
Profiler.BeginSample("SplineHandles.DoHandles");
using (new SplineHandleScope())
{
// Drawing done in two separate passes to make sure the curves are drawn behind the spline elements.
// Draw the curves.
for (int i = 0; i < splines.Count; ++i)
{
DoSegmentsHandles(splines[i]);
}
DoKnotsAndTangentsHandles(splines);
}
Profiler.EndSample();
}
internal static bool IsCurveId(int id)
{
return s_CurveIDs.Contains(id);
}
/// <summary>
/// Creates knot and tangent handles for a spline. Call `DoKnotsAndTangentsHandles` in a `SplineHandleScope`.
/// This method is used internally by `DoHandles`.
/// </summary>
/// <param name="spline">The spline to create knot and tangent handles for.</param>
public static void DoKnotsAndTangentsHandles(SplineInfo spline)
{
SplineHandleUtility.UpdateElementColors();
KnotHandles.ClearVisibleKnots();
// Draw the spline elements.
DrawSplineElements(spline);
//Drawing knots on top of all other elements and above other splines
KnotHandles.DrawVisibleKnots();
}
/// <summary>
/// Creates knot and tangent handles for multiple splines. Call `DoKnotsAndTangentsHandles` in a `SplineHandleScope`.
/// This method is used internally by `DoHandles`.
/// </summary>
/// <param name="splines">The splines to create knot and tangent handles for.</param>
public static void DoKnotsAndTangentsHandles(IReadOnlyList<SplineInfo> splines)
{
SplineHandleUtility.UpdateElementColors();
KnotHandles.ClearVisibleKnots();
// Draw the spline elements.
for (int i = 0; i < splines.Count; ++i)
DrawSplineElements(splines[i]);
//Drawing knots on top of all other elements and above other splines
KnotHandles.DrawVisibleKnots();
}
/// <summary>
/// Creates segment handles for a spline. Call `DoCurvesHandles` in a `SplineHandleScope`.
/// This method is used internally by `DrawHandles`.
/// </summary>
/// <param name="splineInfo">The splineInfo of the spline to draw knots and tangents for.</param>
public static void DoSegmentsHandles(SplineInfo splineInfo)
{
var spline = splineInfo.Spline;
if (spline == null || spline.Count < 2)
return;
var localToWorld = splineInfo.LocalToWorld;
// If the spline isn't closed, skip the last index of the spline
int lastIndex = spline.Closed ? spline.Count - 1 : spline.Count - 2;
if (SplineHandleSettings.ShowMesh)
{
using (var nativeSpline = new NativeSpline(spline, localToWorld))
using (var mesh = new SplineMeshHandle<NativeSpline>())
using (new ZTestScope(UnityEngine.Rendering.CompareFunction.Less))
{
mesh.Do(nativeSpline, SplineHandleSettings.SplineMeshSize, SplineHandleSettings.SplineMeshColor, SplineHandleSettings.SplineMeshResolution);
}
}
s_ControlIDs.Clear();
for (int idIndex = 0; idIndex < lastIndex + 1; ++idIndex)
{
var id = GUIUtility.GetControlID(FocusType.Passive);
s_ControlIDs.Add(id);
s_CurveIDs.Add(id);
}
var drawHandlesAsActive = !SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(splineInfo);
//Draw all the curves at once
SplineCacheUtility.GetCachedPositions(spline, out var positions);
using (new Handles.DrawingScope(SplineHandleUtility.lineColor, localToWorld))
{
using (new ZTestScope(CompareFunction.Less))
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, 4f, positions);
}
using (new Handles.DrawingScope(SplineHandleUtility.lineBehindColor, localToWorld))
{
using (new ZTestScope(CompareFunction.Greater))
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, 4f, positions);
}
if (drawHandlesAsActive)
{
for (int curveIndex = 0; curveIndex < lastIndex + 1; ++curveIndex)
{
if (SplineHandleSettings.FlowDirectionEnabled && Event.current.type == EventType.Repaint)
{
var curve = spline.GetCurve(curveIndex).Transform(localToWorld);
CurveHandles.DrawFlow(curve, spline, curveIndex);
}
}
}
for (int curveIndex = 0; curveIndex < lastIndex + 1; ++curveIndex)
{
CurveHandles.DrawWithHighlight(
s_ControlIDs[curveIndex],
spline,
curveIndex,
localToWorld,
new SelectableKnot(splineInfo, curveIndex),
new SelectableKnot(splineInfo, SplineUtility.NextIndex(curveIndex, spline.Count, spline.Closed)),
drawHandlesAsActive);
}
SplineHandleUtility.canDrawOnCurves = true;
}
static void DrawSplineElements(SplineInfo splineInfo)
{
var spline = splineInfo.Spline;
var drawHandlesAsActive = !SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(splineInfo);
if (drawHandlesAsActive)
{
for (int knotIndex = 0; knotIndex < spline.Count; ++knotIndex)
DrawKnotWithTangentsHandles_Internal(new SelectableKnot(splineInfo, knotIndex));
}
else
{
for (int knotIndex = 0; knotIndex < spline.Count; ++knotIndex)
KnotHandles.DrawInformativeKnot(new SelectableKnot(splineInfo, knotIndex));
}
}
/// <summary>
/// Creates handles for a knot and its tangents if those tangents are modifiable.
/// These handles support the selection and direct manipulation of spline elements.
/// Call `DoKnotWithTangentsHandles` in a `SplineHandleScope`.
/// </summary>
/// <param name="knot">The knot to draw handles for.</param>
public static void DoKnotWithTangentsHandles(SelectableKnot knot)
{
KnotHandles.ClearVisibleKnots();
DrawKnotWithTangentsHandles_Internal(knot);
KnotHandles.DrawVisibleKnots();
}
static void DrawKnotWithTangentsHandles_Internal(SelectableKnot knot)
{
var splineInfo = knot.SplineInfo;
if (SplineUtility.AreTangentsModifiable(splineInfo.Spline.GetTangentMode(knot.KnotIndex)))
DoTangentsHandles(knot);
DrawKnotHandles_Internal(knot);
}
/// <summary>
/// Create handles for a knot. These handles the support selection and direct manipulation of spline elements.
/// Call `DoKnotHandles` in a `SplineHandleScope`.
/// </summary>
/// <param name="knot">The knot to draw handles for.</param>
public static void DoKnotHandles(SelectableKnot knot)
{
KnotHandles.ClearVisibleKnots();
DrawKnotHandles_Internal(knot);
KnotHandles.DrawVisibleKnots();
}
static void DrawKnotHandles_Internal(SelectableKnot knot)
{
var id = GetKnotID(knot);
SelectionHandle(id, knot);
KnotHandles.Draw(id, knot);
}
/// <summary>
/// Create handles for a knot's tangents if those tangents are modifiable. `DoTangentsHandles` does not create handles for the knot.
/// These handles support the selection and direct manipulation of the spline elements.
/// Call `DoTangentsHandles` in a `SplineHandleScope`.
/// </summary>
/// <param name="knot">The knot to draw tangent handles for.</param>
public static void DoTangentsHandles(SelectableKnot knot)
{
if(!knot.IsValid())
return;
var splineInfo = knot.SplineInfo;
var spline = splineInfo.Spline;
var knotIndex = knot.KnotIndex;
var tangentIn = new SelectableTangent(splineInfo, knotIndex, BezierTangent.In);
var tangentOut = new SelectableTangent(splineInfo, knotIndex, BezierTangent.Out);
var controlIdIn = GUIUtility.GetControlID(FocusType.Passive);
var controlIdOut = GUIUtility.GetControlID(FocusType.Passive);
// Tangent In
if (GUIUtility.hotControl == controlIdIn || SplineHandleUtility.ShouldShowTangent(tangentIn) && (spline.Closed || knotIndex != 0))
{
SelectionHandle(controlIdIn, tangentIn);
TangentHandles.Draw(controlIdIn, tangentIn);
}
// Tangent Out
if (GUIUtility.hotControl == controlIdOut || SplineHandleUtility.ShouldShowTangent(tangentOut) && (spline.Closed || knotIndex + 1 != spline.Count))
{
SelectionHandle(controlIdOut, tangentOut);
TangentHandles.Draw(controlIdOut, tangentOut);
}
}
static int GetKnotID(SelectableKnot knot)
{
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
//If a linked knot as already been assigned, return the same id
if (s_KnotsIDs.ContainsKey(k_KnotBuffer[0]))
return s_KnotsIDs[k_KnotBuffer[0]];
//else compute a new id and record it
var id = GUIUtility.GetControlID(FocusType.Passive);
s_KnotsIDs.Add(k_KnotBuffer[0], id);
return id;
}
static void SelectionHandle<T>(int id, T element)
where T : struct, ISelectableElement
{
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(id);
switch (eventType)
{
case EventType.Layout:
case EventType.MouseMove:
if (!ViewToolActive())
{
HandleUtility.AddControl(id, SplineHandleUtility.DistanceToCircle(element.Position, SplineHandleUtility.pickingDistance));
if (GUIUtility.hotControl == 0 && HandleUtility.nearestControl == id)
SplineHandleUtility.SetLastHoveredElement(element, id);
}
break;
case EventType.MouseDown:
if (HandleUtility.nearestControl == id && evt.button == 0)
{
GUIUtility.hotControl = id;
evt.Use();
DirectManipulation.BeginDrag(element.Position, EditorSplineUtility.GetElementRotation(element));
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
EditorSplineUtility.RecordObject(element.SplineInfo, "Move Knot");
var pos = TransformOperation.ApplySmartRounding(DirectManipulation.UpdateDrag(id));
if (element is SelectableTangent tangent)
EditorSplineUtility.ApplyPositionToTangent(tangent, pos);
else
element.Position = pos;
GUI.changed = true;
evt.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id && evt.button == 0)
{
if (!DirectManipulation.IsDragging)
SplineSelectionUtility.HandleSelection(element);
DirectManipulation.EndDrag();
GUI.changed = true;
evt.Use();
GUIUtility.hotControl = 0;
}
break;
case EventType.Repaint:
DirectManipulation.DrawHandles(id, element.Position);
break;
}
}
/// <summary>
/// Draws a handle for a spline.
/// </summary>
/// <param name="spline">The target spline.</param>
/// <typeparam name="T">A type implementing ISpline.</typeparam>
public static void DoSpline<T>(T spline) where T : ISpline => DoSpline(-1, spline);
/// <summary>
/// Draws a handle for a spline.
/// </summary>
/// <param name="controlID">The spline mesh controlID.</param>
/// <param name="spline">The target spline.</param>
/// <typeparam name="T">A type implementing ISpline.</typeparam>
public static void DoSpline<T>(int controlID, T spline) where T : ISpline
{
for(int i = 0; i < spline.GetCurveCount(); ++i)
CurveHandles.Draw(controlID, spline.GetCurve(i));
}
/// <summary>
/// Draws a handle for a <see cref="BezierCurve"/>.
/// </summary>
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
public static void DoCurve(BezierCurve curve) => CurveHandles.Draw(-1, curve);
/// <summary>
/// Draws a handle for a <see cref="BezierCurve"/>.
/// </summary>
/// <param name="controlID">The spline mesh controlID.</param>
/// <param name="curve">The <see cref="BezierCurve"/> to create handles for.</param>
public static void DoCurve(int controlID, BezierCurve curve) => CurveHandles.Draw(controlID, curve);
/// <summary>
/// Draws handles for a knot. These handles are drawn only during repaint events and not on selection.
/// </summary>
/// <param name="knot">The <see cref="SelectableKnot"/> to create handles for.</param>
/// <param name="selected">Set to true to draw the knot handle as a selected element.</param>
/// <param name="hovered">Set to true to draw the knot handle as a hovered element.</param>
public static void DrawKnot(SelectableKnot knot, bool selected = false, bool hovered = false)
=> DrawKnot(-1, knot, selected, hovered);
/// <summary>
/// Draws handles for a knot. These handles are drawn only during repaint events and not on selection.
/// </summary>
/// <param name="controlID">The controlID of the tangent to create handles for.</param>
/// <param name="knot">The <see cref="SelectableKnot"/> to create handles for.</param>
/// <param name="selected">Set to true to draw the knot handle as a selected element.</param>
/// <param name="hovered">Set to true to draw the knot handle as a hovered element.</param>
public static void DrawKnot(int controlID, SelectableKnot knot, bool selected = false, bool hovered = false)
{
KnotHandles.Do(controlID, knot, selected, hovered);
}
/// <summary>
/// Draws handles for a tangent. These handles are drawn only during repaint events and not on selection.
/// </summary>
/// <param name="tangent">The <see cref="SelectableTangent"/> to create handles for.</param>
/// <param name="selected">Set to true to draw the tangent handle as a selected element.</param>
/// <param name="hovered">Set to true to draw the tangent handle as a hovered element.</param>
public static void DrawTangent(SelectableTangent tangent, bool selected = false, bool hovered = false) => DrawTangent(-1, tangent, selected, hovered);
/// <summary>
/// Draws handles for a tangent. These handles are drawn only during repaint events and not on selection.
/// </summary>
/// <param name="controlID">The controlID of the tangent to create handles for.</param>
/// <param name="tangent">The <see cref="SelectableTangent"/> to create handles for.</param>
/// <param name="selected">Set to true to draw the tangent handle as a selected element.</param>
/// <param name="hovered">Set to true to draw the tangent handle as a hovered element.</param>
public static void DrawTangent(int controlID, SelectableTangent tangent, bool selected = false, bool hovered = false)
{
TangentHandles.Do(controlID, tangent, selected, hovered);
}
}
}

11
Packages/com.unity.splines/Editor/Controls/SplineHandles.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fa11edcb768491b4b8b90ccf074e663f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

188
Packages/com.unity.splines/Editor/Controls/SplineMeshHandle.cs

@ -0,0 +1,188 @@
using System;
using UnityEngine;
using UnityEngine.Splines;
using Object = UnityEngine.Object;
namespace UnityEditor.Splines
{
/// <summary>
/// Creates a cylinder mesh along a spline.
/// </summary>
/// <typeparam name="T">The type of ISpline.</typeparam>
public class SplineMeshHandle<T> : IDisposable where T : ISpline
{
class SplineMeshDrawingScope : IDisposable
{
Material m_Material;
int m_HandleZTestId;
int m_BlendSrcModeId;
int m_BlendDstModeId;
float m_PreviousZTest;
int m_PreviousBlendSrcMode;
int m_PreviousBlendDstMode;
public SplineMeshDrawingScope(Material material, Color color)
{
Shader.SetGlobalColor("_HandleColor", color);
Shader.SetGlobalFloat("_HandleSize", 1f);
Shader.SetGlobalMatrix("_ObjectToWorld", Handles.matrix);
if (material == null)
{
m_Material = HandleUtility.handleMaterial;
m_HandleZTestId = Shader.PropertyToID("_HandleZTest");
m_BlendSrcModeId = Shader.PropertyToID("_BlendSrcMode");
m_BlendDstModeId = Shader.PropertyToID("_BlendDstMode");
m_PreviousZTest = m_Material.GetFloat(m_HandleZTestId);
m_PreviousBlendSrcMode = m_Material.GetInt(m_BlendSrcModeId);
m_PreviousBlendDstMode = m_Material.GetInt(m_BlendDstModeId);
m_Material.SetFloat(m_HandleZTestId, (float)Handles.zTest);
m_Material.SetInt(m_BlendSrcModeId, (int)UnityEngine.Rendering.BlendMode.One);
m_Material.SetInt(m_BlendDstModeId, (int)UnityEngine.Rendering.BlendMode.One);
m_Material.SetPass(0);
}
else
material.SetPass(0);
}
public void Dispose()
{
if (m_Material != null)
{
m_Material.SetFloat(m_HandleZTestId, m_PreviousZTest);
m_Material.SetInt(m_BlendSrcModeId, m_PreviousBlendSrcMode);
m_Material.SetInt(m_BlendDstModeId, m_PreviousBlendDstMode);
}
}
}
Mesh m_Mesh;
Material m_Material;
/// <summary>
/// Creates a new mesh handle. This class implements IDisposable to clean up allocated mesh resources. Call
/// <see cref="Dispose"/> when you are finished with the instance.
/// </summary>
public SplineMeshHandle()
{
m_Mesh = new Mesh()
{
hideFlags = HideFlags.HideAndDontSave
};
m_Material = null;
}
/// <summary>
/// Create a new mesh handle. This class implements IDisposable to clean up allocated mesh resources. Call
/// <see cref="Dispose"/> when you are finished with the instance.
/// </summary>
/// <param name="material">The material to render the cylinder mesh with.</param>
public SplineMeshHandle(Material material) : base()
{
m_Material = material;
}
/// <summary>
/// The material to render this mesh with. If null, a default material is used.
/// </summary>
public Material material
{
get => m_Material;
set => m_Material = value;
}
/// <summary>
/// Draws a 3D mesh from a spline.
/// </summary>
/// <param name="spline">The target spline.</param>
/// <param name="size">The width to use for the spline mesh.</param>
/// <param name="color">The color to use for the spline mesh in normal mode.</param>
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
public void Do(T spline, float size, Color color, int resolution = SplineUtility.DrawResolutionDefault)
{
if(Event.current.type != EventType.Repaint)
return;
Do(-1, spline, size, color, resolution);
}
/// <summary>
/// Draws a 3D mesh handle from a spline.
/// </summary>
/// <param name="controlID">The spline mesh controlID.</param>
/// <param name="spline">The target spline.</param>
/// <param name="size">The width to use for the spline mesh.</param>
/// <param name="color">The color to use for the spline mesh in normal mode.</param>
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
public void Do(int controlID, T spline, float size, Color color, int resolution = SplineUtility.DrawResolutionDefault)
{
using (new Handles.DrawingScope(color))
Do(controlID, spline, size, resolution);
}
/// <summary>
/// Draws a 3D mesh from a spline.
/// </summary>
/// <param name="spline">The target spline.</param>
/// <param name="size">The width to use for the spline mesh.</param>
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
public void Do(T spline, float size, int resolution = SplineUtility.DrawResolutionDefault)
{
if (Event.current.type != EventType.Repaint)
return;
Do(-1, spline, size, resolution);
}
/// <summary>
/// Draws a 3D mesh handle from a spline.
/// </summary>
/// <param name="controlID">The spline mesh controlID.</param>
/// <param name="spline">The target spline.</param>
/// <param name="size">The width to use for the spline mesh.</param>
/// <param name="resolution">The resolution to use for the mesh, defines the number of segments per unit
/// with default value of <see cref="SplineUtility.DrawResolutionDefault"/>.</param>
public void Do(int controlID, T spline, float size, int resolution = SplineUtility.DrawResolutionDefault)
{
var evt = Event.current;
switch (evt.type)
{
case EventType.MouseMove:
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
HandleUtility.AddControl(controlID, SplineUtility.GetNearestPoint(spline, ray, out _, out _));
break;
case EventType.Repaint:
var segments = SplineUtility.GetSubdivisionCount(spline.GetLength(), resolution);
SplineMesh.Extrude(spline, m_Mesh, size, 8, segments, !spline.Closed);
var color = GUIUtility.hotControl == controlID
? Handles.selectedColor
: HandleUtility.nearestControl == controlID
? Handles.preselectionColor
: Handles.color;
using (new SplineMeshDrawingScope(m_Material, color))
Graphics.DrawMeshNow(m_Mesh, Handles.matrix);
break;
}
}
/// <summary>
/// Destroys the 3D mesh.
/// </summary>
public void Dispose() => Object.DestroyImmediate(m_Mesh);
}
}

3
Packages/com.unity.splines/Editor/Controls/SplineMeshHandle.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b7822ae247afa5945befac0bc63be0bb
timeCreated: 1654094147

238
Packages/com.unity.splines/Editor/Controls/TangentHandles.cs

@ -0,0 +1,238 @@
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
static class TangentHandles
{
const float k_ColorAlphaFactor = 0.3f;
const float k_TangentLineWidthDefault = 2f;
const float k_TangentLineWidthHover = 3.5f;
const float k_TangentLineWidthSelected = 4.5f;
const float k_TangentStartOffsetFromKnot = 0.22f;
const float k_TangentEndOffsetFromHandle = 0.11f;
const float k_TangentHandleWidth = 2f;
const float k_TangentRotWidthDefault = 1.5f;
const float k_TangentRotDiscWidth = 3f;
internal static void Do(int controlId, SelectableTangent tangent, bool selected = false, bool hovered = false)
{
var owner = tangent.Owner;
Draw(
controlId,
tangent.Position,
EditorSplineUtility.GetElementRotation(math.length(tangent.LocalPosition) > 0 ? (ISelectableElement)tangent : tangent.Owner),
owner.Position,
selected,
false,
hovered,
false,
owner.Mode,
true);
}
internal static void DrawInformativeTangent(SelectableTangent tangent, bool active = true)
{
DrawInformativeTangent(tangent.Position, tangent.Owner.Position, active);
}
internal static void DrawInformativeTangent(Vector3 position, Vector3 knotPosition, bool active = true)
{
if (Event.current.type != EventType.Repaint)
return;
var tangentColor = SplineHandleUtility.elementColor;
if (!active)
tangentColor = Handles.secondaryColor;
var tangentArmColor = tangentColor == SplineHandleUtility.elementColor
? SplineHandleUtility.tangentColor
: tangentColor;
using (new ColorScope(tangentArmColor))
{
var toTangent = position - knotPosition;
var toTangentNorm = math.normalize(toTangent);
var length = math.length(toTangent);
var knotHandleOffset = HandleUtility.GetHandleSize(knotPosition) * k_TangentStartOffsetFromKnot;
length = Mathf.Max(0f, length - knotHandleOffset);
knotPosition += (Vector3)toTangentNorm * knotHandleOffset;
SplineHandleUtility.DrawLineWithWidth(knotPosition, knotPosition + (Vector3)toTangentNorm * length, k_TangentLineWidthDefault);
}
}
internal static void Draw(Vector3 position, Vector3 knotPosition, float3 normal, bool active = true)
{
var knotToTangentDirection = position - knotPosition;
var rotation = quaternion.LookRotationSafe(knotToTangentDirection, normal);
Draw(-1, position, rotation, knotPosition, false, false, false, TangentMode.Broken, active);
}
internal static void Draw(int controlId, SelectableTangent tangent, bool active = true)
{
var (pos, rot) = SplineCacheUtility.GetTangentPositionAndRotation(tangent);
var owner = tangent.Owner;
Draw(
controlId,
pos,
rot,
owner.Position,
SplineSelection.Contains(tangent),
SplineSelection.Contains(tangent.OppositeTangent),
SplineHandleUtility.IsLastHoveredElement(tangent.OppositeTangent),
owner.Mode,
active);
}
static void Draw(int controlId, Vector3 position, Quaternion rotation, Vector3 knotPosition, bool selected, bool oppositeSelected, bool oppositeHovered, TangentMode mode, bool active)
{
var hovered = SplineHandleUtility.IsHoverAvailableForSplineElement() && SplineHandleUtility.IsElementHovered(controlId);
Draw(controlId, position, rotation, knotPosition, selected, oppositeSelected, hovered, oppositeHovered, mode, active);
}
static void Draw(int controlId, Vector3 position, Quaternion rotation, Vector3 knotPosition, bool selected, bool oppositeSelected, bool hovered, bool oppositeHovered, TangentMode mode, bool active)
{
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
return;
var size = HandleUtility.GetHandleSize(position);
var tangentColor = SplineHandleUtility.elementColor;
if (hovered)
tangentColor = SplineHandleUtility.elementPreselectionColor;
else if (selected)
tangentColor = SplineHandleUtility.elementSelectionColor;
if (!active)
tangentColor = Handles.secondaryColor;
var tangentArmColor = tangentColor == SplineHandleUtility.elementColor ?
SplineHandleUtility.tangentColor :
tangentColor;
if (mode == TangentMode.Mirrored)
{
if(oppositeHovered)
tangentArmColor = SplineHandleUtility.elementPreselectionColor;
else if(tangentArmColor == SplineHandleUtility.tangentColor && oppositeSelected)
tangentArmColor =SplineHandleUtility.elementSelectionColor;
}
var rotationDiscWidth = k_TangentRotWidthDefault;
if (hovered)
rotationDiscWidth = k_TangentRotDiscWidth;
using (new ZTestScope(CompareFunction.Less))
{
// Draw tangent arm.
using (new ColorScope(tangentArmColor))
DrawTangentArm(position, knotPosition, size, mode, selected, hovered, oppositeSelected, oppositeHovered);
// Draw tangent shape.
using (new Handles.DrawingScope(tangentColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
DrawTangentShape(size, selected);
}
using (new ZTestScope(CompareFunction.Greater))
{
// Draw tangent arm.
var newTangentArmColor = new Color(tangentArmColor.r, tangentArmColor.g, tangentArmColor.b, tangentArmColor.a * k_ColorAlphaFactor);
using (new ColorScope(newTangentArmColor))
DrawTangentArm(position, knotPosition, size, mode, selected, hovered, oppositeSelected, oppositeHovered);
// Draw tangent shape.
var newDiscColor = new Color(tangentColor.r, tangentColor.g, tangentColor.b, tangentColor.a * k_ColorAlphaFactor);
using (new Handles.DrawingScope(newDiscColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
DrawTangentShape(size, selected);
}
// Draw tangent disc on hover.
if (hovered)
{
var tangentHandleOffset = size * k_TangentEndOffsetFromHandle;
using (new ZTestScope(CompareFunction.Less))
{
using (new Handles.DrawingScope(tangentColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, tangentHandleOffset, rotationDiscWidth);
}
using (new ZTestScope(CompareFunction.Greater))
{
var newDiscColor = new Color(tangentColor.r, tangentColor.g, tangentColor.b, tangentColor.a * k_ColorAlphaFactor);
using (new Handles.DrawingScope(newDiscColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, tangentHandleOffset, rotationDiscWidth);
}
}
}
static void DrawTangentArm(Vector3 position, Vector3 knotPosition, float size, TangentMode mode, bool selected, bool hovered, bool oppositeSelected, bool oppositeHovered)
{
var width = k_TangentLineWidthDefault;
if (!DirectManipulation.IsDragging)
{
if (selected || (mode != TangentMode.Broken && oppositeSelected))
width = k_TangentLineWidthSelected;
else if (hovered || (mode != TangentMode.Broken && oppositeHovered))
width = k_TangentLineWidthHover;
}
var startPos = knotPosition;
var toTangent = position - knotPosition;
var toTangentNorm = math.normalize(toTangent);
var length = math.length(toTangent);
var knotHandleSize = HandleUtility.GetHandleSize(startPos);
var knotHandleOffset = knotHandleSize * k_TangentStartOffsetFromKnot;
var tangentHandleOffset = size * k_TangentEndOffsetFromHandle;
// Reduce the length slightly, so that there's some space between tangent line endings and handles.
length = Mathf.Max(0f, length - knotHandleOffset - tangentHandleOffset);
startPos += (Vector3)toTangentNorm * knotHandleOffset;
SplineHandleUtility.DrawLineWithWidth(startPos + (Vector3)toTangentNorm * length, startPos, width, SplineHandleUtility.denseLineAATex);
}
static void DrawTangentShape(float size, bool selected)
{
var midVector = new Vector3(-.5f, 0, .5f);
if (selected)
{
var factor = 0.7f;
var radius = (selected ? SplineHandleUtility.knotDiscRadiusFactorSelected : SplineHandleUtility.knotDiscRadiusFactorHover) * size;
// As Handles.DrawAAConvexPolygon has no thickness parameter, we're drawing a AA Polyline here so that the polygon has thickness when viewed from a shallow angle.
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex,
k_TangentHandleWidth,
factor * radius * midVector,
factor * radius * Vector3.forward,
factor * radius * Vector3.right,
-factor * radius * Vector3.forward,
-factor * radius * Vector3.right,
factor * radius * midVector);
Handles.DrawAAConvexPolygon(
radius * midVector,
radius * Vector3.forward,
radius * Vector3.right,
-radius * Vector3.forward,
-radius * Vector3.right,
radius * midVector);
}
else
{
var radius = SplineHandleUtility.knotDiscRadiusFactorDefault * size;
//Starting the polyline in the middle of a segment and not to a corner to get an invisible connection.
//Otherwise the connection is really visible in the corner as a small part is missing there.
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex,
k_TangentHandleWidth,
radius * midVector,
radius * Vector3.forward,
radius * Vector3.right,
-radius * Vector3.forward,
-radius * Vector3.right,
radius * midVector);
}
}
}
}

11
Packages/com.unity.splines/Editor/Controls/TangentHandles.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8805f50579982e7458c1b5ecc0119396
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.splines/Editor/Core.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 411641953fc0a2148a0507f5ee6df182
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

302
Packages/com.unity.splines/Editor/Core/CopyPaste.cs

@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
using Object = UnityEngine.Object;
namespace UnityEditor.Splines
{
static class CopyPaste
{
// JSONUtility needs a root object to serialize
[Serializable]
class CopyPasteBuffer
{
public SerializedSpline[] Splines;
public SerializedLink[] Links;
}
[Serializable]
struct SerializedKnot
{
public BezierKnot Knot;
public TangentMode Mode;
public float Tension;
public SerializedKnot(SelectableKnot knot)
{
Knot = knot.GetBezierKnot(false);
Mode = knot.Mode;
Tension = knot.Tension;
}
}
[Serializable]
class SerializedSpline
{
public float4x4 Transform;
public bool Closed;
public SerializedKnot[] Knots;
}
[Serializable]
class SerializedLink
{
public SplineKnotIndex[] Indices;
}
public static bool IsSplineCopyBuffer(string contents)
{
if (string.IsNullOrEmpty(contents))
return false;
var buffer = new CopyPasteBuffer();
try
{
EditorJsonUtility.FromJsonOverwrite(contents, buffer);
}
catch (ArgumentException)
{
return false;
}
return buffer.Splines?.Length > 0;
}
static int CompareKnot(SelectableKnot a, SelectableKnot b)
{
var compareTarget = (int)math.sign(a.SplineInfo.Object.GetInstanceID() - b.SplineInfo.Object.GetInstanceID());
if (compareTarget != 0)
return compareTarget;
var compareSpline = (int)math.sign(a.SplineInfo.Index - b.SplineInfo.Index);
if (compareSpline != 0)
return compareSpline;
return (int)math.sign(a.KnotIndex - b.KnotIndex);
}
public static string Copy(IEnumerable<SelectableKnot> selection)
{
SerializedKnot[] ToArray(SelectableKnot[] original)
{
var result = new SerializedKnot[original.Length];
for (int i = 0; i < result.Length; ++i)
result[i] = new SerializedKnot(original[i]);
return result;
}
void Flatten(List<SelectableKnot[]> arrays, List<SelectableKnot> results)
{
results.Clear();
foreach (var knotArray in arrays)
results.AddRange(knotArray);
}
Dictionary<SelectableKnot, SplineKnotIndex> knotToSerializedIndex = new Dictionary<SelectableKnot, SplineKnotIndex>();
List<SerializedSpline> splines = new List<SerializedSpline>();
List<SelectableKnot> originalKnots = new List<SelectableKnot>(selection);
var connectedKnots = GetConnectedKnots(originalKnots);
foreach (var connectedKnotArray in connectedKnots)
{
// Skip Orphan Knots
if (connectedKnotArray.Length < 2)
continue;
var splineInfo = connectedKnotArray[0].SplineInfo;
splines.Add(new SerializedSpline
{
Closed = splineInfo.Spline.Closed && connectedKnotArray.Length == splineInfo.Spline.Count,
Knots = ToArray(connectedKnotArray),
Transform = splineInfo.LocalToWorld
});
for (int i = 0; i < connectedKnotArray.Length; ++i)
knotToSerializedIndex.Add(connectedKnotArray[i], new SplineKnotIndex(splines.Count - 1, i));
}
// Add the links
List<SplineKnotIndex> indices = new List<SplineKnotIndex>();
List<SerializedLink> links = new List<SerializedLink>();
List<SelectableKnot> knots = new List<SelectableKnot>();
// Update the original knots array with the removal of orphan knots
Flatten(connectedKnots, originalKnots);
foreach (var originalKnot in originalKnots)
{
EditorSplineUtility.GetKnotLinks(originalKnot, knots);
indices.Clear();
foreach (var knot in knots)
{
if (knotToSerializedIndex.TryGetValue(knot, out var index))
{
indices.Add(index);
//Remove the pair to ensure we don't get duplicates for every knot in the same link
knotToSerializedIndex.Remove(knot);
}
}
// Only serialized the link if at least 2 copied knots were linked together
if (indices.Count >= 2)
links.Add(new SerializedLink {Indices = indices.ToArray()});
}
if (splines.Count == 0)
return string.Empty;
CopyPasteBuffer buffer = new CopyPasteBuffer
{
Splines = splines.ToArray(),
Links = links.ToArray(),
};
return EditorJsonUtility.ToJson(buffer);
}
static List<SelectableKnot[]> GetConnectedKnots(List<SelectableKnot> knots)
{
if (knots.Count == 0)
return new List<SelectableKnot[]>();
knots.Sort(CompareKnot);
List<SelectableKnot[]> results = new List<SelectableKnot[]>();
List<SelectableKnot> connected = new List<SelectableKnot> { knots[0] };
for (int i = 1; i < knots.Count; ++i)
{
var previous = connected[^1];
var current = knots[i];
// Check if adjacent and on the same spline as previous
if (!previous.SplineInfo.Equals(current.SplineInfo)
|| previous.KnotIndex + 1 != current.KnotIndex)
{
results.Add(connected.ToArray());
connected.Clear();
}
connected.Add(current);
}
results.Add(connected.ToArray());
// Merge ends if the spline is closed and first and last knots are connected
for (int i = 0; i < results.Count; ++i)
{
var firstKnot = results[i][0];
if (firstKnot.KnotIndex == 0 && firstKnot.SplineInfo.Spline.Closed)
{
// Look for the last knot on the same spline
for (int j = i + 1; j < results.Count; ++j)
{
var lastKnot = results[j][^1];
// Early exit if not on the same spline
if (!lastKnot.SplineInfo.Equals(firstKnot.SplineInfo))
break;
if (lastKnot.KnotIndex == lastKnot.SplineInfo.Spline.Count - 1)
{
// combine both arrays
var a = results[j];
var b = results[i];
var newArray = new SelectableKnot[a.Length + b.Length];
Array.Copy(a, newArray, a.Length);
Array.Copy(b, 0, newArray, a.Length, b.Length);
results[i] = newArray;
results.RemoveAt(j);
break;
}
}
}
}
return results;
}
// Paste will create all new splines in the first active ISplineContainer in the selection.
// Duplicate will try to create new splines in the same container that the knots were copied from.
public static void Paste(string copyPasteBuffer)
{
ISplineContainer target = Selection.GetFiltered<ISplineContainer>(SelectionMode.TopLevel).FirstOrDefault() ??
ObjectFactory.CreateGameObject("New Spline", typeof(SplineContainer)).GetComponent<SplineContainer>();
Paste(copyPasteBuffer, target);
}
public static void Paste(string copyPasteBuffer, ISplineContainer target)
{
if (target == null)
throw new ArgumentNullException(nameof(target));
if (string.IsNullOrEmpty(copyPasteBuffer))
return;
var buffer = new CopyPasteBuffer();
try
{
EditorJsonUtility.FromJsonOverwrite(copyPasteBuffer, buffer);
}
catch (ArgumentException)
{
//If the copy buffer wasn't for a spline copy buffer, we just don't do anything
return;
}
var selection = new List<SelectableKnot>();
var inverse = (target is Component component)
? component.transform.localToWorldMatrix.inverse
: Matrix4x4.identity;
var branches = new List<Spline>(target.Splines);
int splineIndexOffset = branches.Count;
foreach (var serialized in buffer.Splines)
{
var knots = serialized.Knots;
var spline = new Spline(knots.Length);
spline.Closed = serialized.Closed;
var trs = serialized.Transform;
var index = branches.Count;
var info = new SplineInfo(target, index);
branches.Add(spline);
for (int i = 0, c = knots.Length; i < c; ++i)
{
spline.Add(knots[i].Knot.Transform(math.mul(inverse, trs)), knots[i].Mode, knots[i].Tension);
selection.Add(new SelectableKnot(info, i));
}
}
if (target is Object obj)
Undo.RecordObject(obj, "Paste Knots");
target.Splines = branches;
foreach (var link in buffer.Links)
{
var firstIndex = link.Indices[0];
firstIndex.Spline += splineIndexOffset;
for (int i = 1; i < link.Indices.Length; ++i)
{
var indexPair = link.Indices[i];
indexPair.Spline += splineIndexOffset;
target.KnotLinkCollection.Link(firstIndex, indexPair);
}
}
SplineSelection.Clear();
SplineSelection.AddRange(selection);
SceneView.RepaintAll();
}
}
}

3
Packages/com.unity.splines/Editor/Core/CopyPaste.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 641a050c859644e7804f826b7d4d8c0b
timeCreated: 1656006689

41
Packages/com.unity.splines/Editor/Core/PathSettings.cs

@ -0,0 +1,41 @@
using UnityEditor.SettingsManagement;
using UnityEngine;
namespace UnityEditor.Splines
{
sealed class PathSettings
{
static Settings s_SettingsInstance;
public static Settings instance
{
get
{
if (s_SettingsInstance == null)
s_SettingsInstance = new Settings(new [] { new UserSettingsRepository() });
return s_SettingsInstance;
}
}
// Register a new SettingsProvider that will scrape the owning assembly for [UserSetting] marked fields.
[SettingsProvider]
static SettingsProvider CreateSettingsProvider()
{
var provider = new UserSettingsProvider("Preferences/Splines",
instance,
new[] { typeof(PathSettings).Assembly });
return provider;
}
}
/// <summary>
/// The wrapper to define user preferences through Settings Manager.
/// </summary>
/// <typeparam name="T"></typeparam>
class Pref<T> : UserSetting<T>
{
public Pref(string key, T value)
: base(PathSettings.instance, key, value, SettingsScope.User) { }
}
}

11
Packages/com.unity.splines/Editor/Core/PathSettings.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0834e0204621424449fe7f88d7127f07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

45
Packages/com.unity.splines/Editor/Core/SelectionContext.cs

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using Object = UnityEngine.Object;
namespace UnityEditor.Splines
{
[Serializable]
struct SelectableSplineElement : IEquatable<SelectableSplineElement>
{
public Object target;
public int targetIndex;
public int knotIndex;
public int tangentIndex;
public SelectableSplineElement(ISelectableElement element)
{
target = element.SplineInfo.Object;
targetIndex = element.SplineInfo.Index;
knotIndex = element.KnotIndex;
tangentIndex = element is SelectableTangent tangent ? tangent.TangentIndex : -1;
}
public bool Equals(SelectableSplineElement other)
{
return target == other.target && targetIndex == other.targetIndex && knotIndex == other.knotIndex && tangentIndex == other.tangentIndex;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is SelectableSplineElement other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(target, targetIndex, knotIndex, tangentIndex);
}
}
sealed class SelectionContext : ScriptableSingleton<SelectionContext>
{
public List<SelectableSplineElement> selection = new List<SelectableSplineElement>();
public int version;
}
}

11
Packages/com.unity.splines/Editor/Core/SelectionContext.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 33dac614537a60e43ac2b6692f9dc39a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

385
Packages/com.unity.splines/Editor/Core/SplineElement.cs

@ -0,0 +1,385 @@
using System;
using Unity.Mathematics;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
/// <summary>
/// An interface that represents a selectable spline element. A selectable spline element can be a knot or a tangent.
/// `ISelectableElement` is used by the selection to get information about the spline, the knot, and the positions of the spline elements.
/// </summary>
public interface ISelectableElement : IEquatable<ISelectableElement>
{
/// <summary>
/// The <see cref="SplineInfo"/> that describes the spline.
/// </summary>
SplineInfo SplineInfo { get; }
/// <summary>
/// The index of the knot in the spline. If the spline element is a tangent, this is the index of the knot
/// that the tangent is attached to.
/// </summary>
int KnotIndex { get; }
/// <summary>
/// The position of the spline element in local space.
/// </summary>
float3 LocalPosition { get; set; }
/// <summary>
/// The position of the spline element in world space.
/// </summary>
float3 Position { get; set; }
/// <summary>
/// Checks if the element is valid. For example, checks if the spline is not null and the index is valid.
/// </summary>
/// <returns>Returns true if all fields from the element have valid values.</returns>
bool IsValid();
}
/// <summary>
/// Implements the <see cref="ISelectableElement"/> interface. SelectableKnot is used by the
/// spline selection and handles to use tools and handles to manipulate spline elements.
/// </summary>
public struct SelectableKnot : ISelectableElement, IEquatable<SelectableKnot>
{
/// <inheritdoc />
public SplineInfo SplineInfo { get; }
/// <inheritdoc />
public int KnotIndex { get; }
/// <summary>
/// Transforms a knot from local space to world space (Read Only).
/// </summary>
internal float4x4 LocalToWorld => SplineInfo.LocalToWorld;
/// <inheritdoc />
public float3 Position
{
get => math.transform(LocalToWorld, LocalPosition);
set => LocalPosition = math.transform(math.inverse(LocalToWorld), value);
}
/// <inheritdoc />
public float3 LocalPosition
{
get => SplineInfo.Spline[KnotIndex].Position;
set
{
var knot = SplineInfo.Spline[KnotIndex];
knot.Position = value;
SplineInfo.Spline[KnotIndex] = knot;
}
}
/// <inheritdoc />
public bool IsValid()
{
return SplineInfo.Spline != null && KnotIndex >= 0 && KnotIndex < SplineInfo.Spline.Count;
}
/// <summary>
/// The rotation of the spline element in world space.
/// </summary>
public quaternion Rotation
{
get => math.mul(new quaternion(LocalToWorld), LocalRotation);
set => LocalRotation = math.mul(math.inverse(new quaternion(LocalToWorld)), value);
}
/// <summary>
/// The rotation of the spline element in local space.
/// </summary>
public quaternion LocalRotation
{
get => SplineInfo.Spline[KnotIndex].Rotation;
set
{
var knot = SplineInfo.Spline[KnotIndex];
knot.Rotation = math.normalize(value);
SplineInfo.Spline[KnotIndex] = knot;
}
}
/// <summary>
/// The <see cref="TangentMode"/> associated with a knot.
/// </summary>
public TangentMode Mode
{
get => SplineInfo.Spline.GetTangentMode(KnotIndex);
set
{
SplineInfo.Spline.SetTangentMode(KnotIndex, value);
SplineSelectionUtility.ValidateTangentSelection(this);
}
}
/// <summary>
/// The tension associated with a knot. `Tension` is only used if the tangent mode is Auto Smooth.
/// </summary>
public float Tension
{
get => SplineInfo.Spline.GetAutoSmoothTension(KnotIndex);
set => SplineInfo.Spline.SetAutoSmoothTension(KnotIndex, value);
}
/// <summary>
/// Sets the tangent mode of the knot.
/// </summary>
/// <param name="mode">The <see cref="TangentMode"/> to apply to the knot.</param>
/// <param name="main">The tangent to use as the main tangent when the tangent is set to the Mirrored or Continuous tangent mode.
/// The main tangent is not modified, but the other tangent attached to that knot is modified to adopt the new tangent mode.</param>
public void SetTangentMode(TangentMode mode, BezierTangent main)
{
var spline = SplineInfo.Spline;
spline.SetTangentMode(KnotIndex, mode, main);
SplineSelectionUtility.ValidateTangentSelection(this);
}
/// <summary>
/// The In tangent associated with the knot. The In tangent defines the curvature of the segment that enters the knot.
/// </summary>
public SelectableTangent TangentIn => new SelectableTangent(SplineInfo, KnotIndex, BezierTangent.In);
/// <summary>
/// The Out tangent associated with the knot. The Out tangent defines the curvature of the segment that exits the knot.
/// </summary>
public SelectableTangent TangentOut => new SelectableTangent(SplineInfo, KnotIndex, BezierTangent.Out);
/// <summary>
/// Creates a <see cref="SelectableKnot"/> from a SplineInfo and a knot index.
/// </summary>
/// <param name="info">The <see cref="SplineInfo"/> associated with the tangent.</param>
/// <param name="index">The index of the knot.</param>
public SelectableKnot(SplineInfo info, int index)
{
this.SplineInfo = info;
this.KnotIndex = index;
}
/// <summary>
/// Creates the BezierKnot representation associated with a SelectableKnot.
/// </summary>
/// <param name="worldSpace">Set to true for the BezierKnot to be in world space, or set to false for the Bezierknot to be in local space.</param>
/// <returns>The <see cref="BezierKnot"/> associated with the knot.</returns>
public BezierKnot GetBezierKnot(bool worldSpace)
{
return worldSpace ? SplineInfo.Spline[KnotIndex].Transform(LocalToWorld) : SplineInfo.Spline[KnotIndex];
}
/// <summary>
/// Checks if two instances of `SplineElement` are equal.
/// </summary>
/// <param name="other">The <see cref="ISelectableElement"/> to compare against.</param>
/// <returns>
/// Returns true when <paramref name="other"/> is a <see cref="SelectableKnot"/> and the values of each instance are identical.
/// </returns>
public bool Equals(ISelectableElement other)
{
if (other is SelectableKnot knot)
return Equals(knot);
return false;
}
/// <summary>
/// Checks if two instances of SelectableKnot are equal.
/// </summary>
/// <param name="other">The <see cref="SelectableKnot"/> to compare against.</param>
/// <returns>
/// Returns true if the values of each instance of `SelectableKnot` are identical.
/// </returns>
public bool Equals(SelectableKnot other)
{
return Equals(SplineInfo.Spline, other.SplineInfo.Spline) && KnotIndex == other.KnotIndex;
}
/// <summary>
/// Checks if two instances of an object are equal.
/// </summary>
/// <param name="obj">The object to compare against.</param>
/// <returns>
/// Returns true if <paramref name="obj"/> is a <see cref="SelectableKnot"/> and its values are identical to the original instance.
/// </returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is SelectableKnot other && Equals(other);
}
/// <summary>
/// Gets a hash code for this knot.
/// </summary>
/// <returns>
/// A hash code for the <see cref="SelectableKnot"/>.
/// </returns>
public override int GetHashCode()
{
return HashCode.Combine(SplineInfo.Spline, KnotIndex);
}
}
/// <summary>
/// Represents a struct that implements the <see cref="ISelectableElement"/> interface. Spline selection uses
/// `SelectableTangent` and handles to easily manipulate spline elements with tools and handles.
/// </summary>
public struct SelectableTangent : ISelectableElement, IEquatable<SelectableTangent>
{
/// <inheritdoc />
public SplineInfo SplineInfo { get; }
/// <inheritdoc />
public int KnotIndex { get; }
/// <summary>
/// The index of the tangent. A value of 0 represents an In tangent. A value of 1 represents an Out tangent.
/// </summary>
public int TangentIndex { get; }
/// <summary>
/// The knot associated with this tangent.
/// </summary>
public SelectableKnot Owner => new SelectableKnot(SplineInfo, KnotIndex);
/// <summary>
/// The opposite tangent on the knot. If this tangent is the In tangent, then the opposite tangent is the Out tangent. If this tangent is the Out tangent, then the opposite tangent is the In tangent.
/// </summary>
public SelectableTangent OppositeTangent => new SelectableTangent(SplineInfo, KnotIndex, 1 - TangentIndex);
/// <inheritdoc />
public bool IsValid()
{
return SplineInfo.Spline != null
&& KnotIndex >= 0
&& KnotIndex < SplineInfo.Spline.Count
&& TangentIndex >= 0
&& TangentIndex < 2
&& Owner.Mode != TangentMode.Linear
&& Owner.Mode != TangentMode.AutoSmooth;
}
/// <summary>
/// The direction of the tangent in world space.
/// </summary>
public float3 Direction
{
get => MathUtility.MultiplyVector(LocalToWorld, LocalDirection);
set => LocalDirection = MathUtility.MultiplyVector(math.inverse(LocalToWorld), value);
}
/// <summary>
/// The direction of the tangent in local space.
/// </summary>
public float3 LocalDirection
{
get => TangentIndex == (int)BezierTangent.In ? SplineInfo.Spline[KnotIndex].TangentIn : SplineInfo.Spline[KnotIndex].TangentOut;
set
{
var spline = SplineInfo.Spline;
var knot = spline[KnotIndex];
switch (TangentIndex)
{
case (int)BezierTangent.In:
knot.TangentIn = value;
break;
case (int)BezierTangent.Out:
knot.TangentOut = value;
break;
}
spline.SetKnot(KnotIndex, knot, (BezierTangent)TangentIndex);
}
}
/// <summary>
/// Matrix that transforms a tangent point from local space into world space using its associated knot (Read Only).
/// </summary>
internal float4x4 LocalToWorld => math.mul(SplineInfo.LocalToWorld, new float4x4(Owner.LocalRotation, Owner.LocalPosition));
/// <inheritdoc />
public float3 Position
{
get => math.transform(LocalToWorld, LocalPosition);
set => LocalPosition = math.transform(math.inverse(LocalToWorld), value);
}
/// <inheritdoc />
public float3 LocalPosition
{
get => LocalDirection;
set => LocalDirection = value;
}
/// <summary>
/// Creates a new <see cref="SelectableTangent"/> object.
/// </summary>
/// <param name="info">The <see cref="SplineInfo"/> associated with the tangent.</param>
/// <param name="knotIndex">The index of the knot that the tangent is attached to.</param>
/// <param name="tangent">The <see cref="BezierTangent"/> that represents this tangent.</param>
public SelectableTangent(SplineInfo info, int knotIndex, BezierTangent tangent)
: this(info, knotIndex, (int)tangent) { }
/// <summary>
/// Creates a new <see cref="SelectableTangent"/> object.
/// </summary>
/// <param name="info">The <see cref="SplineInfo"/> associated with the tangent.</param>
/// <param name="knotIndex">The index of the knot that the tangent is attached to.</param>
/// <param name="tangentIndex">The index of the tangent. A value of 0 represents an In tangent. A value of 1 represents an Out tangent.</param>
public SelectableTangent(SplineInfo info, int knotIndex, int tangentIndex)
{
SplineInfo = info;
KnotIndex = knotIndex;
TangentIndex = tangentIndex;
}
/// <summary>
/// Checks if two instances of a `SplineElement` are equal.
/// </summary>
/// <param name="other">The <see cref="ISelectableElement"/> to compare against.</param>
/// <returns>
/// Returns true when <paramref name="other"/> is a <see cref="SelectableTangent"/> and the values of each instance are identical.
/// </returns>
public bool Equals(ISelectableElement other)
{
if (other is SelectableTangent tangent)
return Equals(tangent);
return false;
}
/// <summary>
/// Checks if two instances of `SelectableTangent` are equal.
/// </summary>
/// <param name="other">The <see cref="SelectableTangent"/> to compare against.</param>
/// <returns>
/// Returns true if the values of each instance are identical.
/// </returns>
public bool Equals(SelectableTangent other)
{
return Equals(SplineInfo.Spline, other.SplineInfo.Spline) && KnotIndex == other.KnotIndex && TangentIndex == other.TangentIndex;
}
/// <summary>
/// Checks if two objects are equal.
/// </summary>
/// <param name="obj">The object to compare against.</param>
/// <returns>
/// Returns true when <paramref name="obj"/> is a <see cref="SelectableTangent"/> and the values of each instance are identical.
/// </returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is SelectableTangent other && Equals(other);
}
/// <summary>
/// Gets a hash code for this tangent.
/// </summary>
/// <returns>
/// A hash code for the <see cref="SelectableTangent"/>.
/// </returns>
public override int GetHashCode()
{
return HashCode.Combine(SplineInfo.Spline, KnotIndex, TangentIndex);
}
}
}

11
Packages/com.unity.splines/Editor/Core/SplineElement.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78403350ac663ed44a801175a4fe1b8e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

610
Packages/com.unity.splines/Editor/Core/SplineSelection.cs

@ -0,0 +1,610 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Splines;
using Object = UnityEngine.Object;
namespace UnityEditor.Splines
{
/// <summary>
/// Provides methods to track the selection of spline elements, knots, and tangents.
/// `SplineTools` and `SplineHandles` use `SplineSelection` to manage these elements.
/// </summary>
public static class SplineSelection
{
/// <summary>
/// Action that is called when the element selection changes.
/// </summary>
public static event Action changed;
static readonly HashSet<Object> s_ObjectSet = new HashSet<Object>();
static readonly HashSet<SplineInfo> s_SelectedSplineInfo = new HashSet<SplineInfo>();
static Object[] s_SelectedTargetsBuffer = new Object[0];
static SelectionContext context => SelectionContext.instance;
internal static List<SelectableSplineElement> selection => context.selection;
// Tracks selected splines in the SplineReorderableList
static List<SplineInfo> s_SelectedSplines = new ();
/// <summary>
/// The number of elements in the current selection.
/// </summary>
public static int Count => selection.Count;
static HashSet<SelectableTangent> s_AdjacentTangentCache = new HashSet<SelectableTangent>();
static int s_SelectionVersion;
static SplineSelection()
{
context.version = 0;
Undo.undoRedoPerformed += OnUndoRedoPerformed;
EditorSplineUtility.knotInserted += OnKnotInserted;
EditorSplineUtility.knotRemoved += OnKnotRemoved;
Selection.selectionChanged += OnSelectionChanged;
}
static void OnSelectionChanged()
{
ClearInspectorSelectedSplines();
}
static void OnUndoRedoPerformed()
{
if (context.version != s_SelectionVersion)
{
s_SelectionVersion = context.version;
ClearInspectorSelectedSplines();
NotifySelectionChanged();
}
}
/// <summary>
/// Clears the current selection.
/// </summary>
public static void Clear()
{
if (selection.Count == 0)
return;
IncrementVersion();
ClearNoUndo(true);
}
internal static void ClearNoUndo(bool notify)
{
selection.Clear();
if (notify)
NotifySelectionChanged();
}
/// <summary>
/// Checks if the current selection contains at least one element from the given targeted splines.
/// </summary>
/// <param name="targets">The splines to consider when looking at selected elements.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
/// <returns>Returns true if the current selection contains at least an element of the desired type.</returns>
public static bool HasAny<T>(IReadOnlyList<SplineInfo> targets)
where T : struct, ISelectableElement
{
for (int i = 0; i < Count; ++i)
for (int j = 0; j < targets.Count; ++j)
if (TryGetElement(selection[i], targets[j], out T _))
return true;
return false;
}
/// <summary>
/// Gets the active element of the selection. The active element is generally the last one added to this selection.
/// </summary>
/// <param name="targets">The splines to consider when getting the active element.</param>
/// <returns>The <see cref="ISelectableElement"/> that represents the active knot or tangent. Returns null if no active element is found.</returns>
public static ISelectableElement GetActiveElement(IReadOnlyList<SplineInfo> targets)
{
for (int i = 0; i < Count; ++i)
for (int j = 0; j < targets.Count; ++j)
if (TryGetElement(selection[i], targets[j], out ISelectableElement result))
return result;
return null;
}
/// <summary>
/// Gets all the elements of the current selection, filtered by target splines. Elements are added to the given collection.
/// </summary>
/// <param name="targets">The splines to consider when looking at selected elements.</param>
/// <param name="results">The collection to fill with spline elements from the selection.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
public static void GetElements<T>(IReadOnlyList<SplineInfo> targets, ICollection<T> results)
where T : ISelectableElement
{
results.Clear();
for (int i = 0; i < Count; ++i)
for (int j = 0; j < targets.Count; ++j)
if (TryGetElement(selection[i], targets[j], out T result))
results.Add(result);
}
/// <summary>
/// Gets all the elements of the current selection, from a single spline target. Elements are added to the given collection.
/// </summary>
/// <param name="target">The spline to consider when looking at selected elements.</param>
/// <param name="results">The collection to fill with spline elements from the selection.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
public static void GetElements<T>(SplineInfo target, ICollection<T> results)
where T : ISelectableElement
{
results.Clear();
for (int i = 0; i < Count; ++i)
if (TryGetElement(selection[i], target, out T result))
results.Add(result);
}
static bool TryGetElement<T>(SelectableSplineElement element, SplineInfo splineInfo, out T value)
where T : ISelectableElement
{
if (element.target == splineInfo.Container as Object)
{
if (element.targetIndex == splineInfo.Index)
{
if (element.tangentIndex >= 0)
{
var tangent = new SelectableTangent(splineInfo, element.knotIndex, element.tangentIndex);
if (tangent.IsValid() && tangent is T t)
{
value = t;
return true;
}
value = default;
return false;
}
var knot = new SelectableKnot(splineInfo, element.knotIndex);
if (knot.IsValid() && knot is T k)
{
value = k;
return true;
}
}
}
value = default;
return false;
}
internal static Object[] GetAllSelectedTargets()
{
s_ObjectSet.Clear();
foreach (var element in selection)
{
s_ObjectSet.Add(element.target);
}
Array.Resize(ref s_SelectedTargetsBuffer, s_ObjectSet.Count);
s_ObjectSet.CopyTo(s_SelectedTargetsBuffer);
return s_SelectedTargetsBuffer;
}
/// <summary>
/// Used for selecting splines from the inspector, only internal from now.
/// </summary>
internal static IEnumerable<SplineInfo> SelectedSplines => s_SelectedSplines;
/// <summary>
/// Checks if an element is currently the active one in the selection.
/// </summary>
/// <param name="element">The <see cref="ISelectableElement"/> to test.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
/// <returns>Returns true if the element is the active element, false if it is not.</returns>
public static bool IsActive<T>(T element)
where T : ISelectableElement
{
if (selection.Count == 0)
return false;
return IsEqual(element, selection[0]);
}
static bool IsEqual<T>(T element, SelectableSplineElement selectionData)
where T : ISelectableElement
{
int tangentIndex = element is SelectableTangent tangent ? tangent.TangentIndex : -1;
return element.SplineInfo.Object == selectionData.target
&& element.SplineInfo.Index == selectionData.targetIndex
&& element.KnotIndex == selectionData.knotIndex
&& tangentIndex == selectionData.tangentIndex;
}
/// <summary>
/// Sets the active element of the selection.
/// </summary>
/// <param name="element">The <see cref="ISelectableElement"/> to set as the active element.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
public static void SetActive<T>(T element)
where T : ISelectableElement
{
var index = IndexOf(element);
if (index == 0)
return;
IncrementVersion();
if (index > 0)
selection.RemoveAt(index);
var e = new SelectableSplineElement(element);
selection.Insert(0, e);
if(e.target is Component component)
{
//Set the active unity object so the spline is the first target
Object[] unitySelection = Selection.objects;
var target = component.gameObject;
index = Array.IndexOf(unitySelection, target);
if(index > 0)
{
Object prevObj = unitySelection[0];
unitySelection[0] = unitySelection[index];
unitySelection[index] = prevObj;
Selection.objects = unitySelection;
}
}
NotifySelectionChanged();
}
/// <summary>
/// Sets the selection to the element.
/// </summary>
/// <param name="element">The <see cref="ISelectableElement"/> to set as the selection.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
public static void Set<T>(T element)
where T : ISelectableElement
{
IncrementVersion();
ClearNoUndo(false);
selection.Insert(0, new SelectableSplineElement(element));
NotifySelectionChanged();
}
internal static void Set(IEnumerable<SelectableSplineElement> selection)
{
IncrementVersion();
context.selection.Clear();
context.selection.AddRange(selection);
NotifySelectionChanged();
}
/// <summary>
/// Adds an element to the current selection.
/// </summary>
/// <param name="element">The <see cref="ISelectableElement"/> to add to the selection.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
/// <returns>Returns true if the element was added to the selection, and false if the element is already in the selection.</returns>
public static bool Add<T>(T element)
where T : ISelectableElement
{
if (Contains(element))
return false;
IncrementVersion();
selection.Insert(0, new SelectableSplineElement(element));
NotifySelectionChanged();
return true;
}
/// <summary>
/// Add a set of elements to the current selection.
/// </summary>
/// <param name="elements">The set of <see cref="ISelectableElement"/> to add to the selection.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
public static void AddRange<T>(IEnumerable<T> elements)
where T : ISelectableElement
{
bool changed = false;
foreach (var element in elements)
{
if (!Contains(element))
{
if (!changed)
{
changed = true;
IncrementVersion();
}
selection.Insert(0, new SelectableSplineElement(element));
}
}
if (changed)
NotifySelectionChanged();
}
/// <summary>
/// Remove an element from the current selection.
/// </summary>
/// <param name="element">The <see cref="ISelectableElement"/> to remove from the selection.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
/// <returns>Returns true if the element has been removed from the selection, false otherwise.</returns>
public static bool Remove<T>(T element)
where T : ISelectableElement
{
var index = IndexOf(element);
if (index >= 0)
{
IncrementVersion();
selection.RemoveAt(index);
NotifySelectionChanged();
return true;
}
return false;
}
/// <summary>
/// Remove a set of elements from the current selection.
/// </summary>
/// <param name="elements">The set of <see cref="ISelectableElement"/> to remove from the selection.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
/// <returns>Returns true if at least an element has been removed from the selection, false otherwise.</returns>
public static bool RemoveRange<T>(IReadOnlyList<T> elements)
where T : ISelectableElement
{
bool changed = false;
for (int i = 0; i < elements.Count; ++i)
{
var index = IndexOf(elements[i]);
if (index >= 0)
{
if (!changed)
{
IncrementVersion();
changed = true;
}
selection.RemoveAt(index);
}
}
if (changed)
NotifySelectionChanged();
return changed;
}
static int IndexOf<T>(T element)
where T : ISelectableElement
{
for (int i = 0; i < selection.Count; ++i)
if (IsEqual(element, selection[i]))
return i;
return -1;
}
/// <summary>
/// Checks if the selection contains a knot or a tangent.c'est
/// </summary>
/// <param name="element">The element to verify.</param>
/// <typeparam name="T"><see cref="SelectableKnot"/> or <see cref="SelectableTangent"/>.</typeparam>
/// <returns>Returns true if the element is contained in the current selection, false otherwise.</returns>
public static bool Contains<T>(T element)
where T : ISelectableElement
{
return IndexOf(element) >= 0;
}
// Used when the selection is changed in the tools.
internal static void UpdateObjectSelection(IEnumerable<Object> targets)
{
s_ObjectSet.Clear();
foreach (var target in targets)
if (target != null)
s_ObjectSet.Add(target);
bool changed = false;
for (int i = Count - 1; i >= 0; --i)
{
bool removeElement = false;
if (!EditorSplineUtility.Exists(selection[i].target as ISplineContainer, selection[i].targetIndex))
{
removeElement = true;
}
else if (!s_ObjectSet.Contains(selection[i].target))
{
ClearInspectorSelectedSplines();
removeElement = true;
}
else if(selection[i].tangentIndex > 0)
{
// In the case of a tangent, also check that the tangent is still valid if the spline type
// or tangent mode has been updated
var spline = SplineToolContext.GetSpline(selection[i].target, selection[i].targetIndex);
removeElement = !SplineUtility.AreTangentsModifiable(spline.GetTangentMode(selection[i].knotIndex));
}
if (removeElement)
{
if (!changed)
{
changed = true;
IncrementVersion();
}
if (i < selection.Count)
selection.RemoveAt(i);
}
}
if (changed)
{
RebuildAdjacentCache();
NotifySelectionChanged();
}
}
//Used when inserting new elements in spline
static void OnKnotInserted(SelectableKnot inserted)
{
for (var i = 0; i < selection.Count; ++i)
{
var knot = selection[i];
if (knot.target == inserted.SplineInfo.Object
&& knot.targetIndex == inserted.SplineInfo.Index
&& knot.knotIndex >= inserted.KnotIndex)
{
++knot.knotIndex;
selection[i] = knot;
}
}
RebuildAdjacentCache();
}
//Used when deleting an element in spline
static void OnKnotRemoved(SelectableKnot removed)
{
bool changed = false;
for (var i = selection.Count - 1; i >= 0; --i)
{
var knot = selection[i];
if (knot.target == removed.SplineInfo.Object && knot.targetIndex == removed.SplineInfo.Index)
{
if (knot.knotIndex == removed.KnotIndex)
{
if (!changed)
{
changed = true;
IncrementVersion();
}
selection.RemoveAt(i);
}
else if (knot.knotIndex >= removed.KnotIndex)
{
--knot.knotIndex;
selection[i] = knot;
}
}
}
RebuildAdjacentCache();
if (changed)
NotifySelectionChanged();
}
static void IncrementVersion()
{
Undo.RecordObject(context, "Spline Selection Changed");
++s_SelectionVersion;
++context.version;
}
static void NotifySelectionChanged()
{
RebuildAdjacentCache();
changed?.Invoke();
}
static bool TryGetSplineInfo(SelectableSplineElement element, out SplineInfo splineInfo)
{
//Checking null in case the object was destroyed
if (element.target != null && element.target is ISplineContainer container)
{
splineInfo = new SplineInfo(container, element.targetIndex);
return true;
}
splineInfo = default;
return false;
}
static bool TryCast(SelectableSplineElement element, out SelectableTangent result)
{
if (TryGetSplineInfo(element, out var splineInfo) && element.tangentIndex >= 0)
{
result = new SelectableTangent(splineInfo, element.knotIndex, element.tangentIndex);
return true;
}
result = default;
return false;
}
static bool TryCast(SelectableSplineElement element, out SelectableKnot result)
{
if (TryGetSplineInfo(element, out var splineInfo) && element.tangentIndex < 0)
{
result = new SelectableKnot(splineInfo, element.knotIndex);
return true;
}
result = default;
return false;
}
internal static bool IsSelectedOrAdjacentToSelected(SelectableTangent tangent)
{
return s_AdjacentTangentCache.Contains(tangent);
}
static void RebuildAdjacentCache()
{
s_AdjacentTangentCache.Clear();
foreach(var element in selection)
{
SelectableTangent previousOut, currentIn, currentOut, nextIn;
if(TryCast(element, out SelectableKnot knot))
EditorSplineUtility.GetAdjacentTangents(knot, out previousOut, out currentIn, out currentOut, out nextIn);
else if(TryCast(element, out SelectableTangent tangent))
EditorSplineUtility.GetAdjacentTangents(tangent, out previousOut, out currentIn, out currentOut, out nextIn);
else
continue;
s_AdjacentTangentCache.Add(previousOut);
s_AdjacentTangentCache.Add(currentIn);
s_AdjacentTangentCache.Add(currentOut);
s_AdjacentTangentCache.Add(nextIn);
}
}
internal static void ClearInspectorSelectedSplines()
{
s_SelectedSplines.Clear();
}
internal static bool HasActiveSplineSelection()
{
return s_SelectedSplines.Count > 0;
}
// Inspector spline selection is a one-way operation. It can only be set by the SplineReorderableList. Changes
// to selected splines in the Scene or Hierarchy will only clear the selected inspector splines.
internal static void SetInspectorSelectedSplines(SplineContainer container, IEnumerable<int> selected)
{
s_SelectedSplines.Clear();
foreach (var index in selected)
s_SelectedSplines.Add(new SplineInfo(container, index));
IncrementVersion();
context.selection = selection.Where(x => x.target == container &&
(selected.Contains(x.targetIndex) || container.KnotLinkCollection.TryGetKnotLinks(new SplineKnotIndex(x.targetIndex, x.knotIndex), out _))).ToList();
NotifySelectionChanged();
}
internal static bool Contains(SplineInfo info)
{
return s_SelectedSplines.Contains(info);
}
internal static bool Remove(SplineInfo info)
{
return s_SelectedSplines.Remove(info);
}
}
}

11
Packages/com.unity.splines/Editor/Core/SplineSelection.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7529a2acb193d1946b9ce1ecb41dd4d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.splines/Editor/Editor Resources.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d96dfe6cb862ab34894ef767abef2e84
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.splines/Editor/Editor Resources/Icons.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 80c4e990f69fadf42a06e238da57fbfd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On.png

After

Width: 16  |  Height: 16  |  Size: 347 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: a52f5403392ac46e991381bec69935a7
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On@2x.png

After

Width: 32  |  Height: 32  |  Size: 598 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot On@2x.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 9b61528aebb8f4f3fb747bdfa7bc80b0
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot.png

After

Width: 16  |  Height: 16  |  Size: 374 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 07322254109944bb4b45b7b3e0131c6d
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot@2x.png

After

Width: 32  |  Height: 32  |  Size: 707 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/AutoSmoothKnot@2x.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 5703201e903d44191959e7b8d400f961
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool.png

After

Width: 16  |  Height: 16  |  Size: 324 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 954ac4bd24b73a540a2e84414caf856c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool@2x.png

After

Width: 32  |  Height: 32  |  Size: 540 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/KnotPlacementTool@2x.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: fdacd376014f8b743a9561d5652ca823
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent.png

After

Width: 16  |  Height: 16  |  Size: 424 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 3735bf6bd1171ad43896aaea4a3b417f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent@2x.png

After

Width: 32  |  Height: 32  |  Size: 861 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineComponent@2x.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 74e0f4cadf762ff468bb8bb11c2ca3c7
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext.png

After

Width: 16  |  Height: 16  |  Size: 404 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: fbb7a4425fc8e4e5bb4f932ae7f52073
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext@2x.png

After

Width: 32  |  Height: 32  |  Size: 738 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineContext@2x.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 2ad42e04f6841ea64a7f21707d1aec22
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info.png

After

Width: 32  |  Height: 32  |  Size: 738 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: dac10fae1df6549468da9a027ab58f48
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info@2x.png

After

Width: 64  |  Height: 64  |  Size: 1.5 KiB

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/SplineEditMode-Info@2x.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: b7fc12931c76a4c09b3ca863ae523672
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On.png

After

Width: 16  |  Height: 16  |  Size: 331 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: d64c7205ca5bb264892c91598b5e1402
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On@2x.png

After

Width: 32  |  Height: 32  |  Size: 622 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous On@2x.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: bb7bb364d1bd6bf4487c06678f176dff
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous.png

After

Width: 16  |  Height: 16  |  Size: 380 B

146
Packages/com.unity.splines/Editor/Editor Resources/Icons/Tangent_Continuous.png.meta

@ -0,0 +1,146 @@
fileFormatVersion: 2
guid: 791ecff9a0b9fc1439e92e50ceb81dbb
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 128
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Nintendo Switch
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: LinuxHeadlessSimulation
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save