From cd5af77cd1ef68239648bdbebf714ba4b39fb59e Mon Sep 17 00:00:00 2001 From: wada Date: Tue, 18 Feb 2025 11:18:03 +0900 Subject: [PATCH] Update to Cubism 5 SDK for Java R3 --- CHANGELOG.md | 40 ++ build.gradle | 2 +- .../framework/motion/ACubismMotion.java | 69 +++- .../cubism/framework/motion/CubismMotion.java | 341 ++++++++++++------ .../motion/CubismMotionInternal.java | 9 +- .../motion/CubismMotionQueueEntry.java | 2 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 8 files changed, 337 insertions(+), 132 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5961f1..b4bf1ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [5-r.3] - 2025-02-18 + +### Added + +* Add new motion loop processing that seamlessly connects the start and end points of the loop. + * The `isLooped` variable has been moved from the `CubismMotion` class to the `ACubismMotion` class as `isLoop`. + * Add the setter for `isLoop`, `setLoop(boolean loop)`, to class `ACubismMotion`. + * Add the getter for `isLoop`, `getLoop()`, to class `ACubismMotion`. + * The `isLoopFadeIn` variable was moved from class `CubismMotion` to class `ACubismMotion`. + * Add the setter for `isLoopFadeIn`, `setLoopFadeIn(boolean loopFadeIn)`, to class `ACubismMotion`. + * Add the getter for `isLoopFadeIn`, `getLoopFadeIn()`, to class `ACubismMotion`. + * Add a variable `motionBehavior` for version control to the `CubismMotion` class. + +### Changed + +* Change the compile and target SDK version of Android OS to 15.0 (API 35). + * Upgrade the version of Android Gradle Plugin from 8.1.1 to 8.6.1. + * Upgrade the version of Gradle from 8.2 to 8.7. + * Change the minimum version of Android Studio to Ladybug(2024.2.1). +* Change the arguments of `CsmMotionSegmentEvaluationFunction.evaluate` from `(float time, int basePointIndex)` to `(final List points, final float time)` to align with the Cubism SDK for Native codebase. + * Accordingly, change the implementation of the following methods. + * CubismMotion.LinearEvaluator.evaluate() + * CubismMotion.BezierEvaluator.evaluate() + * CubismMotion.BezierEvaluatorCardanoInterpretation.evaluate() + * CubismMotion.SteppedEvaluator.evaluate() + * CubismMotion.InverseSteppedEvaluator.evaluate() + * CubismMotion.bezierEvaluateBinarySearch() +* Change the access level of `CubismMotionQueueEntry` class to public. + +### Deprecated + +* Deprecate the following elements due to the change in the variable declaration location. + * `CubismMotion.isLoop(boolean loop)` + * `CubismMotion.isLoop()` + * `CubismMotion.isLoopFadeIn(boolean loopFadeIn)` + * `CubismMotion.isLoopFadeIn()` + + ## [5-r.2] - 2024-11-07 ### Added @@ -200,6 +238,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * New released! + +[5-r.3]: https://github.com/Live2D/CubismJavaFramework/compare/5-r.2...5-r.3 [5-r.2]: https://github.com/Live2D/CubismJavaFramework/compare/5-r.1...5-r.2 [5-r.1]: https://github.com/Live2D/CubismJavaFramework/compare/5-r.1-beta.3...5-r.1 [5-r.1-beta.3]: https://github.com/Live2D/CubismJavaFramework/compare/5-r.1-beta.2...5-r.1-beta.3 diff --git a/build.gradle b/build.gradle index 0fc313e..32e5ee8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.1.1' + classpath 'com.android.tools.build:gradle:8.6.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/ACubismMotion.java b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/ACubismMotion.java index 24e2373..ad314df 100644 --- a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/ACubismMotion.java +++ b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/ACubismMotion.java @@ -76,15 +76,9 @@ public void setupMotionQueueEntry( // Record the start time of fade-in motionQueueEntry.setFadeInStartTime(userTimeSeconds); - final float duration = getDuration(); - // Deal with the case where the status is set "end" before it has started. if (motionQueueEntry.getEndTime() < 0) { - // If duration == -1, loop motion. - float endTime = (duration <= 0) - ? -1 - : motionQueueEntry.getStartTime() + duration; - motionQueueEntry.setEndTime(endTime); + adjustEndTime(motionQueueEntry); } } @@ -207,6 +201,42 @@ public void setOffsetTime(float offsetSeconds) { this.offsetSeconds = offsetSeconds; } + /** + * Sets whether the motion should loop. + * + * @param loop true to set the motion to loop + */ + public void setLoop(boolean loop) { + isLoop = loop; + } + + /** + * Checks whether the motion is set to loop. + * + * @return true if the motion is set to loop; otherwise false. + */ + public boolean getLoop() { + return isLoop; + } + + /** + * Sets whether to perform fade-in for looping motion. + * + * @param loopFadeIn true to perform fade-in for looping motion + */ + public void setLoopFadeIn(boolean loopFadeIn) { + isLoopFadeIn = loopFadeIn; + } + + /** + * Checks the setting for fade-in of looping motion. + * + * @return true if fade-in for looping motion is set; otherwise false. + */ + public boolean getLoopFadeIn() { + return isLoopFadeIn; + } + /** * Check for event firing. * The input time reference is set to zero at the called motion timing. @@ -305,6 +335,17 @@ protected abstract void doUpdateParameters( CubismMotionQueueEntry motionQueueEntry ); + protected void adjustEndTime(CubismMotionQueueEntry motionQueueEntry) { + final float duration = getDuration(); + + // duration == -1 の場合はループする + final float endTime = (duration <= 0) + ? -1 + : motionQueueEntry.getStartTime() + duration; + + motionQueueEntry.setEndTime(endTime); + } + /** * 指定時間の透明度の値を返す。 * NOTE: 更新後の値を取るには`updateParameters()` の後に呼び出す。 @@ -331,6 +372,20 @@ protected float getModelOpacityValue() { * Start time for motion playback[s] */ protected float offsetSeconds; + + /** + * Enable/Disable loop + */ + protected boolean isLoop; + /** + * flag whether fade-in is enabled at looping. Default value is true. + */ + protected boolean isLoopFadeIn = true; + + /** + * The previous state of `_isLoop`. + */ + protected boolean previousLoopState = isLoop; /** * List of events that have fired */ diff --git a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotion.java b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotion.java index 39b7dd8..87fabda 100644 --- a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotion.java +++ b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotion.java @@ -23,6 +23,15 @@ * Motion class. */ public final class CubismMotion extends ACubismMotion { + /** + * Enumerator for version control of Motion Behavior. + * For details, see the SDK Manual. + */ + public enum MotionBehavior { + MOTION_BEHAVIOR_V1, + MOTION_BEHAVIOR_V2, + } + /** * Create an instance. * @@ -59,36 +68,74 @@ public static CubismMotion create(byte[] buffer) { * Set loop information. * * @param loop loop information + * + * @deprecated Not recommended due to the relocation of isLoop to the base class. + * Use ACubismMotion.setLoop(boolean loop) instead. **/ + @Deprecated public void isLooped(boolean loop) { - isLooped = loop; + CubismDebug.cubismLogWarning("isLoop(boolean loop) is a deprecated function. Please use setLoop(boolean loop)."); + super.setLoop(loop); } /** * Whether the motion loops. * * @return If it loops, return true. + * + * @deprecated Not recommended due to the relocation of isLoop to the base class. + * Use ACubismMotion.getLoop() instead. */ + @Deprecated public boolean isLooped() { - return isLooped; + CubismDebug.cubismLogWarning("isLoop() is a deprecated function. Please use getLoop()."); + return super.getLoop(); } /** * Set the fade-in information at looping. * * @param loopFadeIn fade-in information at looping + * + * @deprecated Not recommended due to the relocation of isLoopFadeIn to the base class. + * Use ACubismMotion.setLoopFadeIn(boolean loopFadeIn) instead. */ + @Deprecated public void isLoopFadeIn(boolean loopFadeIn) { - isLoopFadeIn = loopFadeIn; + CubismDebug.cubismLogWarning("isLoopFadeIn(boolean loopFadeIn) is a deprecated function. Please use setLoopFadeIn(boolean loopFadeIn)"); + super.setLoopFadeIn(loopFadeIn); } /** * Whether the motion fade in at looping. * * @return If it fades in, return true. + * + * @deprecated Not recommended due to the relocation of isLoopFadeIn to the base class. + * Use ACubismMotion.getLoopFadeIn() instead. */ + @Deprecated public boolean isLoopFadeIn() { - return isLoopFadeIn; + CubismDebug.cubismLogWarning("isLoopFadeIn() is a deprecated function. Please use getLoopFadeIn()."); + return super.getLoopFadeIn(); + } + + /** + * Sets the version of the Motion Behavior. + * + * @param motionBehavior the version of the Motion Behavior. + */ + public void setMotionBehavior(MotionBehavior motionBehavior) { + this.motionBehavior = motionBehavior; + } + + /** + * Gets the version of the Motion Behavior. + * + * @return Returns the version of the Motion Behavior. + */ + public MotionBehavior getMotionBehavior() { + return motionBehavior; } /** @@ -175,7 +222,7 @@ public void setEffectIds(List eyeBlinkParameterIds, List lip @Override public float getDuration() { - return isLooped + return isLoop ? -1.0f : loopDurationSeconds; } @@ -280,6 +327,14 @@ protected void doUpdateParameters( modelCurveIdOpacity = CubismFramework.getIdManager().getId(ID_NAME_OPACITY); } + if (motionBehavior == MotionBehavior.MOTION_BEHAVIOR_V2) { + if (previousLoopState != isLoop) { + // 終了時間を再計算する + adjustEndTime(motionQueueEntry); + previousLoopState = isLoop; + } + } + float timeOffsetSeconds = userTimeSeconds - motionQueueEntry.getStartTime(); @@ -301,10 +356,15 @@ protected void doUpdateParameters( // 'Repeat time as necessary' float time = timeOffsetSeconds; + float duration = motionData.duration; + boolean isCorrection = motionBehavior == MotionBehavior.MOTION_BEHAVIOR_V2 && isLoop; - if (isLooped) { - while (time > motionData.duration) { - time -= motionData.duration; + if (isLoop) { + if (motionBehavior == MotionBehavior.MOTION_BEHAVIOR_V2) { + duration += 1.0f / motionData.fps; + } + while (time > duration) { + time -= duration; } } @@ -328,7 +388,7 @@ protected void doUpdateParameters( } // Evaluate curve and call handler. - value = evaluateCurve(curve, time); + value = evaluateCurve(motionData, i, time, isCorrection, duration); if (curve.id.equals(modelCurveIdEyeBlink)) { eyeBlinkValue = value; @@ -369,7 +429,7 @@ protected void doUpdateParameters( final float sourceValue = model.getParameterValue(parameterIndex); // Evaluate curve and apply value. - value = evaluateCurve(curve, time); + value = evaluateCurve(motionData, i, time, isCorrection, duration); if (isUpdatedEyeBlink) { for (int j = 0; j < eyeBlinkParameterIds.size(); j++) { @@ -500,19 +560,13 @@ protected void doUpdateParameters( } // Evaluate curve and apply value. - value = evaluateCurve(curve, time); + value = evaluateCurve(motionData, i, time, isCorrection, duration); model.setParameterValue(parameterIndex, value); } - if (timeOffsetSeconds >= motionData.duration) { - if (isLooped) { - // Initialize - motionQueueEntry.setStartTime(userTimeSeconds); - - if (isLoopFadeIn) { - // If fade-in for loop is enabled in a loop, set fade-in again. - motionQueueEntry.setFadeInStartTime(userTimeSeconds); - } + if (timeOffsetSeconds >= duration) { + if (isLoop) { + UpdateForNextLoop(motionQueueEntry, userTimeSeconds, time); } else { if (onFinishedMotion != null) { onFinishedMotion.execute(this); @@ -523,6 +577,31 @@ protected void doUpdateParameters( lastWeight = fadeWeight; } + private void UpdateForNextLoop(CubismMotionQueueEntry motionQueueEntry, float userTimeSeconds, float time) { + switch (motionBehavior) { + case MOTION_BEHAVIOR_V1: + // 旧ループ処理 + motionQueueEntry.setStartTime(userTimeSeconds); //最初の状態へ + if (isLoopFadeIn) { + //ループ中でループ用フェードインが有効のときは、フェードイン設定し直し + motionQueueEntry.setFadeInStartTime(userTimeSeconds); + } + break; + case MOTION_BEHAVIOR_V2: + default: + motionQueueEntry.setStartTime(userTimeSeconds - time); //最初の状態へ + if (isLoopFadeIn) { + //ループ中でループ用フェードインが有効のときは、フェードイン設定し直し + motionQueueEntry.setFadeInStartTime(userTimeSeconds - time); + } + + if (this.onFinishedMotion != null) { + this.onFinishedMotion.execute(this); + } + break; + } + } + // ID private static final String ID_NAME_OPACITY = "Opacity"; @@ -555,74 +634,76 @@ private enum TargetName { } } - private class LinearEvaluator implements CsmMotionSegmentEvaluationFunction { + private static class LinearEvaluator implements CsmMotionSegmentEvaluationFunction { @Override - public float evaluate(float time, int basePointIndex) { - CubismMotionPoint p0 = motionData.points.get(basePointIndex); - CubismMotionPoint p1 = motionData.points.get(basePointIndex + 1); - - float t = (time - p0.time) / (p1.time - p0.time); + public float evaluate(final List points, final float time) { + float t = (time - points.get(0).time) / (points.get(1).time - points.get(0).time); if (t < 0.0f) { t = 0.0f; } - return p0.value + (p1.value - p0.value) * t; + + return points.get(0).value + ((points.get(1).value - points.get(0).value) * t); } } - private class BezierEvaluator implements CsmMotionSegmentEvaluationFunction { + private static class BezierEvaluator implements CsmMotionSegmentEvaluationFunction { @Override - public float evaluate(final float time, final int basePointIndex) { - final CubismMotionPoint p0 = motionData.points.get(basePointIndex); - final CubismMotionPoint p1 = motionData.points.get(basePointIndex + 1); - final CubismMotionPoint p2 = motionData.points.get(basePointIndex + 2); - final CubismMotionPoint p3 = motionData.points.get(basePointIndex + 3); - - float t = (time - p0.time) / (p3.time - p0.time); + public float evaluate(final List points, final float time) { + float t = (time - points.get(0).time) / (points.get(3).time - points.get(0).time); if (t < 0.0f) { t = 0.0f; } - return getLerpPointsValue(p0, p1, p2, p3, t); + final CubismMotionPoint p01 = lerpPoints(points.get(0), points.get(1), t); + final CubismMotionPoint p12 = lerpPoints(points.get(1), points.get(2), t); + final CubismMotionPoint p23 = lerpPoints(points.get(2), points.get(3), t); + + final CubismMotionPoint p012 = lerpPoints(p01, p12, t); + final CubismMotionPoint p123 = lerpPoints(p12, p23, t); + + return lerpPoints(p012, p123, t).value; } } - private class BezierEvaluatorCardanoInterpretation implements CsmMotionSegmentEvaluationFunction { + private static class BezierEvaluatorCardanoInterpretation implements CsmMotionSegmentEvaluationFunction { @Override - public float evaluate(final float time, final int basePointIndex) { - CubismMotionPoint p0 = motionData.points.get(basePointIndex); - CubismMotionPoint p1 = motionData.points.get(basePointIndex + 1); - CubismMotionPoint p2 = motionData.points.get(basePointIndex + 2); - CubismMotionPoint p3 = motionData.points.get(basePointIndex + 3); - - final float x1 = p0.time; - final float x2 = p3.time; - final float cx1 = p1.time; - final float cx2 = p2.time; + public float evaluate(final List points, final float time) { + final float x1 = points.get(0).time; + final float x2 = points.get(3).time; + final float cx1 = points.get(1).time; + final float cx2 = points.get(2).time; final float a = x2 - 3.0f * cx2 + 3.0f * cx1 - x1; final float b = 3.0f * cx2 - 6.0f * cx1 + 3.0f * x1; final float c = 3.0f * cx1 - 3.0f * x1; final float d = x1 - time; - float t = CubismMath.cardanoAlgorithmForBezier(a, b, c, d); + final float t = CubismMath.cardanoAlgorithmForBezier(a, b, c, d); + + final CubismMotionPoint p01 = lerpPoints(points.get(0), points.get(1), t); + final CubismMotionPoint p12 = lerpPoints(points.get(1), points.get(2), t); + final CubismMotionPoint p23 = lerpPoints(points.get(2), points.get(3), t); - return getLerpPointsValue(p0, p1, p2, p3, t); + final CubismMotionPoint p012 = lerpPoints(p01, p12, t); + final CubismMotionPoint p123 = lerpPoints(p12, p23, t); + + return lerpPoints(p012, p123, t).value; } } - private class SteppedEvaluator implements CsmMotionSegmentEvaluationFunction { + private static class SteppedEvaluator implements CsmMotionSegmentEvaluationFunction { @Override - public float evaluate(float time, int basePointIndex) { - return motionData.points.get(basePointIndex).value; + public float evaluate(final List points, final float time) { + return points.get(0).value; } } - private class InverseSteppedEvaluator implements CsmMotionSegmentEvaluationFunction { + private static class InverseSteppedEvaluator implements CsmMotionSegmentEvaluationFunction { @Override - public float evaluate(final float time, final int basePointIndex) { - return motionData.points.get(basePointIndex + 1).value; + public float evaluate(final List points, final float time) { + return points.get(1).value; } } @@ -630,13 +711,14 @@ public float evaluate(final float time, final int basePointIndex) { private static CubismMotionPoint lerpPoints( final CubismMotionPoint a, final CubismMotionPoint b, - final float t, - CubismMotionPoint motionPoint + final float t ) { - motionPoint.time = a.time + ((b.time - a.time) * t); - motionPoint.value = a.value + ((b.value - a.value) * t); + CubismMotionPoint result = new CubismMotionPoint(); + + result.time = a.time + ((b.time - a.time) * t); + result.value = a.value + ((b.value - a.value) * t); - return motionPoint; + return result; } /** @@ -864,52 +946,20 @@ private void parse(byte[] motionJson) { } } - private float getLerpPointsValue( - CubismMotionPoint p0, - CubismMotionPoint p1, - CubismMotionPoint p2, - CubismMotionPoint p3, - float t - ) { - lerpPoints(p0, p1, t, p01); - lerpPoints(p1, p2, t, p12); - lerpPoints(p2, p3, t, p23); - - lerpPoints(p01, p12, t, p012); - lerpPoints(p12, p23, t, p123); - - return lerpPoints(p012, p123, t, result).value; - } - - // These are only used by 'getLerpPointsValue' method. - // Avoid creating a new CubismMotionPoint instance in 'lerpPoints' method. - private final CubismMotionPoint p01 = new CubismMotionPoint(); - private final CubismMotionPoint p12 = new CubismMotionPoint(); - private final CubismMotionPoint p23 = new CubismMotionPoint(); - private final CubismMotionPoint p012 = new CubismMotionPoint(); - private final CubismMotionPoint p123 = new CubismMotionPoint(); - private final CubismMotionPoint result = new CubismMotionPoint(); - - private float bezierEvaluateBinarySearch(final float time, final int basePointIndex) { + private float bezierEvaluateBinarySearch(final CubismMotionPoint[] points, final float time) { final float x_error = 0.01f; - final CubismMotionPoint p0 = motionData.points.get(basePointIndex); - final CubismMotionPoint p1 = motionData.points.get(basePointIndex + 1); - final CubismMotionPoint p2 = motionData.points.get(basePointIndex + 2); - final CubismMotionPoint p3 = motionData.points.get(basePointIndex + 3); - - float x1 = p0.time; - float x2 = p3.time; - float cx1 = p1.time; - float cx2 = p2.time; + float x1 = points[0].time; + float x2 = points[3].time; + float cx1 = points[1].time; + float cx2 = points[2].time; float ta = 0.0f; float tb = 1.0f; float t = 0.0f; + int i = 0; - int i; - for (i = 0; i < 20; i++) { - + for (boolean var33 = true; i < 20; ++i) { if (time < x1 + x_error) { t = ta; break; @@ -923,10 +973,9 @@ private float bezierEvaluateBinarySearch(final float time, final int basePointIn float centerx = (cx1 + cx2) * 0.5f; cx1 = (x1 + cx1) * 0.5f; cx2 = (x2 + cx2) * 0.5f; - float ctrlx12 = (cx1 + centerx) * 0.5f; + final float ctrlx12 = (cx1 + centerx) * 0.5f; float ctrlx21 = (cx2 + centerx) * 0.5f; centerx = (ctrlx12 + ctrlx21) * 0.5f; - if (time < centerx) { tb = (ta + tb) * 0.5f; if (centerx - x_error < time) { @@ -936,7 +985,8 @@ private float bezierEvaluateBinarySearch(final float time, final int basePointIn x2 = centerx; cx2 = ctrlx12; - } else { + } + else { ta = (ta + tb) * 0.5f; if (time < centerx + x_error) { t = ta; @@ -952,24 +1002,69 @@ private float bezierEvaluateBinarySearch(final float time, final int basePointIn t = (ta + tb) * 0.5f; } - t = CubismMath.rangeF(t, 0.0f, 1.0f); + if (t < 0.0f) + { + t = 0.0f; + } + if (t > 1.0f) + { + t = 1.0f; + } - return getLerpPointsValue(p0, p1, p2, p3, t); + final CubismMotionPoint p01 = lerpPoints(points[0], points[1], t); + final CubismMotionPoint p12 = lerpPoints(points[1], points[2], t); + final CubismMotionPoint p23 = lerpPoints(points[2], points[3], t); + + final CubismMotionPoint p012 = lerpPoints(p01, p12, t); + final CubismMotionPoint p123 = lerpPoints(p12, p23, t); + + return lerpPoints(p012, p123, t).value; + } + + private float correctEndPoint( + final CubismMotionData motionData, + final int segmentIndex, + final int beginIndex, + final int endIndex, + final float time, + final float endTime + ) { + ArrayList motionPoint = new ArrayList(2); + { + final CubismMotionPoint src = motionData.points.get(endIndex); + motionPoint.add(new CubismMotionPoint(src.time, src.value)); + } + { + final CubismMotionPoint src = motionData.points.get(beginIndex); + motionPoint.add(new CubismMotionPoint(src.time, src.value)); + } + motionPoint.get(1).time = endTime; + + switch (motionData.segments.get(segmentIndex).segmentType) { + case STEPPED: + return steppedEvaluator.evaluate(motionPoint, time); + case INVERSESTEPPED: + return inverseSteppedEvaluator.evaluate(motionPoint, time); + case LINEAR: + case BEZIER: + default: + return linearEvaluator.evaluate(motionPoint, time); + } } - private float evaluateCurve(final CubismMotionCurve curve, final float time) { + private float evaluateCurve(final CubismMotionData motionData, final int index, float time, final boolean isCorrection, final float endTime) { // Find segment to evaluate. - int target = -1; + final CubismMotionCurve curve = motionData.curves.get(index); + int target = -1; final int totalSegmentCount = curve.baseSegmentIndex + curve.segmentCount; int pointPosition = 0; - - for (int i = curve.baseSegmentIndex; i < totalSegmentCount; i++) { + for (int i = curve.baseSegmentIndex; i < totalSegmentCount; ++i) { // Get first point of next segment. pointPosition = motionData.segments.get(i).basePointIndex + (motionData.segments.get(i).segmentType == CubismMotionSegmentType.BEZIER - ? 3 - : 1); + ? 3 + : 1); // Break if time lies within current segment. if (motionData.points.get(pointPosition).time > time) { @@ -979,12 +1074,25 @@ private float evaluateCurve(final CubismMotionCurve curve, final float time) { } if (target == -1) { + if (isCorrection && time < endTime) { + // 終点から始点への補正処理 + return correctEndPoint( + motionData, + totalSegmentCount - 1, + motionData.segments.get(curve.baseSegmentIndex).basePointIndex, + pointPosition, + time, + endTime + ); + } + return motionData.points.get(pointPosition).value; } final CubismMotionSegment segment = motionData.segments.get(target); - return segment.evaluator.evaluate(time, segment.basePointIndex); + final List points = motionData.points.subList(segment.basePointIndex, motionData.points.size()); + return segment.evaluator.evaluate(points, time); } /** @@ -995,14 +1103,9 @@ private float evaluateCurve(final CubismMotionCurve curve, final float time) { * length of the sequence of motions defined in the motion3.json file. */ private float loopDurationSeconds = -1.0f; - /** - * enable/Disable loop - */ - private boolean isLooped; - /** - * flag whether fade-in is enabled at looping. Default value is true. - */ - private boolean isLoopFadeIn = true; + + private MotionBehavior motionBehavior = MotionBehavior.MOTION_BEHAVIOR_V2; + /** * last set weight */ diff --git a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionInternal.java b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionInternal.java index 373d3e1..24e03df 100644 --- a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionInternal.java +++ b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionInternal.java @@ -68,6 +68,13 @@ public static class CubismMotionPoint { * value */ public float value; + + public CubismMotionPoint() {} + + public CubismMotionPoint(final float time, final float value) { + this.time = time; + this.value = value; + } } /** @@ -178,6 +185,6 @@ public static class CubismMotionData { * For strategy pattern. */ public interface CsmMotionSegmentEvaluationFunction { - float evaluate(float time, int basePointIndex); + float evaluate(final List points, final float time); } } diff --git a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionQueueEntry.java b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionQueueEntry.java index f19f8b9..8d1d1e6 100644 --- a/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionQueueEntry.java +++ b/framework/src/main/java/com/live2d/sdk/cubism/framework/motion/CubismMotionQueueEntry.java @@ -10,7 +10,7 @@ /** * Manager class for each motion being played by CubismMotionQueueManager. */ -class CubismMotionQueueEntry { +public class CubismMotionQueueEntry { /** * Get the motion. * diff --git a/gradle.properties b/gradle.properties index 83ae449..0529722 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,8 +18,8 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true # Android SDK version that will be used as the compiled project -PROP_COMPILE_SDK_VERSION=34 +PROP_COMPILE_SDK_VERSION=35 # Android SDK version that will be used as the earliest version of android this application can run on PROP_MIN_SDK_VERSION=21 # Android SDK version that will be used as the latest version of android this application has been tested on -PROP_TARGET_SDK_VERSION=34 +PROP_TARGET_SDK_VERSION=35 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 27fbb4c..bc23f3d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jul 11 18:25:17 JST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists