Problem
When exporting a Live2D model, any parameter curve that has only one keyframe (i.e. a parameter held constant for the whole motion) is written to motion3.json as a curve whose Segments array contains only the initial point [time, value] with no segment after it.
This is invalid per the motion3.json spec (a curve must contain at least one segment). Such a file loads fine in the Unity runtime (it never parses motion3.json), but crashes the Cubism Editor "Viewer for OW" and the Cubism SDK motion parser :
java.lang.IndexOutOfBoundsException: Index N out of bounds for length N
at com.live2d.sdk.cubism...
Reproduce
- Extract a Live2D model that contains a motion where at least one parameter curve has a single keyframe (constant value).
- Open the exported
*.model3.json (or *.moc3) in Cubism Editor's Viewer for OW (or load it with any Cubism SDK).
- Loading aborts with
IndexOutOfBoundsException: Index N out of bounds for length N.
Cause
In AssetStudioUtility/CubismLive2DExtractor/CubismMotion3Json.cs:
Each curve's Segments is seeded with the initial point, then segments are appended in a loop:
Segments = new List<float> { firstTime, firstValue };
for (var j = 1; j < curve.Count; j++)
AddSegments(...);
If curve.Count == 1, the loop never runs, so Segments stays [time, value] (no segment). A Cubism parser reads the initial point, advances the segment position to index 2, then unconditionally reads the segment type at index 2 — past the end of the 2-element array. Because the SDK pre-sizes its global point/segment buffers from Meta.TotalSegmentCount / Meta.TotalPointCount, the overrun surfaces as Index N out of bounds for length N.
Proposed fix
After the segment loop in both constructors, if no segment was produced, append a constant Linear segment spanning to
the motion duration, and update the counters.
Added to CubismMotion3Json:
private static void EnsureTerminatingSegment(
SerializableCurve cubismCurve, float duration, float fps,
ref int totalPointCount, ref int totalSegmentCount)
{
if (cubismCurve.Segments.Count > 2) // already has at least one segment
return;
var startTime = cubismCurve.Segments[0];
var value = cubismCurve.Segments[1];
var step = fps > 0f ? 1f / fps : 1f / 30f;
var endTime = duration > startTime ? duration : startTime + step;
cubismCurve.Segments.Add(0f); // Linear segment ID
cubismCurve.Segments.Add(endTime);
cubismCurve.Segments.Add(value);
totalPointCount += 1;
totalSegmentCount++;
}
Call it right after each segment-building loop:
// constructor (CubismFadeMotionData):
} // end of: for (var j = 1; j < ...m_Curve.Count; j++)
EnsureTerminatingSegment(Curves[actualCurveCount], Meta.Duration, Meta.Fps, ref totalPointCount, ref totalSegmentCount);
actualCurveCount++;
totalPointCount++;
// constructor (ImportedKeyframedAnimation):
} // end of: for (var j = 1; j < track.Curve.Count; j++)
EnsureTerminatingSegment(Curves[i], Meta.Duration, Meta.Fps, ref totalPointCount, ref totalSegmentCount);
totalPointCount++;
Problem
When exporting a Live2D model, any parameter curve that has only one keyframe (i.e. a parameter held constant for the whole motion) is written to
motion3.jsonas a curve whoseSegmentsarray contains only the initial point[time, value]with no segment after it.This is invalid per the motion3.json spec (a curve must contain at least one segment). Such a file loads fine in the Unity runtime (it never parses
motion3.json), but crashes the Cubism Editor "Viewer for OW" and the Cubism SDK motion parser :Reproduce
*.model3.json(or*.moc3) in Cubism Editor's Viewer for OW (or load it with any Cubism SDK).IndexOutOfBoundsException: Index N out of bounds for length N.Cause
In
AssetStudioUtility/CubismLive2DExtractor/CubismMotion3Json.cs:Each curve's
Segmentsis seeded with the initial point, then segments are appended in a loop:If curve.Count == 1, the loop never runs, so Segments stays [time, value] (no segment). A Cubism parser reads the initial point, advances the segment position to index 2, then unconditionally reads the segment type at index 2 — past the end of the 2-element array. Because the SDK pre-sizes its global point/segment buffers from Meta.TotalSegmentCount / Meta.TotalPointCount, the overrun surfaces as Index N out of bounds for length N.
Proposed fix
After the segment loop in both constructors, if no segment was produced, append a constant Linear segment spanning to
the motion duration, and update the counters.
Added to CubismMotion3Json:
Call it right after each segment-building loop: