Skip to content

Commit fd4b790

Browse files
committed
improving support for visibility
1 parent b40762a commit fd4b790

33 files changed

+767
-168
lines changed

src/SharpGLTF.Core/Animations/CurveSampler.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,15 @@ public static Single[] InterpolateCubic(ROLIST start, ROLIST outgoingTangent, RO
421421
private static bool _HasZero<T>(this IEnumerable<T> collection) { return collection == null || !collection.Any(); }
422422
private static bool _HasOne<T>(this IEnumerable<T> collection) { return !collection.Skip(1).Any(); }
423423

424+
public static ICurveSampler<Boolean> CreateSampler(this IEnumerable<(Single, Boolean)> collection, bool optimize = false)
425+
{
426+
if (collection._HasZero()) return null;
427+
if (collection._HasOne()) return FixedSampler<Boolean>.Create(collection);
428+
429+
var sampler = new StepSampler<Boolean>(collection, SamplerTraits.Boolean);
430+
return optimize ? sampler.ToFastSampler() : sampler;
431+
}
432+
424433
public static ICurveSampler<Single> CreateSampler(this IEnumerable<(Single, Single)> collection, bool isLinear = true, bool optimize = false)
425434
{
426435
if (collection._HasZero()) return null;

src/SharpGLTF.Core/Animations/CurveSamplers.Traits.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ interface ISamplerTraits<T>
1818

1919
static class SamplerTraits
2020
{
21+
sealed class _Boolean : ISamplerTraits<Boolean>
22+
{
23+
public Boolean Clone(Boolean value) { return value; }
24+
public Boolean InterpolateLinear(Boolean left, Boolean right, float amount) { return amount < 0.5f ? left :right; }
25+
public Boolean InterpolateCubic(Boolean start, Boolean outgoingTangent, Boolean end, Boolean incomingTangent, float amount)
26+
{
27+
return amount < 0.5f ? start : end;
28+
}
29+
}
30+
2131
sealed class _Scalar : ISamplerTraits<Single>
2232
{
2333
public Single Clone(Single value) { return value; }
@@ -110,6 +120,7 @@ public SPARSE InterpolateCubic(SPARSE start, SPARSE outgoingTangent, SPARSE end,
110120
}
111121
}
112122

123+
public static readonly ISamplerTraits<Boolean> Boolean = new _Boolean();
113124
public static readonly ISamplerTraits<Single> Scalar = new _Scalar();
114125
public static readonly ISamplerTraits<Vector2> Vector2 = new _Vector2();
115126
public static readonly ISamplerTraits<Vector3> Vector3 = new _Vector3();

src/SharpGLTF.Core/Animations/FastCurveSampler.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ namespace SharpGLTF.Animations
1111
/// <typeparam name="T">The value sampled at any offset</typeparam>
1212
readonly struct FastCurveSampler<T> : ICurveSampler<T>
1313
{
14+
#region lifecycle
15+
1416
/// <summary>
15-
/// Creates a new, read only <see cref="ICurveSampler{T}"/> that has been optimized for fast sampling.
17+
/// Creates a new, read only <see cref="ICurveSampler{T}"/> that has been optimized for fast sampling
1618
/// </summary>
1719
/// <remarks>
1820
/// Sampling a raw curve with a large number of keys can be underperformant. This code splits the keys into 1 second
@@ -24,8 +26,15 @@ namespace SharpGLTF.Animations
2426
/// <returns>The new, optimized curve sampler.</returns>
2527
public static ICurveSampler<T> CreateFrom<TKey>(IEnumerable<(float, TKey)> sequence, Func<(float, TKey)[], ICurveSampler<T>> chunkFactory)
2628
{
27-
// not enough keys, or not worth optimizing it.
28-
if (!sequence.Skip(3).Any()) return null;
29+
if (sequence == null) throw new ArgumentNullException(nameof(sequence));
30+
if (chunkFactory == null) throw new ArgumentNullException(nameof(chunkFactory));
31+
32+
#pragma warning disable CA1851
33+
if (!sequence.Skip(3).Any()) // not enough keys, or not worth optimizing it, use a standard sampler
34+
{
35+
return chunkFactory.Invoke(sequence.ToArray());
36+
}
37+
#pragma warning restore CA1851
2938

3039
var split = sequence
3140
.SplitByTime()
@@ -40,9 +49,17 @@ private FastCurveSampler(IEnumerable<ICurveSampler<T>> samplers)
4049
_Samplers = samplers.ToArray();
4150
}
4251

52+
#endregion
53+
54+
#region data
55+
4356
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
4457
private readonly ICurveSampler<T>[] _Samplers;
4558

59+
#endregion
60+
61+
#region API
62+
4663
public T GetPoint(float offset)
4764
{
4865
if (offset < 0) offset = 0;
@@ -53,5 +70,7 @@ public T GetPoint(float offset)
5370

5471
return _Samplers[index].GetPoint(offset);
5572
}
73+
74+
#endregion
5675
}
5776
}

src/SharpGLTF.Core/Memory/AttributeFormat.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ internal string _GetDebuggerDisplay()
3939

4040
#region predefined values
4141

42+
public static readonly AttributeFormat Byte1 = new AttributeFormat(ENCODING.UNSIGNED_BYTE);
43+
4244
public static readonly AttributeFormat Float1 = new AttributeFormat(ENCODING.FLOAT);
4345
public static readonly AttributeFormat Float2 = new AttributeFormat(DIMENSIONS.VEC2 ,ENCODING.FLOAT);
4446
public static readonly AttributeFormat Float3 = new AttributeFormat(DIMENSIONS.VEC3, ENCODING.FLOAT);

src/SharpGLTF.Core/Memory/EncodedArrays.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ public static void _CopyTo(this IEnumerable<Int32> src, IList<UInt32> dst, int d
6262
}
6363
}
6464

65+
public static void _CopyTo(this IEnumerable<Boolean> src, IList<UInt32> dst, int dstOffset = 0)
66+
{
67+
using (var ptr = src.GetEnumerator())
68+
{
69+
while (dstOffset < dst.Count && ptr.MoveNext())
70+
{
71+
dst[dstOffset++] = (Byte)(ptr.Current ? 1 : 0);
72+
}
73+
}
74+
}
75+
6576
public static void _CopyTo<T>(this IEnumerable<T> src, IList<T> dst, int dstOffset = 0)
6677
{
6778
using (var ptr = src.GetEnumerator())

src/SharpGLTF.Core/Memory/IAccessorArray.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,62 @@ IEnumerator IEnumerable.GetEnumerator()
105105

106106
bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); }
107107
}
108+
109+
readonly struct BooleanArrayOverIntegerArray : IAccessorArray<Boolean>
110+
{
111+
public BooleanArrayOverIntegerArray(IAccessorArray<uint> source) : this()
112+
{
113+
_Source = source;
114+
}
115+
116+
private readonly IAccessorArray<uint> _Source;
117+
118+
public bool this[int index]
119+
{
120+
get => _Source[index] != 0;
121+
set => _Source[index] = value ? (uint)1 : 0;
122+
}
123+
124+
public bool IsReadOnly => true;
125+
126+
public int Count => _Source.Count;
127+
128+
public bool Contains(Boolean item) { return IndexOf(item) >= 0; }
129+
130+
public int IndexOf(Boolean item)
131+
{
132+
var value = item ? (uint)1 : 0;
133+
return _Source.IndexOf(value);
134+
}
135+
136+
public void CopyTo(Boolean[] array, int arrayIndex)
137+
{
138+
for (int i = 0; i < Count; ++i)
139+
{
140+
array[i + arrayIndex] = this[i];
141+
}
142+
}
143+
144+
public IEnumerator<Boolean> GetEnumerator()
145+
{
146+
var c = _Source.Count;
147+
for (int i = 0; i < c; ++i) yield return this[i];
148+
}
149+
150+
IEnumerator IEnumerable.GetEnumerator()
151+
{
152+
var c = _Source.Count;
153+
for (int i = 0; i < c; ++i) yield return this[i];
154+
}
155+
156+
void IList<Boolean>.Insert(int index, Boolean item) { throw new NotSupportedException(); }
157+
158+
void IList<Boolean>.RemoveAt(int index) { throw new NotSupportedException(); }
159+
160+
void ICollection<Boolean>.Add(Boolean item) { throw new NotSupportedException(); }
161+
162+
void ICollection<Boolean>.Clear() { throw new NotSupportedException(); }
163+
164+
bool ICollection<Boolean>.Remove(Boolean item) { throw new NotSupportedException(); }
165+
}
108166
}

src/SharpGLTF.Core/Memory/MemoryAccessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void Update(BYTES data, MemoryAccessInfo encoding)
6666

6767
public IAccessorArray<T> AsArrayOf<T>()
6868
where T: unmanaged
69-
{
69+
{
7070
if (typeof(T) == typeof(UInt32)) return AsIntegerArray() as IAccessorArray<T>;
7171
if (typeof(T) == typeof(Single)) return AsScalarArray() as IAccessorArray<T>;
7272
if (typeof(T) == typeof(Vector2)) return AsVector2Array() as IAccessorArray<T>;

src/SharpGLTF.Core/Schema2/gltf.Accessors.Arrays.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ public IAccessorArray<UInt32> AsIndicesArray()
110110

111111
#region Vertex Buffer Arrays
112112

113+
public IAccessorArray<UInt32> AsIndexArray() => AsArrayOf<UInt32>();
114+
113115
public IAccessorArray<Single> AsScalarArray() => AsArrayOf<Single>();
114116

115117
public IAccessorArray<Vector2> AsVector2Array() => AsArrayOf<Vector2>();

src/SharpGLTF.Core/Schema2/gltf.AnimationSampler.cs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public interface IAnimationSampler<T>
6767
/// </remarks>
6868
sealed partial class AnimationSampler :
6969
IChildOfList<Animation>,
70+
IAnimationSampler<Boolean>,
7071
IAnimationSampler<Single>,
7172
IAnimationSampler<Vector2>,
7273
IAnimationSampler<Vector3>,
@@ -148,7 +149,29 @@ private Accessor _CreateInputAccessor(ROLIST input)
148149
return accessor;
149150
}
150151

151-
private Accessor _CreateOutputAccessor(ROLIST output)
152+
private Accessor _CreateOutputAccessor(IReadOnlyList<Boolean> output)
153+
{
154+
Guard.NotNull(output, nameof(output));
155+
Guard.MustBeGreaterThan(output.Count, 0, nameof(output.Count));
156+
157+
var root = LogicalParent.LogicalParent;
158+
159+
var buffer = root.CreateBufferView(output.Count * 1 * 1);
160+
161+
System.Diagnostics.Debug.Assert(buffer.ByteStride == 0);
162+
163+
var accessor = root.CreateAccessor("Animation.Output");
164+
165+
accessor.SetData(buffer, 0, output.Count, AttributeFormat.Byte1);
166+
167+
Memory.EncodedArrayUtils._CopyTo(output, accessor.AsArrayOf<UInt32>());
168+
169+
accessor.UpdateBounds();
170+
171+
return accessor;
172+
}
173+
174+
private Accessor _CreateOutputAccessor(IReadOnlyList<Single> output)
152175
{
153176
Guard.NotNull(output, nameof(output));
154177
Guard.MustBeGreaterThan(output.Count, 0, nameof(output.Count));
@@ -344,6 +367,15 @@ private static (Single[] Keys, TValue[] Values) _Split<TValue>(IReadOnlyDictiona
344367
return (keys, vals);
345368
}
346369

370+
internal void SetKeys(IReadOnlyDictionary<Single, Boolean> keyframes)
371+
{
372+
Guard.NotNullOrEmpty(keyframes, nameof(keyframes));
373+
374+
var (keys, values) = _Split(keyframes);
375+
_input = this._CreateInputAccessor(keys).LogicalIndex;
376+
_output = this._CreateOutputAccessor(values).LogicalIndex;
377+
}
378+
347379
internal void SetKeys(IReadOnlyDictionary<Single, Single> keyframes)
348380
{
349381
Guard.NotNullOrEmpty(keyframes, nameof(keyframes));
@@ -533,6 +565,20 @@ internal void SetCubicKeys(IReadOnlyDictionary<Single, (SPARSE8 TangentIn, SPARS
533565
_output = this._CreateOutputAccessor(values, expandedCount).LogicalIndex;
534566
}
535567

568+
/// <inheritdoc/>
569+
IEnumerable<(Single, Boolean)> IAnimationSampler<Boolean>.GetLinearKeys()
570+
{
571+
Guard.IsTrue(this.InterpolationMode == AnimationInterpolationMode.STEP, nameof(InterpolationMode));
572+
573+
var keys = this.Input.AsScalarArray();
574+
var frames = this.Output.AsIndexArray();
575+
var boolFrames = new BooleanArrayOverIntegerArray(frames);
576+
System.Diagnostics.Debug.Assert(frames.Count == boolFrames.Count);
577+
System.Diagnostics.Debug.Assert(frames.Count() == boolFrames.Count());
578+
579+
return keys.Zip(boolFrames, (key, val) => (key, val));
580+
}
581+
536582
/// <inheritdoc/>
537583
IEnumerable<(Single, Single)> IAnimationSampler<Single>.GetLinearKeys()
538584
{
@@ -627,6 +673,12 @@ internal void SetCubicKeys(IReadOnlyDictionary<Single, (SPARSE8 TangentIn, SPARS
627673
return keys.Zip(frames, (key, val) => (key, val));
628674
}
629675

676+
/// <inheritdoc/>
677+
IEnumerable<(Single, (Boolean, Boolean, Boolean))> IAnimationSampler<Boolean>.GetCubicKeys()
678+
{
679+
throw new NotSupportedException();
680+
}
681+
630682
/// <inheritdoc/>
631683
IEnumerable<(Single, (Single, Single, Single))> IAnimationSampler<Single>.GetCubicKeys()
632684
{
@@ -720,6 +772,19 @@ internal void SetCubicKeys(IReadOnlyDictionary<Single, (SPARSE8 TangentIn, SPARS
720772
return keys.Zip(frames, (key, val) => (key, SPARSE8.AsTuple(val.TangentIn, val.Value, val.TangentOut)) );
721773
}
722774

775+
/// <inheritdoc/>
776+
ICurveSampler<Boolean> IAnimationSampler<Boolean>.CreateCurveSampler(bool isolateMemory)
777+
{
778+
var xsampler = this as IAnimationSampler<Boolean>;
779+
780+
switch (this.InterpolationMode)
781+
{
782+
case AnimationInterpolationMode.STEP: return xsampler.GetLinearKeys().CreateSampler(isolateMemory);
783+
}
784+
785+
throw new NotImplementedException();
786+
}
787+
723788
/// <inheritdoc/>
724789
ICurveSampler<Single> IAnimationSampler<Single>.CreateCurveSampler(bool isolateMemory)
725790
{

src/SharpGLTF.Core/Schema2/gltf.Animations.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,23 @@ public void CreateMorphChannel(Node node, IReadOnlyDictionary<Single, (SparseWei
295295
.SetSampler(sampler);
296296
}
297297

298+
public void CreateVisibilityChannel(Node node, IReadOnlyDictionary<Single, Boolean> keyframes)
299+
{
300+
Guard.NotNull(node, nameof(node));
301+
Guard.MustShareLogicalParent(this, node, nameof(node));
302+
Guard.NotNullOrEmpty(keyframes, nameof(keyframes));
303+
304+
var sampler = this._CreateSampler(AnimationInterpolationMode.STEP);
305+
306+
sampler.SetKeys(keyframes);
307+
308+
var ext = node.GetExtension<_NodeVisibility>();
309+
310+
var pointerPath = $"/nodes/{node.LogicalIndex}/extensions/{_NodeVisibility.SCHEMANAME}/visible";
311+
312+
this._UseChannel(pointerPath).SetSampler(sampler);
313+
}
314+
298315
#endregion
299316

300317
#region Validation

0 commit comments

Comments
 (0)