Skip to content

Commit f2b858a

Browse files
Merge pull request #60 from Live2D/develop
Update to Cubism 5 SDK for Native R3
2 parents 0b5951b + d4f1ad7 commit f2b858a

File tree

5 files changed

+246
-21
lines changed

5 files changed

+246
-21
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77

8+
## [5-r.3] - 2025-02-18
9+
10+
### Added
11+
12+
* Add new motion loop processing that seamlessly connects the start and end points of the loop.
13+
* The `_isLoop` variable was moved from class `CubismMotion` to class `ACubismMotion`.
14+
* Add the setter for `_isLoop`, `SetLoop(csmBool loop)`, to class `ACubismMotion`.
15+
* Add the getter for `_isLoop`, `GetLoop()`, to class `ACubismMotion`.
16+
* The `_isLoopFadeIn` variable was moved from class `CubismMotion` to class `ACubismMotion`.
17+
* Add the setter for `_isLoopFadeIn`, `SetLoopFadeIn(csmBool loopFadeIn)`, to class `ACubismMotion`.
18+
* Add the getter for `_isLoopFadeIn`, `GetLoopFadeIn()`, to class `ACubismMotion`.
19+
* Add a variable `_motionBehavior` for version control to the `CubismMotion` class.
20+
21+
### Deprecated
22+
23+
* Deprecate the following elements due to the change in the variable declaration location.
24+
* `CubismMotion::IsLoop(csmBool loop)`
25+
* `CubismMotion::IsLoop()`
26+
* `CubismMotion::IsLoopFadeIn(csmBool loopFadeIn)`
27+
* `CubismMotion::IsLoopFadeIn()`
28+
29+
830
## [5-r.2] - 2024-12-19
931

1032
### Added
@@ -425,6 +447,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
425447
* Fix invalid expressions of `CubismCdiJson`.
426448

427449

450+
[5-r.3]: https://github.com/Live2D/CubismNativeFramework/compare/5-r.2...5-r.3
428451
[5-r.2]: https://github.com/Live2D/CubismNativeFramework/compare/5-r.1...5-r.2
429452
[5-r.1]: https://github.com/Live2D/CubismNativeFramework/compare/5-r.1-beta.4...5-r.1
430453
[5-r.1-beta.4]: https://github.com/Live2D/CubismNativeFramework/compare/5-r.1-beta.3...5-r.1-beta.4

src/Motion/ACubismMotion.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ ACubismMotion::ACubismMotion()
2222
: _fadeInSeconds(-1.0f)
2323
, _fadeOutSeconds(-1.0f)
2424
, _weight(1.0f)
25-
, _offsetSeconds(0.0f) //再生の開始時刻
25+
, _offsetSeconds(0.0f) // 再生の開始時刻
26+
, _isLoop(false) // trueから false へデフォルトを変更
27+
, _isLoopFadeIn(true) // ループ時にフェードインが有効かどうかのフラグ
28+
, _previousLoopState(_isLoop)
2629
, _onBeganMotion(NULL)
2730
, _onBeganMotionCustomData(NULL)
2831
, _onFinishedMotion(NULL)
@@ -70,13 +73,11 @@ void ACubismMotion::SetupMotionQueueEntry(CubismMotionQueueEntry* motionQueueEnt
7073
motionQueueEntry->SetStartTime(userTimeSeconds - _offsetSeconds); //モーションの開始時刻を記録
7174
motionQueueEntry->SetFadeInStartTime(userTimeSeconds); //フェードインの開始時刻
7275

73-
const csmFloat32 duration = GetDuration();
7476

7577
if (motionQueueEntry->GetEndTime() < 0)
7678
{
7779
//開始していないうちに終了設定している場合がある。
78-
motionQueueEntry->SetEndTime((duration <= 0) ? -1 : motionQueueEntry->GetStartTime() + duration);
79-
//duration == -1 の場合はループする
80+
AdjustEndTime(motionQueueEntry);
8081
}
8182

8283
if (this->_onBeganMotion != NULL)
@@ -160,6 +161,26 @@ void ACubismMotion::SetOffsetTime(csmFloat32 offsetSeconds)
160161
this->_offsetSeconds = offsetSeconds;
161162
}
162163

164+
void ACubismMotion::SetLoop(csmBool loop)
165+
{
166+
this->_isLoop = loop;
167+
}
168+
169+
csmBool ACubismMotion::GetLoop() const
170+
{
171+
return this->_isLoop;
172+
}
173+
174+
void ACubismMotion::SetLoopFadeIn(csmBool loopFadeIn)
175+
{
176+
this->_isLoopFadeIn = loopFadeIn;
177+
}
178+
179+
csmBool ACubismMotion::GetLoopFadeIn() const
180+
{
181+
return this->_isLoopFadeIn;
182+
}
183+
163184
const csmVector<const csmString*>& ACubismMotion::GetFiredEvent(csmFloat32 beforeCheckTimeSeconds, csmFloat32 motionTimeSeconds)
164185
{
165186
return _firedEventValues;
@@ -238,4 +259,16 @@ csmFloat32 ACubismMotion::GetModelOpacityValue() const
238259
return 1.0f;
239260
}
240261

262+
void ACubismMotion::AdjustEndTime(CubismMotionQueueEntry* motionQueueEntry)
263+
{
264+
const csmFloat32 duration = GetDuration();
265+
266+
// duration == -1 の場合はループする
267+
const csmFloat32 endTime = (duration <= 0) ?
268+
-1 :
269+
motionQueueEntry->GetStartTime() + duration;
270+
271+
motionQueueEntry->SetEndTime(endTime);
272+
}
273+
241274
}}}

src/Motion/ACubismMotion.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,34 @@ class ACubismMotion
126126
*/
127127
void SetOffsetTime(csmFloat32 offsetSeconds);
128128

129+
/**
130+
* Sets whether the motion should loop.
131+
*
132+
* @param loop true to set the motion to loop
133+
*/
134+
void SetLoop(csmBool loop);
135+
136+
/**
137+
* Checks whether the motion is set to loop.
138+
*
139+
* @return true if the motion is set to loop; otherwise false.
140+
*/
141+
csmBool GetLoop() const;
142+
143+
/**
144+
* Sets whether to perform fade-in for looping motion.
145+
*
146+
* @param loopFadeIn true to perform fade-in for looping motion
147+
*/
148+
void SetLoopFadeIn(csmBool loopFadeIn);
149+
150+
/**
151+
* Checks the setting for fade-in of looping motion.
152+
*
153+
* @return true if fade-in for looping motion is set; otherwise false.
154+
*/
155+
csmBool GetLoopFadeIn() const;
156+
129157
/**
130158
* Returns the triggered user data events.
131159
*
@@ -272,10 +300,15 @@ class ACubismMotion
272300

273301
virtual void DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSeconds, csmFloat32 weight, CubismMotionQueueEntry* motionQueueEntry) = 0;
274302

303+
void AdjustEndTime(CubismMotionQueueEntry* motionQueueEntry);
304+
275305
csmFloat32 _fadeInSeconds;
276306
csmFloat32 _fadeOutSeconds;
277307
csmFloat32 _weight;
278308
csmFloat32 _offsetSeconds;
309+
csmBool _isLoop;
310+
csmBool _isLoopFadeIn;
311+
csmBool _previousLoopState;
279312

280313
csmVector<const csmString*> _firedEventValues;
281314

src/Motion/CubismMotion.cpp

Lines changed: 114 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,34 @@ csmFloat32 InverseSteppedEvaluate(const CubismMotionPoint* points, const csmFloa
187187
return points[1].Value;
188188
}
189189

190-
csmFloat32 EvaluateCurve(const CubismMotionData* motionData, const csmInt32 index, csmFloat32 time)
190+
csmFloat32 CorrectEndPoint(
191+
const CubismMotionData* motionData,
192+
const csmInt32 segmentIndex,
193+
const csmInt32 beginIndex,
194+
const csmInt32 endIndex,
195+
const csmFloat32 time,
196+
const csmFloat32 endTime
197+
)
198+
{
199+
CubismMotionPoint motionPoint[2];
200+
motionPoint[0] = motionData->Points[endIndex];
201+
motionPoint[1] = motionData->Points[beginIndex];
202+
motionPoint[1].Time = endTime;
203+
204+
switch (motionData->Segments[segmentIndex].SegmentType)
205+
{
206+
case CubismMotionSegmentType_Linear:
207+
case CubismMotionSegmentType_Bezier:
208+
default:
209+
return LinearEvaluate(motionPoint, time);
210+
case CubismMotionSegmentType_Stepped:
211+
return SteppedEvaluate(motionPoint, time);
212+
case CubismMotionSegmentType_InverseStepped:
213+
return InverseSteppedEvaluate(motionPoint, time);
214+
}
215+
}
216+
217+
csmFloat32 EvaluateCurve(const CubismMotionData* motionData, const csmInt32 index, csmFloat32 time, const csmBool isCorrection, const csmFloat32 endTime)
191218
{
192219
// Find segment to evaluate.
193220
const CubismMotionCurve& curve = motionData->Curves[index];
@@ -215,6 +242,19 @@ csmFloat32 EvaluateCurve(const CubismMotionData* motionData, const csmInt32 inde
215242

216243
if (target == -1)
217244
{
245+
if (isCorrection && time < endTime)
246+
{
247+
// 終点から始点への補正処理
248+
return CorrectEndPoint(
249+
motionData,
250+
totalSegmentCount - 1,
251+
motionData->Segments[curve.BaseSegmentIndex].BasePointIndex,
252+
pointPosition,
253+
time,
254+
endTime
255+
);
256+
}
257+
218258
return motionData->Points[pointPosition].Value;
219259
}
220260

@@ -229,8 +269,7 @@ csmFloat32 EvaluateCurve(const CubismMotionData* motionData, const csmInt32 inde
229269
CubismMotion::CubismMotion()
230270
: _sourceFrameRate(30.0f)
231271
, _loopDurationSeconds(-1.0f)
232-
, _isLoop(false) // trueから false へデフォルトを変更
233-
, _isLoopFadeIn(true) // ループ時にフェードインが有効かどうかのフラグ
272+
, _motionBehavior(MotionBehavior_V2)
234273
, _lastWeight(0.0f)
235274
, _motionData(NULL)
236275
, _modelCurveIdEyeBlink(NULL)
@@ -282,6 +321,16 @@ void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSec
282321
_modelCurveIdOpacity = CubismFramework::GetIdManager()->GetId(IdNameOpacity);
283322
}
284323

324+
if (_motionBehavior == MotionBehavior_V2)
325+
{
326+
if (_previousLoopState != _isLoop)
327+
{
328+
// 終了時間を再計算する
329+
AdjustEndTime(motionQueueEntry);
330+
_previousLoopState = _isLoop;
331+
}
332+
}
333+
285334
csmFloat32 timeOffsetSeconds = userTimeSeconds - motionQueueEntry->GetStartTime();
286335

287336
if (timeOffsetSeconds < 0.0f)
@@ -320,12 +369,18 @@ void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSec
320369

321370
// 'Repeat' time as necessary.
322371
csmFloat32 time = timeOffsetSeconds;
372+
csmFloat32 duration = _motionData->Duration;
373+
csmBool isCorrection = _motionBehavior == MotionBehavior_V2 && _isLoop;
323374

324375
if (_isLoop)
325376
{
326-
while (time > _motionData->Duration)
377+
if (_motionBehavior == MotionBehavior_V2)
327378
{
328-
time -= _motionData->Duration;
379+
duration += 1.0f / _motionData->Fps;
380+
}
381+
while (time > duration)
382+
{
383+
time -= duration;
329384
}
330385
}
331386

@@ -335,7 +390,7 @@ void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSec
335390
for (c = 0; c < _motionData->CurveCount && curves[c].Type == CubismMotionCurveTarget_Model; ++c)
336391
{
337392
// Evaluate curve and call handler.
338-
value = EvaluateCurve(_motionData, c, time);
393+
value = EvaluateCurve(_motionData, c, time, isCorrection, duration);
339394

340395
if (curves[c].Id == _modelCurveIdEyeBlink)
341396
{
@@ -372,7 +427,7 @@ void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSec
372427
const csmFloat32 sourceValue = model->GetParameterValue(parameterIndex);
373428

374429
// Evaluate curve and apply value.
375-
value = EvaluateCurve(_motionData, c, time);
430+
value = EvaluateCurve(_motionData, c, time, isCorrection, duration);
376431

377432
if (eyeBlinkValue != FLT_MAX)
378433
{
@@ -492,21 +547,16 @@ void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSec
492547
}
493548

494549
// Evaluate curve and apply value.
495-
value = EvaluateCurve(_motionData, c, time);
550+
value = EvaluateCurve(_motionData, c, time, isCorrection, duration);
496551

497552
model->SetParameterValue(parameterIndex, value);
498553
}
499554

500-
if (timeOffsetSeconds >= _motionData->Duration)
555+
if (timeOffsetSeconds >= duration)
501556
{
502557
if (_isLoop)
503558
{
504-
motionQueueEntry->SetStartTime(userTimeSeconds); //最初の状態へ
505-
if (_isLoopFadeIn)
506-
{
507-
//ループ中でループ用フェードインが有効のときは、フェードイン設定し直し
508-
motionQueueEntry->SetFadeInStartTime(userTimeSeconds);
509-
}
559+
UpdateForNextLoop(motionQueueEntry, userTimeSeconds, time);
510560
}
511561
else
512562
{
@@ -522,6 +572,37 @@ void CubismMotion::DoUpdateParameters(CubismModel* model, csmFloat32 userTimeSec
522572
_lastWeight = fadeWeight;
523573
}
524574

575+
void CubismMotion::UpdateForNextLoop(CubismMotionQueueEntry* motionQueueEntry, const csmFloat32 userTimeSeconds, const csmFloat32 time)
576+
{
577+
switch (_motionBehavior)
578+
{
579+
case MotionBehavior_V2:
580+
default:
581+
motionQueueEntry->SetStartTime(userTimeSeconds - time); //最初の状態へ
582+
if (_isLoopFadeIn)
583+
{
584+
//ループ中でループ用フェードインが有効のときは、フェードイン設定し直し
585+
motionQueueEntry->SetFadeInStartTime(userTimeSeconds - time);
586+
}
587+
588+
if (this->_onFinishedMotion != NULL)
589+
{
590+
this->_onFinishedMotion(this);
591+
}
592+
break;
593+
case MotionBehavior_V1:
594+
// 旧ループ処理
595+
596+
motionQueueEntry->SetStartTime(userTimeSeconds); //最初の状態へ
597+
if (_isLoopFadeIn)
598+
{
599+
//ループ中でループ用フェードインが有効のときは、フェードイン設定し直し
600+
motionQueueEntry->SetFadeInStartTime(userTimeSeconds);
601+
}
602+
break;
603+
}
604+
}
605+
525606
void CubismMotion::Parse(const csmByte* motionJson, const csmSizeInt size)
526607
{
527608
_motionData = CSM_NEW CubismMotionData;
@@ -772,24 +853,42 @@ csmFloat32 CubismMotion::GetParameterFadeOutTime(CubismIdHandle parameterId) con
772853

773854
void CubismMotion::IsLoop(csmBool loop)
774855
{
856+
CubismLogWarning("IsLoop(csmBool loop) is a deprecated function. Please use SetLoop(csmBool loop).");
857+
775858
this->_isLoop = loop;
776859
}
777860

778861
csmBool CubismMotion::IsLoop() const
779862
{
863+
CubismLogWarning("IsLoop() is a deprecated function. Please use GetLoop().");
864+
780865
return this->_isLoop;
781866
}
782867

783868
void CubismMotion::IsLoopFadeIn(csmBool loopFadeIn)
784869
{
870+
CubismLogWarning("IsLoopFadeIn(csmBool loopFadeIn) is a deprecated function. Please use SetLoopFadeIn(csmBool loopFadeIn).");
871+
785872
this->_isLoopFadeIn = loopFadeIn;
786873
}
787874

788875
csmBool CubismMotion::IsLoopFadeIn() const
789876
{
877+
CubismLogWarning("IsLoopFadeIn() is a deprecated function. Please use GetLoopFadeIn().");
878+
790879
return this->_isLoopFadeIn;
791880
}
792881

882+
void CubismMotion::SetMotionBehavior(MotionBehavior motionBehavior)
883+
{
884+
_motionBehavior = motionBehavior;
885+
}
886+
887+
CubismMotion::MotionBehavior CubismMotion::GetMotionBehavior() const
888+
{
889+
return _motionBehavior;
890+
}
891+
793892
csmFloat32 CubismMotion::GetLoopDuration()
794893
{
795894
return _loopDurationSeconds;

0 commit comments

Comments
 (0)