using System;
using System.Collections.Generic;
namespace Oni.Dae
{
internal class Sampler : Entity
{
private readonly List inputs = new List();
private float outputScale = 1.0f;
public List Inputs => inputs;
public int FrameCount
{
get
{
var input = inputs.Find(i => i.Semantic == Semantic.Input);
if (input == null)
return 0;
return FMath.RoundToInt32(input.Source.FloatData.Last() * 60.0f) + 1;
}
}
public Sampler Scale(float scale)
{
var newSampler = new Sampler
{
outputScale = scale
};
newSampler.inputs.AddRange(inputs);
return newSampler;
}
public Sampler Split(int offset)
{
var newSampler = new Sampler();
foreach (var input in inputs)
{
var source = input.Source;
switch (input.Semantic)
{
case Semantic.Input:
newSampler.inputs.Add(input);
break;
case Semantic.Interpolation:
newSampler.inputs.Add(input);
break;
case Semantic.Output:
{
var data = new float[source.Count];
for (int i = 0; i < data.Length; i++)
data[i] = source.FloatData[i * source.Stride + offset];
newSampler.inputs.Add(new Input
{
Source = new Source(data, 1),
Semantic = input.Semantic
});
}
break;
case Semantic.InTangent:
case Semantic.OutTangent:
{
var data = new float[source.Count * 2];
for (int i = 0; i < data.Length; i++)
{
data[i + 0] = source.FloatData[i * source.Stride];
data[i + 1] = source.FloatData[i * source.Stride + (offset + 1)];
}
newSampler.inputs.Add(new Input
{
Source = new Source(data, 2),
Semantic = input.Semantic
});
}
break;
}
}
return newSampler;
}
public float[] Sample() => Sample(0, FrameCount - 1);
public float[] Sample(int start, int end)
{
var result = Sample(start, end, 0);
if (outputScale != 1.0f)
{
for (int i = 0; i < result.Length; i++)
result[i] *= outputScale;
}
return result;
}
private float[] Sample(int start, int end, int offset)
{
float[] input = null;
float[] output = null;
int outputStride = 1;
Vector2[] inTangent = null;
Vector2[] outTangent = null;
string[] interpolation = null;
foreach (var i in inputs)
{
switch (i.Semantic)
{
case Semantic.Input:
input = i.Source.FloatData;
break;
case Semantic.Output:
output = i.Source.FloatData;
outputStride = i.Source.Stride;
break;
case Semantic.InTangent:
inTangent = FloatArrayToVector2Array(i.Source.FloatData);
break;
case Semantic.OutTangent:
outTangent = FloatArrayToVector2Array(i.Source.FloatData);
break;
case Semantic.Interpolation:
interpolation = i.Source.NameData;
break;
}
}
if (offset >= outputStride)
throw new ArgumentException("The offset must be less than the output stride", "offset");
float[] result = new float[end - start + 1];
if (input == null || output == null || interpolation == null)
{
//
// If we don't have enough data to sample then we just return 0 for all frames.
//
return result;
}
if (output.Length == outputStride)
{
//
// If the output contains only one element then use that for all frames.
//
for (int i = 0; i < result.Length; i++)
result[i] = output[offset];
return result;
}
float inputFirst = input.First();
float outputFirst = output[offset];
float inputLast = input.Last();
float outputLast = output[output.Length - outputStride + offset];
for (int frame = 0; frame < result.Length; frame++)
{
float t = (frame + start) / 60.0f;
if (t <= inputFirst)
{
result[frame] = outputFirst;
continue;
}
if (t >= inputLast)
{
result[frame] = outputLast;
continue;
}
int index = Array.BinarySearch(input, t);
if (index >= 0)
{
result[frame] = output[index * outputStride + offset];
continue;
}
index = ~index;
if (index == 0)
{
result[frame] = outputFirst;
continue;
}
if (index * outputStride + offset >= output.Length)
{
result[frame] = outputLast;
continue;
}
var p0 = new Vector2(input[index - 1], output[(index - 1) * outputStride + offset]);
var p1 = new Vector2(input[index], output[index * outputStride + offset]);
float s = (t - p0.X) / (p1.X - p0.X);
switch (interpolation[index - 1])
{
default:
Console.Error.WriteLine("Interpolation type '{0}' is not supported, using LINEAR", interpolation[index - 1]);
goto case "LINEAR";
case "LINEAR":
result[frame] = p0.Y + s * (p1.Y - p0.Y);
break;
case "BEZIER":
if (inTangent == null || outTangent == null)
throw new System.IO.InvalidDataException("Bezier interpolation was specified but in/out tangents are not present");
var c0 = outTangent[index - 1];
var c1 = inTangent[index];
float invS = 1.0f - s;
result[frame] =
p0.Y * invS * invS * invS
+ 3.0f * c0.Y * invS * invS * s
+ 3.0f * c1.Y * invS * s * s
+ p1.Y * s * s * s;
break;
}
}
return result;
}
private static Vector2[] FloatArrayToVector2Array(float[] array)
{
var result = new Vector2[array.Length / 2];
for (int i = 0; i < result.Length; i++)
{
result[i].X = array[i * 2 + 0];
result[i].Y = array[i * 2 + 1];
}
return result;
}
}
}