live2d model

This commit is contained in:
gcw_4spBpAfv
2026-03-02 09:25:50 +08:00
parent d63d4b03cf
commit 2f6166ab6c
179 changed files with 100625 additions and 2018 deletions

View File

@@ -0,0 +1,287 @@
# Changelog
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.4.1] - 2025-07-17
### Changed
* Implement support for Android 16KB page size.
* See `CHANGELOG.md` in Core.
## [5-r.4] - 2025-05-15
### Added
* Add an API to `CubismMotionJson` for verifying the consistency of `motion3.json`.
* Add a flag to the arguments of the following methods to enable the function that verifies the consistency of `motion3.json`:
* `CubismUserModel.loadMotion()`
* `CubismMotion.create()`
* `CubismMotion.parse()`
* Add parameter repeat processing that connects the right and left ends of the parameter to create a loop, allowing the motion to repeat.
* Add the variable `isOverriddenParameterRepeat` to the `CubismModel` class for managing parameter repeat flags at the model level.
* Add the variable `userParameterRepeatDataList` to the `CubismModel` class for managing parameter repeat flags for each parameter.
* Add a `getPartParentPartIndices()` function.
### Changed
* Change the access level of the private members in the `CubismModelSettingJson` class to protected.
* Change the default JDK version for compilation to 17 using Gradle's Java toolchain.
### Fixed
* Fix an issue in the `CubismPose` class where the opacity calculation for non-displayed parts differed from the implementation in the other Cubism SDK.
## [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<CubismMotionPoint> 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
* Add function to get `CombinedParameters` listed in `cdi3.json`.
* Add the functionality to call a function when motion playback starts.
### Changed
* Change an expression "overwrite" to "override" for multiply color, screen color, and culling to adapt the actual behavior.
* Change the access level of `CubismMotionJson` class to public.
* Change the threshold for enabling anisotropic filtering.
### Fixed
* Fix a bug in which a method to acquire events fired during motion playback returned incorrect values when called multiple times.
* Fix a potential problem with division by 0 when a pose fade time is set to 0 seconds.
* Fix a bug that thrown an exception when playing CubismExpresionMotion with CubismMotionQueueManager.startMotion().
## [5-r.1] - 2024-03-26
### Added
* Add type constraint to the generics type of `getRenderer` function in `CubismUserModel`.
* Add function `modF()` to compute floating-point remainder in `CubismMath` class.
### Changed
* Change the default value of the flag for debugging from `true` to `false`.
* Change to output log if the argument `motionQueueEntry` is `null` in the `updateFadeWeight()` function of the `ACubismMotion` class.
### Deprecated
* Deprecate the `fadeWeight` variable and the `getFadeWeight()` function of the `CubismExpressionMotion` class.
* The `fadeWeight` variable of the `CubismExpressionMotion` class can cause problems.
* Please use the `getFadeWeight()` function of the `CubismExpressionMotionManager` class with one argument from now on.
* The `startMotion()` function of the `CubismMotionQueueManager` class with the unnecessary second argument `userTimeSeconds` is deprecated.
* Please use the `startMotion()` function with one argument from now on.
### Fixed
* Fix a bug that caused incorrect weight values when expression motions were shared by multiple models.
* Change the way fadeWeight is managed for expression motions.
## [5-r.1-beta.3] - 2024-01-18
### Added
* Add exception catching and error logging handling when an exception is thrown while loading a JSON file.
### Changed
* Change the compile and target SDK version of Android OS to 14.0 (API 34).
* Upgrade the version of Android Gradle Plugin from 8.0.2 to 8.1.1.
* Upgrade the version of Gradle from 8.1.1 to 8.2.
* Change the minimum version of Android Studio to Hedgehog(2023.1.1).
* Change the visibility of the `CubismPhysicsInternal` and `CubismPhysicsJson` classes to `public`.
### Fixed
* Fix an issue where models with a specific number of masks could not be drawn correctly.
* Replace deprecated notation in `build.gradle`.
## [5-r.1-beta.2] - 2023-09-28
### Added
* Add final modifier to some private fields in `CubismModel`.
### Changed
* Change getter functions to get some data without `getIdManager` in `CubismModel`.
* Change some private fields in `CubismModel` to `final` variable.
## [5-r.1-beta.1] - 2023-08-17
### Added
* Add the function to get the ID of a given parameter.(`CubismModel.getParameterId`)
* Add the `CubismExpressionMotionManager` class.
### Changed
* Change the minimum support version of Android OS to 5.0 (API 21).
* Unify Offscreen drawing-related terminology with `OffscreenSurface`.
* Change the visibility of the `CubismId` constructor to package-private.
### Fixed
* Fix the structure of the class in renderer.
* Separate the high precision mask process from the clipping mask setup process.
* Fix a bug that the value applied by multiply was not appropriate during expression transitions.
* Fix a issue that `CubismIdManager` was not used when retrieving `CubismId`.
* Please use `CubismFramework.getIdManager().getId` to get `CubismId`.
### Removed
* Remove an unnecessary dependency from `build.gradle`.
* Remove several arguments of `drawMesh` function.
## [4-r.1] - 2023-05-25
### Added
* Add some functions for checking consistency of MOC3 files.
* Add the function of checking consistency in `CubismMoc.create()`.
* Add the function of checking consistency before loading a model. (`CubismUserModel.loadModel()`)
* Add some functions to change Multiply and Screen colors on a per part basis.
### Changed
* Change access modifiers for methods in `CubismExpressionMotion`. And also chenge it to non-final class, allowing it to be extended by inheritance.
* Change to get opacity according to the current time of the motion.
### Fixed
* Refactor codes of cacheing vertex information in renderer.
* This change does not affect the behavior of this SDK.
* Fix a crash when specifying the number of mask buffers as an integer less than or equal to 0 in the second argument of `setupRenderer` function in `CubismUserModel`.
* Fix the redundant process regarding the renderer to make the code more concise.
* Optimize a drawing process of clipping masks.
* `CubismClippingManagerAndroid` class has a flag to indicate whether mask textures have been cleared or not, and the texture clearing process is only called if they have not been cleared.
## [4-r.1-beta.4] - 2023-03-16
### Fixed
* Fix some problems related to Cubism Core.
* See `CHANGELOG.md` in Core.
## [4-r.1-beta.3] - 2023-03-10
### Added
* Add function to validate MOC3 files.
## [4-r.1-beta.2] - 2023-01-26
### Added
* Add a description of type package to `README.md`.
### Changed
* Change Android SDK API level from 31 (Android 12) to 33 (Android 13).
* Change the name and package of the `CubismRectangle` class to `type/csmRect` to match SDK for Native.
* Move constants related to debugging from `CubismFramework` class to the newly created `CubismFrameworkConfig` class.
* Change implementation to hide `CubismJsonString` from shallow layers. The following functions are affcted by this change.
* `getLayoutMap` function in `ICubismModelSetting` class
* `getLayoutMap` function in `CubismModelSettingJson` class
* `setupFromLayout` function in `CubismModelMatrix` class
* Change the name and arguments of `createRenderer` function in `CubismUserModel`.
* The `RendererType` enumurator is abolished. Please generate a renderer you want to use by yourself and put it in the function as an argument.
### Fixed
* Fix JSON data parsing process to improve performance.
* Fix a problem where `setClippingMaskBufferSize` in `CubismRendererAndroid` would be thrown a `NullPointerException` if there are no clipping masks in the model.
### Removed
* Remove dependencies not to be used.
* Remove the unused method `getMotionMap` in `ICubismModelSetting` and `CubismModelSettingJson` class.
## [4-r.1-beta.1] - 2022-12-08
### Added
* Add support for high-precision masks.
* Implement to throw an exception when a user attempt to give null value to a setter method.
* Add API to allow users to configure culling.
* The number of render textures used can now be increased arbitrarily.
* The maximum number of masks when using multiple render textures has been increased to "number of render textures * 32".
### Changed
* Change the visibility of field variables in CubismClippingContext class from private to public and remove the getter and setter methods.
* Change the specification of the logging functions in `CubismDebug` so that they can take a format string as an argument.
### Fixed
* Change `radianToDirection` function in `CubismMath` so that an instance of CubismVector2 created by an API user is given as second argument, and the calculation result is stored in that instance and returned.
* Change the type of cache variables for vertex information used in `doDrawModel` function in `CubismRendererAndroid` and `setupClippingContext` function in `CubismClippingManagerAndroid` from `Map` to array.
* The cost of converting `int` type to `Integer` type (auto-boxing) that was incurred every frame was removed by this change.
* Fix `updateParticles` and `updateParticlesForStabilization` function in `CubismPhysics` not to create an instance of CubismVector2.
## 4-r.1-alpha.1 - 2022-10-06
### Added
* New released!
[5-r.4.1]: https://github.com/Live2D/CubismJavaFramework/compare/5-r.4...5-r.4.1
[5-r.4]: https://github.com/Live2D/CubismJavaFramework/compare/5-r.3...5-r.4
[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
[5-r.1-beta.2]: https://github.com/Live2D/CubismJavaFramework/compare/5-r.1-beta.1...5-r.1-beta.2
[5-r.1-beta.1]: https://github.com/Live2D/CubismJavaFramework/compare/4-r.1...5-r.1-beta.1
[4-r.1]: https://github.com/Live2D/CubismJavaFramework/compare/4-r.1-beta.4...4-r.1
[4-r.1-beta.4]: https://github.com/Live2D/CubismJavaFramework/compare/4-r.1-beta.3...4-r.1-beta.4
[4-r.1-beta.3]: https://github.com/Live2D/CubismJavaFramework/compare/4-r.1-beta.2...4-r.1-beta.3
[4-r.1-beta.2]: https://github.com/Live2D/CubismJavaFramework/compare/4-r.1-beta.1...4-r.1-beta.2
[4-r.1-beta.1]: https://github.com/Live2D/CubismJavaFramework/compare/4-r.1-alpha.1...4-r.1-beta.1

View File

@@ -0,0 +1,371 @@
# Changelog
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/).
## 2025-07-17
### Changed
* [Unity,Native,Java] Implement support for Android 16KB page size.
## 2025-04-24
### Added
* Add the function `csmGetParameterRepeats`.
* This function retrieves whether the parameters are set to repeat.
### Changed
* Upgrade Core version to 05.01.0000.
### Fixed
* Fix `csmGetParameterKeyCounts()` and `csmGetParameterKeyValues()` symbols in the DLL.
## 2024-12-19
### Removed
* [Native] Remove Visual Studio 2013 (MSVC 120) static library.
## 2024-11-07
### Added
* [Native] Add experimental support `arm64` library for linux.
### Removed
* [Unity,Native,Java] Remove Android ARM v7 library.
## 2024-04-04
### Added
* [Unity] Add library(.so) for HarmonyOS build.
## 2024-03-26
### Remove
* [Unity] Remove built with Emscripten 1.38.48.
* Unity 2021.2 or later uses only Core under `Assets/Live2D/Cubism/Plugins/Experimental/Emscripten/latest`.
## 2023-09-28
### Remove
* Remove bitcode from IOS build.
## 2023-08-17
### Added
* Enhance Blend Shape features.
* Please see [here](https://docs.live2d.com/en/cubism-editor-manual/blend-shape/).
### Changed
* Upgrade Core version to 05.00.0000.
## 2023-05-09
### Changed
* Change the GCC version of the library for Linux from 6.5.0 to 8.3.0.
## 2023-03-16
### Fixed
* Fix a case in which the index of the mask's drawable object was negative value for `csmGetDrawableMasks()`.
* Fix a problem in which `csmHasMocConsistency()` was returned as 0 even though the MOC3 file was in the correct format.
* This problem was occurring in some models using the blendshape weight limit settings.
* Fix a problem that could cause a crash if a MOC3 file that is not in the correct format is loaded with `csmHasMocConsistency()`.
### Changed
* Upgrade Core version to 04.02.0004.
## 2023-03-10
### Added
* Add the function `csmHasMocConsistency`.
* This function verifies that the `MOC3` file is valid.
### Changed
* Upgrade Core version to 04.02.0003.
## 2023-02-21
### Added
* [Web] Added classes related to `Memory`.
* Add the funciton `initializeAmountOfMemory()` to adjust the amount of memory at initialization.
## 2022-10-28
### Fixed
* [Java] Remove unnecessary methods.
## 2022-10-06
### Added
* [Java] Add AAR file for Android.
## 2022-09-08
### Added
* Add the multilingual supported documents.
* Support Visual Studio 2022.
## 2022-08-04
### Fixed
* [Web] Fix `csmGetMocVersion` function argument.
## 2022-07-07
### Added
* Add functions
* `csmGetParameterTypes`
* `csmGetDrawableParentPartIndices`
* Add type `csmMocVersion` and enum. This type is the return value of `csmGetMocVersion`, `csmGetLatestMocVersion`.
### Changed
* Upgrade Core version to 04.02.0002.
## 2022-06-02
### Changed
* Upgrade Core version to 04.02.0001.
### Fixed
* Fixed a bug that caused Multiply Color / Screen Color of different objects to be applied.
## 2022-05-19
### Added
* Support new Multiply Color / Screen Color features.
* Support new Blend Shape features.
### Changed
* Upgrade Core version to 04.02.0000. This upgrade is following Cubism Editor 4.2 features.
## 2022-02-10
### Added
* [Unity] Add bitcode library(.bc) for Emscripten latest version build.
### Changed
* [Unity] Change the bitcode file directory location.
* emsdk latest version build bitcode file in `latest` directory.
* emsdk 1.38.48 build bitcode file in `1_38_48` directory.
## 2021-12-09
### Added
* Add static library(.a) for Mac Catalyst.
## 2021-10-07
### Added
* Add `x86_64` library for Android.
* Add `arm64` library for macOS.
## 2021-03-09
### Added
* Add funtcions for Viewer.
* `csmGetParameterKeyCounts`
* `csmGetParameterKeyValues`
### Changed
* Update Core version to `04.01.0000`.
## 2020-01-30
### Added
* Add static library(.lib) for statically linking DLL.
* Add symbol file for Windows dynamic library (dll).
## 2019-11-19
### Fixed
* Fix linking static libraries for Windows (.lib).
## 2019-11-14
### Added
* Support Visual Studio 2019.
* Support macOS dynamic library (dylib).
### Changed
* Update Windows dynamic library: Use Visual Studio 2019 for building.
### Security
* Bundle certificate and notary ticket to macOS shared library.
## 2019-09-04
### Added
* Support new Inverted Masking features.
* Support ARM64 architecture for Universal Windows Platform.
### Changed
* Upgrade Core version to 04.00.0000 (67108864). This upgrade is following Cubism Editor 4.0 features.
* Add calling convention for *Windows/x86 DLL* only.
### Removed
* Remove bitcode binary due to suspension of *Cubism Bindings.*
## 2019-04-09
### Added
* Support Universal Windows Platform for Windows Store Application.
## 2019-01-31
### Added
* Add API to get the parent part of the specified part.
* Add API to get moc3 version.
## 2018-12-20
### Added
* [Native] Add new function: `csmGetPartParentPartIndices`.
* [Native, 3.3 Support] Support new Warp Deformer features.
### Changed
* Upgrade Core version to 03.03.0000 (50528256). This upgrade is following Cubism Editor 3.3 features.
## 2018-08-22
### Added
* [Native] Add support for Neon.
## 2018-05-14
### Added
* [Native] Add Windows **Visual C++ 2013** library.
* [Windows] Add runtime library choice `MT`, `MD`, `MTd`, `MDd`.
* [iOS] Add support for iPhone Simulator SDK.
### Fixed
* Fix an error occurred when linking libraries for Android `arm64-v8a`.
## 2017-11-17
### Fixed
* Fix processing of vertex index.
## 2017-10-05
### Added
* Provide bitcode for iOS.
## 2017-08-09
### Added
* [Native] Add Android *arm64-v8a* ABI library.
### Fixed
* Fix drawing order in certain scenarios.
## 2017-07-12
### Added
* Add experimental support for Emscripten.
* Add `CHANGELOG.md`.
### Fixed
* Fix access violation in certain scenarios.
* Fix update result in certain scenarios.
## 2017-05-02
### Added
* [Native] Add experimental support for Raspberry PI.
* Add `README.md`.

View File

@@ -0,0 +1,7 @@
## Live2D Proprietary Software License
Live2D Cubism Core is available under Live2D Proprietary Software License.
* [Live2D Proprietary Software License Agreement](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html)
* [Live2D Proprietary Software 使用許諾契約書](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_jp.html)
* [Live2D Proprietary Software 使用授权协议](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_cn.html)

View File

@@ -0,0 +1,22 @@
[English](README.md) / [日本語](README.ja.md)
---
# Live2D Cubism Core
このフォルダーには、Javaアプリケーションを開発するためのコアライブラリファイルが含まれています。
## ファイルリスト
### Live2DCubismCore.aar
このファイルには、CubismCoreの機能といくつかのラッパーが含まれています。
Javaで開発する場合は、このファイルを使用してください。
## ライブラリリスト
| プラットフォーム | アーキテクチャ | jar | aar | パス | 注記 |
| --- |----------|---|-----|---|-----|
| Android | ARM64 | | ✓ | android/Live2DCubismCore.aar | |
| Android | x86 | | ✓ | android/Live2DCubismCore.aar | |
| Android | x86_64 | | ✓ | android/Live2DCubismCore.aar | |

View File

@@ -0,0 +1,22 @@
[English](README.md) / [日本語](README.ja.md)
---
# Live2D Cubism Core
This folder contains core library files for developing Java applications.
## File List
### Live2DCubismCore.aar
This file contains Cubism Core features and some wrapper features.
Use this file when developing with Java.
## Library List
| Platform | Architecture | jar | aar | Path | Note |
| --- | --- | --- | --- |------------------------------| --- |
| Android | ARM64 | |✓ | android/Live2DCubismCore.aar | |
| Android | x86 | | ✓ | android/Live2DCubismCore.aar | |
| Android | x86_64 | | ✓ | android/Live2DCubismCore.aar | |

View File

@@ -0,0 +1,4 @@
The following is a list of files available for redistribution
under the terms of the Live2D Proprietary Software License Agreement:
- android/Live2DCubismCore.aar

Binary file not shown.

View File

@@ -0,0 +1,43 @@
## Definitions
### Live2D Cubism Components
Cubism Java Framework is included in Live2D Cubism Components.
Cubism Java Framework は Live2D Cubism Components に含まれます。
Cubism Java Framework 包括在 Live2D Cubism Components 中。
## Cubism SDK Release License
*All business* users must obtain a Cubism SDK Release License. "Business" means an entity with the annual gross revenue more than ten million (10,000,000) JPY for the most recent fiscal year.
* [Cubism SDK Release License](https://www.live2d.com/en/download/cubism-sdk/release-license/)
直近会計年度の売上高が 1000 万円以上の事業者様がご利用になる場合は、Cubism SDK リリースライセンス(出版許諾契約)に同意していただく必要がございます。
* [Cubism SDK リリースライセンス](https://www.live2d.com/ja/download/cubism-sdk/release-license/)
如果您的企业在最近一个会计年度的销售额达到或超过1000万日元您必须得到Cubism SDK的出版授权许可出版许可协议
* [Cubism SDK发行许可证](https://www.live2d.com/zh-CHS/download/cubism-sdk/release-license/)
## Live2D Open Software License
Live2D Cubism Components is available under Live2D Open Software License.
* [Live2D Open Software License Agreement](https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html)
* [Live2D Open Software 使用許諾契約書](https://www.live2d.com/eula/live2d-open-software-license-agreement_jp.html)
* [Live2D Open Software 使用授权协议](https://www.live2d.com/eula/live2d-open-software-license-agreement_cn.html)
## Live2D Proprietary Software License
Live2D Cubism Core is available under Live2D Proprietary Software License.
* [Live2D Proprietary Software License Agreement](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html)
* [Live2D Proprietary Software 使用許諾契約書](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_jp.html)
* [Live2D Proprietary Software 使用授权协议](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_cn.html)
---
Please contact us from [here](https://www.live2d.jp/contact/) for more license information.

View File

@@ -0,0 +1,112 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Java Framework
Live2D Cubism Editor で出力したモデルをアプリケーションで利用するためのフレームワークです。
モデルを表示、操作するための各種機能を提供します。モデルをロードするにはCubism Coreライブラリと組み合わせて使用します。
## 対応Javaバージョン
このFrameworkは**Java SE 7**以上でコンパイルが可能です。
## ライセンス
本フレームワークを使用する前に、[ライセンス](LICENSE.md)をご確認ください。
## Cubism 5新機能や過去バージョンとの互換性について
本 SDK はCubism 5に対応した製品です。
Cubism 5 Editorに搭載された新機能のSDK対応については [こちら](https://docs.live2d.com/cubism-sdk-manual/cubism-5-new-functions/)をご確認ください。
過去バージョンのCubism SDKとの互換性については [こちら](https://docs.live2d.com/cubism-sdk-manual/compatibility-with-cubism-5/)をご確認ください。
## コンポーネント
各コンポーネントはパッケージごとに提供しています。
### effect
自動まばたきやリップシンクなど、モデルに対してモーション情報をエフェクト的に付加する機能を提供します。
### exception
Cubism SDK frameworkに関連する例外クラス群を提供します。
### id
モデルに設定されたパラメータ名・パーツ名・Drawable 名を独自の型で管理する機能を提供します。
### math
行列計算やベクトル計算など、モデルの操作や描画に必要な算術演算の機能を提供します。
### model
モデルを取り扱うための各種機能(生成、更新、破棄)を提供します。
### motion
モデルにモーションデータを適用するための各種機能(モーション再生、パラメータブレンド)を提供します。
### physics
モデルに物理演算による変形操作を適用するための機能を提供します。
### rendering
各種プラットフォームでモデルを描画するためのグラフィックス命令を実装したレンダラを提供します。
### type
本フレームワーク内で使用する基本的な型をクラスとして定義したものを提供します。
### utils
JSON パーサーやログ出力などのユーティリティ機能を提供します。
## Live2D Cubism Core for Java
当リポジトリには Live2D Cubism Core for Java は同梱されていません。
ダウンロードするには[こちら](https://www.live2d.com/download/cubism-sdk/download-java/)のページを参照ください。
## サンプル
標準的なアプリケーションの実装例については、下記サンプルリポジトリを参照ください。
[CubismJavaSamples](https://github.com/Live2D/CubismJavaSamples)
## マニュアル
[Cubism SDK Manual](https://docs.live2d.com/cubism-sdk-manual/top/)
## 変更履歴
当リポジトリの変更履歴については [CHANGELOG.md](CHANGELOG.md) を参照ください。
## プロジェクトへの貢献
プロジェクトに貢献する方法はたくさんあります。バグのログの記録、このGitHubでのプルリクエストの送信、Live2Dコミュニティでの問題の報告と提案の作成です。
### フォークとプルリクエスト
修正、改善、さらには新機能をもたらすかどうかにかかわらず、プルリクエストに感謝します。メインリポジトリを可能な限りクリーンに保つために、必要に応じて個人用フォークと機能ブランチを作成してください。
### バグ
Live2Dコミュニティでは、問題のレポートと機能リクエストを定期的にチェックしています。バグレポートを提出する前に、Live2Dコミュニティで検索して、問題のレポートまたは機能リクエストがすでに投稿されているかどうかを確認してください。問題がすでに存在する場合は、関連するコメントを追記してください。
### 提案
SDKの将来についてのフィードバックにも関心があります。Live2Dコミュニティで提案や機能のリクエストを送信できます。このプロセスをより効果的にするために、それらをより明確に定義するのに役立つより多くの情報を含めるようお願いしています。
## フォーラム
ユーザー同士でCubism SDKの活用方法の提案や質問をしたい場合は、是非フォーラムをご活用ください。
- [Live2D 公式クリエイターズフォーラム](https://creatorsforum.live2d.com/)
- [Live2D Creator's Forum(English)](https://community.live2d.com/)

112
Live2DFramework/README.md Normal file
View File

@@ -0,0 +1,112 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Java Framework
This is a framework for using models output by Live2D Cubism Editor in applications.
It provides various functions for displaying and manipulating the model. It is used in conjunction with the Cubism Core library to load the model.
## Supported Java Versions
This framework can be compiled with **Java SE 7** or higher.
## License
Please check the [license](LICENSE.md) before using the framework.
## Compatibility with Cubism 5 new features and previous Cubism SDK versions
This SDK is compatible with Cubism 5.
For SDK compatibility with new features in Cubism 5 Editor, please refer to [here](https://docs.live2d.com/en/cubism-sdk-manual/cubism-5-new-functions/).
For compatibility with previous versions of Cubism SDK, please refer to [here](https://docs.live2d.com/en/cubism-sdk-manual/compatibility-with-cubism-5/).
## Components
Each component is offered in the form of Java packages.
### effect
Provides functions such as automatic blinking and lip sync to add motion information as an effect to the model.
### exception
Provides exception classes related to Cubism SDK Framework.
### id
Provides functions to manage the parameter name, part name, and Drawable name set in the model with unique types.
### math
Provides arithmetic functions required for manipulating and drawing the model, such as matrix and vector calculations.
### model
Provides various functions (generate, update, destroy) for handling the model.
### motion
Provides various functions (motion playback, parameter blending) for applying motion data to the model.
### physics
Provides functions for applying transformation manipulations due to physics to the model.
### rendering
Provides a renderer that implements graphics instructions for drawing the model on various platforms.
### type
Provides classes that are defined from the basic types used within this framework.
### utils
Provides utility functions such as JSON parser and log output.
## Live2D Cubism Core for Java
Live2D Cubism Core for Java is not included in this repository.
To download, please refer to [this](https://www.live2d.com/download/cubism-sdk/download-java/) page.
## Samples
Please refer to the following sample repository for implementation examples of standard applications.
[CubismJavaSamples](https://github.com/Live2D/CubismJavaSamples)
## Manual
[Cubism SDK Manual](https://docs.live2d.com/cubism-sdk-manual/top/)
## Changelog
Please refer to [CHANGELOG.md](CHANGELOG.md) for the changelog of this repository.
## Contributing
There are many ways to contribute to the project: logging bugs, submitting pull requests on this GitHub, and reporting issues and making suggestions in Live2D Community.
### Forking And Pull Requests
We very much appreciate your pull requests, whether they bring fixes, improvements, or even new features. To keep the main repository as clean as possible, create a personal fork and feature branches there as needed.
### Bugs
We are regularly checking issue-reports and feature requests at Live2D Community. Before filing a bug report, please do a search in Live2D Community to see if the issue-report or feature request has already been posted. If you find your issue already exists, make relevant comments and add your reaction.
### Suggestions
We're also interested in your feedback for the future of the SDK. You can submit a suggestion or feature request at Live2D Community. To make this process more effective, we're asking that you include more information to help define them more clearly.
## Forum
If you want to suggest or ask questions about how to use the Cubism SDK between users, please use the forum.
- [Live2D Creator's Forum](https://community.live2d.com/)
- [Live2D 公式クリエイターズフォーラム (Japanese)](https://creatorsforum.live2d.com/)

View File

@@ -0,0 +1,17 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
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
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,41 @@
plugins {
id 'com.android.library'
}
android {
namespace = "com.live2d.sdk.cubism.framework"
compileSdk ((project.findProperty("PROP_COMPILE_SDK_VERSION") ?: "33").toString().toInteger())
defaultConfig {
minSdkVersion ((project.findProperty("PROP_MIN_SDK_VERSION") ?: "21").toString().toInteger())
targetSdkVersion ((project.findProperty("PROP_TARGET_SDK_VERSION") ?: "33").toString().toInteger())
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
dependencies {
compileOnly(fileTree(dir: '../Core/android', include: ['Live2DCubismCore.aar']))
testImplementation 'junit:junit:4.13.2'
}

View File

@@ -0,0 +1,25 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
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=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=35

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="com.live2d.sdk.cubism.framework">
</manifest>

View File

@@ -0,0 +1,238 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework;
import com.live2d.sdk.cubism.framework.utils.jsonparser.ACubismJsonValue;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
import java.util.List;
/**
* This class handles cdi.json data.
*/
public class CubismCdiJson {
public static CubismCdiJson create(byte[] buffer) {
CubismJson json;
json = CubismJson.create(buffer);
return new CubismCdiJson(json);
}
// ----Parameters----//
/**
* Get the parameters number.
*
* @return number of parameters
*/
public int getParametersCount() {
if (!existsParameters()) {
return 0;
}
return json.getRoot().get(JsonKey.PARAMETERS.key).size();
}
/**
* Get Parameters ID
*
* @param index index
* @return parameter ID
*/
public String getParametersId(int index) {
return json.getRoot().get(JsonKey.PARAMETERS.key).get(index).get(JsonKey.ID.key).getString();
}
/**
* Get parameters group ID.
*
* @param index index
* @return parameters group ID
*/
public String getParametersGroupId(int index) {
return json.getRoot().get(JsonKey.PARAMETERS.key).get(index).get(JsonKey.GROUP_ID.key).getString();
}
/**
* Get parameters name
*
* @param index index
* @return parameters name
*/
public String getParametersName(int index) {
return json.getRoot().get(JsonKey.PARAMETERS.key).get(index).get(JsonKey.NAME.key).getString();
}
// ----ParameterGroups----//
/**
* Get the number of parameter groups.
*
* @return number of parameter groups
*/
public int getParameterGroupsCount() {
if (!existsParameterGroups()) {
return 0;
}
return json.getRoot().get(JsonKey.PARAMETER_GROUPS.key).size();
}
/**
* ZGet parameter groups ID
*
* @param index index
* @return parameter groups ID
*/
public String getParameterGroupsId(int index) {
return json.getRoot().get(JsonKey.PARAMETER_GROUPS.key).get(index).get(JsonKey.ID.key).getString();
}
/**
* Get parameter groups' group ID.
*
* @param index index
* @return parameter groups' group ID
*/
public String getParameterGroupsGroupId(int index) {
return json.getRoot().get(JsonKey.PARAMETER_GROUPS.key).get(index).get(JsonKey.GROUP_ID.key).getString();
}
/**
* Get parameter groups name
*
* @param index index
* @return parameter groups name
*/
public String getParameterGroupsName(int index) {
return json.getRoot().get(JsonKey.PARAMETER_GROUPS.key).get(index).get(JsonKey.NAME.key).getString();
}
// ----Parts----
/**
* Get the number of parts.
*
* @return number of parts
*/
public int getPartsCount() {
if (!existsParts()) {
return 0;
}
return json.getRoot().get(JsonKey.PARTS.key).size();
}
/**
* Get parts ID.
*
* @param index index
* @return parts ID
*/
public String getPartsId(int index) {
return json.getRoot().get(JsonKey.PARTS.key).get(index).get(JsonKey.ID.key).getString();
}
/**
* Get parts name.
*
* @param index index
* @return parts name
*/
public String getPartsName(int index) {
return json.getRoot().get(JsonKey.PARTS.key).get(index).get(JsonKey.NAME.key).getString();
}
// ----- Combined Parameters -----//
/**
* Returns the number of combined parameters.
*
* @return number of combined parameters
*/
public int getCombinedParametersCount() {
if (!existsCombinedParameters()) {
return 0;
}
return json.getRoot().get(JsonKey.COMBINED_PARAMETERS.key).size();
}
/**
* Returns the pair list of the combined parameter.
*
* @param index index to the desired combined parameters.
* @return pair list of the combined parameter
*/
public List<ACubismJsonValue> getCombinedParameter(int index) {
return json.getRoot().get(JsonKey.COMBINED_PARAMETERS.key).get(index).getList();
}
// JSON keys
private enum JsonKey {
VERSION("Version"),
PARAMETERS("Parameters"),
PARAMETER_GROUPS("ParameterGroups"),
PARTS("Parts"),
COMBINED_PARAMETERS("CombinedParameters"),
ID("Id"),
GROUP_ID("GroupId"),
NAME("Name");
private final String key;
JsonKey(String key) {
this.key = key;
}
}
private CubismCdiJson(CubismJson json) {
this.json = json;
}
/**
* Check if the parameter key exists.
*
* @return If true, the key exists
*/
private boolean existsParameters() {
ACubismJsonValue node = json.getRoot().get(JsonKey.PARAMETERS.key);
return !node.isNull() && !node.isError();
}
/**
* Check if the parameter group key exists.
*
* @return If true, the key exists
*/
private boolean existsParameterGroups() {
ACubismJsonValue node = json.getRoot().get(JsonKey.PARAMETER_GROUPS.key);
return !node.isNull() && !node.isError();
}
/**
* Check if the part's key exists.
*
* @return If true, the key exists
*/
private boolean existsParts() {
ACubismJsonValue node = json.getRoot().get(JsonKey.PARTS.key);
return !node.isNull() && !node.isError();
}
/**
* Returns whether the combined parameters exist in the Display Information File(cdi3.json).
*
* @return If true, the key exists.
*/
private boolean existsCombinedParameters() {
ACubismJsonValue node = json.getRoot().get(JsonKey.COMBINED_PARAMETERS.key);
return !node.isNull() && !node.isError();
}
/**
* cdi.json data
*/
private final CubismJson json;
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework;
/**
* Constant class that holds the default value of the parameter ID.
* <p>
* Default value specifications are based on the following manual.
* <a href="http://docs.live2d.com/cubism-editor-manual/standard-parametor-list/">...</a>
*/
public class CubismDefaultParameterId {
public enum HitAreaId {
PREFIX("HitArea"),
HEAD("Head"),
BODY("Body");
private final String id;
HitAreaId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
public enum PartId {
CORE("Parts01Core"),
ARM_PREFIX("Parts01Arm_"),
ARM_L_PREFIX("Parts01ArmL_"),
ARM_R_PREFIX("Parts01ArmR_");
private final String id;
PartId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
public enum ParameterId {
ANGLE_X("ParamAngleX"),
ANGLE_Y("ParamAngleY"),
ANGLE_Z("ParamAngleZ"),
EYE_L_OPEN("ParamEyeLOpen"),
EYE_L_SMILE("ParamEyeLSmile"),
EYE_R_OPEN("ParamEyeROpen"),
EYE_R_SMILE("ParamEyeRSmile"),
EYE_W_L_Y("ParamBrowLY"),
EYE_BALL_X("ParamEyeBallX"),
EYE_BALL_Y("ParamEyeBallY"),
EYE_BALL_FORM("ParamEyeBallForm"),
BROW_L_X("ParamBrowLX"),
BROW_L_Y("ParamBrowLY"),
BROW_R_X("ParamBrowRX"),
BROW_R_Y("ParamBrowRY"),
BROW_L_ANGLE("ParamBrowLAngle"),
BROW_R_ANGLE("ParamBrowRAngle"),
BROW_L_FORM("ParamBrowLForm"),
BROW_R_FORM("ParamBrowRForm"),
MOUTH_FORM("ParamMouthForm"),
MOUTH_OPEN_Y("ParamMouthOpenY"),
CHEEK("ParamCheek"),
BODY_ANGLE_X("ParamBodyAngleX"),
BODY_ANGLE_Y("ParamBodyAngleY"),
BODY_ANGLE_Z("ParamBodyAngleZ"),
BREATH("ParamBreath"),
ARM_L_A("ParamArmLA"),
ARM_L_B("ParamArmLB"),
ARM_R_A("ParamArmRA"),
ARM_R_B("ParamArmRB"),
HAND_L("ParamHandL"),
HAND_R("ParamHandR"),
HAIR_FRONT("ParamHairFront"),
HAIR_SIDE("ParamHairSide"),
HAIR_BACK("ParamHairBack"),
HAIR_FLUFFY("ParamHairFluffy"),
SHOULDER_Y("ParamShoulderY"),
BUST_X("ParamBustX"),
BUST_Y("ParamBustY"),
BASE_X("ParamBaseX"),
BASE_Y("ParamBaseY"),
NONE("NONE");
private final String id;
ParameterId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
/**
* private constructor
*/
private CubismDefaultParameterId() {}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework;
import com.live2d.sdk.cubism.core.CubismCoreVersion;
import com.live2d.sdk.cubism.core.ICubismLogger;
import com.live2d.sdk.cubism.core.Live2DCubismCore;
import com.live2d.sdk.cubism.framework.CubismFrameworkConfig.LogLevel;
import com.live2d.sdk.cubism.framework.id.CubismIdManager;
import com.live2d.sdk.cubism.framework.rendering.android.CubismRendererAndroid;
import java.util.Locale;
import static com.live2d.sdk.cubism.framework.utils.CubismDebug.cubismLogInfo;
import static com.live2d.sdk.cubism.framework.utils.CubismDebug.cubismLogWarning;
/**
* Entrypoint of Live2D Cubism Original Workflow SDK.
* <p>
* Beginning to use this Framework, call CubismFramework.initialize() method. Terminating the application, call CubismFramework.dispose() method.
*/
public class CubismFramework {
/**
* Inner class that define optional elements to be set in CubismFramework.
*/
public static class Option {
/**
* Set the log output function.
*
* @param logger log output function
*/
public void setLogFunction(ICubismLogger logger) {
if (logger == null) {
throw new IllegalArgumentException("logger is null.");
}
logFunction = logger;
}
/**
* Functional interface of logging.
*/
public ICubismLogger logFunction;
/**
* Log output level.
* (Default value is OFF(Log outputting is not executed.))
*/
public LogLevel loggingLevel = LogLevel.OFF;
}
/**
* Offset value for mesh vertices
*/
public static final int VERTEX_OFFSET = 0;
/**
* Step value for mesh vertices
*/
public static final int VERTEX_STEP = 2;
/**
* Enable Cubism Framework API.
* Required to run this method before using API.
* Once prepared, if you run this again, the inner processes are skipped.
*
* @param option Option Class's instance
* @return if preparing process has finished, return true
*/
public static boolean startUp(final Option option) {
if (s_isStarted) {
cubismLogInfo("CubismFramework.startUp() is already done.");
return s_isStarted;
}
s_option = option;
if (s_option != null) {
Live2DCubismCore.setLogger(option.logFunction);
}
s_isStarted = true;
// Display the version information of Live2D Cubism Core.
final CubismCoreVersion version = Live2DCubismCore.getVersion();
cubismLogInfo(String.format(Locale.US, "Live2D Cubism Core version: %02d.%02d.%04d (%d)", version.getMajor(), version.getMinor(), version.getPatch(), version.getVersionNumber()));
cubismLogInfo("CubismFramework.startUp() is complete.");
return s_isStarted;
}
/**
* Clear each parameter in CubismFramework initialized by startUp() method.
* Use this method at reusing CubismFramework done dispose() method.
*/
public static void cleanUp() {
s_isStarted = false;
s_isInitialized = false;
s_option = null;
s_cubismIdManager = null;
}
/**
* Whether Cubism Framework API has been prepared already.
*
* @return if API has been already prepared, return true
*/
public static boolean isStarted() {
return s_isStarted;
}
/**
* Initializing resources in Cubism Framework, the model is enabled to display.
* If you would like to use initialize() method again, first you need to run dispose() method.
*/
public static void initialize() {
assert (s_isStarted);
if (!s_isStarted) {
cubismLogWarning("CubismFramework is not started.");
return;
}
// Disturb consecutive securing resources.
if (s_isInitialized) {
cubismLogWarning("CubismFramework.initialize() skipped, already initialized.");
return;
}
// ----- Static Release -----
s_cubismIdManager = new CubismIdManager();
s_isInitialized = true;
cubismLogInfo("CubismFramework::Initialize() is complete.");
}
/**
* Releases all resources in the Cubism Framework.
*/
public static void dispose() {
assert (s_isStarted);
if (!s_isStarted) {
cubismLogWarning("CubismFramework is not started.");
return;
}
// If you use dispose() method, it is required to run initialize() method firstly.
if (!s_isInitialized) {
cubismLogWarning("CubismFramework.dispose() skipped, not initialized.");
return;
}
//---- static release ----
s_cubismIdManager = null;
// Release static resources of renderer(cf. Shader programs)
CubismRendererAndroid.staticRelease();
s_isInitialized = false;
cubismLogInfo("CubismFramework.dispose() is complete.");
}
/**
* Whether having already initialized CubismFramework's resources.
*
* @return If securing resources have already done, return true
*/
public static boolean isInitialized() {
return s_isInitialized;
}
/**
* Execute log function bound Core API
*
* @param message log message
*/
public static void coreLogFunction(final String message) {
if (Live2DCubismCore.getLogger() != null) {
Live2DCubismCore.getLogger().print(message);
}
}
/**
* Return the current value of log output level setting.
*
* @return the current value of log output level setting
*/
public static LogLevel getLoggingLevel() {
if (s_option != null) {
return s_option.loggingLevel;
}
return LogLevel.OFF;
}
/**
* Get the instance of ID manager.
*
* @return CubismIdManager class's instance
*/
public static CubismIdManager getIdManager() {
return s_cubismIdManager;
}
/**
* private constructor
*/
private CubismFramework() {}
/**
* Flag whether the framework has been started or not.
*/
private static boolean s_isStarted;
/**
* Flag whether the framework has been initialized or not.
*/
private static boolean s_isInitialized;
/**
* Option object
*/
private static Option s_option;
/**
* CubismIDManager object
*/
private static CubismIdManager s_cubismIdManager;
}

View File

@@ -0,0 +1,67 @@
package com.live2d.sdk.cubism.framework;
/**
* CubismFrameworkで使用される定数の定義クラス。<br>
* デバッグやログに関わる設定をデフォルトから変更したい場合は、このクラスの定数の値を書き換えること。
*/
public class CubismFrameworkConfig {
/**
* ログ出力レベルを定義する列挙体。
*/
public enum LogLevel {
/**
* 詳細ログ出力設定
*/
VERBOSE(0),
/**
* デバッグログ出力設定
*/
DEBUG(1),
/**
* Infoログ出力設定
*/
INFO(2),
/**
* 警告ログ出力設定
*/
WARNING(3),
/**
* エラーログ出力設定
*/
ERROR(4),
/**
* ログ出力オフ設定
*/
OFF(5);
private final int id;
LogLevel(final int id) {
this.id = id;
}
public int getId() {
return id;
}
}
/**
* Cubism SDKにおけるデバッグ機能の有効状態。trueなら有効。
*/
public static final boolean CSM_DEBUG = false;
/**
* ログ出力設定。<br>
* 強制的にログ出力レベルを変える時に定義を有効にする。
*
* @note LogLevel.VERBOSE LogLevel.OFF のいずれかを指定する。
*/
public static final LogLevel CSM_LOG_LEVEL = LogLevel.VERBOSE;
/**
* privateコンストラクタ。
*
* @note 定数クラスのためインスタンス化させない
*/
private CubismFrameworkConfig() {}
}

View File

@@ -0,0 +1,503 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.utils.jsonparser.ACubismJsonValue;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJsonString;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* This class deals with model3.json data.
*/
public class CubismModelSettingJson implements ICubismModelSetting {
public CubismModelSettingJson(byte[] buffer) {
CubismJson json;
json = CubismJson.create(buffer);
this.json = json;
if (jsonFrequencyValue != null) {
jsonFrequencyValue.clear();
} else {
jsonFrequencyValue = new ArrayList<ACubismJsonValue>();
}
// The order should match the enum FrequentNode
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.GROUPS.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.MOC.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.MOTIONS.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.DISPLAY_INFO.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.EXPRESSIONS.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.TEXTURES.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.PHYSICS.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.POSE.key));
jsonFrequencyValue.add(this.json.getRoot().get(JsonKey.HIT_AREAS.key));
}
@Override
public CubismJson getJson() {
return json;
}
@Override
public String getModelFileName() {
if (!existsModelFile()) {
return "";
}
return jsonFrequencyValue.get(FrequentNode.MOC.id).getString();
}
@Override
public int getTextureCount() {
if (!existsTextureFiles()) {
return 0;
}
return json.getRoot()
.get(JsonKey.FILE_REFERENCES.key)
.get(JsonKey.TEXTURES.key)
.size();
}
@Override
public String getTextureDirectory() {
if (!existsTextureFiles()) {
return "";
}
String rowString = jsonFrequencyValue.get(FrequentNode.TEXTURES.id).get(0).getString();
return rowString.split("/")[0];
}
@Override
public String getTextureFileName(int index) {
return jsonFrequencyValue.get(FrequentNode.TEXTURES.id).get(index).getString();
}
@Override
public int getHitAreasCount() {
if (!existsHitAreas()) {
return 0;
}
return jsonFrequencyValue.get(FrequentNode.HIT_AREAS.id).size();
}
@Override
public CubismId getHitAreaId(int index) {
return CubismFramework.getIdManager().getId(jsonFrequencyValue.get(FrequentNode.HIT_AREAS.id).get(index).get(JsonKey.ID.key).getString());
}
@Override
public String getHitAreaName(int index) {
return jsonFrequencyValue.get(FrequentNode.HIT_AREAS.id).get(index).get(JsonKey.NAME.key).getString();
}
@Override
public String getPhysicsFileName() {
if (!existsPhysicsFile()) {
return "";
}
return jsonFrequencyValue.get(FrequentNode.PHYSICS.id).getString();
}
@Override
public String getPoseFileName() {
if (!existsPoseFile()) {
return "";
}
return jsonFrequencyValue.get(FrequentNode.POSE.id).getString();
}
@Override
public String getDisplayInfoFileName() {
if (!existsDisplayInfoFile()) {
return "";
}
return jsonFrequencyValue.get(FrequentNode.DISPLAY_INFO.id).getString();
}
@Override
public int getExpressionCount() {
if (!existsExpressionFile()) {
return 0;
}
return jsonFrequencyValue.get(FrequentNode.EXPRESSIONS.id).size();
}
@Override
public String getExpressionName(int index) {
return jsonFrequencyValue.get(FrequentNode.EXPRESSIONS.id).get(index).get(JsonKey.NAME.key).getString();
}
@Override
public String getExpressionFileName(int index) {
return jsonFrequencyValue.get(FrequentNode.EXPRESSIONS.id).get(index).get(JsonKey.FILEPATH.key).getString();
}
@Override
public int getMotionGroupCount() {
if (!existsMotionGroups()) {
return 0;
}
return jsonFrequencyValue.get(FrequentNode.MOTIONS.id).size();
}
@Override
public String getMotionGroupName(int index) {
if (!existsMotionGroups()) {
return null;
}
return jsonFrequencyValue.get(FrequentNode.MOTIONS.id).getKeys().get(index).getString();
}
@Override
public int getMotionCount(final String groupName) {
if (!existsMotionGroupName(groupName)) {
return 0;
}
return jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).size();
}
@Override
public String getMotionFileName(final String groupName, int index) {
if (!existsMotionGroupName(groupName)) {
return "";
}
return jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).get(index).get(JsonKey.FILEPATH.key).getString();
}
@Override
public String getMotionSoundFileName(final String groupName, int index) {
if (!existsMotionSoundFile(groupName, index)) {
return "";
}
return jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).get(index).get(JsonKey.SOUND_PATH.key).getString();
}
@Override
public float getMotionFadeInTimeValue(final String groupName, int index) {
if (!existsMotionFadeIn(groupName, index)) {
return -1.0f;
}
return jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).get(index).get(JsonKey.FADE_IN_TIME.key).toFloat();
}
@Override
public float getMotionFadeOutTimeValue(String groupName, int index) {
if (!existsMotionFadeOut(groupName, index)) {
return -1.0f;
}
return jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).get(index).get(JsonKey.FADE_OUT_TIME.key).toFloat();
}
@Override
public String getUserDataFile() {
if (!existsUserDataFile()) {
return "";
}
return json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.USER_DATA.key).getString();
}
@Override
public boolean getLayoutMap(Map<String, Float> outLayoutMap) {
Map<CubismJsonString, ACubismJsonValue> map = json.getRoot().get(JsonKey.LAYOUT.key).getMap();
if (map == null) {
return false;
}
boolean result = false;
for (Map.Entry<CubismJsonString, ACubismJsonValue> entry : map.entrySet()) {
outLayoutMap.put(entry.getKey().getString(), entry.getValue().toFloat());
result = true;
}
return result;
}
@Override
public int getEyeBlinkParameterCount() {
if (!existsEyeBlinkParameters()) {
return 0;
}
int eyeBlinkParameterCount = 0;
for (int i = 0; i < jsonFrequencyValue.get(FrequentNode.GROUPS.id).size(); i++) {
ACubismJsonValue refI = jsonFrequencyValue.get(FrequentNode.GROUPS.id).get(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.get(JsonKey.NAME.key).getString().equals(JsonKey.EYE_BLINK.key)) {
eyeBlinkParameterCount = refI.get(JsonKey.IDS.key).getList().size();
break;
}
}
return eyeBlinkParameterCount;
}
@Override
public CubismId getEyeBlinkParameterId(int index) {
if (!existsEyeBlinkParameters()) {
return null;
}
for (int i = 0; i < jsonFrequencyValue.get(FrequentNode.GROUPS.id).size(); ++i) {
ACubismJsonValue refI = jsonFrequencyValue.get(FrequentNode.GROUPS.id).get(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.get(JsonKey.NAME.key).getString().equals(JsonKey.EYE_BLINK.key)) {
return CubismFramework.getIdManager().getId(refI.get(JsonKey.IDS.key).get(index).getString());
}
}
return null;
}
@Override
public int getLipSyncParameterCount() {
if (!existsLipSyncParameters()) {
return 0;
}
int lipSyncParameterCount = 0;
for (int i = 0; i < jsonFrequencyValue.get(FrequentNode.GROUPS.id).size(); i++) {
ACubismJsonValue refI = jsonFrequencyValue.get(FrequentNode.GROUPS.id).get(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.get(JsonKey.NAME.key).getString().equals(JsonKey.LIP_SYNC.key)) {
lipSyncParameterCount = refI.get(JsonKey.IDS.key).getList().size();
break;
}
}
return lipSyncParameterCount;
}
@Override
public CubismId getLipSyncParameterId(int index) {
if (!existsLipSyncParameters()) {
return null;
}
for (int i = 0; i < jsonFrequencyValue.get(FrequentNode.GROUPS.id).size(); i++) {
ACubismJsonValue refI = jsonFrequencyValue.get(FrequentNode.GROUPS.id).get(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.get(JsonKey.NAME.key).getString().equals(JsonKey.LIP_SYNC.key)) {
return CubismFramework.getIdManager().getId(refI.get(JsonKey.IDS.key).get(index).getString());
}
}
return null;
}
/**
* Enum class for frequent nodes.
*/
protected enum FrequentNode {
GROUPS(0),
MOC(1),
MOTIONS(2),
DISPLAY_INFO(3),
EXPRESSIONS(4),
TEXTURES(5),
PHYSICS(6),
POSE(7),
HIT_AREAS(8);
protected final int id;
FrequentNode(final int id) {
this.id = id;
}
}
protected enum JsonKey {
VERSION("Version"),
FILE_REFERENCES("FileReferences"),
GROUPS("Groups"),
LAYOUT("Layout"),
HIT_AREAS("HitAreas"),
MOC("Moc"),
TEXTURES("Textures"),
PHYSICS("Physics"),
DISPLAY_INFO("DisplayInfo"),
POSE("Pose"),
EXPRESSIONS("Expressions"),
MOTIONS("Motions"),
USER_DATA("UserData"),
NAME("Name"),
FILEPATH("File"),
ID("Id"),
IDS("Ids"),
TARGET("Target"),
// Motions
IDLE("Idle"),
TAP_BODY("TapBody"),
PINCH_IN("PinchIn"),
PINCH_OUT("PinchOut"),
SHAKE("Shake"),
FLICK_HEAD("FlickHead"),
PARAMETER("Parameter"),
SOUND_PATH("Sound"),
FADE_IN_TIME("FadeInTime"),
FADE_OUT_TIME("FadeOutTime"),
// Layout
CENTER_X("CenterX"),
CENTER_Y("CenterY"),
X("X"),
Y("Y"),
WIDTH("Width"),
HEIGHT("Height"),
LIP_SYNC("LipSync"),
EYE_BLINK("EyeBlink"),
INIT_PARAMETER("init_param"),
INIT_PARTS_VISIBLE("init_parts_visible"),
VAL("val");
protected final String key;
JsonKey(String key) {
this.key = key;
}
}
// キーが存在するかどうかのチェック
protected boolean existsModelFile() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.MOC.id);
return !node.isNull() && !node.isError();
}
protected boolean existsTextureFiles() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.TEXTURES.id);
return !node.isNull() && !node.isError();
}
protected boolean existsHitAreas() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.HIT_AREAS.id);
return !node.isNull() && !node.isError();
}
protected boolean existsPhysicsFile() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.PHYSICS.id);
return !node.isNull() && !node.isError();
}
protected boolean existsPoseFile() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.POSE.id);
return !node.isNull() && !node.isError();
}
protected boolean existsDisplayInfoFile() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.DISPLAY_INFO.id);
return !node.isNull() && !node.isError();
}
protected boolean existsExpressionFile() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.EXPRESSIONS.id);
return !node.isNull() && !node.isError();
}
protected boolean existsMotionGroups() {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.MOTIONS.id);
return !node.isNull() && !node.isError();
}
protected boolean existsMotionGroupName(String groupName) {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName);
return !node.isNull() && !node.isError();
}
protected boolean existsMotionSoundFile(String groupName, int index) {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).get(index).get(JsonKey.SOUND_PATH.key);
return !node.isNull() && !node.isError();
}
protected boolean existsMotionFadeIn(String groupName, int index) {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).get(index).get(JsonKey.FADE_IN_TIME.key);
return !node.isNull() && !node.isError();
}
protected boolean existsMotionFadeOut(String groupName, int index) {
ACubismJsonValue node = jsonFrequencyValue.get(FrequentNode.MOTIONS.id).get(groupName).get(index).get(JsonKey.FADE_OUT_TIME.key);
return !node.isNull() && !node.isError();
}
protected boolean existsUserDataFile() {
return !json.getRoot().get(JsonKey.FILE_REFERENCES.key).get(JsonKey.USER_DATA.key).isNull();
}
protected boolean existsEyeBlinkParameters() {
if (jsonFrequencyValue.get(FrequentNode.GROUPS.id).isNull() || jsonFrequencyValue.get(FrequentNode.GROUPS.id).isError()) {
return false;
}
for (int i = 0; i < jsonFrequencyValue.get(FrequentNode.GROUPS.id).size(); ++i) {
if (jsonFrequencyValue.get(FrequentNode.GROUPS.id).get(i).get(JsonKey.NAME.key).getString().equals(JsonKey.EYE_BLINK.key)) {
return true;
}
}
return false;
}
protected boolean existsLipSyncParameters() {
if (jsonFrequencyValue.get(FrequentNode.GROUPS.id).isNull() || jsonFrequencyValue.get(FrequentNode.GROUPS.id).isError()) {
return false;
}
for (int i = 0; i < jsonFrequencyValue.get(FrequentNode.GROUPS.id).size(); i++) {
if (jsonFrequencyValue.get(FrequentNode.GROUPS.id).get(i).get(JsonKey.NAME.key).getString().equals(JsonKey.LIP_SYNC.key)) {
return true;
}
}
return false;
}
/**
* model3.json data
*/
protected final CubismJson json;
/**
* Frequent nodes in the _json data
*/
protected List<ACubismJsonValue> jsonFrequencyValue;
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.utils.jsonparser.ACubismJsonValue;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJsonString;
import java.util.Map;
/**
* This interface deal with model setting information.
* <p>
* The class implemented this interface can deal with model setting info.
*/
public interface ICubismModelSetting {
/**
* Get model3.json.
*
* @return model3.json
*/
CubismJson getJson();
/**
* Get the name of Moc file.
*
* @return name of Moc file
*/
String getModelFileName();
/**
* Get a number of textures model uses.
*
* @return number of textures
*/
int getTextureCount();
/**
* Get the name of directory is located textures.
*
* @return name of directory is located textures.
*/
String getTextureDirectory();
/**
* Get the name of textures used by model.
*
* @param index index value of array
* @return the name of textures
*/
String getTextureFileName(int index);
/**
* Get the number of collision detection set to model.
*
* @return the number of collision detection set to model
*/
int getHitAreasCount();
/**
* Get the ID set to collision detection.
*
* @param index index value of array
* @return the ID set to collision detection
*/
CubismId getHitAreaId(int index);
/**
* Get the name set to collision detection
*
* @param index index value of array
* @return the name set to collision detection
*/
String getHitAreaName(int index);
/**
* Get the name of the physics setting file.
*
* @return the name of the physics setting file.
*/
String getPhysicsFileName();
/**
* Get the name of parts switching setting file.
*
* @return Parts switching setting file
*/
String getPoseFileName();
/**
* Get the name of cdi3.json file.
*
* @return
*/
String getDisplayInfoFileName();
/**
* Get the number of expression setting file.
*
* @return the number of expression setting file
*/
int getExpressionCount();
/**
* Get the name(Alias) identifying expression setting file.
*
* @param index index value of array
* @return the name of expression
*/
String getExpressionName(int index);
/**
* Get the name of expression setting file.
*
* @param index index value of array
* @return the name of expression setting file
*/
String getExpressionFileName(int index);
/**
* Get the number of motion groups.
*
* @return the number of motion groups
*/
int getMotionGroupCount();
String getMotionGroupName(int index);
/**
* Get the number of motion included in motion group given to this method by an argument.
*
* @param groupName the name of motion group
* @return the number of motion included in the motion group.
*/
int getMotionCount(final String groupName);
/**
* Get the name of the motion file from group name and index value.
*
* @param groupName the name of the motion group
* @param index index value of array
* @return the name of motion file
*/
String getMotionFileName(final String groupName, int index);
/**
* Get the name of sound file mapped to the motion.
*
* @param groupName the name of motion group
* @param index index value of arrayz
* @return the name of sound file
*/
String getMotionSoundFileName(final String groupName, int index);
/**
* Get the fade-in processing time at start of motion
*
* @param groupName the name of motion group
* @param index index value of array
* @return fade-in processing time[s]
*/
float getMotionFadeInTimeValue(final String groupName, int index);
/**
* Get fade-out processing time at end of motion.
*
* @param groupName the name of motion group
* @param index index value of array
* @return fade-out processing time[s]
*/
float getMotionFadeOutTimeValue(final String groupName, int index);
/**
* Get the name of userdata file.
*
* @return the name of userdata file
*/
String getUserDataFile();
/**
* Get the layout information.
*
* @return if layout information exists, return true
*/
boolean getLayoutMap(Map<String, Float> outLayoutMap);
/**
* Get the number of parameters associated to eye blink.
*
* @return the number of parameters associated to eye blink
*/
int getEyeBlinkParameterCount();
/**
* Get the parameter ID associated to eye blink.
*
* @param index index value of array
* @return parameter ID
*/
CubismId getEyeBlinkParameterId(int index);
/**
* Get the number of parameters associated to lip sync.
*
* @return the number of parameters associated to lip sync
*/
int getLipSyncParameterCount();
/**
* Get the parameter ID associated to lip sync.
*
* @param index index value of array
* @return parameter ID
*/
CubismId getLipSyncParameterId(int index);
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.effect;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.live2d.sdk.cubism.framework.math.CubismMath.PI;
import static com.live2d.sdk.cubism.framework.math.CubismMath.sinF;
/**
* This class offers the breath function.
*/
public class CubismBreath {
/**
* This inner class has breath parameter information.
*/
public static class BreathParameterData {
/**
* Constructor
*
* @param parameterId A parameter ID bound breathing info.
* @param offset A wave offset when breathing is taken as a sine wave
* @param peak A wave height when breathing is taken as a sine wave
* @param cycle A wave cycle when breathing is taken as a sine wave
* @param weight A weight to a parameter
*/
public BreathParameterData(
CubismId parameterId,
float offset,
float peak,
float cycle,
float weight
) {
this.parameterId = parameterId;
this.offset = offset;
this.peak = peak;
this.cycle = cycle;
this.weight = weight;
}
/**
* Copy constructor
*
* @param data BreathParameterData instance
*/
public BreathParameterData(BreathParameterData data) {
this.parameterId = data.parameterId;
this.offset = data.offset;
this.peak = data.peak;
this.cycle = data.cycle;
this.weight = data.weight;
}
/**
* A parameter ID bound breath info.
*/
public final CubismId parameterId;
/**
* A wave offset when breathing is taken as a sine wave
*/
public final float offset;
/**
* A wave height when breathing is taken as a sine wave
*/
public final float peak;
/**
* A wave cycle when breathing is taken as a sine wave
*/
public final float cycle;
/**
* A weight to a parameter
*/
public final float weight;
}
/**
* Creates a {@code CubismBreath} instance.
*
* @return a {@code CubismBreath} instance
*/
public static CubismBreath create() {
return new CubismBreath();
}
/**
* Updates the parameters of the model.
*
* @param model the target model
* @param deltaTimeSeconds the delta time[s]
*/
public void updateParameters(CubismModel model, float deltaTimeSeconds) {
userTimeSeconds += deltaTimeSeconds;
final float t = userTimeSeconds * 2.0f * PI;
for (int i = 0; i < breathParameters.size(); i++) {
BreathParameterData breathData = breathParameters.get(i);
final float value = breathData.offset + (breathData.peak * sinF(t / breathData.cycle));
model.addParameterValue(
breathData.parameterId,
value,
breathData.weight);
}
}
/**
* Bind parameters of breath.
*
* @param breathParameters A parameters list bound breath
* @throws IllegalArgumentException if an argument is null
*/
public void setParameters(List<BreathParameterData> breathParameters) {
if (breathParameters == null) {
throw new IllegalArgumentException("breathParameters is null.");
}
this.breathParameters = breathParameters;
areBreathParametersChanged = true;
}
/**
* Returns the parameters bound breath.
* <p>
* This method returns the copy of list of breath parameters. It is recommended that this method not be used where it is executed every frame.
* </p>
*
* @return The parameters bound breath
*/
public List<BreathParameterData> getParameters() {
// If there is a change in the parameters list, the read-only list is made.
if (areBreathParametersChanged) {
cachedImmutableBreathParameters = Collections.unmodifiableList(breathParameters);
areBreathParametersChanged = false;
}
return cachedImmutableBreathParameters;
}
/**
* private constructor
*/
private CubismBreath() {}
/**
* A parameters list bound breath
*/
private List<BreathParameterData> breathParameters = new ArrayList<BreathParameterData>();
private boolean areBreathParametersChanged = true;
private List<BreathParameterData> cachedImmutableBreathParameters;
/**
* total elapsed time[s]
*/
private float userTimeSeconds;
}

View File

@@ -0,0 +1,324 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.effect;
import com.live2d.sdk.cubism.framework.ICubismModelSetting;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This class offers auto eyeblink function.
*/
public class CubismEyeBlink {
/**
* Eyeblink states
*/
public enum EyeState {
/**
* Initial state
*/
FIRST,
/**
* Non-eyeblink state
*/
INTERVAL,
/**
* Closing-eye state
*/
CLOSING,
/**
* Closed-eye state
*/
CLOSED,
/**
* Opening-eye state
*/
OPENING
}
/**
* Create a CubismEyeBlink instance.
* (If an argument is not passed, this function creates empty instance.)
*
* @return modelSetting model setting information
*/
public static CubismEyeBlink create() {
return new CubismEyeBlink(null);
}
/**
* Create instance.
*
* @param modelSetting model setting information
* @return instance
*/
public static CubismEyeBlink create(ICubismModelSetting modelSetting) {
return new CubismEyeBlink(modelSetting);
}
/**
* Update model's parameters.
*
* @param model the target model
* @param deltaTimeSeconds delta time[s]
*/
public void updateParameters(final CubismModel model, final float deltaTimeSeconds) {
userTimeSeconds += deltaTimeSeconds;
switch (blinkingState) {
case CLOSING:
updateParametersClosing(model);
break;
case CLOSED:
updateParametersClosed(model);
break;
case OPENING:
updateParametersOpening(model);
break;
case INTERVAL:
updateParametersInterval(model);
break;
case FIRST:
default:
updateParametersFirst(model);
break;
}
}
/**
* Get the blink interval.
*
* @param blinkingInterval eye-blinking interval[s]
*/
public void setBlinkingInterval(float blinkingInterval) {
blinkingIntervalSeconds = blinkingInterval;
}
/**
* Set eye-blink motion parameters.
*
* @param closing a duration of the motion at closing eye[s]
* @param closed a duration at closing eye[s]
* @param opening a duration of the motion at opening eye[s]
*/
public void setBlinkingSettings(float closing, float closed, float opening) {
closingSeconds = closing;
closedSeconds = closed;
openingSeconds = opening;
}
/**
* Set a parameter IDs list to blink.
*
* @param parameterIds A parameter IDs list
* @throws IllegalArgumentException if an argument is null
*/
public void setParameterIds(List<CubismId> parameterIds) {
if (parameterIds == null) {
throw new IllegalArgumentException("parameterIds is null.");
}
this.parameterIds = parameterIds;
}
/**
* Get the parameter IDs list to eye-blink.
* <p>This method creates all new elements with the parameter ID and adds them to the list and returns them.
* Therefore, it is recommended that this method not be used where it is executed every frame, as it may affect performance.
*
* @return A parameter IDs list
*/
public List<CubismId> getParameterIds() {
// If there is a change in the ids list, the read-only list is made.
if (areIdsListChanged) {
cachedImmutableIdsList = Collections.unmodifiableList(parameterIds);
areIdsListChanged = false;
}
return cachedImmutableIdsList;
}
/**
* This constant is becomes "true" if the eye blinking parameter specified by ID is specified to close when the value is 0, and "false" if the parameter is specified to close when the value is 1.
*/
private static final boolean CLOSE_IF_ZERO = true;
/**
* Update model parameters when _blinkingState is CLOSING.
*
* @param model target model
*/
private void updateParametersClosing(CubismModel model) {
float time = (userTimeSeconds - stateStartTimeSeconds) / closingSeconds;
if (time >= 1.0f) {
time = 1.0f;
blinkingState = EyeState.CLOSED;
stateStartTimeSeconds = userTimeSeconds;
}
float parameterValue = 1.0f - time;
setParameterValueToAllIds(model, parameterValue);
}
/**
* Update model parameters when _blinkingState is CLOSED.
*
* @param model target model
*/
private void updateParametersClosed(CubismModel model) {
float time = (userTimeSeconds - stateStartTimeSeconds) / closedSeconds;
if (time >= 1.0f) {
blinkingState = EyeState.OPENING;
stateStartTimeSeconds = userTimeSeconds;
}
float parameterValue = 0.0f;
setParameterValueToAllIds(model, parameterValue);
}
/**
* Update model parameters when _blinkingState is OPENING.
*
* @param model target model
*/
private void updateParametersOpening(CubismModel model) {
float time = (userTimeSeconds - stateStartTimeSeconds) / openingSeconds;
if (time >= 1.0f) {
time = 1.0f;
blinkingState = EyeState.INTERVAL;
nextBlinkingTime = determineNextBlinkingTiming();
}
float parameterValue = time;
setParameterValueToAllIds(model, parameterValue);
}
/**
* Update model parameters when _blinkingState is INTERVAL.
*
* @param model target model
*/
private void updateParametersInterval(CubismModel model) {
if (nextBlinkingTime < userTimeSeconds) {
blinkingState = EyeState.CLOSING;
stateStartTimeSeconds = userTimeSeconds;
}
float parameterValue = 1.0f;
setParameterValueToAllIds(model, parameterValue);
}
/**
* Update model parameters when _blinkingState is FIRST.
*
* @param model target model
*/
private void updateParametersFirst(CubismModel model) {
blinkingState = EyeState.INTERVAL;
nextBlinkingTime = determineNextBlinkingTiming();
float parameterValue = 1.0f;
setParameterValueToAllIds(model, parameterValue);
}
/**
* Set the given value for all IDs.
* CLOSE_IF_ZERO constant is "false", given argument is reversed(multiplied -1).
*
* @param model target model
* @param value value to be set
*/
private void setParameterValueToAllIds(CubismModel model, float value) {
if (!CubismEyeBlink.CLOSE_IF_ZERO) {
value *= -1;
}
for (int i = 0; i < parameterIds.size(); i++) {
CubismId id = parameterIds.get(i);
model.setParameterValue(id, value);
}
}
/**
* Decide next eye blinking timing.
*
* @return the time of next eye blink
*/
private float determineNextBlinkingTiming() {
final float r = (float) Math.random();
return userTimeSeconds + r * (2.0f * blinkingIntervalSeconds - 1.0f);
}
/**
* private constructor
*
* @param modelSetting model setting information
*/
private CubismEyeBlink(ICubismModelSetting modelSetting) {
if (modelSetting == null) {
return;
}
for (int i = 0; i < modelSetting.getEyeBlinkParameterCount(); i++) {
CubismId eyeBlinkId = modelSetting.getEyeBlinkParameterId(i);
if (eyeBlinkId != null) {
parameterIds.add(eyeBlinkId);
}
}
}
/**
* current blinking state
*/
private EyeState blinkingState = EyeState.FIRST;
/**
* target parameter IDs list
*/
private List<CubismId> parameterIds = new ArrayList<CubismId>();
/**
* If the elemtns of _parameterIds are changed, this flag becomes true.
*/
private boolean areIdsListChanged = true;
/**
* List of _parameterIds made read-only by the Collections.unmodifiableList method. This class's getter returns this immutable list.
*/
private List<CubismId> cachedImmutableIdsList;
/**
* next blinking time[s]
*/
private float nextBlinkingTime;
/**
* time at starting current state[s]
*/
private float stateStartTimeSeconds;
/**
* interval of eye blink
*/
private float blinkingIntervalSeconds = 4.0f;
/**
* duration of the motion at closing eye[s]
*/
private float closingSeconds = 0.1f;
/**
* duration at the closing eye[s]
*/
private float closedSeconds = 0.05f;
/**
* duration of the motion at opening eye[s]
*/
private float openingSeconds = 0.15f;
/**
* total elapsed time[s]
*/
private float userTimeSeconds;
}

View File

@@ -0,0 +1,400 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.effect;
import com.live2d.sdk.cubism.framework.CubismFramework;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.utils.jsonparser.ACubismJsonValue;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJsonString;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* This class deals with parts opacity value and settings.
*/
public class CubismPose {
/**
* Manage data related parts.
*/
public static class PartData {
/**
* Default constructor
*/
public PartData() {}
/**
* Copy constructor
*
* @param partData original part data
*/
public PartData(PartData partData) {
partId = partData.partId;
parameterIndex = partData.parameterIndex;
partIndex = partData.partIndex;
linkedParameter.addAll(partData.linkedParameter);
}
public void initialize(CubismModel model) {
parameterIndex = model.getParameterIndex(partId);
partIndex = model.getPartIndex(partId);
model.setParameterValue(parameterIndex, 1);
}
/**
* Part ID
*/
public CubismId partId;
/**
* Parameter index
*/
public int parameterIndex;
/**
* Part index
*/
public int partIndex;
/**
* Linked parameters list
*/
public List<PartData> linkedParameter = new ArrayList<PartData>();
}
/**
* Create a CubismPose instance
*
* @param pose3json the byte data of pose3.json
* @return the created instance
*/
public static CubismPose create(byte[] pose3json) {
CubismPose pose = new CubismPose();
final CubismJson json;
json = CubismJson.create(pose3json);
ACubismJsonValue root = json.getRoot();
Map<CubismJsonString, ACubismJsonValue> rootMap = root.getMap();
// Set the fade time.
if (!root.get(JsonTag.FADE_IN.tag).isNull()) {
pose.fadeTimeSeconds = root.get(JsonTag.FADE_IN.tag).toFloat(DEFAULT_FADE_IN_SECONDS);
if (pose.fadeTimeSeconds < 0.0f) {
pose.fadeTimeSeconds = DEFAULT_FADE_IN_SECONDS;
}
}
// Parts group
ACubismJsonValue poseListInfo = root.get(JsonTag.GROUPS.tag);
int poseCount = poseListInfo.size();
for (int poseIndex = 0; poseIndex < poseCount; poseIndex++) {
ACubismJsonValue idListInfo = poseListInfo.get(poseIndex);
int idCount = idListInfo.size();
int groupCount = 0;
for (int groupIndex = 0; groupIndex < idCount; groupIndex++) {
ACubismJsonValue partInfo = idListInfo.get(groupIndex);
PartData partData = setupPartGroup(partInfo);
pose.partGroups.add(partData);
groupCount++;
}
pose.partGroupCounts.add(groupCount);
}
return pose;
}
/**
* Update model's parameters.
*
* @param model the target model
* @param deltaTimeSeconds delta time[s]
*/
public void updateParameters(final CubismModel model, float deltaTimeSeconds) {
// If given model is different from previous model, it is required to initialize some parameters.
if (model != lastModel) {
reset(model);
}
lastModel = model;
// If a negative time is given, 0 value is set.
if (deltaTimeSeconds < 0.0f) {
deltaTimeSeconds = 0.0f;
}
int beginIndex = 0;
for (int i = 0; i < partGroupCounts.size(); i++) {
int partGroupCount = partGroupCounts.get(i);
doFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
beginIndex += partGroupCount;
}
copyPartOpacities(model);
}
private static PartData setupPartGroup(ACubismJsonValue partInfo) {
final CubismId parameterId = CubismFramework.getIdManager().getId(partInfo.get(JsonTag.ID.tag).getString());
final PartData partData = new PartData();
partData.partId = CubismFramework.getIdManager().getId(parameterId);
ACubismJsonValue link = partInfo.get(JsonTag.LINK.tag);
if (link != null) {
setupLinkedPart(partData, link);
}
return partData;
}
/**
* Setup linked parts.
*
* @param partData part data to be done setting
* @param linkedListInfo linked parts list information
*/
private static void setupLinkedPart(PartData partData, ACubismJsonValue linkedListInfo) {
final int linkCount = linkedListInfo.size();
for (int index = 0; index < linkCount; index++) {
final CubismId linkedPartId = CubismFramework.getIdManager().getId(linkedListInfo.get(index).getString());
PartData linkedPart = new PartData();
linkedPart.partId = linkedPartId;
partData.linkedParameter.add(linkedPart);
}
}
/**
* Initialize display.
*
* @param model the target model(Parameters that initial opacity is not 0, the opacity is set to 1.)
*/
private void reset(final CubismModel model) {
int beginIndex = 0;
for (int j = 0; j < partGroupCounts.size(); j++) {
int groupCount = partGroupCounts.get(j);
for (int i = beginIndex; i < beginIndex + groupCount; i++) {
partGroups.get(i).initialize(model);
final int partsIndex = partGroups.get(i).partIndex;
final int paramIndex = partGroups.get(i).parameterIndex;
if (partsIndex < 0) {
continue;
}
if (i == beginIndex) {
model.setPartOpacity(partsIndex, 1.0f);
model.setParameterValue(paramIndex, 1.0f);
}
final float value =
i == beginIndex
? 1.0f
: 0.0f;
model.setPartOpacity(partsIndex, value);
model.setParameterValue(paramIndex, value);
List<PartData> link = partGroups.get(i).linkedParameter;
if (link != null) {
for (PartData data : link) {
data.initialize(model);
}
}
}
beginIndex += groupCount;
}
}
/**
* Parts opacity is copied and set to linked parts.
*
* @param model the target model
*/
private void copyPartOpacities(CubismModel model) {
for (int i = 0; i < partGroups.size(); i++) {
PartData partData = partGroups.get(i);
if (partData.linkedParameter == null) {
continue;
}
final int partIndex = partData.partIndex;
final float opacity = model.getPartOpacity(partIndex);
for (int j = 0; j < partData.linkedParameter.size(); j++) {
PartData linkedPart = partData.linkedParameter.get(j);
final int linkedPartIndex = linkedPart.partIndex;
if (linkedPartIndex < 0) {
continue;
}
model.setPartOpacity(linkedPartIndex, opacity);
}
}
}
/**
* Fade parts
*
* @param model the target model
* @param deltaTimeSeconds delta time[s]
* @param beginIndex the head index of parts groups done fading
* @param partGroupCount the number of parts groups done fading
*/
private void doFade(CubismModel model,
float deltaTimeSeconds,
int beginIndex,
int partGroupCount) {
int visiblePartIndex = -1;
float newOpacity = 1.0f;
// Get parts displayed now.
for (int i = beginIndex; i < beginIndex + partGroupCount; i++) {
final int paramIndex = partGroups.get(i).parameterIndex;
if (model.getParameterValue(paramIndex) > EPSILON) {
if (visiblePartIndex >= 0) {
break;
}
newOpacity = calculateOpacity(model, i, deltaTimeSeconds);
visiblePartIndex = i;
}
}
if (visiblePartIndex < 0) {
visiblePartIndex = 0;
newOpacity = 1.0f;
}
// Set opacity to displayed, and non-displayed parts
for (int i = beginIndex; i < beginIndex + partGroupCount; i++) {
final int partsIndex = partGroups.get(i).partIndex;
// Setting of displayed parts
if (visiblePartIndex == i) {
model.setPartOpacity(partsIndex, newOpacity);
}
// Setting of non-displayed parts
else {
final float opacity = model.getPartOpacity(partsIndex);
final float result = calcNonDisplayedPartsOpacity(opacity, newOpacity);
model.setPartOpacity(partsIndex, result);
}
}
}
/**
* Calculate the new part opacity. This method is used at fading.
*
* @param model target model
* @param index part index
* @param deltaTime delta time[s]
* @return new calculated opacity. If fade time is 0.0[s], return 1.0f.
*/
private float calculateOpacity(CubismModel model, int index, float deltaTime) {
if (fadeTimeSeconds == 0) {
return 1.0f;
}
final int partIndex = partGroups.get(index).partIndex;
float opacity = model.getPartOpacity(partIndex);
opacity += deltaTime / fadeTimeSeconds;
if (opacity > 1.0f) {
opacity = 1.0f;
}
return opacity;
}
/**
* Calculate opacity of non-displayed parts.
*
* @param currentOpacity current part opacity
* @param newOpacity calculated opacity
* @return opacity for non-displayed part
*/
private float calcNonDisplayedPartsOpacity(float currentOpacity, final float newOpacity) {
final float PHI = 0.5f;
final float BACK_OPACITY_THRESHOLD = 0.15f;
float a1; // opacity to be calculated
if (newOpacity < PHI) {
// Linear equation passing through (0,1),(PHI,PHI)
a1 = newOpacity * (PHI - 1) / PHI + 1.0f;
} else {
a1 = (1 - newOpacity) * PHI / (1.0f - PHI);
}
final float backOpacity = (1.0f - a1) * (1.0f - newOpacity);
if (backOpacity > BACK_OPACITY_THRESHOLD) {
a1 = 1.0f - BACK_OPACITY_THRESHOLD / (1.0f - newOpacity);
}
// The opacity is raised if the opacity is larger(more thicken) than the calculated opacity.
if (currentOpacity > a1) {
currentOpacity = a1;
}
return currentOpacity;
}
/**
* Epsilon value
*/
private static final float EPSILON = 0.001f;
/**
* Default fade-in duration[s]
*/
private static final float DEFAULT_FADE_IN_SECONDS = 0.5f;
// Tags of Pose3.json
private enum JsonTag {
FADE_IN("FadeInTime"),
LINK("Link"),
GROUPS("Groups"),
ID("Id");
private final String tag;
JsonTag(String tag) {
this.tag = tag;
}
}
/**
* Parts group
*/
private final List<PartData> partGroups = new ArrayList<PartData>();
/**
* Each parts group number
*/
private final List<Integer> partGroupCounts = new ArrayList<Integer>();
/**
* Fade time[s]
*/
private float fadeTimeSeconds = DEFAULT_FADE_IN_SECONDS;
/**
* Previous operated model
*/
private CubismModel lastModel;
}

View File

@@ -0,0 +1,4 @@
/**
* Provide the function to add motion information to the model in an effect-like manner, such as automatic blinking and lip-sync.
*/
package com.live2d.sdk.cubism.framework.effect;

View File

@@ -0,0 +1,22 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.exception;
/**
* Cubism SDK専用の例外
*/
public class CubismException extends Exception {
/**
* エラーメッセージを持つ例外を構築する
*
* @param msg エラーメッセージ
*/
public CubismException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.exception;
/**
* CubismJsonに関連する例外
*/
public class CubismJsonParseException extends CubismRuntimeException {
/**
* エラーメッセージを持たない例外を構築する
*/
public CubismJsonParseException() {
super(DEFAULT_MESSAGE);
}
/**
* 指定されたエラーメッセージを持つ例外を構築する
*
* @param msg エラーメッセージ
*/
public CubismJsonParseException(String msg) {
super(DEFAULT_MESSAGE + msg);
}
public CubismJsonParseException(String msg, int lineNumber) {
super("line " + lineNumber + "\n" + DEFAULT_MESSAGE + msg);
}
public CubismJsonParseException(String msg, Throwable cause) {
super(DEFAULT_MESSAGE + msg, cause);
}
public CubismJsonParseException(Throwable cause) {
super(cause);
}
private static final String DEFAULT_MESSAGE = "Failed to parse the JSON data. ";
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.exception;
/**
* CubismJsonに関連する実行時例外
*/
public class CubismJsonRuntimeException extends CubismRuntimeException {
/**
* エラーメッセージを持たない実行時例外を構築する
*/
public CubismJsonRuntimeException() {
super("");
}
/**
* 指定されたエラーメッセージを持つ例外を構築する
*
* @param msg エラーメッセージ
*/
public CubismJsonRuntimeException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.exception;
/**
* The exception class which reports that there is a syntax error in the parsed JSON string.
*/
public class CubismJsonSyntaxErrorException extends CubismJsonParseException {
/**
* Construct the exception which has the default message.
*/
public CubismJsonSyntaxErrorException() {
super(DEFAULT_MESSAGE);
}
/**
* Construct the exception which has the specified message.
*/
public CubismJsonSyntaxErrorException(String msg) {
super(DEFAULT_MESSAGE + msg);
}
/**
* Create exception with the specified message and the line number at syntax error.
*
* @param msg error message describing what happened.
* @param lineNumber line number at syntax error
*/
public CubismJsonSyntaxErrorException(String msg, int lineNumber) {
super(DEFAULT_MESSAGE + msg, lineNumber);
// String errorPoint = "line " + lineNumber + "\n";
// super(errorPoint + msg)
}
public CubismJsonSyntaxErrorException(String msg, Throwable cause) {
super(DEFAULT_MESSAGE + msg, cause);
}
public CubismJsonSyntaxErrorException(Throwable cause) {
super(cause);
}
private static final String DEFAULT_MESSAGE = "SyntaxError: ";
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.exception;
/**
* Cubism SDK専用の実行時例外
*/
public class CubismRuntimeException extends RuntimeException {
/**
* エラーメッセージを持つ実行時例外を構築する
*
* @param msg エラーメッセージ
*/
public CubismRuntimeException(String msg) {
super(msg);
}
/**
* Creates exception with the specified message and cause.
*
* @param msg error message describeing what happened.
* @param cause cause root exception that caused this exception to be thrown.
*/
public CubismRuntimeException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Create exception with the specified cause. Consider using {@link #CubismRuntimeException(String, Throwable)} instead if you can describe what happened.
*
* @param cause cause root exception that caused this exception to be thrown.
*/
public CubismRuntimeException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,4 @@
/**
* Provide the exception classes about Cubism SDK Framework.
*/
package com.live2d.sdk.cubism.framework.exception;

View File

@@ -0,0 +1,62 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.id;
/**
* The name of parameters, parts and Drawable is held in this class.
*/
public class CubismId {
/**
* Get ID name
*/
public String getString() {
return id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismId cubismId = (CubismId) o;
return id != null ? id.equals(cubismId.id) : cubismId.id == null;
}
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
/**
* Constructor
*
* @param id A ID name
* @throws IllegalArgumentException if an argument is null
*/
CubismId(String id) {
if (id == null) {
throw new IllegalArgumentException("id is null.");
}
this.id = id;
}
/**
* Copy constructor
*
* @param id the CubismId instance
*/
CubismId(CubismId id) {
this.id = id.getString();
}
/**
* ID name
*/
private final String id;
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.id;
import java.util.ArrayList;
import java.util.List;
/**
* Manager class of ID names
*/
public class CubismIdManager {
/**
* Register IDs from list
*
* @param ids id list
*/
public void registerIds(List<String> ids) {
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
registerId(id);
}
}
/**
* Register the specified number of IDs from the given list.
*
* @param ids ID list
* @param count number of IDs to be registered
*/
public void registerIds(List<String> ids, int count) {
for (int i = 0; i < count; i++) {
registerId(ids.get(i));
}
}
/**
* Register ID name
*
* @param id ID name
* @return ID instance
*/
public CubismId registerId(String id) {
CubismId foundId = findId(id);
if (foundId != null) {
return foundId;
}
CubismId cubismId = new CubismId(id);
ids.add(cubismId);
return cubismId;
}
/**
* Register ID.
*
* @param id ID instance
* @return ID instance
*/
public CubismId registerId(CubismId id) {
return registerId(id.getString());
}
/**
* Get ID from ID name.
* If the given ID has not registered, register the ID, too.
*
* @param id ID name
* @return ID instance
*/
public CubismId getId(String id) {
return registerId(id);
}
/**
* Get ID from ID instance.
* If the given ID has not registered, register the ID, too.
*
* @param id ID instance
* @return ID instance
*/
public CubismId getId(CubismId id) {
return registerId(id);
}
/**
* Check whether the ID has been already registered from an ID name.
*
* @return If given ID has been already registered, return true
*/
public boolean isExist(String id) {
return findId(id) != null;
}
public boolean isExist(CubismId id) {
return findId(id) != null;
}
/**
* Search an ID from given ID name.
*
* @param foundId ID name
* @return If there is a registered ID, return the CubismId instance.
*/
private CubismId findId(String foundId) {
for (int i = 0; i < ids.size(); i++) {
CubismId id = ids.get(i);
if (id.getString().equals(foundId)) {
return id;
}
}
return null;
}
/**
* Search an ID from given ID instance.
*
* @param foundId ID instance
* @return If there is a registered ID, return the CubismId instance.
*/
private CubismId findId(CubismId foundId) {
for (int i = 0; i < ids.size(); i++) {
CubismId id = ids.get(i);
if (id.equals(foundId)) {
return id;
}
}
return null;
}
/**
* The registered IDs list.
*/
private final List<CubismId> ids = new ArrayList<CubismId>();
}

View File

@@ -0,0 +1,4 @@
/**
* Provide the function to manage parameter names, part names, and drawable names set in the model with the Cubism SDK original type.
*/
package com.live2d.sdk.cubism.framework.id;

View File

@@ -0,0 +1,297 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.math;
import com.live2d.sdk.cubism.framework.utils.CubismDebug;
/**
* Utility class used for numerical calculations, etc.
*/
public class CubismMath {
public static final float PI = 3.1415926535897932384626433832795f;
public static final float EPSILON = 0.00001f;
public static float clampF(float val, float min, float max) {
if (val < min) {
return min;
} else if (max < val) {
return max;
}
return val;
}
/**
* Returns the value of the first argument in the range of minimum and maximum values.
*
* @param value target value
* @param min lower bound
* @param max upper bound
* @return value within the range of minimum and maximum values
*/
public static float rangeF(float value, float min, float max) {
if (value < min) {
value = min;
} else if (value > max) {
value = max;
}
return value;
}
/**
* Caluculate sine value from radian angle value.
*
* @param x angle(radian)
* @return sine value
*/
public static float sinF(float x) {
return (float) (Math.sin(x));
}
/**
* Caluculate cosine value from radian angle value.
*
* @param x angle(radian)
* @return cosine value
*/
public static float cosF(float x) {
return (float) (Math.cos(x));
}
/**
* Calculate the absolute value.
*
* @param x target value
* @return calculated absolute value
*/
public static float absF(float x) {
return Math.abs(x);
}
/**
* Calculate the square root.
*
* @param x target value
* @return calculated square root value
*/
public static float sqrtF(float x) {
return (float) (Math.sqrt(x));
}
/**
* Calculate the easing processed sin. Can be used for easing during fade in/out.
*
* @param value target value
* @return eased sin value
*/
public static float getEasingSine(float value) {
if (value < 0.0f) {
return 0.0f;
} else if (value > 1.0f) {
return 1.0f;
}
return (float) (0.5f - 0.5f * Math.cos(value * PI));
}
/**
* Convert an angle value to a radian value.
*
* @param degrees angle value[degree]
* @return radian value converted from angle value
*/
public static float degreesToRadian(float degrees) {
return (degrees / 180.0f) * PI;
}
/**
* Convert a radian value to an angle value
*
* @param radian radian value[rad]
* @return angle value converted from radian value
*/
public static float radianToDegrees(float radian) {
return (radian * 180.0f) / PI;
}
/**
* Calculate a radian value from two vectors.
*
* @param from position vector of a starting point
* @param to position vector of an end point
* @return direction vector calculated from radian value
*/
public static float directionToRadian(CubismVector2 from, CubismVector2 to) {
float q1 = (float) Math.atan2(to.y, to.x);
float q2 = (float) Math.atan2(from.y, from.x);
float radian = q1 - q2;
while (radian < -PI) {
radian += PI * 2.0f;
}
while (radian > PI) {
radian -= PI * 2.0f;
}
return radian;
}
/**
* Calculate an angle value from two vectors.
*
* @param from position vector of a starting point
* @param to position vector of an end point
* @return direction vector calculated from angle value
*/
public static float directionToDegrees(CubismVector2 from, CubismVector2 to) {
float radian = directionToRadian(from, to);
float degree = radianToDegrees(radian);
if ((to.x - from.x) > 0.0f) {
degree = -degree;
}
return degree;
}
/**
* Convert a radian value to a direction vector.
*
* @param totalAngle radian value
* @param result CubismVector2 instance for storing calculation results
* @return direction vector calculated from radian value
*/
public static CubismVector2 radianToDirection(float totalAngle, CubismVector2 result) {
result.x = sinF(totalAngle);
result.y = cosF(totalAngle);
return result;
}
/**
* Calculate the solution to the quadratic equation.
*
* @param a coefficient value of quadratic term
* @param b coefficient value of the first order term
* @param c value of constant term
* @return solution of a quadratic equation
*/
public static float quadraticEquation(float a, float b, float c) {
if (absF(a) < EPSILON) {
if (absF(b) < EPSILON) {
return -c;
}
return -c / b;
}
return -(b + sqrtF(b * b - 4.0f * a * c)) / (2.0f * a);
}
/**
* Calculate the solution of the cubic equation corresponding to the Bezier's t-value by the Cardano's formula.
* Returns a solution that has a value of 0.0~1.0 when it is a multiple solution.
*
* @param a coefficient value of cubic term
* @param b coefficient value of quadratic term
* @param c coefficient value of the first order term
* @param d value of constant term
* @return solution between 0.0~1.0
*/
public static float cardanoAlgorithmForBezier(float a, float b, float c, float d) {
if (absF(a) < EPSILON) {
return rangeF(quadraticEquation(b, c, d), 0.0f, 1.0f);
}
float ba = b / a;
float ca = c / a;
float da = d / a;
float p = (3.0f * ca - ba * ba) / 3.0f;
float p3 = p / 3.0f;
float q = (2.0f * ba * ba * ba - 9.0f * ba * ca + 27.0f * da) / 27.0f;
float q2 = q / 2.0f;
float discriminant = q2 * q2 + p3 * p3 * p3;
final float center = 0.5f;
final float threshold = center + 0.01f;
if (discriminant < 0.0f) {
float mp3 = -p / 3.0f;
float mp33 = mp3 * mp3 * mp3;
float r = sqrtF(mp33);
float t = -q / (2.0f * r);
float cosphi = rangeF(t,
-1.0f, 1.0f);
float phi = (float) Math.acos(cosphi);
float crtr = (float) Math.cbrt(r);
float t1 = 2.0f * crtr;
float root1 = t1 * cosF(phi / 3.0f) - ba / 3.0f;
if (Math.abs(root1 - center) < threshold) {
return rangeF(root1, 0.0f, 1.0f);
}
float root2 = t1 * cosF((phi + 2.0f * PI) / 3.0f) - ba / 3.0f;
if (Math.abs(root2 - center) < threshold) {
return rangeF(root2, 0.0f, 1.0f);
}
float root3 = t1 * cosF((phi + 4.0f * PI) / 3.0f) - ba / 3.0f;
return rangeF(root3, 0.0f, 1.0f);
}
if (discriminant == 0.0f) {
float u1;
if (q2 < 0.0f) {
u1 = (float) Math.cbrt(-q2);
} else {
u1 = (float) -Math.cbrt(q2);
}
float root1 = 2.0f * u1 - ba / 3.0f;
if (Math.abs(root1 - center) < threshold) {
return rangeF(root1, 0.0f, 1.0f);
}
float root2 = -u1 - ba / 3.0f;
return rangeF(root2, 0.0f, 1.0f);
}
float sd = sqrtF(discriminant);
float u1 = (float) Math.cbrt(sd - q2);
float v1 = (float) Math.cbrt(sd + q2);
float root1 = u1 - v1 - ba / 3.0f;
return rangeF(root1, 0.0f, 1.0f);
}
/**
* 浮動小数点の余りを求める。
*
* @param dividend 被除数(割られる値)
* @param divisor 除数(割る値)
* @return 余り
*/
public static float modF(float dividend, float divisor) {
if (
Float.isInfinite(dividend)
|| Float.compare(divisor, 0.0f) == 0
|| Float.isNaN(dividend)
|| Float.isNaN(divisor)
) {
CubismDebug.cubismLogWarning("dividend: %f, divisor: %f ModF() returns 'NaN'.", dividend, divisor);
return Float.NaN;
}
return dividend % divisor;
}
/**
* private constructor.
* (Prevent instantiation.)
*/
private CubismMath() {}
}

View File

@@ -0,0 +1,365 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.math;
import java.util.Arrays;
/**
* Utility class for 4x4 matrix.
*/
public class CubismMatrix44 {
/**
* Creates a {@code CubismMatrix4x4} instance.
* The created instance is initialized to a unit matrix.
*
* @return a {@code CubismMatrix44} instance
*/
public static CubismMatrix44 create() {
return new CubismMatrix44();
}
/**
* Creates a {@code CubismMatrix44} instance with float array (length == 16).
* If the argument (float array) is either {@code null} or it does not have a size of 16, throws {@code IllegalArgumentException}.
*
* @param matrix the 4x4 (length == 16) matrix represented by 16 floating-point numbers array
* @return a {@code CubismMatrix44} instance
*
* @throws IllegalArgumentException if the argument is either {@code null} or it does not have a size of 16.
*/
public static CubismMatrix44 create(float[] matrix) {
if (isNot4x4Matrix(matrix)) {
throw new IllegalArgumentException("The passed array is either 'null' or does not have a size of 16.");
}
return new CubismMatrix44(matrix);
}
/**
* Creates new {@code CubismMatrix44} instance with a {@code CubismMatrix44} instance.
* This method works the same as the copy method.
*
* <p>
* If the argument {@code null}, throws {@code IllegalArgumentException}.
*
* @param matrix the 4x4 matrix represented by a {@code CubismMatrix44}
* @return a {@code CubismMatrix44} instance
*
* @throws IllegalArgumentException if the argument is either {@code null} or it does not have a size of 16.
*/
public static CubismMatrix44 create(CubismMatrix44 matrix) {
if (matrix == null) {
throw new IllegalArgumentException("The passed CubismMatrix44 instance is 'null'");
}
return new CubismMatrix44(matrix.tr);
}
/**
* Returns the product of the two matrices received.
* This is a matrix calculation, therefore note that exchanging the first and second arguments will produce different results.
*
* @param multiplicand the multiplicand matrix
* @param multiplier the multiplier matrix
* @param dst the destination array
* @throws IllegalArgumentException if the argument is either {@code null} or it does not have a size of 16.
*/
public static void multiply(float[] multiplicand, float[] multiplier, float[] dst) {
if (isNot4x4Matrix(multiplicand) || isNot4x4Matrix(multiplier) || isNot4x4Matrix(dst)) {
throw new IllegalArgumentException("The passed array is either 'null' or does not have a size of 16.");
}
for (int i = 0; i < 16; i++) {
matrixForMultiplication[i] = 0.0f;
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
matrixForMultiplication[j + i * 4] += multiplicand[k + i * 4] * multiplier[j + k * 4];
}
}
}
System.arraycopy(matrixForMultiplication, 0, dst, 0, 16);
}
/**
* Initialize to a unit matrix.
*/
public void loadIdentity() {
resetMatrixForCalcToUnitMatrix();
setMatrix(matrixForCalculation);
}
/**
* Return the matrix as an array of floating point numbers.
*
* @return the 4x4 matrix represented by 16 floating-point numbers array
*/
public float[] getArray() {
return tr;
}
/**
* Sets this matrix with the given {@code CubismMatrix44} instance.
*
* @param matrix the new 4x4 matrix represented by {@code CubismMatrix44} instance
*/
public void setMatrix(CubismMatrix44 matrix) {
System.arraycopy(matrix.tr, 0, tr, 0, 16);
}
/**
* Sets this matrix with the array of float type (length == 16).
*
* @param matrix the new 4x4 matrix represented by 16 floating-point numbers array
* @throws IllegalArgumentException if the argument is either {@code null} or it does not have a size of 16.
*/
public void setMatrix(float[] matrix) {
if (isNot4x4Matrix(matrix)) {
throw new IllegalArgumentException("The passed array is either 'null' or does not have a size of 16.");
}
System.arraycopy(matrix, 0, tr, 0, 16);
}
/**
* Returns the scale of the X-axis.
*
* @return the scale of the X-axis
*/
public float getScaleX() {
return tr[0];
}
/**
* Returns the scale of the Y-axis.
*
* @return the scale of the Y-axis
*/
public float getScaleY() {
return tr[5];
}
/**
* Returns the amount of movement in the X-axis direction.
*
* @return the amount of movement in the X-axis direction
*/
public float getTranslateX() {
return tr[12];
}
/**
* Returns the amount of movement in the Y-axis direction.
*
* @return the amount of movement in the Y-axis direction
*/
public float getTranslateY() {
return tr[13];
}
/**
* Returns the X-coordinate value transformed by the current matrix.
* <p>
* For example, used to obtain the position on the screen from the vertex coordinates of a model in the local coordinate system.
*
* @param src the X-coordinate value to be transformed
* @return the X-coordinate value transformed by the current matrix
*/
public float transformX(float src) {
return tr[0] * src + tr[12];
}
/**
* Returns the Y-coordinate value transformed by the current matrix.
* <p>
* For example, used to obtain the position on the screen from the vertex coordinates of a model in the local coordinate system.
*
* @param src the Y-coordinate value to be transformed
* @return the Y-coordinate value transformed by the current matrix
*/
public float transformY(float src) {
return tr[5] * src + tr[13];
}
/**
* Returns the X-coordinate value inverse-transformed by the current matrix.
* <p>
* For example, used to obtain the coordinates in the model's local coordinate system from the entered coordinates in the screen coordinate system, such as collision detection.
*
* @param src the X-coordinate value to be inverse-transformed
* @return the X-coordinate value inverse-transformed by the current matrix
*/
public float invertTransformX(float src) {
return (src - tr[12]) / tr[0];
}
/**
* Returns the Y-coordinate value inverse-transformed by the current matrix.
* <p>
* For example, used to obtain the coordinates in the model's local coordinate system from the entered coordinates in the screen coordinate system, such as collision detection.
*
* @param src the Y-coordinate value to be inverse-transformed
* @return the Y-coordinate value inverse-transformed by the current matrix
*/
public float invertTransformY(float src) {
return (src - tr[13]) / tr[5];
}
/**
* Translates the current matrix relatively by the amount of the arguments.
* The coordinate of the arguments must be entered in a screen coodinate system.
*
* @param x the amount of movement in X-axis direction
* @param y the amount of movement in Y-axis direction
*/
public void translateRelative(float x, float y) {
resetMatrixForCalcToUnitMatrix();
matrixForCalculation[12] = x;
matrixForCalculation[13] = y;
multiply(matrixForCalculation, tr, tr);
}
/**
* Translates the current matrix to the position specified by the arguments.
* The coordinate of the arguments must be entered in a screen coodinate system.
*
* @param x X-coordinate of destination
* @param y Y-coordinate of destination
*/
public void translate(float x, float y) {
tr[12] = x;
tr[13] = y;
}
/**
* Translates the X-coordinate of the current matrix to the position specified by the argument.
* The coordinate of the argument must be entered in a screen coodinate system.
*
* @param x X-coordinate of destination
*/
public void translateX(float x) {
tr[12] = x;
}
/**
* Translates the Y-coordinate of the current matrix to the position specified by the argument.
* The coordinate of the argument must be entered in a screen coodinate system.
*
* @param y Y-coordinate of destination
*/
public void translateY(float y) {
tr[13] = y;
}
/**
* Sets relatively the scaling rate to the current matrix.
*
* @param x the scaling rate in the X-axis direction
* @param y the scaling rate in the Y-axis direction
*/
public void scaleRelative(float x, float y) {
resetMatrixForCalcToUnitMatrix();
matrixForCalculation[0] = x;
matrixForCalculation[5] = y;
multiply(matrixForCalculation, tr, tr);
}
/**
* Sets the scaling rate of the current matrix the specified scale by the arguments.
*
* @param x the scaling rate in the X-axis direction
* @param y the scaling rate in the Y-axis direction
*/
public void scale(float x, float y) {
tr[0] = x;
tr[5] = y;
}
/**
* Returns the product of the current matrix and the given {@code CubismMatrix44} instance for the argument.
*
* @param multiplier the multiplier matrix
*/
public void multiplyByMatrix(CubismMatrix44 multiplier) {
multiply(tr, multiplier.tr, tr);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismMatrix44 that = (CubismMatrix44) o;
return Arrays.equals(tr, that.tr);
}
@Override
public int hashCode() {
return Arrays.hashCode(tr);
}
/**
* Constructs a new 4x4 matrix initialized with a unit matrix without taking any arguments.
*/
protected CubismMatrix44() {
tr = new float[16];
loadIdentity();
}
/**
* Constructs a new 4x4 matrix. It is initialized with a unit matrix represented by the float array that has a size of 16 passed for the argument.
*
* <p>
* This is not a public method, therefore it does not expect that {@code null} value or other illegal value is passed.
*
* @param matrix the 4x4 (length == 16) matrix represented by 16 floating-point numbers array
*/
protected CubismMatrix44(float[] matrix) {
tr = new float[16];
setMatrix(matrix);
}
/**
* The 4×4 matrix array.
*/
protected float[] tr;
/**
* If the argument given is 4x4 matrix array, returns {@code true}. If the argument is {@code null} or does not have a size of 16, returns {@code false}.
*
* @param array the float array to be checked
* @return {@code true} if the argument given is a 4x4 matrix, otherwise {@code false}
*/
private static boolean isNot4x4Matrix(float[] array) {
return (array == null || array.length != 16);
}
/**
* Resets the variable '_4x4MatrixForCalculation' to a unit matrix.
*/
private static void resetMatrixForCalcToUnitMatrix() {
for (int i = 0; i < 16; i++) {
matrixForCalculation[i] = 0.0f;
}
matrixForCalculation[0] = 1.0f;
matrixForCalculation[5] = 1.0f;
matrixForCalculation[10] = 1.0f;
matrixForCalculation[15] = 1.0f;
}
/**
* The 4x4 matrix array for matrix calculation.
* This exists for avoiding creating a new float array at running method.
*/
private static final float[] matrixForCalculation = new float[16];
/**
* The 4x4 matrix array for 'multiply' method.
* Prevents _4x4MatrixForCalculation from resetting the multiplicand information when it is passed to the 'multiply' method.
*/
private static final float[] matrixForMultiplication = new float[16];
}

View File

@@ -0,0 +1,243 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.math;
import java.util.Map;
/**
* 4x4 matrix class for setting model coordinates.
*/
public class CubismModelMatrix extends CubismMatrix44 {
/**
* Create the new CubismModelMatrix instance with the width and height passed as arguments.
*
* @param w width
* @param h height
* @return CubismModelMatrix instance with the width and height
*
* @throws IllegalArgumentException if arguments equals 0 or are less than 0
*/
public static CubismModelMatrix create(float w, float h) {
if (w <= 0 || h <= 0) {
throw new IllegalArgumentException("width or height equals 0 or is less than 0.");
}
return new CubismModelMatrix(w, h);
}
/**
* Create the new CubismModelMatrix instance from the CubismModelMatrix instance.
* It works the same way as a copy constructor.
*
* @param modelMatrix CubismModelMatrix instance to be copied
* @return Copied CubismModelMatrix instance
*/
public static CubismModelMatrix create(CubismModelMatrix modelMatrix) {
return new CubismModelMatrix(modelMatrix);
}
/**
* Set the width.
*
* @param w width
*/
public void setWidth(float w) {
final float scaleX = w / width;
scale(scaleX, scaleX);
}
/**
* Set the height.
*
* @param h height
*/
public void setHeight(float h) {
final float scaleX = h / height;
scale(scaleX, scaleX);
}
/**
* Set the position.
*
* @param x X-axis position
* @param y Y-axis position
*/
public void setPosition(float x, float y) {
translate(x, y);
}
/**
* Set the center position.
* Be sure to set the width or height before using this method.
*
* @param x center position of X-axis
* @param y center position of Y-axis
*/
public void setCenterPosition(float x, float y) {
centerX(x);
centerY(y);
}
/**
* Set the position of the upper edge.
*
* @param y the position of the upper edge
*/
public void top(float y) {
setY(y);
}
/**
* Set the position of the bottom edge.
*
* @param y the position of the bottom edge
*/
public void bottom(float y) {
final float h = height * getScaleY();
translateY(y - h);
}
/**
* Set the position of the left edge.
*
* @param x the position of the left edge
*/
public void left(float x) {
setX(x);
}
/**
* Set the position of the right edge.
*
* @param x the position of the right edge
*/
public void right(float x) {
final float w = width * getScaleX();
translateX(x - w);
}
/**
* Set the center position of X-axis.
*
* @param x center position of X-axis
*/
public void centerX(float x) {
final float w = width * getScaleX();
translateX(x - (w / 2.0f));
}
/**
* Set the position of X-axis.
*
* @param x position of X-axis
*/
public void setX(float x) {
translateX(x);
}
/**
* Set the center position of Y-axis.
*
* @param y center position of Y-axis
*/
public void centerY(float y) {
final float h = height * getScaleY();
translateY(y - (h / 2.0f));
}
/**
* Set the position of Y-axis.
*
* @param y position of Y-axis
*/
public void setY(float y) {
translateY(y);
}
/**
* Set position from layout information.
*
* @param layout layout information
*/
public void setupFromLayout(Map<String, Float> layout) {
final String keyWidth = "width";
final String keyHeight = "height";
final String keyX = "x";
final String keyY = "y";
final String keyCenterX = "center_x";
final String keyCenterY = "center_y";
final String keyTop = "top";
final String keyBottom = "bottom";
final String keyLeft = "left";
final String keyRight = "right";
for (Map.Entry<String, Float> entry : layout.entrySet()) {
String key = entry.getKey();
if (key.equals(keyWidth)) {
setWidth(entry.getValue());
} else if (key.equals(keyHeight)) {
setHeight(entry.getValue());
}
}
for (Map.Entry<String, Float> entry : layout.entrySet()) {
String key = entry.getKey();
float value = entry.getValue();
if (key.equals(keyX)) {
setX(value);
} else if (key.equals(keyY)) {
setY(value);
} else if (key.equals(keyCenterX)) {
centerX(value);
} else if (key.equals(keyCenterY)) {
centerY(value);
} else if (key.equals(keyTop)) {
top(value);
} else if (key.equals(keyBottom)) {
bottom(value);
} else if (key.equals(keyLeft)) {
left(value);
} else if (key.equals(keyRight)) {
right(value);
}
}
}
/**
* Constructor
*/
private CubismModelMatrix(float w, float h) {
super();
width = w;
height = h;
setHeight(2.0f);
}
/**
* Copy constructor
*
* @param modelMatrix model matrix to be copied
*/
private CubismModelMatrix(CubismModelMatrix modelMatrix) {
super();
System.arraycopy(modelMatrix.tr, 0, this.tr, 0, 16);
width = modelMatrix.width;
height = modelMatrix.height;
}
/**
* width
*/
private final float width;
/**
* height
*/
private final float height;
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.math;
/**
* This class provides face orientation control functionality.
*/
public class CubismTargetPoint {
/**
* Update face orientation control.
*
* @param deltaTimeSeconds delta time[s]
*/
public void update(float deltaTimeSeconds) {
// Add delta time
userTimeSeconds += deltaTimeSeconds;
// Set the maximum speed considering the average speed when swinging the head from the center to the left or right.
// The swing of the face is set from the center (0.0) to the left and right (+-1.0).
final float faceParamMaxV = 40.0f / 10.0f;
// Upper limit of the speed that can be changed per frame.
final float maxV = faceParamMaxV / FRAME_RATE;
if (lastTimeSeconds == 0.0f) {
lastTimeSeconds = userTimeSeconds;
return;
}
final float deltaTimeWeight = (userTimeSeconds - lastTimeSeconds) * FRAME_RATE;
lastTimeSeconds = userTimeSeconds;
// Calculate the time it takes to reach the maximum speed.
final float timeToMaxSpeed = 0.15f;
final float frameToMaxSpeed = timeToMaxSpeed * FRAME_RATE; // sec * frame/sec
final float maxA = deltaTimeWeight * maxV / frameToMaxSpeed;
final float dx = faceTargetX - faceX;
final float dy = faceTargetY - faceY;
// In the case of there is no change.
if (CubismMath.absF(dx) <= EPSILON && CubismMath.absF(dy) <= EPSILON) {
return;
}
// If the speed is greater than the maximum speed, reduce the speed.
final float d = CubismMath.sqrtF((dx * dx) + (dy * dy));
// Maximum velocity vector in the direction of travel.
final float vx = maxV * dx / d;
final float vy = maxV * dy / d;
// Calculate the change (acceleration) from the current speed to the new speed.
float ax = vx - faceVX;
float ay = vy - faceVY;
float a = CubismMath.sqrtF((ax * ax) + (ay * ay));
// At acceleration.
if (a < -maxA || a > maxA) {
ax *= maxA / a;
ay *= maxA / a;
}
// Add the acceleration to the original speed to obtain the new speed.
faceVX += ax;
faceVY += ay;
// Processing for smooth deceleration when approaching the desired direction
// Calculate the maximum speed currently available based on the relationship between distance and speed at which an object can stop at a set acceleration, and slow down if the speed is greater than that.
// Humans are inherently more flexible because they can adjust force (acceleration) with muscle power, but this is a simple process.
{
// Relational expression between acceleration, velocity, and distance.
// 2 6 2 3
// sqrt(a t + 16 a h t - 8 a h) - a t
// v = --------------------------------------
// 2
// 4 t - 2
// (t=1)
// Time t is calculated in advance as 1/60 (frame rate, no units) for acceleration and velocity. Therefore, it can be erased as t = 1 (unverified)
final float maxV2 = 0.5f * (CubismMath.sqrtF((maxA * maxA) + 16.0f * maxA * d - 8.0f * maxA * d) - maxA);
final float curV = CubismMath.sqrtF((faceVX * faceVX) + (faceVY * faceVY));
// Decelerate to maximum speed when current speed > maximum speed.
if (curV > maxV2) {
faceVX *= maxV2 / curV;
faceVY *= maxV2 / curV;
}
}
faceX += faceVX;
faceY += faceVY;
}
/**
* Get the face orientation value on the X-axis.
*
* @return X-axis face orientation value (-1.0 - 1.0)
*/
public float getX() {
return faceX;
}
/**
* Get the face orientation value on the Y-axis.
*
* @return Y-axis face orientation value (-1.0 - 1.0)
*/
public float getY() {
return faceY;
}
/**
* Set a target value for face orientation.
*
* @param x X-axis face orientation value (-1.0 - 1.0)
* @param y Y-axis face orientation value (-1.0 - 1.0)
*/
public void set(float x, float y) {
faceTargetX = x;
faceTargetY = y;
}
/**
* Framerate per seconds[s]
*/
private static final int FRAME_RATE = 30;
/**
* Epsilon value
*/
private static final float EPSILON = 0.01f;
/**
* X target value for face orientation (getting closer to this value)
*/
private float faceTargetX;
/**
* Y target value for face orientation (getting closer to this value)
*/
private float faceTargetY;
/**
* face orientation X (-1.0 - 1.0)
*/
private float faceX;
/**
* face orientation Y (-1.0 - 1.0)
*/
private float faceY;
/**
* speed of change in face orientation X
*/
private float faceVX;
/**
* speed of change in face orientation Y
*/
private float faceVY;
/**
* last executed time[s]
*/
private float lastTimeSeconds;
/**
* total elapsed time[s]
*/
private float userTimeSeconds;
}

View File

@@ -0,0 +1,310 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.math;
/**
* This class offers 2D vector function.
*/
public class CubismVector2 {
/**
* Constructor.
*/
public CubismVector2() {}
/**
* Constructor.
*
* @param x x-value
* @param y y-value
*/
public CubismVector2(float x, float y) {
set(x, y);
}
/**
* Copy constructor.
*
* @param vec the vector which is copied
*/
public CubismVector2(CubismVector2 vec) {
set(vec);
}
/**
* 第1引数と第2引数のベクトルを加算して、計算結果を第3引数のベクトルにコピーする。
*
* @param augend 被加数のベクトル
* @param addend 加数のベクトル
* @param dest 代入先のベクトル
* @return 代入先のベクトル
*/
public static CubismVector2 add(CubismVector2 augend, CubismVector2 addend, CubismVector2 dest) {
dest.x = augend.x + addend.x;
dest.y = augend.y + addend.y;
return dest;
}
/**
* 第1引数と第2引数のベクトルを引き算して、計算結果を第3引数のベクトルにコピーする。
*
* @param minuend 被減数
* @param subtrahend 減数
* @param dest 代入先のベクトル
* @return 代入先のベクトル
*/
public static CubismVector2 subtract(CubismVector2 minuend, CubismVector2 subtrahend, CubismVector2 dest) {
dest.x = minuend.x - subtrahend.x;
dest.y = minuend.y - subtrahend.y;
return dest;
}
/**
* 第1引数と第2引数のベクトルを掛け算して、計算結果を第3引数のベクトルにコピーする。
*
* @param multiplicand 被乗数
* @param multiplier 乗数
* @param dest 代入先のベクトル
* @return 代入先のベクトル
*/
public static CubismVector2 multiply(CubismVector2 multiplicand, CubismVector2 multiplier, CubismVector2 dest) {
dest.x = multiplicand.x * multiplier.x;
dest.y = multiplicand.y * multiplier.y;
return dest;
}
/**
* 第1引数のベクトルと第2引数のスカラーを掛け算して、計算結果を第3引数のベクトルにコピーする。
*
* @param multiplicand 被乗数のベクトル
* @param multiplier 乗数のスカラー
* @param dest 代入先のベクトル
* @return 代入先のベクトル
*/
public static CubismVector2 multiply(CubismVector2 multiplicand, float multiplier, CubismVector2 dest) {
dest.x = multiplicand.x * multiplier;
dest.y = multiplicand.y * multiplier;
return dest;
}
/**
* 第1引数と第2引数のベクトルを割り算して、計算結果を第3引数のベクトルにコピーする。
*
* @param dividend 被除数
* @param divisor 除数
* @param dest 代入先のベクトル
* @return 代入先のベクトル
*/
public static CubismVector2 divide(CubismVector2 dividend, CubismVector2 divisor, CubismVector2 dest) {
dest.x = dividend.x / divisor.x;
dest.y = dividend.y / divisor.y;
return dest;
}
/**
* 第1引数のベクトルと第2引数のスカラーを割り算して、計算結果を第3引数のベクトルにコピーする。
*
* @param dividend 被除数のベクトル
* @param divisor 除数のスカラー
* @param dest 代入先のベクトル
* @return 代入先のベクトル
*/
public static CubismVector2 divide(CubismVector2 dividend, float divisor, CubismVector2 dest) {
dest.x = dividend.x / divisor;
dest.y = dividend.y / divisor;
return dest;
}
/**
* Add the given vector to this vector.
*
* @param vec the added vector
* @return calculated result
*/
public CubismVector2 add(CubismVector2 vec) {
this.x += vec.x;
this.y += vec.y;
return this;
}
/**
* Subtract the given vector from this.
*
* @param vec the subtracted vector
* @return the calculated result
*/
public CubismVector2 subtract(CubismVector2 vec) {
this.x -= vec.x;
this.y -= vec.y;
return this;
}
/**
* Multiply this vector by the given vector.
*
* @param vec multiplier
* @return the calculated result
*/
public CubismVector2 multiply(CubismVector2 vec) {
x *= vec.x;
y *= vec.y;
return this;
}
/**
* Multiply this vector by a scalar value.
*
* @param scalar a scalar value
* @return a calculated value
*/
public CubismVector2 multiply(float scalar) {
x *= scalar;
y *= scalar;
return this;
}
/**
* Divide this vector by the given vector.
*
* @param vec divisor
* @return the calculated result
*/
public CubismVector2 divide(CubismVector2 vec) {
x /= vec.x;
y /= vec.y;
return this;
}
/**
* Divide this vector by a scalar value.
*
* @param scalar a scalar value
* @return a calculated value
*/
public CubismVector2 divide(float scalar) {
this.x /= scalar;
this.y /= scalar;
return this;
}
/**
* Normalize this vector.
*/
public void normalize() {
float length = (float) (Math.pow((x * x) + (y * y), 0.5f));
x /= length;
y /= length;
}
/**
* Get the length of this vector.
*
* @return the length of this vector
*/
public float getLength() {
return CubismMath.sqrtF(x * x + y * y);
}
/**
* Get a distance between vectors.
*
* @param vec a position vector
* @return a distance between vectors
*/
public float getDistanceWith(CubismVector2 vec) {
return CubismMath.sqrtF(((this.x - vec.x) * (this.x - vec.x)) + ((this.y - vec.y) * (this.y - vec.y)));
}
/**
* Calculate dot product
*
* @param vec a vector
* @return a calculated result
*/
public float dot(CubismVector2 vec) {
return (this.x * vec.x) + (this.y * vec.y);
}
/**
* 与えられたベクトルのx, yの値をこのベクトルのx, yに設定する。
*
* @param vec copied vector
* @return this vector for chaining
*/
public CubismVector2 set(CubismVector2 vec) {
this.x = vec.x;
this.y = vec.y;
return this;
}
/**
* 与えられたx, yの値をこのベクトルのx, yに設定する。
*
* @param x x value
* @param y y value
* @return this vector for chaining
*/
public CubismVector2 set(float x, float y) {
this.x = x;
this.y = y;
return this;
}
/**
* Sets the components of this vector to 0.
*
* @return this vector for chaining
*/
public CubismVector2 setZero() {
this.x = 0.0f;
this.y = 0.0f;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismVector2 that = (CubismVector2) o;
if (Float.compare(that.x, x) != 0) return false;
return Float.compare(that.y, y) == 0;
}
@Override
public int hashCode() {
int result = (x != 0.0f ? Float.floatToIntBits(x) : 0);
result = 31 * result + (y != 0.0f ? Float.floatToIntBits(y) : 0);
return result;
}
/**
* X-value
*/
public float x;
/**
* Y-value
*/
public float y;
}

View File

@@ -0,0 +1,294 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.math;
/**
* Class of 4x4 matrices that can be used to reposition the camera.
*/
public class CubismViewMatrix extends CubismMatrix44 {
public CubismViewMatrix() {}
/**
* Move for the given arguments.
* The amount of movement is adjusted.
*
* @param x amount of X-axis movement
* @param y amount of Y-axis movement
*/
public void adjustTranslate(float x, float y) {
if (tr[0] * maxLeft + (tr[12] + x) > screenLeft) {
x = screenLeft - tr[0] * maxLeft - tr[12];
}
if (tr[0] * maxRight + (tr[12] + x) < screenRight) {
x = screenRight - tr[0] * maxRight - tr[12];
}
if (tr[5] * maxTop + (tr[13] + y) < screenTop) {
y = screenTop - tr[5] * maxTop - tr[13];
}
if (tr[5] * maxBottom + (tr[13] + y) > screenBottom) {
y = screenBottom - tr[5] * maxBottom - tr[13];
}
float[] tr = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
x, y, 0.0f, 1.0f
};
multiply(tr, this.tr, this.tr);
}
/**
* Scale with the given arguments.
* The amount of scaling is adjusted.
*
* @param cx center position of X-axis to be scaled
* @param cy center position of Y-axis to be scaled
* @param scale scaling rate
*/
public void adjustScale(final float cx, final float cy, float scale) {
float maxScale = getMaxScale();
float minScale = getMinScale();
float targetScale = scale * tr[0];
if (targetScale < minScale) {
if (tr[0] > 0.0f) {
scale = minScale / tr[0];
}
} else if (targetScale > maxScale) {
if (tr[0] > 0.0f) {
scale = maxScale / tr[0];
}
}
float[] tr1 = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
cx, cy, 0.0f, 1.0f
};
float[] tr2 = {
scale, 0.0f, 0.0f, 0.0f,
0.0f, scale, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
float[] tr3 = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-cx, -cy, 0.0f, 1.0f
};
multiply(tr3, tr, tr);
multiply(tr2, tr, tr);
multiply(tr1, tr, tr);
}
/**
* Get the X-axis position of the left side of the logical coordinate corresponding to the device.
*
* @return X-axis position of the left side of the logical coordinate corresponding to the device
*/
public float getScreenLeft() {
return screenLeft;
}
/**
* Get the X-axis position of the right side of the logical coordinate corresponding to the device.
*
* @return X-axis position of the right side of the logical coordinate corresponding to the device
*/
public float getScreenRight() {
return screenRight;
}
/**
* Get the Y-axis position of the top side of the logical coordinate corresponding to the device.
*
* @return Y-axis position of the top side of the logical coordinate corresponding to the device
*/
public float getScreenTop() {
return screenTop;
}
/**
* Get the Y-axis position of the bottom side of the logical coordinate corresponding to the device.
*
* @return Y-axis position of the bottom side of the logical coordinate corresponding to the device
*/
public float getScreenBottom() {
return screenBottom;
}
/**
* Get the maximum X-axis position of the left side.
*
* @return maximum X-axis position of the left side
*/
public float getMaxLeft() {
return maxLeft;
}
/**
* Get the maximum X-axis position of the right side.
*
* @return maximum X-axis position of the right side
*/
public float getMaxRight() {
return maxRight;
}
/**
* Get the maximum Y-axis position of the top side.
*
* @return maximum Y-axis position of the top side
*/
public float getMaxTop() {
return maxTop;
}
/**
* Get the maximum Y-axis position of the bottom side.
*
* @return maximum Y-axis position of the bottom side
*/
public float getMaxBottom() {
return maxBottom;
}
/**
* Get the maximum scaling.
*
* @return maximum scaling
*/
public float getMaxScale() {
return maxScale;
}
/**
* Set the maximum scaling.
*
* @param maxScale maximum scaling
*/
public void setMaxScale(float maxScale) {
this.maxScale = maxScale;
}
/**
* Check to see if the magnification ratio is at its maximum.
*
* @return Return true if the scaling factor is at its maximum.
*/
public boolean isMaxScale() {
return getScaleX() >= maxScale;
}
/**
* Get the minimum scaling.
*
* @return minimum scaling
*/
public float getMinScale() {
return minScale;
}
/**
* Set the minimum scaling.
*
* @param minScale minimum scaling
*/
public void setMinScale(float minScale) {
this.minScale = minScale;
}
/**
* Check to see if the magnification ratio is at its minimum.
*
* @return Return true if the scaling factor is at its minimum.
*/
public boolean isMinScale() {
return getScaleX() <= minScale;
}
/**
* Set the range on the logical coordinates corresponding to the device.
*
* @param left position of X-axis on the left side
* @param right position of X-axis on the right side
* @param bottom position of Y-axis on the bottom side
* @param top position of Y-axis on the top side
*/
public void setScreenRect(float left, float right, float bottom, float top) {
screenLeft = left;
screenRight = right;
screenBottom = bottom;
screenTop = top;
}
/**
* Set the movable range on the logical coordinates corresponding to the device.
*
* @param left position of X-axis on the left side
* @param right position of X-axis on the right side
* @param bottom position of Y-axis on the bottom side
* @param top position of Y-axis on the top side
*/
public void setMaxScreenRect(float left, float right, float bottom, float top) {
maxLeft = left;
maxRight = right;
maxBottom = bottom;
maxTop = top;
}
/**
* range on logical coordinates corresponding to device (left side X-axis position)
*/
private float screenLeft;
/**
* range on logical coordinates corresponding to device (right side X-axis position)
*/
private float screenRight;
/**
* range on logical coordinates corresponding to device (top side Y-axis position)
*/
private float screenBottom;
/**
* range on logical coordinates corresponding to device (bottom side Y-axis position)
*/
private float screenTop;
/**
* Moveable range on logical coordinates (left side X-axis position)
*/
private float maxLeft;
/**
* Moveable range on logical coordinates (right side X-axis position)
*/
private float maxRight;
/**
* Moveable range on logical coordinates (top side Y-axis position)
*/
private float maxBottom;
/**
* Moveable range on logical coordinates (bottom side Y-axis position)
*/
private float maxTop;
/**
* maximum value of scaling rate
*/
private float maxScale;
/**
* minimum value of scaling rate
*/
private float minScale;
}

View File

@@ -0,0 +1,4 @@
/**
* Provide the function for arthmetic operations required for operating and drawing models, such as matrix and vector calculations.
*/
package com.live2d.sdk.cubism.framework.math;

View File

@@ -0,0 +1,163 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.model;
import com.live2d.sdk.cubism.core.Live2DCubismCore;
import com.live2d.sdk.cubism.framework.utils.CubismDebug;
import java.text.ParseException;
/**
* Moc data manager class
*/
public class CubismMoc {
/**
* バッファからMocファイルを読み取り、Mocデータを作成する。
* NOTE: デフォルトではMOC3の整合性をチェックしない。
*
* @param mocBytes MOC3ファイルのバイト配列バッファ
* @return MOC3ファイルのインスタンス
*/
public static CubismMoc create(byte[] mocBytes) {
return create(mocBytes, false);
}
/**
* バッファからMocファイルを読み取り、Mocデータを作成する。
*
* @param mocBytes MOC3ファイルのバイト配列バッファ
* @param shouldCheckMocConsistency MOC3の整合性をチェックするか。trueならチェックする。
* @return MOC3ファイルのインスタンス
*/
public static CubismMoc create(byte[] mocBytes, boolean shouldCheckMocConsistency) {
com.live2d.sdk.cubism.core.CubismMoc moc;
if (mocBytes == null) {
return null;
}
if (shouldCheckMocConsistency) {
// .moc3の整合性を確認する。
boolean consistency = hasMocConsistency(mocBytes);
if (!consistency) {
CubismDebug.cubismLogError("Inconsistent MOC3.");
return null;
}
}
try {
moc = com.live2d.sdk.cubism.core.CubismMoc.instantiate(mocBytes);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
CubismMoc cubismMoc = new CubismMoc(moc);
cubismMoc.mocVersion = Live2DCubismCore.getMocVersion(mocBytes);
return cubismMoc;
}
/**
* Return the latest .moc3 Version.
*
* @return the latest .moc3 Version
*/
public static int getLatestMocVersion() {
return Live2DCubismCore.getLatestMocVersion();
}
/**
* .moc3ファイルがロードされたメモリを参照し、フォーマットが正しいかチェックする。不正なファイルかどうかのチェック
* Native CoreのcsmHasMocConsistencyに対応する。
*
* @param mocBytes .moc3が読まれたデータ配列
*
* @return .moc3が有効なデータであるかどうか。有効なデータならtrue
*/
public static boolean hasMocConsistency(byte[] mocBytes) {
return Live2DCubismCore.hasMocConsistency(mocBytes);
}
/**
* Create a model.
*
* @return the model created from Moc data
*/
public CubismModel createModel() {
com.live2d.sdk.cubism.core.CubismModel model;
try {
model = moc.instantiateModel();
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
} catch (IllegalStateException e) {
e.printStackTrace();
return null;
}
CubismModel cubismModel = new CubismModel(model);
cubismModel.initialize();
modelCount++;
return cubismModel;
}
/**
* Close the Moc instance.
*/
public void delete() {
assert (modelCount == 0);
if (moc != null) {
moc.close();
}
moc = null;
}
/**
* Delete the model given in the argument.
*
* @param model model instance
*/
public void deleteModel(CubismModel model) {
model.close();
modelCount--;
}
/**
* Return the .moc3 Version of the loaded model.
*
* @return the .moc3 Version of the loaded model
*/
public int getMocVersion() {
return mocVersion;
}
/**
* private constructor
*/
private CubismMoc(com.live2d.sdk.cubism.core.CubismMoc moc) {
this.moc = moc;
}
/**
* Moc data
*/
private com.live2d.sdk.cubism.core.CubismMoc moc;
/**
* Number of models created by the Moc data
*/
private int modelCount;
/**
* .moc3 version of the loaded model
*/
private int mocVersion;
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.model;
import com.live2d.sdk.cubism.framework.CubismFramework;
import com.live2d.sdk.cubism.framework.id.CubismId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This class is a manager of user data. It can load, manage user data.
*/
public class CubismModelUserData {
/**
* Class for recording the user data read from JSON.
*/
public static class CubismModelUserDataNode {
/**
* Constructor
*
* @param targetType user data target type
* @param targetId ID of user data target
* @param value user data
*/
public CubismModelUserDataNode(
CubismId targetType,
CubismId targetId,
String value
) {
if (value == null) {
throw new IllegalArgumentException("value is null.");
}
this.targetType = CubismFramework.getIdManager().getId(targetType);
this.targetId = CubismFramework.getIdManager().getId(targetId);
this.value = value;
}
/**
* User data target type
*/
public final CubismId targetType;
/**
* User data target ID
*/
public final CubismId targetId;
/**
* User data
*/
public final String value;
}
/**
* Create an instance.
*
* @param buffer a buffer where userdata3.json is loaded.
* @return the created instance. If parsing JSON data failed, return null.
*/
public static CubismModelUserData create(byte[] buffer) {
CubismModelUserData modelUserData = new CubismModelUserData();
boolean isSuccessful = modelUserData.parseUserData(buffer);
if (isSuccessful) {
return modelUserData;
}
return null;
}
/**
* Get the user data list of ArtMesh.
*
* @return the user data list
*/
public List<CubismModelUserDataNode> getArtMeshUserData() {
if (areArtMeshUserDataNodesChanged) {
cachedImmutableArtMeshUserDataNodes = Collections.unmodifiableList(artMeshUserDataNodes);
areArtMeshUserDataNodesChanged = false;
}
return cachedImmutableArtMeshUserDataNodes;
}
/**
* Get the user data of ArtMesh.
*
* @param index index of data to be obtained
* @return CubismModelUserDataNode instance
*/
public CubismModelUserDataNode getArtMeshUserData(int index) {
return artMeshUserDataNodes.get(index);
}
/**
* ID name "ArtMesh"
*/
private static final String ART_MESH = "ArtMesh";
/**
* Parse a userdata3.json data.
*
* @param buffer a buffer where userdata3.json is loaded.
* @return If parsing userdata3.json is successful, return true.
*/
private boolean parseUserData(byte[] buffer) {
CubismModelUserDataJson userdata3Json;
userdata3Json = new CubismModelUserDataJson(buffer);
final CubismId artMeshType = CubismFramework.getIdManager().getId(ART_MESH);
final int nodeCount = userdata3Json.getUserDataCount();
for (int i = 0; i < nodeCount; i++) {
final CubismId targetType = CubismFramework.getIdManager().getId(userdata3Json.getUserDataTargetType(i));
final CubismId targetId = userdata3Json.getUserDataId(i);
final String value = userdata3Json.getUserDataValue(i);
final CubismModelUserDataNode addedNode = new CubismModelUserDataNode(
targetType,
targetId,
value
);
userDataNodes.add(addedNode);
if (addedNode.targetType.equals(artMeshType)) {
artMeshUserDataNodes.add(addedNode);
}
}
return true;
}
/**
* the list which has a user data struct class
*/
private final List<CubismModelUserDataNode> userDataNodes = new ArrayList<CubismModelUserDataNode>();
/**
* 閲覧リスト保持
*/
private final List<CubismModelUserDataNode> artMeshUserDataNodes = new ArrayList<CubismModelUserDataNode>();
private boolean areArtMeshUserDataNodesChanged = true;
private List<CubismModelUserDataNode> cachedImmutableArtMeshUserDataNodes;
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.model;
import com.live2d.sdk.cubism.framework.CubismFramework;
import com.live2d.sdk.cubism.framework.exception.CubismJsonParseException;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
/**
* This class deals with an userdata3.json data.
*/
class CubismModelUserDataJson {
/**
* Constructor
*
* @param userdata3Json byte data of userdata3.json
* @throws CubismJsonParseException If parsing JSON failed, return CubismJsonException is thrown.
*/
public CubismModelUserDataJson(byte[] userdata3Json) {
final CubismJson json;
json = CubismJson.create(userdata3Json);
this.json = json;
}
/**
* Get the number of user data in userdata3.json.
*
* @return the number of user data
*/
public int getUserDataCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.USER_DATA_COUNT.key).toInt();
}
/**
* Get the total user data string number.
*
* @return the total user data string number
*/
public int getTotalUserDataSize() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.TOTAL_USER_DATA_SIZE.key).toInt();
}
/**
* Get the user data type of specified number.
*
* @param index index
* @return user data type
*/
public String getUserDataTargetType(int index) {
return json.getRoot().get(JsonKey.USER_DATA.key).get(index).get(JsonKey.TARGET.key).getString();
}
/**
* Get the user data target ID of the specified number.
*
* @param index index
* @return a user data target ID
*/
public CubismId getUserDataId(int index) {
return CubismFramework.getIdManager().getId(json.getRoot().get(JsonKey.USER_DATA.key).get(index).get(JsonKey.ID.key).getString());
}
/**
* Get the user data string of the specified number.
*
* @param index index
* @return user data
*/
public String getUserDataValue(int index) {
return json.getRoot().get(JsonKey.USER_DATA.key).get(index).get(JsonKey.VALUE.key).getString();
}
private enum JsonKey {
META("Meta"),
USER_DATA_COUNT("UserDataCount"),
TOTAL_USER_DATA_SIZE("TotalUserDataSize"),
USER_DATA("UserData"),
TARGET("Target"),
ID("Id"),
VALUE("Value");
private final String key;
JsonKey(String key) {
this.key = key;
}
}
/**
* JSON data
*/
private final CubismJson json;
}

View File

@@ -0,0 +1,564 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.model;
import static com.live2d.sdk.cubism.framework.CubismFramework.VERTEX_OFFSET;
import static com.live2d.sdk.cubism.framework.CubismFramework.VERTEX_STEP;
import static com.live2d.sdk.cubism.framework.utils.CubismDebug.cubismLogError;
import static com.live2d.sdk.cubism.framework.utils.CubismDebug.cubismLogInfo;
import com.live2d.sdk.cubism.framework.effect.CubismBreath;
import com.live2d.sdk.cubism.framework.effect.CubismEyeBlink;
import com.live2d.sdk.cubism.framework.effect.CubismPose;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.math.CubismModelMatrix;
import com.live2d.sdk.cubism.framework.math.CubismTargetPoint;
import com.live2d.sdk.cubism.framework.motion.CubismExpressionMotion;
import com.live2d.sdk.cubism.framework.motion.CubismExpressionMotionManager;
import com.live2d.sdk.cubism.framework.motion.CubismMotion;
import com.live2d.sdk.cubism.framework.motion.CubismMotionManager;
import com.live2d.sdk.cubism.framework.motion.CubismMotionQueueManager;
import com.live2d.sdk.cubism.framework.motion.IBeganMotionCallback;
import com.live2d.sdk.cubism.framework.motion.ICubismMotionEventFunction;
import com.live2d.sdk.cubism.framework.motion.IFinishedMotionCallback;
import com.live2d.sdk.cubism.framework.physics.CubismPhysics;
import com.live2d.sdk.cubism.framework.rendering.CubismRenderer;
/**
* This is the base class of the model that the user actually utilizes. The user defined model class inherits this class.
*/
public abstract class CubismUserModel {
/**
* A callback for registering with CubismMotionQueueManager for an event.
* Call the EventFired which is inherited from CubismUserModel.
*
* @param eventValue the string data of the fired event
* @param model an instance inherited with CubismUserModel
*/
public static void cubismDefaultMotionEventCallback(String eventValue, CubismUserModel model) {
if (model != null) {
model.motionEventFired(eventValue);
}
}
/**
* Get the collision detection.
* <p>
* Get whether the Drawable has been hit at the specified position.
*
* @param drawableId Drawable ID which will be verified.
* @param pointX X-position
* @param pointY Y-position
* @return true If it is hit, return true.
*/
public boolean isHit(CubismId drawableId, float pointX, float pointY) {
final int drawIndex = model.getDrawableIndex(drawableId);
// If there are no hit Drawable, return false
if (drawIndex < 0) {
return false;
}
final int count = model.getDrawableVertexCount(drawIndex);
final float[] vertices = model.getDrawableVertices(drawIndex);
float left = vertices[0];
float right = vertices[0];
float top = vertices[1];
float bottom = vertices[1];
for (int i = 1; i < count; ++i) {
float x = vertices[VERTEX_OFFSET + i * VERTEX_STEP];
float y = vertices[VERTEX_OFFSET + i * VERTEX_STEP + 1];
if (x < left) {
// Min x
left = x;
}
if (x > right) {
// Max x
right = x;
}
if (y < top) {
// Min y
top = y;
}
if (y > bottom) {
// Max y
bottom = y;
}
}
final float tx = modelMatrix.invertTransformX(pointX);
final float ty = modelMatrix.invertTransformY(pointY);
return (left <= tx) && (tx <= right) && (top <= ty) && (ty <= bottom);
}
/**
* 生成されたレンダラーを受け取って初期化する。<br>
* このメソッドを使用した場合、クリッピングマスクの描画に使われるバッファのデフォルト枚数は1枚となる。
*
* @note 引数にnullが与えられた場合`NullPointerException`が投げられる。
*
* @param renderer CubismRendererを継承したレンダラークラスのインスタンス
*/
public void setupRenderer(CubismRenderer renderer) {
setupRenderer(renderer,1);
}
/**
* 生成されたレンダラーを受け取って初期化する。<br>
* クリッピングマスクの描画に使うバッファの枚数をデフォルトの1枚より増やしたい場合は、このメソッドを使用する。
*
* @note 第1引数にnullが与えられた場合`NullPointerException`が投げられる。
*
* @param renderer CubismRendererを継承したレンダラークラスのインスタンス
* @param maskBufferCount 生成したいマスクバッファの枚数
*/
public void setupRenderer(CubismRenderer renderer, int maskBufferCount) {
this.renderer = renderer;
// Bind a renderer with a model instance
this.renderer.initialize(model, maskBufferCount);
}
/**
* Do a standard process at firing the event.
* <p>
* This method deals with the case where an Event occurs during the playback process.
* It is basically overrided by inherited class.
* If it is not overrided, output log.
*
* @param eventValue the string data of the fired event
*/
public void motionEventFired(String eventValue) {
cubismLogInfo(eventValue);
}
/**
* Get initializing status.
*
* @return If this class is initialized, return true.
*/
public boolean isInitialized() {
return isInitialized;
}
/**
* Set an initializing setting.
*
* @param isInitialized initializing status
*/
public void isInitialized(boolean isInitialized) {
this.isInitialized = isInitialized;
}
/**
* Get the updating status.
*
* @return If this class is updated, return true.
*/
public boolean isUpdated() {
return isUpdated;
}
/**
* Set an updating status.
*
* @param isUpdated updating status
*/
public void isUpdated(boolean isUpdated) {
this.isUpdated = isUpdated;
}
/**
* Set an information of mouse dragging.
*
* @param x X-position of the cursor being dragging
* @param y Y-position of the cursor being dragging
*/
public void setDragging(float x, float y) {
dragManager.set(x, y);
}
/**
* Set an acceleration information.
*
* @param x Acceleration in X-axis direction
* @param y Acceleration in Y-axis direction
* @param z Acceleration in Z-axis direction
*/
public void setAcceleration(float x, float y, float z) {
accelerationX = x;
accelerationY = y;
accelerationZ = z;
}
/**
* Get the model matrix.
* This method returns a copy of this _modelMatrix.
*
* @return the model matrix
*/
public CubismModelMatrix getModelMatrix() {
return modelMatrix;
}
/**
* Get the opacity.
*
* @return the opacity
*/
public float getOpacity() {
return opacity;
}
/**
* Set an opacity.
*
* @param opacity an opacity
*/
public void setOpacity(float opacity) {
this.opacity = opacity;
}
/*
* Get the model.
*
* @return the model
*/
public CubismModel getModel() {
return model;
}
/**
* Get the renderer. This method is the generics. The user have to give the renderer type that they would like to use.
* <p>
* (example of use) this{@literal .<CubismRendererAndroid>getRenderer();}
* </p>
*
* @param <T> renderer type to use
* @return renderer instance
*/
public <T extends CubismRenderer> T getRenderer() {
return (T) renderer;
}
/**
* Constructor
*/
protected CubismUserModel() {
// Because this class inherits MotionQueueManager, the usage is the same.
motionManager.setEventCallback(cubismDefaultMotionEventCallback, this);
}
/**
* モデルデータを読み込む。
* NOTE: デフォルトではMOC3の整合性をチェックしない。
*
* @param buffer MOC3ファイルが読み込まれているバイト配列バッファ
*/
protected void loadModel(final byte[] buffer) {
loadModel(buffer, false);
}
/**
* モデルデータを読み込む。
*
* @param buffer MOC3ファイルが読み込まれているバイト配列バッファ
*/
protected void loadModel(final byte[] buffer, boolean shouldCheckMocConsistency) {
final CubismMoc moc = CubismMoc.create(buffer, shouldCheckMocConsistency);
if (moc == null) {
cubismLogError("Failed to create CubismMoc instance.");
return;
}
this.moc = moc;
final CubismModel model = this.moc.createModel();
if (model == null) {
cubismLogError("Failed to create the model.");
return;
}
this.model = model;
this.model.saveParameters();
modelMatrix = CubismModelMatrix.create(this.model.getCanvasWidth(), this.model.getCanvasHeight());
}
/**
* Delete Moc and Model instances.
*/
protected void delete() {
if (moc == null || model == null) {
return;
}
moc.deleteModel(model);
moc.delete();
model.close();
renderer.close();
moc = null;
model = null;
renderer = null;
}
/**
* Load a motion data.
*
* @param buffer a buffer where motion3.json file is loaded.
* @param onFinishedMotionHandler the callback method called at finishing motion play. If it is null, callbacking methods is not conducting.
* @param onBeganMotionHandler the callback method called at beginning motion play. If it is null, callbacking methods is not conducting.
* @param shouldCheckMotionConsistency flag to validate the consistency of motion3.json.
* @return motion class
*/
protected CubismMotion loadMotion(
byte[] buffer,
IFinishedMotionCallback onFinishedMotionHandler,
IBeganMotionCallback onBeganMotionHandler,
boolean shouldCheckMotionConsistency
) {
try {
return CubismMotion.create(buffer, onFinishedMotionHandler, onBeganMotionHandler, shouldCheckMotionConsistency);
} catch (Exception e) {
cubismLogError("Failed to loadMotion(). %s", e.getMessage());
return null;
}
}
/**
* Load a motion data.
* This method does not check the consistency of motion3.json.
* To check the consistency of motion3.json,
* use {@link #loadMotion(byte[], IFinishedMotionCallback, IBeganMotionCallback, boolean)}
* and set the fourth argument to `true`.
*
* @param buffer a buffer where motion3.json file is loaded.
* @param onFinishedMotionHandler the callback method called at finishing motion play. If it is null, callbacking methods is not conducting.
* @param onBeganMotionHandler the callback method called at beginning motion play. If it is null, callbacking methods is not conducting.
* @return motion class
*/
protected CubismMotion loadMotion(
byte[] buffer,
IFinishedMotionCallback onFinishedMotionHandler,
IBeganMotionCallback onBeganMotionHandler
) {
return loadMotion(buffer, onFinishedMotionHandler, onBeganMotionHandler, false);
}
/**
* Load a motion data.
* This method does not set any callback functions.
*
* @param buffer a buffer where motion3.json file is loaded.
* @param shouldCheckMotionConsistency flag to validate the consistency of motion3.json.
* @return motion class
*/
protected CubismMotion loadMotion(byte[] buffer, boolean shouldCheckMotionConsistency) {
return loadMotion(buffer, null, null, shouldCheckMotionConsistency);
}
/**
* Load a motion data.
* This method does not check the consistency of motion3.json.
* To check the consistency of motion3.json,
* use {@link #loadMotion(byte[], boolean)}
* and set the second argument to `true`.
* This method does not set any callback functions.
*
* @param buffer a buffer where motion3.json file is loaded.
* @return motion class
*/
protected CubismMotion loadMotion(byte[] buffer) {
return loadMotion(buffer, null, null, false);
}
/**
* Load a expression data.
*
* @param buffer a buffer where exp3.json is loaded
* @return motion class
*/
protected CubismExpressionMotion loadExpression(final byte[] buffer) {
try {
return CubismExpressionMotion.create(buffer);
} catch (Exception e) {
cubismLogError("Failed to loadExpressionMotion(). %s", e.getMessage());
return null;
}
}
/**
* Load pose data.
*
* @param buffer a buffer where pose3.json is loaded.
*/
protected void loadPose(final byte[] buffer) {
try {
pose = CubismPose.create(buffer);
} catch (Exception e) {
cubismLogError("Failed to loadPose(). %s", e.getMessage());
}
}
/**
* Load physics data.
*
* @param buffer a buffer where physics3.json is loaded.
*/
protected void loadPhysics(final byte[] buffer) {
try {
physics = CubismPhysics.create(buffer);
} catch (Exception e) {
cubismLogError("Failed to loadPhysics(). %s", e.getMessage());
}
}
/**
* Load a user data attached the model.
*
* @param buffer a buffer where userdata3.json is loaded.
*/
protected void loadUserData(final byte[] buffer) {
try {
modelUserData = CubismModelUserData.create(buffer);
} catch (Exception e) {
cubismLogError("Failed to loadUserData(). %s", e.getMessage());
}
}
/**
* A Moc data,
*/
protected CubismMoc moc;
/**
* A model instance
*/
protected CubismModel model;
/**
* A motion manager
*/
protected CubismMotionManager motionManager = new CubismMotionManager();
/**
* A expression manager
*/
protected CubismExpressionMotionManager expressionManager = new CubismExpressionMotionManager();
/**
* Auto eye-blink
*/
protected CubismEyeBlink eyeBlink;
/**
* Breathing
*/
protected CubismBreath breath;
/**
* A model matrix
*/
protected CubismModelMatrix modelMatrix;
/**
* m
* Pose manager
*/
protected CubismPose pose;
/**
* A mouse dragging manager
*/
protected CubismTargetPoint dragManager = new CubismTargetPoint();
/**
* physics
*/
protected CubismPhysics physics;
/**
* A user data
*/
protected CubismModelUserData modelUserData;
/**
* An initializing status
*/
protected boolean isInitialized;
/**
* An updating status
*/
protected boolean isUpdated;
/**
* Opacity
*/
protected float opacity = 1.0f;
/**
* A lip-sync status
*/
protected boolean lipSync = true;
/**
* A control value of the last lip-sync
*/
protected float lastLipSyncValue;
/**
* An X-position of mouse dragging
*/
protected float dragX;
/**
* An Y-position of mouse dragging
*/
protected float dragY;
/**
* An acceleration in X-axis direction
*/
protected float accelerationX;
/**
* An acceleration in Y-axis direction
*/
protected float accelerationY;
/**
* An acceleration in Z-axis direction
*/
protected float accelerationZ;
/**
* MOC3の整合性を検証するか。検証するならtrue。
*/
protected boolean mocConsistency;
/**
* motion3.jsonの整合性を検証するか。検証するならtrue。
*/
protected boolean motionConsistency;
/**
* Whether it is debug mode
*/
protected boolean debugMode;
/**
* An entity of CubismMotionEventFunction.
*/
private static final ICubismMotionEventFunction cubismDefaultMotionEventCallback = new ICubismMotionEventFunction() {
@Override
public void apply(
CubismMotionQueueManager caller,
String eventValue,
Object customData
) {
if (customData != null) {
((CubismUserModel) customData).motionEventFired(eventValue);
}
}
};
/**
* A renderer
*/
private CubismRenderer renderer;
}

View File

@@ -0,0 +1,4 @@
/**
* Provide various functions (generate, update, destroy) for handling models.
*/
package com.live2d.sdk.cubism.framework.model;

View File

@@ -0,0 +1,403 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.math.CubismMath;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.utils.CubismDebug;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Abstract base class for motion.
* This class manages motion playback by MotionQueueManager.
*/
public abstract class ACubismMotion {
/**
* Update model's parameters.
*
* @param model target model
* @param motionQueueEntry motion managed by CubismMotionQueueManager
* @param userTimeSeconds total delta time[s]
*/
public void updateParameters(
CubismModel model,
CubismMotionQueueEntry motionQueueEntry,
float userTimeSeconds
) {
if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) {
return;
}
setupMotionQueueEntry(motionQueueEntry, userTimeSeconds);
float fadeWeight = updateFadeWeight(motionQueueEntry, userTimeSeconds);
//---- 全てのパラメータIDをループする ----
doUpdateParameters(model, userTimeSeconds, fadeWeight, motionQueueEntry);
// 後処理
// 終了時刻を過ぎたら終了フラグを立てるCubismMotionQueueManager
if (motionQueueEntry.getEndTime() > 0.0f && motionQueueEntry.getEndTime() < userTimeSeconds) {
motionQueueEntry.isFinished(true); // 終了
}
}
/**
* モーションの再生を開始するためのセットアップを行う。
*
* @param motionQueueEntry CubismMotionQueueManagerによって管理されるモーション
* @param userTimeSeconds 総再生時間(秒)
*/
public void setupMotionQueueEntry(
CubismMotionQueueEntry motionQueueEntry,
final float userTimeSeconds
) {
if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) {
return;
}
if (motionQueueEntry.isStarted()) {
return;
}
motionQueueEntry.isStarted(true);
// Record the start time of the motion.
motionQueueEntry.setStartTime(userTimeSeconds - offsetSeconds);
// Record the start time of fade-in
motionQueueEntry.setFadeInStartTime(userTimeSeconds);
// Deal with the case where the status is set "end" before it has started.
if (motionQueueEntry.getEndTime() < 0) {
adjustEndTime(motionQueueEntry);
}
}
/**
* モーションフェードのウェイト値を更新する。
*
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param userTimeSeconds デルタ時間の積算値[秒]
* @return 更新されたウェイト値
*/
public float updateFadeWeight(CubismMotionQueueEntry motionQueueEntry, float userTimeSeconds) {
if(motionQueueEntry == null) {
CubismDebug.cubismLogError("motionQueueEntry is null.");
}
float fadeWeight = weight; // 現在の値と掛け合わせる割合
// ---- フェードイン・アウトの処理 ----
// 単純なサイン関数でイージングする。
final float fadeIn = fadeInSeconds == 0.0f
? 1.0f
: CubismMath.getEasingSine((userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / fadeInSeconds);
final float fadeOut = (fadeOutSeconds == 0.0f || motionQueueEntry.getEndTime() < 0.0f)
? 1.0f
: CubismMath.getEasingSine((motionQueueEntry.getEndTime() - userTimeSeconds) / fadeOutSeconds);
fadeWeight = fadeWeight * fadeIn * fadeOut;
motionQueueEntry.setState(userTimeSeconds, fadeWeight);
assert (0.0f <= fadeWeight && fadeWeight <= 1.0f);
return fadeWeight;
}
/**
* Set the time it takes to fade in.
*
* @param fadeInSeconds time for fade in [s]
*/
public void setFadeInTime(float fadeInSeconds) {
this.fadeInSeconds = fadeInSeconds;
}
/**
* Get the time it takes to fade in.
*
* @return time for fade in[s]
*/
public float getFadeInTime() {
return fadeInSeconds;
}
/**
* Set a time it takes to fade out.
*
* @param fadeOutSeconds time for fade out[s]
*/
public void setFadeOutTime(float fadeOutSeconds) {
this.fadeOutSeconds = fadeOutSeconds;
}
/**
* Get the time it takes to fade out.
*
* @return time for fade out[s]
*/
public float getFadeOutTime() {
return fadeOutSeconds;
}
/**
* Set the weight to be applied to the motion.
*
* @param weight weight(0.0 - 1.0)
*/
public void setWeight(float weight) {
this.weight = weight;
}
/**
* Get the weight to be applied to the motion.
*
* @return weight(0.0 - 1.0)
*/
public float getWeight() {
return weight;
}
/**
* Get the duration of the motion.
*
* @return duration of motion[s]
* (If it is a loop, "-1".
* Override if it is not a loop.
* If the value is positive, the process ends at the time it is retrieved.
* When the value is "-1", the process will not end unless there is a stop command from outside)
*/
public float getDuration() {
return -1.0f;
}
/**
* Get the duration of one motion loop.
*
* @return duration of one motion loop[s]
* <p>
* (If it does not loop, it returns the same value as GetDuration().
* Return "-1" in case the duration of one loop cannot be defined (e.g., a subclass that keeps moving programmatically)).
*/
public float getLoopDuration() {
return -1.0f;
}
/**
* Set the start time for motion playback.
*
* @param offsetSeconds start time for motion playback[s]
*/
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.
*
* @param beforeCheckTimeSeconds last event check time [s]
* @param motionTimeSeconds playback time this time [s]
* @return list of events that have fired
*/
public List<String> getFiredEvent(float beforeCheckTimeSeconds, float motionTimeSeconds) {
return Collections.unmodifiableList(firedEventValues);
}
/**
* Registers a motion playback start callback.
* It is not called in the following states:
* 1. when the currently playing motion is set as "loop"
* 2. when null is registered in the callback
*
* @param onBeganMotionHandler start-of-motion playback callback function
*/
public void setBeganMotionHandler(IBeganMotionCallback onBeganMotionHandler) {
onBeganMotion = onBeganMotionHandler;
}
/**
* Get the start-of-motion playback callback function.
*
* @return registered start-of-motion playback callback function; if null, no function is registered
*/
public IBeganMotionCallback getBeganMotionCallback() {
return onBeganMotion;
}
/**
* Registers a motion playback end callback.
* It is called when the isFinished flag is set.
* It is not called in the following states:
* 1. when the currently playing motion is set as "loop"
* 2. when null is registered in the callback
*
* @param onFinishedMotionHandler end-of-motion playback callback function
*/
public void setFinishedMotionHandler(IFinishedMotionCallback onFinishedMotionHandler) {
onFinishedMotion = onFinishedMotionHandler;
}
/**
* Get the end-of-motion playback callback function.
*
* @return registered end-of-motion playback callback function; if null, no function is registered
*/
public IFinishedMotionCallback getFinishedMotionCallback() {
return onFinishedMotion;
}
/**
* Check to see if a transparency curve exists.
*
* @return return true if the key exists
*/
public boolean isExistModelOpacity() {
return false;
}
/**
* Return the index of the transparency curve.
*
* @return success: index of the transparency curve
*/
public int getModelOpacityIndex() {
return -1;
}
/**
* Return the ID of the transparency curve.
*
* @return success: atransparency curve
*/
public CubismId getModelOpacityId(int index) {
return null;
}
/**
* Perform parameter updates for the model.
*
* @param model target model
* @param userTimeSeconds total delta time[s]
* @param weight weight of motion
* @param motionQueueEntry motion managed by CubismMotionQueueManager
*/
protected abstract void doUpdateParameters(
CubismModel model,
float userTimeSeconds,
float weight,
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()` の後に呼び出す。
*
* @return success : モーションの当該時間におけるOpacityの値
*/
protected float getModelOpacityValue() {
return 1.0f;
}
/**
* Time for fade-in [s]
*/
protected float fadeInSeconds = -1.0f;
/**
* Time for fade-out[s]
*/
protected float fadeOutSeconds = -1.0f;
/**
* Weight of motion
*/
protected float weight = 1.0f;
/**
* 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
*/
protected List<String> firedEventValues = new ArrayList<String>();
/**
* Start-of-motion playback callback function
*/
protected IBeganMotionCallback onBeganMotion;
/**
* End-of-motion playback callback function
*/
protected IFinishedMotionCallback onFinishedMotion;
}

View File

@@ -0,0 +1,354 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
import com.live2d.sdk.cubism.framework.CubismFramework;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.utils.jsonparser.ACubismJsonValue;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
import java.util.ArrayList;
import java.util.List;
/**
* A motion class for facial expressions.
*/
public class CubismExpressionMotion extends ACubismMotion {
/**
* Calculation method of facial expression parameter values.
*/
public enum ExpressionBlendType {
/**
* Addition
*/
ADD("Add"),
/**
* Multiplication
*/
MULTIPLY("Multiply"),
/**
* Overwriting
*/
OVERWRITE("Overwrite");
private final String type;
ExpressionBlendType(String type) {
this.type = type;
}
}
/**
* Internal class for expression parameter information.
*/
public static class ExpressionParameter {
public ExpressionParameter(CubismId id, ExpressionBlendType method, float value) {
if (id == null || method == null) {
throw new IllegalArgumentException("id or method is null.");
}
this.parameterId = id;
this.blendType = method;
this.value = value;
}
/**
* Parameter ID
*/
public final CubismId parameterId;
/**
* Type of parameter calculation
*/
public final ExpressionBlendType blendType;
/**
* Value
*/
public final float value;
}
/**
* Default fade duration.
*/
public static final float DEFAULT_FADE_TIME = 1.0f;
/**
* 加算適用の初期値
*/
public static final float DEFAULT_ADDITIVE_VALUE = 0.0f;
/**
* 乗算適用の初期値
*/
public static final float DEFAULT_MULTIPLY_VALUE = 1.0f;
/**
* Create an ACubismMotion instance.
*
* @param buffer buffer where exp3.json file is loaded
* @return created instance
*/
public static CubismExpressionMotion create(byte[] buffer) {
CubismExpressionMotion expression = new CubismExpressionMotion();
expression.parse(buffer);
return expression;
}
/**
* モデルの表情に関するパラメータを計算する。
*
* @param model 対象のモデル
* @param userTimeSeconds デルタ時間の積算値[秒]
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param expressionParameterValues モデルに適用する各パラメータの値
* @param expressionIndex 表情のインデックス
* @param fadeWeight 表情のウェイト
*/
public void calculateExpressionParameters(
CubismModel model,
float userTimeSeconds,
CubismMotionQueueEntry motionQueueEntry,
List<CubismExpressionMotionManager.ExpressionParameterValue> expressionParameterValues,
int expressionIndex,
float fadeWeight
) {
if(motionQueueEntry == null || expressionParameterValues == null) {
return;
}
if (!motionQueueEntry.isAvailable()) {
return;
}
// CubismExpressionMotion.fadeWeight は廃止予定です。
// 互換性のために処理は残りますが、実際には使用しておりません。
this.fadeWeight = updateFadeWeight(motionQueueEntry, userTimeSeconds);
// モデルに適用する値を計算
for (int i = 0; i < expressionParameterValues.size(); i++) {
CubismExpressionMotionManager.ExpressionParameterValue expParamValue = expressionParameterValues.get(i);
if (expParamValue.parameterId == null) {
continue;
}
final float currentParameterValue = expParamValue.overwriteValue = model.getParameterValue(expParamValue.parameterId);
List<ExpressionParameter> expressionParameters = getExpressionParameters();
int parameterIndex = -1;
for (int j = 0; j < expressionParameters.size(); j++) {
if (expParamValue.parameterId != expressionParameters.get(j).parameterId) {
continue;
}
parameterIndex = j;
break;
}
// 再生中のExpressionが参照していないパラメータは初期値を適用
if (parameterIndex < 0) {
if (expressionIndex == 0) {
expParamValue.additiveValue = DEFAULT_ADDITIVE_VALUE;
expParamValue.multiplyValue = DEFAULT_MULTIPLY_VALUE;
expParamValue.overwriteValue = currentParameterValue;
} else {
expParamValue.additiveValue = calculateValue(expParamValue.additiveValue, DEFAULT_ADDITIVE_VALUE, fadeWeight);
expParamValue.multiplyValue = calculateValue(expParamValue.multiplyValue, DEFAULT_MULTIPLY_VALUE, fadeWeight);
expParamValue.overwriteValue = calculateValue(expParamValue.overwriteValue, currentParameterValue, fadeWeight);
}
continue;
}
// 値を計算
float value = expressionParameters.get(parameterIndex).value;
float newAdditiveValue, newMultiplyValue, newOverwriteValue;
switch (expressionParameters.get(parameterIndex).blendType) {
case ADD:
newAdditiveValue = value;
newMultiplyValue = DEFAULT_MULTIPLY_VALUE;
newOverwriteValue = currentParameterValue;
break;
case MULTIPLY:
newAdditiveValue = DEFAULT_ADDITIVE_VALUE;
newMultiplyValue = value;
newOverwriteValue = currentParameterValue;
break;
case OVERWRITE:
newAdditiveValue = DEFAULT_ADDITIVE_VALUE;
newMultiplyValue = DEFAULT_MULTIPLY_VALUE;
newOverwriteValue = value;
break;
default:
return;
}
if (expressionIndex == 0) {
expParamValue.additiveValue = newAdditiveValue;
expParamValue.multiplyValue = newMultiplyValue;
expParamValue.overwriteValue = newOverwriteValue;
} else {
expParamValue.additiveValue = (expParamValue.additiveValue * (1.0f - fadeWeight)) + newAdditiveValue * fadeWeight;
expParamValue.multiplyValue = (expParamValue.multiplyValue * (1.0f - fadeWeight)) + newMultiplyValue * fadeWeight;
expParamValue.overwriteValue = (expParamValue.overwriteValue * (1.0f - fadeWeight)) + newOverwriteValue * fadeWeight;
}
}
}
/**
* 表情が参照しているパラメータを取得する。
*
* @return 表情が参照しているパラメータ
*/
public List<ExpressionParameter> getExpressionParameters() {
return parameters;
}
/**
* 現在の表情のフェードのウェイト値を取得する。
*
* @return 表情のフェードのウェイト値
*
* @deprecated CubismExpressionMotion.fadeWeightが削除予定のため非推奨。
* CubismExpressionMotionManager.getFadeWeight(int index) を使用してください。
* @see CubismExpressionMotionManager#getFadeWeight(int index)
*/
@Deprecated
public float getFadeWeight() {
return fadeWeight;
}
/**
* デフォルトコンストラクタ
*/
protected CubismExpressionMotion() {}
@Override
protected void doUpdateParameters(
final CubismModel model,
final float userTimeSeconds,
final float weight,
final CubismMotionQueueEntry motionQueueEntry
) {
for (int i = 0; i < parameters.size(); i++) {
ExpressionParameter parameter = parameters.get(i);
switch (parameter.blendType) {
// Relative change: Addition
case ADD:
model.addParameterValue(parameter.parameterId, parameter.value, weight);
break;
// Relative change: Multiplication
case MULTIPLY:
model.multiplyParameterValue(parameter.parameterId, parameter.value, weight);
break;
// Relatice change: Overwriting
case OVERWRITE:
model.setParameterValue(parameter.parameterId, parameter.value, weight);
break;
default:
// When you set a value that is not in the specification, it is already in the addition mode.
break;
}
}
}
/**
* exp3.jsonをパースする。
*
* @param exp3Json exp3.jsonが読み込まれているbyte配列
*/
protected void parse(byte[] exp3Json) {
CubismJson json = CubismJson.create(exp3Json);
setFadeInTime(json.getRoot().get(ExpressionKey.FADE_IN.key).toFloat(DEFAULT_FADE_TIME));
setFadeOutTime(json.getRoot().get(ExpressionKey.FADE_OUT.key).toFloat(DEFAULT_FADE_TIME));
ACubismJsonValue jsonParameters = json.getRoot().get(ExpressionKey.PARAMETERS.key);
// Each parameter setting
for (int i = 0; i < jsonParameters.size(); i++) {
final ACubismJsonValue param = jsonParameters.get(i);
// Parameter ID
final CubismId parameterId = CubismFramework.getIdManager().getId(param.get(ExpressionKey.ID.key).getString());
// Setting of calculation method.
final ExpressionBlendType blendType = getBlendMethod(param);
// Value
final float value = param.get(ExpressionKey.VALUE.key).toFloat();
// Create a configuration object and add it to the list.
ExpressionParameter item = new ExpressionParameter(parameterId, blendType, value);
this.parameters.add(item);
}
}
/**
* Get the calculation method for the parameter values of expressions set in JSON.
*
* @param parameter JSON parameter value
* @return calculation method set in JSON
*/
private static ExpressionBlendType getBlendMethod(ACubismJsonValue parameter) {
final String method = parameter.get(ExpressionKey.BLEND.key).getString();
if (method.equals(ExpressionBlendType.ADD.type)) {
return ExpressionBlendType.ADD;
} else if (method.equals(ExpressionBlendType.MULTIPLY.type)) {
return ExpressionBlendType.MULTIPLY;
} else if (method.equals(ExpressionBlendType.OVERWRITE.type)) {
return ExpressionBlendType.OVERWRITE;
}
// If the value that is not in the specifications is set, it can be recovered by setting addition mode.
else {
return ExpressionBlendType.ADD;
}
}
/**
* Key of exp3.json.
*/
private enum ExpressionKey {
FADE_IN("FadeInTime"),
FADE_OUT("FadeOutTime"),
PARAMETERS("Parameters"),
ID("Id"),
VALUE("Value"),
BLEND("Blend");
private final String key;
ExpressionKey(String key) {
this.key = key;
}
}
/**
* 入力された値でブレンド計算をする。
*
* @param source 現在の値
* @param destination 適用する値
*
* @return 計算されたブレンド値
*/
private float calculateValue(float source, float destination, float fadeWeight) {
return (source * (1.0f - fadeWeight)) + (destination * fadeWeight);
}
/**
* Parameter information list for facial expressions
*/
private final List<ExpressionParameter> parameters = new ArrayList<>();
/**
* 表情の現在のウェイト
*
* @deprecated 不具合を引き起こす要因となるため非推奨。
*/
@Deprecated
private float fadeWeight;
}

View File

@@ -0,0 +1,275 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
* <p>
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.math.CubismMath;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.utils.CubismDebug;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class CubismExpressionMotionManager extends CubismMotionQueueManager {
public static class ExpressionParameterValue {
/**
* パラメータID
*/
public CubismId parameterId;
/**
* 加算値
*/
public float additiveValue;
/**
* 乗算値
*/
public float multiplyValue;
/**
* 上書き値
*/
public float overwriteValue;
}
/**
* 再生中の表情モーションの優先度を取得する。
*
* @return 表情モーションの優先度
*/
public int getCurrentPriority() {
return currentPriority;
}
/**
* 予約中の表情モーションの優先度を取得する。
*
* @return 表情モーションの優先度
*/
public int getReservePriority() {
return reservePriority;
}
/**
* 現在の表情のフェードのウェイト値を取得する。
*
* @param index 取得する表情モーションのインデックス
*
* @return 表情のフェードのウェイト値
*
* @throws IllegalArgumentException if an argument is an invalid value.
*/
public float getFadeWeight(int index) {
if(fadeWeights.isEmpty()) {
throw new IllegalArgumentException("No motion during playback.");
}
if(fadeWeights.size() <= index || index < 0) {
throw new IllegalArgumentException("The index is an invalid value.");
}
return fadeWeights.get(index);
}
/**
* 予約中の表情モーションの優先度を設定する。
*
* @param priority 設定する表情モーションの優先度
*/
public void setReservePriority(int priority) {
this.reservePriority = priority;
}
/**
* 優先度を設定して表情モーションを開始する。
*
* @param motion 開始する表情モーション
* @param priority 優先度
* @return 開始した表情モーションの識別番号。個別のモーションが終了したか否かを判断するisFinished()の引数で使用する。開始できないときは「-1」を返します。
*/
public int startMotionPriority(ACubismMotion motion, int priority) {
if (priority == reservePriority) {
reservePriority = 0; // 予約を解除
}
currentPriority = priority; // 再生中モーションの優先度を設定
return startMotion(motion);
}
/**
* 表情モーションを更新して、モデルにパラメータ値を反映する。
*
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
* @return 表情モーションが更新されたかどうか。更新されたならtrue。
*/
public boolean updateMotion(CubismModel model, float deltaTimeSeconds) {
userTimeSeconds += deltaTimeSeconds;
boolean isUpdated = false;
List<CubismMotionQueueEntry> motions = getCubismMotionQueueEntries();
float expressionWeight = 0.0f;
int expressionIndex = 0;
// motionQueueEntryの中にあるmotionインスタンスがnullの場合、motionQueueEntryインスタンス自体をnullにする
// for文でnullを順次削除する方式だと例外を出してしまうため。
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry entry = motions.get(i);
CubismExpressionMotion expressionMotion = (CubismExpressionMotion) entry.getCubismMotion();
if (expressionMotion == null) {
motions.set(i, null);
}
}
// 予めnull要素を全て削除
motions.removeAll(nullSet);
while (fadeWeights.size() < motions.size()) {
fadeWeights.add(0.0f);
}
// ------ 処理を行う ------
// 既に表情モーションがあれば終了フラグを立てる
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry motionQueueEntry = motions.get(i);
CubismExpressionMotion expressionMotion = (CubismExpressionMotion) motionQueueEntry.getCubismMotion();
List<CubismExpressionMotion.ExpressionParameter> expressionParameters = expressionMotion.getExpressionParameters();
if (motionQueueEntry.isAvailable()) {
// 再生中のExpressionが参照しているパラメータをすべてリストアップ
for (int paramIndex = 0; paramIndex < expressionParameters.size(); paramIndex++) {
if (expressionParameters.get(paramIndex).parameterId == null) {
continue;
}
int index = -1;
// リストにパラメータIDが存在するか検索
for (int j = 0; j < expressionParameterValues.size(); j++) {
if (expressionParameterValues.get(j).parameterId != expressionParameters.get(paramIndex).parameterId) {
continue;
}
index = j;
break;
}
if (index >= 0) {
continue;
}
// パラメータがリストに存在しないなら新規追加
ExpressionParameterValue item = new ExpressionParameterValue();
item.parameterId = expressionParameters.get(paramIndex).parameterId;
item.additiveValue = CubismExpressionMotion.DEFAULT_ADDITIVE_VALUE;
item.multiplyValue = CubismExpressionMotion.DEFAULT_MULTIPLY_VALUE;
item.overwriteValue = model.getParameterValue(item.parameterId);
expressionParameterValues.add(item);
}
}
// ------ 値を計算する ------
expressionMotion.setupMotionQueueEntry(motionQueueEntry, userTimeSeconds);
setFadeWeight(expressionIndex, expressionMotion.updateFadeWeight(motionQueueEntry, userTimeSeconds));
expressionMotion.calculateExpressionParameters(
model,
userTimeSeconds,
motionQueueEntry,
expressionParameterValues,
expressionIndex,
getFadeWeight(expressionIndex)
);
final float easingSine = expressionMotion.getFadeInTime() == 0.0f
? 1.0f
: CubismMath.getEasingSine((userTimeSeconds - motionQueueEntry.getFadeInStartTime()) / expressionMotion.getFadeInTime());
expressionWeight += easingSine;
isUpdated = true;
if (motionQueueEntry.isTriggeredFadeOut()) {
// フェードアウト開始
motionQueueEntry.startFadeOut(motionQueueEntry.getFadeOutSeconds(), userTimeSeconds);
}
expressionIndex++;
}
// ------ 最新のExpressionのフェードが完了していればそれ以前を削除する ------
if (motions.size() > 1) {
float latestFadeWeight = getFadeWeight(fadeWeights.size() - 1);
if (latestFadeWeight >= 1.0f) {
// 配列の最後の要素は削除しない
for (int i = motions.size() - 2; i >= 0; i--) {
// forでremoveすることはできない。nullをセットしておいて後で削除する。
motions.set(i, null);
fadeWeights.remove(i);
}
motions.removeAll(nullSet);
}
}
if (expressionWeight > 1.0f) {
expressionWeight = 1.0f;
}
// モデルに各値を適用
for (int i = 0; i < expressionParameterValues.size(); i++) {
ExpressionParameterValue v = expressionParameterValues.get(i);
model.setParameterValue(
v.parameterId,
(v.overwriteValue + v.additiveValue) * v.multiplyValue,
expressionWeight);
v.additiveValue = CubismExpressionMotion.DEFAULT_ADDITIVE_VALUE;
v.multiplyValue = CubismExpressionMotion.DEFAULT_MULTIPLY_VALUE;
}
return isUpdated;
}
/**
* Set the weight of expression fade.
*
* @param index index of the expression motion to be set
* @param expressionFadeWeight weight value of expression fade
*/
private void setFadeWeight(int index, float expressionFadeWeight) {
if (index < 0 || fadeWeights.isEmpty() || fadeWeights.size() <= index) {
CubismDebug.cubismLogWarning("Failed to set the fade weight value. The element at that index does not exist.");
return;
}
fadeWeights.set(index, expressionFadeWeight);
}
// nullが格納されたSet。null要素だけListから排除する際に使用される。
private static final Set<Object> nullSet = Collections.singleton(null);
/**
* モデルに適用する各パラメータの値
*/
private final List<ExpressionParameterValue> expressionParameterValues = new ArrayList<>();
/**
* 現在再生中の表情モーションの優先度
*/
private int currentPriority;
/**
* 再生予定の表情モーションの優先度。再生中は0になる。
* 表情モーションファイルを別スレッドで読み込むときの機能。
*/
private int reservePriority;
/**
* 再生中の表情モーションのウェイトのリスト
*/
private final List<Float> fadeWeights = new ArrayList<>();
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
import com.live2d.sdk.cubism.framework.id.CubismId;
import java.util.ArrayList;
import java.util.List;
/**
* Internal data used by the CubismMotion class.
*/
class CubismMotionInternal {
/**
* Type of motion curve.
*/
public enum CubismMotionCurveTarget {
/**
* motion curve for the model
*/
MODEL,
/**
* motion curve for parameters
*/
PARAMETER,
/**
* motion curve for part opacity
*/
PART_OPACITY
}
/**
* Type of motion curve segment.
*/
public enum CubismMotionSegmentType {
/**
* linear
*/
LINEAR,
/**
* bezier curve
*/
BEZIER,
/**
* step
*/
STEPPED,
/**
* inverse step
*/
INVERSESTEPPED
}
/**
* Motion curve control points.
*/
public static class CubismMotionPoint {
/**
* time[s]
*/
public float time;
/**
* value
*/
public float value;
public CubismMotionPoint() {}
public CubismMotionPoint(final float time, final float value) {
this.time = time;
this.value = value;
}
}
/**
* Segment of motion curve.
*/
public static class CubismMotionSegment {
/**
* used evaluation function
*/
public CsmMotionSegmentEvaluationFunction evaluator;
/**
* index to the first segment
*/
public int basePointIndex;
/**
* type of segment
*/
public CubismMotionSegmentType segmentType = CubismMotionSegmentType.LINEAR;
}
/**
* Motion curve
*/
public static class CubismMotionCurve {
/**
* type of curve
*/
public CubismMotionCurveTarget type = CubismMotionCurveTarget.MODEL;
/**
* curve ID
*/
public CubismId id;
/**
* number of segments
*/
public int segmentCount;
/**
* index to the first segment
*/
public int baseSegmentIndex;
/**
* time for fade-in[s]
*/
public float fadeInTime;
/**
* time for fade-out[s]
*/
public float fadeOutTime;
}
/**
* Motion event
*/
public static class CubismMotionEvent {
/**
* duration of event
*/
public float fireTime;
/**
* value
*/
public String value;
}
/**
* Motion data
*/
public static class CubismMotionData {
/**
* motion duration
*/
public float duration;
/**
* Whether the motion loops
*/
public boolean isLooped;
/**
* number of curves
*/
public int curveCount;
/**
* number of UserData
*/
public int eventCount;
/**
* framerate per second
*/
public float fps;
/**
* list of curves
*/
public List<CubismMotionCurve> curves = new ArrayList<CubismMotionCurve>();
/**
* list of segments
*/
public List<CubismMotionSegment> segments = new ArrayList<CubismMotionSegment>();
/**
* list of points
*/
public List<CubismMotionPoint> points = new ArrayList<CubismMotionPoint>();
/**
* list of events
*/
public List<CubismMotionEvent> events = new ArrayList<CubismMotionEvent>();
}
/**
* For strategy pattern.
*/
public interface CsmMotionSegmentEvaluationFunction {
float evaluate(final List<CubismMotionPoint> points, final float time);
}
}

View File

@@ -0,0 +1,382 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
import com.live2d.sdk.cubism.framework.CubismFramework;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.motion.CubismMotionInternal.CubismMotionSegmentType;
import com.live2d.sdk.cubism.framework.utils.CubismDebug;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
/**
* Container for motion3.json.
*/
public class CubismMotionJson {
/**
* Flag type of Bezier curve interpretation method
*/
public enum EvaluationOptionFlag {
/**
* Regulatory status of Bezier handle.
*/
ARE_BEZIERS_RESTRICTED
}
public CubismMotionJson(byte[] buffer) {
CubismJson json;
json = CubismJson.create(buffer);
this.json = json;
}
/**
* Get the duration of the motion.
*
* @return motion duration[s]
*/
public float getMotionDuration() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.DURATION.key).toFloat();
}
/**
* Whether the motion loops.
*
* @return If the motion loops, return true.
*/
public boolean isMotionLoop() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.LOOP.key).toBoolean();
}
/**
* Returns the consistency of the motion3.json file.
*
* @return true if the file is consistent; otherwise returns false.
*/
public boolean hasConsistency() {
boolean result = true;
final int actualCurveListSize = json.getRoot().get(JsonKey.CURVES.key).getList().size();
int actualTotalSegmentCount = 0;
int actualTotalPointCount = 0;
// Counting.
for (int curvePosition = 0; curvePosition < actualCurveListSize; ++curvePosition) {
for (int segmentPosition = 0; segmentPosition < getMotionCurveSegmentCount(curvePosition);) {
if (segmentPosition == 0) {
actualTotalPointCount += 1;
segmentPosition += 2;
}
final CubismMotionSegmentType segment = getMotionCurveSegmentType(curvePosition, segmentPosition);
switch (segment) {
case LINEAR:
actualTotalPointCount += 1;
segmentPosition += 3;
break;
case BEZIER:
actualTotalPointCount += 3;
segmentPosition += 7;
break;
case STEPPED:
actualTotalPointCount += 1;
segmentPosition += 3;
break;
case INVERSESTEPPED:
actualTotalPointCount += 1;
segmentPosition += 3;
break;
default:
assert(false);
break;
}
++actualTotalSegmentCount;
}
}
// Check that the counts match the metadata.
if (actualCurveListSize != getMotionCurveCount()) {
CubismDebug.cubismLogWarning("The number of curves does not match the metadata.");
result = false;
}
if (actualTotalSegmentCount != getMotionTotalSegmentCount()) {
CubismDebug.cubismLogWarning("The number of segment does not match the metadata.");
result = false;
}
if (actualTotalPointCount != getMotionTotalPointCount()) {
CubismDebug.cubismLogWarning("The number of point does not match the metadata.");
result = false;
}
return result;
}
/**
* Get the state of the interpretation flag of Bezier curve handles in motion.
*
* @param flagType the flag type specified by EvaluationOptionFlag.
* @return If the flag is present, return true.
*/
public boolean getEvaluationOptionFlag(EvaluationOptionFlag flagType) {
if (EvaluationOptionFlag.ARE_BEZIERS_RESTRICTED == flagType) {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.ARE_BEZIERS_RESTRICTED.key).toBoolean();
}
return false;
}
/**
* Get the number of the motion curves.
*
* @return number of motion curve
*/
public int getMotionCurveCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.CURVE_COUNT.key).toInt();
}
/**
* Get the framerate of the motion.
*
* @return framerate[FPS]
*/
public float getMotionFps() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.FPS.key).toFloat();
}
/**
* Get the total number of motion segments.
*
* @return total number of motion segments
*/
public int getMotionTotalSegmentCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.TOTAL_SEGMENT_COUNT.key).toInt();
}
/**
* Get the total number of control points for the curve of the motion.
*
* @return total number of control points for the curve of the motion
*/
public int getMotionTotalPointCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.TOTAL_POINT_COUNT.key).toInt();
}
/**
* Whether a fade-in time is set for the motion.
*
* @return If motion fade-in time is set, return true.
*/
public boolean existsMotionFadeInTime() {
return !json.getRoot().get(JsonKey.META.key).get(JsonKey.FADE_IN_TIME.key).isNull();
}
/**
* Whether a fade-out time is set for the motion.
*
* @return If motion fade-out time is set, return true.
*/
public boolean existsMotionFadeOutTime() {
return !json.getRoot().get(JsonKey.META.key).get(JsonKey.FADE_OUT_TIME.key).isNull();
}
/**
* Get the motion fade-in duration.
*
* @return fade-in duration[s]
*/
public float getMotionFadeInTime() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.FADE_IN_TIME.key).toFloat();
}
/**
* Get the motion fade-out duration.
*
* @return fade-out duration[s]
*/
public float getMotionFadeOutTime() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.FADE_OUT_TIME.key).toFloat();
}
/**
* Get the type of motion curve.
*
* @param curveIndex index of curve
* @return type of motion curve
*/
public String getMotionCurveTarget(int curveIndex) {
return json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.TARGET.key).getString();
}
/**
* Get ID of motion curve.
*
* @param curveIndex index of curve
* @return curve ID
*/
public CubismId getMotionCurveId(int curveIndex) {
return CubismFramework.getIdManager().getId(json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.ID.key).getString());
}
/**
* Whether the fade-in duration is set for the motion's curve.
*
* @param curveIndex index of curve
* @return If fade-in duration is set, return true.
*/
public boolean existsMotionCurveFadeInTime(int curveIndex) {
return !json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.FADE_IN_TIME.key).isNull();
}
/**
* Whether the fade-out duration is set for the motion's curve.
*
* @param curveIndex index of curve
* @return If fade-out duration is set, return true.
*/
public boolean existsMotionCurveFadeOutTime(int curveIndex) {
return !json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.FADE_OUT_TIME.key).isNull();
}
/**
* Get the fade-in duration of the motion curve.
*
* @param curveIndex index of curve
* @return fade-in duration[s]
*/
public float getMotionCurveFadeInTime(int curveIndex) {
return json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.FADE_IN_TIME.key).toFloat();
}
/**
* Get the fade-out duration of the motion curve.
*
* @param curveIndex index of curve
* @return fade-out duration[s]
*/
public float getMotionCurveFadeOutTime(int curveIndex) {
return json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.FADE_OUT_TIME.key).toFloat();
}
/**
* Get the number of segments in the curve of the motion.
*
* @param curveIndex index of curve
* @return number of segments in the curve of the motion
*/
public int getMotionCurveSegmentCount(int curveIndex) {
return json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.SEGMENTS.key).getList().size();
}
/**
* Get the value of a segment of a motion curve.
*
* @param curveIndex index of curve
* @param segmentIndex index of segment
* @return value of segment
*/
public float getMotionCurveSegment(int curveIndex, int segmentIndex) {
return json.getRoot().get(JsonKey.CURVES.key).get(curveIndex).get(JsonKey.SEGMENTS.key).get(segmentIndex).toFloat();
}
/**
* Get the type of a segment of a motion curve.
*
* @param curveIndex index of curve
* @param segmentIndex index of segment
* @return the type of segment
*/
public CubismMotionSegmentType getMotionCurveSegmentType(final int curveIndex, final int segmentIndex) {
final int segmentTypeValue = (int) (getMotionCurveSegment(curveIndex, segmentIndex));
switch (segmentTypeValue) {
case 0:
return CubismMotionSegmentType.LINEAR;
case 1:
return CubismMotionSegmentType.BEZIER;
case 2:
return CubismMotionSegmentType.STEPPED;
case 3:
return CubismMotionSegmentType.INVERSESTEPPED;
default:
assert (false);
return null;
}
}
/**
* Get the number of events.
*
* @return number of events
*/
public int getEventCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.USER_DATA_COUNT.key).toInt();
}
/**
* Get the total number of characters in the event.
*
* @return total number of characters in the event
*/
public int getTotalEventValueSize() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.TOTAL_USER_DATA_SIZE.key).toInt();
}
/**
* Get the event duration.
*
* @param userDataIndex index of events
* @return event duration[s]
*/
public float getEventTime(int userDataIndex) {
return json.getRoot().get(JsonKey.USER_DATA.key).get(userDataIndex).get(JsonKey.TIME.key).toFloat();
}
/**
* Get the event string.
*
* @param userDataIndex index of event
* @return event strings
*/
public String getEventValue(int userDataIndex) {
return json.getRoot().get(JsonKey.USER_DATA.key).get(userDataIndex).get(JsonKey.VALUE.key).getString();
}
private enum JsonKey {
META("Meta"),
DURATION("Duration"),
LOOP("Loop"),
ARE_BEZIERS_RESTRICTED("AreBeziersRestricted"),
CURVE_COUNT("CurveCount"),
FPS("Fps"),
TOTAL_SEGMENT_COUNT("TotalSegmentCount"),
TOTAL_POINT_COUNT("TotalPointCount"),
CURVES("Curves"),
TARGET("Target"),
ID("Id"),
FADE_IN_TIME("FadeInTime"),
FADE_OUT_TIME("FadeOutTime"),
SEGMENTS("Segments"),
USER_DATA("UserData"),
USER_DATA_COUNT("UserDataCount"),
TOTAL_USER_DATA_SIZE("TotalUserDataSize"),
TIME("Time"),
VALUE("Value");
private final String key;
JsonKey(String key) {
this.key = key;
}
}
/**
* motion3.json data
*/
private final CubismJson json;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
import com.live2d.sdk.cubism.framework.model.CubismModel;
/**
* Class for managing motion playback, used to play ACubismMotion subclasses such as CubismMotion motion.
* * If another motion do startMotion() during playback, it will smoothly change to the new motion and the old motion will be suspended.
* * Use multiple CubismMotionManager instances to play multiple motions at the same time, such as when motions for facial expressions and body motions are made separately.
*/
public class CubismMotionManager extends CubismMotionQueueManager {
/**
* Set a priority and start the motion.
*
* @param motion motion instance
* @param priority priority of motion
* @return Returns the identification number(OptionalInt) of the motion that has started. Used as an argument for isFinished(), which judges whether an individual motion has been completed. When it cannot be started, it returns an empty OptionalInt.
*/
public int startMotionPriority(ACubismMotion motion, int priority) {
if (priority == reservationPriority) {
reservationPriority = 0; // Cancel the reservation.
}
// Set priority of the motion during playback.
currentPriority = priority;
return startMotion(motion);
}
/**
* Update the motion and reflect the parameter values to the model.
*
* @param model target model
* @param deltaTimeSeconds delta time[s]
* @return If it is updated, return true.
*/
public boolean updateMotion(CubismModel model, float deltaTimeSeconds) {
userTimeSeconds += deltaTimeSeconds;
final boolean isUpdated = doUpdateMotion(model, userTimeSeconds);
if (isFinished()) {
currentPriority = 0; // 再生中モーションの優先度を解除
}
return isUpdated;
}
/**
* Get the priority of the motion being played now.
*
* @return priority of the motion
*/
public int getCurrentPriority() {
return currentPriority;
}
/**
* Get the priority of the reserved motion.
*
* @return priority of the motion
*/
public int getReservationPriority() {
return reservationPriority;
}
/**
* Set the priority to the reserved motion.
*
* @param priority motion's priority
*/
public void setReservationPriority(int priority) {
reservationPriority = priority;
}
/**
* Reserve motion with a priority.
* <p>
* If the given priority is lower than the already existing reserved priority and the priority of the current motion, it is not reserved and "false" is returned.
* </p>
*
* @param priority motion's priority
* @return If reserving the motion is successful, return true.
*/
public boolean reserveMotion(int priority) {
if (priority <= reservationPriority || priority <= currentPriority) {
return false;
}
reservationPriority = priority;
return true;
}
/**
* Priority of the currently playing motion.
*/
private int currentPriority;
/**
* Priority of the motion to be played. The value becomes 0 during playback. This is function for loading motion files in a separate thread.
*/
private int reservationPriority;
}

View File

@@ -0,0 +1,288 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
/**
* Manager class for each motion being played by CubismMotionQueueManager.
*/
public class CubismMotionQueueEntry {
/**
* Get the motion.
*
* @return motion instance
*/
public ACubismMotion getMotion() {
return motion;
}
/**
* Set a motion instance.
*
* @param motion motion instance
*/
public void setMotion(ACubismMotion motion) {
this.motion = motion;
}
/**
* Set the fade-out to the start state.
*
* @param fadeOutSeconds time it takes to fade out[s]
*/
public void setFadeOut(float fadeOutSeconds) {
this.fadeOutSeconds = fadeOutSeconds;
isTriggeredFadeOut = true;
}
/**
* Start fade-out.
*
* @param fadeOutSeconds time it takes to fade out[s]
* @param userTimeSeconds total delta time[s]
*/
public void startFadeOut(float fadeOutSeconds, float userTimeSeconds) {
final float newEndTimeSeconds = userTimeSeconds + fadeOutSeconds;
isTriggeredFadeOut = true;
if (endTimeSeconds < 0.0f || newEndTimeSeconds < endTimeSeconds) {
endTimeSeconds = newEndTimeSeconds;
}
}
/**
* Get the start time of the motion.
*
* @return start time of the motion[s]
*/
public float getStartTime() {
return startTimeSeconds;
}
/**
* Set the start time of the motion.
*
* @param startTime start time of the motion[s]
*/
public void setStartTime(float startTime) {
startTimeSeconds = startTime;
}
/**
* Get the start time of the fade-in.
*
* @return start time of the fade-in[s]
*/
public float getFadeInStartTime() {
return fadeInStartTimeSeconds;
}
/**
* Set the start time of fade-in.
*
* @param startTime start time of fade-in[s]
*/
public void setFadeInStartTime(float startTime) {
fadeInStartTimeSeconds = startTime;
}
/**
* Get the end time of the fade-in.
*
* @return end time of the fade-in
*/
public float getEndTime() {
return endTimeSeconds;
}
/**
* Set end time of the motion.
*
* @param endTime end time of the motion[s]
*/
public void setEndTime(float endTime) {
endTimeSeconds = endTime;
}
/**
* Whether the motion is finished.
*
* @return If the motion is finished, return true.
*/
public boolean isFinished() {
return finished;
}
/**
* Set end state of the motion.
*
* @param isMotionFinished If true, set the motion to the end state.
*/
public void isFinished(boolean isMotionFinished) {
finished = isMotionFinished;
}
/**
* Whether the motion is started.
*
* @return If the motion is started, return true.
*/
public boolean isStarted() {
return started;
}
/**
* Set start state of the motion.
*
* @param isMotionStarted If true, set the motion to the start state.
*/
public void isStarted(boolean isMotionStarted) {
started = isMotionStarted;
}
/**
* Get the status of the motion.(Enable/Disable)
*
* @return If the motion is valid, return true.
*/
public boolean isAvailable() {
return available;
}
/**
* Set the status of the motion.(Enable/Disable)
*
* @param isMotionAvailable If it is true, the motion is enabled.
*/
public void isAvailable(boolean isMotionAvailable) {
available = isMotionAvailable;
}
/**
* Set the state of the motion.
*
* @param timeSeconds current time[s]
* @param weight weight of motion
*/
public void setState(float timeSeconds, float weight) {
stateTimeSeconds = timeSeconds;
stateWeight = weight;
}
/**
* Get the current time of the motion.
*
* @return current time of the motion.
*/
public float getStateTime() {
return stateTimeSeconds;
}
/**
* Get the weight of the motion.
*
* @return weight of the motion
*/
public float getStateWeight() {
return stateWeight;
}
/**
* Get the time when the last event firing was checked.
*
* @return time when the last event firing was checked[s]
*/
public float getLastCheckEventTime() {
return lastEventCheckSeconds;
}
/**
* Set the time when the last event firing was checked.
*
* @param checkTime time when the last event firing was checked[s]
*/
public void setLastCheckEventTime(float checkTime) {
lastEventCheckSeconds = checkTime;
}
/**
* Get the starting status of the fade-out.
*
* @return Whether fade out is started
*/
public boolean isTriggeredFadeOut() {
return isTriggeredFadeOut;
}
/**
* Get fade-out duration.
*
* @return fade-out duration
*/
public float getFadeOutSeconds() {
return fadeOutSeconds;
}
/**
* ACubismMotionを継承したクラスのインスタンスを取得する。
*
* @return モーションのインスタンス
*/
public ACubismMotion getCubismMotion() {
return motion;
}
/**
* motion
*/
private ACubismMotion motion;
/**
* Enable flag
*/
private boolean available = true;
/**
* finished flag
*/
private boolean finished;
/**
* start flag(0.9.00 or later)
*/
private boolean started;
/**
* Motion playback start time[s]
*/
private float startTimeSeconds = -1.0f;
/**
* Fade-in start time[s] (When in a loop, only the first time.)
*/
private float fadeInStartTimeSeconds;
/**
* Scheduled end time[s]
*/
private float endTimeSeconds = -1.0f;
/**
* state of time[s]
*/
private float stateTimeSeconds;
/**
* state of weight
*/
private float stateWeight;
/**
* last event check time
*/
private float lastEventCheckSeconds;
/**
* fade-out duration of the motion[s]
*/
private float fadeOutSeconds;
/**
* Whether the motion fade-out is started
*/
private boolean isTriggeredFadeOut;
}

View File

@@ -0,0 +1,259 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* The manager class for playing motions. This is used to play ACubismMotion's subclasses such as CubismMotion's motion.
* <p>
* If another motion is done "StartMotion()" during playback, the motion changes smoothly to the new motion and the old motion is interrupted. When multiple motions are played back simultaneously (For example, separate motions for facial expressions, body motions, etc.), multiple CubismMotionQueueManager instances are used.
*/
public class CubismMotionQueueManager {
/**
* 引数で指定したモーションを再生する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始する。
*
* @param motion 開始するモーション
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するisFinished()の引数として使用する。開始できない場合は「-1」を返す。
*/
public int startMotion(ACubismMotion motion) {
if (motion == null) {
return -1;
}
// 既にモーションがあれば終了フラグを立てる。
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry entry = motions.get(i);
if (entry == null) {
continue;
}
entry.setFadeOut(entry.getMotion().getFadeOutTime());
}
CubismMotionQueueEntry motionQueueEntry = new CubismMotionQueueEntry();
motionQueueEntry.setMotion(motion);
motions.add(motionQueueEntry);
if (motion.onBeganMotion != null) {
motion.onBeganMotion.execute(motion);
}
return System.identityHashCode(motionQueueEntry);
}
/**
* Start the specified motion. If there is already a motion of the same type, set the end flag for the existing motion and start fading out.
*
* @param motion motion to start
* @param userTimeSeconds total user time[s]
* @return Returns the identification number(OptionalInt) of the motion that has started. Used as an argument for isFinished(), which judges whether an individual motion has been completed. When it cannot be started, it returns an empty OptionalInt.
*
* @deprecated 第2引数userTimeSecondsを関数内で使用していないため非推奨。startMotion(ACubismMotion motion)を使用してください。
* @see #startMotion(ACubismMotion)
*/
@Deprecated
public int startMotion(ACubismMotion motion, float userTimeSeconds) {
if (motion == null) {
return -1;
}
// If there is already motion, flag it as finished.
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry entry = motions.get(i);
if (entry == null) {
continue;
}
entry.setFadeOut(entry.getMotion().getFadeOutTime());
}
CubismMotionQueueEntry motionQueueEntry = new CubismMotionQueueEntry();
motionQueueEntry.setMotion(motion);
motions.add(motionQueueEntry);
return System.identityHashCode(motionQueueEntry);
}
public boolean isFinished() {
// ---- Do processing ----
// If there is already a motion, flag it as finished.
// At first, remove the null elements from motions list.
motions.removeAll(nullSet);
// motionがnullならば要素をnullとする
// 後でnull要素を全て削除する。
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry motionQueueEntry = motions.get(i);
ACubismMotion motion = motionQueueEntry.getMotion();
if (motion == null) {
motions.set(i, null);
continue;
}
if (!motionQueueEntry.isFinished()) {
return false;
}
}
motions.removeAll(nullSet);
return true;
}
public boolean isFinished(int motionQueueEntryNumber) {
// ---- Do processing ----
// If there is already a motion, flag it as finished.
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry motionQueueEntry = motions.get(i);
if (motionQueueEntry == null) {
continue;
}
if (System.identityHashCode(motionQueueEntry) == motionQueueEntryNumber && !motionQueueEntry.isFinished()) {
return false;
}
}
return true;
}
/**
* Stop all motions.
*/
public void stopAllMotions() {
motions.clear();
}
/**
* Get the specified CubismMotionQueueEntry instance.
*
* @param motionQueueEntryNumber identification number of the motion
* @return specified CubismMotionQueueEntry object. If not found, empty Optional is returned.
*/
public CubismMotionQueueEntry getCubismMotionQueueEntry(int motionQueueEntryNumber) {
// ---- Do processing ----
// If there is already a motion, flag it as finished.
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry motionQueueEntry = motions.get(i);
if (motionQueueEntry == null) {
continue;
}
if (System.identityHashCode(motionQueueEntry) == motionQueueEntryNumber) {
return motionQueueEntry;
}
}
return null;
}
/**
* CubismMotionQueueEntryのリストを取得する。
*
* @return CubismMotionQueueEntryのリスト
*/
public List<CubismMotionQueueEntry> getCubismMotionQueueEntries() {
return motions;
}
/**
* Register the callback function to receive events.
*
* @param callback callback function
* @param customData data to be given to callback
*/
public void setEventCallback(ICubismMotionEventFunction callback, Object customData) {
eventCallback = callback;
eventCustomData = customData;
}
/**
* Update the motion and reflect the parameter values to the model.
*
* @param model target model
* @param userTimeSeconds total delta time[s]
* @return If reflecting the parameter value to the model(the motion is changed.) is successed, return true.
*/
protected boolean doUpdateMotion(CubismModel model, float userTimeSeconds) {
boolean isUpdated = false;
// ---- Do processing ----
// If there is already a motion, flag it as finished.
// At first, remove the null elements from motions list.
motions.removeAll(nullSet);
for (int i = 0; i < motions.size(); i++) {
CubismMotionQueueEntry motionQueueEntry = motions.get(i);
ACubismMotion motion = motionQueueEntry.getMotion();
if (motion == null) {
motions.set(i, null);
continue;
}
motion.updateParameters(model, motionQueueEntry, userTimeSeconds);
isUpdated = true;
// Inspect user-triggered events.
final List<String> firedList = motion.getFiredEvent(
motionQueueEntry.getLastCheckEventTime() - motionQueueEntry.getStartTime(),
userTimeSeconds - motionQueueEntry.getStartTime());
for (int j = 0; j < firedList.size(); j++) {
String event = firedList.get(j);
eventCallback.apply(this, event, eventCustomData);
}
motionQueueEntry.setLastCheckEventTime(userTimeSeconds);
// If any processes have already been finished, delete them.
if (motionQueueEntry.isFinished()) {
motions.set(i, null);
} else {
if (motionQueueEntry.isTriggeredFadeOut()) {
motionQueueEntry.startFadeOut(motionQueueEntry.getFadeOutSeconds(), userTimeSeconds);
}
}
}
motions.removeAll(nullSet);
return isUpdated;
}
/**
* total delta time[s]
*/
protected float userTimeSeconds;
/**
* List of motions
*/
private final List<CubismMotionQueueEntry> motions = new ArrayList<CubismMotionQueueEntry>();
/**
* Callback function
*/
private ICubismMotionEventFunction eventCallback;
/**
* Data to be given to the callback
*/
private Object eventCustomData;
// nullが格納されたSet. null要素だけListから排除する際に使用される。
private final Set<Object> nullSet = Collections.singleton(null);
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
/**
* モーション再生開始コールバック
*/
public interface IBeganMotionCallback {
void execute(ACubismMotion motion);
}

View File

@@ -0,0 +1,16 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
public interface ICubismMotionEventFunction {
void apply(
CubismMotionQueueManager caller,
String eventValue,
Object customData
);
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.motion;
/**
* モーション再生終了コールバック
*/
public interface IFinishedMotionCallback {
void execute(ACubismMotion motion);
}

View File

@@ -0,0 +1,4 @@
/**
* Provide various functions (motion playback, parameter blending) for applying motion data to models.
*/
package com.live2d.sdk.cubism.framework.motion;

View File

@@ -0,0 +1,278 @@
/*
*
* * Copyright(c) Live2D Inc. All rights reserved.
* *
* * Use of this source code is governed by the Live2D Open Software license
* * that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*
*/
package com.live2d.sdk.cubism.framework.physics;
import com.live2d.sdk.cubism.framework.math.CubismMath;
import com.live2d.sdk.cubism.framework.math.CubismVector2;
import com.live2d.sdk.cubism.framework.physics.CubismPhysicsInternal.NormalizedPhysicsParameterValueGetter;
import com.live2d.sdk.cubism.framework.physics.CubismPhysicsInternal.PhysicsScaleGetter;
import com.live2d.sdk.cubism.framework.physics.CubismPhysicsInternal.PhysicsValueGetter;
import java.util.List;
/**
* This is the set of algorithms used in CubismPhysics class.
* <p>
* In Cubism SDK for Java Framework's CubismPhysics, employs a design pattern called the "Strategy Pattern". This class is a collection of those 'strategies'.
*/
class CubismPhysicsFunctions {
public static class GetInputTranslationXFromNormalizedParameterValue implements NormalizedPhysicsParameterValueGetter {
@Override
public void getNormalizedParameterValue(
CubismVector2 targetTranslation,
float[] targetAngle,
float value,
float parameterMinimumValue,
float parameterMaximumValue,
float parameterDefaultValue,
CubismPhysicsInternal.CubismPhysicsNormalization normalizationPosition,
CubismPhysicsInternal.CubismPhysicsNormalization normalizationAngle,
boolean isInverted,
float weight
) {
targetTranslation.x += normalizeParameterValue(
value,
parameterMinimumValue,
parameterMaximumValue,
parameterDefaultValue,
normalizationPosition.minimumValue,
normalizationPosition.maximumValue,
normalizationPosition.defaultValue,
isInverted
) * weight;
}
}
public static class GetInputTranslationYFromNormalizedParameterValue implements NormalizedPhysicsParameterValueGetter {
@Override
public void getNormalizedParameterValue(
CubismVector2 targetTranslation,
float[] targetAngle,
float value,
float parameterMinimumValue,
float parameterMaximumValue,
float parameterDefaultValue,
CubismPhysicsInternal.CubismPhysicsNormalization normalizationPosition,
CubismPhysicsInternal.CubismPhysicsNormalization normalizationAngle,
boolean isInverted,
float weight
) {
targetTranslation.y += normalizeParameterValue(
value,
parameterMinimumValue,
parameterMaximumValue,
parameterDefaultValue,
normalizationPosition.minimumValue,
normalizationPosition.maximumValue,
normalizationPosition.defaultValue,
isInverted
) * weight;
}
}
public static class GetInputAngleFromNormalizedParameterValue implements NormalizedPhysicsParameterValueGetter {
@Override
public void getNormalizedParameterValue(
CubismVector2 targetTranslation,
float[] targetAngle,
float value,
float parameterMinimumValue,
float parameterMaximumValue,
float parameterDefaultValue,
CubismPhysicsInternal.CubismPhysicsNormalization normalizationPosition,
CubismPhysicsInternal.CubismPhysicsNormalization normalizationAngle,
boolean isInverted,
float weight
) {
targetAngle[0] += normalizeParameterValue(
value,
parameterMinimumValue,
parameterMaximumValue,
parameterDefaultValue,
normalizationAngle.minimumValue,
normalizationAngle.maximumValue,
normalizationAngle.defaultValue,
isInverted
) * weight;
}
}
public static class GetOutputTranslationX implements PhysicsValueGetter {
@Override
public float getValue(
CubismVector2 translation,
List<CubismPhysicsInternal.CubismPhysicsParticle> particles,
int baseParticleIndex,
int particleIndex,
boolean isInverted,
CubismVector2 parentGravity
) {
float outputValue = translation.x;
if (isInverted) {
outputValue *= -1.0f;
}
return outputValue;
}
}
public static class GetOutputTranslationY implements PhysicsValueGetter {
@Override
public float getValue(
CubismVector2 translation,
List<CubismPhysicsInternal.CubismPhysicsParticle> particles,
int baseParticleIndex,
int particleIndex,
boolean isInverted,
CubismVector2 parentGravity
) {
float outputValue = translation.y;
if (isInverted) {
outputValue *= -1.0f;
}
return outputValue;
}
}
public static class GetOutputAngle implements PhysicsValueGetter {
@Override
public float getValue(
CubismVector2 translation,
List<CubismPhysicsInternal.CubismPhysicsParticle> particles,
int baseParticleIndex,
int particleIndex,
boolean isInverted,
CubismVector2 parentGravity
) {
float outputValue;
tmpGravity.x = parentGravity.x;
tmpGravity.y = parentGravity.y;
if (particleIndex >= 2) {
tmpGravity.x = particles.get(baseParticleIndex + particleIndex - 1).position.x - particles.get(baseParticleIndex + particleIndex - 2).position.x;
tmpGravity.y = particles.get(baseParticleIndex + particleIndex - 1).position.y - particles.get(baseParticleIndex + particleIndex - 2).position.y;
} else {
tmpGravity.multiply(-1.0f);
}
outputValue = CubismMath.directionToRadian(tmpGravity, translation);
if (isInverted) {
outputValue *= -1.0f;
}
return outputValue;
}
}
private static final CubismVector2 tmpGravity = new CubismVector2();
public static class GetOutputScaleTranslationX implements PhysicsScaleGetter {
@Override
public float getScale(CubismVector2 translationScale, float angleScale) {
return translationScale.x;
}
}
public static class GetOutputScaleTranslationY implements PhysicsScaleGetter {
@Override
public float getScale(CubismVector2 translationScale, float angleScale) {
return translationScale.y;
}
}
public static class GetOutputScaleAngle implements PhysicsScaleGetter {
@Override
public float getScale(CubismVector2 translationScale, float angleScale) {
return angleScale;
}
}
private static float normalizeParameterValue(
float value,
float parameterMinimum,
float parameterMaximum,
float parameterDefault,
float normalizedMinimum,
float normalizedMaximum,
float normalizedDefault,
boolean isInverted
) {
float result = 0.0f;
final float maxValue = Math.max(parameterMaximum, parameterMinimum);
if (maxValue < value) {
value = maxValue;
}
final float minValue = Math.min(parameterMaximum, parameterMinimum);
if (minValue > value) {
value = minValue;
}
final float minNormValue = Math.min(normalizedMinimum, normalizedMaximum);
final float maxNormValue = Math.max(normalizedMinimum, normalizedMaximum);
final float middleNormValue = normalizedDefault;
final float middleValue = getDefaultValue(minValue, maxValue);
final float paramValue = value - middleValue;
switch ((int) Math.signum(paramValue)) {
case 1: {
final float nLength = maxNormValue - middleNormValue;
final float pLength = maxValue - middleValue;
if (pLength != 0.0f) {
result = paramValue * (nLength / pLength);
result += middleNormValue;
}
break;
}
case -1: {
final float nLength = minNormValue - middleNormValue;
final float pLength = minValue - middleValue;
if (pLength != 0.0f) {
result = paramValue * (nLength / pLength);
result += middleNormValue;
}
break;
}
case 0: {
result = middleNormValue;
break;
}
default: {
break;
}
}
return (isInverted) ? result
: (result * (-1.0f));
}
private static float getRangeValue(float min, float max) {
float maxValue = Math.max(min, max);
float minValue = Math.min(min, max);
return CubismMath.absF(maxValue - minValue);
}
private static float getDefaultValue(float min, float max) {
float minValue = Math.min(min, max);
return minValue + (getRangeValue(min, max) / 2.0f);
}
}

View File

@@ -0,0 +1,370 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.physics;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.math.CubismVector2;
import java.util.ArrayList;
import java.util.List;
/**
* Internal data of CubismPhysics.
*/
public class CubismPhysicsInternal {
/**
* Types of physics operations to be applied.
*/
public enum CubismPhysicsTargetType {
/**
* Apply physics operation to parameters
*/
PARAMETER
}
/**
* Types of input for physics operations.
*/
public enum CubismPhysicsSource {
/**
* From X-axis
*/
X,
/**
* From Y-axis
*/
Y,
/**
* From angle
*/
ANGLE
}
/**
* External forces used in physics operations.
*/
public static class PhysicsJsonEffectiveForces {
/**
* Gravity
*/
public CubismVector2 gravity;
/**
* Wind
*/
public CubismVector2 wind;
}
/**
* Parameter information for physics operations.
*/
public static class CubismPhysicsParameter {
/**
* Parameter ID
*/
public CubismId Id;
/**
* Type of destination
*/
public CubismPhysicsTargetType targetType;
}
/**
* Normalization information for physics operations.
*/
public static class CubismPhysicsNormalization {
/**
* Minimum value
*/
public float minimumValue;
/**
* Maximum value
*/
public float maximumValue;
/**
* Default value
*/
public float defaultValue;
}
/**
* Information on a particle used for physics operations.
*/
public static class CubismPhysicsParticle {
/**
* Initial position
*/
public CubismVector2 initialPosition = new CubismVector2();
/**
* Mobility
*/
public float mobility;
/**
* Delay
*/
public float delay;
/**
* Acceleration
*/
public float acceleration;
/**
* Distance
*/
public float radius;
/**
* Current position
*/
public CubismVector2 position = new CubismVector2();
/**
* Last position
*/
public CubismVector2 lastPosition = new CubismVector2();
/**
* Last gravity
*/
public CubismVector2 lastGravity = new CubismVector2();
/**
* Current force
*/
public CubismVector2 force = new CubismVector2();
/**
* Current velocity
*/
public CubismVector2 velocity = new CubismVector2();
}
/**
* Manager of phycal points in physics operations.
*/
public static class CubismPhysicsSubRig {
/**
* number of inputs
*/
public int inputCount;
/**
* number of outputs
*/
public int outputCount;
/**
* number of particles
*/
public int particleCount;
/**
* First index of inputs
*/
public int baseInputIndex;
/**
* First index of outputs
*/
public int baseOutputIndex;
/**
* First index of particles
*/
public int baseParticleIndex;
/**
* Normalized position
*/
public CubismPhysicsNormalization normalizationPosition = new CubismPhysicsNormalization();
/**
* Normalized angle
*/
public CubismPhysicsNormalization normalizationAngle = new CubismPhysicsNormalization();
}
/**
* Input information for physics operations.
*/
public static class CubismPhysicsInput {
/**
* Input source parameter
*/
public CubismPhysicsParameter source = new CubismPhysicsParameter();
/**
* Index of input source parameter
*/
public int sourceParameterIndex;
/**
* Weight
*/
public float weight;
/**
* Type of input
*/
public CubismPhysicsSource type;
/**
* Whether the value is inverted.
*/
public boolean reflect;
/**
* Function to get normalized parameter values
*/
public NormalizedPhysicsParameterValueGetter getNormalizedParameterValue;
}
/**
* Output information for physics operations.
*/
public static class CubismPhysicsOutput {
/**
* Output destination parameter
*/
public CubismPhysicsParameter destination = new CubismPhysicsParameter();
/**
* Index of output destination parameter
*/
public int destinationParameterIndex;
/**
* Pendulum index
*/
public int vertexIndex;
/**
* transition scale
*/
public CubismVector2 transitionScale = new CubismVector2();
/**
* Angle scale
*/
public float angleScale;
/**
* Weight
*/
public float weight;
/**
* Type of output
*/
public CubismPhysicsSource type;
/**
* Whether the value is inverted
*/
public boolean reflect;
/**
* Value when the value is below the minimum value
*/
public float valueBelowMinimum;
/**
* Value when the maximum value is exceeded.
*/
public float valueExceededMaximum;
/**
* Function to get the value for physics operation.
*/
public PhysicsValueGetter getValue;
/**
* Function to get the scale value for physics operation
*/
public PhysicsScaleGetter getScale;
}
/**
* Physics operation data
*/
public static class CubismPhysicsRig {
/**
* Number of physics point for physics operation
*/
public int subRigCount;
/**
* List of physics point management for physics operation
*/
public List<CubismPhysicsSubRig> settings = new ArrayList<CubismPhysicsSubRig>();
/**
* List of inputs for physics operation
*/
public List<CubismPhysicsInput> inputs = new ArrayList<CubismPhysicsInput>();
/**
* List of outputs for physics operation
*/
public List<CubismPhysicsOutput> outputs = new ArrayList<CubismPhysicsOutput>();
/**
* List of particles for physics operation
*/
public List<CubismPhysicsParticle> particles = new ArrayList<CubismPhysicsParticle>();
/**
* Gravity
*/
public CubismVector2 gravity = new CubismVector2();
/**
* Wind
*/
public CubismVector2 wind = new CubismVector2();
/**
* Physics operation FPS
*/
public float fps;
}
/**
* Functional interface with a function which gets normalized parameters.
*/
public interface NormalizedPhysicsParameterValueGetter {
/**
* Get normalized parameters.
*
* @param targetTransition the move value of the calculation result
* @param targetAngle the angle of the calculation result
* @param value the value of the parameter
* @param parameterMinimumValue the minimum value of the parameter
* @param parameterMaximumValue the maximum value of the parameter
* @param parameterDefaultValue the default value of the parameter
* @param normalizationPosition the normalized position
* @param normalizationAngle the normalized angle
* @param isInverted Whether the value is inverted
* @param weight a weight
*/
void getNormalizedParameterValue(
CubismVector2 targetTransition,
float[] targetAngle,
float value,
float parameterMinimumValue,
float parameterMaximumValue,
float parameterDefaultValue,
CubismPhysicsNormalization normalizationPosition,
CubismPhysicsNormalization normalizationAngle,
boolean isInverted,
float weight
);
}
/**
* Functional interface with a function for getting values of physics operations.
*/
public interface PhysicsValueGetter {
/**
* Get values of physics operations.
*
* @param transition a transition value
* @param particles a particles list
* @param particleIndex a particle index
* @param isInverted Whether the value is inverted
* @param parentGravity a gravity
* @return the value
*/
float getValue(
CubismVector2 transition,
List<CubismPhysicsParticle> particles,
int baseParticleIndex,
int particleIndex,
boolean isInverted,
CubismVector2 parentGravity
);
}
/**
* Functional interface with a function for getting the scale of physics operations.
*/
public interface PhysicsScaleGetter {
/**
* Get a scale of physics operations.
*
* @param transitionScale transition scale
* @param angleScale angle scale
* @return scale value
*/
float getScale(CubismVector2 transitionScale, float angleScale);
}
}

View File

@@ -0,0 +1,424 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.physics;
import com.live2d.sdk.cubism.framework.CubismFramework;
import com.live2d.sdk.cubism.framework.id.CubismId;
import com.live2d.sdk.cubism.framework.math.CubismVector2;
import com.live2d.sdk.cubism.framework.utils.jsonparser.CubismJson;
/**
* A manager of physics3.json.
*/
public class CubismPhysicsJson {
/**
* Constructor
*
* @param buffer a buffer where physics3.json is loaded.
*/
public CubismPhysicsJson(final byte[] buffer) {
json = CubismJson.create(buffer);
}
/**
* Get the gravity vector.
*
* @return gravity vector
*/
public CubismVector2 getGravity() {
CubismVector2 gravity = new CubismVector2();
gravity.x = json.getRoot().get(JsonKey.META.key).get(JsonKey.EFFECTIVE_FORCES.key).get(JsonKey.GRAVITY.key).get(JsonKey.X.key).toFloat();
gravity.y = json.getRoot().get(JsonKey.META.key).get(JsonKey.EFFECTIVE_FORCES.key).get(JsonKey.GRAVITY.key).get(JsonKey.Y.key).toFloat();
return gravity;
}
/**
* Get the wind vector.
*
* @return wind vector
*/
public CubismVector2 getWind() {
CubismVector2 wind = new CubismVector2();
wind.x = json.getRoot().get(JsonKey.META.key).get(JsonKey.EFFECTIVE_FORCES.key).get(JsonKey.WIND.key).get(JsonKey.X.key).toFloat();
wind.y = json.getRoot().get(JsonKey.META.key).get(JsonKey.EFFECTIVE_FORCES.key).get(JsonKey.WIND.key).get(JsonKey.Y.key).toFloat();
return wind;
}
/**
* Get the assumed FPS of physics operations.
* If there is no FPS information in physics3.json, return 0.0f.
*
* @return physics operation FPS
*/
public float getFps() {
// If FPS information does not exist in physics3.json, 0.0f is returned.
return json.getRoot().get(JsonKey.META.key).get(JsonKey.FPS.key).toFloat(0.0f);
}
/**
* Get the number of physics settings.
*
* @return the number of physics settings.
*/
public int getSubRigCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.PHYSICS_SETTING_COUNT.key).toInt();
}
/**
* Get the total number of inputs.
*
* @return total number of inputs
*/
public int getTotalInputCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.TOTAL_INPUT_COUNT.key).toInt();
}
/**
* Get the total number of outputs.
*
* @return the total number of outputs
*/
public int getTotalOutputCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.TOTAL_OUTPUT_COUNT.key).toInt();
}
/**
* Get the number of vertices.
*
* @return the number of vertices
*/
public int getVertexCount() {
return json.getRoot().get(JsonKey.META.key).get(JsonKey.VERTEX_COUNT.key).toInt();
}
/**
* Get the minimum value of normalized position.
*
* @param physicsSettingIndex physics setting index
* @return the minimum value of normalized position
*/
public float getNormalizationPositionMinimumValue(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.NORMALIZATION.key).get(JsonKey.POSITION.key).get(JsonKey.MINIMUM.key).toFloat();
}
/**
* Get the maximum value of normalized position.
*
* @param physicsSettingIndex physics setting index
* @return the maximum value of normalized position
*/
public float getNormalizationPositionMaximumValue(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.NORMALIZATION.key).get(JsonKey.POSITION.key).get(JsonKey.MAXIMUM.key).toFloat();
}
/**
* Get the default value of normalized position.
*
* @param physicsSettingIndex physics setting index
* @return the maximum value of normalized position
*/
public float getNormalizationPositionDefaultValue(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.NORMALIZATION.key).get(JsonKey.POSITION.key).get(JsonKey.DEFAULT.key).toFloat();
}
/**
* Get the minimum value of normalized angle.
*
* @param physicsSettingIndex physics setting index
* @return the minimum value of normalized angle
*/
public float getNormalizationAngleMinimumValue(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.NORMALIZATION.key).get(JsonKey.ANGLE.key).get(JsonKey.MINIMUM.key).toFloat();
}
/**
* Get the maximum value of normalized angle.
*
* @param physicsSettingIndex physics setting index
* @return the maximum value of normalized angle
*/
public float getNormalizationAngleMaximumValue(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.NORMALIZATION.key).get(JsonKey.ANGLE.key).get(JsonKey.MAXIMUM.key).toFloat();
}
/**
* Get the default value of normalized angle.
*
* @param physicsSettingIndex physics setting index
* @return the default value of normalized angle
*/
public float getNormalizationAngleDefaultValue(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.NORMALIZATION.key).get(JsonKey.ANGLE.key).get(JsonKey.DEFAULT.key).toFloat();
}
/**
* Get the number of the input.
*
* @param physicsSettingIndex physics setting index
* @return the number of inputs
*/
public int getInputCount(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.INPUT.key).getList().size();
}
/**
* Get the weight of the input.
*
* @param physicsSettingIndex physics setting index
* @param inputIndex an input index
* @return the weight of inputs
*/
public float getInputWeight(int physicsSettingIndex, int inputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.INPUT.key).get(inputIndex).get(JsonKey.WEIGHT.key).toFloat();
}
/**
* Get the inversion of the input.
*
* @param physicsSettingIndex physics setting index
* @param inputIndex an input index
* @return the inversion of the input
*/
public boolean getInputReflect(int physicsSettingIndex, int inputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.INPUT.key).get(inputIndex).get(JsonKey.REFLECT.key).toBoolean();
}
/**
* Get the kind of the input.
*
* @param physicsSettingIndex physics setting index
* @param inputIndex an input index
* @return 入力の種類 the kind of the input
*/
public String getInputType(int physicsSettingIndex, int inputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.INPUT.key).get(inputIndex).get(JsonKey.TYPE.key).getString();
}
/**
* Get the ID of the input destination.
*
* @param physicsSettingIndex physics setting index
* @param inputIndex an input index
* @return the input destination ID
*/
public CubismId getInputSourceId(int physicsSettingIndex, int inputIndex) {
return CubismFramework.getIdManager().getId(json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.INPUT.key).get(inputIndex).get(JsonKey.SOURCE.key).get(JsonKey.ID.key).getString());
}
/**
* Get the number of outputs.
*
* @param physicsSettingIndex physics setting index
* @return the number of outputs
*/
public int getOutputCount(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.OUTPUT.key).getList().size();
}
/**
* Get the index of output vertices.
*
* @param physicsSettingIndex physics setting index
* @param outputIndex an output index
* @return the index of output vertices
*/
public int getOutputVertexIndex(int physicsSettingIndex, int outputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.OUTPUT.key).get(outputIndex).get(JsonKey.VERTEX_INDEX.key).toInt();
}
/**
* Get the scale of output angle.
*
* @param physicsSettingIndex physics setting index
* @param outputIndex output index
* @return the scale of output angle
*/
public float getOutputAngleScale(int physicsSettingIndex, int outputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.OUTPUT.key).get(outputIndex).get(JsonKey.SCALE.key).toFloat();
}
/**
* Get the weight of the output.
*
* @param physicsSettingIndex physics setting index
* @param outputIndex an output index
* @return the weight of the output
*/
public float getOutputWeight(int physicsSettingIndex, int outputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.OUTPUT.key).get(outputIndex).get(JsonKey.WEIGHT.key).toFloat();
}
/**
* Get the ID of the output destination.
*
* @param physicsSettingIndex physics setting index
* @param outputIndex an output index
* @return the output destination ID
*/
public CubismId getOutputsDestinationId(int physicsSettingIndex, int outputIndex) {
return CubismFramework.getIdManager().getId(json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.OUTPUT.key).get(outputIndex).get(JsonKey.DESTINATION.key).get(JsonKey.ID.key).getString());
}
/**
* Get the kind of the output.
*
* @param physicsSettingIndex physics setting index
* @param outputIndex output index
* @return the kind of the output
*/
public String getOutputType(int physicsSettingIndex, int outputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.OUTPUT.key).get(outputIndex).get(JsonKey.TYPE.key).getString();
}
/**
* Get the inversion of the output.
*
* @param physicsSettingIndex physics setting index
* @param outputIndex output index
* @return the inversion of the output
*/
public boolean getOutputReflect(int physicsSettingIndex, int outputIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.OUTPUT.key).get(outputIndex).get(JsonKey.REFLECT.key).toBoolean();
}
/**
* Get the number of particles.
*
* @param physicsSettingIndex physics setting index
* @return the number of particles
*/
public int getParticleCount(int physicsSettingIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.VERTICES.key).getList().size();
}
/**
* Get the mobility of the particles.
*
* @param physicsSettingIndex physics setting index
* @param vertexIndex the vertex index
* @return the mobility of the particles
*/
public float getParticleMobility(int physicsSettingIndex, int vertexIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.VERTICES.key).get(vertexIndex).get(JsonKey.MOBILITY.key).toFloat();
}
/**
* Get the delay of the particle.
*
* @param physicsSettingIndex physics setting index
* @param vertexIndex the vertex index
* @return the delay of the particle
*/
public float getParticleDelay(int physicsSettingIndex, int vertexIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.VERTICES.key).get(vertexIndex).get(JsonKey.DELAY.key).toFloat();
}
/**
* Get the acceleration of the particle.
*
* @param physicsSettingIndex physics setting index
* @param vertexIndex vertex index
* @return the acceleration of the particle.
*/
public float getParticleAcceleration(int physicsSettingIndex, int vertexIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.VERTICES.key).get(vertexIndex).get(JsonKey.ACCELERATION.key).toFloat();
}
/**
* Get the distance of the particle.
*
* @param physicsSettingIndex physics setting index
* @param vertexIndex vertex index
* @return the distance of the particle
*/
public float getParticleRadius(int physicsSettingIndex, int vertexIndex) {
return json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.VERTICES.key).get(vertexIndex).get(JsonKey.RADIUS.key).toFloat();
}
/**
* Get the position of the particle.
*
* @param physicsSettingIndex physics setting index
* @param vertexIndex vertex index
* @return the position of the particle
*/
public CubismVector2 getParticlePosition(int physicsSettingIndex, int vertexIndex) {
float x = json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.VERTICES.key).get(vertexIndex).get(JsonKey.POSITION.key).get(JsonKey.X.key).toFloat();
float y = json.getRoot().get(JsonKey.PHYSICS_SETTINGS.key).get(physicsSettingIndex).get(JsonKey.VERTICES.key).get(vertexIndex).get(JsonKey.POSITION.key).get(JsonKey.Y.key).toFloat();
return new CubismVector2(x, y);
}
//----- private constants -----
private enum JsonKey {
POSITION("Position"),
X("X"),
Y("Y"),
ANGLE("Angle"),
TYPE("Type"),
ID("Id"),
// Meta
META("Meta"),
EFFECTIVE_FORCES("EffectiveForces"),
TOTAL_INPUT_COUNT("TotalInputCount"),
TOTAL_OUTPUT_COUNT("TotalOutputCount"),
PHYSICS_SETTING_COUNT("PhysicsSettingCount"),
GRAVITY("Gravity"),
WIND("Wind"),
VERTEX_COUNT("VertexCount"),
FPS("Fps"),
// Physics Settings
PHYSICS_SETTINGS("PhysicsSettings"),
NORMALIZATION("Normalization"),
MINIMUM("Minimum"),
MAXIMUM("Maximum"),
DEFAULT("Default"),
REFLECT("Reflect"),
WEIGHT("Weight"),
// Input
INPUT("Input"),
SOURCE("Source"),
// Output
OUTPUT("Output"),
SCALE("Scale"),
VERTEX_INDEX("VertexIndex"),
DESTINATION("Destination"),
// Particle
VERTICES("Vertices"),
MOBILITY("Mobility"),
DELAY("Delay"),
RADIUS("Radius"),
ACCELERATION("Acceleration");
private final String key;
JsonKey(String key) {
this.key = key;
}
}
private CubismPhysicsJson(CubismJson json) {
this.json = json;
}
/**
* physics3.json data
*/
private final CubismJson json;
}

View File

@@ -0,0 +1,4 @@
/**
* Provide functions for applying physics-based deformation operations to models.
*/
package com.live2d.sdk.cubism.framework.physics;

View File

@@ -0,0 +1,136 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering;
import com.live2d.sdk.cubism.framework.math.CubismMatrix44;
import com.live2d.sdk.cubism.framework.rendering.android.CubismClippingContextAndroid;
import com.live2d.sdk.cubism.framework.type.csmRectF;
import java.util.ArrayList;
import java.util.List;
/**
* クリッピングについての設定を保持するクラス
* サブクラスに環境依存のフィールドを保持する。
*/
public abstract class ACubismClippingContext {
/**
* 渡されたレンダラーの種類に基づき、適切なクリッピングコンテキストを生成する。
*
* @param type 生成するレンダラーの種類
* @param manager このクリッピングコンテキストを保持するマネージャーのインスタンス
* @param clippingDrawableIndices クリッピングマスクのIDの配列
* @param clipCount クリッピングマスクの数
*
* @return 生成したクリッピングコンテキストのインスタンス
*/
public static ACubismClippingContext createClippingContext(
CubismRenderer.RendererType type,
ICubismClippingManager manager,
int[] clippingDrawableIndices,
int clipCount
) {
switch (type) {
case ANDROID:
return new CubismClippingContextAndroid(manager, clippingDrawableIndices, clipCount);
case UNKNOWN:
default:
throw new IllegalArgumentException("Failed to create a clipping context. The specified renderer type may be incorrect.");
}
}
/**
* コンストラクタ
*
* @param manager このクリッピングコンテキストを保持するマネージャーのインスタンス
* @param clippingDrawableIndices クリッピングマスクのIDの配列
* @param clipCount クリッピングマスクの数
*/
public ACubismClippingContext(
ICubismClippingManager manager,
int[] clippingDrawableIndices,
int clipCount
) {
if (clippingDrawableIndices == null || manager == null) {
throw new IllegalArgumentException("The argument is null.");
}
// クリップしている(=マスク用のDrawableのインデックスリスト
clippingIdList = clippingDrawableIndices;
// マスクの数
clippingIdCount = clipCount;
// このコンテキストを保持するマネージャー
owner = manager;
}
/**
* このマスクにクリップされる描画オブジェクトを追加する
*
* @param drawableIndex クリッピング対象に追加する描画オブジェクトのインデックス
*/
public void addClippedDrawable(int drawableIndex) {
clippedDrawableIndexList.add(drawableIndex);
}
/**
* このマスクを管理するマネージャのインスタンスを取得する。
*
* @return クリッピングマネージャのインスタンス
*/
public abstract ICubismClippingManager getClippingManager();
/**
* 現在の描画状態でマスクの準備が必要ならtrue
*/
public boolean isUsing;
/**
* クリッピングマスクのIDの配列
*/
public final int[] clippingIdList;
/**
* クリッピングマスクの数
*/
public final int clippingIdCount;
/**
* RGBAのいずれのチャンネルにこのクリップを配置するか0:R, 1:G, 2:B, 3:A
*/
public int layoutChannelIndex;
/**
* マスク用チャンネルのどの領域にマスクを入れるか(View座標-1..1, UVは0..1に直す)
*/
public final csmRectF layoutBounds = csmRectF.create();
/**
* このクリッピングで、クリッピングされる全ての描画オブジェクトの囲み矩形(毎回更新)
*/
public final csmRectF allClippedDrawRect = csmRectF.create();
/**
* マスクの位置計算結果を保持する行列
*/
public final CubismMatrix44 matrixForMask = CubismMatrix44.create();
/**
* 描画オブジェクトの位置計算結果を保持する行列
*/
public final CubismMatrix44 matrixForDraw = CubismMatrix44.create();
/**
* このマスクにクリップされる描画オブジェクトのリスト
*/
public final List<Integer> clippedDrawableIndexList = new ArrayList<>();
/**
* このマスクが割り当てられるレンダーテクスチャ(フレームバッファ)やカラーバッファのインデックス
*/
public int bufferIndex;
/**
* このマスクを管理しているマネージャーのインスタンス
*/
protected ICubismClippingManager owner;
}

View File

@@ -0,0 +1,584 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering;
import static com.live2d.sdk.cubism.framework.CubismFramework.VERTEX_OFFSET;
import static com.live2d.sdk.cubism.framework.CubismFramework.VERTEX_STEP;
import static com.live2d.sdk.cubism.framework.utils.CubismDebug.cubismLogError;
import com.live2d.sdk.cubism.framework.math.CubismMatrix44;
import com.live2d.sdk.cubism.framework.math.CubismVector2;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.type.csmRectF;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
/**
* クリッピングマネージャーの抽象骨格クラス
*
* @param <T_ClippingContext> ACubismClippingContextを継承した型
* @param <T_OffscreenSurface> CubismOffscreenSurface型
*/
public abstract class ACubismClippingManager<
T_ClippingContext extends ACubismClippingContext,
T_OffscreenSurface
> implements Closeable, ICubismClippingManager {
/**
* コンストラクタ
*/
public ACubismClippingManager() {
CubismRenderer.CubismTextureColor tmp = new CubismRenderer.CubismTextureColor();
tmp.r = 1.0f;
tmp.g = 0.0f;
tmp.b = 0.0f;
tmp.a = 0.0f;
channelColors.add(tmp);
tmp = new CubismRenderer.CubismTextureColor();
tmp.r = 0.0f;
tmp.g = 1.0f;
tmp.b = 0.0f;
tmp.a = 0.0f;
channelColors.add(tmp);
tmp = new CubismRenderer.CubismTextureColor();
tmp.r = 0.0f;
tmp.g = 0.0f;
tmp.b = 1.0f;
tmp.a = 0.0f;
channelColors.add(tmp);
tmp = new CubismRenderer.CubismTextureColor();
tmp.r = 0.0f;
tmp.g = 0.0f;
tmp.b = 0.0f;
tmp.a = 1.0f;
channelColors.add(tmp);
}
@Override
public void close() {
clippingContextListForMask.clear();
clippingContextListForDraw.clear();
channelColors.clear();
if (clearedMaskBufferFlags != null) {
clearedMaskBufferFlags = null;
}
}
@Override
public void initialize(
CubismRenderer.RendererType type,
CubismModel model,
int maskBufferCount
) {
renderTextureCount = maskBufferCount;
// レンダーテクスチャのクリアフラグの配列の初期化
clearedMaskBufferFlags = new boolean[renderTextureCount];
final int drawableCount = model.getDrawableCount(); // 描画オブジェクトの数
final int[][] drawableMasks = model.getDrawableMasks(); // 描画オブジェクトをマスクする描画オブジェクトのインデックスのリスト
final int[] drawableMaskCounts = model.getDrawableMaskCounts(); // 描画オブジェクトをマスクする描画オブジェクトの数
// クリッピングマスクを使う描画オブジェクトを全て登録する。
// クリッピングマスクは、通常数個程度に限定して使うものとする。
for (int i = 0; i < drawableCount; i++) {
if (drawableMaskCounts[i] <= 0) {
// クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない)
clippingContextListForDraw.add(null);
continue;
}
// 既にあるClipContextと同じかチェックする。
T_ClippingContext cc = findSameClip(drawableMasks[i], drawableMaskCounts[i]);
if (cc == null) {
// 同一のマスクが存在していない場合は生成する。
cc = (T_ClippingContext) ACubismClippingContext.createClippingContext(
type,
this,
drawableMasks[i],
drawableMaskCounts[i]
);
clippingContextListForMask.add(cc);
}
cc.addClippedDrawable(i);
clippingContextListForDraw.add(cc);
}
}
@Override
public void setupMatrixForHighPrecision(CubismModel model, boolean isRightHanded) {
// 全てのクリッピングを用意する。
// 同じクリップ複数の場合はまとめて1つのクリップを使う場合は1度だけ設定する。
int usingClipCount = 0;
for (int clipIndex = 0; clipIndex < clippingContextListForMask.size(); clipIndex++) {
// 1つのクリッピングマスクに関して
T_ClippingContext cc = clippingContextListForMask.get(clipIndex);
// このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
calcClippedDrawTotalBounds(model, cc);
if (cc.isUsing) {
usingClipCount++; // 使用中としてカウント
}
}
// マスク行列作成処理
if (usingClipCount <= 0) {
return; // クリッピングマスクが存在しない場合何もしない。
}
setupLayoutBounds(0);
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる。
if (clearedMaskBufferFlags.length != renderTextureCount) {
clearedMaskBufferFlags = new boolean[renderTextureCount];
}
// マスクのクリアフラグを毎フレーム開始時に初期化する。
else {
for (int i = 0; i < renderTextureCount; i++) {
clearedMaskBufferFlags[i] = false;
}
}
// 実際にマスクを生成する。
// 全てのマスクをどのようにレイアウトして描くかを決定し、ClipContext, ClippedDrawContextに記憶する。
for (int clipIndex = 0; clipIndex < clippingContextListForMask.size(); clipIndex++) {
// ---- 実際に1つのマスクを描く ----
T_ClippingContext clipContext = clippingContextListForMask.get(clipIndex);
csmRectF allClippedDrawRect = clipContext.allClippedDrawRect; // このマスクを使う、全ての描画オブジェクトの論理座標上の囲み矩形
csmRectF layoutBoundsOnTex01 = clipContext.layoutBounds; // このマスクを収める
final float margin = 0.05f;
float scaleX, scaleY;
final float ppu = model.getPixelPerUnit();
final float maskPixelWidth = clipContext.getClippingManager().getClippingMaskBufferSize().x;
final float maskPixelHeight = clipContext.getClippingManager().getClippingMaskBufferSize().y;
final float physicalMaskWidth = layoutBoundsOnTex01.getWidth() * maskPixelWidth;
final float physicalMaskHeight = layoutBoundsOnTex01.getHeight() * maskPixelHeight;
tmpBoundsOnModel.setRect(allClippedDrawRect);
if (tmpBoundsOnModel.getWidth() * ppu > physicalMaskWidth) {
tmpBoundsOnModel.expand(allClippedDrawRect.getWidth() * margin, 0.0f);
scaleX = layoutBoundsOnTex01.getWidth() / tmpBoundsOnModel.getWidth();
} else {
scaleX = ppu / physicalMaskWidth;
}
if (tmpBoundsOnModel.getHeight() * ppu > physicalMaskHeight) {
tmpBoundsOnModel.expand(0.0f, allClippedDrawRect.getHeight() * margin);
scaleY = layoutBoundsOnTex01.getHeight() / tmpBoundsOnModel.getHeight();
} else {
scaleY = ppu / physicalMaskHeight;
}
// マスク生成時に使う行列を求める。
createMatrixForMask(isRightHanded, layoutBoundsOnTex01, scaleX, scaleY);
clipContext.matrixForMask.setMatrix(tmpMatrixForMask.getArray());
clipContext.matrixForDraw.setMatrix(tmpMatrixForDraw.getArray());
}
}
@Override
public void createMatrixForMask(
boolean isRightHanded,
csmRectF layoutBoundsOnTex01,
float scaleX,
float scaleY
) {
// マスク作成用の行列の計算
tmpMatrix.loadIdentity();
{
// Layout0..1を、-1..1に変換
tmpMatrix.translateRelative(-1.0f, -1.0f);
tmpMatrix.scaleRelative(2.0f, 2.0f);
// view to Layout0..1
tmpMatrix.translateRelative(
layoutBoundsOnTex01.getX(),
layoutBoundsOnTex01.getY()
);
tmpMatrix.scaleRelative(scaleX, scaleY);
tmpMatrix.translateRelative(
-tmpBoundsOnModel.getX(),
-tmpBoundsOnModel.getY()
);
}
tmpMatrixForMask.setMatrix(tmpMatrix);
// 描画用の行列の計算
tmpMatrix.loadIdentity();
{
tmpMatrix.translateRelative(
layoutBoundsOnTex01.getX(),
layoutBoundsOnTex01.getY() * ((isRightHanded) ? -1.0f : 1.0f)
);
tmpMatrix.scaleRelative(scaleX, scaleY * ((isRightHanded) ? -1.0f : 1.0f));
tmpMatrix.translateRelative(
-tmpBoundsOnModel.getX(),
-tmpBoundsOnModel.getY()
);
}
tmpMatrixForDraw.setMatrix(tmpMatrix);
}
@Override
public void setupLayoutBounds(int usingClipCount) {
final int useClippingMaskMaxCount = renderTextureCount <= 1
? CLIPPING_MASK_MAX_COUNT_ON_DEFAULT
: CLIPPING_MASK_MAX_COUNT_ON_MULTI_RENDER_TEXTURE * renderTextureCount;
if (usingClipCount <= 0 || usingClipCount > useClippingMaskMaxCount) {
if (usingClipCount > useClippingMaskMaxCount) {
// マスクの制限数の警告を出す
int count = usingClipCount - useClippingMaskMaxCount;
cubismLogError(
"not supported mask count : %d\n[Details] render texture count: %d\n, mask count : %d",
count,
renderTextureCount,
usingClipCount
);
}
// この場合は一つのマスクターゲットを毎回クリアして使用する
for (int index = 0; index < clippingContextListForMask.size(); index++) {
T_ClippingContext cc = clippingContextListForMask.get(index);
cc.layoutChannelIndex = 0; // どうせ毎回消すので固定で良い
cc.layoutBounds.setX(0.0f);
cc.layoutBounds.setY(0.0f);
cc.layoutBounds.setWidth(1.0f);
cc.layoutBounds.setHeight(1.0f);
cc.bufferIndex = 0;
}
return;
}
// レンダーテクスチャが1枚なら9分割する最大36枚
final int layoutCountMaxValue = renderTextureCount <= 1 ? 9 : 8;
// ひとつのRenderTextureを極力いっぱいに使ってマスクをレイアウトする。
// マスクグループの数が4以下ならRGBA各チャンネルにつずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。
// NOTE: 1枚に割り当てるマスクの分割数を取りたいため、小数点は切り上げる。
final int countPerSheetDiv = (usingClipCount + renderTextureCount - 1) / renderTextureCount; // レンダーテクスチャ1枚あたり何枚割り当てるか
final int reduceLayoutTextureCount = usingClipCount % renderTextureCount; // レイアウトの数を1枚減らすレンダーテクスチャの数この数だけのレンダーテクスチャが対象
// RGBAを順番に使っていく。
final int divCount = countPerSheetDiv / COLOR_CHANNEL_COUNT; // 1チャンネルに配置する基本のマスク個数
final int modCount = countPerSheetDiv % COLOR_CHANNEL_COUNT; // 余り、この番号のチャンネルまでに1つずつ配分するインデックスではない
// RGBAそれぞれのチャンネルを用意していく(0:R , 1:G , 2:B, 3:A, )
int curClipIndex = 0; // 順番に設定していく
for (int renderTextureIndex = 0; renderTextureIndex < renderTextureCount; renderTextureIndex++) {
for (int channelIndex = 0; channelIndex < COLOR_CHANNEL_COUNT; channelIndex++) {
// このチャンネルにレイアウトする数
// NOTE: レイアウト数 = 1チャンネルに配置する基本のマスク + 余りのマスクを置くチャンネルなら1つ追加
int layoutCount = divCount + (channelIndex < modCount ? 1 : 0);
// レイアウトの数を1枚減らす場合にそれを行うチャンネルを決定
// divが0の時は正常なインデックスの範囲になるように調整
final int checkChannelIndex = modCount + (divCount < 1 ? -1 : 0);
// 今回が対象のチャンネルかつ、レイアウトの数を1枚減らすレンダーテクスチャが存在する場合
if (channelIndex == checkChannelIndex && reduceLayoutTextureCount > 0) {
// 現在のレンダーテクスチャが、対象のレンダーテクスチャであればレイアウトの数を1枚減らす。
layoutCount -= !(renderTextureIndex < reduceLayoutTextureCount) ? 1 : 0;
}
// 分割方法を決定する。
if (layoutCount == 0) {
// 何もしない。
} else if (layoutCount == 1) {
// 全てをそのまま使う。
T_ClippingContext cc = clippingContextListForMask.get(curClipIndex++);
cc.layoutChannelIndex = channelIndex;
csmRectF bounds = cc.layoutBounds;
bounds.setX(0.0f);
bounds.setY(0.0f);
bounds.setWidth(1.0f);
bounds.setHeight(1.0f);
cc.bufferIndex = renderTextureIndex;
} else if (layoutCount == 2) {
for (int i = 0; i < layoutCount; i++) {
final int xpos = i % 2;
T_ClippingContext cc = clippingContextListForMask.get(curClipIndex++);
cc.layoutChannelIndex = channelIndex;
csmRectF bounds = cc.layoutBounds;
// UVを2つに分解して使う
bounds.setX(xpos * 0.5f);
bounds.setY(0.0f);
bounds.setWidth(0.5f);
bounds.setHeight(1.0f);
cc.bufferIndex = renderTextureIndex;
}
} else if (layoutCount <= 4) {
// 4分割して使う
for (int i = 0; i < layoutCount; i++) {
final int xpos = i % 2;
final int ypos = i / 2;
T_ClippingContext cc = clippingContextListForMask.get(curClipIndex++);
cc.layoutChannelIndex = channelIndex;
csmRectF bounds = cc.layoutBounds;
bounds.setX(xpos * 0.5f);
bounds.setY(ypos * 0.5f);
bounds.setWidth(0.5f);
bounds.setHeight(0.5f);
cc.bufferIndex = renderTextureIndex;
}
} else if (layoutCount <= layoutCountMaxValue) {
// 9分割して使う
for (int i = 0; i < layoutCount; i++) {
final int xpos = i % 3;
final int ypos = i / 3;
T_ClippingContext cc = clippingContextListForMask.get(curClipIndex++);
cc.layoutChannelIndex = channelIndex;
csmRectF bounds = cc.layoutBounds;
bounds.setX(xpos / 3.0f);
bounds.setY(ypos / 3.0f);
bounds.setWidth(1.0f / 3.0f);
bounds.setHeight(1.0f / 3.0f);
cc.bufferIndex = renderTextureIndex;
}
}
// マスクの制限枚数を超えた場合の処理
else {
int count = usingClipCount - useClippingMaskMaxCount;
cubismLogError(
"not supported mask count : %d\n[Details] render texture count: %d\n, mask count : %d",
count,
renderTextureCount,
usingClipCount
);
// 開発モードの場合は停止させる。
assert false;
// 引き続き実行する場合、 SetupShaderProgramでオーバーアクセスが発生するので仕方なく適当に入れておく。
// もちろん描画結果はろくなことにならない。
for (int i = 0; i < layoutCount; i++) {
T_ClippingContext cc = clippingContextListForMask.get(curClipIndex++);
cc.layoutChannelIndex = 0;
csmRectF bounds = cc.layoutBounds;
bounds.setX(0.0f);
bounds.setY(0.0f);
bounds.setWidth(1.0f);
bounds.setHeight(1.0f);
cc.bufferIndex = 0;
}
}
}
}
}
@Override
public CubismVector2 getClippingMaskBufferSize() {
return clippingMaskBufferSize;
}
@Override
public void setClippingMaskBufferSize(float width, float height) {
clippingMaskBufferSize.set(width, height);
}
@Override
public int getRenderTextureCount() {
return renderTextureCount;
}
@Override
public CubismRenderer.CubismTextureColor getChannelFlagAsColor(int channelIndex) {
return channelColors.get(channelIndex);
}
/**
* 既にマスクを作っているかを確認する。
* 作っているようであれば該当するクリッピングマスクのインスタンスを返す。
* 作っていなければnullを返す。
*
* @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのリスト
* @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数
* @return 該当するクリッピングマスクが存在すればインスタンスを返し、なければnullを返す。
*/
public T_ClippingContext findSameClip(int[] drawableMasks, int drawableMaskCounts) {
// 作成済みClippingContextと一致するか確認
for (int i = 0; i < clippingContextListForMask.size(); i++) {
T_ClippingContext clipContext = clippingContextListForMask.get(i);
final int count = clipContext.clippingIdCount;
if (count != drawableMaskCounts) {
// 個数が違う場合は別物
continue;
}
int sameCount = 0;
// 同じIDを持つか確認。配列の数が同じなので、一致した個数が同じなら同じ物を持つとする。
for (int j = 0; j < count; j++) {
final int clipId = clipContext.clippingIdList[j];
for (int k = 0; k < count; k++) {
if (drawableMasks[k] == clipId) {
sameCount++;
break;
}
}
}
if (sameCount == count) {
return clipContext;
}
}
return null; // 見つからなかった。
}
/**
* マスクされる描画オブジェクト群全体を囲む矩形(モデル座標系)を計算する。
*
* @param model モデルのインスタンス
* @param clippingContext クリッピングマスクのコンテキスト
*/
public void calcClippedDrawTotalBounds(CubismModel model, T_ClippingContext clippingContext) {
// 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形
float clippedDrawTotalMinX = Float.MAX_VALUE;
float clippedDrawTotalMinY = Float.MAX_VALUE;
float clippedDrawTotalMaxX = -Float.MAX_VALUE;
float clippedDrawTotalMaxY = -Float.MAX_VALUE;
// このマスクが実際に必要か判定する。
// このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある。
final int clippedDrawCount = clippingContext.clippedDrawableIndexList.size();
for (int clippedDrawableIndex = 0; clippedDrawableIndex < clippedDrawCount; clippedDrawableIndex++) {
// マスクを使用する描画オブジェクトの描画される矩形を求める。
final int drawableIndex = clippingContext.clippedDrawableIndexList.get(clippedDrawableIndex);
final int drawableVertexCount = model.getDrawableVertexCount(drawableIndex);
float[] drawableVertices = model.getDrawableVertices(drawableIndex);
float minX = Float.MAX_VALUE;
float minY = Float.MAX_VALUE;
float maxX = -Float.MAX_VALUE;
float maxY = -Float.MAX_VALUE;
int loop = drawableVertexCount * VERTEX_STEP;
for (int pi = VERTEX_OFFSET; pi < loop; pi += VERTEX_STEP) {
float x = drawableVertices[pi];
float y = drawableVertices[pi + 1];
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
if (minX == Float.MAX_VALUE) {
continue; // 有効な点が1つも取れなかったのでスキップする
}
// 全体の矩形に反映
if (minX < clippedDrawTotalMinX) clippedDrawTotalMinX = minX;
if (maxX > clippedDrawTotalMaxX) clippedDrawTotalMaxX = maxX;
if (minY < clippedDrawTotalMinY) clippedDrawTotalMinY = minY;
if (maxY > clippedDrawTotalMaxY) clippedDrawTotalMaxY = maxY;
}
if (clippedDrawTotalMinX == Float.MAX_VALUE) {
clippingContext.isUsing = false;
csmRectF clippedDrawRect = clippingContext.allClippedDrawRect;
clippedDrawRect.setX(0.0f);
clippedDrawRect.setY(0.0f);
clippedDrawRect.setWidth(0.0f);
clippedDrawRect.setHeight(0.0f);
} else {
clippingContext.isUsing = true;
float w = clippedDrawTotalMaxX - clippedDrawTotalMinX;
float h = clippedDrawTotalMaxY - clippedDrawTotalMinY;
csmRectF clippedDrawRect = clippingContext.allClippedDrawRect;
clippedDrawRect.setX(clippedDrawTotalMinX);
clippedDrawRect.setY(clippedDrawTotalMinY);
clippedDrawRect.setWidth(w);
clippedDrawRect.setHeight(h);
}
}
/**
* 画面描画に使用するクリッピングマスクのリストを取得する。
*
* @return 画面描画に使用するクリッピングマスクのリスト
*/
public List<T_ClippingContext> getClippingContextListForDraw() {
return clippingContextListForDraw;
}
/**
* オフスクリーンフレームのインスタンス
*/
protected T_OffscreenSurface currentMaskBuffer;
/**
* マスクのクリアフラグの配列
*/
protected boolean[] clearedMaskBufferFlags;
/**
* カラーチャンネル(RGBA)のフラグのリスト(0:R, 1:G, 2:B, 3:A)
*/
protected final List<CubismRenderer.CubismTextureColor> channelColors = new ArrayList<>();
/**
* マスク用クリッピングコンテキストのリスト
*/
protected final List<T_ClippingContext> clippingContextListForMask = new ArrayList<>();
/**
* 描画用クリッピングコンテキストのリスト
*/
protected final List<T_ClippingContext> clippingContextListForDraw = new ArrayList<>();
/**
* クリッピングマスクのバッファサイズ初期値256
*/
protected final CubismVector2 clippingMaskBufferSize = new CubismVector2(256, 256);
/**
* 生成するレンダーテクスチャの枚数
*/
protected int renderTextureCount;
/**
* 一時計算用行列
*/
protected CubismMatrix44 tmpMatrix = CubismMatrix44.create();
/**
* マスク計算のための一時計算用行列
*/
protected CubismMatrix44 tmpMatrixForMask = CubismMatrix44.create();
/**
* 描画用の一時計算用行列
*/
protected CubismMatrix44 tmpMatrixForDraw = CubismMatrix44.create();
/**
* マスク配置計算用の一時計算用矩形
*/
protected csmRectF tmpBoundsOnModel = csmRectF.create();
}

View File

@@ -0,0 +1,385 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering;
import com.live2d.sdk.cubism.framework.math.CubismMatrix44;
import com.live2d.sdk.cubism.framework.model.CubismModel;
/**
* A renderer which processes drawing models.
* <p>
* Environment-dependent drawing instructions are written in subclasses that inherit from this class.
*/
public abstract class CubismRenderer {
/**
* レンダラーの種類
*/
public enum RendererType {
ANDROID,
UNKNOWN // 不明・未定義なレンダラー
}
/**
* Color blending mode
*/
public enum CubismBlendMode {
NORMAL, // 通常
ADDITIVE, // 加算
MULTIPLICATIVE, // 乗算
MASK // マスク
}
/**
* Data class for handling texture colors in RGBA.
*/
public static class CubismTextureColor {
public CubismTextureColor() {}
public CubismTextureColor(
float r,
float g,
float b,
float a
) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismTextureColor that = (CubismTextureColor) o;
if (Float.compare(that.r, r) != 0) return false;
if (Float.compare(that.g, g) != 0) return false;
if (Float.compare(that.b, b) != 0) return false;
return Float.compare(that.a, a) == 0;
}
@Override
public int hashCode() {
int result = (r != 0.0f ? Float.floatToIntBits(r) : 0);
result = 31 * result + (g != 0.0f ? Float.floatToIntBits(g) : 0);
result = 31 * result + (b != 0.0f ? Float.floatToIntBits(b) : 0);
result = 31 * result + (a != 0.0f ? Float.floatToIntBits(a) : 0);
return result;
}
/**
* Red channel
*/
public float r = 1.0f;
/**
* Green channel
*/
public float g = 1.0f;
/**
* Blue channel
*/
public float b = 1.0f;
/**
* Alpha channel
*/
public float a = 1.0f;
}
public static void delete() {
}
public static void staticRelease() {
}
/**
* Initialize this renderer.
* A model instance has the information which is required to initialize the renderer.
*
* @param model model instance
*/
public void initialize(CubismModel model) {
this.model = model;
}
/**
* レンダラーの初期化処理を実行する。<br>
* マスクバッファを2つ以上作成する場合はこのメソッドを使用する。第2引数に何も入れない場合のデフォルト値は1となる。
*
* @param model モデルのインスタンス
* @param maskBufferCount バッファの生成数
*/
abstract public void initialize(CubismModel model, int maskBufferCount);
public void close() {
model.close();
}
/**
* Draw the model.
* <p>
* Calling following methods before and after DoDrawModel() method is required.
* ・saveProfile()
* ・restoreProfile()
* These methods return the state of the renderer to the state before drawing the model.
*/
public void drawModel() {
if (getModel() == null) {
return;
}
/*
* Call the following methods before and after DoDrawModel() method.
* ・saveProfile()
* ・restoreProfile()
* These methods save and restore the renderer's drawing settings to the state immediately before the model is drawn.
*/
saveProfile();
doDrawModel();
restoreProfile();
}
/**
* Get the Model-View-Projection Matrix.
*
* @return Model-View-Projection Matrix
*/
public CubismMatrix44 getMvpMatrix() {
return mvpMatrix44;
}
/**
* Set the Model-View-Projection Matrix
*
* @param matrix4x4 Model-View-Projection Matrix
*/
public void setMvpMatrix(final CubismMatrix44 matrix4x4) {
mvpMatrix44.setMatrix(matrix4x4);
}
/**
* Set colors of a model.
* Each color is specified in the range 0.0~1.0. (1.0 is standard value)
*
* @param red value of red channel
* @param green value of green channel
* @param blue value of blue channel
* @param alpha value of alpha channel
*/
public void setModelColor(float red, float green, float blue, float alpha) {
modelColor.r = constrain(red);
modelColor.g = constrain(green);
modelColor.b = constrain(blue);
modelColor.a = constrain(alpha);
}
/**
* Set colors of a model.
*
* @param color CubismTextureColor instance
*/
public void setModelColor(CubismTextureColor color) {
modelColor.r = color.r;
modelColor.g = color.g;
modelColor.b = color.b;
modelColor.a = color.a;
}
/**
* Get the model's colors.
* Each color is specified in the range 0.0~1.0. (1.0 is standard value)
*
* @return the color information of RGBA.
*/
public CubismTextureColor getModelColor() {
return modelColor;
}
/**
* 透明度を考慮したモデルの色を計算する。
*
* @param opacity 透明度
* @return RGBAのカラー情報
*/
public CubismTextureColor getModelColorWithOpacity(float opacity) {
tmpModelColor.r = modelColor.r;
tmpModelColor.g = modelColor.g;
tmpModelColor.b = modelColor.b;
tmpModelColor.a = modelColor.a;
tmpModelColor.a *= opacity;
if (isPremultipliedAlpha()) {
tmpModelColor.r *= tmpModelColor.a;
tmpModelColor.g *= tmpModelColor.a;
tmpModelColor.b *= tmpModelColor.a;
}
return tmpModelColor;
}
/**
* Get the validity or invalidity of multiplied alpha.
*
* @return If multiplied alpha is valid, return true.
*/
public boolean isPremultipliedAlpha() {
return isPremultipliedAlpha;
}
/**
* Set the validity or invalidity of multiplied alpha.
*
* @param enable a state of premultiplied alpha.
*/
public void isPremultipliedAlpha(boolean enable) {
isPremultipliedAlpha = enable;
}
/**
* Get the validity or invalid of culling.
*
* @return If culling is valid, return true.
*/
public boolean isCulling() {
return isCulling;
}
/**
* Set the validity or invalidity of culling.
*
* @param enable a state of culling.
*/
public void isCulling(boolean enable) {
isCulling = enable;
}
/**
* Get the parameters for anisotropic filtering of textures.
*
* @return the parameters for anisotropic filtering of textures
*/
public float getAnisotropy() {
return anisotropy;
}
/**
* Set the parameters for anisotropic filtering of textures.
* The influence of parameter values depends on the implementation of the renderer.
*
* @param value parameter value
*/
public void setAnisotropy(float value) {
anisotropy = value;
}
/**
* Get the rendered model.
*
* @return the rendered model
*/
public CubismModel getModel() {
return model;
}
/**
* Change the mask rendering method.
* In the case of false, the mask is divided into a single texture and rendered(default is this).
* This method is high speed, but the upper limit of the number of masks is limited to 36 and the quality is also rough.
* In the case of true, the necessary mask is redrawn before drawing parts.
* The rendering quality is high, but the drawing processing load increase.
*
* @param isHigh rendering quality
*/
public void isUsingHighPrecisionMask(boolean isHigh) {
useHighPrecisionMask = isHigh;
}
/**
* Get the mask rendering method.
*
* @return the mask rendering method
*/
public boolean isUsingHighPrecisionMask() {
return useHighPrecisionMask;
}
/**
* Constructor
*/
protected CubismRenderer() {
mvpMatrix44 = CubismMatrix44.create();
mvpMatrix44.loadIdentity();
}
/**
* the model drawing inplementation.
*/
protected abstract void doDrawModel();
/**
* Save the state of the renderer just before drawing the model.
*/
protected abstract void saveProfile();
/**
* Restore the state of the renderer just before drawing the model.
*/
protected abstract void restoreProfile();
/**
* Limit a value to a range of 0.0 to 1.0.
*
* @param target value to limit
* @return value after limit processing
*/
private float constrain(float target) {
if (target < 0.0f) {
return 0.0f;
} else return Math.min(target, 1.0f);
}
/**
* Model-View-Projection Matrix
*/
private final CubismMatrix44 mvpMatrix44;
/**
* the color which model has.(RGBA)
*/
private final CubismTextureColor modelColor = new CubismTextureColor();
/**
* whether culling is valid
*/
private boolean isCulling;
/**
* whether premultiplied alpha is valid
*/
private boolean isPremultipliedAlpha;
/**
* parameters for anisotropic filtering of textures
*/
private float anisotropy;
/**
* the model to be rendered
*/
private CubismModel model;
/**
* If this is false, the masks are drawn together. If this is true, the masks are redrawn for each part drawing.
*/
private boolean useHighPrecisionMask;
/**
* 計算用の色格納用インスタンス
*/
private final CubismTextureColor tmpModelColor = new CubismTextureColor();
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering;
import com.live2d.sdk.cubism.framework.math.CubismVector2;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.type.csmRectF;
public interface ICubismClippingManager {
/**
* 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4
*/
int COLOR_CHANNEL_COUNT = 4;
/**
* 通常のフレームバッファ1枚あたりのマスク最大数
*/
int CLIPPING_MASK_MAX_COUNT_ON_DEFAULT = 36;
/**
* フレームバッファが2枚以上ある場合のフレームバッファ1枚あたりのマスク最大数
*/
int CLIPPING_MASK_MAX_COUNT_ON_MULTI_RENDER_TEXTURE = 32;
/**
* マネージャーの初期化処理
* クリッピングマスクを使う描画オブジェクトの登録を行う。
*
* @param type レンダラーの種類
* @param model モデルのインスタンス
* @param maskBufferCount バッファの生成数
*/
void initialize(
CubismRenderer.RendererType type,
CubismModel model,
int maskBufferCount
);
/**
* 高精細マスク処理用の行列を計算する。
*
* @param model モデルのインスタンス
* @param isRightHanded 処理が右手系かどうか。右手系ならtrue
*/
void setupMatrixForHighPrecision(CubismModel model, boolean isRightHanded);
/**
* マスク作成・描画用の行列を作成する。
*
* @param isRightHanded 座標を右手系として扱うかどうか。右手系として扱うならtrue
* @param layoutBoundsOnTex01 マスクを収める領域
* @param scaleX 描画オブジェクトのX方向への伸縮率
* @param scaleY 描画オブジェクトのY方向への伸縮率
*/
void createMatrixForMask(
boolean isRightHanded,
csmRectF layoutBoundsOnTex01,
float scaleX,
float scaleY
);
/**
* クリッピングコンテキストを配置するレイアウト。
* 1つのレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする。
* マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2, 2, 1, 1と配置する。
*
* @param usingClipCount 配置するクリッピングコンテキストの数
*/
void setupLayoutBounds(int usingClipCount);
/**
* クリッピングマスクバッファのサイズを取得する。
*
* @return クリッピングマスクバッファのサイズ
*/
CubismVector2 getClippingMaskBufferSize();
/**
* クリッピングマスクバッファのサイズを設定する。
*
* @param width クリッピングマスクバッファの幅
* @param height クリッピングマスクバッファの高さ
*/
void setClippingMaskBufferSize(float width, float height);
/**
* このバッファのレンダーテクスチャの枚数を取得する。
*
* @return このバッファのレンダーテクスチャの枚数
*/
int getRenderTextureCount();
/**
* カラーチャンネルRGBAのフラグを取得する。
*
* @param channelIndex カラーチャンネルRGBAの番号0:R, 1:G, 2:B, 3:A
* @return カラーチャンネルのフラグ
*/
CubismRenderer.CubismTextureColor getChannelFlagAsColor(int channelIndex);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering.android;
import com.live2d.sdk.cubism.framework.rendering.ACubismClippingContext;
import com.live2d.sdk.cubism.framework.rendering.ICubismClippingManager;
/**
* Context of Clipping Mask
*/
public class CubismClippingContextAndroid extends ACubismClippingContext {
/**
* コンストラクタ
*
* @param manager このクリッピングコンテキストを保持するマネージャーのインスタンス
* @param clipingDrawableIndices クリッピングマスクのIDの配列
* @param clipCount クリッピングマスクの数
*/
public CubismClippingContextAndroid(
ICubismClippingManager manager,
int[] clipingDrawableIndices,
int clipCount
) {
super(manager, clipingDrawableIndices, clipCount);
}
/**
* このマスクを管理するマネージャーのインスタンスを返す。
*
* @return クリッピングマネージャーのインスタンス。
* @throws ClassCastException 具象クラスへのキャストに失敗しました。
*/
@Override
public CubismClippingManagerAndroid getClippingManager() {
// キャストに失敗する可能性があります。その場合は内部でClassCastExceptionが送出されます。
return (CubismClippingManagerAndroid) owner;
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering.android;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.rendering.ACubismClippingManager;
import com.live2d.sdk.cubism.framework.type.csmRectF;
import java.io.Closeable;
import static android.opengl.GLES20.*;
/**
* クリッピングマスクの処理を実行するクラス
*/
class CubismClippingManagerAndroid extends ACubismClippingManager<
CubismClippingContextAndroid,
CubismOffscreenSurfaceAndroid
> implements Closeable {
/**
* コンストラクタ
*/
public CubismClippingManagerAndroid() {
super();
}
/**
* クリッピングコンテキストを作成する。モデル描画時に実行する。
*
* @param model モデルのインスタンス
* @param renderer レンダラーのインスタンス
* @param lastFBO フレームバッファ
* @param lastViewport ビューポート
*/
public void setupClippingContext(CubismModel model, CubismRendererAndroid renderer, int[] lastFBO, int[] lastViewport) {
// Prepare all clipping.
// Set only once when using the same clip (or a group of clips if there are multiple clips).
int usingClipCount = 0;
for (int i = 0; i < clippingContextListForMask.size(); i++) {
CubismClippingContextAndroid clipContext = clippingContextListForMask.get(i);
// Calculate the rectangle that encloses the entire group of drawing objects that use this clip.
calcClippedDrawTotalBounds(model, clipContext);
if (clipContext.isUsing) {
// Count as in use.
usingClipCount++;
}
}
if (!(usingClipCount > 0)) {
return;
}
// Process of creating mask.
// Set up a viewport with the same size as the generated MaskBuffer.
glViewport(0, 0, (int) clippingMaskBufferSize.x, (int) clippingMaskBufferSize.y);
// 後の計算のためにインデックスの最初をセットする。
currentMaskBuffer = renderer.getMaskBuffer(0);
// マスク描画処理
currentMaskBuffer.beginDraw(lastFBO);
// バッファをクリアする
renderer.preDraw();
// Determine the layout of each mask.
setupLayoutBounds(usingClipCount);
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる。
if (clearedMaskBufferFlags.length != renderTextureCount) {
clearedMaskBufferFlags = new boolean[renderTextureCount];
}
// マスクのクリアフラグを毎フレーム開始時に初期化する。
else {
for (int i = 0; i < renderTextureCount; i++) {
clearedMaskBufferFlags[i] = false;
}
}
// ---------- Mask Drawing Process -----------
// Actually generate the masks.
// Determine how to layout and draw all the masks, and store them in ClipContext and ClippedDrawContext.
for (int j = 0; j < clippingContextListForMask.size(); j++) {
CubismClippingContextAndroid clipContext = clippingContextListForMask.get(j);
// The enclosing rectangle in logical coordinates of all drawing objects that use this mask.
csmRectF allClippedDrawRect = clipContext.allClippedDrawRect;
// Fit the mask in here.
csmRectF layoutBoundsOnTex01 = clipContext.layoutBounds;
final float margin = 0.05f;
// clipContextに設定したオフスクリーンサーフェスをインデックスで取得
final CubismOffscreenSurfaceAndroid clipContextOffscreenSurface = renderer.getMaskBuffer(clipContext.bufferIndex);
// 現在のオフスクリーンサーフェスがclipContextのものと異なる場合
if (currentMaskBuffer != clipContextOffscreenSurface) {
currentMaskBuffer.endDraw();
currentMaskBuffer = clipContextOffscreenSurface;
// マスク用RenderTextureをactiveにセット。
currentMaskBuffer.beginDraw(lastFBO);
// バッファをクリアする。
renderer.preDraw();
}
// Use a rectangle on the model coordinates with margins as appropriate.
tmpBoundsOnModel.setRect(allClippedDrawRect);
tmpBoundsOnModel.expand(
allClippedDrawRect.getWidth() * margin,
allClippedDrawRect.getHeight() * margin
);
// ######## It is best to keep the size to a minimum, rather than using the entire allocated space.
// Find the formula for the shader. If rotation is not taken into account, the formula is as follows.
// movePeriod' = movePeriod * scaleX + offX [[ movePeriod' = (movePeriod - tmpBoundsOnModel.movePeriod)*scale + layoutBoundsOnTex01.movePeriod ]]
float scaleX = layoutBoundsOnTex01.getWidth() / tmpBoundsOnModel.getWidth();
float scaleY = layoutBoundsOnTex01.getHeight() / tmpBoundsOnModel.getHeight();
// Calculate the matrix to be used for mask generation.
createMatrixForMask(false, layoutBoundsOnTex01, scaleX, scaleY);
clipContext.matrixForMask.setMatrix(tmpMatrixForMask);
clipContext.matrixForDraw.setMatrix(tmpMatrixForDraw);
// 実際の描画を行う。
final int clipDrawCount = clipContext.clippingIdCount;
for (int i = 0; i < clipDrawCount; i++) {
final int clipDrawIndex = clipContext.clippingIdList[i];
// If vertex information is not updated and reliable, pass drawing.
if (!model.getDrawableDynamicFlagVertexPositionsDidChange(clipDrawIndex)) {
continue;
}
renderer.isCulling(model.getDrawableCulling(clipDrawIndex));
// マスクがクリアされていないなら処理する。
if (!clearedMaskBufferFlags[clipContext.bufferIndex]) {
// マスクをクリアする。
// (仮仕様) 1が無効描かれない領域、0が有効描かれる領域。シェーダーCd*Csで0に近い値をかけてマスクを作る。1をかけると何も起こらない
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
clearedMaskBufferFlags[clipContext.bufferIndex] = true;
}
// Apply this special transformation to draw it.
// Switching channel is also needed.(A,R,G,B)
renderer.setClippingContextBufferForMask(clipContext);
renderer.drawMeshAndroid(
model,
clipDrawIndex
);
}
}
// --- Post Processing ---
// Return the drawing target
currentMaskBuffer.endDraw();
renderer.setClippingContextBufferForMask(null);
glViewport(lastViewport[0], lastViewport[1], lastViewport[2], lastViewport[3]);
}
}

View File

@@ -0,0 +1,123 @@
package com.live2d.sdk.cubism.framework.rendering.android;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
/**
* Drawableの情報を格納するバッファをキャッシュし保持するクラス。
*/
class CubismDrawableInfoCachesHolder {
public CubismDrawableInfoCachesHolder(CubismModel model) {
final int drawableCount = model.getDrawableCount();
final int[] renderOrder = model.getDrawableRenderOrders();
final int[] sortedDrawableIndexList = new int[drawableCount];
// Sort the index by drawing order
for (int i = 0; i < drawableCount; i++) {
final int order = renderOrder[i];
sortedDrawableIndexList[order] = i;
}
vertexArrayCaches = new FloatBuffer[drawableCount];
uvArrayCaches = new FloatBuffer[drawableCount];
indexArrayCaches = new ShortBuffer[drawableCount];
for (int i = 0; i < drawableCount; i++) {
final int drawableIndex = sortedDrawableIndexList[i];
// Vertex Array
{
float[] vertexArray = model.getDrawableVertices(drawableIndex);
ByteBuffer bb = ByteBuffer.allocateDirect(vertexArray.length * 4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer buffer = bb.asFloatBuffer();
vertexArrayCaches[drawableIndex] = buffer;
}
// UV Array
{
float[] uvArray = model.getDrawableVertexUvs(drawableIndex);
ByteBuffer bb = ByteBuffer.allocateDirect(uvArray.length * 4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer buffer = bb.asFloatBuffer();
uvArrayCaches[drawableIndex] = buffer;
}
// Index Array
{
short[] indexArray = model.getDrawableVertexIndices(drawableIndex);
ByteBuffer bb = ByteBuffer.allocateDirect(indexArray.length * 4);
bb.order(ByteOrder.nativeOrder());
ShortBuffer buffer = bb.asShortBuffer();
indexArrayCaches[drawableIndex] = buffer;
}
}
}
/**
* Drawableの頂点をキャッシュされたバッファに入れて返す。
*
* @param drawableIndex 取得したいDrawableのインデックス
* @param drawableVertices Drawableの頂点が格納された配列
* @return 頂点バッファ
*/
public FloatBuffer setUpVertexArray(int drawableIndex, float[] drawableVertices) {
FloatBuffer vertexArray = vertexArrayCaches[drawableIndex];
vertexArray.clear();
vertexArray.put(drawableVertices);
vertexArray.position(0);
return vertexArray;
}
/**
* DrawableのUV情報をキャッシュされたバッファに入れて返す。
*
* @param drawableIndex 取得したいDrawableのインデックス
* @param drawableVertexUvs DrawableのUV情報が入った配列
* @return UV情報バッファ
*/
public FloatBuffer setUpUvArray(int drawableIndex, float[] drawableVertexUvs) {
FloatBuffer uvArray = uvArrayCaches[drawableIndex];
uvArray.clear();
uvArray.put(drawableVertexUvs);
uvArray.position(0);
return uvArray;
}
/**
* Drawableの頂点に対するポリゴンの対応番号をキャッシュされたバッファに入れて返す。
*
* @param drawableIndex 取得したいDrawableのインデックス
* @param drawableIndices Drawableの頂点に対するポリゴンの対応番号の配列
* @return 頂点に対するポリゴンの対応番号のバッファ
*/
public ShortBuffer setUpIndexArray(int drawableIndex, short[] drawableIndices) {
ShortBuffer indexArray = indexArrayCaches[drawableIndex];
indexArray.clear();
indexArray.put(drawableIndices);
indexArray.position(0);
return indexArray;
}
/**
* Drawableの頂点のキャッシュ配列
*/
private final FloatBuffer[] vertexArrayCaches;
/**
* DrawableのUV情報のキャッシュ配列
*/
private final FloatBuffer[] uvArrayCaches;
/**
* Drawableの頂点に対するポリゴンの対応番号のキャッシュ配列
*/
private final ShortBuffer[] indexArrayCaches;
}

View File

@@ -0,0 +1,311 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering.android;
import com.live2d.sdk.cubism.framework.math.CubismVector2;
import java.nio.IntBuffer;
import java.util.Arrays;
import static android.opengl.GLES20.*;
/**
* This class is for drawing offscreen.
**/
public class CubismOffscreenSurfaceAndroid {
/**
* Constructor
*/
public CubismOffscreenSurfaceAndroid() {}
/**
* Copy constructor
*
* @param offscreenSurface offscreen surface buffer
*/
public CubismOffscreenSurfaceAndroid(CubismOffscreenSurfaceAndroid offscreenSurface) {
renderTexture = Arrays.copyOf(offscreenSurface.renderTexture, offscreenSurface.renderTexture.length);
colorBuffer = Arrays.copyOf(offscreenSurface.colorBuffer, offscreenSurface.colorBuffer.length);
oldFBO = Arrays.copyOf(offscreenSurface.oldFBO, offscreenSurface.oldFBO.length);
bufferWidth = offscreenSurface.bufferWidth;
bufferHeight = offscreenSurface.bufferHeight;
isColorBufferInherited = offscreenSurface.isColorBufferInherited;
}
/**
* Begin drawing to the specific drawing target.
*
* @param restoreFBO If it is not "null", EndDraw will run glBindFrameBuffer this value.
**/
public void beginDraw(int[] restoreFBO) {
if (renderTexture == null) {
return;
}
// Remember the back buffer surface.
if (restoreFBO == null) {
glGetIntegerv(GL_FRAMEBUFFER_BINDING, IntBuffer.wrap(oldFBO));
} else {
oldFBO = restoreFBO;
}
// Set the RenderTexture for the mask to active.
glBindFramebuffer(GL_FRAMEBUFFER, renderTexture[0]);
}
/**
* Begin drawing to the specific drawing target.
* <p>
* This method reproduces default argument of C++. The users can use this method instead of specifying null as an argument.
* </p>
*/
public void beginDraw() {
beginDraw(null);
}
/**
* Finish drawing.
**/
public void endDraw() {
if (renderTexture == null) {
return;
}
// Return the drawing target.
glBindFramebuffer(GL_FRAMEBUFFER, oldFBO[0]);
}
/**
* Clear the rendering target.
* Note: Call this after BeginDraw().
*
* @param r red(0.0~1.0)
* @param g green(0.0~1.0)
* @param b blue(0.0~1.0)
* @param a α(0.0~1.0)
*/
public void clear(final float r, final float g, final float b, final float a) {
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT);
}
/**
* Create CubismOffscreenSurface.
* <p>
* This method reproduces default argument of C++. The users can use this method instead of specifying null as colorBuffer(2rd) argument.
* </p>
*
* @param displayBufferSize buffer size(Vector type)
*/
public void createOffscreenSurface(CubismVector2 displayBufferSize) {
createOffscreenSurface((int) displayBufferSize.x, (int) displayBufferSize.y, null);
}
/**
* Create CubismOffscreenSurface.
*
* @param displayBufferSize buffer size
* @param colorBuffer if non-zero, use colorBuffer as pixel storage area.
*/
public void createOffscreenSurface(final CubismVector2 displayBufferSize, final int[] colorBuffer) {
createOffscreenSurface((int) displayBufferSize.x, (int) displayBufferSize.y, colorBuffer);
}
/**
* Create CubismOffscreenSurface.
* <p>
* This method reproduces default argument of C++. The users can use this method instead of specifying null as colorBuffer(3rd) argument.
* </p>
*
* @param displayBufferWidth buffer width
* @param displayBufferHeight buffer height
*/
public void createOffscreenSurface(int displayBufferWidth, int displayBufferHeight) {
createOffscreenSurface(displayBufferWidth, displayBufferHeight, null);
}
/**
* Create CubismOffscreenSurface.
*
* @param displayBufferWidth buffer width
* @param displayBufferHeight buffer height
* @param colorBuffer if non-zero, use colorBuffer as pixel storage area.
*/
public void createOffscreenSurface(final int displayBufferWidth, final int displayBufferHeight, final int[] colorBuffer) {
// いったん削除
destroyOffscreenSurface();
int[] ret = new int[1];
// Create new offscreen surface
if (colorBuffer == null) {
this.colorBuffer = new int[1];
glGenTextures(1, this.colorBuffer, 0);
glBindTexture(GL_TEXTURE_2D, this.colorBuffer[0]);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
displayBufferWidth,
displayBufferHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
null);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
isColorBufferInherited = false;
}
// Use the designated color buffer.
else {
this.colorBuffer = colorBuffer;
isColorBufferInherited = true;
}
int[] tmpFBO = new int[1];
glGetIntegerv(GL_FRAMEBUFFER_BINDING, tmpFBO, 0);
glGenFramebuffers(1, ret, 0);
glBindFramebuffer(GL_FRAMEBUFFER, ret[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this.colorBuffer[0], 0);
glBindFramebuffer(GL_FRAMEBUFFER, tmpFBO[0]);
this.renderTexture = new int[1];
this.renderTexture[0] = ret[0];
bufferWidth = displayBufferWidth;
bufferHeight = displayBufferHeight;
}
/**
* Destroy CubismOffscreenSurface
*/
public void destroyOffscreenSurface() {
if (!isColorBufferInherited && (colorBuffer != null)) {
glDeleteTextures(1, colorBuffer, 0);
colorBuffer = null;
}
if (renderTexture != null) {
glDeleteFramebuffers(1, renderTexture, 0);
renderTexture = null;
}
}
/**
* レンダーテクスチャのアドレスintの配列型を取得する。
*
* @return レンダーテクスチャのアドレス
*/
public int[] getRenderTexture() {
return renderTexture;
}
/**
* Get color buffer.
*
* @return color buffer
*/
public int[] getColorBuffer() {
return colorBuffer;
}
/**
* Get buffer width
*
* @return buffer width
*/
public int getBufferWidth() {
return bufferWidth;
}
/**
* Get buffer height.
*
* @return buffer height
*/
public int getBufferHeight() {
return bufferHeight;
}
/**
* Whether render texture is valid.
*
* @return If it is valid, return true
*/
public boolean isValid() {
return renderTexture != null;
}
/**
* Whether buffer size is the same.
*
* @param bufferSize buffer size
* @return Whether buffer size is the same
*/
public boolean isSameSize(final CubismVector2 bufferSize) {
int width = (int) bufferSize.x;
int height = (int) bufferSize.y;
return (width == bufferWidth && height == bufferHeight);
}
/**
* Whether buffer size is the same.
*
* @param width buffer width
* @param height buffer height
* @return Whether buffer size is the same
*/
public boolean isSameSize(final int width, final int height) {
return (width == bufferWidth && height == bufferHeight);
}
/**
* texture as rendering target. It is called frame buffer.
*/
private int[] renderTexture;
/**
* color buffer
*/
private int[] colorBuffer = new int[1];
/**
* old frame buffer
*/
private int[] oldFBO = new int[1];
/**
* width specified at Create() method
*/
private int bufferWidth;
/**
* height specified at Create() method
*/
private int bufferHeight;
/**
* Whether the color buffer is the one set by the argument
*/
private boolean isColorBufferInherited;
}

View File

@@ -0,0 +1,520 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering.android;
import android.opengl.GLES11Ext;
import com.live2d.sdk.cubism.framework.math.CubismVector2;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.rendering.CubismRenderer;
import com.live2d.sdk.cubism.framework.utils.CubismDebug;
import java.nio.ShortBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static android.opengl.GLES20.*;
import static com.live2d.sdk.cubism.framework.CubismFrameworkConfig.CSM_DEBUG;
/**
* The class that implements drawing instructions for Android.
*/
public class CubismRendererAndroid extends CubismRenderer {
/**
* Create the renderer instance for Android platform.
*
* @return renderer instance
*/
public static CubismRenderer create() {
return new CubismRendererAndroid();
}
/**
* Release static resources that this renderer keeps.
*/
public static void staticRelease() {
CubismRendererAndroid.doStaticRelease();
}
/**
* Tegra processor support.
* Enable/Disable drawing by extension method.
*
* @param extMode Whether to draw using the extended method
* @param extPAMode Whether to enable the PA setting for the extended method
*/
public static void setExtShaderMode(boolean extMode, boolean extPAMode) {
CubismShaderAndroid.setExtShaderMode(extMode, extPAMode);
CubismShaderAndroid.deleteInstance();
}
/**
* Android-Tegra support. Reload shader programs.
*/
public static void reloadShader() {
CubismShaderAndroid.deleteInstance();
}
@Override
public void initialize(CubismModel model) {
initialize(model, 1);
}
@Override
public void initialize(CubismModel model, int maskBufferCount) {
// 頂点情報をキャッシュする。
drawableInfoCachesHolder = new CubismDrawableInfoCachesHolder(model);
if (model.isUsingMasking()) {
// マスクバッファの枚数として、0または負の値が指定されている場合は強制的に1枚と設定し、警告ログを出力する。
// Webと違いCubismOffscreenSurfaceの配列を作成するため、こちらで不正値を検知し修正する。
if (maskBufferCount < 1) {
maskBufferCount = 1;
CubismDebug.cubismLogWarning("The number of render textures must be an integer greater than or equal to 1. Set the number of render textures to 1.");
}
// Initialize clipping mask and buffer preprocessing method
clippingManager = new CubismClippingManagerAndroid();
clippingManager.initialize(
RendererType.ANDROID,
model,
maskBufferCount
);
offscreenSurfaces = new CubismOffscreenSurfaceAndroid[maskBufferCount];
for (int i = 0; i < maskBufferCount; i++) {
CubismOffscreenSurfaceAndroid offscreenSurface = new CubismOffscreenSurfaceAndroid();
offscreenSurface.createOffscreenSurface(clippingManager.getClippingMaskBufferSize(), null);
offscreenSurfaces[i] = offscreenSurface;
}
}
sortedDrawableIndexList = new int[model.getDrawableCount()];
super.initialize(model);
}
@Override
public void close() {
super.close();
if (clippingManager != null) {
clippingManager.close();
}
if (offscreenSurfaces != null) {
for (int i = 0; i < offscreenSurfaces.length; i++) {
if (offscreenSurfaces[i].isValid()) {
offscreenSurfaces[i].destroyOffscreenSurface();
}
}
}
drawableInfoCachesHolder = null;
}
/**
* Bind processing of OpenGL textures.
*
* @param modelTextureIndex number of the model texture to set
* @param glTextureIndex number of the OpenGL texture to bind
*/
public void bindTexture(int modelTextureIndex, int glTextureIndex) {
textures.put(modelTextureIndex, glTextureIndex);
areTexturesChanged = true;
}
/**
* Get textures list bound to OpenGL.
*
* @return textures list
*/
public Map<Integer, Integer> getBoundTextures() {
if (areTexturesChanged) {
cachedImmutableTextures = Collections.unmodifiableMap(textures);
areTexturesChanged = false;
}
return cachedImmutableTextures;
}
/**
* Get the size of clipping mask buffer.
*
* @return size of clipping mask buffer
*/
private CubismVector2 getClippingMaskBufferSize() {
return clippingManager.getClippingMaskBufferSize();
}
/**
* Set the size of clipping mask buffer.
* This method's processing cost is high because the MaskBuffer for the mask is destroyed and recreated.
*
* @param width width of MaskBufferSize
* @param height height of MaskBufferSize
*/
public void setClippingMaskBufferSize(final float width, final float height) {
if (clippingManager == null) {
return;
}
// インスタンス破棄前にレンダーテクスチャの数を保存
final int renderTextureCount = this.clippingManager.getRenderTextureCount();
// Destroy and recreate instances to change the size of MaskBuffer
clippingManager = new CubismClippingManagerAndroid();
clippingManager.setClippingMaskBufferSize(width, height);
CubismModel model = getModel();
clippingManager.initialize(
RendererType.ANDROID,
model,
renderTextureCount
);
}
/**
* レンダーテクスチャの枚数を取得する。
*
* @return レンダーテクスチャの枚数
*/
public int getRenderTextureCount() {
return clippingManager.getRenderTextureCount();
}
/**
* Draw the drawing objects (ArtMesh). <br>
* Both polygon mesh and the texture number is given to this method.
*
* @param model number of the drawed texture
* @param index index of the drawing object
*/
protected void drawMeshAndroid(
final CubismModel model,
final int index
) {
if (!CSM_DEBUG) {
// If the texture referenced by the model is not bound, skip drawing.
if (textures.get(model.getDrawableTextureIndex(index)) == null) {
return;
}
}
// Enabling/disabling culling
if (isCulling()) {
glEnable(GL_CULL_FACE);
} else {
glDisable(GL_CULL_FACE);
}
// In Cubism3 OpenGL, CCW becomes surface for both masks and art meshes.
glFrontFace(GL_CCW);
// マスク生成時
if (isGeneratingMask()) {
CubismShaderAndroid.getInstance().setupShaderProgramForMask(this, model, index);
} else {
CubismShaderAndroid.getInstance().setupShaderProgramForDraw(this, model, index);
}
// Draw the prygon mesh
final int indexCount = model.getDrawableVertexIndexCount(index);
final ShortBuffer indexArrayBuffer = drawableInfoCachesHolder.setUpIndexArray(
index,
model.getDrawableVertexIndices(index)
);
glDrawElements(
GL_TRIANGLES,
indexCount,
GL_UNSIGNED_SHORT,
indexArrayBuffer
);
// post-processing
glUseProgram(0);
setClippingContextBufferForDraw(null);
setClippingContextBufferForMask(null);
}
// This is only used by 'drawMeshAndroid' method.
// Avoid creating a new CubismTextureColor instance.
private final CubismTextureColor modelColorRGBA = new CubismTextureColor();
@Override
protected void doDrawModel() {
final CubismModel model = getModel();
// In the case of clipping mask and buffer preprocessing method
if (clippingManager != null) {
preDraw();
// If offscreen frame buffer size is different from clipping mask buffer size, recreate it.
for (int i = 0; i < clippingManager.getRenderTextureCount(); i++) {
CubismOffscreenSurfaceAndroid offscreenSurface = offscreenSurfaces[i];
if (!offscreenSurface.isSameSize(clippingManager.getClippingMaskBufferSize())) {
offscreenSurface.createOffscreenSurface(clippingManager.getClippingMaskBufferSize(), null);
}
}
if (isUsingHighPrecisionMask()) {
clippingManager.setupMatrixForHighPrecision(getModel(), false);
} else {
clippingManager.setupClippingContext(
getModel(),
this,
rendererProfile.lastFBO,
rendererProfile.lastViewport
);
}
}
// preDraw() method is called twice.
preDraw();
final int drawableCount = model.getDrawableCount();
final int[] renderOrder = model.getDrawableRenderOrders();
// Sort the index by drawing order
for (int i = 0; i < drawableCount; i++) {
final int order = renderOrder[i];
sortedDrawableIndexList[order] = i;
}
// Draw process
for (int i = 0; i < drawableCount; i++) {
final int drawableIndex = sortedDrawableIndexList[i];
// If Drawable is not in the display state, the process is passed.
if (!model.getDrawableDynamicFlagIsVisible(drawableIndex)) {
continue;
}
// Set clipping mask
CubismClippingContextAndroid clipContext = (clippingManager != null)
? clippingManager.getClippingContextListForDraw().get(drawableIndex)
: null;
// マスクを描く必要がある
if (clipContext != null && isUsingHighPrecisionMask()) {
// 描くことになっていた
if (clipContext.isUsing) {
// 生成したOffscreenSurfaceと同じサイズでビューポートを設定
glViewport(0, 0, (int) clippingManager.getClippingMaskBufferSize().x, (int) clippingManager.getClippingMaskBufferSize().y);
// バッファをクリアする
preDraw();
// マスク描画処理
// マスク用RenderTextureをactiveにセット
getMaskBuffer(clipContext.bufferIndex).beginDraw(rendererProfile.lastFBO);
// マスクをクリアする。
// 1が無効描かれない領域、0が有効描かれる領域。シェーダーでCd*Csで0に近い値をかけてマスクを作る。1をかけると何も起こらない。
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
final int clipDrawCount = clipContext.clippingIdCount;
for (int index = 0; index < clipDrawCount; index++) {
final int clipDrawIndex = clipContext.clippingIdList[index];
// 頂点情報が更新されておらず、信頼性がない場合は描画をパスする
if (!getModel().getDrawableDynamicFlagVertexPositionsDidChange(clipDrawIndex)) {
continue;
}
isCulling(getModel().getDrawableCulling(clipDrawIndex));
// 今回専用の変換を適用して描く
// チャンネルも切り替える必要があるA,R,G,B
setClippingContextBufferForMask(clipContext);
drawMeshAndroid(model, clipDrawIndex);
}
// --- 後処理 ---
for (int j = 0; j < clippingManager.getRenderTextureCount(); j++) {
offscreenSurfaces[j].endDraw();
setClippingContextBufferForMask(null);
glViewport(
rendererProfile.lastViewport[0],
rendererProfile.lastViewport[1],
rendererProfile.lastViewport[2],
rendererProfile.lastViewport[3]
);
}
}
// クリッピングマスクをセットする
setClippingContextBufferForDraw(clipContext);
isCulling(model.getDrawableCulling(drawableIndex));
drawMeshAndroid(model, drawableIndex);
}
postDraw();
}
@Override
protected void saveProfile() {
rendererProfile.save();
}
@Override
protected void restoreProfile() {
rendererProfile.restore();
}
/**
* Release OpenGLES2 static shader programs.
*/
static void doStaticRelease() {
CubismShaderAndroid.deleteInstance();
}
/**
* Get the clipping context to draw to the mask texture
*
* @return the clipping context to draw to the mask texture
*/
CubismClippingContextAndroid getClippingContextBufferForMask() {
return clippingContextBufferForMask;
}
/**
* Set the clipping context to draw to the mask texture
*
* @param clip clipping context to draw to the mask texture
*/
void setClippingContextBufferForMask(CubismClippingContextAndroid clip) {
clippingContextBufferForMask = clip;
}
/**
* Get the clipping context to draw on display
*
* @return the clipping context to draw on display
*/
CubismClippingContextAndroid getClippingContextBufferForDraw() {
return clippingContextBufferForDraw;
}
/**
* Set the clipping context to draw on display
*
* @param clip the clipping context to draw on display
*/
void setClippingContextBufferForDraw(CubismClippingContextAndroid clip) {
clippingContextBufferForDraw = clip;
}
/**
* Get the offscreen frame buffer
*
* @return the offscreen frame buffer
*/
CubismOffscreenSurfaceAndroid getMaskBuffer(int index) {
return offscreenSurfaces[index];
}
CubismDrawableInfoCachesHolder getDrawableInfoCachesHolder() {
return drawableInfoCachesHolder;
}
/**
* Additional proccesing at the start of drawing
* This method implements the necessary processing for the clipping mask before drawing the model
*/
void preDraw() {
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glColorMask(true, true, true, true);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// If the buffer has been bound before, it needs to be destroyed
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Anisotropic filtering. If it is not supported, do not set it
if (getAnisotropy() >= 1.0f) {
for (Map.Entry<Integer, Integer> entry : textures.entrySet()) {
glBindTexture(GL_TEXTURE_2D, entry.getValue());
glTexParameterf(GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_MAX_ANISOTROPY_EXT, getAnisotropy());
}
}
}
/**
* Additinal processing after drawing is completed.
*/
void postDraw() {
}
/**
* テクスチャマップにバインドされたテクスチャIDを取得する。
* バインドされていなければダミーとして-1を返します。
*
* @param textureId テクスチャID
* @return バインドされたテクスチャID
*/
int getBoundTextureId(int textureId) {
Integer boundTextureId = textures.get(textureId);
return boundTextureId == null ? -1 : boundTextureId;
}
/**
* Frame buffer for drawing mask
*/
CubismOffscreenSurfaceAndroid[] offscreenSurfaces;
/**
* マスク生成時かどうかを判定する。
*
* @return マスク生成時かどうか。生成時ならtrue。
*/
private boolean isGeneratingMask() {
return getClippingContextBufferForMask() != null;
}
/**
* Map between the textures referenced by the model and the textures bound by the renderer
*/
private final Map<Integer, Integer> textures = new HashMap<Integer, Integer>(32);
private boolean areTexturesChanged = true;
private Map<Integer, Integer> cachedImmutableTextures;
/**
* A list of drawing object indices arranged in drawing order
*/
private int[] sortedDrawableIndexList;
/**
* the object which keeps the OpenGL state
*/
private final CubismRendererProfileAndroid rendererProfile = new CubismRendererProfileAndroid();
/**
* Clipping mask management object
*/
private CubismClippingManagerAndroid clippingManager;
/**
* Clippping context for drawing on mask texture
*/
private CubismClippingContextAndroid clippingContextBufferForMask;
/**
* Clipping context for drawing on the screen
*/
private CubismClippingContextAndroid clippingContextBufferForDraw;
/**
* Drawable情報のキャッシュ変数
*/
private CubismDrawableInfoCachesHolder drawableInfoCachesHolder;
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering.android;
import static android.opengl.GLES20.*;
/**
* Class that saves and restores the OpenGL ES 2.0 state just before drawing the Cubism model.
*/
class CubismRendererProfileAndroid {
/**
* Save OpenGL ES 2.0 state.
*/
public void save() {
//-- push state --
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, lastArrayBufferBinding, 0);
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, lastElementArrayBufferBinding, 0);
glGetIntegerv(GL_CURRENT_PROGRAM, lastProgram, 0);
glGetIntegerv(GL_ACTIVE_TEXTURE, lastActiveTexture, 0);
// Activate Texture Unit1 (It is the target to be set thereafter)
glActiveTexture(GL_TEXTURE1);
glGetIntegerv(GL_TEXTURE_BINDING_2D, lastTexture1Binding2D, 0);
// Activate Texture Unit0 (It is the target to be set thereafter)
glActiveTexture(GL_TEXTURE0);
glGetIntegerv(GL_TEXTURE_BINDING_2D, lastTexture0Binding2D, 0);
glGetVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, lastVertexAttribArrayEnabled[0], 0);
glGetVertexAttribiv(1, GL_VERTEX_ATTRIB_ARRAY_ENABLED, lastVertexAttribArrayEnabled[1], 0);
glGetVertexAttribiv(2, GL_VERTEX_ATTRIB_ARRAY_ENABLED, lastVertexAttribArrayEnabled[2], 0);
glGetVertexAttribiv(3, GL_VERTEX_ATTRIB_ARRAY_ENABLED, lastVertexAttribArrayEnabled[3], 0);
lastScissorTest = glIsEnabled(GL_SCISSOR_TEST);
lastStencilTest = glIsEnabled(GL_STENCIL_TEST);
lastDepthTest = glIsEnabled(GL_DEPTH_TEST);
lastCullFace = glIsEnabled(GL_CULL_FACE);
lastBlend = glIsEnabled(GL_BLEND);
glGetIntegerv(GL_FRONT_FACE, lastFrontFace, 0);
glGetBooleanv(GL_COLOR_WRITEMASK, lastColorMask, 0);
// backup blending
glGetIntegerv(GL_BLEND_SRC_RGB, lastBlendingSrcRGB, 0);
glGetIntegerv(GL_BLEND_DST_RGB, lastBlendingDstRGB, 0);
glGetIntegerv(GL_BLEND_SRC_ALPHA, lastBlendingSrcAlpha, 0);
glGetIntegerv(GL_BLEND_DST_ALPHA, lastBlendingDstAlpha, 0);
// Save the FBO and viewport just before drawing the model.
glGetIntegerv(GL_FRAMEBUFFER_BINDING, lastFBO, 0);
glGetIntegerv(GL_VIEWPORT, lastViewport, 0);
}
/**
* Restore OpenGL ES 2.0 state which is saved.
*/
public void restore() {
glUseProgram(lastProgram[0]);
setGlEnableVertexAttribArray(0, lastVertexAttribArrayEnabled[0][0]);
setGlEnableVertexAttribArray(1, lastVertexAttribArrayEnabled[1][0]);
setGlEnableVertexAttribArray(2, lastVertexAttribArrayEnabled[2][0]);
setGlEnableVertexAttribArray(3, lastVertexAttribArrayEnabled[3][0]);
setGlEnable(GL_SCISSOR_TEST, lastScissorTest);
setGlEnable(GL_STENCIL_TEST, lastStencilTest);
setGlEnable(GL_DEPTH_TEST, lastDepthTest);
setGlEnable(GL_CULL_FACE, lastCullFace);
setGlEnable(GL_BLEND, lastBlend);
glFrontFace(lastFrontFace[0]);
glColorMask(
lastColorMask[0],
lastColorMask[1],
lastColorMask[2],
lastColorMask[3]
);
// If the buffer was bound before, it needs to be destroyed.
glBindBuffer(GL_ARRAY_BUFFER, lastArrayBufferBinding[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lastElementArrayBufferBinding[0]);
// Restore Texture Unit1.
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, lastTexture1Binding2D[0]);
// Restore Texture Unit0.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, lastTexture0Binding2D[0]);
glActiveTexture(lastActiveTexture[0]);
// restore blending
glBlendFuncSeparate(
lastBlendingSrcRGB[0],
lastBlendingDstRGB[0],
lastBlendingSrcAlpha[0],
lastBlendingDstAlpha[0]
);
}
/**
* FBO just before model drawing
*/
public final int[] lastFBO = new int[1];
/**
* Viewport just before drawing the model
*/
public final int[] lastViewport = new int[4];
/**
* Set enable/disable of OpenGL ES 2.0 features.
*
* @param index index of function to enable/disable
* @param enabled If true, enable it.
*/
private void setGlEnable(int index, boolean enabled) {
if (enabled) {
glEnable(index);
} else {
glDisable(index);
}
}
/**
* Set enable/disable for Vertex Attribute Array feature in OpenGL ES 2.0.
*
* @param index index of function to enable/disable
* @param isEnabled If true, enable it.
*/
private void setGlEnableVertexAttribArray(int index, int isEnabled) {
// It true
if (isEnabled != 0) {
glEnableVertexAttribArray(index);
}
// If false
else {
glDisableVertexAttribArray(index);
}
}
/**
* Vertex buffer just before drawing the model
*/
private final int[] lastArrayBufferBinding = new int[1];
/**
* Element buffer just before drawing the model
*/
private final int[] lastElementArrayBufferBinding = new int[1];
/**
* Shader program buffer just before drawing the model
*/
private final int[] lastProgram = new int[1];
/**
* The active texture just before drawing the model
*/
private final int[] lastActiveTexture = new int[1];
/**
* Texture unit0 just before model drawing
*/
private final int[] lastTexture0Binding2D = new int[1];
/**
* Texture unit1 just before model drawing
*/
private final int[] lastTexture1Binding2D = new int[1];
/**
* GL_VERTEX_ATTRIB_ARRAY_ENABLED parameter just before model drawing
*/
private final int[][] lastVertexAttribArrayEnabled = new int[4][1];
/**
* GL_SCISSOR_TEST parameter just before drawing the model
*/
private boolean lastScissorTest;
/**
* GL_BLEND parameter just before model drawing
*/
private boolean lastBlend;
/**
* GL_STENCIL_TEST parameter just before drawing the model
*/
private boolean lastStencilTest;
/**
* GL_DEPTH_TEST parameter just before drawing the model
*/
private boolean lastDepthTest;
/**
* GL_CULL_FACE parameter just before drawing the model
*/
private boolean lastCullFace;
/**
* GL_FRONT_FACE parameter just before model drawing
*/
private final int[] lastFrontFace = new int[1];
/**
* GL_COLOR_WRITEMASK parameter just before model drawing
*/
private final boolean[] lastColorMask = new boolean[4];
/**
* GL_BLEND_SRC_RGB parameter just before model drawing
*/
private final int[] lastBlendingSrcRGB = new int[1];
/**
* GL_BLEND_DST_RGB parameter just before model drawing
*/
private final int[] lastBlendingDstRGB = new int[1];
/**
* GL_BLEND_SRC_ALPHA parameter just before model drawing
*/
private final int[] lastBlendingSrcAlpha = new int[1];
/**
* GL_BLEND_DST_ALPHA parameter just before model drawing
*/
private final int[] lastBlendingDstAlpha = new int[1];
}

View File

@@ -0,0 +1,878 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering.android;
import com.live2d.sdk.cubism.framework.math.CubismMatrix44;
import com.live2d.sdk.cubism.framework.model.CubismModel;
import com.live2d.sdk.cubism.framework.rendering.CubismRenderer;
import com.live2d.sdk.cubism.framework.type.csmRectF;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import static android.opengl.GLES20.*;
import static com.live2d.sdk.cubism.framework.rendering.android.CubismShaderPrograms.*;
import static com.live2d.sdk.cubism.framework.utils.CubismDebug.cubismLogError;
/**
* This class manage a shader program for Android(OpenGL ES 2.0). This is singleton.
*/
class CubismShaderAndroid {
/**
* Tegra processor support. Enable/Disable drawing by extension method.
*
* @param extMode Whether to draw using the extended method.
* @param extPAMode Enables/disables the PA setting for the extension method.
*/
public static void setExtShaderMode(boolean extMode, boolean extPAMode) {
CubismShaderAndroid.EXT_MODE = extMode;
CubismShaderAndroid.EXT_PA_MODE = extPAMode;
}
/**
* Get this singleton instance.
*
* @return singleton instance
*/
public static CubismShaderAndroid getInstance() {
if (s_instance == null) {
s_instance = new CubismShaderAndroid();
}
return s_instance;
}
/**
* Delete this singleton instance.
*/
public static void deleteInstance() {
s_instance = null;
}
/**
* Setup shader program.
*
* @param renderer renderer instance
* @param model rendered model
* @param index target artmesh index
*/
public void setupShaderProgramForDraw(
CubismRendererAndroid renderer,
CubismModel model,
int index
) {
if (shaderSets.isEmpty()) {
generateShaders();
}
// Blending
int srcColor;
int dstColor;
int srcAlpha;
int dstAlpha;
// shaderSets用のオフセット計算
final boolean isMasked = renderer.getClippingContextBufferForDraw() != null; // この描画オブジェクトはマスク対象か?
final boolean isInvertedMask = model.getDrawableInvertedMask(index);
final boolean isPremultipliedAlpha = renderer.isPremultipliedAlpha();
final int offset = (isMasked ? (isInvertedMask ? 2 : 1) : 0) + (isPremultipliedAlpha ? 3 : 0);
// シェーダーセット
CubismShaderSet shaderSet;
switch (model.getDrawableBlendMode(index)) {
case NORMAL:
default:
shaderSet = this.shaderSets.get(ShaderNames.NORMAL.getId() + offset);
srcColor = GL_ONE;
dstColor = GL_ONE_MINUS_SRC_ALPHA;
srcAlpha = GL_ONE;
dstAlpha = GL_ONE_MINUS_SRC_ALPHA;
break;
case ADDITIVE:
shaderSet = shaderSets.get(ShaderNames.ADD.getId() + offset);
srcColor = GL_ONE;
dstColor = GL_ONE;
srcAlpha = GL_ZERO;
dstAlpha = GL_ONE;
break;
case MULTIPLICATIVE:
shaderSet = shaderSets.get(ShaderNames.MULT.getId() + offset);
srcColor = GL_DST_COLOR;
dstColor = GL_ONE_MINUS_SRC_ALPHA;
srcAlpha = GL_ZERO;
dstAlpha = GL_ONE;
break;
}
glUseProgram(shaderSet.shaderProgram);
// キャッシュされたバッファを取得し、実際のデータを格納する。
CubismDrawableInfoCachesHolder drawableInfoCachesHolder = renderer.getDrawableInfoCachesHolder();
// vertex array
FloatBuffer vertexArrayBuffer = drawableInfoCachesHolder.setUpVertexArray(
index,
model.getDrawableVertices(index)
);
// uv array
FloatBuffer uvArrayBuffer = drawableInfoCachesHolder.setUpUvArray(
index,
model.getDrawableVertexUvs(index)
);
// setting of vertex array
glEnableVertexAttribArray(shaderSet.attributePositionLocation);
glVertexAttribPointer(
shaderSet.attributePositionLocation,
2,
GL_FLOAT,
false,
Float.SIZE / Byte.SIZE * 2,
vertexArrayBuffer
);
// setting of texture vertex
glEnableVertexAttribArray(shaderSet.attributeTexCoordLocation);
glVertexAttribPointer(
shaderSet.attributeTexCoordLocation,
2,
GL_FLOAT,
false,
Float.SIZE / Byte.SIZE * 2,
uvArrayBuffer
);
if (isMasked) {
glActiveTexture(GL_TEXTURE1);
// OffscreenSurfaceに描かれたテクスチャ
int tex = renderer.getMaskBuffer(renderer.getClippingContextBufferForDraw().bufferIndex).getColorBuffer()[0];
glBindTexture(GL_TEXTURE_2D, tex);
glUniform1i(shaderSet.samplerTexture1Location, 1);
// set up a matrix to convert View-coordinates to ClippingContext coordinates
glUniformMatrix4fv(
shaderSet.uniformClipMatrixLocation,
1,
false,
renderer.getClippingContextBufferForDraw().matrixForDraw.getArray(),
0
);
// Set used color channel.
final int channelIndex = renderer.getClippingContextBufferForDraw().layoutChannelIndex;
CubismRenderer.CubismTextureColor colorChannel = renderer
.getClippingContextBufferForDraw()
.getClippingManager()
.getChannelFlagAsColor(channelIndex);
glUniform4f(
shaderSet.uniformChannelFlagLocation,
colorChannel.r,
colorChannel.g,
colorChannel.b,
colorChannel.a
);
}
// texture setting
int textureId = renderer.getBoundTextureId(
model.getDrawableTextureIndex(index)
);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
glUniform1i(shaderSet.samplerTexture0Location, 0);
// coordinate transformation
CubismMatrix44 matrix44 = renderer.getMvpMatrix();
glUniformMatrix4fv(
shaderSet.uniformMatrixLocation,
1,
false,
matrix44.getArray(),
0
);
// ベース色の取得
CubismRenderer.CubismTextureColor baseColor = renderer.getModelColorWithOpacity(
model.getDrawableOpacity(index)
);
CubismRenderer.CubismTextureColor multiplyColor = model.getMultiplyColor(index);
CubismRenderer.CubismTextureColor screenColor = model.getScreenColor(index);
glUniform4f(
shaderSet.uniformBaseColorLocation,
baseColor.r,
baseColor.g,
baseColor.b,
baseColor.a
);
glUniform4f(
shaderSet.uniformMultiplyColorLocation,
multiplyColor.r,
multiplyColor.g,
multiplyColor.b,
multiplyColor.a
);
glUniform4f(
shaderSet.uniformScreenColorLocation,
screenColor.r,
screenColor.g,
screenColor.b,
screenColor.a
);
glBlendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha);
}
public void setupShaderProgramForMask(
CubismRendererAndroid renderer,
CubismModel model,
int index
) {
if (shaderSets.isEmpty()) {
generateShaders();
}
// Blending
int srcColor;
int dstColor;
int srcAlpha;
int dstAlpha;
CubismShaderSet shaderSet = shaderSets.get(ShaderNames.SETUP_MASK.id);
glUseProgram(shaderSet.shaderProgram);
// texture setting
int textureId = renderer.getBoundTextureId(model.getDrawableTextureIndex(index));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
glUniform1i(shaderSet.samplerTexture0Location, 0);
// キャッシュされたバッファを取得し、実際のデータを格納する。
CubismDrawableInfoCachesHolder drawableInfoCachesHolder = renderer.getDrawableInfoCachesHolder();
// vertex array
FloatBuffer vertexArrayBuffer = drawableInfoCachesHolder.setUpVertexArray(
index,
model.getDrawableVertices(index)
);
// uv array
FloatBuffer uvArrayBuffer = drawableInfoCachesHolder.setUpUvArray(
index,
model.getDrawableVertexUvs(index)
);
// setting of vertex array
glEnableVertexAttribArray(shaderSet.attributePositionLocation);
glVertexAttribPointer(
shaderSet.attributePositionLocation,
2,
GL_FLOAT,
false,
Float.SIZE / Byte.SIZE * 2,
vertexArrayBuffer
);
// setting of texture vertex
glEnableVertexAttribArray(shaderSet.attributeTexCoordLocation);
glVertexAttribPointer(
shaderSet.attributeTexCoordLocation,
2,
GL_FLOAT,
false,
Float.SIZE / Byte.SIZE * 2,
uvArrayBuffer
);
// channels
final int channelIndex = renderer.getClippingContextBufferForMask().layoutChannelIndex;
CubismRenderer.CubismTextureColor colorChannel = renderer
.getClippingContextBufferForMask()
.getClippingManager()
.getChannelFlagAsColor(channelIndex);
glUniform4f(
shaderSet.uniformChannelFlagLocation,
colorChannel.r,
colorChannel.g,
colorChannel.b,
colorChannel.a
);
glUniformMatrix4fv(
shaderSet.uniformClipMatrixLocation,
1,
false,
renderer.getClippingContextBufferForMask().matrixForMask.getArray(),
0
);
csmRectF rect = renderer.getClippingContextBufferForMask().layoutBounds;
glUniform4f(
shaderSet.uniformBaseColorLocation,
rect.getX() * 2.0f - 1.0f,
rect.getY() * 2.0f - 1.0f,
rect.getRight() * 2.0f - 1.0f,
rect.getBottom() * 2.0f - 1.0f
);
CubismRenderer.CubismTextureColor multiplyColor = model.getMultiplyColor(index);
CubismRenderer.CubismTextureColor screenColor = model.getScreenColor(index);
glUniform4f(
shaderSet.uniformMultiplyColorLocation,
multiplyColor.r,
multiplyColor.g,
multiplyColor.b,
multiplyColor.a
);
glUniform4f(
shaderSet.uniformScreenColorLocation,
screenColor.r,
screenColor.g,
screenColor.b,
screenColor.a
);
srcColor = GL_ZERO;
dstColor = GL_ONE_MINUS_SRC_COLOR;
srcAlpha = GL_ZERO;
dstAlpha = GL_ONE_MINUS_SRC_ALPHA;
glBlendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha);
}
/**
* Singleton instance.
*/
private static CubismShaderAndroid s_instance;
/**
* Tegra support. Drawing with Extended Method.
*/
private static boolean EXT_MODE;
/**
* Variable for setting the PA of the extension method.
*/
private static boolean EXT_PA_MODE;
/**
* Shader names
*/
private enum ShaderNames {
// Setup Mask
SETUP_MASK(0),
// Normal
NORMAL(1),
NORMAL_MASKED(2),
NORMAL_MASKED_INVERTED(3),
NORMAL_PREMULTIPLIED_ALPHA(4),
NORMAL_MASKED_PREMULTIPLIED_ALPHA(5),
NORMAL_MASKED_INVERTED_PREMULTIPLIED_ALPHA(6),
// Add
ADD(7),
ADD_MASKED(8),
ADD_MASKED_INVERTED(9),
ADD_PREMULTIPLIED_ALPHA(10),
ADD_MASKED_PREMULTIPLIED_ALPHA(11),
ADD_MASKED_PREMULTIPLIED_ALPHA_INVERTED(12),
// Multi
MULT(13),
MULT_MASKED(14),
MULT_MASKED_INVERTED(15),
MULT_PREMULTIPLIED_ALPHA(16),
MULT_MASKED_PREMULTIPLIED_ALPHA(17),
MULT_MASKED_PREMULTIPLIED_ALPHA_INVERTED(18);
private final int id;
ShaderNames(int id) {
this.id = id;
}
private int getId() {
return id;
}
}
/**
* Data class that holds the addresses of shader programs and shader variables
*/
private static class CubismShaderSet {
/**
* address of shader program.
*/
int shaderProgram;
/**
* Address of the variable to be passed to the shader program (Position)
*/
int attributePositionLocation;
/**
* Address of the variable to be passed to the shader program (TexCoord)
*/
int attributeTexCoordLocation;
/**
* Address of the variable to be passed to the shader program (Matrix)
*/
int uniformMatrixLocation;
/**
* Address of the variable to be passed to the shader program (ClipMatrix)
*/
int uniformClipMatrixLocation;
/**
* Address of the variable to be passed to the shader program (Texture0)
*/
int samplerTexture0Location;
/**
* Address of the variable to be passed to the shader program (Texture1)
*/
int samplerTexture1Location;
/**
* Address of the variable to be passed to the shader program (BaseColor)
*/
int uniformBaseColorLocation;
/**
* Address of the variable to be passed to the shader program (MultiplyColor)
*/
int uniformMultiplyColorLocation;
/**
* Address of the variable to be passed to the shader program (ScreenColor)
*/
int uniformScreenColorLocation;
/**
* Address of the variable to be passed to the shader program (ChannelFlag)
*/
int uniformChannelFlagLocation;
}
/**
* private constructor.
*/
private CubismShaderAndroid() {
}
/**
* Release shader programs.
*/
private void releaseShaderProgram() {
for (CubismShaderSet shaderSet : shaderSets) {
glDeleteProgram(shaderSet.shaderProgram);
shaderSet.shaderProgram = 0;
}
shaderSets.clear();
}
/**
* Initialize and generate shader programs.
*/
private void generateShaders() {
for (int i = 0; i < SHADER_COUNT; i++) {
shaderSets.add(new CubismShaderSet());
}
if (EXT_MODE) {
shaderSets.get(0).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_SETUP_MASK, FRAG_SHADER_SRC_SETUP_MASK_TEGRA);
shaderSets.get(1).shaderProgram = loadShaderProgram(VERT_SHADER_SRC, FRAG_SHADER_SRC_TEGRA);
shaderSets.get(2).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK_TEGRA);
shaderSets.get(3).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK_INVERTED_TEGRA);
shaderSets.get(4).shaderProgram = loadShaderProgram(VERT_SHADER_SRC, FRAG_SHADER_SRC_PREMULTIPLIED_ALPHA_TEGRA);
shaderSets.get(5).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK_PREMULTIPLIED_ALPHA_TEGRA);
shaderSets.get(6).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK_INVERTED_PREMULTIPLIED_ALPHA_TEGRA);
} else {
shaderSets.get(0).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_SETUP_MASK, FRAG_SHADER_SRC_SETUP_MASK);
shaderSets.get(1).shaderProgram = loadShaderProgram(VERT_SHADER_SRC, FRAG_SHADER_SRC);
shaderSets.get(2).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK);
shaderSets.get(3).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK_INVERTED);
shaderSets.get(4).shaderProgram = loadShaderProgram(VERT_SHADER_SRC, FRAG_SHADER_SRC_PREMULTIPLIED_ALPHA);
shaderSets.get(5).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK_PREMULTIPLIED_ALPHA);
shaderSets.get(6).shaderProgram = loadShaderProgram(VERT_SHADER_SRC_MASKED, FRAG_SHADER_SRC_MASK_INVERTED_PREMULTIPLIED_ALPHA);
}
// 加算も通常と同じシェーダーを利用する
shaderSets.get(7).shaderProgram = shaderSets.get(1).shaderProgram;
shaderSets.get(8).shaderProgram = shaderSets.get(2).shaderProgram;
shaderSets.get(9).shaderProgram = shaderSets.get(3).shaderProgram;
shaderSets.get(10).shaderProgram = shaderSets.get(4).shaderProgram;
shaderSets.get(11).shaderProgram = shaderSets.get(5).shaderProgram;
shaderSets.get(12).shaderProgram = shaderSets.get(6).shaderProgram;
// 乗算も通常と同じシェーダーを利用する
shaderSets.get(13).shaderProgram = shaderSets.get(1).shaderProgram;
shaderSets.get(14).shaderProgram = shaderSets.get(2).shaderProgram;
shaderSets.get(15).shaderProgram = shaderSets.get(3).shaderProgram;
shaderSets.get(16).shaderProgram = shaderSets.get(4).shaderProgram;
shaderSets.get(17).shaderProgram = shaderSets.get(5).shaderProgram;
shaderSets.get(18).shaderProgram = shaderSets.get(6).shaderProgram;
// Setup mask
shaderSets.get(0).attributePositionLocation = glGetAttribLocation(shaderSets.get(0).shaderProgram, "a_position");
shaderSets.get(0).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(0).shaderProgram, "a_texCoord");
shaderSets.get(0).samplerTexture0Location = glGetUniformLocation(shaderSets.get(0).shaderProgram, "s_texture0");
shaderSets.get(0).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(0).shaderProgram, "u_clipMatrix");
shaderSets.get(0).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(0).shaderProgram, "u_channelFlag");
shaderSets.get(0).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(0).shaderProgram, "u_baseColor");
shaderSets.get(0).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(0).shaderProgram, "u_multiplyColor");
shaderSets.get(0).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(0).shaderProgram, "u_screenColor");
// 通常
shaderSets.get(1).attributePositionLocation = glGetAttribLocation(shaderSets.get(1).shaderProgram, "a_position");
shaderSets.get(1).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(1).shaderProgram, "a_texCoord");
shaderSets.get(1).samplerTexture0Location = glGetUniformLocation(shaderSets.get(1).shaderProgram, "s_texture0");
shaderSets.get(1).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(1).shaderProgram, "u_matrix");
shaderSets.get(1).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(1).shaderProgram, "u_baseColor");
shaderSets.get(1).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(1).shaderProgram, "u_multiplyColor");
shaderSets.get(1).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(1).shaderProgram, "u_screenColor");
// 通常(クリッピング)
shaderSets.get(2).attributePositionLocation = glGetAttribLocation(shaderSets.get(2).shaderProgram, "a_position");
shaderSets.get(2).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(2).shaderProgram, "a_texCoord");
shaderSets.get(2).samplerTexture0Location = glGetUniformLocation(shaderSets.get(2).shaderProgram, "s_texture0");
shaderSets.get(2).samplerTexture1Location = glGetUniformLocation(shaderSets.get(2).shaderProgram, "s_texture1");
shaderSets.get(2).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(2).shaderProgram, "u_matrix");
shaderSets.get(2).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(2).shaderProgram, "u_clipMatrix");
shaderSets.get(2).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(2).shaderProgram, "u_channelFlag");
shaderSets.get(2).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(2).shaderProgram, "u_baseColor");
shaderSets.get(2).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(2).shaderProgram, "u_multiplyColor");
shaderSets.get(2).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(2).shaderProgram, "u_screenColor");
// 通常(クリッピング・反転)
shaderSets.get(3).attributePositionLocation = glGetAttribLocation(shaderSets.get(3).shaderProgram, "a_position");
shaderSets.get(3).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(3).shaderProgram, "a_texCoord");
shaderSets.get(3).samplerTexture0Location = glGetUniformLocation(shaderSets.get(3).shaderProgram, "s_texture0");
shaderSets.get(3).samplerTexture1Location = glGetUniformLocation(shaderSets.get(3).shaderProgram, "s_texture1");
shaderSets.get(3).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(3).shaderProgram, "u_matrix");
shaderSets.get(3).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(3).shaderProgram, "u_clipMatrix");
shaderSets.get(3).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(3).shaderProgram, "u_channelFlag");
shaderSets.get(3).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(3).shaderProgram, "u_baseColor");
shaderSets.get(3).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(3).shaderProgram, "u_multiplyColor");
shaderSets.get(3).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(3).shaderProgram, "u_screenColor");
// 通常PremultipliedAlpha
shaderSets.get(4).attributePositionLocation = glGetAttribLocation(shaderSets.get(4).shaderProgram, "a_position");
shaderSets.get(4).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(4).shaderProgram, "a_texCoord");
shaderSets.get(4).samplerTexture0Location = glGetUniformLocation(shaderSets.get(4).shaderProgram, "s_texture0");
shaderSets.get(4).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(4).shaderProgram, "u_matrix");
shaderSets.get(4).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(4).shaderProgram, "u_baseColor");
shaderSets.get(4).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(4).shaderProgram, "u_multiplyColor");
shaderSets.get(4).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(4).shaderProgram, "u_screenColor");
// 通常クリッピング、PremultipliedAlpha
shaderSets.get(5).attributePositionLocation = glGetAttribLocation(shaderSets.get(5).shaderProgram, "a_position");
shaderSets.get(5).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(5).shaderProgram, "a_texCoord");
shaderSets.get(5).samplerTexture0Location = glGetUniformLocation(shaderSets.get(5).shaderProgram, "s_texture0");
shaderSets.get(5).samplerTexture1Location = glGetUniformLocation(shaderSets.get(5).shaderProgram, "s_texture1");
shaderSets.get(5).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(5).shaderProgram, "u_matrix");
shaderSets.get(5).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(5).shaderProgram, "u_clipMatrix");
shaderSets.get(5).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(5).shaderProgram, "u_channelFlag");
shaderSets.get(5).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(5).shaderProgram, "u_baseColor");
shaderSets.get(5).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(5).shaderProgram, "u_multiplyColor");
shaderSets.get(5).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(5).shaderProgram, "u_screenColor");
// 通常クリッピング・反転、PremultipliedAlpha
shaderSets.get(6).attributePositionLocation = glGetAttribLocation(shaderSets.get(6).shaderProgram, "a_position");
shaderSets.get(6).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(6).shaderProgram, "a_texCoord");
shaderSets.get(6).samplerTexture0Location = glGetUniformLocation(shaderSets.get(6).shaderProgram, "s_texture0");
shaderSets.get(6).samplerTexture1Location = glGetUniformLocation(shaderSets.get(6).shaderProgram, "s_texture1");
shaderSets.get(6).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(6).shaderProgram, "u_matrix");
shaderSets.get(6).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(6).shaderProgram, "u_clipMatrix");
shaderSets.get(6).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(6).shaderProgram, "u_channelFlag");
shaderSets.get(6).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(6).shaderProgram, "u_baseColor");
shaderSets.get(6).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(6).shaderProgram, "u_multiplyColor");
shaderSets.get(6).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(6).shaderProgram, "u_screenColor");
// 加算
shaderSets.get(7).attributePositionLocation = glGetAttribLocation(shaderSets.get(7).shaderProgram, "a_position");
shaderSets.get(7).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(7).shaderProgram, "a_texCoord");
shaderSets.get(7).samplerTexture0Location = glGetUniformLocation(shaderSets.get(7).shaderProgram, "s_texture0");
shaderSets.get(7).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(7).shaderProgram, "u_matrix");
shaderSets.get(7).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(7).shaderProgram, "u_baseColor");
shaderSets.get(7).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(7).shaderProgram, "u_multiplyColor");
shaderSets.get(7).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(7).shaderProgram, "u_screenColor");
// 加算(クリッピング)
shaderSets.get(8).attributePositionLocation = glGetAttribLocation(shaderSets.get(8).shaderProgram, "a_position");
shaderSets.get(8).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(8).shaderProgram, "a_texCoord");
shaderSets.get(8).samplerTexture0Location = glGetUniformLocation(shaderSets.get(8).shaderProgram, "s_texture0");
shaderSets.get(8).samplerTexture1Location = glGetUniformLocation(shaderSets.get(8).shaderProgram, "s_texture1");
shaderSets.get(8).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(8).shaderProgram, "u_matrix");
shaderSets.get(8).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(8).shaderProgram, "u_clipMatrix");
shaderSets.get(8).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(8).shaderProgram, "u_channelFlag");
shaderSets.get(8).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(8).shaderProgram, "u_baseColor");
shaderSets.get(8).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(8).shaderProgram, "u_multiplyColor");
shaderSets.get(8).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(8).shaderProgram, "u_screenColor");
// 加算(クリッピング・反転)
shaderSets.get(9).attributePositionLocation = glGetAttribLocation(shaderSets.get(9).shaderProgram, "a_position");
shaderSets.get(9).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(9).shaderProgram, "a_texCoord");
shaderSets.get(9).samplerTexture0Location = glGetUniformLocation(shaderSets.get(9).shaderProgram, "s_texture0");
shaderSets.get(9).samplerTexture1Location = glGetUniformLocation(shaderSets.get(9).shaderProgram, "s_texture1");
shaderSets.get(9).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(9).shaderProgram, "u_matrix");
shaderSets.get(9).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(9).shaderProgram, "u_clipMatrix");
shaderSets.get(9).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(9).shaderProgram, "u_channelFlag");
shaderSets.get(9).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(9).shaderProgram, "u_baseColor");
shaderSets.get(9).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(9).shaderProgram, "u_multiplyColor");
shaderSets.get(9).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(9).shaderProgram, "u_screenColor");
// 加算PremultipliedAlpha
shaderSets.get(10).attributePositionLocation = glGetAttribLocation(shaderSets.get(10).shaderProgram, "a_position");
shaderSets.get(10).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(10).shaderProgram, "a_texCoord");
shaderSets.get(10).samplerTexture0Location = glGetUniformLocation(shaderSets.get(10).shaderProgram, "s_texture0");
shaderSets.get(10).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(10).shaderProgram, "u_matrix");
shaderSets.get(10).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(10).shaderProgram, "u_baseColor");
shaderSets.get(10).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(10).shaderProgram, "u_multiplyColor");
shaderSets.get(10).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(10).shaderProgram, "u_screenColor");
// 加算クリッピング、PremultipliedAlpha
shaderSets.get(11).attributePositionLocation = glGetAttribLocation(shaderSets.get(11).shaderProgram, "a_position");
shaderSets.get(11).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(11).shaderProgram, "a_texCoord");
shaderSets.get(11).samplerTexture0Location = glGetUniformLocation(shaderSets.get(11).shaderProgram, "s_texture0");
shaderSets.get(11).samplerTexture1Location = glGetUniformLocation(shaderSets.get(11).shaderProgram, "s_texture1");
shaderSets.get(11).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(11).shaderProgram, "u_matrix");
shaderSets.get(11).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(11).shaderProgram, "u_clipMatrix");
shaderSets.get(11).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(11).shaderProgram, "u_channelFlag");
shaderSets.get(11).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(11).shaderProgram, "u_baseColor");
shaderSets.get(11).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(11).shaderProgram, "u_multiplyColor");
shaderSets.get(11).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(11).shaderProgram, "u_screenColor");
// 加算クリッピング・反転、PremultipliedAlpha
shaderSets.get(12).attributePositionLocation = glGetAttribLocation(shaderSets.get(12).shaderProgram, "a_position");
shaderSets.get(12).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(12).shaderProgram, "a_texCoord");
shaderSets.get(12).samplerTexture0Location = glGetUniformLocation(shaderSets.get(12).shaderProgram, "s_texture0");
shaderSets.get(12).samplerTexture1Location = glGetUniformLocation(shaderSets.get(12).shaderProgram, "s_texture1");
shaderSets.get(12).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(12).shaderProgram, "u_matrix");
shaderSets.get(12).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(12).shaderProgram, "u_clipMatrix");
shaderSets.get(12).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(12).shaderProgram, "u_channelFlag");
shaderSets.get(12).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(12).shaderProgram, "u_baseColor");
shaderSets.get(12).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(12).shaderProgram, "u_multiplyColor");
shaderSets.get(12).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(12).shaderProgram, "u_screenColor");
// 乗算
shaderSets.get(13).attributePositionLocation = glGetAttribLocation(shaderSets.get(13).shaderProgram, "a_position");
shaderSets.get(13).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(13).shaderProgram, "a_texCoord");
shaderSets.get(13).samplerTexture0Location = glGetUniformLocation(shaderSets.get(13).shaderProgram, "s_texture0");
shaderSets.get(13).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(13).shaderProgram, "u_matrix");
shaderSets.get(13).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(13).shaderProgram, "u_baseColor");
shaderSets.get(13).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(13).shaderProgram, "u_multiplyColor");
shaderSets.get(13).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(13).shaderProgram, "u_screenColor");
// 乗算(クリッピング)
shaderSets.get(14).attributePositionLocation = glGetAttribLocation(shaderSets.get(14).shaderProgram, "a_position");
shaderSets.get(14).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(14).shaderProgram, "a_texCoord");
shaderSets.get(14).samplerTexture0Location = glGetUniformLocation(shaderSets.get(14).shaderProgram, "s_texture0");
shaderSets.get(14).samplerTexture1Location = glGetUniformLocation(shaderSets.get(14).shaderProgram, "s_texture1");
shaderSets.get(14).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(14).shaderProgram, "u_matrix");
shaderSets.get(14).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(14).shaderProgram, "u_clipMatrix");
shaderSets.get(14).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(14).shaderProgram, "u_channelFlag");
shaderSets.get(14).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(14).shaderProgram, "u_baseColor");
shaderSets.get(14).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(14).shaderProgram, "u_multiplyColor");
shaderSets.get(14).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(14).shaderProgram, "u_screenColor");
// 乗算(クリッピング・反転)
shaderSets.get(15).attributePositionLocation = glGetAttribLocation(shaderSets.get(15).shaderProgram, "a_position");
shaderSets.get(15).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(15).shaderProgram, "a_texCoord");
shaderSets.get(15).samplerTexture0Location = glGetUniformLocation(shaderSets.get(15).shaderProgram, "s_texture0");
shaderSets.get(15).samplerTexture1Location = glGetUniformLocation(shaderSets.get(15).shaderProgram, "s_texture1");
shaderSets.get(15).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(15).shaderProgram, "u_matrix");
shaderSets.get(15).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(15).shaderProgram, "u_clipMatrix");
shaderSets.get(15).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(15).shaderProgram, "u_channelFlag");
shaderSets.get(15).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(15).shaderProgram, "u_baseColor");
shaderSets.get(15).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(15).shaderProgram, "u_multiplyColor");
shaderSets.get(15).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(15).shaderProgram, "u_screenColor");
// 乗算PremultipliedAlpha
shaderSets.get(16).attributePositionLocation = glGetAttribLocation(shaderSets.get(16).shaderProgram, "a_position");
shaderSets.get(16).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(16).shaderProgram, "a_texCoord");
shaderSets.get(16).samplerTexture0Location = glGetUniformLocation(shaderSets.get(16).shaderProgram, "s_texture0");
shaderSets.get(16).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(16).shaderProgram, "u_matrix");
shaderSets.get(16).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(16).shaderProgram, "u_baseColor");
shaderSets.get(16).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(16).shaderProgram, "u_multiplyColor");
shaderSets.get(16).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(16).shaderProgram, "u_screenColor");
// 乗算クリッピング、PremultipliedAlpha
shaderSets.get(17).attributePositionLocation = glGetAttribLocation(shaderSets.get(17).shaderProgram, "a_position");
shaderSets.get(17).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(17).shaderProgram, "a_texCoord");
shaderSets.get(17).samplerTexture0Location = glGetUniformLocation(shaderSets.get(17).shaderProgram, "s_texture0");
shaderSets.get(17).samplerTexture1Location = glGetUniformLocation(shaderSets.get(17).shaderProgram, "s_texture1");
shaderSets.get(17).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(17).shaderProgram, "u_matrix");
shaderSets.get(17).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(17).shaderProgram, "u_clipMatrix");
shaderSets.get(17).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(17).shaderProgram, "u_channelFlag");
shaderSets.get(17).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(17).shaderProgram, "u_baseColor");
shaderSets.get(17).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(17).shaderProgram, "u_multiplyColor");
shaderSets.get(17).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(17).shaderProgram, "u_screenColor");
// 乗算クリッピング・反転、PremultipliedAlpha
shaderSets.get(18).attributePositionLocation = glGetAttribLocation(shaderSets.get(18).shaderProgram, "a_position");
shaderSets.get(18).attributeTexCoordLocation = glGetAttribLocation(shaderSets.get(18).shaderProgram, "a_texCoord");
shaderSets.get(18).samplerTexture0Location = glGetUniformLocation(shaderSets.get(18).shaderProgram, "s_texture0");
shaderSets.get(18).samplerTexture1Location = glGetUniformLocation(shaderSets.get(18).shaderProgram, "s_texture1");
shaderSets.get(18).uniformMatrixLocation = glGetUniformLocation(shaderSets.get(18).shaderProgram, "u_matrix");
shaderSets.get(18).uniformClipMatrixLocation = glGetUniformLocation(shaderSets.get(18).shaderProgram, "u_clipMatrix");
shaderSets.get(18).uniformChannelFlagLocation = glGetUniformLocation(shaderSets.get(18).shaderProgram, "u_channelFlag");
shaderSets.get(18).uniformBaseColorLocation = glGetUniformLocation(shaderSets.get(18).shaderProgram, "u_baseColor");
shaderSets.get(18).uniformMultiplyColorLocation = glGetUniformLocation(shaderSets.get(18).shaderProgram, "u_multiplyColor");
shaderSets.get(18).uniformScreenColorLocation = glGetUniformLocation(shaderSets.get(18).shaderProgram, "u_screenColor");
}
private void setAttribLocation(final int shaderIndex) {
CubismShaderSet shader = shaderSets.get(shaderIndex);
shader.attributePositionLocation = glGetAttribLocation(shader.shaderProgram, "a_position");
shader.attributeTexCoordLocation = glGetAttribLocation(shader.shaderProgram, "a_texCoord");
shader.samplerTexture0Location = glGetUniformLocation(shader.shaderProgram, "s_texture0");
shader.uniformMatrixLocation = glGetUniformLocation(shader.shaderProgram, "u_matrix");
shader.uniformBaseColorLocation = glGetUniformLocation(shader.shaderProgram, "u_baseColor");
}
private void setAttribLocationClipping(final int shaderIndex) {
CubismShaderSet shader = shaderSets.get(shaderIndex);
shader.attributePositionLocation = glGetAttribLocation(shader.shaderProgram, "a_position");
shader.attributeTexCoordLocation = glGetAttribLocation(shader.shaderProgram, "a_texCoord");
shader.samplerTexture0Location = glGetUniformLocation(shader.shaderProgram, "s_texture0");
shader.samplerTexture1Location = glGetUniformLocation(shader.shaderProgram, "s_texture1");
shader.uniformMatrixLocation = glGetUniformLocation(shader.shaderProgram, "u_matrix");
shader.uniformClipMatrixLocation = glGetUniformLocation(shader.shaderProgram, "u_clipMatrix");
shader.uniformChannelFlagLocation = glGetUniformLocation(shader.shaderProgram, "u_channelFlag");
shader.uniformBaseColorLocation = glGetUniformLocation(shader.shaderProgram, "u_baseColor");
}
/**
* Load shader program.
*
* @param vertShaderSrc source of vertex shader
* @param fragShaderSrc source of fragment shader
* @return reference value to the shader program
*/
private int loadShaderProgram(final String vertShaderSrc, final String fragShaderSrc) {
int[] vertShader = new int[1];
int[] fragShader = new int[1];
// Create shader program.
int shaderProgram = glCreateProgram();
if (!compileShaderSource(vertShader, GL_VERTEX_SHADER, vertShaderSrc)) {
cubismLogError("Vertex shader compile error!");
return 0;
}
// Create and compile fragment shader.
if (!compileShaderSource(fragShader, GL_FRAGMENT_SHADER, fragShaderSrc)) {
cubismLogError("Fragment shader compile error!");
return 0;
}
// Attach vertex shader to program.
glAttachShader(shaderProgram, vertShader[0]);
// Attach fragment shader to program.
glAttachShader(shaderProgram, fragShader[0]);
// Link program.
if (!linkProgram(shaderProgram)) {
cubismLogError("Failed to link program: " + shaderProgram);
glDeleteShader(vertShader[0]);
glDeleteShader(fragShader[0]);
glDeleteProgram(shaderProgram);
return 0;
}
// Release vertex and fragment shaders.
glDetachShader(shaderProgram, vertShader[0]);
glDeleteShader(vertShader[0]);
glDetachShader(shaderProgram, fragShader[0]);
glDeleteShader(fragShader[0]);
return shaderProgram;
}
/**
* Compile shader program.
*
* @param shader reference value to compiled shader program
* @param shaderType shader type(Vertex/Fragment)
* @param shaderSource source of shader program
* @return If compilling succeeds, return true
*/
private boolean compileShaderSource(int[] shader, int shaderType, final String shaderSource) {
if (shader == null || shader.length == 0) {
return false;
}
shader[0] = glCreateShader(shaderType);
glShaderSource(shader[0], shaderSource);
glCompileShader(shader[0]);
int[] logLength = new int[1];
glGetShaderiv(shader[0], GL_INFO_LOG_LENGTH, IntBuffer.wrap(logLength));
if (logLength[0] > 0) {
String log = glGetShaderInfoLog(shader[0]);
cubismLogError("Shader compile log: " + log);
}
int[] status = new int[1];
glGetShaderiv(shader[0], GL_COMPILE_STATUS, IntBuffer.wrap(status));
if (status[0] == GL_FALSE) {
glDeleteShader(shader[0]);
return false;
}
return true;
}
/**
* Link shader program.
*
* @param shaderProgram reference value to a shader program to link
* @return If linking succeeds, return true
*/
private boolean linkProgram(int shaderProgram) {
glLinkProgram(shaderProgram);
int[] logLength = new int[1];
glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, IntBuffer.wrap(logLength));
if (logLength[0] > 0) {
String log = glGetProgramInfoLog(shaderProgram);
cubismLogError("Program link log: " + log);
}
int[] status = new int[1];
glGetProgramiv(shaderProgram, GL_LINK_STATUS, IntBuffer.wrap(status));
return status[0] != GL_FALSE;
}
/**
* Validate shader program.
*
* @param shaderProgram reference value to shader program to be validated
* @return If there is no problem, return true
*/
private boolean validateProgram(int shaderProgram) {
glValidateProgram(shaderProgram);
int[] logLength = new int[1];
glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, IntBuffer.wrap(logLength));
if (logLength[0] > 0) {
String log = glGetProgramInfoLog(shaderProgram);
cubismLogError("Validate program log: " + log);
}
int[] status = new int[1];
glGetProgramiv(shaderProgram, GL_VALIDATE_STATUS, IntBuffer.wrap(status));
return status[0] != GL_FALSE;
}
/**
* Variable that holds the loaded shader program.
*/
private final List<CubismShaderSet> shaderSets = new ArrayList<CubismShaderSet>();
}

View File

@@ -0,0 +1,428 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.rendering.android;
/**
* This class has raw GLSL codes.
* Number of shaders = for generating mask + (normal + adding + premultiplication) * (without mask + with mask + with mask inverted + without mask for premultiplied alpha + with mask for premultiplied alpha + with mask inverted for premultiplied alpha)
*/
class CubismShaderPrograms {
/**
* The enum class that represents the precision of floating point numbers used in GLSL.
*/
public enum CsmFragmentShaderFpPrecision {
HIGH("highp"),
MID("mediump"),
LOW("lowp");
private final String value;
CsmFragmentShaderFpPrecision(String value) {
this.value = value;
}
}
/**
* Number of GLSL codes.
* <p>
* Number of the shaders = for generating masks + (Normal + Add + Multiply) * (No mask + mask + inverted mask + no mask for premultiplied alpha + mask for premultiplied alpha + inverted mask for premultiplied alpha)
* </p>
*/
public static final int SHADER_COUNT = 19;
/**
* Floating point number's precision used in GLSL. (Default value: "highp")
*/
private static CsmFragmentShaderFpPrecision precision = CsmFragmentShaderFpPrecision.HIGH;
/**
* Vertex shader code for setting up mask.
*/
public static final String VERT_SHADER_SRC_SETUP_MASK =
"#version 100\n"
+ "attribute vec4 a_position;\n"
+ "attribute vec2 a_texCoord;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_myPos;\n"
+ "uniform mat4 u_clipMatrix;\n"
+ "void main()\n"
+ "{\n"
+ "gl_Position = u_clipMatrix * a_position;\n"
+ "v_myPos = u_clipMatrix * a_position;\n"
+ "v_texCoord = a_texCoord;\n"
+ "v_texCoord.y = 1.0 - v_texCoord.y;\n"
+ "}";
/**
* Fragment shader code for setting up mask.
*/
public static final String FRAG_SHADER_SRC_SETUP_MASK =
"#version 100\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_myPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "void main()\n"
+ "{\n"
+ "float isInside = "
+ "step(u_baseColor.x, v_myPos.x/v_myPos.w)"
+ "* step(u_baseColor.y, v_myPos.y/v_myPos.w)"
+ "* step(v_myPos.x/v_myPos.w, u_baseColor.z)"
+ "* step(v_myPos.y/v_myPos.w, u_baseColor.w);\n"
+ "gl_FragColor = u_channelFlag * texture2D(s_texture0 , v_texCoord).a * isInside;\n"
+ "}";
/**
* Fragment shader code for setting up mask for Tegra
*/
public static final String FRAG_SHADER_SRC_SETUP_MASK_TEGRA =
"#version 100\n"
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_myPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "void main()\n"
+ "{\n"
+ "float isInside = "
+ "step(u_baseColor.x, v_myPos.x/v_myPos.w)"
+ "* step(u_baseColor.y, v_myPos.y/v_myPos.w)"
+ "* step(v_myPos.x/v_myPos.w, u_baseColor.z)"
+ "* step(v_myPos.y/v_myPos.w, u_baseColor.w);\n"
+ "gl_FragColor = u_channelFlag * texture2D(s_texture0 , v_texCoord).a * isInside;\n"
+ "}";
//----- Vertex Shader Programs -----
// Normal & Add & Multi common part
/**
* Vertex shader code.
*/
public static final String VERT_SHADER_SRC =
"#version 100\n"
+ "attribute vec4 a_position;\n" // v.vertex
+ "attribute vec2 a_texCoord;\n" // v.texcoord
+ "varying vec2 v_texCoord;\n" // v2f.texcoord
+ "uniform mat4 u_matrix;\n"
+ "void main()\n"
+ "{\n"
+ "gl_Position = u_matrix * a_position;\n"
+ "v_texCoord = a_texCoord;\n"
+ "v_texCoord.y = 1.0 - v_texCoord.y;\n"
+ "}";
/**
* Vertex shader code for masked things.
*/
public static final String VERT_SHADER_SRC_MASKED =
"#version 100\n"
+ "attribute vec4 a_position;\n"
+ "attribute vec2 a_texCoord;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform mat4 u_matrix;\n"
+ "uniform mat4 u_clipMatrix;\n"
+ "void main()\n"
+ "{\n"
+ "gl_Position = u_matrix * a_position;\n"
+ "v_clipPos = u_clipMatrix * a_position;\n"
+ "v_texCoord = a_texCoord;\n"
+ "v_texCoord.y = 1.0 - v_texCoord.y;\n"
+ "}";
//----- Fragment Shader Programs -----
// Normal & Add & Mult common part
/**
* Fragment shader code.
*/
public static final String FRAG_SHADER_SRC =
"#version 100\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n" // v2f.texcoord
+ "uniform sampler2D s_texture0;\n" // _MainTex
+ "uniform vec4 u_baseColor;\n" // v2f.color
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);\n"
+ "vec4 color = texColor * u_baseColor;\n"
+ "gl_FragColor = vec4(color.rgb * color.a, color.a);\n"
+ "}";
/**
* Fragment shader code for Tegra.
*/
public static final String FRAG_SHADER_SRC_TEGRA =
"#version 100\n"
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n" //v2f.texcoord
+ "uniform sampler2D s_texture0;\n" //_MainTex
+ "uniform vec4 u_baseColor;\n" //v2f.color
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);\n"
+ "vec4 color = texColor * u_baseColor;\n"
+ "gl_FragColor = vec4(color.rgb * color.a, color.a);\n"
+ "}";
// Normal & Add & Mult common (PremultipliedAlpha)
/**
* Fragment shader code for premultipiled alpha.
*/
public static final String FRAG_SHADER_SRC_PREMULTIPLIED_ALPHA =
"#version 100\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n" // v2f.texcoord
+ "uniform sampler2D s_texture0;\n" // _MainTex
+ "uniform vec4 u_baseColor;\n" // v2f.color
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);\n"
+ "gl_FragColor = texColor * u_baseColor;\n"
+ "}";
/**
* Fragment shader code for premultiplied alpha for Tegra.
*/
public static final String FRAG_SHADER_SRC_PREMULTIPLIED_ALPHA_TEGRA =
"#version 100\n"
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n" //v2f.texcoord
+ "uniform sampler2D s_texture0;\n" //_MainTex
+ "uniform vec4 u_baseColor;\n" //v2f.color
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);\n"
+ "gl_FragColor = texColor * u_baseColor;\n"
+ "}";
// Normal & Add & Mult common(for drawing the clipped thing)
/**
* Fragment shader code for masked things.
*/
public static final String FRAG_SHADER_SRC_MASK =
"#version 100\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);\n"
+ "vec4 col_formask = texColor * u_baseColor;\n"
+ "col_formask.rgb = col_formask.rgb * col_formask.a;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * maskVal;\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
/**
* Fragment shader code for masked things. (for Tegra)
*/
public static final String FRAG_SHADER_SRC_MASK_TEGRA =
"#version 100\n"
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor;\n"
+ "col_formask.rgb = col_formask.rgb * col_formask.a;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * maskVal;\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
// Normal & Add & Mult common (For clipped and reversed use drawing)
/**
* Fragment shader code for inverted mask.
*/
public static final String FRAG_SHADER_SRC_MASK_INVERTED =
"#version 100\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);\n"
+ "vec4 col_formask = texColor * u_baseColor;\n"
+ "col_formask.rgb = col_formask.rgb * col_formask.a;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * (1.0 - maskVal);\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
/**
* Fragment shader code for inverted mask. (for Tegra)
*/
public static final String FRAG_SHADER_SRC_MASK_INVERTED_TEGRA =
"#version 100\n"
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor;\n"
+ "col_formask.rgb = col_formask.rgb * col_formask.a;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * (1.0 - maskVal);\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
/**
* Fragment shader code for the masked things at premultiplied alpha.
*/
public static final String FRAG_SHADER_SRC_MASK_PREMULTIPLIED_ALPHA =
"#version 100\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);\n"
+ "vec4 col_formask = texColor * u_baseColor;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * maskVal;\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
/**
* Fragment shader code for the masked things at premultiplied alpha. (for Tegra)
*/
public static final String FRAG_SHADER_SRC_MASK_PREMULTIPLIED_ALPHA_TEGRA =
"#version 100\n"
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * maskVal;\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
// Normal & Add & Mult common (For clipped and reversed use drawing, in the case of PremultipliedAlpha)
/**
* Fragment shader code for inverted mask at premultiplied alpha.
*/
public static final String FRAG_SHADER_SRC_MASK_INVERTED_PREMULTIPLIED_ALPHA =
"#version 100\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "uniform vec4 u_multiplyColor;\n"
+ "uniform vec4 u_screenColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 texColor = texture2D(s_texture0, v_texCoord);\n"
+ "texColor.rgb = texColor.rgb * u_multiplyColor.rgb;\n"
+ "texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);\n"
+ "vec4 col_formask = texColor * u_baseColor;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * (1.0 - maskVal);\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
/**
* Fragment shader code for inverted mask at premultiplied alpha(for Tegra)
*/
public static final String FRAG_SHADER_SRC_MASK_INVERTED_PREMULTIPLIED_ALPHA_TEGRA =
"#version 100\n"
+ "#extension GL_NV_shader_framebuffer_fetch : enable\n"
+ "precision " + precision.value + " float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying vec4 v_clipPos;\n"
+ "uniform sampler2D s_texture0;\n"
+ "uniform sampler2D s_texture1;\n"
+ "uniform vec4 u_channelFlag;\n"
+ "uniform vec4 u_baseColor;\n"
+ "void main()\n"
+ "{\n"
+ "vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor;\n"
+ "vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;\n"
+ "float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;\n"
+ "col_formask = col_formask * (1.0 - maskVal);\n"
+ "gl_FragColor = col_formask;\n"
+ "}";
/**
* Set the floating point number's precision used in GLSL.
*
* @param p precision
*/
public void setFragmentShaderFpPrecision(CubismShaderPrograms.CsmFragmentShaderFpPrecision p) {
precision = p;
}
}

View File

@@ -0,0 +1,4 @@
/**
* Provide the renderer that implemetns graphics instructions for rendering models on the Android platform.
*/
package com.live2d.sdk.cubism.framework.rendering.android;

View File

@@ -0,0 +1,4 @@
/**
* Provide the renderer that implements graphics instructions for rendering models on various platforms.
*/
package com.live2d.sdk.cubism.framework.rendering;

View File

@@ -0,0 +1,230 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.type;
/**
* This class defines a rectangle form(a coordinate and a length is float value)
*/
public class csmRectF {
/**
* Create the new CubismRectangle instance.
*
* @return CubismRectangle instance
*/
public static csmRectF create() {
return new csmRectF();
}
/**
* Create the new CubismRectangle instance with each parameter.
*
* @param x x-coordinate
* @param y y-coordinate
* @param w width
* @param h height
* @return CubismRectangle instance
*/
public static csmRectF create(
float x,
float y,
float w,
float h
) {
return new csmRectF(x, y, w, h);
}
/**
* Create the new CubismRectangle instance.
* This method works the same way as a copy constructor.
*
* @param r CubismRectangle instance to be copied
* @return CubismRectangle instance
*/
public static csmRectF create(csmRectF r) {
return new csmRectF(r);
}
/**
* Get the x-coordinate at center of this rectangle.
*
* @return the x-coord at center of this rect.
*/
public float getCenterX() {
return x + 0.5f * width;
}
/**
* Get the y-coordinate at center of this rectangle.
*
* @return the y-coord at center of this rect
*/
public float getCenterY() {
return y + 0.5f * height;
}
/**
* Get the x-coordinate at right end of this rectangle.
*
* @return x-coord at right end
*/
public float getRight() {
return x + width;
}
/**
* Get the y-coordinate at bottom of this rectangle.
*
* @return y-coord at bottom
*/
public float getBottom() {
return y + height;
}
/**
* Set a value to this rectangle.
*/
public void setRect(csmRectF r) {
x = r.getX();
y = r.getY();
width = r.getWidth();
height = r.getHeight();
}
/**
* Scale width and height with the center of this rectangle as axis.
*
* @param w the amount of scaling into width
* @param h the amount of scaling into height
*/
public void expand(final float w, final float h) {
x -= w;
y -= h;
width += w * 2.0f;
height += h * 2.0f;
}
/**
* Get this x-coordinate
*
* @return x-coord
*/
public float getX() {
return x;
}
/**
* Set x-coordinate to this one.
*
* @param x x-coord
*/
public void setX(float x) {
this.x = x;
}
/**
* Get this y-coordinate
*
* @return y-coord
*/
public float getY() {
return y;
}
/**
* Set y-coordinate to this one.
*
* @param y y-coord
*/
public void setY(float y) {
this.y = y;
}
/**
* Get the width of this.
*
* @return width
*/
public float getWidth() {
return width;
}
/**
* Set the width to this one.
*
* @param width width set to this
*/
public void setWidth(float width) {
this.width = width;
}
/**
* Get the height of this.
*
* @return height
*/
public float getHeight() {
return height;
}
/**
* Set the height to this one.
*
* @param height height set to this
*/
public void setHeight(float height) {
this.height = height;
}
/**
* Constructor
*/
private csmRectF() {}
/**
* Constructor with each value.
*
* @param x left end x-coord
* @param y top end y-coord
* @param w width
* @param h height
*/
private csmRectF(
final float x,
final float y,
final float w,
final float h
) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
}
private csmRectF(csmRectF r) {
setRect(r);
}
/**
* Left end x-coordinate
*/
private float x;
/**
* Top end y-coordinate
*/
private float y;
/**
* Width
*/
private float width;
/**
* Height
*/
private float height;
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils;
import com.live2d.sdk.cubism.framework.CubismFramework;
import com.live2d.sdk.cubism.framework.CubismFrameworkConfig.LogLevel;
import static com.live2d.sdk.cubism.framework.CubismFrameworkConfig.CSM_LOG_LEVEL;
/**
* A utility class for debugging.
* <p>
* Log output, dump byte, and so on.
*/
public class CubismDebug {
/**
* Output log. Set log level to 1st argument.
* At using {@link CubismFramework#initialize()} function, if the log level is lower than set log output level, log output is not executed.
*
* @param logLevel log level setting
* @param message format string
* @param args variadic arguments
*/
public static void print(final LogLevel logLevel, final String message, Object... args) {
// If the log level is lower than set log output level in Option class, log outputting is not executed.
if (logLevel.getId() < CubismFramework.getLoggingLevel().getId()) {
return;
}
String format = String.format(message, args);
CubismFramework.coreLogFunction(format);
}
/**
* Dump out a specified length of data.
* <p>
* If the log output level is below the level set in the option at {@link CubismFramework#initialize()}, it will not be logged.
*
* @param logLevel setting of log level
* @param data data to dump
* @param length length of dumping
*/
public static void dumpBytes(final LogLevel logLevel, final byte[] data, int length) {
for (int i = 0; i < length; i++) {
if (i % 16 == 0 && i > 0) {
print(logLevel, "\n");
} else if (i % 8 == 0 && i > 0) {
print(logLevel, " ");
}
print(logLevel, "%02X", (data[i] & 0xFF));
}
}
/**
* Display the normal message.
*
* @param message message
*/
public static void cubismLogPrint(LogLevel logLevel, String message, Object... args) {
print(logLevel, "[CSM]" + message, args);
}
/**
* Display a newline message.
*
* @param message message
*/
public static void cubismLogPrintln(LogLevel logLevel, String message, Object... args) {
cubismLogPrint(logLevel, message + "\n", args);
}
/**
* Show detailed message.
*
* @param message message
*/
public static void cubismLogVerbose(String message, Object... args) {
if (CSM_LOG_LEVEL.getId() <= LogLevel.VERBOSE.getId()) {
cubismLogPrintln(LogLevel.VERBOSE, "[V]" + message, args);
}
}
/**
* Display the debug message.
*
* @param message message
*/
public static void cubismLogDebug(String message, Object... args) {
if (CSM_LOG_LEVEL.getId() <= LogLevel.DEBUG.getId()) {
cubismLogPrintln(LogLevel.DEBUG, "[D]" + message, args);
}
}
/**
* Display informational messages.
*
* @param message message
*/
public static void cubismLogInfo(String message, Object... args) {
if (CSM_LOG_LEVEL.getId() <= LogLevel.INFO.getId()) {
cubismLogPrintln(LogLevel.INFO, "[I]" + message, args);
}
}
/**
* Display a warning message.
*
* @param message message
*/
public static void cubismLogWarning(String message, Object... args) {
if (CSM_LOG_LEVEL.getId() <= LogLevel.WARNING.getId()) {
cubismLogPrintln(LogLevel.WARNING, "[W]" + message, args);
}
}
/**
* Display a error message.
*
* @param message message.
*/
public static void cubismLogError(String message, Object... args) {
if (CSM_LOG_LEVEL.getId() <= LogLevel.ERROR.getId()) {
cubismLogPrintln(LogLevel.ERROR, "[E]" + message, args);
}
}
/**
* Private constructor
*/
private CubismDebug() {}
}

View File

@@ -0,0 +1,228 @@
package com.live2d.sdk.cubism.framework.utils.jsonparser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public abstract class ACubismJsonValue {
/**
* If the class implemented this interface is {@code CubismJsonObject}, returns the value corresponding to the key.
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @param key the key of the JSON Object
* @return the value of the JSON Object
*/
public ACubismJsonValue get(String key) {
ACubismJsonValue nullValue = new CubismJsonNullValue();
nullValue.setErrorNotForClientCall(JsonError.TYPE_MISMATCH.message);
return nullValue;
}
/**
* If the class implemented this interface is {@code CubismJsonArray}, returns the value corresponding to the index of the argument.
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @param index index of the element to return
* @return the value corresponding to the index
*/
public ACubismJsonValue get(int index) {
ACubismJsonValue errorValue = new CubismJsonErrorValue();
errorValue.setErrorNotForClientCall(JsonError.TYPE_MISMATCH.message);
return errorValue;
}
/**
* Returns the JSON Value's string expression.
*
* @return the JSON Value's string expression
*/
public abstract String getString(String defaultValue, String indent);
public String getString(String defaultValue) {
return getString(defaultValue, "");
}
public String getString() {
return getString("", "");
}
/**
* If the class implemented this interface is {@code CubismJsonObject}, returns the map of {@code CubismJsonString} and {@code ICubismJsonValue}.
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @return the map of strings and values
*/
public Map<CubismJsonString, ACubismJsonValue> getMap() {
return null;
}
/**
* もしこのインターフェースが実装されているクラスが{@code CubismJsonObject}ならば、{@code CubismJsonString}と{@code ACubismJsonValue}をペアにしたMapを返す。<br>
* もし他の具象クラスに実装されていた場合は、{@code null}を返す。
*
* @param defaultValue デフォルト値
* @return デフォルト値
*/
public Map<CubismJsonString, ACubismJsonValue> getMap(Map<CubismJsonString, ACubismJsonValue> defaultValue) {
return defaultValue;
}
/**
* If the class implemented this interface is {@code CubismJsonArray}, returns the list of {@code ICubismJsonValue}.
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @return the list of {@code ICubismJsonValue}
*/
public List<ACubismJsonValue> getList() {
return new ArrayList<ACubismJsonValue>();
}
/**
* もしこのインターフェースが実装されているクラスが{@code CubismJsonArray}ならば、{@code ACubismJsonValue}のListを返す。<br>
* もし他の具象クラスに実装されていた場合は、{@code null}を返す。
*
* @param defaultValue デフォルト値
* @return デフォルト値
*/
public List<ACubismJsonValue> getList(List<ACubismJsonValue> defaultValue) {
return defaultValue;
}
/**
* If the class implemented this interface is {@code CubismJsonObject}, returns the set of the keys({@code CubismJsonString}).
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @return the set of the keys({@code CubismJsonString})
*/
public List<CubismJsonString> getKeys() {
return Collections.emptyList();
}
/**
* If the class implemented this interface is {@code CubismJsonObject} and {@code CubismJsonArray}, returns the number of JSON Value that they hold.
* <p>
* If this is implemented in {@code CubismJsonString}, returns the length of the string.
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @return the number of JSON Value that they hold or the length of the string
*/
public int size() {
return 0;
}
/**
* If the class implemented this interface is {@code CubismJsonNumber}, returns the value cast to an integer value.
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @return the value cast to an integer value
*/
public int toInt() {
return 0;
}
public int toInt(int defaultValue) {
return defaultValue;
}
/**
* If the class implemented this interface is {@code CubismJsonNumber}, returns the value cast to a float value.
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @return the value cast to an float value
*/
public float toFloat() {
return 0.0f;
}
public float toFloat(float defaultValue) {
return defaultValue;
}
/**
* If the class implemented this interface is {@code CubismJsonBoolean}, returns the boolean value it holds
* <p>
* If this is implemented in other concrete class, {@code UnsupportedOperationException} is thrown.
*
* @return the boolean value that the class has
*/
public boolean toBoolean() {
return false;
}
public boolean toBoolean(boolean defaultValue) {
return defaultValue;
}
public boolean isError() {
return false;
}
/**
* If the class implemented this interface is {@code CubismJsonNullValue}, returns {@code true}.
* <p>
* If this is implemented in other concrete class, returns {@code false}.
*
* @return If this JSON Value is Null Value, returns true
*/
public boolean isNull() {
return false;
}
public boolean isBoolean() {
return false;
}
public boolean isNumber() {
return false;
}
public boolean isString() {
return false;
}
public boolean isArray() {
return false;
}
public boolean isObject() {
return false;
}
/**
* Valueにエラー値をセットする。
*
* @param errorMsg エラーメッセージ
* @return JSON Null Value
*/
public ACubismJsonValue setErrorNotForClientCall(String errorMsg) {
stringBuffer = errorMsg;
return new CubismJsonNullValue();
}
/**
* エラーメッセージ定義
*/
protected enum JsonError {
TYPE_MISMATCH("Error: type mismatch"),
INDEX_OUT_OF_BOUNDS("Error: index out of bounds");
public final String message;
JsonError(String message) {
this.message = message;
}
}
protected String stringBuffer;
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
import com.live2d.sdk.cubism.framework.exception.CubismJsonParseException;
import com.live2d.sdk.cubism.framework.exception.CubismJsonSyntaxErrorException;
import java.io.IOException;
/**
* This class has some functions related to JSON.
*/
public class CubismJson {
/**
* Creates the JSON object.
*
* @param buffer byte data of the JSON
* @return JSON object
*
* @throws IllegalArgumentException If the argument is null
*/
public static CubismJson create(byte[] buffer) {
if (buffer == null || buffer.length == 0) {
throw new IllegalArgumentException("Parsed JSON data is empty.");
}
CubismJson json = new CubismJson();
json.parse(buffer);
return json;
}
/**
* Get a root of a parsed JSON.
*
* @return JSON root
*/
public ACubismJsonValue getRoot() {
return root;
}
/**
* Private constructor
*/
private CubismJson() {}
/**
* Parse JSON string.
*
* @param buffer JSON byte data
*/
private void parse(byte[] buffer) {
try {
String json = new String(buffer, "UTF-8");
lexer = new CubismJsonLexer(json);
token = lexer.getNextToken();
root = createValue();
} catch (IOException e) {
throw new CubismJsonParseException("It seems that an error has occured in the input/output processing", e);
}
}
/**
* Construct a JSON value.
*
* @return JSON Value
*/
private ACubismJsonValue createValue() throws CubismJsonParseException, IOException {
// JSON Object
if (token.getTokenType() == CubismJsonToken.TokenType.LBRACE) {
objectNestingLevel++;
CubismJsonObject object = createObject();
// If parsing is midway, the next token is read.
if (objectNestingLevel != 0 || arrayNestingLevel != 0) {
token = lexer.getNextToken();
}
return object;
}
// JSON Array
else if (token.getTokenType() == CubismJsonToken.TokenType.LSQUARE_BRACKET) {
arrayNestingLevel++;
CubismJsonArray array = createArray();
// If parsing is midway, the next token is read.
if (objectNestingLevel != 0 || arrayNestingLevel != 0) {
token = lexer.getNextToken();
}
return array;
}
// JSON Number
else if (token.getTokenType() == CubismJsonToken.TokenType.NUMBER) {
CubismJsonNumber number = CubismJsonNumber.valueOf(token.getNumberValue());
if (objectNestingLevel != 0 || arrayNestingLevel != 0) {
token = lexer.getNextToken();
}
return number;
}
// JSON String
else if (token.getTokenType() == CubismJsonToken.TokenType.STRING) {
CubismJsonString string = CubismJsonString.valueOf(token.getStringValue());
if (objectNestingLevel != 0 || arrayNestingLevel != 0) {
token = lexer.getNextToken();
}
return string;
}
// JSON Boolean(true or false)
else if (token.getTokenType() == CubismJsonToken.TokenType.BOOLEAN) {
CubismJsonBoolean bool = CubismJsonBoolean.valueOf(token.getBooleanValue());
if (objectNestingLevel != 0 || arrayNestingLevel != 0) {
token = lexer.getNextToken();
}
return bool;
}
// JSON null value
else if (token.getTokenType() == CubismJsonToken.TokenType.NULL) {
CubismJsonNullValue nullValue = new CubismJsonNullValue();
if (objectNestingLevel != 0 || arrayNestingLevel != 0) {
token = lexer.getNextToken();
}
return nullValue;
} else {
throw new CubismJsonSyntaxErrorException("Incorrect JSON format.", lexer.getCurrentLineNumber() - 1);
}
}
/**
* Construct a JSON object
*
* @return JSON Object
*/
private CubismJsonObject createObject() throws CubismJsonParseException, IOException {
CubismJsonObject object = new CubismJsonObject();
token = lexer.getNextToken();
// If the next token is braces, this object is regarded as empty object
if (token.getTokenType() == CubismJsonToken.TokenType.RBRACE) {
objectNestingLevel--;
return object;
} else {
// Continue reading until closed by '}'
// If the format is not "string : value (, string : value, ...)", an exception is thrown.
while (true) {
CubismJsonString string;
ACubismJsonValue value;
// Construct a string value
if (token.getTokenType() == CubismJsonToken.TokenType.STRING) {
string = CubismJsonString.valueOf(token.getStringValue());
} else {
throw new CubismJsonSyntaxErrorException("JSON Object's format is incorrect.", lexer.getCurrentLineNumber());
}
token = lexer.getNextToken();
// If it is not divided by colon, an exception is thrown.
if (token.getTokenType() != CubismJsonToken.TokenType.COLON) {
throw new CubismJsonSyntaxErrorException("JSON Object's format is incorrect.", lexer.getCurrentLineNumber());
}
token = lexer.getNextToken();
value = createValue();
// Put a pair of string and value into object
object.putPair(string, value);
// If the next token is comma, reading is continued. If the next token is '}', it is done to "break".
if (token.getTokenType() == CubismJsonToken.TokenType.RBRACE) {
objectNestingLevel--;
break;
} else if (token.getTokenType() == CubismJsonToken.TokenType.COMMA) {
token = lexer.getNextToken();
} else {
throw new CubismJsonSyntaxErrorException("JSON Object's format is incorrect.", lexer.getCurrentLineNumber() - 1);
}
}
}
return object;
}
/**
* Construct a JSON array.
*
* @return JSON Array
*
* @throws CubismJsonParseException an exception related to parsing
*/
private CubismJsonArray createArray() throws CubismJsonParseException, IOException {
CubismJsonArray array = new CubismJsonArray();
token = lexer.getNextToken();
// If the next token is square brackets, this array is regarded as empty array.
if (token.getTokenType() == CubismJsonToken.TokenType.RSQUARE_BRACKET) {
arrayNestingLevel--;
return array;
} else {
// Continue reading until closed by ']'
// If the format is not "value (, value, ...)", an exception is thrown.
while (true) {
ACubismJsonValue value;
// Construct a value
value = createValue();
// Put the value into array
array.putValue(value);
// If the next token is comma, reading is continued. If the next token is ']', it is done to "break".
if (token.getTokenType() == CubismJsonToken.TokenType.RSQUARE_BRACKET) {
arrayNestingLevel--;
break;
} else if (token.getTokenType() == CubismJsonToken.TokenType.COMMA) {
token = lexer.getNextToken();
} else {
throw new CubismJsonSyntaxErrorException("JSON Array's format is incorrect.", lexer.getCurrentLineNumber() - 1);
}
}
}
return array;
}
/**
* JSON root
*/
private ACubismJsonValue root;
/**
* JSON lexer
*/
private CubismJsonLexer lexer;
/**
* JSON token
*/
private CubismJsonToken token;
/**
* A nest level of JSON object. If left brace is appeared, nest level increases by 1, and if right brace is done, it decreases by 1.
*/
private int objectNestingLevel;
private int arrayNestingLevel;
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
import java.util.ArrayList;
import java.util.List;
/**
* This class expresses JSON Array
*/
class CubismJsonArray extends ACubismJsonValue {
/**
* Add a JSON Value.
*
* @param value JSON value
*/
public void putValue(ACubismJsonValue value) {
this.value.add(value);
}
@Override
public ACubismJsonValue get(String key) {
return new CubismJsonErrorValue().setErrorNotForClientCall(JsonError.TYPE_MISMATCH.message);
}
@Override
public ACubismJsonValue get(int index) {
if (index < 0 || value.size() <= index) {
return new CubismJsonErrorValue().setErrorNotForClientCall(JsonError.INDEX_OUT_OF_BOUNDS.message);
}
ACubismJsonValue value = this.value.get(index);
if (value == null) {
return new CubismJsonNullValue();
}
return value;
}
@Override
public String getString(String defaultValue, String indent) {
// バッファに格納されている文字列を空にする
bufferForGetString.delete(0, bufferForGetString.length());
bufferForGetString.append(indent);
bufferForGetString.append("[\n");
for (int i = 0; i < value.size(); i++) {
bufferForGetString.append(indent);
bufferForGetString.append(" ");
bufferForGetString.append(value.get(i).getString(indent + " "));
bufferForGetString.append("\n");
}
bufferForGetString.append(indent);
bufferForGetString.append("]\n");
stringBuffer = bufferForGetString.toString();
return stringBuffer;
}
@Override
public List<ACubismJsonValue> getList() {
return value;
}
@Override
public int size() {
return value.size();
}
@Override
public boolean isArray() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismJsonArray that = (CubismJsonArray) o;
return value.equals(that.value);
}
@Override
public int hashCode() {
return value.hashCode();
}
/**
* {@code getString}メソッドで使われる一時的な文字列バッファの最小容量。
*/
private static final int MINIMUM_CAPACITY = 128;
/**
* JSON Array value
*/
private final List<ACubismJsonValue> value = new ArrayList<ACubismJsonValue>();
/**
* {@code getString}で使用される文字列バッファ
*/
private final StringBuffer bufferForGetString = new StringBuffer(MINIMUM_CAPACITY);
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
/**
* This class expresses a boolean value.
*/
class CubismJsonBoolean extends ACubismJsonValue {
/**
* Get a boolean instance
*
* @param value a boolean value
* @return a JSON boolean value
*/
public static CubismJsonBoolean valueOf(final boolean value) {
return new CubismJsonBoolean(value);
}
@Override
public String getString(String defaultValue, String indent) {
return stringBuffer;
}
@Override
public boolean toBoolean() {
return value;
}
@Override
public boolean toBoolean(boolean defaultValue) {
return value;
}
@Override
public boolean isBoolean() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismJsonBoolean that = (CubismJsonBoolean) o;
return value == that.value;
}
@Override
public int hashCode() {
return (value ? 1 : 0);
}
/**
* Private constructor
*
* @param value a boolean value
*/
private CubismJsonBoolean(boolean value) {
this.value = value;
stringBuffer = String.valueOf(value);
}
/**
* A boolean value
*/
private final boolean value;
}

View File

@@ -0,0 +1,28 @@
/*
*
* * Copyright(c) Live2D Inc. All rights reserved.
* *
* * Use of this source code is governed by the Live2D Open Software license
* * that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
class CubismJsonErrorValue extends ACubismJsonValue {
@Override
public String getString(String defaultValue, String indent) {
return stringBuffer;
}
@Override
public boolean isError() {
return true;
}
@Override
public ACubismJsonValue setErrorNotForClientCall(String s) {
this.stringBuffer = s;
return this;
}
}

View File

@@ -0,0 +1,458 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
import com.live2d.sdk.cubism.framework.exception.CubismJsonParseException;
import java.io.IOException;
import java.util.Arrays;
/**
* This class offers a function of JSON lexer.
*/
class CubismJsonLexer {
/**
* Package-private constructor
*
* @param json string of JSON
*/
public CubismJsonLexer(String json) {
// 上位層で、nullだったら例外を出しているため、
// 引数がnullであることは考えられない
assert json != null;
// char配列に変換する
jsonChars = json.toCharArray();
jsonCharsLength = jsonChars.length;
// トークン解析用のバッファを初期化
// 初期容量は128。128文字を超えるトークンが出現するならばその都度拡張する。
parsedTokonBuffer = new char[MINIMUM_CAPACITY];
}
/**
* Get a next token.
*/
public CubismJsonToken getNextToken() throws CubismJsonParseException, IOException {
// Skip blank characters
while (isWhiteSpaceChar(nextChar)) {
updateNextChar();
}
// null文字で埋める
Arrays.fill(parsedTokonBuffer, 0, bufferIndex, '\0');
bufferIndex = 0;
// A Number token
// A process when beginning at minus sign
if (nextChar == '-') {
append('-');
updateNextChar();
if (Character.isDigit(nextChar)) {
buildNumber();
String numberStr = String.copyValueOf(parsedTokonBuffer, 0, bufferIndex);
NUMBER.setNumberValue(Double.parseDouble(numberStr));
return NUMBER;
} else {
throw new CubismJsonParseException("Number's format is incorrect.", lineNumber);
}
}
// A process when beginning at a number except 0.
else if (Character.isDigit(nextChar)) {
buildNumber();
String numberStr = String.copyValueOf(parsedTokonBuffer, 0, bufferIndex);
NUMBER.setNumberValue(Double.parseDouble(numberStr));
return NUMBER;
}
// true
else if (nextChar == 't') {
append(nextChar);
updateNextChar();
for (int i = 0; i < 3; i++) {
append(nextChar);
updateNextChar();
}
// If "value" does not create true value, send an exception.
String trueString = String.copyValueOf(parsedTokonBuffer, 0, bufferIndex);
if (!trueString.equals("true")) {
throw new CubismJsonParseException("Boolean's format or spell is incorrect.", lineNumber);
}
return TRUE;
}
// false
else if (nextChar == 'f') {
append(nextChar);
updateNextChar();
for (int i = 0; i < 4; i++) {
append(nextChar);
updateNextChar();
}
// If the value does not equals to "false" value, send the exception.
String falseString = String.copyValueOf(parsedTokonBuffer, 0, bufferIndex);
if (!falseString.equals("false")) {
throw new CubismJsonParseException("Boolean's format or spell is incorrect.", lineNumber);
}
return FALSE;
}
// null
else if (nextChar == 'n') {
append(nextChar);
updateNextChar();
for (int i = 0; i < 3; i++) {
append(nextChar);
updateNextChar();
}
// If the JSON value does not equal to the "null" value, send an exception.
String nullString = String.copyValueOf(parsedTokonBuffer, 0, bufferIndex);
if (!nullString.equals("null")) {
throw new CubismJsonParseException("JSON Null's format or spell is incorrect.", lineNumber);
}
return NULL;
} else if (nextChar == '{') {
updateNextChar();
return LBRACE;
} else if (nextChar == '}') {
updateNextChar();
return RBRACE;
} else if (nextChar == '[') {
updateNextChar();
return LSQUARE_BRACKET;
} else if (nextChar == ']') {
updateNextChar();
return RSQUARE_BRACKET;
}
// If next character is double quote, string token is created.
else if (nextChar == '"') {
updateNextChar();
// Until closing by double quote("), it is continued to read.
while (nextChar != '"') {
// Consider a escape sequence.
if (nextChar == '\\') {
updateNextChar();
buildEscapedString();
} else {
append(nextChar);
}
updateNextChar();
}
updateNextChar();
STRING.setStringValue(String.valueOf(parsedTokonBuffer, 0, bufferIndex));
return STRING;
}
// Colon(:)
else if (nextChar == ':') {
updateNextChar();
return COLON;
}
// Comma(,)
else if (nextChar == ',') {
updateNextChar();
return COMMA;
}
throw new CubismJsonParseException("The JSON is not closed properly, or there is some other malformed form.", lineNumber);
}
/**
* Return current line number.
*
* @return current line number
*/
public int getCurrentLineNumber() {
return lineNumber;
}
/**
* Build number string.
*
* @throws CubismJsonParseException the exception at failing to parse
*/
private void buildNumber() throws CubismJsonParseException {
if (nextChar == '0') {
append(nextChar);
updateNextChar();
buildDoubleOrExpNumber();
} else {
append(nextChar);
updateNextChar();
// Repeat processes until appearing a character except dot, exponential expression or number.
while (Character.isDigit(nextChar)) {
append(nextChar);
updateNextChar();
}
buildDoubleOrExpNumber();
}
}
/**
* Build double or exponential number.
*
* @throws CubismJsonParseException the exception at failing to parse
*/
private void buildDoubleOrExpNumber() throws CubismJsonParseException {
// If the next character is dot, floating point number is created.
if (nextChar == '.') {
buildDoubleNumber();
}
// If there is an e or E, it is considered an exponential expression.
if (nextChar == 'e' || nextChar == 'E') {
buildExponents();
}
}
/**
* Return floating point number as strings(StringBuilder).
*
* @throws CubismJsonParseException the exception at failing to parse
*/
private void buildDoubleNumber() throws CubismJsonParseException {
append('.');
updateNextChar();
// If the character following dot sign is not a number, an exception is thrown.
if (!Character.isDigit(nextChar)) {
throw new CubismJsonParseException("Number's format is incorrect.", lineNumber);
}
do {
append(nextChar);
updateNextChar();
} while (Character.isDigit(nextChar));
}
/**
* Build a number string used an exponential expression.
*
* @throws CubismJsonParseException the exception at failing to parse
*/
private void buildExponents() throws CubismJsonParseException {
append(nextChar);
updateNextChar();
// Handle cases where a number is preceded by a sign.
if (nextChar == '+') {
append(nextChar);
updateNextChar();
} else if (nextChar == '-') {
append(nextChar);
updateNextChar();
}
// If the character is not a number or a sign, an exception is thrown.
if (!Character.isDigit(nextChar)) {
throw new CubismJsonParseException(String.copyValueOf(parsedTokonBuffer, 0, bufferIndex) + "\n: " + "Exponent value's format is incorrect.", lineNumber);
}
do {
append(nextChar);
updateNextChar();
} while (Character.isDigit(nextChar));
}
/**
* Build a string used an escape sequence.
*
* @throws CubismJsonParseException the exception at failing to parse
*/
private void buildEscapedString() throws CubismJsonParseException {
switch (nextChar) {
case '"':
case '\\':
case '/':
append(nextChar);
break;
case 'b':
append('\b');
break;
case 'f':
append('\f');
break;
case 'n':
append('\n');
break;
case 'r':
append('\r');
break;
case 't':
append('\t');
break;
case 'u': {
// バッファをクリアする
bufferForHexadecimalString.delete(0, 16);
bufferForHexadecimalString.append('\\');
bufferForHexadecimalString.append('u');
for (int i = 0; i < 4; i++) {
updateNextChar();
bufferForHexadecimalString.append(nextChar);
}
// Check whether it is hex number. If there is a problem, an exception is thrown.
String tmp = bufferForHexadecimalString.toString();
if (!tmp.matches("\\\\u[a-fA-F0-9]{4}")) {
throw new CubismJsonParseException(bufferForHexadecimalString + "\n: " + "The unicode notation is incorrect.", lineNumber);
}
for (int i = 0; i < tmp.length(); i++) {
append(tmp.charAt(i));
}
break;
}
}
}
/**
* {@code buildEscapedString}の16進数の文字コードをパースする箇所で使用されるバッファ。
*/
private static final StringBuffer bufferForHexadecimalString = new StringBuffer();
/**
* Whether a character is white space character.
*
* @param c checked character
* @return If the character is white space character, return true
*/
private boolean isWhiteSpaceChar(char c) {
return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
}
/**
* Read a next character
*/
private void updateNextChar() {
// 文字を全部読んだら、次の文字をnull文字にセットしてreturnする
if (charIndex >= jsonCharsLength) {
nextChar = '\0';
return;
}
nextChar = jsonChars[charIndex];
charIndex++;
// 改行コードがあれば行数をインクリメントする
if (nextChar == '\n') {
lineNumber++;
}
}
/**
* Tokonのパース用の文字列バッファに、引数で指定された文字リテラルを追加する。
*
* @param c 追加する文字リテラル
*/
private void append(char c) {
// Tokenをパースするためのバッファがいっぱいになったら、バッファサイズを2倍にする
if (bufferLength == bufferIndex) {
bufferLength *= 2;
char[] tmp = new char[bufferLength];
System.arraycopy(parsedTokonBuffer, 0, tmp, 0, bufferIndex);
parsedTokonBuffer = tmp;
}
parsedTokonBuffer[bufferIndex] = c;
bufferIndex++;
}
// Tokenを都度生成せずに定数として保持する
/**
* 左波カッコ'{'のトークン
*/
private static final CubismJsonToken LBRACE = new CubismJsonToken(CubismJsonToken.TokenType.LBRACE);
/**
* 右波カッコ'}'のトークン
*/
private static final CubismJsonToken RBRACE = new CubismJsonToken(CubismJsonToken.TokenType.RBRACE);
/**
* 左角カッコ'['のトークン
*/
private static final CubismJsonToken LSQUARE_BRACKET = new CubismJsonToken(CubismJsonToken.TokenType.LSQUARE_BRACKET);
/**
* 左角カッコ'['のトークン
*/
private static final CubismJsonToken RSQUARE_BRACKET = new CubismJsonToken(CubismJsonToken.TokenType.RSQUARE_BRACKET);
/**
* コロン':'のトークン
*/
private static final CubismJsonToken COLON = new CubismJsonToken(CubismJsonToken.TokenType.COLON);
/**
* カンマ','のトークン
*/
private static final CubismJsonToken COMMA = new CubismJsonToken(CubismJsonToken.TokenType.COMMA);
/**
* 真偽値'true'のトークン
*/
private static final CubismJsonToken TRUE = new CubismJsonToken(true);
/**
* 真偽値'false'のトークン
*/
private static final CubismJsonToken FALSE = new CubismJsonToken(false);
/**
* 'null'のトークン
*/
private static final CubismJsonToken NULL = new CubismJsonToken();
// 中の値を書き換えて使用する
/**
* 文字列のトークン
*/
private static final CubismJsonToken STRING = new CubismJsonToken("");
/**
* 数値のトークン
*/
private static final CubismJsonToken NUMBER = new CubismJsonToken(0.0);
/**
* jsonChars配列の初期サイズ。
* これを超えるトークンが出現した場合はサイズを2倍に拡張する。
*/
private static final int MINIMUM_CAPACITY = 128;
/**
* パースするJSON文字列
*/
private final char[] jsonChars;
/**
* 現在読んでいる文字のインデックス
*/
private int charIndex;
/**
* パースするJSON文字列の文字数
*/
private final int jsonCharsLength;
/**
* 行数。改行文字が出てくるたびにインクリメントされる。
*/
private int lineNumber = 1;
/**
* the next character
*/
private char nextChar = ' ';
/**
* トークンのパース時に使用されるバッファ
*/
private char[] parsedTokonBuffer;
/**
* {@code parsedTokonBuffer}の最後尾のインデックス
*/
private int bufferIndex;
/**
* {@code parsedTokonBuffer}の容量
*/
private int bufferLength = MINIMUM_CAPACITY;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
/**
* This class expresses a JSON null Value.
* It has no fields and methods.
*/
class CubismJsonNullValue extends ACubismJsonValue {
public CubismJsonNullValue() {
stringBuffer = "NullValue";
}
@Override
public String getString(String defaultValue, String indent) {
return stringBuffer;
}
@Override
public boolean isNull() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismJsonNullValue that = (CubismJsonNullValue) o;
return stringBuffer != null ? stringBuffer.equals(that.stringBuffer) : that.stringBuffer == null;
}
@Override
public int hashCode() {
return stringBuffer != null ? stringBuffer.hashCode() : 0;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
/**
* This class expresses a number value.
* It has double type number.
*/
class CubismJsonNumber extends ACubismJsonValue {
/**
* Get an instance of number type.
*
* @param value number value
* @return an instance of number type
*/
public static CubismJsonNumber valueOf(double value) {
return new CubismJsonNumber(value);
}
@Override
public String getString(String defaultValue, String indent) {
return stringBuffer;
}
@Override
public int toInt() {
return (int) value;
}
@Override
public int toInt(int defaultValue) {
return (int) value;
}
@Override
public float toFloat() {
return (float) value;
}
@Override
public float toFloat(float defaultValue) {
return (float) value;
}
@Override
public boolean isNumber() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismJsonNumber that = (CubismJsonNumber) o;
return Float.compare((float) that.value, (float) value) == 0;
}
@Override
public int hashCode() {
long temp = Float.floatToIntBits((float) value);
return (int) (temp ^ (temp >>> 32));
}
/**
* private constructor
*
* @param value number value
*/
private CubismJsonNumber(double value) {
this.value = value;
stringBuffer = String.valueOf(value);
}
/**
* number value
*/
private final double value;
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class expresses JSON Object.
* If duplicate key is put, an error is not returned. However, the value corresponding duplicate key is overridden with the value corresponding the key which is defined later.
*/
class CubismJsonObject extends ACubismJsonValue {
/**
* Put a pair of string and value
*
* @param string key(string)
* @param value value
*/
public void putPair(CubismJsonString string, ACubismJsonValue value) {
this.value.put(string, value);
// Add to key list
keys.add(string);
}
@Override
public ACubismJsonValue get(String key) {
CubismJsonString str = CubismJsonString.valueOf(key);
ACubismJsonValue value = this.value.get(str);
if (value == null) {
return new CubismJsonNullValue();
}
return value;
}
@Override
public ACubismJsonValue get(int index) {
return new CubismJsonErrorValue().setErrorNotForClientCall(JsonError.TYPE_MISMATCH.message);
}
@Override
public String getString(String defaultValue, String indent) {
// バッファをクリアする
bufferForGetString.delete(0, bufferForGetString.length());
bufferForGetString.append(indent);
bufferForGetString.append("{\n");
for (int i = 0; i < keys.size(); i++) {
CubismJsonString key = keys.get(i);
ACubismJsonValue value = this.value.get(key);
assert value != null;
bufferForGetString.append(indent);
bufferForGetString.append(" ");
bufferForGetString.append(key);
bufferForGetString.append(" : ");
bufferForGetString.append(value.getString(indent + " "));
bufferForGetString.append("\n");
}
bufferForGetString.append(indent);
bufferForGetString.append("}\n");
stringBuffer = bufferForGetString.toString();
return stringBuffer;
}
@Override
public Map<CubismJsonString, ACubismJsonValue> getMap() {
return value;
}
@Override
public List<CubismJsonString> getKeys() {
return keys;
}
@Override
public int size() {
return value.size();
}
@Override
public boolean isObject() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CubismJsonObject)) return false;
CubismJsonObject that = (CubismJsonObject) o;
if (!value.equals(that.value)) return false;
return keys.equals(that.keys);
}
@Override
public int hashCode() {
int result = value.hashCode();
result = 31 * result + keys.hashCode();
return result;
}
/**
* {@code getString}メソッドで使われる一時的な文字列バッファの最小容量。
*/
private static final int MINIMUM_CAPACITY = 128;
/**
* JSON Object value(map of JSON String and JSON Value)
*/
private final Map<CubismJsonString, ACubismJsonValue> value = new HashMap<CubismJsonString, ACubismJsonValue>();
/**
* List of keys to speed up access to the specified index
*/
private final List<CubismJsonString> keys = new ArrayList<CubismJsonString>();
/**
* {@code getString}で使用される文字列バッファ
*/
private final StringBuffer bufferForGetString = new StringBuffer(MINIMUM_CAPACITY);
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
public class CubismJsonString extends ACubismJsonValue {
/**
* stringのインスタンスを得る
*
* @return stringのインスタンス
*
* @throws IllegalArgumentException If the given value is 'null'.
*/
public static CubismJsonString valueOf(String value) {
if (value == null) {
throw new IllegalArgumentException("The value is null.");
}
return new CubismJsonString(value);
}
@Override
public String getString(String defaultValue, String indent) {
return stringBuffer;
}
@Override
public boolean isString() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CubismJsonString that = (CubismJsonString) o;
return stringBuffer != null ? stringBuffer.equals(that.stringBuffer) : that.stringBuffer == null;
}
@Override
public int hashCode() {
return stringBuffer != null ? stringBuffer.hashCode() : 0;
}
private CubismJsonString(String value) {
stringBuffer = value;
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at http://live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;
/**
* This class expresses JSON Tokens.
*/
class CubismJsonToken {
/**
* Token types
*/
public enum TokenType {
NUMBER, // ex) 0, 1.0, -1.2e+3
STRING, // ex) "test"
BOOLEAN, // 'true' or 'false'
LBRACE, // '{'
RBRACE, // '}'
LSQUARE_BRACKET, // '['
RSQUARE_BRACKET, // ']'
COMMA,
COLON,
NULL, // JSON null value
}
/**
* Construct a CubismJsonToken by specifying only the token type.
*
* @param type token type
*/
public CubismJsonToken(TokenType type) {
tokenType = type;
}
/**
* Construct a string token.
*
* @param value string value
*/
public CubismJsonToken(String value) {
tokenType = TokenType.STRING;
stringValue = value;
}
/**
* Construct a number token.
*
* @param value number value
*/
public CubismJsonToken(double value) {
tokenType = TokenType.NUMBER;
numberValue = value;
}
/**
* Construct a boolean token.
*
* @param value boolean value
*/
public CubismJsonToken(boolean value) {
tokenType = TokenType.BOOLEAN;
booleanValue = value;
}
/**
* Construct a null token.
*/
public CubismJsonToken() {
tokenType = TokenType.NULL;
}
/**
* Get the type of token.
*
* @return token type
*/
public TokenType getTokenType() {
return tokenType;
}
/**
* Get string value.
*
* @return string value
*/
public String getStringValue() {
return stringValue;
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
/**
* Get number value.
*
* @return number value
*/
public double getNumberValue() {
return numberValue;
}
public void setNumberValue(double numberValue) {
this.numberValue = numberValue;
}
/**
* Get boolean value.
*
* @return boolean value
*/
public boolean getBooleanValue() {
return booleanValue;
}
/**
* Token type
*/
private final TokenType tokenType;
/**
* String value
*/
private String stringValue;
/**
* Number value
*/
private double numberValue;
/**
* Boolean value
*/
private boolean booleanValue;
}

View File

@@ -0,0 +1,4 @@
/**
* Provide a function of JSON parser.
*/
package com.live2d.sdk.cubism.framework.utils.jsonparser;

View File

@@ -0,0 +1,4 @@
/**
* Provide utility functions such as a JSON parser and log output.
*/
package com.live2d.sdk.cubism.framework.utils;

View File

@@ -0,0 +1,25 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
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=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=35

Binary file not shown.

View File

@@ -0,0 +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.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
Live2DFramework/gradlew vendored Normal file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
Live2DFramework/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

Some files were not shown because too many files have changed in this diff Show More