Compare commits

...

4 Commits

Author SHA1 Message Date
e2hang
8e3b87051d prod: remove test_reminder, add system notifications, write complete debug journal
Changes:
- Remove test_reminder behavior (debug artifact)
- System tray notifications via QSystemTrayIcon.showMessage()
- IM-style chat bubbles: QLabel pool, tight grouping, tail corners
- Thread safety fix: QThread.currentThread() in add_message()
- docs: complete debug journal (11 sections, from first segfault to final fix)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 14:50:52 +08:00
e2hang
16c24430e3 fix: IM-style chat bubbles with QLabel pool + thread safety fix
Root cause fix: QThread.currentThread() instead of QApplication.instance().thread()
in add_message() — this was the bug causing all segfaults.

Bubble design:
- QLabel widgets with full Qt stylesheet support (border-radius, colors)
- Self: blue rounded bubbles, right-aligned, bottom-right tail
- Other: white rounded bubbles, left-aligned, bottom-left tail
- Sender labels ('你' / ' EzVibe') at group boundaries
- Widget pool: reuse, never delete — avoids layout cascade
- Consecutive messages bundled with tighter margins

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 14:12:52 +08:00
e2hang
710bc84c3f fix: segfault root cause — QThread.currentThread() thread affinity check
Root cause: add_message() used QApplication.instance().thread() to
check thread affinity, but this always returns the main thread (same
as container.thread()), so the check was always False. Qt widget
operations (deleteLater, insertWidget, setHtml) were executed from
the scheduler background thread, corrupting internal state.

Fix: use QThread.currentThread() to compare against the actual
executing thread.

Also: replace QScrollArea+QLabel with QTextBrowser for message
display, eliminating widget management entirely.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 13:57:42 +08:00
e2hang
798e5c2f7d checkpoint: segfault root cause analysis + Live2D stability fixes
- _refresh_messages: widget pool reuse to avoid layout cascade → QWebEngineView crash
- viewer.html: QWebChannel bridge, texture resize, burst render, gl.finish
- live2d_view.py: debug checkpoints, playMotion/setExpression render-on-demand
- main.py: Chromium flags --no-sandbox --in-process-gpu --disable-gpu-rasterization --ignore-gpu-blocklist
- scheduler.py: test_reminder re-enabled
- docs: complete root cause analysis
2026-05-23 13:33:58 +08:00
1399 changed files with 558247 additions and 183 deletions

View File

@@ -606,23 +606,24 @@ class AgentBrain:
activity = context.get("activity_level", 0.5)
emotion = emotion_state or "idle"
now = time.time()
# P0 规则:高频工作 + 非烦躁状态 → 强制健康提醒
# Phase 2.3:使用 Fallback 文案缓存,立即返回不等待 LLM
if activity < 0.15 and emotion != "annoyed":
if self._check_cooldown("remind_health"):
return {
"type": "remind_stretch",
"message": self._fallback_cache.get("remind_stretch"),
"priority": 0,
}
# 喝水提醒(更低优先级)
if activity < 0.4 and self._check_cooldown("remind_water"):
# 喝水提醒:每 30 分钟无条件触发
if not self._is_cooldown_active("remind_water"):
self._record_cooldown("remind_water")
return {
"type": "remind_water",
"message": self._fallback_cache.get("remind_water"),
"priority": 1,
"priority": 0,
}
# 起身伸展提醒:每 40 分钟无条件触发
if not self._is_cooldown_active("remind_stretch"):
self._record_cooldown("remind_stretch")
return {
"type": "remind_stretch",
"message": self._fallback_cache.get("remind_stretch"),
"priority": 0,
}
# 情绪驱动的闲聊触发
@@ -631,7 +632,8 @@ class AgentBrain:
import random
if random.random() < trigger_prob:
nudge = self._emotion_nudge_message(emotion)
if nudge and self._check_cooldown(f"nudge_{emotion}"):
if nudge and not self._is_cooldown_active(f"nudge_{emotion}"):
self._record_cooldown(f"nudge_{emotion}")
return {"type": "nudge", "message": nudge, "priority": 2}
return None
@@ -677,26 +679,32 @@ class AgentBrain:
return random.choice(opts)
return None
def _check_cooldown(self, action_type: str, cooldown: float = 120.0) -> bool:
def _is_cooldown_active(self, action_type: str, cooldown: float = 120.0) -> bool:
"""
检查行为是否在冷却中。
参数
----
action_type : str
行为类型。
cooldown : float
冷却时间(秒),默认 120s2分钟
检查行为是否在冷却中(只读,不修改状态)
返回
----
bool True = 可以触发(不在冷却中False = 冷却中
bool True = 冷却中False = 可以触发
"""
now = time.time()
last = self._action_cooldown.get(action_type, 0.0)
if now - last < cooldown:
return (now - last) < cooldown
def _record_cooldown(self, action_type: str) -> None:
"""记录行为冷却时间戳。"""
self._action_cooldown[action_type] = time.time()
def _check_cooldown(self, action_type: str, cooldown: float = 120.0) -> bool:
"""
兼容旧接口:检查并记录冷却(一次性完成)。
注意:此方法同时检查和记录冷却,适合单次调用的场景。
对于分步判断+记录的复杂场景,请使用 _is_cooldown_active + _record_cooldown。
"""
if self._is_cooldown_active(action_type, cooldown):
return False
self._action_cooldown[action_type] = now
self._record_cooldown(action_type)
return True
# ----------------------------------------------------------------

View File

@@ -431,7 +431,7 @@ class BehaviorScheduler:
name="remind_stretch",
action_fn=_stretch_reminder,
priority=0,
cooldown=3600.0, # 60 分钟
cooldown=2400.0, # 40 分钟
tags=["health", "posture"],
probability=1.0,
description="久坐伸展提醒",
@@ -711,6 +711,79 @@ class BehaviorScheduler:
"description": behavior.description,
}
def get_next_reminder_info(self) -> dict[str, Any] | None:
"""
返回最近一次可触发的 P0 健康提醒的倒计时信息。
返回
----
dict 包含 name, message, seconds_remaining, is_ready无 P0 提醒时返回 None。
"""
now = time.time()
p0_behaviors = [b for b in self._behaviors.values() if b.priority == 0 and b.enabled]
if not p0_behaviors:
return None
candidates = []
for b in p0_behaviors:
elapsed = now - b._last_triggered
remaining = max(0.0, b.cooldown - elapsed)
candidates.append({
"name": b.name,
"cooldown_seconds": b.cooldown,
"seconds_remaining": round(remaining, 1),
"is_ready": b.is_ready(now),
})
# 按剩余时间降序,优先返回即将可触发的
candidates.sort(key=lambda x: x["seconds_remaining"])
for c in candidates:
result = {
"name": c["name"],
"seconds_remaining": c["seconds_remaining"],
"is_ready": c["is_ready"],
"cooldown_seconds": c["cooldown_seconds"],
}
# 附加消息(与内置行为对应)
if c["name"] == "remind_water":
result["message"] = "💧 喝水提醒"
elif c["name"] == "remind_stretch":
result["message"] = "🦵 伸展提醒"
else:
result["message"] = f"{c['name']}"
return result
return None
def get_all_reminders_countdown(self) -> dict[str, dict[str, Any]]:
"""
返回所有 P0 健康提醒的倒计时信息字典。
返回
----
dict {behavior_name: {name, seconds_remaining, is_ready, cooldown_seconds, message}}
"""
now = time.time()
result = {}
for b in self._behaviors.values():
if b.priority != 0 or not b.enabled:
continue
elapsed = now - b._last_triggered
remaining = max(0.0, b.cooldown - elapsed)
info: dict[str, Any] = {
"name": b.name,
"seconds_remaining": round(remaining, 1),
"is_ready": b.is_ready(now),
"cooldown_seconds": b.cooldown,
}
if b.name == "remind_water":
info["message"] = "💧 喝水"
elif b.name == "remind_stretch":
info["message"] = "🦵 伸展"
else:
info["message"] = f"{b.name}"
result[b.name] = info
return result
def get_status(self) -> dict[str, Any]:
"""
返回调度器完整状态(用于调试/监控面板)。

View File

@@ -0,0 +1,389 @@
# 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.5] - 2026-04-02
### Changed
* Change motion calculation order updates to be performed by `CubismUpdateScheduler`.
* Change to configure the parameter IDs and settings used for target tracking in `CubismLook`.
### Removed
* Remove deprecated functions from `LAppWavFileHandler`:
* `getInstance()` (use `new LAppWavFileHandler()` instead)
* `releaseInstance()` (no longer needed with constructor pattern)
* `s_instance` variable (no longer needed with constructor pattern)
## [5-r.5-beta.3.1] - 2026-02-19
### Fixed
* Fix a bug where rendering results were not as expected when using Blend mode on semi-transparent objects.
* See `CHANGELOG.md` in Framework.
## [5-r.5-beta.3] - 2026-01-29
### Changed
* Replace standard `Array`, `Map`, and `String` with `csmVector`, `csmMap`, and `csmString`.
* Change to specify shader files from the Sample layer.
### Fixed
* Fix background image distortion when window size is changed.
* Fix the operation of `CubismOffscreenRenderTargetManager` to be more efficient.
* Improve shader file loading timing to reduce rendering completion time.
## [5-r.5-beta.2] - 2025-10-14
### Added
* Add flag reset processing for the `CubismOffscreenRenderTargetManager` class to improve the efficiency of FrameBuffer used in Offscreen drawing.
### Fixed
* Fix for the efficiency improvements in offscreen drawing introduced in `Framework`.
## [5-r.5-beta.1] - 2025-08-26
### Added
* Add `Ren` model.
* Add `LAppLive2DManager.setOffscreenSize()`.
* This function is to set the size of the `CubismRenderTarget`.
### Changed
* Change to allow resources in `Framework/Shaders` to be registered as shaders.
### Fixed
* Fix an issue where breakpoints were not working when running the `npm: serve` task.
## [5-r.4] - 2025-05-15
### Fixed
* Fix an issue where the priority was not reset if the motion was not read correctly.
* Add a flag to enable the function that verifies the consistency when loading `motion3.json`.
## [5-r.3] - 2025-02-18
### Changed
* Change ESLint version to `9.17.0`.
* With this update, we have added the eslint.confing.mjs required for FlatConfig.
## [5-r.2] - 2024-12-19
### Added
* Add a function to notify when motion playback starts.
### Changed
* Modify to run `tsc --noEmit` command during development build for type checking.
* Change to be able to handle multiple `<canvas>`.
* The interfaces MouseEvent and TouchEvent are deprecated and consolidate into PointerEvent.
* Change to overwrite motion fade by the value specified in .model3.json on Framework.
* Change the function for playing back expression motions from CustomExpressionMotionManager.startMotionPriority() to CustomExpressionMotionManager.startMotion().
### Fixed
* Fix `eslintrc.yml` to conform to the exact wording.
* Fix a bug that the eye tracking behavior was not released when dragging is released outside of a canvas element.
* Fix an issue where `WebGLRenderingContext.deleteTexture()` was not being called in `releaseTextures()`, `releaseTextureByTexture()`, and `releaseTextureByFilePath()`.
## [5-r.1] - 2024-03-26
### Changed
* Change development environment from webpack to Vite.
## [5-r.1-beta.4] - 2024-01-18
### Added
* Add `getPcmDataChannel()` and `getWavSamplingRate()` functions to `LAppWavFileHandler`.
### Changed
* Change return type of `loadWavFile()` to `Promise<boolean>`.
* Deprecate the use of the Singleton pattern in `LAppWavFileHandler`.
* This change marks the following functions and variables as deprecated.
* `s_instance`
* `getInstance()`
* `releaseInstance()`
* Change target to `es6`.
* Change to use `webgl2`.
### Fixed
* Fix to check for error when reading json.
### Removed
* Remove use of `polyfill`, `watwg-fetch` and `experimental-webgl`.
## [5-r.1-beta.3] - 2023-11-30
### Added
* Add `LAppGlManager` class.
### Changed
* Change `LAppDelegate` so that it no longer holds the WebGLContext and moved its role to `LAppGlManager`.
## [5-r.1-beta.2] - 2023-09-28
### Changed
* Replace the sample model `Mao` with the updated version that is compatible with Cubism 5.0.
## [5-r.1-beta.1] - 2023-08-17
### Added
* Add Wankoromochi as a model bundled with SDK.
### Fixed
* Fix blurry image and models on mobile devices. by [@Tsar](https://github.com/Tsar)
* Fix a bug that caused scroll-blocking violations to appear in the log.
## [4-r.7] - 2023-05-25
### Added
* Add compiler options `noImplicitAny` and `useUnknownInCatchVariables` to `tsconfig.json`.
* Add some functions for checking consistency of MOC3 files.
* Add the function of checking consistency in `lappmodel.setupModel()`.
* Add the function of checking consistency before loading a model. (`lappmodel.hasMocConsistencyFromFile()`)
* This feature is enabled by default. Please see the following manual for more information.
* https://docs.live2d.com/cubism-sdk-manual/moc3-consistency/
### Fixed
* Fix a problem in which `haru` motion and voice were incorrect combination.
## [4-r.6.2] - 2023-03-16
### Fixed
* Fix some problems related to Cubism Core.
* See `CHANGELOG.md` in Core.
## [4-r.6.1] - 2023-03-10
### Added
* Add funciton to validate MOC3 files.
* See `CHANGELOG.md` in Core and Framework.
## [4-r.6] - 2023-02-21
### Removed
* Remove Debugger for Chrome from recommended extensions.
* Use `Javascript Debugger`, a built-in feature of Visual Studio Code.
## [4-r.5] - 2022-09-08
### Added
* Add the multilingual supported documents.
## [4-r.5-beta.5] - 2022-08-04
### Changed
* Update `Mao` model.
### Fixed
* Fix crash with exception when reading .moc3 files of unsupported versions.
## [4-r.5-beta.4] - 2022-07-07
### Added
* Add `Mao` model.
## [4-r.5-beta.3] - 2022-06-16
### Fixed
* Fix a problem in which ViewPort was sometimes not set correctly.
### Removed
* End support for Internet Explorer.
## [4-r.5-beta.2] - 2022-06-02
### Fixed
* Fixed a bug that caused Multiply Color / Screen Color of different objects to be applied.
* See `CHANGELOG.md` in Core.
* No modifications to Samples and Framework.
## [4-r.5-beta.1] - 2022-05-19
### Added
* Support Multiply Color / Screen Color added in Cubism 4.2.
* See Framework and Core.
## [4-r.4] - 2021-12-09
### Changed
* Update sample models. (Made with Cubism Editor 4.1.02)
### Fixed
* Fix a bug where the move process would affect other models while displaying multiple models. by [@catCoder](https://community.live2d.com/discussion/1043/multiple-models-when-using-translaterelative)
* Fix breathing behavior was different from Cubism Viewer (for OW).
## [4-r.3] - 2021-06-10
### Fixed
* Fixed the model path. It was causing a 404 when an exact path was required.
## [4-r.3-beta.1] - 2021-05-13
### Added
* Add the sample to manipulate the lip-sync from a waveform on the audio file(.wav).
* Add sample voices to `Haru`.
## [4-r.2] - 2021-03-09
### Added
* Add dynamic screen size and touch detection support.
### Fixed
* Adjust size calculation for models displayed in a window and fix to use a view matrix.
* Avoiding needless namespace syntax to simplify imports by [@cocor-au-lait](https://github.com/cocor-au-lait)
## [4-r.1] - 2020-01-30
### Added
* Add development workflow using *Webpack Dev Server*.
* Add `README.md` to Sample project.
* Add Prettier and ESLint for format and check code quality.
### Changed
* `/Framework` directory is now git submodule.
* See [Cubism Web Framework CHANGELOG](/Framework/CHANGELOG.md) about framework changes.
* Rename `/Sample` directory to `/Samples`
* Move `/Resources` directory to just below `/Samples` directory.
* Move `package.json` and `tsconfig.json` to Sample project.
* Reformat code using Prettier and ESLint.
### Removed
* Remove some unused functions in Demo project.
* Remove Webpack settings for framework.
## [4-beta.2] - 2019-11-14
### Fixed
* Fix file name case of `cubismrenderer_webgl.ts`.
## [4-beta.1] - 2019-09-04
### Added
* Add method for Moc file version.
* Support new Inverted Masking features.
* Add `.editorconfig` and `.gitattributes` to manage file formats.
* Add `CHANGELOG.md`.
* Add way to create a simpler local server.
* Add sample model `/Sample/TypeScript/Demo/Resources/Rice`.
### Changed
* Upgrade Core version to 04.00.0000 (67108864).
* Convert all file formats according to `.editorconfig`.
* Rename `cubismrenderer_WebGL.ts` to `cubismrenderer_webgl.ts`.
* What was `CubismSdkPackage.json` is currently being changed to`cubism-info.yml`.
* Upgrade all dependency packages and lock package version by adding `package-lock.json`.
* Update `README.md`.
* Delete guidance from `README.md` due to suspension of *Cubism Bindings*.
* Update `.gitignore`.
### Fixed
* Fix issue with reloading model images in WebKit.
[5-r.5]: https://github.com/Live2D/CubismWebSamples/compare/5-r.5-beta.3.1...5-r.5
[5-r.5-beta.3.1]: https://github.com/Live2D/CubismWebSamples/compare/5-r.5-beta.3...5-r.5-beta.3.1
[5-r.5-beta.3]: https://github.com/Live2D/CubismWebSamples/compare/5-r.5-beta.2...5-r.5-beta.3
[5-r.5-beta.2]: https://github.com/Live2D/CubismWebSamples/compare/5-r.5-beta.1...5-r.5-beta.2
[5-r.5-beta.1]: https://github.com/Live2D/CubismWebSamples/compare/5-r.4...5-r.5-beta.1
[5-r.4]: https://github.com/Live2D/CubismWebSamples/compare/5-r.3...5-r.4
[5-r.3]: https://github.com/Live2D/CubismWebSamples/compare/5-r.2...5-r.3
[5-r.2]: https://github.com/Live2D/CubismWebSamples/compare/5-r.1...5-r.2
[5-r.1]: https://github.com/Live2D/CubismWebSamples/compare/5-r.1-beta.4...5-r.1
[5-r.1-beta.4]: https://github.com/Live2D/CubismWebSamples/compare/5-r.1-beta.3...5-r.1-beta.4
[5-r.1-beta.3]: https://github.com/Live2D/CubismWebSamples/compare/5-r.1-beta.2...5-r.1-beta.3
[5-r.1-beta.2]: https://github.com/Live2D/CubismWebSamples/compare/5-r.1-beta.1...5-r.1-beta.2
[5-r.1-beta.1]: https://github.com/Live2D/CubismWebSamples/compare/4-r.7...5-r.1-beta.1
[4-r.7]: https://github.com/Live2D/CubismWebSamples/compare/4-r.6.2...4-r.7
[4-r.6.2]: https://github.com/Live2D/CubismWebSamples/compare/4-r.6.1...4-r.6.2
[4-r.6.1]: https://github.com/Live2D/CubismWebSamples/compare/4-r.6...4-r.6.1
[4-r.6]: https://github.com/Live2D/CubismWebSamples/compare/4-r.5...4-r.6
[4-r.5]: https://github.com/Live2D/CubismWebSamples/compare/4-r.5-beta.5...4-r.5
[4-r.5-beta.5]: https://github.com/Live2D/CubismWebSamples/compare/4-r.5-beta.4...4-r.5-beta.5
[4-r.5-beta.4]: https://github.com/Live2D/CubismWebSamples/compare/4-r.5-beta.3...4-r.5-beta.4
[4-r.5-beta.3]: https://github.com/Live2D/CubismWebSamples/compare/4-r.5-beta.2...4-r.5-beta.3
[4-r.5-beta.2]: https://github.com/Live2D/CubismWebSamples/compare/4-r.5-beta.1...4-r.5-beta.2
[4-r.5-beta.1]: https://github.com/Live2D/CubismWebSamples/compare/4-r.4...4-r.5-beta.1
[4-r.4]: https://github.com/Live2D/CubismWebSamples/compare/4-r.3...4-r.4
[4-r.3]: https://github.com/Live2D/CubismWebSamples/compare/4-r.3-beta.1...4-r.3
[4-r.3-beta.1]: https://github.com/Live2D/CubismWebSamples/compare/4-r.2...4-r.3-beta.1
[4-r.2]: https://github.com/Live2D/CubismWebSamples/compare/4-r.1...4-r.2
[4-r.1]: https://github.com/Live2D/CubismWebSamples/compare/4-beta.2...4-r.1
[4-beta.2]: https://github.com/Live2D/CubismWebSamples/compare/4-beta.1...4-beta.2
[4-beta.1]: https://github.com/Live2D/CubismWebSamples/compare/e36ab2233a89de9225f64e5a02d521bc7235bd03...4-beta.1

View File

@@ -0,0 +1,426 @@
# 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/).
## 2026-04-02
### Removed
* [Unity] Remove Android x86 library.
## 2026-01-29
### Added
* Add Arm64 static library for iphonesimulator.
## 2026-01-08
### Changed
* Upgrade Core version to 06.00.0001.
### Fixed
* Fix offscreen opacity calculation.
## 2025-10-30
### Removed
* [Native] Remove Visual Studio 2015 (MSVC 140) static library.
## 2025-10-14
### Added
* Add `csmGetMocVersion(mocBytes: ArrayBuffer)` with simplified arguments from `csmGetMocVersion(moc: Moc, mocBytes: ArrayBuffer)`.
* The previous version `csmGetMocVersion(moc: Moc, mocBytes: ArrayBuffer)` is now deprecated and is planned to be removed in the future.
## 2025-08-26
### Added
* Enhanced model rendering features have been added.
### Changed
* Upgrade Core version to 06.00.0000.
## 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.
### Changed
* Upgrade Core version to 05.01.0000.
## 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,30 @@
[English](README.md) / [日本語](README.ja.md)
---
# Live2D Cubism Core
このフォルダーには、JavaScriptまたはTypeScriptアプリケーションを開発するためのコアライブラリファイルが含まれています。
## ファイルリスト
### live2dcubismcore.d.ts
このファイルには、`live2dcubismcore.js`に関するTypeScriptの型情報が含まれています。
TypeScriptで開発する場合は、このファイルを`live2dcubismcore.js`とともに使用してください。
### live2dcubismcore.js
このファイルには、CubismCoreの機能といくつかのラッパーが含まれています。
JavaScriptで開発する場合は、このファイルを使用してください。
### live2dcubismcore.js.map
このファイルは、`live2dcubismcore.d.ts``live2dcubismcore.js`の間のソースマップです。
デバッグ時にこのファイルを使用します。
### live2dcubismcore.min.js
このファイルは、`live2dcubismcore.js`のminify版です。
このファイルを本番環境で使用します。

View File

@@ -0,0 +1,30 @@
[English](README.md) / [日本語](README.ja.md)
---
# Live2D Cubism Core
This folder contains core library files for developing JavaScript or TypeScript applications.
## File List
### live2dcubismcore.d.ts
This file contains typescript type information about `live2dcubismcore.js`.
Use this file with `live2dcubismcore.js` when developing with TypeScript.
### live2dcubismcore.js
This file contains Cubism Core features and some wrapper features.
Use this file when developing with JavaScript.
### live2dcubismcore.js.map
This file is the source map between `live2dcubismcore.d.ts` and `live2dcubismcore.js`.
Use this file when debugging.
### live2dcubismcore.min.js
This file is the minified version of `live2dcubismcore.js`.
Use this file in production.

View File

@@ -0,0 +1,6 @@
The following is a list of files available for redistribution
under the terms of the Live2D Proprietary Software License Agreement:
- live2dcubismcore.d.ts
- live2dcubismcore.js
- live2dcubismcore.min.js

View File

@@ -0,0 +1,463 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Proprietary Software license
* that can be found at https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html.
*/
declare namespace Live2DCubismCore {
/** Cubism version identifier. */
type csmVersion = number;
/** moc3 version identifier. */
type csmMocVersion = number;
/** Parameter type identifier. */
type csmParameterType = number;
/** Necessary alignment for mocs (in bytes). */
const AlignofMoc: number;
/** Necessary alignment for models (in bytes). */
const AlignofModel: number;
/** .moc3 file version Unknown */
const MocVersion_Unknown: number;
/** .moc3 file version 3.0.00 - 3.2.07 */
const MocVersion_30: number;
/** .moc3 file version 3.3.00 - 3.3.03 */
const MocVersion_33: number;
/** .moc3 file version 4.0.00 - 4.1.05 */
const MocVersion_40: number;
/** .moc3 file version 4.2.00 - 4.2.04 */
const MocVersion_42: number;
/** .moc3 file version 5.0.00 - 5.2.03 */
const MocVersion_50: number;
/** .moc3 file version 5.3.00 - */
const MocVersion_53: number;
/** Normal Parameter. */
const ParameterType_Normal: number;
/** Parameter for blend shape. */
const ParameterType_BlendShape: number;
/** Normal blend. */
const ColorBlendType_Normal: number;
/** Add blend. */
const ColorBlendType_Add: number;
/** AddGlow blend. */
const ColorBlendType_AddGlow: number;
/** Darken blend. */
const ColorBlendType_Darken: number;
/** Multiply blend. */
const ColorBlendType_Multiply: number;
/** ColorBurn blend. */
const ColorBlendType_ColorBurn: number;
/** LinearBurn blend. */
const ColorBlendType_LinearBurn: number;
/** Lighten blend. */
const ColorBlendType_Lighten: number;
/** Screen blend. */
const ColorBlendType_Screen: number;
/** ColorDodge blend. */
const ColorBlendType_ColorDodge: number;
/** Overlay blend. */
const ColorBlendType_Overlay: number;
/** SoftLight blend. */
const ColorBlendType_SoftLight: number;
/** HardLight blend. */
const ColorBlendType_HardLight: number;
/** LinearLight blend. */
const ColorBlendType_LinearLight: number;
/** Hue blend. */
const ColorBlendType_Hue: number;
/** Color blend. */
const ColorBlendType_Color: number;
/** Add compatible blend. */
const ColorBlendType_AddCompatible: number;
/** Multiply compatible blend. */
const ColorBlendType_MultiplyCompatible: number;
/** Over blend. */
const AlphaBlendType_Over: number;
/** Atop blend. */
const AlphaBlendType_Atop: number;
/** Out blend. */
const AlphaBlendType_Out: number;
/** ConjointOver blend. */
const AlphaBlendType_ConjointOver: number;
/** DisjointOver blend. */
const AlphaBlendType_DisjointOver: number;
/** Log handler.
*
* @param message Null-terminated string message to log.
*/
interface csmLogFunction {
(message: string): void;
}
/** Cubism version. */
class Version {
/**
* Queries Core version.
*
* @return Core version.
*/
static csmGetVersion(): csmVersion;
/**
* Gets Moc file supported latest version.
*
* @return Moc file latest format version.
*/
static csmGetLatestMocVersion(): csmMocVersion;
/**
* Gets Moc file's format version
*
* @param {Moc} moc Moc
* @param {ArrayBuffer} mocBytes Arraybuffer
* @returns {csmMocVersion} Moc file format version.
*/
static csmGetMocVersion(moc: Moc, mocBytes: ArrayBuffer): csmMocVersion;
/**
* Gets Moc file's format version
*
* @param {ArrayBuffer} mocBytes Moc bytes.
* @returns {csmMocVersion} Moc file format version.
*/
static csmGetMocVersion(mocBytes: ArrayBuffer): csmMocVersion;
private constructor();
}
/** Cubism logging. */
class Logging {
private static logFunction;
/**
* Sets log handler.
*
* @param handler Handler to use.
*/
static csmSetLogFunction(handler: csmLogFunction): void;
/**
* Queries log handler.
*
* @return Log handler.
*/
static csmGetLogFunction(): csmLogFunction;
/**
* Wrap log function.
*
* @param messagePtr number
*
* @return string
*/
private static wrapLogFunction;
private constructor();
}
/** Cubism moc. */
class Moc {
/**
* Checks consistency of a moc.
*
* @param mocBytes Moc bytes.
*
* @returns '1' if Moc is valid; '0' otherwise.
*/
hasMocConsistency(mocBytes: ArrayBuffer): number;
/** Creates [[Moc]] from [[ArrayBuffer]].
*
* @param buffer Array buffer
*
* @return [[Moc]] on success; [[null]] otherwise.
*/
static fromArrayBuffer(buffer: ArrayBuffer): Moc;
/** Releases instance. */
_release(): void;
/** Native moc. */
_ptr: number;
/**
* Initializes instance.
*
* @param mocBytes Moc bytes.
*/
private constructor();
}
/** Cubism model. */
class Model {
/** Parameters. */
parameters: Parameters;
/** Parts. */
parts: Parts;
/** Drawables. */
drawables: Drawables;
/** Offscreen. */
offscreens: Offscreens;
/** Canvas information. */
canvasinfo: CanvasInfo;
/** Object render orders. */
private renderOrders;
/**
* Creates [[Model]] from [[Moc]].
*
* @param moc Moc
*
* @return [[Model]] on success; [[null]] otherwise.
*/
static fromMoc(moc: Moc): Model;
/**
* Gets object render orders.
*
* @returns {Int32Array} Object render orders.
*/
getRenderOrders(): Int32Array;
/** Updates instance. */
update(): void;
/** Releases instance. */
release(): void;
/** Native model. */
_ptr: number;
/**
* Initializes instance.
*
* @param moc Moc
*/
private constructor();
}
/** Canvas information interface. */
class CanvasInfo {
/** Width of native model canvas. */
CanvasWidth: number;
/** Height of native model canvas. */
CanvasHeight: number;
/** Coordinate origin of X axis. */
CanvasOriginX: number;
/** Coordinate origin of Y axis. */
CanvasOriginY: number;
/** Pixels per unit of native model. */
PixelsPerUnit: number;
/**
* Initializes instance.
*
* @param modelPtr Native model pointer.
*/
constructor(modelPtr: number);
}
/** Cubism model parameters */
class Parameters {
/** Parameter count. */
count: number;
/** Parameter IDs. */
ids: Array<string>;
/** Minimum parameter values. */
minimumValues: Float32Array;
/** Parameter types. */
types: Int32Array;
/** Maximum parameter values. */
maximumValues: Float32Array;
/** Default parameter values. */
defaultValues: Float32Array;
/** Parameter values. */
values: Float32Array;
/** Parameter Repeat informations. */
repeats: Int32Array;
/** Number of key values of each parameter. */
keyCounts: Int32Array;
/** Key values of each parameter. */
keyValues: Array<Float32Array>;
/**
* Initializes instance.
*
* @param modelPtr Native model.
*/
constructor(modelPtr: number);
}
/** Cubism model parts */
class Parts {
/** Part count. */
count: number;
/** Part IDs. */
ids: Array<string>;
/** Opacity values. */
opacities: Float32Array;
/** Part's parent part indices. */
parentIndices: Int32Array;
/** Part's offscreen indices. If the part does not use an offscreen, the value is '-1'. */
offscreenIndices: Int32Array;
/**
* Initializes instance.
*
* @param modelPtr Native model.
*/
constructor(modelPtr: number);
}
/** Cubism model drawables */
class Drawables {
/** Drawable count. */
count: number;
/** Drawable IDs. */
ids: Array<string>;
/** Constant drawable flags. */
constantFlags: Uint8Array;
/** Dynamic drawable flags. */
dynamicFlags: Uint8Array;
/** Drawable texture indices. */
textureIndices: Int32Array;
/** Drawable draw orders. */
drawOrders: Int32Array;
/** Drawable opacities. */
opacities: Float32Array;
/** Mask count for each drawable. */
maskCounts: Int32Array;
/** Masks for each drawable. */
masks: Array<Int32Array>;
/** Number of vertices of each drawable. */
vertexCounts: Int32Array;
/** 2D vertex position data of each drawable. */
vertexPositions: Array<Float32Array>;
/** 2D texture coordinate data of each drawables. */
vertexUvs: Array<Float32Array>;
/** Number of triangle indices for each drawable. */
indexCounts: Int32Array;
/** Triangle index data for each drawable. */
indices: Array<Uint16Array>;
/** Information multiply color. */
multiplyColors: Float32Array;
/** Information Screen color. */
screenColors: Float32Array;
/** Indices of drawables parent part. */
parentPartIndices: Int32Array;
/** Blend modes of drawables. */
blendModes: Int32Array;
/** Resets all dynamic drawable flags.. */
resetDynamicFlags(): void;
/** Native model. */
private _modelPtr;
/**
* Initializes instance.
*
* @param modelPtr Native model.
*/
constructor(modelPtr: number);
}
/** Cubism model offscreens */
class Offscreens {
/** Number of offscreens. */
count: number;
/** Offscreen blend modes. */
blendModes: Int32Array;
/** Offscreen opacities. */
opacities: Float32Array;
/** Offscreen's owner indices. */
ownerIndices: Int32Array;
/** Offscreen's multiply colors. */
multiplyColors: Float32Array;
/** Offscreen's screen colors. */
screenColors: Float32Array;
/** Offscreen's mask counts. */
maskCounts: Int32Array;
/** Offscreen's masks. */
masks: Array<Int32Array>;
/** Offscreen's constant flags. */
constantFlags: Uint8Array;
/**
* Initializes instance.
*
* @param modelPtr Native model.
*/
constructor(modelPtr: number);
}
/** Utility functions. */
class Utils {
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasBlendAdditiveBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasBlendMultiplicativeBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasIsDoubleSidedBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasIsInvertedMaskBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasIsVisibleBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasVisibilityDidChangeBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasOpacityDidChangeBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasDrawOrderDidChangeBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasRenderOrderDidChangeBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasVertexPositionsDidChangeBit(bitfield: number): boolean;
/**
* Checks whether flag is set in bitfield.
*
* @param bitfield Bitfield to query against.
*
* @return [[true]] if bit set; [[false]] otherwise
*/
static hasBlendColorDidChangeBit(bitfield: number): boolean;
}
/** Memory functions. */
class Memory {
/**
* HACK:
* Extend memory size allocated during module initialization.
* If the specified size is less than or equal to 16777216(byte), the default of 16 MB is allocated.
*
* @see https://github.com/emscripten-core/emscripten/blob/main/src/settings.js#L161
*
* @param size allocated memory size [byte(s)]
*/
static initializeAmountOfMemory(size: number): void;
private constructor();
}
/** Emscripten Cubism Core module. */
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,463 @@
# 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.5] - 2026-04-02
### Added
* Add functionality to change motion calculation order.
* Add `cubismlook` class that implements the target tracking feature.
* The target tracking feature can now specify parameter IDs through the `Framework`.
### Changed
* Change multiply and screen color functions to separate class with renamed methods.
### Fixed
* Fix unnecessary multiply color and screen color settings in mask drawing.
### Removed
* Remove deprecated functions from CubismMotion:
* `setIsLoop()` (use `setLoop()` instead)
* `isLoop()` (use `getLoop()` instead)
* `setIsLoopFadeIn()` (use `setLoopFadeIn()` instead)
* `isLoopFadeIn()` (use `getLoopFadeIn()` instead)
* Remove deprecated functions from CubismExpressionMotionManager:
* `getCurrentPriority()` (priority is not used in expression motion playback)
* `getReservePriority()` (priority is not used in expression motion playback)
* `setReservePriority()` (priority is not used in expression motion playback)
* `startMotionPriority()` (use `startMotion()` instead)
* Remove deprecated fields from CubismExpressionMotionManager:
* `_currentPriority` (priority is not used in expression motion playback)
* `_reservePriority` (priority is not used in expression motion playback)
* Remove deprecated function from CubismExpressionMotion:
* `getFadeWeight()` (use `CubismExpressionMotionManager.getFadeWeight()` instead)
* Remove deprecated field from CubismExpressionMotion:
* `_fadeWeight` (can cause bugs)
* Remove deprecated functions from CubismModel:
* `getOverwriteFlagForModelCullings()` (renamed to `getOverrideFlagForModelCullings()`)
* `setOverwriteFlagForModelCullings()` (renamed to `setOverrideFlagForModelCullings()`)
* `getOverwriteFlagForDrawableCullings()` (renamed to `getOverrideFlagForDrawableCullings()`)
* `setOverwriteFlagForDrawableCullings()` (renamed to `setOverrideFlagForDrawableCullings()`)
## [5-r.5-beta.3.1] - 2026-02-19
### Fixed
* Fix a bug where rendering results were not as expected when using Blend mode on semi-transparent objects.
* Fix a bug where the `stopAllMotions()` function in the `CubismMotionQueueManager` class did not work correctly.
## [5-r.5-beta.3] - 2026-01-29
### Changed
* Replace standard `Array`, `Map`, and `String` with `csmVector`, `csmMap`, and `csmString`.
* Change to allow shader files to be specified outside the `CubismFramework`.
* Change the shader strings written in the `CubismShader_WebGL` class to use external files.
### Fixed
* Fix the operation of `CubismOffscreenRenderTargetManager` to be more efficient.
* Fix the timing of beforeDrawModelRenderTarget call.
## [5-r.5-beta.2] - 2025-10-14
### Added
* Add classes `CubismOffscreenRenderTarget_WebGL` and `CubismOffscreenManager` that reuse `WebGLFramebuffer` for Offscreen drawing.
* Add `getMocVersionFromBuffer()`function.
* Support for retrieving MOC version from unsupported new MOC3 files.
### Removed
* Remove unnecessary classes and variables.
## [5-r.5-beta.1] - 2025-08-26
### Added
* Add `cubismrendertarget_webgl.ts`.
* Define a class `CubismRenderTarget_WebGL` that manages the frame buffer and color buffer.
* Add support for Blend mode and Offscreen drawing.
### Changed
* Change the rendering context requirement in WebGL environments to `WebGL2RenderingContext`.
* In particular, when using the blend modes added in Cubism 5.3 and later, `WebGL2RenderingContext.blitFramebuffer()` is used.
* Change the clipping mask processing uses class `CubismRenderTarget_WebGL`.
* Change the API called to retrieve the drawable render order from `getDrawableRenderOrders()` to `getRenderOrders()` in CubismCore.
* See `CHANGELOG.md` in Core.
## [5-r.4] - 2025-05-15
### Added
* 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.
* 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()`
### Fixed
* Fix an issue with detecting the end of playback when looping motion.
## [5-r.3] - 2025-02-18
### Added
* Add new motion loop processing that seamlessly connects the start and end points of the loop.
* The `_isLoop` variable was moved from class `CubismMotion` to class `ACubismMotion`.
* Add the setter for `_isLoop`, `setLoop()`, 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()`, 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 ESLint version to `9.17.0`.
* With this update, we have added the eslint.confing.mjs required for FlatConfig.
### Deprecated
* Deprecate the following elements due to the change in the variable declaration location.
* `CubismMotion.setIsLoop()`
* `CubismMotion.isLoop()`
* `CubismMotion.setIsLoopFadeIn()`
* `CubismMotion.isLoopFadeIn()`
## [5-r.2] - 2024-12-19
### Added
* Add the functionality to call a function when motion playback starts.
* Add an API to `CubismMotionJson` for verifying the consistency of `motion3.json`. by [@pillowtrucker](https://github.com/Live2D/CubismNativeFramework/pull/57)
* This API is ported from `Cubism Native Framework`.
### Changed
* Change to create and manage a `CubismShader` for each `GLRenderingContext`.
* Change the access level of the private members in `CubismModelSettingJson` class to protected.
* Move JSON key strings set to the member variables of `CubismModelSettingJson` class.
* Change `FrequestNode` to be exported as part of the module.
* Change to permit to overwrite motion fade by the value specified in .model3.json on `CubismUserModel.loadMotion()`.
* Change the value of pi used in the calculation of `CubismBreath.updateParameters()` to `Math.PI`.
### Deprecated
* Deprecate the following elements because a priority value is not actually used during expression motion playback:
* `CubismExpressionMotionManager._currentPriority`
* `CubismExpressionMotionManager._reservePriority`
* `CubismExpressionMotionManager.startMotionPriority()`
* `CubismExpressionMotionManager.getCurrentPriority()`
* `CubismExpressionMotionManager.getReservePriority()`
* `CubismExpressionMotionManager.setReservePriority()`
Please use the `CubismMotionQueueManager.startMotion()` instead of `CubismExpressionMotionManager.startMotionPriority()`.
### Fixed
* Fix an issue where already registered keys could be added on `csmMap.appendKey()`.
* Fix a bug that caused an error when playing `CubismExpresionMotion` with `CubismMotionQueueManager.startMotion()`.
* Fix an issue where `CubismMath.cardanoAlgorithmForBezier()` was using a different function than Cubism SDK for Native.
* Fix a potential problem with division by 0 when a pose fade time is set to 0 seconds.
* Fix an issue where `CubismPose._fadeTimeSeconds` does not become 0.
## [5-r.1] - 2024-03-26
### Added
* Add function `mod()` to compute floating-point remainder in `CubismMath` class.
### Changed
* Change an expression "overwrite" to "override" for multiply color, screen color, and culling to adapt the actual behavior.
* Change the weight value in `Expression` from `CubismExpressionMotion` to have it in the `CubismExpressionMotionManager`.
* Reorganize the names of some functions and variables.
* This is a change that depends on fixing `eslintrc.yml`.
* Change to output log if the argument `motionQueueEntry` is `null` in the `updateFadeWeight()` function of the `ACubismMotion` class.
### Fixed
* Fix `eslintrc.yml` to conform to the exact wording.
### 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 third argument `userTimeSeconds` is deprecated.
* Please use the `startMotion()` function with one arguments from now on.
## [5-r.1-beta.4] - 2024-01-18
### Changed
* Change target to `es6`.
### Fixed
* Fix an issue where models with a specific number of masks could not be drawn correctly.
* Fix to check for error when reading json.
## [5-r.1-beta.3] - 2023-11-30
## [5-r.1-beta.2] - 2023-09-28
### Added
* Add a comment for clarity for the function whose usage is not intuitive.
## [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 visibility of the `CubismId` constructor to private.
* Please use `CubismFramework.getIdManager().getId()` to get `CubismId`.
* Change the word `DrawMesh` to `DrawMeshWebGL`.
### Fixed
* Fix a bug that the value applied by multiply was not appropriate during expression transitions.
* Fix the structure of the class in renderer.
* Fix a issue where `ARRAY_BUFFER` was used on multiple targets.
* Separate shader class from `cubismrenderer` class.
* Separate the high precision mask process from the clipping mask setup process.
### Removed
* Remove several arguments of `DrawMesh` function.
## [4-r.7] - 2023-05-25
### Added
* Add compiler options `noImplicitAny` and `useUnknownInCatchVariables` to `tsconfig.json`.
* Add some function for checking consistency of MOC3.
* Add the function of checking consistency on reviving a MOC3. (`CubismMoc::Create`)
* Add a function to parse the opacity from `.motion3.json`.
* Add some functions to change Multiply and Screen colors on a per part basis.
### Changed
* Change access specifier for `CubismExpressionMotion`.
### Fixed
* Fix to support added compiler options `noImplicitAny` and `useUnknownInCatchVariables`.
## [4-r.6.2] - 2023-03-16
### Fixed
* Fix some problems related to Cubism Core.
* See `CHANGELOG.md` in Core.
## [4-r.6.1] - 2023-03-10
### Added
* Add function to validate MOC3 files.
## [4-r.6] - 2023-02-21
### Added
* Add support for high-precision masks.
* 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".
* Add API to allow users to configure culling.
### Changed
* Change to not reference `CubismClippingManager_WebGL` on models that do not use clipping masks.
### Fixed
* Fix a crash when a `WebGLRenderingContext` is not registered with `Cubism Renderer_WebGL`.
* It now displays a warning and does not draw models.
* Fix a bug when displaying a model with culling set, some of the other drawn images are missing.
* Fix a bug that caused update information for some models not to be updated when multiple models are displayed.
* Call the function to extend the initial memory with CubismFramework.initialize(). See `CHANGELOG.md` in Core.
## [4-r.5] - 2022-09-08
### Added
* Add the multilingual supported documents.
* Add immediate stabilization of physics.
* Implemented a process to switch between `CubismJson` parsing and `JSON.parse()`.
## [4-r.5-beta.5] - 2022-08-04
### Fixed
* Fix `csmGetMocVersion` function argument.
* Fix a bug in which processing was interrupted when an invalid vertex was specified in the middle of a physics operation.
* Fix crash with exception when reading .moc3 files of unsupported versions.
* Fix physics system input to be split by the physics setting time.
## [4-r.5-beta.4] - 2022-07-07
### Added
* Add a function to get the latest .moc3 Version and the .moc3 Version of the loaded model.
* Add a function to get the type of parameters of the model.
* Add a function to get the parent part of the model's Drawable.
## [4-r.5-beta.3] - 2022-06-16
### Fixed
* `getDrawableTextureIndices` function in `CubismModel` has been renamed to `getDrawableTextureIndex` because the name was not correct.
* `getDrawableTextureIndices` function is marked as deprecated.
* Fix physics system behaviour when exists Physics Fps Setting in .physics3.json.
## [4-r.5-beta.2] - 2022-06-02
### Fixed
* Fixed a bug that caused Multiply Color / Screen Color of different objects to be applied.
* See `CHANGELOG.md` in Core.
* No modifications to Samples and Framework.
## [4-r.5-beta.1] - 2022-05-19
### Added
* Add processing related to Multiply Color / Screen Color added in Cubism 4.2.
### Fixed
* Fix usage of Anisotropy filtering.
* Fix model was not displayed when the number of masks exceeded the limit.
* Fix getTextureDirectory() to return the directory name of the 0th texture path.
## [4-r.4] - 2021-12-09
### Fixed
* Fix useless void 0.
* Fix a warning when `SegmentType` could not be obtained when loading motion.
* Fix return correct error values for out-of-index arguments in cubismjson by [@cocor-au-lait](https://github.com/cocor-au-lait).
* Fix a bug that motions currently played do not fade out when play a motion.
## [4-r.3] - 2021-06-10
### Fixed
* Fix motion event time value from Int to Float.
## [4-r.3-beta.1] - 2021-05-13
### Added
* Implement a function to get the correct value when the time axis of the Bezier handle cannot be linear.
## [4-r.2] - 2021-03-09
### Fixed
* Fix implementation of `iterator#increment` in `csmmap` and `csmvector`.
* Fix delay in starting fade-out for expressions.
* Fix Physics input reflect flag on evaluate.
* Fix reference size of model matrix.
* Fix `Int` to `Float` when getting `PhysicsSettings.Vertices.Radius` in `physics3.json` parsing.
* **[INFO]** This fix may change the behavior of the physics operations.
The behavior changes if the value of `PhysicsSettings.Vertices.Radius` in `physics3.json` is less than `1.0`.
If you want to return to the behavior before Cubism SDK for Web R1,
change the value of the corresponding `PhysicsSettings.Vertices.Radius` to `0`.
* This fix is related to fix applied to `Cubism Editor 4.0.05 beta1` and later.
Please see [Cubism Editor Changelog](https://docs.live2d.com/cubism-editor-manual/updates4/).
* **Fix physics and scene blending settings where the length of the pendulum would be converted to an integer when displayed.**
### Changed
* Rename the function name that handles seconds from `Time` to `Seconds`.
* Avoiding needless namespace syntax to simplify imports by [@cocor-au-lait](https://github.com/cocor-au-lait)
## [4-r.1] - 2020-01-30
### Added
* Add `.editorconfig`, `.gitattributes` and `.gitignore`.
* Add document `README.md` and `CHANGELOG.md`.
* Add `package.json` for development and build.
* Add Prettier and ESLint for format and check code quolity.
### Changed
* Move source files to `/src` directory.
* Reformat code using Prettier and ESLint.
[5-r.5]: https://github.com/Live2D/CubismWebFramework/compare/5-r.5-beta.3.1...5-r.5
[5-r.5-beta.3.1]: https://github.com/Live2D/CubismWebFramework/compare/5-r.5-beta.3...5-r.5-beta.3.1
[5-r.5-beta.3]: https://github.com/Live2D/CubismWebFramework/compare/5-r.5-beta.2...5-r.5-beta.3
[5-r.5-beta.2]: https://github.com/Live2D/CubismWebFramework/compare/5-r.5-beta.1...5-r.5-beta.2
[5-r.5-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/5-r.4...5-r.5-beta.1
[5-r.4]: https://github.com/Live2D/CubismWebFramework/compare/5-r.3...5-r.4
[5-r.3]: https://github.com/Live2D/CubismWebFramework/compare/5-r.2...5-r.3
[5-r.2]: https://github.com/Live2D/CubismWebFramework/compare/5-r.1...5-r.2
[5-r.1]: https://github.com/Live2D/CubismWebFramework/compare/5-r.1-beta.4...5-r.1
[5-r.1-beta.4]: https://github.com/Live2D/CubismWebFramework/compare/5-r.1-beta.3...5-r.1-beta.4
[5-r.1-beta.3]: https://github.com/Live2D/CubismWebFramework/compare/5-r.1-beta.2...5-r.1-beta.3
[5-r.1-beta.2]: https://github.com/Live2D/CubismWebFramework/compare/5-r.1-beta.1...5-r.1-beta.2
[5-r.1-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.7...5-r.1-beta.1
[4-r.7]: https://github.com/Live2D/CubismWebFramework/compare/4-r.6.2...4-r.7
[4-r.6.2]: https://github.com/Live2D/CubismWebFramework/compare/4-r.6.1...4-r.6.2
[4-r.6.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.6...4-r.6.1
[4-r.6]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5...4-r.6
[4-r.5]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.5...4-r.5
[4-r.5-beta.5]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.4...4-r.5-beta.5
[4-r.5-beta.4]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.3...4-r.5-beta.4
[4-r.5-beta.3]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.2...4-r.5-beta.3
[4-r.5-beta.2]: https://github.com/Live2D/CubismWebFramework/compare/4-r.5-beta.1...4-r.5-beta.2
[4-r.5-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.4...4-r.5-beta.1
[4-r.4]: https://github.com/Live2D/CubismWebFramework/compare/4-r.3...4-r.4
[4-r.3]: https://github.com/Live2D/CubismWebFramework/compare/4-r.3-beta.1...4-r.3
[4-r.3-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.2...4-r.3-beta.1
[4-r.2]: https://github.com/Live2D/CubismWebFramework/compare/4-r.1...4-r.2
[4-r.1]: https://github.com/Live2D/CubismWebFramework/compare/ce2585a919ac6e99f64dd468933772c6f1abbcc7...4-r.1

View File

@@ -0,0 +1,45 @@
## Definitions
### Live2D Cubism Components
Cubism Web Framework is included in Live2D Cubism Components.
Cubism Web Framework は Live2D Cubism Components に含まれます。
Cubism Web 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](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,170 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Web Framework
Live2D Cubism Editor で出力したモデルをアプリケーションで利用するためのフレームワークです。
モデルを表示、操作するための各種機能を提供します。
モデルをロードするには Live2D Cubism Core ライブラリと組み合わせて使用します。
ビルドを行うことで、ブラウザで利用可能な JavaScript ライブラリとして利用することができます。
## ライセンス
本 SDK を使用する前に、[ライセンス](LICENSE.md)をご確認ください。
## Cubism 5.3新機能や過去バージョンとの互換性について
本 SDK はCubism 5.3に対応した製品です。
Cubism 5.3 Editorに搭載された新機能のSDK対応については [こちら](https://docs.live2d.com/cubism-sdk-manual/cubism-5-3-new-functions/)をご確認ください。
過去バージョンのCubism SDKとの互換性については [こちら](https://docs.live2d.com/cubism-sdk-manual/compatibility-with-cubism-5-3/)をご確認ください。
## 開発環境
### Node.js
* 25.8.2
* 24.14.1
### TypeScript
5.9.3
## 開発環境構築
1. [Node.js] と [Visual Studio Code] をインストールします
1. Visual Studio Code で本プロジェクトを開き、推奨拡張機能をインストールします
* 拡張機能タブから `@recommended` と入力することで確認できます
1. コマンドパレット(*View > Command Palette...*)で `>Tasks: Run Task` を入力してタスク一覧を表示します
1. `npm: install` を選択して依存パッケージのダウンロードを行います
コマンドパレットのタスク一覧から各種コマンドを実行することができます。
NOTE: デバッグ用の設定は、`.vscode/tasks.json` に記述しています。
## タスク一覧
### `npm: build`
ソースファイルのビルドを行い、`dist` ディレクトリに出力します。
`tsconfig.json` を編集することで設定内容を変更できます。
### `npm: test`
TypeScript の型チェックテストを行います。
`tsconfig.json` を編集することで設定内容を変更できます。
### `npm: lint`
`src` ディレクトリ内の TypeScript ファイルの静的解析を行います。
`eslint.config.mjs` を編集することで設定内容を変更できます。
### `npm: lint:fix`
`src` ディレクトリ内の TypeScript ファイルの静的解析及び自動修正を行います。
`eslint.config.mjs` を編集することで設定内容を変更できます。
### `npm: clean`
ビルド成果物ディレクトリ(`dist`)を削除します。
## コンポーネント
### effect
自動まばたきやリップシンクなど、モデルに対してモーション情報をエフェクト的に付加する機能を提供します。
### id
モデルに設定されたパラメータ名・パーツ名・Drawable名を独自の型で管理する機能を提供します。
### math
行列計算やベクトル計算など、モデルの操作や描画に必要な算術演算の機能を提供します。
### model
モデルを取り扱うための各種機能(生成、更新、破棄)を提供します。
### motion
モデルにモーションデータを適用するための各種機能(モーション再生、パラメータブレンド)を提供します。
### physics
モデルに物理演算による変形操作を適用するための機能を提供します。
### rendering
モデルを描画するためのグラフィックス命令を実装したレンダラを提供します。
### type
フレームワーク内で使用する型定義を提供します。
### utils
JSONパーサーやログ出力などのユーティリティ機能を提供します。
## Live2D Cubism Core for Web
当リポジトリには Cubism Core for Web は同梱されていません。
[Cubism SDK for Web] からダウンロードしてください。
[Cubism SDK for Web]: https://www.live2d.com/download/cubism-sdk/download-web/
## サンプル
標準的なアプリケーションの実装例は [CubismWebSamples] を参照ください。
[CubismWebSamples]: https://github.com/Live2D/CubismWebSamples
## マニュアル
[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/)

View File

@@ -0,0 +1,170 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Web 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 Live2D Cubism Core library to load the model.
You can use it as a JavaScript library that can be used in the browser by building.
## License
Please check the [license](LICENSE.md) before using this SDK.
## Compatibility with Cubism 5.3 new features and previous Cubism SDK versions
This SDK is compatible with Cubism 5.3.
For SDK compatibility with new features in Cubism 5.3 Editor, please refer to [here](https://docs.live2d.com/en/cubism-sdk-manual/cubism-5-3-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-3/).
## Development environment
### Node.js
* 25.8.2
* 24.14.1
### TypeScript
5.9.3
## Development environment construction
1. Install [Node.js] and [Visual Studio Code]
1. Open this project in Visual Studio Code and install the recommended extensions
* You can check them by typing `@recommended` from the Extensions tab
1. Enter `>Tasks: Run Task` in the command palette (*View > Command Palette...*) to display the task list
1. Select `npm: install` to download the dependent packages
You can execute various commands from the task list on the command palette.
NOTE: Settings for debugging are described in `.vscode/tasks.json`.
## Task list
### `npm: build`
Builds the source file and outputs to the `dist` directory.
You can change the settings by editing `tsconfig.json`.
### `npm: test`
Performs a TypeScript type check test.
You can change the settings by editing `tsconfig.json`.
### `npm: lint`
Performs static analysis of TypeScript files in the `src` directory.
You can change the settings by editing `eslint.config.mjs`.
### `npm: lint:fix`
Performs static analysis and automatic modification of TypeScript files in the `src` directory.
You can change the settings by editing `eslint.config.mjs`.
### `npm: clean`
Deletes the build deliverable directory (`dist`).
## Components
### effect
Provides functions such as automatic blinking and lip sync to add motion information as an effect to the model.
### 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.
### type
Provides type definitions for use within the framework.
### utils
Provides utility functions such as JSON parser and log output.
## Live2D Cubism Core for Web
Cubism Core for Web is not included in this repository.
Please download it from the [Cubism SDK for Web].
[Cubism SDK for Web]: https://www.live2d.com/download/cubism-sdk/download-web/
## Samples
Please refer to [CubismWebSamples] for implementation examples of standard applications.
[CubismWebSamples]: https://github.com/Live2D/CubismWebSamples
## 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,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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
vec4 OverlapRgba(vec3 color, vec3 colorSource, vec3 colorDestination, vec3 parameter)
{
vec3 rgb = color * parameter.x + colorSource * parameter.y + colorDestination * parameter.z;
float alpha = parameter.x + parameter.y + parameter.z;
return vec4(rgb, alpha);
}
#if defined(ALPHA_BLEND_OVER)
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
{
vec3 parameter = vec3(colorSource.a * colorDestination.a, colorSource.a * (1.0 - colorDestination.a), colorDestination.a * (1.0 - colorSource.a));
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
}
#elif defined(ALPHA_BLEND_ATOP)
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
{
vec3 parameter = vec3(colorSource.a * colorDestination.a, 0, colorDestination.a * (1.0 - colorSource.a));
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
}
#elif defined(ALPHA_BLEND_OUT)
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
{
vec3 parameter = vec3(0.0, 0.0, colorDestination.a * (1.0 - colorSource.a));
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
}
#elif defined(ALPHA_BLEND_CONJOINTOVER)
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
{
vec3 parameter = vec3(min(colorSource.a, colorDestination.a), max(colorSource.a - colorDestination.a, 0.0), max(colorDestination.a - colorSource.a, 0.0));
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
}
#elif defined(ALPHA_BLEND_DISJOINTOVER)
vec4 AlphaBlend(vec3 color, vec4 colorSource, vec4 colorDestination)
{
vec3 parameter = vec3(max(colorSource.a + colorDestination.a - 1.0, 0.0), min(colorSource.a, 1.0 - colorDestination.a), min(colorDestination.a, 1.0 - colorSource.a));
return OverlapRgba(color, colorSource.rgb, colorDestination.rgb, parameter);
}
#else
#error not supported alpha blend function
#endif

View File

@@ -0,0 +1,298 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
#if defined(COLOR_BLEND_NORMAL)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return colorSource;
}
#elif defined(COLOR_BLEND_ADDCOMPATIBLE)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(0.0);
}
#elif defined(COLOR_BLEND_MULTCOMPATIBLE)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(0.0);
}
#elif defined(COLOR_BLEND_ADD)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return min(colorSource + colorDestination, 1.0);
}
#elif defined(COLOR_BLEND_ADDGLOW)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return colorSource + colorDestination;
}
#elif defined(COLOR_BLEND_DARKEN)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return min(colorSource, colorDestination);
}
#elif defined(COLOR_BLEND_MULTIPLY)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return colorSource * colorDestination;
}
#elif defined(COLOR_BLEND_COLORBURN)
float ColorBurn(float colorSource, float colorDestination)
{
if (abs(colorDestination - 1.0) < 0.000001)
{
return 1.0;
}
else if (abs(colorSource) < 0.000001)
{
return 0.0;
}
return 1.0 - min(1.0, (1.0 - colorDestination) / colorSource);
}
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(
ColorBurn(colorSource.r, colorDestination.r),
ColorBurn(colorSource.g, colorDestination.g),
ColorBurn(colorSource.b, colorDestination.b)
);
}
#elif defined(COLOR_BLEND_LINEARBURN)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return max(vec3(0.0), colorSource + colorDestination - 1.0);
}
#elif defined(COLOR_BLEND_LIGHTEN)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return max(colorSource, colorDestination);
}
#elif defined(COLOR_BLEND_SCREEN)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return colorSource + colorDestination - colorSource * colorDestination;
}
#elif defined(COLOR_BLEND_COLORDODGE)
float ColorDodge(float colorSource, float colorDestination)
{
if (colorDestination <= 0.0)
{
return 0.0;
}
else if (colorSource == 1.0)
{
return 1.0;
}
return min(1.0, colorDestination / (1.0 - colorSource));
}
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(
ColorDodge(colorSource.r, colorDestination.r),
ColorDodge(colorSource.g, colorDestination.g),
ColorDodge(colorSource.b, colorDestination.b)
);
}
#elif defined(COLOR_BLEND_OVERLAY)
float Overlay(float colorSource, float colorDestination)
{
float mul = 2.0 * colorSource * colorDestination;
float scr = 1.0 - 2.0 * (1.0 - colorSource) * (1.0 - colorDestination) ;
return colorDestination < 0.5 ? mul : scr ;
}
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(
Overlay(colorSource.r, colorDestination.r),
Overlay(colorSource.g, colorDestination.g),
Overlay(colorSource.b, colorDestination.b)
);
}
#elif defined(COLOR_BLEND_SOFTLIGHT)
float SoftLight(float colorSource, float colorDestination)
{
float val1 = colorDestination - (1.0 - 2.0 * colorSource) * colorDestination * (1.0 - colorDestination);
float val2 = colorDestination + (2.0 * colorSource - 1.0) * colorDestination * ((16.0 * colorDestination - 12.0) * colorDestination + 3.0);
float val3 = colorDestination + (2.0 * colorSource - 1.0) * (sqrt(colorDestination) - colorDestination);
if (colorSource <= 0.5)
{
return val1;
}
else if (colorDestination <= 0.25)
{
return val2;
}
return val3;
}
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(
SoftLight(colorSource.r, colorDestination.r),
SoftLight(colorSource.g, colorDestination.g),
SoftLight(colorSource.b, colorDestination.b)
);
}
#elif defined(COLOR_BLEND_HARDLIGHT)
float HardLight(float colorSource, float colorDestination)
{
float mul = 2.0 * colorSource * colorDestination;
float scr = 1.0 - 2.0 * (1.0 - colorSource) * (1.0 - colorDestination);
if (colorSource < 0.5)
{
return mul;
}
return scr;
}
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(
HardLight(colorSource.r, colorDestination.r),
HardLight(colorSource.g, colorDestination.g),
HardLight(colorSource.b, colorDestination.b)
);
}
#elif defined(COLOR_BLEND_LINEARLIGHT)
float LinearLight(float colorSource, float colorDestination)
{
float burn = max(0.0, 2.0 * colorSource + colorDestination - 1.0);
float dodge = min(1.0, 2.0 * (colorSource - 0.5) + colorDestination);
if (colorSource < 0.5)
{
return burn;
}
return dodge;
}
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return vec3(
LinearLight(colorSource.r, colorDestination.r),
LinearLight(colorSource.g, colorDestination.g),
LinearLight(colorSource.b, colorDestination.b)
);
}
#elif defined(COLOR_BLEND_HUE) || defined(COLOR_BLEND_COLOR)
const float rCoeff = 0.30;
const float gCoeff = 0.59;
const float bCoeff = 0.11;
float GetMax(vec3 rgbC)
{
return max(rgbC.r, max(rgbC.g, rgbC.b));
}
float GetMin(vec3 rgbC)
{
return min(rgbC.r, min(rgbC.g, rgbC.b));
}
float GetRange(vec3 rgbC)
{
return max(rgbC.r, max(rgbC.g, rgbC.b)) - min(rgbC.r, min(rgbC.g, rgbC.b));
}
float Saturation(vec3 rgbC)
{
return GetRange(rgbC);
}
float Luma(vec3 rgbC)
{
return rCoeff * rgbC.r + gCoeff * rgbC.g + bCoeff * rgbC.b;
}
vec3 ClipColor(vec3 rgbC)
{
float luma = Luma(rgbC);
float maxv = GetMax(rgbC);
float minv = GetMin(rgbC);
vec3 outputColor = rgbC;
outputColor = minv < 0.0 ? luma + (outputColor - luma) * luma / (luma - minv) : outputColor;
outputColor = maxv > 1.0 ? luma + (outputColor - luma) * (1.0 - luma) / (maxv - luma) : outputColor;
return outputColor;
}
vec3 SetLuma(vec3 rgbC, float luma)
{
return ClipColor(rgbC + (luma - Luma(rgbC)));
}
vec3 SetSaturation(vec3 rgbC, float saturation)
{
float maxv = GetMax(rgbC);
float minv = GetMin(rgbC);
float medv = rgbC.r + rgbC.g + rgbC.b - maxv - minv;
float outputMax, outputMed, outputMin;
outputMax = minv < maxv ? saturation : 0.0;
outputMed = minv < maxv ? (medv - minv) * saturation / (maxv - minv) : 0.0;
outputMin = 0.0;
if(rgbC.r == maxv)
{
return rgbC.b < rgbC.g ? vec3(outputMax, outputMed, outputMin) : vec3(outputMax, outputMin, outputMed);
}
else if(rgbC.g == maxv)
{
return rgbC.r < rgbC.b ? vec3(outputMin, outputMax, outputMed) : vec3(outputMed, outputMax, outputMin);
}
else // if(rgbC.b == maxv)
{
return rgbC.g < rgbC.r ? vec3(outputMed, outputMin, outputMax) : vec3(outputMin, outputMed, outputMax);
}
}
#if defined(COLOR_BLEND_HUE)
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return SetLuma(SetSaturation(colorSource, Saturation(colorDestination)), Luma(colorDestination));
}
#else
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination)
{
return SetLuma(colorSource, Luma(colorDestination)) ;
}
#endif
#else
#error not supported color blend function.
#endif

View File

@@ -0,0 +1,18 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
precision mediump float;
varying vec2 v_texCoord;
uniform vec4 u_baseColor;
uniform sampler2D s_texture0;
void main()
{
gl_FragColor = texture2D(s_texture0, v_texCoord) * u_baseColor;
}

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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
precision mediump float;
varying vec2 v_texCoord; //v2f.texcoord
varying vec4 v_clipPos;
uniform sampler2D s_texture0; //_MainTex
uniform sampler2D s_texture1; // _ClippingMaskTex
uniform vec4 u_channelFlag;
uniform vec4 u_baseColor; //v2f.color
uniform vec4 u_multiplyColor;
uniform vec4 u_screenColor;
void main()
{
vec4 texColor = texture2D(s_texture0, v_texCoord);
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
vec4 col_formask = texColor * u_baseColor;
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
col_formask = col_formask * (1.0 - maskVal);
gl_FragColor = col_formask;
}

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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
precision mediump float;
varying vec2 v_texCoord; //v2f.texcoord
varying vec4 v_clipPos;
uniform vec4 u_baseColor; //v2f.color
uniform vec4 u_channelFlag;
uniform sampler2D s_texture0; //_MainTex
uniform sampler2D s_texture1; // _ClippingMaskTex
uniform vec4 u_multiplyColor;
uniform vec4 u_screenColor;
void main()
{
vec4 texColor = texture2D(s_texture0, v_texCoord);
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
vec4 col_formask = texColor * u_baseColor;
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
col_formask = col_formask * maskVal;
gl_FragColor = col_formask;
}

View File

@@ -0,0 +1,23 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
precision mediump float;
varying vec2 v_texCoord; //v2f.texcoord
uniform vec4 u_baseColor; //v2f.color
uniform sampler2D s_texture0; //_MainTex
uniform vec4 u_multiplyColor;
uniform vec4 u_screenColor;
void main()
{
vec4 texColor = texture2D(s_texture0, v_texCoord);
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
texColor.rgb = (texColor.rgb + u_screenColor.rgb * texColor.a) - (texColor.rgb * u_screenColor.rgb);
vec4 color = texColor * u_baseColor;
gl_FragColor = vec4(color.rgb, color.a);
}

View File

@@ -0,0 +1,67 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
varying vec2 v_texCoord; //v2f.texcoord
varying vec2 v_blendCoord;
varying vec4 v_clipPos;
uniform sampler2D s_texture0; //_MainTex
uniform sampler2D s_blendTexture;
uniform vec4 u_baseColor; //v2f.color
uniform vec4 u_multiplyColor;
uniform vec4 u_screenColor;
uniform sampler2D s_texture1; // _ClippingMaskTex
uniform float u_invertClippingMask;
uniform vec4 u_channelFlag;
vec3 ColorBlend(vec3 colorSource, vec3 colorDestination);
vec4 AlphaBlend(vec3 C, vec3 Cs, float As, vec3 Cd, float Ad);
void main()
{
vec4 renderTextureColor = texture2D(s_blendTexture, v_blendCoord);
vec3 colorDestination = renderTextureColor.rgb;
float alphaDestination = renderTextureColor.a;
if (alphaDestination < 0.00001)
{
colorDestination = vec3(0.0, 0.0, 0.0);
}
else {
colorDestination /= alphaDestination;
}
vec4 texColor = texture2D(s_texture0, v_texCoord);
texColor.rgb *= u_multiplyColor.rgb;
texColor.rgb = (texColor.rgb + u_screenColor.rgb) - (texColor.rgb * u_screenColor.rgb);
texColor *= u_baseColor;
vec3 colorSource = texColor.rgb;
float alphaSource = texColor.a;
if (alphaSource < 0.00001)
{
colorSource = vec3(0.0, 0.0, 0.0);
}
else {
colorSource /= alphaSource;
}
#ifdef CLIPPING_MASK
float maskVal = 1.0;
vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;
maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;
maskVal = abs(u_invertClippingMask - maskVal);
alphaSource *= maskVal;
#endif
vec4 source = vec4(colorSource.r, colorSource.g, colorSource.b, alphaSource);
vec4 destination = vec4(colorDestination.r, colorDestination.g, colorDestination.b, alphaDestination);
gl_FragColor = AlphaBlend(ColorBlend(colorSource, colorDestination), source, destination);
}

View File

@@ -0,0 +1,25 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
precision mediump float;
varying vec2 v_texCoord; //v2f.texcoord
varying vec4 v_myPos;
uniform vec4 u_baseColor; //v2f.color
uniform vec4 u_channelFlag;
uniform sampler2D s_texture0; //_MainTex
void main()
{
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);
gl_FragColor = u_channelFlag * texture2D(s_texture0, v_texCoord).a * isInside;
}

View File

@@ -0,0 +1,19 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
uniform mat4 u_matrix;
void main()
{
gl_Position = u_matrix * a_position;
v_texCoord = a_texCoord;
v_texCoord.y = 1.0 - v_texCoord.y;
}

View File

@@ -0,0 +1,31 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
varying vec2 v_blendCoord;
varying vec4 v_clipPos;
uniform mat4 u_matrix;
uniform mat4 u_clipMatrix;
void main()
{
gl_Position = u_matrix * a_position;
#ifdef CLIPPING_MASK
v_clipPos = u_clipMatrix * a_position;
#else
v_clipPos = vec4(0.0);
#endif
v_texCoord = a_texCoord;
v_texCoord.y = 1.0 - v_texCoord.y;
vec2 ndcPos = gl_Position.xy / gl_Position.w;
v_blendCoord = ndcPos * 0.5 + 0.5;
}

View File

@@ -0,0 +1,17 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main()
{
v_texCoord = a_texCoord;
gl_Position = a_position;
}

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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
varying vec4 v_clipPos;
uniform mat4 u_matrix;
uniform mat4 u_clipMatrix;
void main()
{
gl_Position = u_matrix * a_position;
v_clipPos = u_clipMatrix * a_position;
v_texCoord = a_texCoord;
v_texCoord.y = 1.0 - v_texCoord.y;
}

View File

@@ -0,0 +1,21 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
varying vec4 v_myPos;
uniform mat4 u_clipMatrix;
void main()
{
gl_Position = u_clipMatrix * a_position;
v_myPos = u_clipMatrix * a_position;
v_texCoord = a_texCoord;
v_texCoord.y = 1.0 - v_texCoord.y;
}

View File

@@ -0,0 +1,183 @@
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
import eslintConfigPrettier from 'eslint-config-prettier';
import eslintPluginPrettier from 'eslint-plugin-prettier';
export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.eslintRecommended,
...tseslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
eslintConfigPrettier,
{
languageOptions:
{
parserOptions:
{
sourceType: 'module',
ecmaVersion: 2020,
project: './tsconfig.json',
},
globals:
{
...globals.browser,
},
},
plugins:
{
'prettier': eslintPluginPrettier,
},
rules:
{
'prettier/prettier': [
'error',
{
singleQuote: true,
trailingComma: 'none',
arrowParens: 'avoid',
},
],
camelcase: 'off',
'@typescript-eslint/naming-convention': [
'warn',
{
selector: 'default',
format: ['camelCase'],
},
{
selector: 'import',
format: ['PascalCase'],
},
{
selector: 'variable',
format: [],
custom:
{
// 指定の文字列で始まるものと特定の文字を含むものは許容
regex: '^[A-Z]|^csm|^iterator|Shader',
match: true,
},
modifiers: ['exported', 'const'],
},
{
selector: 'variable',
format: ['camelCase'],
},
{
selector: 'variable',
format: [],
custom: {
// 指定の文字列で始まるものは許容
regex: '^[A-Z]|^s_',
match: true,
},
modifiers: ['global'],
},
{
selector: 'enum',
format: ['PascalCase'],
},
{
selector: 'enumMember',
format: [],
custom: {
// 大文字から始まること
regex: '^[A-Z]',
match: true,
},
},
{
selector: 'classProperty',
format: ['PascalCase'],
modifiers: ['static', 'readonly'],
},
{
selector: 'classProperty',
format: ['camelCase'],
leadingUnderscore: 'allow',
},
{
selector: 'class',
format: [],
custom: {
// 指定の文字列で始まるか、指定の文字列で終わること
regex: '^[A-Z]|^csm|^iterator|_WebGL$',
match: true,
},
},
{
selector: 'interface',
format: ['camelCase', 'PascalCase'],
},
{
selector: 'parameter',
format: ['camelCase'],
},
{
selector: 'classMethod',
format: ['camelCase'],
},
{
selector: 'objectLiteralProperty',
format: ['camelCase', 'PascalCase'],
},
{
selector: 'typeAlias',
format: [],
custom: {
// 指定の文字列で始まるものは許容
regex: '^[A-Z]|^[a-z]|^CSM_|^csm|^iterator',
match: true,
},
modifiers: ['exported'],
},
{
selector: 'typeAlias',
format: ['camelCase'],
},
{
selector: 'typeParameter',
format: [],
custom: {
// 「大文字+アンダースコア以外の文字」、あるいは「大文字1文字」
// あるいは、「`T`+アンダースコア」で始まる場合
regex: '^[A-Z][^_]|^[A-Z]|^T_$',
match: true,
},
leadingUnderscore: 'allow',
},
]/* @typescript-eslint/naming-convention */,
'no-empty-function': 'off',
'@typescript-eslint/no-empty-function': [
'error',
{
allow: [
'constructors',
],
},
],
'no-fallthrough': 'warn',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/restrict-plus-operands': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'no-inner-declarations': 'off',
'no-global-assign': 'off',
'prefer-const': 'warn',
},
},
{
// ignores property はなぜか単独で指定していないと効果がない。
ignores: [
'**/*.*',
'!src/**/*.ts',
],
},
);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
{
"private": true,
"scripts": {
"build": "tsc",
"test": "tsc --noEmit",
"lint": "eslint",
"lint:fix": "eslint --fix",
"clean": "rimraf dist"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
"prettier": "^3.8.1",
"rimraf": "^6.1.3",
"typescript": "^5.9.3",
"typescript-eslint": "^8.57.2"
}
}

View File

@@ -0,0 +1,118 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
/**
* @brief パラメータIDのデフォルト値を保持する定数<br>
* デフォルト値の仕様は以下のマニュアルに基づく<br>
* https://docs.live2d.com/cubism-editor-manual/standard-parametor-list/
*/
export const CubismDefaultParameterId = Object.freeze<Record<string, string>>({
// パーツID
HitAreaPrefix: 'HitArea',
HitAreaHead: 'Head',
HitAreaBody: 'Body',
PartsIdCore: 'Parts01Core',
PartsArmPrefix: 'Parts01Arm_',
PartsArmLPrefix: 'Parts01ArmL_',
PartsArmRPrefix: 'Parts01ArmR_',
// パラメータID
ParamAngleX: 'ParamAngleX',
ParamAngleY: 'ParamAngleY',
ParamAngleZ: 'ParamAngleZ',
ParamEyeLOpen: 'ParamEyeLOpen',
ParamEyeLSmile: 'ParamEyeLSmile',
ParamEyeROpen: 'ParamEyeROpen',
ParamEyeRSmile: 'ParamEyeRSmile',
ParamEyeBallX: 'ParamEyeBallX',
ParamEyeBallY: 'ParamEyeBallY',
ParamEyeBallForm: 'ParamEyeBallForm',
ParamBrowLY: 'ParamBrowLY',
ParamBrowRY: 'ParamBrowRY',
ParamBrowLX: 'ParamBrowLX',
ParamBrowRX: 'ParamBrowRX',
ParamBrowLAngle: 'ParamBrowLAngle',
ParamBrowRAngle: 'ParamBrowRAngle',
ParamBrowLForm: 'ParamBrowLForm',
ParamBrowRForm: 'ParamBrowRForm',
ParamMouthForm: 'ParamMouthForm',
ParamMouthOpenY: 'ParamMouthOpenY',
ParamCheek: 'ParamCheek',
ParamBodyAngleX: 'ParamBodyAngleX',
ParamBodyAngleY: 'ParamBodyAngleY',
ParamBodyAngleZ: 'ParamBodyAngleZ',
ParamBreath: 'ParamBreath',
ParamArmLA: 'ParamArmLA',
ParamArmRA: 'ParamArmRA',
ParamArmLB: 'ParamArmLB',
ParamArmRB: 'ParamArmRB',
ParamHandL: 'ParamHandL',
ParamHandR: 'ParamHandR',
ParamHairFront: 'ParamHairFront',
ParamHairSide: 'ParamHairSide',
ParamHairBack: 'ParamHairBack',
ParamHairFluffy: 'ParamHairFluffy',
ParamShoulderY: 'ParamShoulderY',
ParamBustX: 'ParamBustX',
ParamBustY: 'ParamBustY',
ParamBaseX: 'ParamBaseX',
ParamBaseY: 'ParamBaseY',
ParamNONE: 'NONE:'
});
// Namespace definition for compatibility.
import * as $ from './cubismdefaultparameterid';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const HitAreaBody = $.CubismDefaultParameterId.HitAreaBody;
export const HitAreaHead = $.CubismDefaultParameterId.HitAreaHead;
export const HitAreaPrefix = $.CubismDefaultParameterId.HitAreaPrefix;
export const ParamAngleX = $.CubismDefaultParameterId.ParamAngleX;
export const ParamAngleY = $.CubismDefaultParameterId.ParamAngleY;
export const ParamAngleZ = $.CubismDefaultParameterId.ParamAngleZ;
export const ParamArmLA = $.CubismDefaultParameterId.ParamArmLA;
export const ParamArmLB = $.CubismDefaultParameterId.ParamArmLB;
export const ParamArmRA = $.CubismDefaultParameterId.ParamArmRA;
export const ParamArmRB = $.CubismDefaultParameterId.ParamArmRB;
export const ParamBaseX = $.CubismDefaultParameterId.ParamBaseX;
export const ParamBaseY = $.CubismDefaultParameterId.ParamBaseY;
export const ParamBodyAngleX = $.CubismDefaultParameterId.ParamBodyAngleX;
export const ParamBodyAngleY = $.CubismDefaultParameterId.ParamBodyAngleY;
export const ParamBodyAngleZ = $.CubismDefaultParameterId.ParamBodyAngleZ;
export const ParamBreath = $.CubismDefaultParameterId.ParamBreath;
export const ParamBrowLAngle = $.CubismDefaultParameterId.ParamBrowLAngle;
export const ParamBrowLForm = $.CubismDefaultParameterId.ParamBrowLForm;
export const ParamBrowLX = $.CubismDefaultParameterId.ParamBrowLX;
export const ParamBrowLY = $.CubismDefaultParameterId.ParamBrowLY;
export const ParamBrowRAngle = $.CubismDefaultParameterId.ParamBrowRAngle;
export const ParamBrowRForm = $.CubismDefaultParameterId.ParamBrowRForm;
export const ParamBrowRX = $.CubismDefaultParameterId.ParamBrowRX;
export const ParamBrowRY = $.CubismDefaultParameterId.ParamBrowRY;
export const ParamBustX = $.CubismDefaultParameterId.ParamBustX;
export const ParamBustY = $.CubismDefaultParameterId.ParamBustY;
export const ParamCheek = $.CubismDefaultParameterId.ParamCheek;
export const ParamEyeBallForm = $.CubismDefaultParameterId.ParamEyeBallForm;
export const ParamEyeBallX = $.CubismDefaultParameterId.ParamEyeBallX;
export const ParamEyeBallY = $.CubismDefaultParameterId.ParamEyeBallY;
export const ParamEyeLOpen = $.CubismDefaultParameterId.ParamEyeLOpen;
export const ParamEyeLSmile = $.CubismDefaultParameterId.ParamEyeLSmile;
export const ParamEyeROpen = $.CubismDefaultParameterId.ParamEyeROpen;
export const ParamEyeRSmile = $.CubismDefaultParameterId.ParamEyeRSmile;
export const ParamHairBack = $.CubismDefaultParameterId.ParamHairBack;
export const ParamHairFluffy = $.CubismDefaultParameterId.ParamHairFluffy;
export const ParamHairFront = $.CubismDefaultParameterId.ParamHairFront;
export const ParamHairSide = $.CubismDefaultParameterId.ParamHairSide;
export const ParamHandL = $.CubismDefaultParameterId.ParamHandL;
export const ParamHandR = $.CubismDefaultParameterId.ParamHandR;
export const ParamMouthForm = $.CubismDefaultParameterId.ParamMouthForm;
export const ParamMouthOpenY = $.CubismDefaultParameterId.ParamMouthOpenY;
export const ParamNONE = $.CubismDefaultParameterId.ParamNONE;
export const ParamShoulderY = $.CubismDefaultParameterId.ParamShoulderY;
export const PartsArmLPrefix = $.CubismDefaultParameterId.PartsArmLPrefix;
export const PartsArmPrefix = $.CubismDefaultParameterId.PartsArmPrefix;
export const PartsArmRPrefix = $.CubismDefaultParameterId.PartsArmRPrefix;
export const PartsIdCore = $.CubismDefaultParameterId.PartsIdCore;
}

View File

@@ -0,0 +1,32 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
//========================================================
// ログ出力関数の設定
//========================================================
//---------- ログ出力レベル 選択項目 定義 ----------
// 詳細ログ出力設定
export const CSM_LOG_LEVEL_VERBOSE = 0;
// デバッグログ出力設定
export const CSM_LOG_LEVEL_DEBUG = 1;
// Infoログ出力設定
export const CSM_LOG_LEVEL_INFO = 2;
// 警告ログ出力設定
export const CSM_LOG_LEVEL_WARNING = 3;
// エラーログ出力設定
export const CSM_LOG_LEVEL_ERROR = 4;
// ログ出力オフ設定
export const CSM_LOG_LEVEL_OFF = 5;
/**
* ログ出力レベル設定。
*
* 強制的にログ出力レベルを変える時に定義を有効にする。
* CSM_LOG_LEVEL_VERBOSE CSM_LOG_LEVEL_OFF を選択する。
*/
export const CSM_LOG_LEVEL: number = CSM_LOG_LEVEL_VERBOSE;

View File

@@ -0,0 +1,790 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismModelSetting } from './icubismmodelsetting';
import { CubismIdHandle } from './id/cubismid';
import { CubismFramework } from './live2dcubismframework';
import { CubismJson, Value } from './utils/cubismjson';
export enum FrequestNode {
FrequestNode_Groups, // getRoot().getValueByString(Groups)
FrequestNode_Moc, // getRoot().getValueByString(FileReferences).getValueByString(Moc)
FrequestNode_Motions, // getRoot().getValueByString(FileReferences).getValueByString(Motions)
FrequestNode_Expressions, // getRoot().getValueByString(FileReferences).getValueByString(Expressions)
FrequestNode_Textures, // getRoot().getValueByString(FileReferences).getValueByString(Textures)
FrequestNode_Physics, // getRoot().getValueByString(FileReferences).getValueByString(Physics)
FrequestNode_Pose, // getRoot().getValueByString(FileReferences).getValueByString(Pose)
FrequestNode_HitAreas // getRoot().getValueByString(HitAreas)
}
/**
* Model3Jsonパーサー
*
* model3.jsonファイルをパースして値を取得する
*/
export class CubismModelSettingJson extends ICubismModelSetting {
/**
* 引数付きコンストラクタ
*
* @param buffer Model3Jsonをバイト配列として読み込んだデータバッファ
* @param size Model3Jsonのデータサイズ
*/
public constructor(buffer: ArrayBuffer, size: number) {
super();
this._json = CubismJson.create(buffer, size);
if (this.getJson()) {
this._jsonValue = [
// 順番はenum FrequestNodeと一致させる
this.getJson().getRoot().getValueByString(this.groups),
this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.moc),
this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.motions),
this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.expressions),
this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.textures),
this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.physics),
this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.pose),
this.getJson().getRoot().getValueByString(this.hitAreas)
];
}
}
/**
* デストラクタ相当の処理
*/
public release(): void {
CubismJson.delete(this._json);
this._jsonValue = null;
}
/**
* CubismJsonオブジェクトを取得する
*
* @return CubismJson
*/
public getJson(): CubismJson {
return this._json;
}
/**
* Mocファイルの名前を取得する
* @return Mocファイルの名前
*/
public getModelFileName(): string {
if (!this.isExistModelFile()) {
return '';
}
return this._jsonValue[FrequestNode.FrequestNode_Moc].getRawString();
}
/**
* モデルが使用するテクスチャの数を取得する
* テクスチャの数
*/
public getTextureCount(): number {
if (!this.isExistTextureFiles()) {
return 0;
}
return this._jsonValue[FrequestNode.FrequestNode_Textures].getSize();
}
/**
* テクスチャが配置されたディレクトリの名前を取得する
* @return テクスチャが配置されたディレクトリの名前
*/
public getTextureDirectory(): string {
const texturePath = this._jsonValue[FrequestNode.FrequestNode_Textures]
.getValueByIndex(0)
.getRawString();
const pathArray = texturePath.split('/');
// 最後の要素はテクスチャ名なので不要
const arrayLength = pathArray.length - 1;
let textureDirectoryStr = '';
// 分割したパスを結合
for (let i = 0; i < arrayLength; i++) {
textureDirectoryStr += pathArray[i];
if (i < arrayLength - 1) {
textureDirectoryStr += '/';
}
}
return textureDirectoryStr;
}
/**
* モデルが使用するテクスチャの名前を取得する
* @param index 配列のインデックス値
* @return テクスチャの名前
*/
public getTextureFileName(index: number): string {
return this._jsonValue[FrequestNode.FrequestNode_Textures]
.getValueByIndex(index)
.getRawString();
}
/**
* モデルに設定された当たり判定の数を取得する
* @return モデルに設定された当たり判定の数
*/
public getHitAreasCount(): number {
if (!this.isExistHitAreas()) {
return 0;
}
return this._jsonValue[FrequestNode.FrequestNode_HitAreas].getSize();
}
/**
* 当たり判定に設定されたIDを取得する
*
* @param index 配列のindex
* @return 当たり判定に設定されたID
*/
public getHitAreaId(index: number): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._jsonValue[FrequestNode.FrequestNode_HitAreas]
.getValueByIndex(index)
.getValueByString(this.id)
.getRawString()
);
}
/**
* 当たり判定に設定された名前を取得する
* @param index 配列のインデックス値
* @return 当たり判定に設定された名前
*/
public getHitAreaName(index: number): string {
return this._jsonValue[FrequestNode.FrequestNode_HitAreas]
.getValueByIndex(index)
.getValueByString(this.name)
.getRawString();
}
/**
* 物理演算設定ファイルの名前を取得する
* @return 物理演算設定ファイルの名前
*/
public getPhysicsFileName(): string {
if (!this.isExistPhysicsFile()) {
return '';
}
return this._jsonValue[FrequestNode.FrequestNode_Physics].getRawString();
}
/**
* パーツ切り替え設定ファイルの名前を取得する
* @return パーツ切り替え設定ファイルの名前
*/
public getPoseFileName(): string {
if (!this.isExistPoseFile()) {
return '';
}
return this._jsonValue[FrequestNode.FrequestNode_Pose].getRawString();
}
/**
* 表情設定ファイルの数を取得する
* @return 表情設定ファイルの数
*/
public getExpressionCount(): number {
if (!this.isExistExpressionFile()) {
return 0;
}
return this._jsonValue[FrequestNode.FrequestNode_Expressions].getSize();
}
/**
* 表情設定ファイルを識別する名前(別名)を取得する
* @param index 配列のインデックス値
* @return 表情の名前
*/
public getExpressionName(index: number): string {
return this._jsonValue[FrequestNode.FrequestNode_Expressions]
.getValueByIndex(index)
.getValueByString(this.name)
.getRawString();
}
/**
* 表情設定ファイルの名前を取得する
* @param index 配列のインデックス値
* @return 表情設定ファイルの名前
*/
public getExpressionFileName(index: number): string {
return this._jsonValue[FrequestNode.FrequestNode_Expressions]
.getValueByIndex(index)
.getValueByString(this.filePath)
.getRawString();
}
/**
* モーショングループの数を取得する
* @return モーショングループの数
*/
public getMotionGroupCount(): number {
if (!this.isExistMotionGroups()) {
return 0;
}
return this._jsonValue[FrequestNode.FrequestNode_Motions].getKeys().length;
}
/**
* モーショングループの名前を取得する
* @param index 配列のインデックス値
* @return モーショングループの名前
*/
public getMotionGroupName(index: number): string {
if (!this.isExistMotionGroups()) {
return null;
}
return this._jsonValue[FrequestNode.FrequestNode_Motions].getKeys()[index];
}
/**
* モーショングループに含まれるモーションの数を取得する
* @param groupName モーショングループの名前
* @return モーショングループの数
*/
public getMotionCount(groupName: string): number {
if (!this.isExistMotionGroupName(groupName)) {
return 0;
}
return this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getSize();
}
/**
* グループ名とインデックス値からモーションファイル名を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return モーションファイルの名前
*/
public getMotionFileName(groupName: string, index: number): string {
if (!this.isExistMotionGroupName(groupName)) {
return '';
}
return this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getValueByIndex(index)
.getValueByString(this.filePath)
.getRawString();
}
/**
* モーションに対応するサウンドファイルの名前を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return サウンドファイルの名前
*/
public getMotionSoundFileName(groupName: string, index: number): string {
if (!this.isExistMotionSoundFile(groupName, index)) {
return '';
}
return this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getValueByIndex(index)
.getValueByString(this.soundPath)
.getRawString();
}
/**
* モーション開始時のフェードイン処理時間を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return フェードイン処理時間[秒]
*/
public getMotionFadeInTimeValue(groupName: string, index: number): number {
if (!this.isExistMotionFadeIn(groupName, index)) {
return -1.0;
}
return this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getValueByIndex(index)
.getValueByString(this.fadeInTime)
.toFloat();
}
/**
* モーション終了時のフェードアウト処理時間を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return フェードアウト処理時間[秒]
*/
public getMotionFadeOutTimeValue(groupName: string, index: number): number {
if (!this.isExistMotionFadeOut(groupName, index)) {
return -1.0;
}
return this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getValueByIndex(index)
.getValueByString(this.fadeOutTime)
.toFloat();
}
/**
* ユーザーデータのファイル名を取得する
* @return ユーザーデータのファイル名
*/
public getUserDataFile(): string {
if (!this.isExistUserDataFile()) {
return '';
}
return this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.userData)
.getRawString();
}
/**
* レイアウト情報を取得する
* @param outLayoutMap Mapクラスのインスタンス
* @return true レイアウト情報が存在する
* @return false レイアウト情報が存在しない
*/
public getLayoutMap(outLayoutMap: Map<string, number>): boolean {
// 存在しない要素にアクセスするとエラーになるためValueがnullの場合はnullを代入する
const map: Map<string, Value> = this.getJson()
.getRoot()
.getValueByString(this.layout)
.getMap();
if (map == null) {
return false;
}
let ret = false;
for (const element of map) {
outLayoutMap.set(element[0], element[1].toFloat());
ret = true;
}
return ret;
}
/**
* 目パチに関連付けられたパラメータの数を取得する
* @return 目パチに関連付けられたパラメータの数
*/
public getEyeBlinkParameterCount(): number {
if (!this.isExistEyeBlinkParameters()) {
return 0;
}
let num = 0;
for (
let i = 0;
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
i++
) {
const refI: Value =
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.getValueByString(this.name).getRawString() == this.eyeBlink) {
num = refI.getValueByString(this.ids).getVector().length;
break;
}
}
return num;
}
/**
* 目パチに関連付けられたパラメータのIDを取得する
* @param index 配列のインデックス値
* @return パラメータID
*/
public getEyeBlinkParameterId(index: number): CubismIdHandle {
if (!this.isExistEyeBlinkParameters()) {
return null;
}
for (
let i = 0;
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
i++
) {
const refI: Value =
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.getValueByString(this.name).getRawString() == this.eyeBlink) {
return CubismFramework.getIdManager().getId(
refI.getValueByString(this.ids).getValueByIndex(index).getRawString()
);
}
}
return null;
}
/**
* リップシンクに関連付けられたパラメータの数を取得する
* @return リップシンクに関連付けられたパラメータの数
*/
public getLipSyncParameterCount(): number {
if (!this.isExistLipSyncParameters()) {
return 0;
}
let num = 0;
for (
let i = 0;
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
i++
) {
const refI: Value =
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.getValueByString(this.name).getRawString() == this.lipSync) {
num = refI.getValueByString(this.ids).getVector().length;
break;
}
}
return num;
}
/**
* リップシンクに関連付けられたパラメータの数を取得する
* @param index 配列のインデックス値
* @return パラメータID
*/
public getLipSyncParameterId(index: number): CubismIdHandle {
if (!this.isExistLipSyncParameters()) {
return null;
}
for (
let i = 0;
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
i++
) {
const refI: Value =
this._jsonValue[FrequestNode.FrequestNode_Groups].getValueByIndex(i);
if (refI.isNull() || refI.isError()) {
continue;
}
if (refI.getValueByString(this.name).getRawString() == this.lipSync) {
return CubismFramework.getIdManager().getId(
refI.getValueByString(this.ids).getValueByIndex(index).getRawString()
);
}
}
return null;
}
/**
* モデルファイルのキーが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistModelFile(): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Moc];
return !node.isNull() && !node.isError();
}
/**
* テクスチャファイルのキーが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistTextureFiles(): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Textures];
return !node.isNull() && !node.isError();
}
/**
* 当たり判定のキーが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistHitAreas(): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_HitAreas];
return !node.isNull() && !node.isError();
}
/**
* 物理演算ファイルのキーが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistPhysicsFile(): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Physics];
return !node.isNull() && !node.isError();
}
/**
* ポーズ設定ファイルのキーが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistPoseFile(): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Pose];
return !node.isNull() && !node.isError();
}
/**
* 表情設定ファイルのキーが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistExpressionFile(): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Expressions];
return !node.isNull() && !node.isError();
}
/**
* モーショングループのキーが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistMotionGroups(): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions];
return !node.isNull() && !node.isError();
}
/**
* 引数で指定したモーショングループのキーが存在するかどうかを確認する
* @param groupName グループ名
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistMotionGroupName(groupName: string): boolean {
const node: Value =
this._jsonValue[FrequestNode.FrequestNode_Motions].getValueByString(
groupName
);
return !node.isNull() && !node.isError();
}
/**
* 引数で指定したモーションに対応するサウンドファイルのキーが存在するかどうかを確認する
* @param groupName グループ名
* @param index 配列のインデックス値
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistMotionSoundFile(groupName: string, index: number): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getValueByIndex(index)
.getValueByString(this.soundPath);
return !node.isNull() && !node.isError();
}
/**
* 引数で指定したモーションに対応するフェードイン時間のキーが存在するかどうかを確認する
* @param groupName グループ名
* @param index 配列のインデックス値
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistMotionFadeIn(groupName: string, index: number): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getValueByIndex(index)
.getValueByString(this.fadeInTime);
return !node.isNull() && !node.isError();
}
/**
* 引数で指定したモーションに対応するフェードアウト時間のキーが存在するかどうかを確認する
* @param groupName グループ名
* @param index 配列のインデックス値
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistMotionFadeOut(groupName: string, index: number): boolean {
const node: Value = this._jsonValue[FrequestNode.FrequestNode_Motions]
.getValueByString(groupName)
.getValueByIndex(index)
.getValueByString(this.fadeOutTime);
return !node.isNull() && !node.isError();
}
/**
* UserDataのファイル名が存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistUserDataFile(): boolean {
const node: Value = this.getJson()
.getRoot()
.getValueByString(this.fileReferences)
.getValueByString(this.userData);
return !node.isNull() && !node.isError();
}
/**
* 目ぱちに対応付けられたパラメータが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistEyeBlinkParameters(): boolean {
if (
this._jsonValue[FrequestNode.FrequestNode_Groups].isNull() ||
this._jsonValue[FrequestNode.FrequestNode_Groups].isError()
) {
return false;
}
for (
let i = 0;
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
++i
) {
if (
this._jsonValue[FrequestNode.FrequestNode_Groups]
.getValueByIndex(i)
.getValueByString(this.name)
.getRawString() == this.eyeBlink
) {
return true;
}
}
return false;
}
/**
* リップシンクに対応付けられたパラメータが存在するかどうかを確認する
* @return true キーが存在する
* @return false キーが存在しない
*/
protected isExistLipSyncParameters(): boolean {
if (
this._jsonValue[FrequestNode.FrequestNode_Groups].isNull() ||
this._jsonValue[FrequestNode.FrequestNode_Groups].isError()
) {
return false;
}
for (
let i = 0;
i < this._jsonValue[FrequestNode.FrequestNode_Groups].getSize();
++i
) {
if (
this._jsonValue[FrequestNode.FrequestNode_Groups]
.getValueByIndex(i)
.getValueByString(this.name)
.getRawString() == this.lipSync
) {
return true;
}
}
return false;
}
protected _json: CubismJson;
protected _jsonValue: Array<Value>;
/**
* Model3Jsonのキー文字列
*/
protected readonly version = 'Version';
protected readonly fileReferences = 'FileReferences';
protected readonly groups = 'Groups';
protected readonly layout = 'Layout';
protected readonly hitAreas = 'HitAreas';
protected readonly moc = 'Moc';
protected readonly textures = 'Textures';
protected readonly physics = 'Physics';
protected readonly pose = 'Pose';
protected readonly expressions = 'Expressions';
protected readonly motions = 'Motions';
protected readonly userData = 'UserData';
protected readonly name = 'Name';
protected readonly filePath = 'File';
protected readonly id = 'Id';
protected readonly ids = 'Ids';
protected readonly target = 'Target';
// Motions
protected readonly idle = 'Idle';
protected readonly tapBody = 'TapBody';
protected readonly pinchIn = 'PinchIn';
protected readonly pinchOut = 'PinchOut';
protected readonly shake = 'Shake';
protected readonly flickHead = 'FlickHead';
protected readonly parameter = 'Parameter';
protected readonly soundPath = 'Sound';
protected readonly fadeInTime = 'FadeInTime';
protected readonly fadeOutTime = 'FadeOutTime';
// Layout
protected readonly centerX = 'CenterX';
protected readonly centerY = 'CenterY';
protected readonly x = 'X';
protected readonly y = 'Y';
protected readonly width = 'Width';
protected readonly height = 'Height';
protected readonly lipSync = 'LipSync';
protected readonly eyeBlink = 'EyeBlink';
protected readonly initParameter = 'init_param';
protected readonly initPartsVisible = 'init_parts_visible';
protected readonly val = 'val';
}
// Namespace definition for compatibility.
import * as $ from './cubismmodelsettingjson';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismModelSettingJson = $.CubismModelSettingJson;
export type CubismModelSettingJson = $.CubismModelSettingJson;
export const FrequestNode = $.FrequestNode;
export type FrequestNode = $.FrequestNode;
}

View File

@@ -0,0 +1,123 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismModel } from '../model/cubismmodel';
/**
* 呼吸機能
*
* 呼吸機能を提供する。
*/
export class CubismBreath {
/**
* インスタンスの作成
*/
public static create(): CubismBreath {
return new CubismBreath();
}
/**
* インスタンスの破棄
* @param instance 対象のCubismBreath
*/
public static delete(instance: CubismBreath): void {
if (instance != null) {
instance = null;
}
}
/**
* 呼吸のパラメータの紐づけ
* @param breathParameters 呼吸を紐づけたいパラメータのリスト
*/
public setParameters(breathParameters: Array<BreathParameterData>): void {
this._breathParameters = breathParameters;
}
/**
* 呼吸に紐づいているパラメータの取得
* @return 呼吸に紐づいているパラメータのリスト
*/
public getParameters(): Array<BreathParameterData> {
return this._breathParameters;
}
/**
* モデルのパラメータの更新
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
*/
public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
this._currentTime += deltaTimeSeconds;
const t: number = this._currentTime * 2.0 * Math.PI;
for (let i = 0; i < this._breathParameters.length; ++i) {
const data: BreathParameterData = this._breathParameters[i];
model.addParameterValueById(
data.parameterId,
data.offset + data.peak * Math.sin(t / data.cycle),
data.weight
);
}
}
/**
* コンストラクタ
*/
public constructor() {
this._currentTime = 0.0;
}
_breathParameters: Array<BreathParameterData>; // 呼吸にひもづいているパラメータのリスト
_currentTime: number; // 積算時間[秒]
}
/**
* 呼吸のパラメータ情報
*/
export class BreathParameterData {
/**
* コンストラクタ
* @param parameterId 呼吸をひもづけるパラメータID
* @param offset 呼吸を正弦波としたときの、波のオフセット
* @param peak 呼吸を正弦波としたときの、波の高さ
* @param cycle 呼吸を正弦波としたときの、波の周期
* @param weight パラメータへの重み
*/
constructor(
parameterId?: CubismIdHandle,
offset?: number,
peak?: number,
cycle?: number,
weight?: number
) {
this.parameterId = parameterId == undefined ? null : parameterId;
this.offset = offset == undefined ? 0.0 : offset;
this.peak = peak == undefined ? 0.0 : peak;
this.cycle = cycle == undefined ? 0.0 : cycle;
this.weight = weight == undefined ? 0.0 : weight;
}
parameterId: CubismIdHandle; // 呼吸をひもづけるパラメータID\
offset: number; // 呼吸を正弦波としたときの、波のオフセット
peak: number; // 呼吸を正弦波としたときの、波の高さ
cycle: number; // 呼吸を正弦波としたときの、波の周期
weight: number; // パラメータへの重み
}
// Namespace definition for compatibility.
import * as $ from './cubismbreath';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const BreathParameterData = $.BreathParameterData;
export type BreathParameterData = $.BreathParameterData;
export const CubismBreath = $.CubismBreath;
export type CubismBreath = $.CubismBreath;
}

View File

@@ -0,0 +1,234 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismModelSetting } from '../icubismmodelsetting';
import { CubismIdHandle } from '../id/cubismid';
import { CubismModel } from '../model/cubismmodel';
/**
* 自動まばたき機能
*
* 自動まばたき機能を提供する。
*/
export class CubismEyeBlink {
/**
* インスタンスを作成する
* @param modelSetting モデルの設定情報
* @return 作成されたインスタンス
* @note 引数がNULLの場合、パラメータIDが設定されていない空のインスタンスを作成する。
*/
public static create(
modelSetting: ICubismModelSetting = null
): CubismEyeBlink {
return new CubismEyeBlink(modelSetting);
}
/**
* インスタンスの破棄
* @param eyeBlink 対象のCubismEyeBlink
*/
public static delete(eyeBlink: CubismEyeBlink): void {
if (eyeBlink != null) {
eyeBlink = null;
}
}
/**
* まばたきの間隔の設定
* @param blinkingInterval まばたきの間隔の時間[秒]
*/
public setBlinkingInterval(blinkingInterval: number): void {
this._blinkingIntervalSeconds = blinkingInterval;
}
/**
* まばたきのモーションの詳細設定
* @param closing まぶたを閉じる動作の所要時間[秒]
* @param closed まぶたを閉じている動作の所要時間[秒]
* @param opening まぶたを開く動作の所要時間[秒]
*/
public setBlinkingSetting(
closing: number,
closed: number,
opening: number
): void {
this._closingSeconds = closing;
this._closedSeconds = closed;
this._openingSeconds = opening;
}
/**
* まばたきさせるパラメータIDのリストの設定
* @param parameterIds パラメータのIDのリスト
*/
public setParameterIds(parameterIds: Array<CubismIdHandle>): void {
this._parameterIds = parameterIds;
}
/**
* まばたきさせるパラメータIDのリストの取得
* @return パラメータIDのリスト
*/
public getParameterIds(): Array<CubismIdHandle> {
return this._parameterIds;
}
/**
* モデルのパラメータの更新
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
*/
public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
this._userTimeSeconds += deltaTimeSeconds;
let parameterValue: number;
let t = 0.0;
const blinkingState: EyeState = this._blinkingState;
switch (blinkingState) {
case EyeState.EyeState_Closing:
t =
(this._userTimeSeconds - this._stateStartTimeSeconds) /
this._closingSeconds;
if (t >= 1.0) {
t = 1.0;
this._blinkingState = EyeState.EyeState_Closed;
this._stateStartTimeSeconds = this._userTimeSeconds;
}
parameterValue = 1.0 - t;
break;
case EyeState.EyeState_Closed:
t =
(this._userTimeSeconds - this._stateStartTimeSeconds) /
this._closedSeconds;
if (t >= 1.0) {
this._blinkingState = EyeState.EyeState_Opening;
this._stateStartTimeSeconds = this._userTimeSeconds;
}
parameterValue = 0.0;
break;
case EyeState.EyeState_Opening:
t =
(this._userTimeSeconds - this._stateStartTimeSeconds) /
this._openingSeconds;
if (t >= 1.0) {
t = 1.0;
this._blinkingState = EyeState.EyeState_Interval;
this._nextBlinkingTime = this.determinNextBlinkingTiming();
}
parameterValue = t;
break;
case EyeState.EyeState_Interval:
if (this._nextBlinkingTime < this._userTimeSeconds) {
this._blinkingState = EyeState.EyeState_Closing;
this._stateStartTimeSeconds = this._userTimeSeconds;
}
parameterValue = 1.0;
break;
case EyeState.EyeState_First:
default:
this._blinkingState = EyeState.EyeState_Interval;
this._nextBlinkingTime = this.determinNextBlinkingTiming();
parameterValue = 1.0;
break;
}
if (!CubismEyeBlink.CloseIfZero) {
parameterValue = -parameterValue;
}
for (let i = 0; i < this._parameterIds.length; ++i) {
model.setParameterValueById(this._parameterIds[i], parameterValue);
}
}
/**
* コンストラクタ
* @param modelSetting モデルの設定情報
*/
public constructor(modelSetting: ICubismModelSetting) {
this._blinkingState = EyeState.EyeState_First;
this._nextBlinkingTime = 0.0;
this._stateStartTimeSeconds = 0.0;
this._blinkingIntervalSeconds = 4.0;
this._closingSeconds = 0.1;
this._closedSeconds = 0.05;
this._openingSeconds = 0.15;
this._userTimeSeconds = 0.0;
this._parameterIds = new Array<CubismIdHandle>();
if (modelSetting == null) {
return;
}
this._parameterIds.length = modelSetting.getEyeBlinkParameterCount();
for (let i = 0; i < modelSetting.getEyeBlinkParameterCount(); ++i) {
this._parameterIds[i] = modelSetting.getEyeBlinkParameterId(i);
}
}
/**
* 次の瞬きのタイミングの決定
*
* @return 次のまばたきを行う時刻[秒]
*/
public determinNextBlinkingTiming(): number {
const r: number = Math.random();
return (
this._userTimeSeconds + r * (2.0 * this._blinkingIntervalSeconds - 1.0)
);
}
_blinkingState: number; // 現在の状態
_parameterIds: Array<CubismIdHandle>; // 操作対象のパラメータのIDのリスト
_nextBlinkingTime: number; // 次のまばたきの時刻[秒]
_stateStartTimeSeconds: number; // 現在の状態が開始した時刻[秒]
_blinkingIntervalSeconds: number; // まばたきの間隔[秒]
_closingSeconds: number; // まぶたを閉じる動作の所要時間[秒]
_closedSeconds: number; // まぶたを閉じている動作の所要時間[秒]
_openingSeconds: number; // まぶたを開く動作の所要時間[秒]
_userTimeSeconds: number; // デルタ時間の積算値[秒]
/**
* IDで指定された目のパラメータが、0のときに閉じるなら true 、1の時に閉じるなら false 。
*/
static readonly CloseIfZero: boolean = true;
}
/**
* まばたきの状態
*
* まばたきの状態を表す列挙型
*/
export enum EyeState {
EyeState_First = 0, // 初期状態
EyeState_Interval, // まばたきしていない状態
EyeState_Closing, // まぶたが閉じていく途中の状態
EyeState_Closed, // まぶたが閉じている状態
EyeState_Opening // まぶたが開いていく途中の状態
}
// Namespace definition for compatibility.
import * as $ from './cubismeyeblink';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismEyeBlink = $.CubismEyeBlink;
export type CubismEyeBlink = $.CubismEyeBlink;
export const EyeState = $.EyeState;
export type EyeState = $.EyeState;
}

View File

@@ -0,0 +1,120 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismModel } from '../model/cubismmodel';
/**
* ターゲットによるパラメータ追従機能
*
* ドラッグ入力に対するパラメータ追従機能を提供する。
*/
export class CubismLook {
/**
* インスタンスの作成
*/
public static create(): CubismLook {
return new CubismLook();
}
/**
* インスタンスの破棄
* @param instance 対象のCubismDrag
*/
public static delete(instance: CubismLook): void {
if (instance != null) {
instance = null;
}
}
/**
* ターゲット追従のパラメータの紐づけ
* @param lookParameters ターゲット追従を紐づけたいパラメータのリスト
*/
public setParameters(lookParameters: Array<LookParameterData>): void {
this._lookParameters = lookParameters;
}
/**
* ターゲット追従に紐づいているパラメータの取得
* @return ターゲット追従に紐づいているパラメータのリスト
*/
public getParameters(): Array<LookParameterData> {
return this._lookParameters;
}
/**
* モデルのパラメータの更新
* @param model 対象のモデル
* @param dragX ターゲットのX座標
* @param dragY ターゲットのY座標
*/
public updateParameters(
model: CubismModel,
dragX: number,
dragY: number
): void {
for (let i = 0; i < this._lookParameters.length; ++i) {
const data: LookParameterData = this._lookParameters[i];
model.addParameterValueById(
data.parameterId,
data.factorX * dragX +
data.factorY * dragY +
data.factorXY * dragX * dragY
);
}
}
/**
* コンストラクタ
*/
public constructor() {
this._lookParameters = new Array<LookParameterData>();
}
_lookParameters: Array<LookParameterData>; // ターゲット追従に紐づいているパラメータのリスト
}
/**
* ターゲット追従のパラメータ情報
*/
export class LookParameterData {
/**
* コンストラクタ
* @param parameterId ターゲット追従を紐づけるパラメータID
* @param factorX X方向ドラッグ入力に対する係数
* @param factorY Y方向ドラッグ入力に対する係数
* @param factorXY XY積ドラッグ入力に対する係数
*/
constructor(
parameterId?: CubismIdHandle,
factorX?: number,
factorY?: number,
factorXY?: number
) {
this.parameterId = parameterId == undefined ? null : parameterId;
this.factorX = factorX == undefined ? 0.0 : factorX;
this.factorY = factorY == undefined ? 0.0 : factorY;
this.factorXY = factorXY == undefined ? 0.0 : factorXY;
}
parameterId: CubismIdHandle; // ターゲット追従を紐づけるパラメータID
factorX: number; // X方向ドラッグ入力に対する係数
factorY: number; // Y方向ドラッグ入力に対する係数
factorXY: number; // XY積ドラッグ入力に対する係数
}
// Namespace definition for compatibility.
import * as $ from './cubismlook';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const LookParameterData = $.LookParameterData;
export type LookParameterData = $.LookParameterData;
export const CubismLook = $.CubismLook;
export type CubismLook = $.CubismLook;
}

View File

@@ -0,0 +1,396 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { CubismModel } from '../model/cubismmodel';
import { CubismJson, Value } from '../utils/cubismjson';
const Epsilon = 0.001;
const DefaultFadeInSeconds = 0.5;
// Pose.jsonのタグ
const FadeIn = 'FadeInTime';
const Link = 'Link';
const Groups = 'Groups';
const Id = 'Id';
/**
* パーツの不透明度の設定
*
* パーツの不透明度の管理と設定を行う。
*/
export class CubismPose {
/**
* インスタンスの作成
* @param pose3json pose3.jsonのデータ
* @param size pose3.jsonのデータのサイズ[byte]
* @return 作成されたインスタンス
*/
public static create(pose3json: ArrayBuffer, size: number): CubismPose {
const json: CubismJson = CubismJson.create(pose3json, size);
if (!json) {
return null;
}
const ret: CubismPose = new CubismPose();
const root: Value = json.getRoot();
// フェード時間の指定
if (!root.getValueByString(FadeIn).isNull()) {
ret._fadeTimeSeconds = root
.getValueByString(FadeIn)
.toFloat(DefaultFadeInSeconds);
if (ret._fadeTimeSeconds < 0.0) {
ret._fadeTimeSeconds = DefaultFadeInSeconds;
}
}
// パーツグループ
const poseListInfo: Value = root.getValueByString(Groups);
const poseCount: number = poseListInfo.getSize();
ret._partGroupCounts.length = poseCount;
for (let poseIndex = 0; poseIndex < poseCount; ++poseIndex) {
const idListInfo: Value = poseListInfo.getValueByIndex(poseIndex);
const idCount: number = idListInfo.getSize();
let groupCount = 0;
for (let groupIndex = 0; groupIndex < idCount; ++groupIndex) {
const partInfo: Value = idListInfo.getValueByIndex(groupIndex);
const partData: PartData = new PartData();
const parameterId: CubismIdHandle =
CubismFramework.getIdManager().getId(
partInfo.getValueByString(Id).getRawString()
);
partData.partId = parameterId;
// リンクするパーツの設定
if (!partInfo.getValueByString(Link).isNull()) {
const linkListInfo: Value = partInfo.getValueByString(Link);
const linkCount: number = linkListInfo.getSize();
for (let linkIndex = 0; linkIndex < linkCount; ++linkIndex) {
const linkPart: PartData = new PartData();
const linkId: CubismIdHandle = CubismFramework.getIdManager().getId(
linkListInfo.getValueByIndex(linkIndex).getString()
);
linkPart.partId = linkId;
partData.link.push(linkPart);
}
}
ret._partGroups.push(partData.clone());
++groupCount;
}
ret._partGroupCounts[poseIndex] = groupCount;
}
CubismJson.delete(json);
return ret;
}
/**
* インスタンスを破棄する
* @param pose 対象のCubismPose
*/
public static delete(pose: CubismPose): void {
if (pose != null) {
pose = null;
}
}
/**
* モデルのパラメータの更新
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
*/
public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
// 前回のモデルと同じでない場合は初期化が必要
if (model != this._lastModel) {
// パラメータインデックスの初期化
this.reset(model);
}
this._lastModel = model;
// 設定から時間を変更すると、経過時間がマイナスになる事があるので、経過時間0として対応
if (deltaTimeSeconds < 0.0) {
deltaTimeSeconds = 0.0;
}
let beginIndex = 0;
for (let i = 0; i < this._partGroupCounts.length; i++) {
const partGroupCount: number = this._partGroupCounts[i];
this.doFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
beginIndex += partGroupCount;
}
this.copyPartOpacities(model);
}
/**
* 表示を初期化
* @param model 対象のモデル
* @note 不透明度の初期値が0でないパラメータは、不透明度をに設定する
*/
public reset(model: CubismModel): void {
let beginIndex = 0;
for (let i = 0; i < this._partGroupCounts.length; ++i) {
const groupCount: number = this._partGroupCounts[i];
for (let j: number = beginIndex; j < beginIndex + groupCount; ++j) {
this._partGroups[j].initialize(model);
const partsIndex: number = this._partGroups[j].partIndex;
const paramIndex: number = this._partGroups[j].parameterIndex;
if (partsIndex < 0) {
continue;
}
model.setPartOpacityByIndex(partsIndex, j == beginIndex ? 1.0 : 0.0);
model.setParameterValueByIndex(paramIndex, j == beginIndex ? 1.0 : 0.0);
for (let k = 0; k < this._partGroups[j].link.length; ++k) {
this._partGroups[j].link[k].initialize(model);
}
}
beginIndex += groupCount;
}
}
/**
* パーツの不透明度をコピー
*
* @param model 対象のモデル
*/
public copyPartOpacities(model: CubismModel): void {
for (
let groupIndex = 0;
groupIndex < this._partGroups.length;
++groupIndex
) {
const partData: PartData = this._partGroups[groupIndex];
if (partData.link.length == 0) {
continue; // 連動するパラメータはない
}
const partIndex: number = this._partGroups[groupIndex].partIndex;
const opacity: number = model.getPartOpacityByIndex(partIndex);
for (let linkIndex = 0; linkIndex < partData.link.length; ++linkIndex) {
const linkPart: PartData = partData.link[linkIndex];
const linkPartIndex: number = linkPart.partIndex;
if (linkPartIndex < 0) {
continue;
}
model.setPartOpacityByIndex(linkPartIndex, opacity);
}
}
}
/**
* パーツのフェード操作を行う。
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
* @param beginIndex フェード操作を行うパーツグループの先頭インデックス
* @param partGroupCount フェード操作を行うパーツグループの個数
*/
public doFade(
model: CubismModel,
deltaTimeSeconds: number,
beginIndex: number,
partGroupCount: number
): void {
let visiblePartIndex = -1;
let newOpacity = 1.0;
const phi = 0.5;
const backOpacityThreshold = 0.15;
// 現在、表示状態になっているパーツを取得
for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
const partIndex: number = this._partGroups[i].partIndex;
const paramIndex: number = this._partGroups[i].parameterIndex;
if (model.getParameterValueByIndex(paramIndex) > Epsilon) {
if (visiblePartIndex >= 0) {
break;
}
visiblePartIndex = i;
// ゼロ除算の回避
if (this._fadeTimeSeconds == 0) {
newOpacity = 1.0;
continue;
}
newOpacity = model.getPartOpacityByIndex(partIndex);
// 新しい不透明度を計算
newOpacity += deltaTimeSeconds / this._fadeTimeSeconds;
if (newOpacity > 1.0) {
newOpacity = 1.0;
}
}
}
if (visiblePartIndex < 0) {
visiblePartIndex = 0;
newOpacity = 1.0;
}
// 表示パーツ、非表示パーツの不透明度を設定する
for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
const partsIndex: number = this._partGroups[i].partIndex;
// 表示パーツの設定
if (visiblePartIndex == i) {
model.setPartOpacityByIndex(partsIndex, newOpacity); // 先に設定
}
// 非表示パーツの設定
else {
let opacity: number = model.getPartOpacityByIndex(partsIndex);
let a1: number; // 計算によって求められる不透明度
if (newOpacity < phi) {
a1 = (newOpacity * (phi - 1)) / phi + 1.0; // (0,1),(phi,phi)を通る直線式
} else {
a1 = ((1 - newOpacity) * phi) / (1.0 - phi); // (1,0),(phi,phi)を通る直線式
}
// 背景の見える割合を制限する場合
const backOpacity: number = (1.0 - a1) * (1.0 - newOpacity);
if (backOpacity > backOpacityThreshold) {
a1 = 1.0 - backOpacityThreshold / (1.0 - newOpacity);
}
if (opacity > a1) {
opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる
}
model.setPartOpacityByIndex(partsIndex, opacity);
}
}
}
/**
* コンストラクタ
*/
public constructor() {
this._fadeTimeSeconds = DefaultFadeInSeconds;
this._lastModel = null;
this._partGroups = new Array<PartData>();
this._partGroupCounts = new Array<number>();
}
_partGroups: Array<PartData>; // パーツグループ
_partGroupCounts: Array<number>; // それぞれのパーツグループの個数
_fadeTimeSeconds: number; // フェード時間[秒]
_lastModel: CubismModel; // 前回操作したモデル
}
/**
* パーツにまつわるデータを管理
*/
export class PartData {
/**
* コンストラクタ
*/
constructor(v?: PartData) {
this.parameterIndex = 0;
this.partIndex = 0;
this.link = new Array<PartData>();
if (v != undefined) {
this.partId = v.partId;
this.link.length = v.link.length;
for (let i = 0; i < v.link.length; i++) {
this.link[i] = v.link[i].clone();
}
}
}
/**
* =演算子のオーバーロード
*/
public assignment(v: PartData): PartData {
this.partId = v.partId;
let dstIndex: number = this.link.length;
this.link.length += v.link.length;
for (const partData of v.link) {
this.link[dstIndex++] = partData.clone();
}
return this;
}
/**
* 初期化
* @param model 初期化に使用するモデル
*/
public initialize(model: CubismModel): void {
this.parameterIndex = model.getParameterIndex(this.partId);
this.partIndex = model.getPartIndex(this.partId);
model.setParameterValueByIndex(this.parameterIndex, 1);
}
/**
* オブジェクトのコピーを生成する
*/
public clone(): PartData {
const clonePartData: PartData = new PartData();
clonePartData.partId = this.partId;
clonePartData.parameterIndex = this.parameterIndex;
clonePartData.partIndex = this.partIndex;
clonePartData.link = new Array<PartData>();
clonePartData.link.length = this.link.length;
for (let i = 0; i < this.link.length; i++) {
clonePartData.link[i] = this.link[i].clone();
}
return clonePartData;
}
partId: CubismIdHandle; // パーツID
parameterIndex: number; // パラメータのインデックス
partIndex: number; // パーツのインデックス
link: Array<PartData>; // 連動するパラメータ
}
// Namespace definition for compatibility.
import * as $ from './cubismpose';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismPose = $.CubismPose;
export type CubismPose = $.CubismPose;
export const PartData = $.PartData;
export type PartData = $.PartData;
}

View File

@@ -0,0 +1,51 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
/**
* メモリアロケーションを抽象化したクラス
*
* メモリ確保・解放処理をプラットフォーム側で実装して
* フレームワークから呼び出すためのインターフェース
*/
export abstract class ICubismAllocator {
/**
* アラインメント制約なしのヒープ・メモリーを確保します
*
* @param size 確保するバイト数
* @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す
*/
public abstract allocate(size: number): any;
/**
* アラインメント制約なしのヒープ・メモリーを解放します。
*
* @param memory 解放するメモリのアドレス
*/
public abstract deallocate(memory: any): void;
/**
* アラインメント制約有のヒープ・メモリーを確保します。
* @param size 確保するバイト数
* @param alignment メモリーブロックのアラインメント幅
* @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す
*/
public abstract allocateAligned(size: number, alignment: number): any;
/**
* アラインメント制約ありのヒープ・メモリーを解放します。
* @param alignedMemory 解放するメモリのアドレス
*/
public abstract deallocateAligned(alignedMemory: any): void;
}
// Namespace definition for compatibility.
import * as $ from './icubismallcator';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const ICubismAllocator = $.ICubismAllocator;
export type ICubismAllocator = $.ICubismAllocator;
}

View File

@@ -0,0 +1,202 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from './id/cubismid';
/**
* モデル設定情報を取り扱う関数を宣言した純粋仮想クラス。
*
* このクラスを継承することで、モデル設定情報を取り扱うクラスになる。
*/
export abstract class ICubismModelSetting {
/**
* Mocファイルの名前を取得する
* @return Mocファイルの名前
*/
public abstract getModelFileName(): string;
/**
* モデルが使用するテクスチャの数を取得する
* テクスチャの数
*/
public abstract getTextureCount(): number;
/**
* テクスチャが配置されたディレクトリの名前を取得する
* @return テクスチャが配置されたディレクトリの名前
*/
public abstract getTextureDirectory(): string;
/**
* モデルが使用するテクスチャの名前を取得する
* @param index 配列のインデックス値
* @return テクスチャの名前
*/
public abstract getTextureFileName(index: number): string;
/**
* モデルに設定された当たり判定の数を取得する
* @return モデルに設定された当たり判定の数
*/
public abstract getHitAreasCount(): number;
/**
* 当たり判定に設定されたIDを取得する
*
* @param index 配列のindex
* @return 当たり判定に設定されたID
*/
public abstract getHitAreaId(index: number): CubismIdHandle;
/**
* 当たり判定に設定された名前を取得する
* @param index 配列のインデックス値
* @return 当たり判定に設定された名前
*/
public abstract getHitAreaName(index: number): string;
/**
* 物理演算設定ファイルの名前を取得する
* @return 物理演算設定ファイルの名前
*/
public abstract getPhysicsFileName(): string;
/**
* パーツ切り替え設定ファイルの名前を取得する
* @return パーツ切り替え設定ファイルの名前
*/
public abstract getPoseFileName(): string;
/**
* 表情設定ファイルの数を取得する
* @return 表情設定ファイルの数
*/
public abstract getExpressionCount(): number;
/**
* 表情設定ファイルを識別する名前(別名)を取得する
* @param index 配列のインデックス値
* @return 表情の名前
*/
public abstract getExpressionName(index: number): string;
/**
* 表情設定ファイルの名前を取得する
* @param index 配列のインデックス値
* @return 表情設定ファイルの名前
*/
public abstract getExpressionFileName(index: number): string;
/**
* モーショングループの数を取得する
* @return モーショングループの数
*/
public abstract getMotionGroupCount(): number;
/**
* モーショングループの名前を取得する
* @param index 配列のインデックス値
* @return モーショングループの名前
*/
public abstract getMotionGroupName(index: number): string;
/**
* モーショングループに含まれるモーションの数を取得する
* @param groupName モーショングループの名前
* @return モーショングループの数
*/
public abstract getMotionCount(groupName: string): number;
/**
* グループ名とインデックス値からモーションファイル名を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return モーションファイルの名前
*/
public abstract getMotionFileName(groupName: string, index: number): string;
/**
* モーションに対応するサウンドファイルの名前を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return サウンドファイルの名前
*/
public abstract getMotionSoundFileName(
groupName: string,
index: number
): string;
/**
* モーション開始時のフェードイン処理時間を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return フェードイン処理時間[秒]
*/
public abstract getMotionFadeInTimeValue(
groupName: string,
index: number
): number;
/**
* モーション終了時のフェードアウト処理時間を取得する
* @param groupName モーショングループの名前
* @param index 配列のインデックス値
* @return フェードアウト処理時間[秒]
*/
public abstract getMotionFadeOutTimeValue(
groupName: string,
index: number
): number;
/**
* ユーザーデータのファイル名を取得する
* @return ユーザーデータのファイル名
*/
public abstract getUserDataFile(): string;
/**
* レイアウト情報を取得する
* @param outLayoutMap Mapクラスのインスタンス
* @return true レイアウト情報が存在する
* @return false レイアウト情報が存在しない
*/
public abstract getLayoutMap(outLayoutMap: Map<string, number>): boolean;
/**
* 目パチに関連付けられたパラメータの数を取得する
* @return 目パチに関連付けられたパラメータの数
*/
public abstract getEyeBlinkParameterCount(): number;
/**
* 目パチに関連付けられたパラメータのIDを取得する
* @param index 配列のインデックス値
* @return パラメータID
*/
public abstract getEyeBlinkParameterId(index: number): CubismIdHandle;
/**
* リップシンクに関連付けられたパラメータの数を取得する
* @return リップシンクに関連付けられたパラメータの数
*/
public abstract getLipSyncParameterCount(): number;
/**
* リップシンクに関連付けられたパラメータの数を取得する
* @param index 配列のインデックス値
* @return パラメータID
*/
public abstract getLipSyncParameterId(index: number): CubismIdHandle;
}
// Namespace definition for compatibility.
import * as $ from './icubismmodelsetting';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const ICubismModelSetting = $.ICubismModelSetting;
export type ICubismModelSetting = $.ICubismModelSetting;
}

View File

@@ -0,0 +1,85 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
/**
* パラメータ名・パーツ名・Drawable名を保持
*
* パラメータ名・パーツ名・Drawable名を保持するクラス。
*
* @note 指定したID文字列からCubismIdを取得する際はこのクラスの生成メソッドを呼ばず、
* CubismIdManager().getId(id)を使用してください
*/
export class CubismId {
/**
* 内部で使用するCubismIdクラス生成メソッド
*
* @param id ID文字列
* @return CubismId
* @note 指定したID文字列からCubismIdを取得する際は
* CubismIdManager().getId(id)を使用してください
*/
public static createIdInternal(id: string) {
return new CubismId(id);
}
/**
* ID名を取得する
*/
public getString() {
return this._id;
}
/**
* idを比較
* @param c 比較するid
* @return 同じならばtrue,異なっていればfalseを返す
*/
public isEqual(c: string | CubismId): boolean {
if (typeof c === 'string') {
return this._id == c;
} else if (c instanceof CubismId) {
return this._id == c._id;
}
return false;
}
/**
* idを比較
* @param c 比較するid
* @return 同じならばtrue,異なっていればfalseを返す
*/
public isNotEqual(c: string | CubismId): boolean {
if (typeof c == 'string') {
return !(this._id == c);
} else if (c instanceof CubismId) {
return !(this._id == c._id);
}
return false;
}
/**
* プライベートコンストラクタ
*
* @note ユーザーによる生成は許可しません
*/
private constructor(id: string) {
this._id = id;
}
private _id: string; // ID名
}
export declare type CubismIdHandle = CubismId;
// Namespace definition for compatibility.
import * as $ from './cubismid';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismId = $.CubismId;
export type CubismId = $.CubismId;
export type CubismIdHandle = $.CubismIdHandle;
}

View File

@@ -0,0 +1,114 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismId } from './cubismid';
/**
* ID名の管理
*
* ID名を管理する。
*/
export class CubismIdManager {
/**
* コンストラクタ
*/
public constructor() {
this._ids = new Array<CubismId>();
}
/**
* デストラクタ相当の処理
*/
public release(): void {
for (let i = 0; i < this._ids.length; ++i) {
this._ids[i] = void 0;
}
this._ids = null;
}
/**
* ID名をリストから登録
*
* @param ids ID名リスト
* @param count IDの個数
*/
public registerIds(ids: string[]): void {
for (let i = 0; i < ids.length; i++) {
this.registerId(ids[i]);
}
}
/**
* ID名を登録
*
* @param id ID名
*/
public registerId(id: string): CubismId {
let result: CubismId = null;
if ('string' == typeof id) {
if ((result = this.findId(id)) != null) {
return result;
}
result = CubismId.createIdInternal(id);
this._ids.push(result);
} else {
return this.registerId(id);
}
return result;
}
/**
* ID名からIDを取得する
*
* @param id ID名
*/
public getId(id: string): CubismId {
return this.registerId(id);
}
/**
* ID名からIDの確認
*
* @return true 存在する
* @return false 存在しない
*/
public isExist(id: string): boolean {
if ('string' == typeof id) {
return this.findId(id) != null;
}
return this.isExist(id);
}
/**
* ID名からIDを検索する。
*
* @param id ID名
* @return 登録されているID。なければNULL。
*/
private findId(id: string): CubismId {
for (let i = 0; i < this._ids.length; ++i) {
if (this._ids[i].getString() == id) {
return this._ids[i];
}
}
return null;
}
private _ids: Array<CubismId>; // 登録されているIDのリスト
}
// Namespace definition for compatibility.
import * as $ from './cubismidmanager';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismIdManager = $.CubismIdManager;
export type CubismIdManager = $.CubismIdManager;
}

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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdManager } from './id/cubismidmanager';
import { CubismRenderer } from './rendering/cubismrenderer';
import {
CSM_ASSERT,
CubismLogInfo,
CubismLogWarning
} from './utils/cubismdebug';
import { Value } from './utils/cubismjson';
export function strtod(s: string, endPtr: string[]): number {
let index = 0;
for (let i = 1; ; i++) {
const testC: string = s.slice(i - 1, i);
// 指数・マイナスの可能性があるのでスキップする
if (testC == 'e' || testC == '-' || testC == 'E') {
continue;
} // 文字列の範囲を広げていく
const test: string = s.substring(0, i);
const number = Number(test);
if (isNaN(number)) {
// 数値として認識できなくなったので終了
break;
} // 最後に数値としてできたindexを格納しておく
index = i;
}
let d = parseFloat(s); // パースした数値
if (isNaN(d)) {
// 数値として認識できなくなったので終了
d = NaN;
}
endPtr[0] = s.slice(index); // 後続の文字列
return d;
}
// ファイルスコープの変数を初期化
let s_isStarted = false;
let s_isInitialized = false;
let s_option: Option = null;
let s_cubismIdManager: CubismIdManager = null;
/**
* Framework内で使う定数の宣言
*/
export const Constant = Object.freeze<Record<string, number>>({
vertexOffset: 0, // メッシュ頂点のオフセット値
vertexStep: 2 // メッシュ頂点のステップ値
});
export function csmDelete<T>(address: T): void {
if (!address) {
return;
}
address = void 0;
}
/**
* Live2D Cubism SDK Original Workflow SDKのエントリポイント
* 利用開始時はCubismFramework.initialize()を呼び、CubismFramework.dispose()で終了する。
*/
export class CubismFramework {
/**
* Cubism FrameworkのAPIを使用可能にする。
* APIを実行する前に必ずこの関数を実行すること。
* 一度準備が完了して以降は、再び実行しても内部処理がスキップされます。
*
* @param option Optionクラスのインスタンス
*
* @return 準備処理が完了したらtrueが返ります。
*/
public static startUp(option: Option = null): boolean {
if (s_isStarted) {
CubismLogInfo('CubismFramework.startUp() is already done.');
return s_isStarted;
}
s_option = option;
if (s_option != null) {
Live2DCubismCore.Logging.csmSetLogFunction(s_option.logFunction);
}
s_isStarted = true;
// Live2D Cubism Coreバージョン情報を表示
if (s_isStarted) {
const version: number = Live2DCubismCore.Version.csmGetVersion();
const major: number = (version & 0xff000000) >> 24;
const minor: number = (version & 0x00ff0000) >> 16;
const patch: number = version & 0x0000ffff;
const versionNumber: number = version;
CubismLogInfo(
`Live2D Cubism Core version: {0}.{1}.{2} ({3})`,
('00' + major).slice(-2),
('00' + minor).slice(-2),
('0000' + patch).slice(-4),
versionNumber
);
}
CubismLogInfo('CubismFramework.startUp() is complete.');
return s_isStarted;
}
/**
* StartUp()で初期化したCubismFrameworkの各パラメータをクリアします。
* Dispose()したCubismFrameworkを再利用する際に利用してください。
*/
public static cleanUp(): void {
s_isStarted = false;
s_isInitialized = false;
s_option = null;
s_cubismIdManager = null;
}
/**
* Cubism Framework内のリソースを初期化してモデルを表示可能な状態にします。<br>
* 再度Initialize()するには先にDispose()を実行する必要があります。
*
* @param memorySize 初期化時メモリ量 [byte(s)]
* 複数モデル表示時などにモデルが更新されない際に使用してください。
* 指定する際は必ず1024*1024*16 byte(16MB)以上の値を指定してください。
* それ以外はすべて1024*1024*16 byteに丸めます。
*/
public static initialize(memorySize = 0): void {
CSM_ASSERT(s_isStarted);
if (!s_isStarted) {
CubismLogWarning('CubismFramework is not started.');
return;
}
// --- s_isInitializedによる連続初期化ガード ---
// 連続してリソース確保が行われないようにする。
// 再度Initialize()するには先にDispose()を実行する必要がある。
if (s_isInitialized) {
CubismLogWarning(
'CubismFramework.initialize() skipped, already initialized.'
);
return;
}
//---- static 初期化 ----
Value.staticInitializeNotForClientCall();
s_cubismIdManager = new CubismIdManager();
// --- HACK: 初期化時メモリ量の拡張(単位byte) ---
// 複数モデル表示時などにモデルが更新されない際に使用してください。
// 指定する際は必ず1024*1024*16 byte(16MB)以上の値を指定してください。
// それ以外はすべて1024*1024*16 byteに丸めます。
Live2DCubismCore.Memory.initializeAmountOfMemory(memorySize);
s_isInitialized = true;
CubismLogInfo('CubismFramework.initialize() is complete.');
}
/**
* Cubism Framework内の全てのリソースを解放します。
* ただし、外部で確保されたリソースについては解放しません。
* 外部で適切に破棄する必要があります。
*/
public static dispose(): void {
CSM_ASSERT(s_isStarted);
if (!s_isStarted) {
CubismLogWarning('CubismFramework is not started.');
return;
}
// --- s_isInitializedによる未初期化解放ガード ---
// dispose()するには先にinitialize()を実行する必要がある。
if (!s_isInitialized) {
// false...リソース未確保の場合
CubismLogWarning('CubismFramework.dispose() skipped, not initialized.');
return;
}
Value.staticReleaseNotForClientCall();
s_cubismIdManager.release();
s_cubismIdManager = null;
// レンダラの静的リソース(シェーダプログラム他)を解放する
CubismRenderer.staticRelease();
s_isInitialized = false;
CubismLogInfo('CubismFramework.dispose() is complete.');
}
/**
* Cubism FrameworkのAPIを使用する準備が完了したかどうか
* @return APIを使用する準備が完了していればtrueが返ります。
*/
public static isStarted(): boolean {
return s_isStarted;
}
/**
* Cubism Frameworkのリソース初期化がすでに行われているかどうか
* @return リソース確保が完了していればtrueが返ります
*/
public static isInitialized(): boolean {
return s_isInitialized;
}
/**
* Core APIにバインドしたログ関数を実行する
*
* @praram message ログメッセージ
*/
public static coreLogFunction(message: string): void {
// Return if logging not possible.
if (!Live2DCubismCore.Logging.csmGetLogFunction()) {
return;
}
Live2DCubismCore.Logging.csmGetLogFunction()(message);
}
/**
* 現在のログ出力レベル設定の値を返す。
*
* @return 現在のログ出力レベル設定の値
*/
public static getLoggingLevel(): LogLevel {
if (s_option != null) {
return s_option.loggingLevel;
}
return LogLevel.LogLevel_Off;
}
/**
* IDマネージャのインスタンスを取得する
* @return CubismManagerクラスのインスタンス
*/
public static getIdManager(): CubismIdManager {
return s_cubismIdManager;
}
/**
* 静的クラスとして使用する
* インスタンス化させない
*/
private constructor() {}
}
export class Option {
logFunction: Live2DCubismCore.csmLogFunction; // ログ出力の関数オブジェクト
loggingLevel: LogLevel; // ログ出力レベルの設定
}
/**
* ログ出力のレベル
*/
export enum LogLevel {
LogLevel_Verbose = 0, // 詳細ログ
LogLevel_Debug, // デバッグログ
LogLevel_Info, // Infoログ
LogLevel_Warning, // 警告ログ
LogLevel_Error, // エラーログ
LogLevel_Off // ログ出力無効
}
// Namespace definition for compatibility.
import * as $ from './live2dcubismframework';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const Constant = $.Constant;
export const csmDelete = $.csmDelete;
export const CubismFramework = $.CubismFramework;
export type CubismFramework = $.CubismFramework;
}

View File

@@ -0,0 +1,376 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismVector2 } from './cubismvector2';
/**
* 数値計算などに使用するユーティリティクラス
*/
export class CubismMath {
static readonly Epsilon: number = 0.00001;
/**
* 第一引数の値を最小値と最大値の範囲に収めた値を返す
*
* @param value 収められる値
* @param min 範囲の最小値
* @param max 範囲の最大値
* @return 最小値と最大値の範囲に収めた値
*/
static range(value: number, min: number, max: number): number {
if (value < min) {
value = min;
} else if (value > max) {
value = max;
}
return value;
}
/**
* サイン関数の値を求める
*
* @param x 角度値(ラジアン)
* @return サイン関数sin(x)の値
*/
static sin(x: number): number {
return Math.sin(x);
}
/**
* コサイン関数の値を求める
*
* @param x 角度値(ラジアン)
* @return コサイン関数cos(x)の値
*/
static cos(x: number): number {
return Math.cos(x);
}
/**
* 値の絶対値を求める
*
* @param x 絶対値を求める値
* @return 値の絶対値
*/
static abs(x: number): number {
return Math.abs(x);
}
/**
* 平方根(ルート)を求める
* @param x -> 平方根を求める値
* @return 値の平方根
*/
static sqrt(x: number): number {
return Math.sqrt(x);
}
/**
* 立方根を求める
* @param x -> 立方根を求める値
* @return 値の立方根
*/
static cbrt(x: number): number {
if (x === 0) {
return x;
}
let cx: number = x;
const isNegativeNumber: boolean = cx < 0;
if (isNegativeNumber) {
cx = -cx;
}
let ret: number;
if (cx === Infinity) {
ret = Infinity;
} else {
ret = Math.exp(Math.log(cx) / 3);
ret = (cx / (ret * ret) + 2 * ret) / 3;
}
return isNegativeNumber ? -ret : ret;
}
/**
* イージング処理されたサインを求める
* フェードイン・アウト時のイージングに利用できる
*
* @param value イージングを行う値
* @return イージング処理されたサイン値
*/
static getEasingSine(value: number): number {
if (value < 0.0) {
return 0.0;
} else if (value > 1.0) {
return 1.0;
}
return 0.5 - 0.5 * this.cos(value * Math.PI);
}
/**
* 大きい方の値を返す
*
* @param left 左辺の値
* @param right 右辺の値
* @return 大きい方の値
*/
static max(left: number, right: number): number {
return left > right ? left : right;
}
/**
* 小さい方の値を返す
*
* @param left 左辺の値
* @param right 右辺の値
* @return 小さい方の値
*/
static min(left: number, right: number): number {
return left > right ? right : left;
}
public static clamp(val: number, min: number, max: number): number {
if (val < min) {
return min;
} else if (max < val) {
return max;
}
return val;
}
/**
* 角度値をラジアン値に変換する
*
* @param degrees 角度値
* @return 角度値から変換したラジアン値
*/
static degreesToRadian(degrees: number): number {
return (degrees / 180.0) * Math.PI;
}
/**
* ラジアン値を角度値に変換する
*
* @param radian ラジアン値
* @return ラジアン値から変換した角度値
*/
static radianToDegrees(radian: number): number {
return (radian * 180.0) / Math.PI;
}
/**
* 2つのベクトルからラジアン値を求める
*
* @param from 始点ベクトル
* @param to 終点ベクトル
* @return ラジアン値から求めた方向ベクトル
*/
static directionToRadian(from: CubismVector2, to: CubismVector2): number {
const q1: number = Math.atan2(to.y, to.x);
const q2: number = Math.atan2(from.y, from.x);
let ret: number = q1 - q2;
while (ret < -Math.PI) {
ret += Math.PI * 2.0;
}
while (ret > Math.PI) {
ret -= Math.PI * 2.0;
}
return ret;
}
/**
* 2つのベクトルから角度値を求める
*
* @param from 始点ベクトル
* @param to 終点ベクトル
* @return 角度値から求めた方向ベクトル
*/
static directionToDegrees(from: CubismVector2, to: CubismVector2): number {
const radian: number = this.directionToRadian(from, to);
let degree: number = this.radianToDegrees(radian);
if (to.x - from.x > 0.0) {
degree = -degree;
}
return degree;
}
/**
* ラジアン値を方向ベクトルに変換する。
*
* @param totalAngle ラジアン値
* @return ラジアン値から変換した方向ベクトル
*/
static radianToDirection(totalAngle: number): CubismVector2 {
const ret: CubismVector2 = new CubismVector2();
ret.x = this.sin(totalAngle);
ret.y = this.cos(totalAngle);
return ret;
}
/**
* 三次方程式の三次項の係数が0になったときに補欠的に二次方程式の解をもとめる。
* a * x^2 + b * x + c = 0
*
* @param a -> 二次項の係数値
* @param b -> 一次項の係数値
* @param c -> 定数項の値
* @return 二次方程式の解
*/
static quadraticEquation(a: number, b: number, c: number): number {
if (this.abs(a) < CubismMath.Epsilon) {
if (this.abs(b) < CubismMath.Epsilon) {
return -c;
}
return -c / b;
}
return -(b + this.sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
}
/**
* カルダの公式によってベジェのt値に該当する次方程式の解を求める。
* 重解になったときには0.01.0の値になる解を返す。
*
* a * x^3 + b * x^2 + c * x + d = 0
*
* @param a -> 三次項の係数値
* @param b -> 二次項の係数値
* @param c -> 一次項の係数値
* @param d -> 定数項の値
* @return 0.01.0の間にある解
*/
static cardanoAlgorithmForBezier(
a: number,
b: number,
c: number,
d: number
): number {
if (this.abs(a) < CubismMath.Epsilon) {
return this.range(this.quadraticEquation(b, c, d), 0.0, 1.0);
}
const ba: number = b / a;
const ca: number = c / a;
const da: number = d / a;
const p: number = (3.0 * ca - ba * ba) / 3.0;
const p3: number = p / 3.0;
const q: number = (2.0 * ba * ba * ba - 9.0 * ba * ca + 27.0 * da) / 27.0;
const q2: number = q / 2.0;
const discriminant: number = q2 * q2 + p3 * p3 * p3;
const center = 0.5;
const threshold: number = center + 0.01;
if (discriminant < 0.0) {
const mp3: number = -p / 3.0;
const mp33: number = mp3 * mp3 * mp3;
const r: number = this.sqrt(mp33);
const t: number = -q / (2.0 * r);
const cosphi: number = this.range(t, -1.0, 1.0);
const phi: number = Math.acos(cosphi);
const crtr: number = this.cbrt(r);
const t1: number = 2.0 * crtr;
const root1: number = t1 * this.cos(phi / 3.0) - ba / 3.0;
if (this.abs(root1 - center) < threshold) {
return this.range(root1, 0.0, 1.0);
}
const root2: number =
t1 * this.cos((phi + 2.0 * Math.PI) / 3.0) - ba / 3.0;
if (this.abs(root2 - center) < threshold) {
return this.range(root2, 0.0, 1.0);
}
const root3: number =
t1 * this.cos((phi + 4.0 * Math.PI) / 3.0) - ba / 3.0;
return this.range(root3, 0.0, 1.0);
}
if (discriminant == 0.0) {
let u1: number;
if (q2 < 0.0) {
u1 = this.cbrt(-q2);
} else {
u1 = -this.cbrt(q2);
}
const root1: number = 2.0 * u1 - ba / 3.0;
if (this.abs(root1 - center) < threshold) {
return this.range(root1, 0.0, 1.0);
}
const root2: number = -u1 - ba / 3.0;
return this.range(root2, 0.0, 1.0);
}
const sd: number = this.sqrt(discriminant);
const u1: number = this.cbrt(sd - q2);
const v1: number = this.cbrt(sd + q2);
const root1: number = u1 - v1 - ba / 3.0;
return this.range(root1, 0.0, 1.0);
}
/**
* 浮動小数点の余りを求める。
*
* @param dividend 被除数(割られる値)
* @param divisor 除数(割る値)
* @return 余り
*/
static mod(dividend: number, divisor: number): number {
if (
!isFinite(dividend) ||
divisor === 0 ||
isNaN(dividend) ||
isNaN(divisor)
) {
console.warn(
`divided: ${dividend}, divisor: ${divisor} mod() returns 'NaN'.`
);
return NaN;
}
// 絶対値に変換する。
const absDividend = Math.abs(dividend);
const absDivisor = Math.abs(divisor);
// 絶対値で割り算する。
let result =
absDividend - Math.floor(absDividend / absDivisor) * absDivisor;
// 符号を被除数のものに指定する。
result *= Math.sign(dividend);
return result;
}
/**
* コンストラクタ
*/
private constructor() {}
}
// Namespace definition for compatibility.
import * as $ from './cubismmath';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMath = $.CubismMath;
export type CubismMath = $.CubismMath;
}

View File

@@ -0,0 +1,361 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismMath } from './cubismmath';
/**
* 4x4の行列
*
* 4x4行列の便利クラス。
*/
export class CubismMatrix44 {
/**
* コンストラクタ
*/
public constructor() {
this._tr = new Float32Array(16); // 4 * 4のサイズ
this.loadIdentity();
}
/**
* 受け取った2つの行列の乗算を行う。
*
* @param a 行列a
* @param b 行列b
*
* @return 乗算結果の行列
*/
public static multiply(
a: Float32Array,
b: Float32Array,
dst: Float32Array
): void {
const c: Float32Array = new Float32Array([
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0
]);
const n = 4;
for (let i = 0; i < n; ++i) {
for (let j = 0; j < n; ++j) {
for (let k = 0; k < n; ++k) {
c[j + i * 4] += a[k + i * 4] * b[j + k * 4];
}
}
}
for (let i = 0; i < 16; ++i) {
dst[i] = c[i];
}
}
/**
* 単位行列に初期化する
*/
public loadIdentity(): void {
const c: Float32Array = new Float32Array([
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
1.0
]);
this.setMatrix(c);
}
/**
* 行列を設定
*
* @param tr 16個の浮動小数点数で表される4x4の行列
*/
public setMatrix(tr: Float32Array): void {
for (let i = 0; i < 16; ++i) {
this._tr[i] = tr[i];
}
}
/**
* 行列を浮動小数点数の配列で取得
*
* @return 16個の浮動小数点数で表される4x4の行列
*/
public getArray(): Float32Array {
return this._tr;
}
/**
* X軸の拡大率を取得
*
* @return X軸の拡大率
*/
public getScaleX(): number {
return this._tr[0];
}
/**
* Y軸の拡大率を取得する
*
* @return Y軸の拡大率
*/
public getScaleY(): number {
return this._tr[5];
}
/**
* X軸の移動量を取得
*
* @return X軸の移動量
*/
public getTranslateX(): number {
return this._tr[12];
}
/**
* Y軸の移動量を取得
*
* @return Y軸の移動量
*/
public getTranslateY(): number {
return this._tr[13];
}
/**
* X軸の値を現在の行列で計算
*
* @param src X軸の値
*
* @return 現在の行列で計算されたX軸の値
*/
public transformX(src: number): number {
return this._tr[0] * src + this._tr[12];
}
/**
* Y軸の値を現在の行列で計算
*
* @param src Y軸の値
*
* @return 現在の行列で計算されたY軸の値
*/
public transformY(src: number): number {
return this._tr[5] * src + this._tr[13];
}
/**
* X軸の値を現在の行列で逆計算
*/
public invertTransformX(src: number): number {
return (src - this._tr[12]) / this._tr[0];
}
/**
* Y軸の値を現在の行列で逆計算
*/
public invertTransformY(src: number): number {
return (src - this._tr[13]) / this._tr[5];
}
/**
* 現在の行列の位置を起点にして移動
*
* 現在の行列の位置を起点にして相対的に移動する。
*
* @param x X軸の移動量
* @param y Y軸の移動量
*/
public translateRelative(x: number, y: number): void {
const tr1: Float32Array = new Float32Array([
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
x,
y,
0.0,
1.0
]);
CubismMatrix44.multiply(tr1, this._tr, this._tr);
}
/**
* 現在の行列の位置を移動
*
* 現在の行列の位置を指定した位置へ移動する
*
* @param x X軸の移動量
* @param y y軸の移動量
*/
public translate(x: number, y: number): void {
this._tr[12] = x;
this._tr[13] = y;
}
/**
* 現在の行列のX軸の位置を指定した位置へ移動する
*
* @param x X軸の移動量
*/
public translateX(x: number): void {
this._tr[12] = x;
}
/**
* 現在の行列のY軸の位置を指定した位置へ移動する
*
* @param y Y軸の移動量
*/
public translateY(y: number): void {
this._tr[13] = y;
}
/**
* 現在の行列の拡大率を相対的に設定する
*
* @param x X軸の拡大率
* @param y Y軸の拡大率
*/
public scaleRelative(x: number, y: number): void {
const tr1: Float32Array = new Float32Array([
x,
0.0,
0.0,
0.0,
0.0,
y,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0
]);
CubismMatrix44.multiply(tr1, this._tr, this._tr);
}
/**
* 現在の行列の拡大率を指定した倍率に設定する
*
* @param x X軸の拡大率
* @param y Y軸の拡大率
*/
public scale(x: number, y: number): void {
this._tr[0] = x;
this._tr[5] = y;
}
/**
* 引数で与えられた行列にこの行列を乗算する。
* (引数で与えられた行列) * (この行列)
*
* @note 関数名と実際の計算内容に乖離があるため、今後計算順が修正される可能性があります。
* @param m 行列
*/
public multiplyByMatrix(m: CubismMatrix44): void {
CubismMatrix44.multiply(m.getArray(), this._tr, this._tr);
}
/**
* 現在の行列の逆行列を求める。
*
* @return 現在の行列で計算された逆行列の値を返す
*/
public getInvert(): CubismMatrix44 {
const r00 = this._tr[0];
const r10 = this._tr[1];
const r20 = this._tr[2];
const r01 = this._tr[4];
const r11 = this._tr[5];
const r21 = this._tr[6];
const r02 = this._tr[8];
const r12 = this._tr[9];
const r22 = this._tr[10];
const tx = this._tr[12];
const ty = this._tr[13];
const tz = this._tr[14];
const det =
r00 * (r11 * r22 - r12 * r21) -
r01 * (r10 * r22 - r12 * r20) +
r02 * (r10 * r21 - r11 * r20);
const dst = new CubismMatrix44();
if (CubismMath.abs(det) < CubismMath.Epsilon) {
dst.loadIdentity();
return dst;
}
const invDet = 1.0 / det;
const inv00 = (r11 * r22 - r12 * r21) * invDet;
const inv01 = -(r01 * r22 - r02 * r21) * invDet;
const inv02 = (r01 * r12 - r02 * r11) * invDet;
const inv10 = -(r10 * r22 - r12 * r20) * invDet;
const inv11 = (r00 * r22 - r02 * r20) * invDet;
const inv12 = -(r00 * r12 - r02 * r10) * invDet;
const inv20 = (r10 * r21 - r11 * r20) * invDet;
const inv21 = -(r00 * r21 - r01 * r20) * invDet;
const inv22 = (r00 * r11 - r01 * r10) * invDet;
dst._tr[0] = inv00;
dst._tr[1] = inv10;
dst._tr[2] = inv20;
dst._tr[3] = 0.0;
dst._tr[4] = inv01;
dst._tr[5] = inv11;
dst._tr[6] = inv21;
dst._tr[7] = 0.0;
dst._tr[8] = inv02;
dst._tr[9] = inv12;
dst._tr[10] = inv22;
dst._tr[11] = 0.0;
dst._tr[12] = -(inv00 * tx + inv01 * ty + inv02 * tz);
dst._tr[13] = -(inv10 * tx + inv11 * ty + inv12 * tz);
dst._tr[14] = -(inv20 * tx + inv21 * ty + inv22 * tz);
dst._tr[15] = 1.0;
return dst;
}
/**
* オブジェクトのコピーを生成する
*/
public clone(): CubismMatrix44 {
const cloneMatrix: CubismMatrix44 = new CubismMatrix44();
for (let i = 0; i < this._tr.length; i++) {
cloneMatrix._tr[i] = this._tr[i];
}
return cloneMatrix;
}
protected _tr: Float32Array; // 4x4行列データ
}
// Namespace definition for compatibility.
import * as $ from './cubismmatrix44';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMatrix44 = $.CubismMatrix44;
export type CubismMatrix44 = $.CubismMatrix44;
}

View File

@@ -0,0 +1,217 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismMatrix44 } from './cubismmatrix44';
/**
* モデル座標設定用の4x4行列
*
* モデル座標設定用の4x4行列クラス
*/
export class CubismModelMatrix extends CubismMatrix44 {
/**
* コンストラクタ
*
* @param w 横幅
* @param h 縦幅
*/
constructor(w?: number, h?: number) {
super();
this._width = w !== undefined ? w : 0.0;
this._height = h !== undefined ? h : 0.0;
this.setHeight(2.0);
}
/**
* 横幅を設定
*
* @param w 横幅
*/
public setWidth(w: number): void {
const scaleX: number = w / this._width;
const scaleY: number = scaleX;
this.scale(scaleX, scaleY);
}
/**
* 縦幅を設定
* @param h 縦幅
*/
public setHeight(h: number): void {
const scaleX: number = h / this._height;
const scaleY: number = scaleX;
this.scale(scaleX, scaleY);
}
/**
* 位置を設定
*
* @param x X軸の位置
* @param y Y軸の位置
*/
public setPosition(x: number, y: number): void {
this.translate(x, y);
}
/**
* 中心位置を設定
*
* @param x X軸の中心位置
* @param y Y軸の中心位置
*
* @note widthかheightを設定したあとでないと、拡大率が正しく取得できないためずれる。
*/
public setCenterPosition(x: number, y: number) {
this.centerX(x);
this.centerY(y);
}
/**
* 上辺の位置を設定する
*
* @param y 上辺のY軸位置
*/
public top(y: number): void {
this.setY(y);
}
/**
* 下辺の位置を設定する
*
* @param y 下辺のY軸位置
*/
public bottom(y: number) {
const h: number = this._height * this.getScaleY();
this.translateY(y - h);
}
/**
* 左辺の位置を設定
*
* @param x 左辺のX軸位置
*/
public left(x: number): void {
this.setX(x);
}
/**
* 右辺の位置を設定
*
* @param x 右辺のX軸位置
*/
public right(x: number): void {
const w = this._width * this.getScaleX();
this.translateX(x - w);
}
/**
* X軸の中心位置を設定
*
* @param x X軸の中心位置
*/
public centerX(x: number): void {
const w = this._width * this.getScaleX();
this.translateX(x - w / 2.0);
}
/**
* X軸の位置を設定
*
* @param x X軸の位置
*/
public setX(x: number): void {
this.translateX(x);
}
/**
* Y軸の中心位置を設定
*
* @param y Y軸の中心位置
*/
public centerY(y: number): void {
const h: number = this._height * this.getScaleY();
this.translateY(y - h / 2.0);
}
/**
* Y軸の位置を設定する
*
* @param y Y軸の位置
*/
public setY(y: number): void {
this.translateY(y);
}
/**
* レイアウト情報から位置を設定
*
* @param layout レイアウト情報
*/
public setupFromLayout(layout: Map<string, number>): void {
const keyWidth = 'width';
const keyHeight = 'height';
const keyX = 'x';
const keyY = 'y';
const keyCenterX = 'center_x';
const keyCenterY = 'center_y';
const keyTop = 'top';
const keyBottom = 'bottom';
const keyLeft = 'left';
const keyRight = 'right';
for (const item of layout) {
const key: string = item[0];
const value: number = item[1];
if (key == keyWidth) {
this.setWidth(value);
} else if (key == keyHeight) {
this.setHeight(value);
}
}
for (const item of layout) {
const key: string = item[0];
const value: number = item[1];
if (key == keyX) {
this.setX(value);
} else if (key == keyY) {
this.setY(value);
} else if (key == keyCenterX) {
this.centerX(value);
} else if (key == keyCenterY) {
this.centerY(value);
} else if (key == keyTop) {
this.top(value);
} else if (key == keyBottom) {
this.bottom(value);
} else if (key == keyLeft) {
this.left(value);
} else if (key == keyRight) {
this.right(value);
}
}
}
private _width: number; // 横幅
private _height: number; // 縦幅
}
// Namespace definition for compatibility.
import * as $ from './cubismmodelmatrix';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismModelMatrix = $.CubismModelMatrix;
export type CubismModelMatrix = $.CubismModelMatrix;
}

View File

@@ -0,0 +1,169 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismMath } from './cubismmath';
const FrameRate = 30;
const Epsilon = 0.01;
/**
* 顔の向きの制御機能
*
* 顔の向きの制御機能を提供するクラス。
*/
export class CubismTargetPoint {
/**
* コンストラクタ
*/
public constructor() {
this._faceTargetX = 0.0;
this._faceTargetY = 0.0;
this._faceX = 0.0;
this._faceY = 0.0;
this._faceVX = 0.0;
this._faceVY = 0.0;
this._lastTimeSeconds = 0.0;
this._userTimeSeconds = 0.0;
}
/**
* 更新処理
*/
public update(deltaTimeSeconds: number): void {
// デルタ時間を加算する
this._userTimeSeconds += deltaTimeSeconds;
// 首を中央から左右に振るときの平均的な速さは 秒速度。加速・減速を考慮して、その2倍を最高速度とする
// 顔の振り具合を、中央0.0)から、左右は(+-1.0)とする
const faceParamMaxV: number = 40.0 / 10.0; // 7.5秒間に40分移動(5.3/sc)
const maxV: number = (faceParamMaxV * 1.0) / FrameRate; // 1frameあたりに変化できる速度の上限
if (this._lastTimeSeconds == 0.0) {
this._lastTimeSeconds = this._userTimeSeconds;
return;
}
const deltaTimeWeight: number =
(this._userTimeSeconds - this._lastTimeSeconds) * FrameRate;
this._lastTimeSeconds = this._userTimeSeconds;
// 最高速度になるまでの時間を
const timeToMaxSpeed = 0.15;
const frameToMaxSpeed: number = timeToMaxSpeed * FrameRate; // sec * frame/sec
const maxA: number = (deltaTimeWeight * maxV) / frameToMaxSpeed; // 1frameあたりの加速度
// 目指す向きは、dx, dy方向のベクトルとなる
const dx: number = this._faceTargetX - this._faceX;
const dy: number = this._faceTargetY - this._faceY;
if (CubismMath.abs(dx) <= Epsilon && CubismMath.abs(dy) <= Epsilon) {
return; // 変化なし
}
// 速度の最大よりも大きい場合は、速度を落とす
const d: number = CubismMath.sqrt(dx * dx + dy * dy);
// 進行方向の最大速度ベクトル
const vx: number = (maxV * dx) / d;
const vy: number = (maxV * dy) / d;
// 現在の速度から、新規速度への変化(加速度)を求める
let ax: number = vx - this._faceVX;
let ay: number = vy - this._faceVY;
const a: number = CubismMath.sqrt(ax * ax + ay * ay);
// 加速のとき
if (a < -maxA || a > maxA) {
ax *= maxA / a;
ay *= maxA / a;
}
// 加速度を元の速度に足して、新速度とする
this._faceVX += ax;
this._faceVY += ay;
// 目的の方向に近づいたとき、滑らかに減速するための処理
// 設定された加速度で止まる事の出来る距離と速度の関係から
// 現在とりうる最高速度を計算し、それ以上の時は速度を落とす
// ※本来、人間は筋力で力(加速度)を調整できるため、より自由度が高いが、簡単な処理で済ませている
{
// 加速度、速度、距離の関係式。
// 2 6 2 3
// sqrt(a t + 16 a h t - 8 a h) - a t
// v = --------------------------------------
// 2
// 4 t - 2
// (t=1)
// 時刻tは、あらかじめ加速度、速度を1/60(フレームレート、単位なし)で
// 考えているので、tとして消してよい※未検証
const maxV: number =
0.5 *
(CubismMath.sqrt(maxA * maxA + 16.0 * maxA * d - 8.0 * maxA * d) -
maxA);
const curV: number = CubismMath.sqrt(
this._faceVX * this._faceVX + this._faceVY * this._faceVY
);
if (curV > maxV) {
// 現在の速度 > 最高速度のとき、最高速度まで減速
this._faceVX *= maxV / curV;
this._faceVY *= maxV / curV;
}
}
this._faceX += this._faceVX;
this._faceY += this._faceVY;
}
/**
* X軸の顔の向きの値を取得
*
* @return X軸の顔の向きの値-1.0 ~ 1.0
*/
public getX(): number {
return this._faceX;
}
/**
* Y軸の顔の向きの値を取得
*
* @return Y軸の顔の向きの値-1.0 ~ 1.0
*/
public getY(): number {
return this._faceY;
}
/**
* 顔の向きの目標値を設定
*
* @param x X軸の顔の向きの値-1.0 ~ 1.0
* @param y Y軸の顔の向きの値-1.0 ~ 1.0
*/
public set(x: number, y: number): void {
this._faceTargetX = x;
this._faceTargetY = y;
}
private _faceTargetX: number; // 顔の向きのX目標値この値に近づいていく
private _faceTargetY: number; // 顔の向きのY目標値この値に近づいていく
private _faceX: number; // 顔の向きX-1.0 ~ 1.0
private _faceY: number; // 顔の向きY-1.0 ~ 1.0
private _faceVX: number; // 顔の向きの変化速度X
private _faceVY: number; // 顔の向きの変化速度Y
private _lastTimeSeconds: number; // 最後の実行時間[秒]
private _userTimeSeconds: number; // デルタ時間の積算値[秒]
}
// Namespace definition for compatibility.
import * as $ from './cubismtargetpoint';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismTargetPoint = $.CubismTargetPoint;
export type CubismTargetPoint = $.CubismTargetPoint;
}

View File

@@ -0,0 +1,172 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
/**
* 2次元ベクトル型
*
* 2次元ベクトル型の機能を提供する。
*/
export class CubismVector2 {
/**
* コンストラクタ
*/
public constructor(
public x?: number,
public y?: number
) {
this.x = x == undefined ? 0.0 : x;
this.y = y == undefined ? 0.0 : y;
}
/**
* ベクトルの加算
*
* @param vector2 加算するベクトル値
* @return 加算結果 ベクトル値
*/
public add(vector2: CubismVector2): CubismVector2 {
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
ret.x = this.x + vector2.x;
ret.y = this.y + vector2.y;
return ret;
}
/**
* ベクトルの減算
*
* @param vector2 減算するベクトル値
* @return 減算結果 ベクトル値
*/
public substract(vector2: CubismVector2): CubismVector2 {
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
ret.x = this.x - vector2.x;
ret.y = this.y - vector2.y;
return ret;
}
/**
* ベクトルの乗算
*
* @param vector2 乗算するベクトル値
* @return 乗算結果 ベクトル値
*/
public multiply(vector2: CubismVector2): CubismVector2 {
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
ret.x = this.x * vector2.x;
ret.y = this.y * vector2.y;
return ret;
}
/**
* ベクトルの乗算(スカラー)
*
* @param scalar 乗算するスカラー値
* @return 乗算結果 ベクトル値
*/
public multiplyByScaler(scalar: number): CubismVector2 {
return this.multiply(new CubismVector2(scalar, scalar));
}
/**
* ベクトルの除算
*
* @param vector2 除算するベクトル値
* @return 除算結果 ベクトル値
*/
public division(vector2: CubismVector2): CubismVector2 {
const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
ret.x = this.x / vector2.x;
ret.y = this.y / vector2.y;
return ret;
}
/**
* ベクトルの除算(スカラー)
*
* @param scalar 除算するスカラー値
* @return 除算結果 ベクトル値
*/
public divisionByScalar(scalar: number): CubismVector2 {
return this.division(new CubismVector2(scalar, scalar));
}
/**
* ベクトルの長さを取得する
*
* @return ベクトルの長さ
*/
public getLength(): number {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
/**
* ベクトルの距離の取得
*
* @param a 点
* @return ベクトルの距離
*/
public getDistanceWith(a: CubismVector2): number {
return Math.sqrt(
(this.x - a.x) * (this.x - a.x) + (this.y - a.y) * (this.y - a.y)
);
}
/**
* ドット積の計算
*
* @param a 値
* @return 結果
*/
public dot(a: CubismVector2): number {
return this.x * a.x + this.y * a.y;
}
/**
* 正規化の適用
*/
public normalize(): void {
const length: number = Math.pow(this.x * this.x + this.y * this.y, 0.5);
this.x = this.x / length;
this.y = this.y / length;
}
/**
* 等しさの確認(等しいか?)
*
* 値が等しいか?
*
* @param rhs 確認する値
* @return true 値は等しい
* @return false 値は等しくない
*/
public isEqual(rhs: CubismVector2): boolean {
return this.x == rhs.x && this.y == rhs.y;
}
/**
* 等しさの確認(等しくないか?)
*
* 値が等しくないか?
*
* @param rhs 確認する値
* @return true 値は等しくない
* @return false 値は等しい
*/
public isNotEqual(rhs: CubismVector2): boolean {
return !this.isEqual(rhs);
}
}
// Namespace definition for compatibility.
import * as $ from './cubismvector2';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismVector2 = $.CubismVector2;
export type CubismVector2 = $.CubismVector2;
}

View File

@@ -0,0 +1,339 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismMatrix44 } from './cubismmatrix44';
/**
* カメラの位置変更に使うと便利な4x4行列
*
* カメラの位置変更に使うと便利な4x4行列のクラス。
*/
export class CubismViewMatrix extends CubismMatrix44 {
/**
* コンストラクタ
*/
public constructor() {
super();
this._screenLeft = 0.0;
this._screenRight = 0.0;
this._screenTop = 0.0;
this._screenBottom = 0.0;
this._maxLeft = 0.0;
this._maxRight = 0.0;
this._maxTop = 0.0;
this._maxBottom = 0.0;
this._maxScale = 0.0;
this._minScale = 0.0;
}
/**
* 移動を調整
*
* @param x X軸の移動量
* @param y Y軸の移動量
*/
public adjustTranslate(x: number, y: number): void {
if (this._tr[0] * this._maxLeft + (this._tr[12] + x) > this._screenLeft) {
x = this._screenLeft - this._tr[0] * this._maxLeft - this._tr[12];
}
if (this._tr[0] * this._maxRight + (this._tr[12] + x) < this._screenRight) {
x = this._screenRight - this._tr[0] * this._maxRight - this._tr[12];
}
if (this._tr[5] * this._maxTop + (this._tr[13] + y) < this._screenTop) {
y = this._screenTop - this._tr[5] * this._maxTop - this._tr[13];
}
if (
this._tr[5] * this._maxBottom + (this._tr[13] + y) >
this._screenBottom
) {
y = this._screenBottom - this._tr[5] * this._maxBottom - this._tr[13];
}
const tr1: Float32Array = new Float32Array([
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
x,
y,
0.0,
1.0
]);
CubismMatrix44.multiply(tr1, this._tr, this._tr);
}
/**
* 拡大率を調整
*
* @param cx 拡大を行うX軸の中心位置
* @param cy 拡大を行うY軸の中心位置
* @param scale 拡大率
*/
public adjustScale(cx: number, cy: number, scale: number): void {
const maxScale: number = this.getMaxScale();
const minScale: number = this.getMinScale();
const targetScale = scale * this._tr[0];
if (targetScale < minScale) {
if (this._tr[0] > 0.0) {
scale = minScale / this._tr[0];
}
} else if (targetScale > maxScale) {
if (this._tr[0] > 0.0) {
scale = maxScale / this._tr[0];
}
}
const tr1: Float32Array = new Float32Array([
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
cx,
cy,
0.0,
1.0
]);
const tr2: Float32Array = new Float32Array([
scale,
0.0,
0.0,
0.0,
0.0,
scale,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0
]);
const tr3: Float32Array = new Float32Array([
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
-cx,
-cy,
0.0,
1.0
]);
CubismMatrix44.multiply(tr3, this._tr, this._tr);
CubismMatrix44.multiply(tr2, this._tr, this._tr);
CubismMatrix44.multiply(tr1, this._tr, this._tr);
}
/**
* デバイスに対応する論理座養生の範囲の設定
*
* @param left 左辺のX軸の位置
* @param right 右辺のX軸の位置
* @param bottom 下辺のY軸の位置
* @param top 上辺のY軸の位置
*/
public setScreenRect(
left: number,
right: number,
bottom: number,
top: number
): void {
this._screenLeft = left;
this._screenRight = right;
this._screenBottom = bottom;
this._screenTop = top;
}
/**
* デバイスに対応する論理座標上の移動可能範囲の設定
* @param left 左辺のX軸の位置
* @param right 右辺のX軸の位置
* @param bottom 下辺のY軸の位置
* @param top 上辺のY軸の位置
*/
public setMaxScreenRect(
left: number,
right: number,
bottom: number,
top: number
): void {
this._maxLeft = left;
this._maxRight = right;
this._maxTop = top;
this._maxBottom = bottom;
}
/**
* 最大拡大率の設定
* @param maxScale 最大拡大率
*/
public setMaxScale(maxScale: number): void {
this._maxScale = maxScale;
}
/**
* 最小拡大率の設定
* @param minScale 最小拡大率
*/
public setMinScale(minScale: number): void {
this._minScale = minScale;
}
/**
* 最大拡大率の取得
* @return 最大拡大率
*/
public getMaxScale(): number {
return this._maxScale;
}
/**
* 最小拡大率の取得
* @return 最小拡大率
*/
public getMinScale(): number {
return this._minScale;
}
/**
* 拡大率が最大になっているかを確認する
*
* @return true 拡大率は最大
* @return false 拡大率は最大ではない
*/
public isMaxScale(): boolean {
return this.getScaleX() >= this._maxScale;
}
/**
* 拡大率が最小になっているかを確認する
*
* @return true 拡大率は最小
* @return false 拡大率は最小ではない
*/
public isMinScale(): boolean {
return this.getScaleX() <= this._minScale;
}
/**
* デバイスに対応する論理座標の左辺のX軸位置を取得する
* @return デバイスに対応する論理座標の左辺のX軸位置
*/
public getScreenLeft(): number {
return this._screenLeft;
}
/**
* デバイスに対応する論理座標の右辺のX軸位置を取得する
* @return デバイスに対応する論理座標の右辺のX軸位置
*/
public getScreenRight(): number {
return this._screenRight;
}
/**
* デバイスに対応する論理座標の下辺のY軸位置を取得する
* @return デバイスに対応する論理座標の下辺のY軸位置
*/
public getScreenBottom(): number {
return this._screenBottom;
}
/**
* デバイスに対応する論理座標の上辺のY軸位置を取得する
* @return デバイスに対応する論理座標の上辺のY軸位置
*/
public getScreenTop(): number {
return this._screenTop;
}
/**
* 左辺のX軸位置の最大値の取得
* @return 左辺のX軸位置の最大値
*/
public getMaxLeft(): number {
return this._maxLeft;
}
/**
* 右辺のX軸位置の最大値の取得
* @return 右辺のX軸位置の最大値
*/
public getMaxRight(): number {
return this._maxRight;
}
/**
* 下辺のY軸位置の最大値の取得
* @return 下辺のY軸位置の最大値
*/
public getMaxBottom(): number {
return this._maxBottom;
}
/**
* 上辺のY軸位置の最大値の取得
* @return 上辺のY軸位置の最大値
*/
public getMaxTop(): number {
return this._maxTop;
}
private _screenLeft: number; // デバイスに対応する論理座標上の範囲左辺X軸位置
private _screenRight: number; // デバイスに対応する論理座標上の範囲右辺X軸位置
private _screenTop: number; // デバイスに対応する論理座標上の範囲上辺Y軸位置
private _screenBottom: number; // デバイスに対応する論理座標上の範囲下辺Y軸位置
private _maxLeft: number; // 論理座標上の移動可能範囲左辺X軸位置
private _maxRight: number; // 論理座標上の移動可能範囲右辺X軸位置
private _maxTop: number; // 論理座標上の移動可能範囲上辺Y軸位置
private _maxBottom: number; // 論理座標上の移動可能範囲下辺Y軸位置
private _maxScale: number; // 拡大率の最大値
private _minScale: number; // 拡大率の最小値
}
// Namespace definition for compatibility.
import * as $ from './cubismviewmatrix';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismViewMatrix = $.CubismViewMatrix;
export type CubismViewMatrix = $.CubismViewMatrix;
}

View File

@@ -0,0 +1,155 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CSM_ASSERT, CubismLogError } from '../utils/cubismdebug';
import { CubismModel } from './cubismmodel';
/**
* Mocデータの管理
*
* Mocデータの管理を行うクラス。
*/
export class CubismMoc {
/**
* Mocデータの作成
*/
public static create(
mocBytes: ArrayBuffer,
shouldCheckMocConsistency: boolean
): CubismMoc {
let cubismMoc: CubismMoc = null;
if (shouldCheckMocConsistency) {
// .moc3の整合性を確認
const consistency = this.hasMocConsistency(mocBytes);
if (!consistency) {
// 整合性が確認できなければ処理しない
CubismLogError(`Inconsistent MOC3.`);
return cubismMoc;
}
}
const moc: Live2DCubismCore.Moc =
Live2DCubismCore.Moc.fromArrayBuffer(mocBytes);
if (moc) {
cubismMoc = new CubismMoc(moc);
cubismMoc._mocVersion =
Live2DCubismCore.Version.csmGetMocVersion(mocBytes);
}
return cubismMoc;
}
/**
* Mocデータを削除
*
* Mocデータを削除する
*/
public static delete(moc: CubismMoc): void {
moc._moc._release();
moc._moc = null;
moc = null;
}
/**
* モデルを作成する
*
* @return Mocデータから作成されたモデル
*/
createModel(): CubismModel {
let cubismModel: CubismModel = null;
const model: Live2DCubismCore.Model = Live2DCubismCore.Model.fromMoc(
this._moc
);
if (model) {
cubismModel = new CubismModel(model);
cubismModel.initialize();
++this._modelCount;
}
return cubismModel;
}
/**
* モデルを削除する
*/
deleteModel(model: CubismModel): void {
if (model != null) {
model.release();
model = null;
--this._modelCount;
}
}
/**
* コンストラクタ
*/
private constructor(moc: Live2DCubismCore.Moc) {
this._moc = moc;
this._modelCount = 0;
this._mocVersion = 0;
}
/**
* デストラクタ相当の処理
*/
public release(): void {
CSM_ASSERT(this._modelCount == 0);
this._moc._release();
this._moc = null;
}
/**
* 最新の.moc3 Versionを取得
*/
public getLatestMocVersion(): number {
return Live2DCubismCore.Version.csmGetLatestMocVersion();
}
/**
* 読み込んだモデルの.moc3 Versionを取得
*/
public getMocVersion(): number {
return this._mocVersion;
}
/**
* Mocファイルのbufferから.moc3 Versionを取得
* @param mocBytes Mocファイルのバイト配列
* @returns .moc3 Version番号
*/
public static getMocVersionFromBuffer(mocBytes: ArrayBuffer): number {
return Live2DCubismCore.Version.csmGetMocVersion(mocBytes);
}
/**
* .moc3 の整合性を検証する
*/
public static hasMocConsistency(mocBytes: ArrayBuffer): boolean {
const isConsistent =
Live2DCubismCore.Moc.prototype.hasMocConsistency(mocBytes);
return isConsistent === 1 ? true : false;
}
_moc: Live2DCubismCore.Moc; // Mocデータ
_modelCount: number; // Mocデータから作られたモデルの個数
_mocVersion: number; // 読み込んだモデルの.moc3 Version
}
// Namespace definition for compatibility.
import * as $ from './cubismmoc';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMoc = $.CubismMoc;
export type CubismMoc = $.CubismMoc;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { CubismModelUserDataJson } from './cubismmodeluserdatajson';
const ArtMesh = 'ArtMesh';
/**
* ユーザーデータインターフェース
*
* Jsonから読み込んだユーザーデータを記録しておくための構造体
*/
export class CubismModelUserDataNode {
targetType: CubismIdHandle; // ユーザーデータターゲットタイプ
targetId: CubismIdHandle; // ユーザーデータターゲットのID
value: string; // ユーザーデータ
}
/**
* ユーザデータの管理クラス
*
* ユーザデータをロード、管理、検索インターフェイス、解放までを行う。
*/
export class CubismModelUserData {
/**
* インスタンスの作成
*
* @param buffer userdata3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
* @return 作成されたインスタンス
*/
public static create(buffer: ArrayBuffer, size: number): CubismModelUserData {
const ret: CubismModelUserData = new CubismModelUserData();
ret.parseUserData(buffer, size);
return ret;
}
/**
* インスタンスを破棄する
*
* @param modelUserData 破棄するインスタンス
*/
public static delete(modelUserData: CubismModelUserData): void {
if (modelUserData != null) {
modelUserData.release();
modelUserData = null;
}
}
/**
* ArtMeshのユーザーデータのリストの取得
*
* @return ユーザーデータリスト
*/
public getArtMeshUserDatas(): Array<CubismModelUserDataNode> {
return this._artMeshUserDataNode;
}
/**
* userdata3.jsonのパース
*
* @param buffer userdata3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public parseUserData(buffer: ArrayBuffer, size: number): void {
let json: CubismModelUserDataJson = new CubismModelUserDataJson(
buffer,
size
);
if (!json) {
json.release();
json = void 0;
return;
}
const typeOfArtMesh = CubismFramework.getIdManager().getId(ArtMesh);
const nodeCount: number = json.getUserDataCount();
let dstIndex = this._userDataNodes.length;
this._userDataNodes.length = nodeCount;
for (let i = 0; i < nodeCount; i++) {
const addNode: CubismModelUserDataNode = new CubismModelUserDataNode();
addNode.targetId = json.getUserDataId(i);
addNode.targetType = CubismFramework.getIdManager().getId(
json.getUserDataTargetType(i)
);
addNode.value = json.getUserDataValue(i);
this._userDataNodes[dstIndex++] = addNode;
if (addNode.targetType == typeOfArtMesh) {
this._artMeshUserDataNode.push(addNode);
}
}
json.release();
json = void 0;
}
/**
* コンストラクタ
*/
public constructor() {
this._userDataNodes = new Array<CubismModelUserDataNode>();
this._artMeshUserDataNode = new Array<CubismModelUserDataNode>();
}
/**
* デストラクタ相当の処理
*
* ユーザーデータ構造体配列を解放する
*/
public release(): void {
for (let i = 0; i < this._userDataNodes.length; ++i) {
this._userDataNodes[i] = null;
}
this._userDataNodes = null;
}
private _userDataNodes: Array<CubismModelUserDataNode>; // ユーザーデータ構造体配列
private _artMeshUserDataNode: Array<CubismModelUserDataNode>; // 閲覧リストの保持
}
// Namespace definition for compatibility.
import * as $ from './cubismmodeluserdata';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismModelUserData = $.CubismModelUserData;
export type CubismModelUserData = $.CubismModelUserData;
export const CubismModelUserDataNode = $.CubismModelUserDataNode;
export type CubismModelUserDataNode = $.CubismModelUserDataNode;
}

View File

@@ -0,0 +1,117 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { CubismJson } from '../utils/cubismjson';
const Meta = 'Meta';
const UserDataCount = 'UserDataCount';
const TotalUserDataSize = 'TotalUserDataSize';
const UserData = 'UserData';
const Target = 'Target';
const Id = 'Id';
const Value = 'Value';
export class CubismModelUserDataJson {
/**
* コンストラクタ
* @param buffer userdata3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public constructor(buffer: ArrayBuffer, size: number) {
this._json = CubismJson.create(buffer, size);
}
/**
* デストラクタ相当の処理
*/
public release(): void {
CubismJson.delete(this._json);
}
/**
* ユーザーデータ個数の取得
* @return ユーザーデータの個数
*/
public getUserDataCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(UserDataCount)
.toInt();
}
/**
* ユーザーデータ総文字列数の取得
*
* @return ユーザーデータ総文字列数
*/
public getTotalUserDataSize(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalUserDataSize)
.toInt();
}
/**
* ユーザーデータのタイプの取得
*
* @return ユーザーデータのタイプ
*/
public getUserDataTargetType(i: number): string {
return this._json
.getRoot()
.getValueByString(UserData)
.getValueByIndex(i)
.getValueByString(Target)
.getRawString();
}
/**
* ユーザーデータのターゲットIDの取得
*
* @param i インデックス
* @return ユーザーデータターゲットID
*/
public getUserDataId(i: number): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._json
.getRoot()
.getValueByString(UserData)
.getValueByIndex(i)
.getValueByString(Id)
.getRawString()
);
}
/**
* ユーザーデータの文字列の取得
*
* @param i インデックス
* @return ユーザーデータ
*/
public getUserDataValue(i: number): string {
return this._json
.getRoot()
.getValueByString(UserData)
.getValueByIndex(i)
.getValueByString(Value)
.getRawString();
}
private _json: CubismJson;
}
// Namespace definition for compatibility.
import * as $ from './cubismmodeluserdatajson';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismModelUserDataJson = $.CubismModelUserDataJson;
export type CubismModelUserDataJson = $.CubismModelUserDataJson;
}

View File

@@ -0,0 +1,521 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismBreath } from '../effect/cubismbreath';
import { CubismEyeBlink } from '../effect/cubismeyeblink';
import { CubismPose } from '../effect/cubismpose';
import { ICubismModelSetting } from '../icubismmodelsetting';
import { CubismIdHandle } from '../id/cubismid';
import { Constant } from '../live2dcubismframework';
import { CubismModelMatrix } from '../math/cubismmodelmatrix';
import { CubismTargetPoint } from '../math/cubismtargetpoint';
import {
ACubismMotion,
BeganMotionCallback,
FinishedMotionCallback
} from '../motion/acubismmotion';
import { CubismExpressionMotion } from '../motion/cubismexpressionmotion';
import { CubismExpressionMotionManager } from '../motion/cubismexpressionmotionmanager';
import { CubismMotion } from '../motion/cubismmotion';
import { CubismMotionManager } from '../motion/cubismmotionmanager';
import { CubismMotionQueueManager } from '../motion/cubismmotionqueuemanager';
import { CubismPhysics } from '../physics/cubismphysics';
import { CubismRenderer_WebGL } from '../rendering/cubismrenderer_webgl';
import { CubismLogError, CubismLogInfo } from '../utils/cubismdebug';
import { CubismMoc } from './cubismmoc';
import { CubismModel } from './cubismmodel';
import { CubismModelUserData } from './cubismmodeluserdata';
/**
* ユーザーが実際に使用するモデル
*
* ユーザーが実際に使用するモデルの基底クラス。これを継承してユーザーが実装する。
*/
export class CubismUserModel {
/**
* 初期化状態の取得
*
* 初期化されている状態か?
*
* @return true 初期化されている
* @return false 初期化されていない
*/
public isInitialized(): boolean {
return this._initialized;
}
/**
* 初期化状態の設定
*
* 初期化状態を設定する。
*
* @param v 初期化状態
*/
public setInitialized(v: boolean): void {
this._initialized = v;
}
/**
* 更新状態の取得
*
* 更新されている状態か?
*
* @return true 更新されている
* @return false 更新されていない
*/
public isUpdating(): boolean {
return this._updating;
}
/**
* 更新状態の設定
*
* 更新状態を設定する
*
* @param v 更新状態
*/
public setUpdating(v: boolean): void {
this._updating = v;
}
/**
* マウスドラッグ情報の設定
*
* @param ドラッグしているカーソルのX位置
* @param ドラッグしているカーソルのY位置
*/
public setDragging(x: number, y: number): void {
this._dragManager.set(x, y);
}
/**
* モデル行列を取得する
* @return モデル行列
*/
public getModelMatrix(): CubismModelMatrix {
return this._modelMatrix;
}
/**
* モデルを描画したバッファを設定する
*
* @param width モデルを描画したバッファの幅
* @param height モデルを描画したバッファの高さ
*/
public setRenderTargetSize(width: number, height: number): void {
if (this._renderer) {
this._renderer.setRenderTargetSize(width, height);
}
}
/**
* 不透明度の設定
*
* @param a 不透明度
*/
public setOpacity(a: number): void {
this._opacity = a;
}
/**
* 不透明度の取得
*
* @return 不透明度
*/
public getOpacity(): number {
return this._opacity;
}
/**
* モデルデータを読み込む
*
* @param buffer moc3ファイルが読み込まれているバッファ
*/
public loadModel(buffer: ArrayBuffer, shouldCheckMocConsistency = false) {
this._moc = CubismMoc.create(buffer, shouldCheckMocConsistency);
if (this._moc == null) {
CubismLogError('Failed to CubismMoc.create().');
return;
}
this._model = this._moc.createModel();
if (this._model == null) {
CubismLogError('Failed to CreateModel().');
return;
}
this._model.saveParameters();
this._modelMatrix = new CubismModelMatrix(
this._model.getCanvasWidth(),
this._model.getCanvasHeight()
);
}
/**
* モーションデータを読み込む
* @param buffer motion3.jsonファイルが読み込まれているバッファ
* @param size バッファのサイズ
* @param name モーションの名前
* @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数
* @param onBeganMotionHandler モーション再生開始時に呼び出されるコールバック関数
* @param modelSetting モデル設定
* @param group モーショングループ名
* @param index モーションインデックス
* @param shouldCheckMotionConsistency motion3.json整合性チェックするかどうか
* @return モーションクラス
*/
public loadMotion(
buffer: ArrayBuffer,
size: number,
name: string,
onFinishedMotionHandler?: FinishedMotionCallback,
onBeganMotionHandler?: BeganMotionCallback,
modelSetting?: ICubismModelSetting,
group?: string,
index?: number,
shouldCheckMotionConsistency: boolean = false
): CubismMotion {
if (buffer == null || size == 0) {
CubismLogError('Failed to loadMotion().');
return null;
}
const motion: CubismMotion = CubismMotion.create(
buffer,
size,
onFinishedMotionHandler,
onBeganMotionHandler,
shouldCheckMotionConsistency
);
if (motion == null) {
CubismLogError(`Failed to create motion from buffer in LoadMotion()`);
return null;
}
// 必要であればモーションフェード値を上書き
if (modelSetting) {
const fadeInTime: number = modelSetting.getMotionFadeInTimeValue(
group,
index
);
if (fadeInTime >= 0.0) {
motion.setFadeInTime(fadeInTime);
}
const fadeOutTime = modelSetting.getMotionFadeOutTimeValue(group, index);
if (fadeOutTime >= 0.0) {
motion.setFadeOutTime(fadeOutTime);
}
}
return motion;
}
/**
* 表情データの読み込み
* @param buffer expファイルが読み込まれているバッファ
* @param size バッファのサイズ
* @param name 表情の名前
*/
public loadExpression(
buffer: ArrayBuffer,
size: number,
name: string
): ACubismMotion {
if (buffer == null || size == 0) {
CubismLogError('Failed to loadExpression().');
return null;
}
return CubismExpressionMotion.create(buffer, size);
}
/**
* ポーズデータの読み込み
* @param buffer pose3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public loadPose(buffer: ArrayBuffer, size: number): void {
if (buffer == null || size == 0) {
CubismLogError('Failed to loadPose().');
return;
}
this._pose = CubismPose.create(buffer, size);
}
/**
* モデルに付属するユーザーデータを読み込む
* @param buffer userdata3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public loadUserData(buffer: ArrayBuffer, size: number): void {
if (buffer == null || size == 0) {
CubismLogError('Failed to loadUserData().');
return;
}
this._modelUserData = CubismModelUserData.create(buffer, size);
}
/**
* 物理演算データの読み込み
* @param buffer physics3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public loadPhysics(buffer: ArrayBuffer, size: number): void {
if (buffer == null || size == 0) {
CubismLogError('Failed to loadPhysics().');
return;
}
this._physics = CubismPhysics.create(buffer, size);
}
/**
* 当たり判定の取得
* @param drawableId 検証したいDrawableのID
* @param pointX X位置
* @param pointY Y位置
* @return true ヒットしている
* @return false ヒットしていない
*/
public isHit(
drawableId: CubismIdHandle,
pointX: number,
pointY: number
): boolean {
const drawIndex: number = this._model.getDrawableIndex(drawableId);
if (drawIndex < 0) {
return false; // 存在しない場合はfalse
}
const count: number = this._model.getDrawableVertexCount(drawIndex);
const vertices: Float32Array = this._model.getDrawableVertices(drawIndex);
let left: number = vertices[0];
let right: number = vertices[0];
let top: number = vertices[1];
let bottom: number = vertices[1];
for (let j = 1; j < count; ++j) {
const x = vertices[Constant.vertexOffset + j * Constant.vertexStep];
const y = vertices[Constant.vertexOffset + j * Constant.vertexStep + 1];
if (x < left) {
left = x; // Min x
}
if (x > right) {
right = x; // Max x
}
if (y < top) {
top = y; // Min y
}
if (y > bottom) {
bottom = y; // Max y
}
}
const tx: number = this._modelMatrix.invertTransformX(pointX);
const ty: number = this._modelMatrix.invertTransformY(pointY);
return left <= tx && tx <= right && top <= ty && ty <= bottom;
}
/**
* モデルの取得
* @return モデル
*/
public getModel(): CubismModel {
return this._model;
}
/**
* 読み込めないMocファイルの.moc3 Versionを取得
* @param mocBytes 読み込めないMocファイルのバイト配列
* @returns .moc3 Version番号
*/
public getMocVersionFromBuffer(mocBytes: ArrayBuffer): number {
return CubismMoc.getMocVersionFromBuffer(mocBytes);
}
/**
* レンダラの取得
* @return レンダラ
*/
public getRenderer(): CubismRenderer_WebGL {
return this._renderer;
}
/**
* レンダラを作成して初期化を実行する
* @param width レンダリングする幅
* @param height レンダリングする高さ
* @param maskBufferCount バッファの生成数
*/
public createRenderer(
width: number,
height: number,
maskBufferCount = 1
): void {
if (this._renderer) {
this.deleteRenderer();
}
this._renderer = new CubismRenderer_WebGL(width, height);
this._renderer.initialize(this._model, maskBufferCount);
}
/**
* レンダラの解放
*/
public deleteRenderer(): void {
if (this._renderer != null) {
this._renderer.release();
this._renderer = null;
}
}
/**
* イベント発火時の標準処理
*
* Eventが再生処理時にあった場合の処理をする。
* 継承で上書きすることを想定している。
* 上書きしない場合はログ出力をする。
*
* @param eventValue 発火したイベントの文字列データ
*/
public motionEventFired(eventValue: string): void {
CubismLogInfo('{0}', eventValue);
}
/**
* イベント用のコールバック
*
* CubismMotionQueueManagerにイベント用に登録するためのCallback。
* CubismUserModelの継承先のEventFiredを呼ぶ。
*
* @param caller 発火したイベントを管理していたモーションマネージャー、比較用
* @param eventValue 発火したイベントの文字列データ
* @param customData CubismUserModelを継承したインスタンスを想定
*/
public static cubismDefaultMotionEventCallback(
caller: CubismMotionQueueManager,
eventValue: string,
customData: CubismUserModel
): void {
const model: CubismUserModel = customData;
if (model != null) {
model.motionEventFired(eventValue);
}
}
/**
* コンストラクタ
*/
public constructor() {
// 各変数初期化
this._moc = null;
this._model = null;
this._motionManager = null;
this._expressionManager = null;
this._eyeBlink = null;
this._breath = null;
this._modelMatrix = null;
this._pose = null;
this._dragManager = null;
this._physics = null;
this._modelUserData = null;
this._initialized = false;
this._updating = false;
this._opacity = 1.0;
this._mocConsistency = false;
this._debugMode = false;
this._renderer = null;
// モーションマネージャーを作成
this._motionManager = new CubismMotionManager();
this._motionManager.setEventCallback(
CubismUserModel.cubismDefaultMotionEventCallback,
this
);
// 表情マネージャーを作成
this._expressionManager = new CubismExpressionMotionManager();
// ドラッグによるアニメーション
this._dragManager = new CubismTargetPoint();
}
/**
* デストラクタに相当する処理
*/
public release() {
if (this._motionManager != null) {
this._motionManager.release();
this._motionManager = null;
}
if (this._expressionManager != null) {
this._expressionManager.release();
this._expressionManager = null;
}
if (this._moc != null) {
this._moc.deleteModel(this._model);
this._moc.release();
this._moc = null;
}
this._modelMatrix = null;
CubismPose.delete(this._pose);
CubismEyeBlink.delete(this._eyeBlink);
CubismBreath.delete(this._breath);
this._dragManager = null;
CubismPhysics.delete(this._physics);
CubismModelUserData.delete(this._modelUserData);
this.deleteRenderer();
}
protected _moc: CubismMoc; // Mocデータ
protected _model: CubismModel; // Modelインスタンス
protected _motionManager: CubismMotionManager; // モーション管理
protected _expressionManager: CubismExpressionMotionManager; // 表情管理
protected _eyeBlink: CubismEyeBlink; // 自動まばたき
protected _breath: CubismBreath; // 呼吸
protected _modelMatrix: CubismModelMatrix; // モデル行列
protected _pose: CubismPose; // ポーズ管理
protected _dragManager: CubismTargetPoint; // マウスドラッグ
protected _physics: CubismPhysics; // 物理演算
protected _modelUserData: CubismModelUserData; // ユーザーデータ
protected _initialized: boolean; // 初期化されたかどうか
protected _updating: boolean; // 更新されたかどうか
protected _opacity: number; // 不透明度
protected _mocConsistency: boolean; // MOC3整合性検証するかどうか
protected _motionConsistency: boolean; // motion3.json整合性検証するかどうか
protected _debugMode: boolean; // デバッグモードかどうか
private _renderer: CubismRenderer_WebGL; // レンダラ
}
// Namespace definition for compatibility.
import * as $ from './cubismusermodel';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismUserModel = $.CubismUserModel;
export type CubismUserModel = $.CubismUserModel;
}

View File

@@ -0,0 +1,443 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismMath } from '../math/cubismmath';
import { CubismModel } from '../model/cubismmodel';
import { CSM_ASSERT, CubismDebug } from '../utils/cubismdebug';
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
/** モーション再生開始コールバック関数定義 */
export type BeganMotionCallback = (self: ACubismMotion) => void;
/** モーション再生終了コールバック関数定義 */
export type FinishedMotionCallback = (self: ACubismMotion) => void;
/**
* モーションの抽象基底クラス
*
* モーションの抽象基底クラス。MotionQueueManagerによってモーションの再生を管理する。
*/
export abstract class ACubismMotion {
/**
* インスタンスの破棄
*/
public static delete(motion: ACubismMotion): void {
motion.release();
motion = null;
}
/**
* コンストラクタ
*/
public constructor() {
this._fadeInSeconds = -1.0;
this._fadeOutSeconds = -1.0;
this._weight = 1.0;
this._offsetSeconds = 0.0; // 再生の開始時刻
this._isLoop = false; // ループするか
this._isLoopFadeIn = true; // ループ時にフェードインが有効かどうかのフラグ。初期値では有効。
this._previousLoopState = this._isLoop;
this._firedEventValues = new Array<string>();
}
/**
* デストラクタ相当の処理
*/
public release(): void {
this._weight = 0.0;
}
/**
* モデルのパラメータ
* @param model 対象のモデル
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param userTimeSeconds デルタ時間の積算値[秒]
*/
public updateParameters(
model: CubismModel,
motionQueueEntry: CubismMotionQueueEntry,
userTimeSeconds: number
): void {
if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) {
return;
}
this.setupMotionQueueEntry(motionQueueEntry, userTimeSeconds);
const fadeWeight = this.updateFadeWeight(motionQueueEntry, userTimeSeconds);
//---- 全てのパラメータIDをループする ----
this.doUpdateParameters(
model,
userTimeSeconds,
fadeWeight,
motionQueueEntry
);
// 後処理
// 終了時刻を過ぎたら終了フラグを立てる(CubismMotionQueueManager)
if (
motionQueueEntry.getEndTime() > 0 &&
motionQueueEntry.getEndTime() < userTimeSeconds
) {
motionQueueEntry.setIsFinished(true); // 終了
}
}
/**
* @brief モデルの再生開始処理
*
* モーションの再生を開始するためのセットアップを行う。
*
* @param[in] motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param[in] userTimeSeconds デルタ時間の積算値[秒]
*/
public setupMotionQueueEntry(
motionQueueEntry: CubismMotionQueueEntry,
userTimeSeconds: number
) {
if (motionQueueEntry == null || motionQueueEntry.isStarted()) {
return;
}
if (!motionQueueEntry.isAvailable()) {
return;
}
motionQueueEntry.setIsStarted(true);
motionQueueEntry.setStartTime(userTimeSeconds - this._offsetSeconds); // モーションの開始時刻を記録
motionQueueEntry.setFadeInStartTime(userTimeSeconds); // フェードインの開始時刻
if (motionQueueEntry.getEndTime() < 0.0) {
// 開始していないうちに終了設定している場合がある
this.adjustEndTime(motionQueueEntry);
}
// 再生開始コールバック
if (motionQueueEntry._motion._onBeganMotion) {
motionQueueEntry._motion._onBeganMotion(motionQueueEntry._motion);
}
}
/**
* @brief モデルのウェイト更新
*
* モーションのウェイトを更新する。
*
* @param[in] motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param[in] userTimeSeconds デルタ時間の積算値[秒]
*/
public updateFadeWeight(
motionQueueEntry: CubismMotionQueueEntry,
userTimeSeconds: number
): number {
if (motionQueueEntry == null) {
CubismDebug.print(LogLevel.LogLevel_Error, 'motionQueueEntry is null.');
}
let fadeWeight: number = this._weight; // 現在の値と掛け合わせる割合
//---- フェードイン・アウトの処理 ----
// 単純なサイン関数でイージングする
const fadeIn: number =
this._fadeInSeconds == 0.0
? 1.0
: CubismMath.getEasingSine(
(userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
this._fadeInSeconds
);
const fadeOut: number =
this._fadeOutSeconds == 0.0 || motionQueueEntry.getEndTime() < 0.0
? 1.0
: CubismMath.getEasingSine(
(motionQueueEntry.getEndTime() - userTimeSeconds) /
this._fadeOutSeconds
);
fadeWeight = fadeWeight * fadeIn * fadeOut;
motionQueueEntry.setState(userTimeSeconds, fadeWeight);
CSM_ASSERT(0.0 <= fadeWeight && fadeWeight <= 1.0);
return fadeWeight;
}
/**
* フェードインの時間を設定する
* @param fadeInSeconds フェードインにかかる時間[秒]
*/
public setFadeInTime(fadeInSeconds: number): void {
this._fadeInSeconds = fadeInSeconds;
}
/**
* フェードアウトの時間を設定する
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
*/
public setFadeOutTime(fadeOutSeconds: number): void {
this._fadeOutSeconds = fadeOutSeconds;
}
/**
* フェードアウトにかかる時間の取得
* @return フェードアウトにかかる時間[秒]
*/
public getFadeOutTime(): number {
return this._fadeOutSeconds;
}
/**
* フェードインにかかる時間の取得
* @return フェードインにかかる時間[秒]
*/
public getFadeInTime(): number {
return this._fadeInSeconds;
}
/**
* モーション適用の重みの設定
* @param weight 重み0.0 - 1.0
*/
public setWeight(weight: number): void {
this._weight = weight;
}
/**
* モーション適用の重みの取得
* @return 重み0.0 - 1.0
*/
public getWeight(): number {
return this._weight;
}
/**
* モーションの長さの取得
* @return モーションの長さ[秒]
*
* @note ループの時は「-1」。
* ループでない場合は、オーバーライドする。
* 正の値の時は取得される時間で終了する。
* 「-1」の時は外部から停止命令がない限り終わらない処理となる。
*/
public getDuration(): number {
return -1.0;
}
/**
* モーションのループ1回分の長さの取得
* @return モーションのループ一回分の長さ[秒]
*
* @note ループしない場合は、getDuration()と同じ値を返す
* ループ一回分の長さが定義できない場合(プログラム的に動き続けるサブクラスなど)の場合は「-1」を返す
*/
public getLoopDuration(): number {
return -1.0;
}
/**
* モーション再生の開始時刻の設定
* @param offsetSeconds モーション再生の開始時刻[秒]
*/
public setOffsetTime(offsetSeconds: number): void {
this._offsetSeconds = offsetSeconds;
}
/**
* ループ情報の設定
* @param loop ループ情報
*/
public setLoop(loop: boolean): void {
this._isLoop = loop;
}
/**
* ループ情報の取得
* @return true ループする
* @return false ループしない
*/
public getLoop(): boolean {
return this._isLoop;
}
/**
* ループ時のフェードイン情報の設定
* @param loopFadeIn ループ時のフェードイン情報
*/
public setLoopFadeIn(loopFadeIn: boolean) {
this._isLoopFadeIn = loopFadeIn;
}
/**
* ループ時のフェードイン情報の取得
*
* @return true する
* @return false しない
*/
public getLoopFadeIn(): boolean {
return this._isLoopFadeIn;
}
/**
* モデルのパラメータ更新
*
* イベント発火のチェック。
* 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。
*
* @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒]
* @param motionTimeSeconds 今回の再生時間[秒]
*/
public getFiredEvent(
beforeCheckTimeSeconds: number,
motionTimeSeconds: number
): Array<string> {
return this._firedEventValues;
}
/**
* モーションを更新して、モデルにパラメータ値を反映する
* @param model 対象のモデル
* @param userTimeSeconds デルタ時間の積算値[秒]
* @param weight モーションの重み
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @return true モデルへパラメータ値の反映あり
* @return false モデルへのパラメータ値の反映なし(モーションの変化なし)
*/
public abstract doUpdateParameters(
model: CubismModel,
userTimeSeconds: number,
weight: number,
motionQueueEntry: CubismMotionQueueEntry
): void;
/**
* モーション再生開始コールバックの登録
*
* モーション再生開始コールバックを登録する。
* 以下の状態の際には呼び出されない:
* 1. 再生中のモーションが「ループ」として設定されているとき
* 2. コールバックが登録されていない時
*
* @param onBeganMotionHandler モーション再生開始コールバック関数
*/
public setBeganMotionHandler = (onBeganMotionHandler: BeganMotionCallback) =>
(this._onBeganMotion = onBeganMotionHandler);
/**
* モーション再生開始コールバックの取得
*
* モーション再生開始コールバックを取得する。
*
* @return 登録されているモーション再生開始コールバック関数
*/
public getBeganMotionHandler = () => this._onBeganMotion;
/**
* モーション再生終了コールバックの登録
*
* モーション再生終了コールバックを登録する。
* isFinishedフラグを設定するタイミングで呼び出される。
* 以下の状態の際には呼び出されない:
* 1. 再生中のモーションが「ループ」として設定されているとき
* 2. コールバックが登録されていない時
*
* @param onFinishedMotionHandler モーション再生終了コールバック関数
*/
public setFinishedMotionHandler = (
onFinishedMotionHandler: FinishedMotionCallback
) => (this._onFinishedMotion = onFinishedMotionHandler);
/**
* モーション再生終了コールバックの取得
*
* モーション再生終了コールバックを取得する。
*
* @return 登録されているモーション再生終了コールバック関数
*/
public getFinishedMotionHandler = () => this._onFinishedMotion;
/**
* 透明度のカーブが存在するかどうかを確認する
*
* @return true -> キーが存在する
* false -> キーが存在しない
*/
public isExistModelOpacity(): boolean {
return false;
}
/**
* 透明度のカーブのインデックスを返す
*
* @return success:透明度のカーブのインデックス
*/
public getModelOpacityIndex(): number {
return -1;
}
/**
* 透明度のIdを返す
*
* @param index モーションカーブのインデックス
* @return success:透明度のId
*/
public getModelOpacityId(index: number): CubismIdHandle {
return null;
}
/**
* 指定時間の透明度の値を返す
*
* @return success:モーションの現在時間におけるOpacityの値
*
* @note 更新後の値を取るにはUpdateParameters() の後に呼び出す。
*/
protected getModelOpacityValue(): number {
return 1.0;
}
/**
* 終了時刻の調整
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
*/
protected adjustEndTime(motionQueueEntry: CubismMotionQueueEntry) {
const duration = this.getDuration();
// duration == -1 の場合はループする
const endTime =
duration <= 0.0 ? -1 : motionQueueEntry.getStartTime() + duration;
motionQueueEntry.setEndTime(endTime);
}
public _fadeInSeconds: number; // フェードインにかかる時間[秒]
public _fadeOutSeconds: number; // フェードアウトにかかる時間[秒]
public _weight: number; // モーションの重み
public _offsetSeconds: number; // モーション再生の開始時間[秒]
public _isLoop: boolean; // ループが有効かのフラグ
public _isLoopFadeIn: boolean; // ループ時にフェードインが有効かどうかのフラグ
public _previousLoopState: boolean; // 前回の `_isLoop` の状態
public _firedEventValues: Array<string>;
// モーション再生開始コールバック関数
public _onBeganMotion?: BeganMotionCallback;
// モーション再生終了コールバック関数
public _onFinishedMotion?: FinishedMotionCallback;
}
// Namespace definition for compatibility.
import * as $ from './acubismmotion';
import { CubismIdHandle } from '../id/cubismid';
import { LogLevel } from '../live2dcubismframework';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const ACubismMotion = $.ACubismMotion;
export type ACubismMotion = $.ACubismMotion;
export type BeganMotionCallback = $.BeganMotionCallback;
export type FinishedMotionCallback = $.FinishedMotionCallback;
}

View File

@@ -0,0 +1,60 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
import { CubismBreath } from '../effect/cubismbreath';
/**
* Updater for breath effects.
* Handles the management of breath animation through the CubismBreath class.
*/
export class CubismBreathUpdater extends ICubismUpdater {
private _breath: CubismBreath;
/**
* Constructor
*
* @param breath CubismBreath reference
*/
constructor(breath: CubismBreath);
/**
* Constructor
*
* @param breath CubismBreath reference
* @param executionOrder Order of operations
*/
constructor(breath: CubismBreath, executionOrder: number);
constructor(breath: CubismBreath, executionOrder?: number) {
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Breath);
this._breath = breath;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
this._breath.updateParameters(model, deltaTimeSeconds);
}
}
// Namespace definition for compatibility.
import * as $ from './cubismbreathupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismBreathUpdater = $.CubismBreathUpdater;
export type CubismBreathUpdater = $.CubismBreathUpdater;
}

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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { CubismModel } from '../model/cubismmodel';
import { CubismJson, Value } from '../utils/cubismjson';
import { ACubismMotion } from './acubismmotion';
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
// exp3.jsonのキーとデフォルト
const ExpressionKeyFadeIn = 'FadeInTime';
const ExpressionKeyFadeOut = 'FadeOutTime';
const ExpressionKeyParameters = 'Parameters';
const ExpressionKeyId = 'Id';
const ExpressionKeyValue = 'Value';
const ExpressionKeyBlend = 'Blend';
const BlendValueAdd = 'Add';
const BlendValueMultiply = 'Multiply';
const BlendValueOverwrite = 'Overwrite';
const DefaultFadeTime = 1.0;
/**
* 表情のモーション
*
* 表情のモーションクラス。
*/
export class CubismExpressionMotion extends ACubismMotion {
static readonly DefaultAdditiveValue = 0.0; // 加算適用の初期値
static readonly DefaultMultiplyValue = 1.0; // 乗算適用の初期値
/**
* インスタンスを作成する。
* @param buffer expファイルが読み込まれているバッファ
* @param size バッファのサイズ
* @return 作成されたインスタンス
*/
public static create(
buffer: ArrayBuffer,
size: number
): CubismExpressionMotion {
const expression: CubismExpressionMotion = new CubismExpressionMotion();
expression.parse(buffer, size);
return expression;
}
/**
* モデルのパラメータの更新の実行
* @param model 対象のモデル
* @param userTimeSeconds デルタ時間の積算値[秒]
* @param weight モーションの重み
* @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
*/
public doUpdateParameters(
model: CubismModel,
userTimeSeconds: number,
weight: number,
motionQueueEntry: CubismMotionQueueEntry
): void {
for (let i = 0; i < this._parameters.length; ++i) {
const parameter: ExpressionParameter = this._parameters[i];
switch (parameter.blendType) {
case ExpressionBlendType.Additive: {
model.addParameterValueById(
parameter.parameterId,
parameter.value,
weight
);
break;
}
case ExpressionBlendType.Multiply: {
model.multiplyParameterValueById(
parameter.parameterId,
parameter.value,
weight
);
break;
}
case ExpressionBlendType.Overwrite: {
model.setParameterValueById(
parameter.parameterId,
parameter.value,
weight
);
break;
}
default:
// 仕様にない値を設定した時はすでに加算モードになっている
break;
}
}
}
/**
* @brief 表情によるモデルのパラメータの計算
*
* モデルの表情に関するパラメータを計算する。
*
* @param[in] model 対象のモデル
* @param[in] userTimeSeconds デルタ時間の積算値[秒]
* @param[in] motionQueueEntry CubismMotionQueueManagerで管理されているモーション
* @param[in] expressionParameterValues モデルに適用する各パラメータの値
* @param[in] expressionIndex 表情のインデックス
* @param[in] fadeWeight 表情のウェイト
*/
public calculateExpressionParameters(
model: CubismModel,
userTimeSeconds: number,
motionQueueEntry: CubismMotionQueueEntry,
expressionParameterValues: Array<ExpressionParameterValue>,
expressionIndex: number,
fadeWeight: number
) {
if (motionQueueEntry == null || expressionParameterValues == null) {
return;
}
if (!motionQueueEntry.isAvailable()) {
return;
}
// モデルに適用する値を計算
for (let i = 0; i < expressionParameterValues.length; ++i) {
const expressionParameterValue = expressionParameterValues[i];
if (expressionParameterValue.parameterId == null) {
continue;
}
const currentParameterValue = (expressionParameterValue.overwriteValue =
model.getParameterValueById(expressionParameterValue.parameterId));
const expressionParameters = this.getExpressionParameters();
let parameterIndex = -1;
for (let j = 0; j < expressionParameters.length; ++j) {
if (
expressionParameterValue.parameterId !=
expressionParameters[j].parameterId
) {
continue;
}
parameterIndex = j;
break;
}
// 再生中のExpressionが参照していないパラメータは初期値を適用
if (parameterIndex < 0) {
if (expressionIndex == 0) {
expressionParameterValue.additiveValue =
CubismExpressionMotion.DefaultAdditiveValue;
expressionParameterValue.multiplyValue =
CubismExpressionMotion.DefaultMultiplyValue;
expressionParameterValue.overwriteValue = currentParameterValue;
} else {
expressionParameterValue.additiveValue = this.calculateValue(
expressionParameterValue.additiveValue,
CubismExpressionMotion.DefaultAdditiveValue,
fadeWeight
);
expressionParameterValue.multiplyValue = this.calculateValue(
expressionParameterValue.multiplyValue,
CubismExpressionMotion.DefaultMultiplyValue,
fadeWeight
);
expressionParameterValue.overwriteValue = this.calculateValue(
expressionParameterValue.overwriteValue,
currentParameterValue,
fadeWeight
);
}
continue;
}
// 値を計算
const value = expressionParameters[parameterIndex].value;
let newAdditiveValue, newMultiplyValue, newOverwriteValue;
switch (expressionParameters[parameterIndex].blendType) {
case ExpressionBlendType.Additive:
newAdditiveValue = value;
newMultiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
newOverwriteValue = currentParameterValue;
break;
case ExpressionBlendType.Multiply:
newAdditiveValue = CubismExpressionMotion.DefaultAdditiveValue;
newMultiplyValue = value;
newOverwriteValue = currentParameterValue;
break;
case ExpressionBlendType.Overwrite:
newAdditiveValue = CubismExpressionMotion.DefaultAdditiveValue;
newMultiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
newOverwriteValue = value;
break;
default:
return;
}
if (expressionIndex == 0) {
expressionParameterValue.additiveValue = newAdditiveValue;
expressionParameterValue.multiplyValue = newMultiplyValue;
expressionParameterValue.overwriteValue = newOverwriteValue;
} else {
expressionParameterValue.additiveValue =
expressionParameterValue.additiveValue * (1.0 - fadeWeight) +
newAdditiveValue * fadeWeight;
expressionParameterValue.multiplyValue =
expressionParameterValue.multiplyValue * (1.0 - fadeWeight) +
newMultiplyValue * fadeWeight;
expressionParameterValue.overwriteValue =
expressionParameterValue.overwriteValue * (1.0 - fadeWeight) +
newOverwriteValue * fadeWeight;
}
}
}
/**
* @brief 表情が参照しているパラメータを取得
*
* 表情が参照しているパラメータを取得する
*
* @return 表情パラメータ
*/
public getExpressionParameters() {
return this._parameters;
}
protected parse(buffer: ArrayBuffer, size: number) {
const json: CubismJson = CubismJson.create(buffer, size);
if (!json) {
return;
}
const root: Value = json.getRoot();
this.setFadeInTime(
root.getValueByString(ExpressionKeyFadeIn).toFloat(DefaultFadeTime)
); // フェードイン
this.setFadeOutTime(
root.getValueByString(ExpressionKeyFadeOut).toFloat(DefaultFadeTime)
); // フェードアウト
// 各パラメータについて
const parameterCount = root
.getValueByString(ExpressionKeyParameters)
.getSize();
let dstIndex: number = this._parameters.length;
this._parameters.length += parameterCount;
for (let i = 0; i < parameterCount; ++i) {
const param: Value = root
.getValueByString(ExpressionKeyParameters)
.getValueByIndex(i);
const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId(
param.getValueByString(ExpressionKeyId).getRawString()
); // パラメータID
const value: number = param
.getValueByString(ExpressionKeyValue)
.toFloat(); // 値
// 計算方法の設定
let blendType: ExpressionBlendType;
if (
param.getValueByString(ExpressionKeyBlend).isNull() ||
param.getValueByString(ExpressionKeyBlend).getString() == BlendValueAdd
) {
blendType = ExpressionBlendType.Additive;
} else if (
param.getValueByString(ExpressionKeyBlend).getString() ==
BlendValueMultiply
) {
blendType = ExpressionBlendType.Multiply;
} else if (
param.getValueByString(ExpressionKeyBlend).getString() ==
BlendValueOverwrite
) {
blendType = ExpressionBlendType.Overwrite;
} else {
// その他 仕様にない値を設定した時は加算モードにすることで復旧
blendType = ExpressionBlendType.Additive;
}
// 設定オブジェクトを作成してリストに追加する
const item: ExpressionParameter = new ExpressionParameter();
item.parameterId = parameterId;
item.blendType = blendType;
item.value = value;
this._parameters[dstIndex++] = item;
}
CubismJson.delete(json); // JSONデータは不要になったら削除する
}
/**
* @brief ブレンド計算
*
* 入力された値でブレンド計算をする。
*
* @param source 現在の値
* @param destination 適用する値
* @param weight ウェイト
* @return 計算結果
*/
public calculateValue(
source: number,
destination: number,
fadeWeight: number
): number {
return source * (1.0 - fadeWeight) + destination * fadeWeight;
}
/**
* コンストラクタ
*/
protected constructor() {
super();
this._parameters = new Array<ExpressionParameter>();
}
private _parameters: Array<ExpressionParameter>; // 表情のパラメータ情報リスト
}
/**
* 表情パラメータ値の計算方式
*/
export enum ExpressionBlendType {
Additive = 0, // 加算
Multiply = 1, // 乗算
Overwrite = 2 // 上書き
}
/**
* 表情のパラメータ情報
*/
export class ExpressionParameter {
parameterId: CubismIdHandle; // パラメータID
blendType: ExpressionBlendType; // パラメータの演算種類
value: number; // 値
}
// Namespace definition for compatibility.
import * as $ from './cubismexpressionmotion';
import { ExpressionParameterValue } from './cubismexpressionmotionmanager';
import { CubismDefaultParameterId } from '../cubismdefaultparameterid';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismExpressionMotion = $.CubismExpressionMotion;
export type CubismExpressionMotion = $.CubismExpressionMotion;
export const ExpressionBlendType = $.ExpressionBlendType;
export type ExpressionBlendType = $.ExpressionBlendType;
export const ExpressionParameter = $.ExpressionParameter;
export type ExpressionParameter = $.ExpressionParameter;
}

View File

@@ -0,0 +1,282 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismId, CubismIdHandle } from '../id/cubismid';
import { LogLevel, csmDelete } from '../live2dcubismframework';
import { CubismModel } from '../model/cubismmodel';
import { CubismExpressionMotion } from './cubismexpressionmotion';
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
import { CubismMotionQueueManager } from './cubismmotionqueuemanager';
/**
* @brief パラメータに適用する表情の値を持たせる構造体
*/
export class ExpressionParameterValue {
parameterId: CubismIdHandle; // パラメーターID
additiveValue: number; // 加算値
multiplyValue: number; // 乗算値
overwriteValue: number; // 上書き値
}
/**
* @brief 表情モーションの管理
*
* 表情モーションの管理をおこなうクラス。
*/
export class CubismExpressionMotionManager extends CubismMotionQueueManager {
/**
* コンストラクタ
*/
public constructor() {
super();
this._expressionParameterValues = new Array<ExpressionParameterValue>();
this._fadeWeights = new Array<number>();
}
/**
* デストラクタ相当の処理
*/
public release(): void {
if (this._expressionParameterValues) {
csmDelete(this._expressionParameterValues);
this._expressionParameterValues = null;
}
if (this._fadeWeights) {
csmDelete(this._fadeWeights);
this._fadeWeights = null;
}
}
/**
* @brief 再生中のモーションのウェイトを取得する。
*
* @param[in] index 表情のインデックス
* @return 表情モーションのウェイト
*/
public getFadeWeight(index: number): number {
if (
index < 0 ||
this._fadeWeights.length < 1 ||
index >= this._fadeWeights.length
) {
console.warn(
'Failed to get the fade weight value. The element at that index does not exist.'
);
return -1;
}
return this._fadeWeights[index];
}
/**
* @brief モーションのウェイトの設定。
*
* @param[in] index 表情のインデックス
* @param[in] index 表情モーションのウェイト
*/
public setFadeWeight(index: number, expressionFadeWeight: number): void {
if (
index < 0 ||
this._fadeWeights.length < 1 ||
this._fadeWeights.length <= index
) {
console.warn(
'Failed to set the fade weight value. The element at that index does not exist.'
);
return;
}
this._fadeWeights[index] = expressionFadeWeight;
}
/**
* @brief モーションの更新
*
* モーションを更新して、モデルにパラメータ値を反映する。
*
* @param[in] model 対象のモデル
* @param[in] deltaTimeSeconds デルタ時間[秒]
* @return true 更新されている
* false 更新されていない
*/
public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean {
this._userTimeSeconds += deltaTimeSeconds;
let updated = false;
const motions = this.getCubismMotionQueueEntries();
let expressionWeight = 0.0;
let expressionIndex = 0;
if (this._fadeWeights.length !== motions.length) {
const difference = motions.length - this._fadeWeights.length;
let dstIndex: number = this._fadeWeights.length;
this._fadeWeights.length += difference;
// TODO:
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
// this._fadeWeights.fill(0.0, dstIndex, this._fadeWeights.length)
for (let i = 0; i < difference; i++) {
this._fadeWeights[dstIndex++] = 0.0;
}
}
// ------- 処理を行う --------
// 既にモーションがあれば終了フラグを立てる
for (let i = 0; i < this._motions.length; ) {
const motionQueueEntry = this._motions[i];
if (motionQueueEntry == null) {
motions.splice(i, 1); //削除
continue;
}
const expressionMotion = <CubismExpressionMotion>(
motionQueueEntry.getCubismMotion()
);
if (expressionMotion == null) {
csmDelete(motionQueueEntry);
motions.splice(i, 1); //削除
continue;
}
const expressionParameters = expressionMotion.getExpressionParameters();
if (motionQueueEntry.isAvailable()) {
// 再生中のExpressionが参照しているパラメータをすべてリストアップ
for (let i = 0; i < expressionParameters.length; ++i) {
if (expressionParameters[i].parameterId == null) {
continue;
}
let index = -1;
// リストにパラメータIDが存在するか検索
for (let j = 0; j < this._expressionParameterValues.length; ++j) {
if (
this._expressionParameterValues[j].parameterId !=
expressionParameters[i].parameterId
) {
continue;
}
index = j;
break;
}
if (index >= 0) {
continue;
}
// パラメータがリストに存在しないなら新規追加
const item: ExpressionParameterValue = new ExpressionParameterValue();
item.parameterId = expressionParameters[i].parameterId;
item.additiveValue = CubismExpressionMotion.DefaultAdditiveValue;
item.multiplyValue = CubismExpressionMotion.DefaultMultiplyValue;
item.overwriteValue = model.getParameterValueById(item.parameterId);
this._expressionParameterValues.push(item);
}
}
// ------ 値を計算する ------
expressionMotion.setupMotionQueueEntry(
motionQueueEntry,
this._userTimeSeconds
);
this.setFadeWeight(
expressionIndex,
expressionMotion.updateFadeWeight(
motionQueueEntry,
this._userTimeSeconds
)
);
expressionMotion.calculateExpressionParameters(
model,
this._userTimeSeconds,
motionQueueEntry,
this._expressionParameterValues,
expressionIndex,
this.getFadeWeight(expressionIndex)
);
expressionWeight +=
expressionMotion.getFadeInTime() == 0.0
? 1.0
: CubismMath.getEasingSine(
(this._userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
expressionMotion.getFadeInTime()
);
updated = true;
if (motionQueueEntry.isTriggeredFadeOut()) {
// フェードアウト開始
motionQueueEntry.startFadeOut(
motionQueueEntry.getFadeOutSeconds(),
this._userTimeSeconds
);
}
++i;
++expressionIndex;
}
// ----- 最新のExpressionのフェードが完了していればそれ以前を削除する ------
if (motions.length > 1) {
const latestFadeWeight: number = this.getFadeWeight(
this._fadeWeights.length - 1
);
if (latestFadeWeight >= 1.0) {
// 配列の最後の要素は削除しない
for (let i = motions.length - 2; i >= 0; --i) {
const motionQueueEntry = motions[i];
csmDelete(motionQueueEntry);
motions.splice(i, 1);
this._fadeWeights.splice(i, 1);
}
}
}
if (expressionWeight > 1.0) {
expressionWeight = 1.0;
}
// モデルに各値を適用
for (let i = 0; i < this._expressionParameterValues.length; ++i) {
const expressionParameterValue = this._expressionParameterValues[i];
model.setParameterValueById(
expressionParameterValue.parameterId,
(expressionParameterValue.overwriteValue +
expressionParameterValue.additiveValue) *
expressionParameterValue.multiplyValue,
expressionWeight
);
expressionParameterValue.additiveValue =
CubismExpressionMotion.DefaultAdditiveValue;
expressionParameterValue.multiplyValue =
CubismExpressionMotion.DefaultMultiplyValue;
}
return updated;
}
private _expressionParameterValues: Array<ExpressionParameterValue>; ///< モデルに適用する各パラメータの値
private _fadeWeights: Array<number>; ///< 再生中の表情のウェイト
private _startExpressionTime: number; ///< 表情の再生開始時刻
}
// Namespace definition for compatibility.
import * as $ from './cubismexpressionmotionmanager';
import { CubismMath } from '../math/cubismmath';
import { CubismDebug, CubismLogError } from '../utils/cubismdebug';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismExpressionMotionManager = $.CubismExpressionMotionManager;
export type CubismExpressionMotionManager = $.CubismExpressionMotionManager;
}

View File

@@ -0,0 +1,66 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
import { CubismExpressionMotionManager } from './cubismexpressionmotionmanager';
/**
* Updater for expression effects.
* Handles the management of expression motion through the CubismExpressionMotionManager.
*/
export class CubismExpressionUpdater extends ICubismUpdater {
private _expressionManager: CubismExpressionMotionManager;
/**
* Constructor
*
* @param expressionManager CubismExpressionMotionManager reference
*/
constructor(expressionManager: CubismExpressionMotionManager);
/**
* Constructor
*
* @param expressionManager CubismExpressionMotionManager reference
* @param executionOrder Order of operations
*/
constructor(
expressionManager: CubismExpressionMotionManager,
executionOrder: number
);
constructor(
expressionManager: CubismExpressionMotionManager,
executionOrder?: number
) {
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Expression);
this._expressionManager = expressionManager;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
this._expressionManager.updateMotion(model, deltaTimeSeconds);
}
}
// Namespace definition for compatibility.
import * as $ from './cubismexpressionupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismExpressionUpdater = $.CubismExpressionUpdater;
export type CubismExpressionUpdater = $.CubismExpressionUpdater;
}

View File

@@ -0,0 +1,76 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
import { CubismEyeBlink } from '../effect/cubismeyeblink';
/**
* Updater for eye blink effects.
* Handles the management of eye blink animation through the CubismEyeBlink class.
*/
export class CubismEyeBlinkUpdater extends ICubismUpdater {
private _motionUpdated: () => boolean;
private _eyeBlink: CubismEyeBlink;
/**
* Constructor
*
* @param motionUpdated Motion update flag reference
* @param eyeBlink CubismEyeBlink reference
*/
constructor(motionUpdated: () => boolean, eyeBlink: CubismEyeBlink);
/**
* Constructor
*
* @param motionUpdated Motion update flag reference
* @param eyeBlink CubismEyeBlink reference
* @param executionOrder Order of operations
*/
constructor(
motionUpdated: () => boolean,
eyeBlink: CubismEyeBlink,
executionOrder: number
);
constructor(
motionUpdated: () => boolean,
eyeBlink: CubismEyeBlink,
executionOrder?: number
) {
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_EyeBlink);
this._motionUpdated = motionUpdated;
this._eyeBlink = eyeBlink;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
if (!this._motionUpdated()) {
// メインモーションの更新がないとき
// 目パチ
this._eyeBlink.updateParameters(model, deltaTimeSeconds);
}
}
}
// Namespace definition for compatibility.
import * as $ from './cubismeyeblinkupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismEyeBlinkUpdater = $.CubismEyeBlinkUpdater;
export type CubismEyeBlinkUpdater = $.CubismEyeBlinkUpdater;
}

View File

@@ -0,0 +1,104 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
import { CubismIdHandle } from '../id/cubismid';
import { IParameterProvider } from './iparameterprovider';
/**
* Updater for lip sync effects.
* Handles the management of lip sync animation through parameter providers.
*/
export class CubismLipSyncUpdater extends ICubismUpdater {
private _lipSyncIds: Array<CubismIdHandle>;
private _audioProvider: IParameterProvider | null;
/**
* Constructor
*
* @param lipSyncIds Array of lip sync parameter IDs
* @param audioProvider Audio parameter provider
*/
constructor(
lipSyncIds: Array<CubismIdHandle>,
audioProvider: IParameterProvider | null
);
/**
* Constructor
*
* @param lipSyncIds Array of lip sync parameter IDs
* @param audioProvider Audio parameter provider
* @param executionOrder Order of operations
*/
constructor(
lipSyncIds: Array<CubismIdHandle>,
audioProvider: IParameterProvider | null,
executionOrder: number
);
constructor(
lipSyncIds: Array<CubismIdHandle>,
audioProvider: IParameterProvider | null,
executionOrder?: number
) {
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_LipSync);
this._lipSyncIds = [...lipSyncIds]; // Copy array
this._audioProvider = audioProvider;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
if (this._audioProvider) {
const updateSuccessful = this._audioProvider.update(deltaTimeSeconds);
if (updateSuccessful) {
const lipSyncValue = this._audioProvider.getParameter();
// Apply lip sync value to all registered parameters
for (let i = 0; i < this._lipSyncIds.length; i++) {
model.addParameterValueById(this._lipSyncIds[i], lipSyncValue);
}
}
}
}
/**
* Set audio parameter provider.
*
* @param audioProvider Audio parameter provider to set
*/
setAudioProvider(audioProvider: IParameterProvider | null): void {
this._audioProvider = audioProvider;
}
/**
* Get audio parameter provider.
*
* @return Current audio parameter provider
*/
getAudioProvider(): IParameterProvider | null {
return this._audioProvider;
}
}
// Namespace definition for compatibility.
import * as $ from './cubismlipsyncupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismLipSyncUpdater = $.CubismLipSyncUpdater;
export type CubismLipSyncUpdater = $.CubismLipSyncUpdater;
}

View File

@@ -0,0 +1,77 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
import { CubismTargetPoint } from '../math/cubismtargetpoint';
import { CubismLook } from '../effect/cubismlook';
/**
* Updater for look effects.
* Handles the management of dragging motion through the MotionQueueManager.
*/
export class CubismLookUpdater extends ICubismUpdater {
private _look: CubismLook;
private _dragManager: CubismTargetPoint;
/**
* Constructor
*
* @param look CubismLook reference
* @param dragManager CubismTargetPoint reference
*/
constructor(look: CubismLook, dragManager: CubismTargetPoint);
/**
* Constructor
*
* @param look CubismLook reference
* @param dragManager CubismTargetPoint reference
* @param executionOrder Order of operations
*/
constructor(
look: CubismLook,
dragManager: CubismTargetPoint,
executionOrder: number
);
constructor(
look: CubismLook,
dragManager: CubismTargetPoint,
executionOrder?: number
) {
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Drag);
this._look = look;
this._dragManager = dragManager;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
this._dragManager.update(deltaTimeSeconds);
const dragX = this._dragManager.getX();
const dragY = this._dragManager.getY();
this._look.updateParameters(model, dragX, dragY);
}
}
// Namespace definition for compatibility.
import * as $ from './cubismlookupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismLookUpdater = $.CubismLookUpdater;
export type CubismLookUpdater = $.CubismLookUpdater;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,155 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
/**
* @brief モーションカーブの種類
*
* モーションカーブの種類。
*/
export enum CubismMotionCurveTarget {
CubismMotionCurveTarget_Model, // モデルに対して
CubismMotionCurveTarget_Parameter, // パラメータに対して
CubismMotionCurveTarget_PartOpacity // パーツの不透明度に対して
}
/**
* @brief モーションカーブのセグメントの種類
*
* モーションカーブのセグメントの種類。
*/
export enum CubismMotionSegmentType {
CubismMotionSegmentType_Linear = 0, // リニア
CubismMotionSegmentType_Bezier = 1, // ベジェ曲線
CubismMotionSegmentType_Stepped = 2, // ステップ
CubismMotionSegmentType_InverseStepped = 3 // インバースステップ
}
/**
* @brief モーションカーブの制御点
*
* モーションカーブの制御点。
*/
export class CubismMotionPoint {
time = 0.0; // 時間[秒]
value = 0.0; // 値
}
/**
* モーションカーブのセグメントの評価関数
*
* @param points モーションカーブの制御点リスト
* @param time 評価する時間[秒]
*/
export interface csmMotionSegmentEvaluationFunction {
(points: CubismMotionPoint[], time: number): number;
}
/**
* @brief モーションカーブのセグメント
*
* モーションカーブのセグメント。
*/
export class CubismMotionSegment {
/**
* @brief コンストラクタ
*
* コンストラクタ。
*/
public constructor() {
this.evaluate = null;
this.basePointIndex = 0;
this.segmentType = 0;
}
evaluate: csmMotionSegmentEvaluationFunction; // 使用する評価関数
basePointIndex: number; // 最初のセグメントへのインデックス
segmentType: number; // セグメントの種類
}
/**
* @brief モーションカーブ
*
* モーションカーブ。
*/
export class CubismMotionCurve {
public constructor() {
this.type = CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
this.segmentCount = 0;
this.baseSegmentIndex = 0;
this.fadeInTime = 0.0;
this.fadeOutTime = 0.0;
}
type: CubismMotionCurveTarget; // カーブの種類
id: CubismIdHandle; // カーブのID
segmentCount: number; // セグメントの個数
baseSegmentIndex: number; // 最初のセグメントのインデックス
fadeInTime: number; // フェードインにかかる時間[秒]
fadeOutTime: number; // フェードアウトにかかる時間[秒]
}
/**
* イベント。
*/
export class CubismMotionEvent {
fireTime = 0.0;
value: string;
}
/**
* @brief モーションデータ
*
* モーションデータ。
*/
export class CubismMotionData {
public constructor() {
this.duration = 0.0;
this.loop = false;
this.curveCount = 0;
this.eventCount = 0;
this.fps = 0.0;
this.curves = new Array<CubismMotionCurve>();
this.segments = new Array<CubismMotionSegment>();
this.points = new Array<CubismMotionPoint>();
this.events = new Array<CubismMotionEvent>();
}
duration: number; // モーションの長さ[秒]
loop: boolean; // ループするかどうか
curveCount: number; // カーブの個数
eventCount: number; // UserDataの個数
fps: number; // フレームレート
curves: Array<CubismMotionCurve>; // カーブのリスト
segments: Array<CubismMotionSegment>; // セグメントのリスト
points: Array<CubismMotionPoint>; // ポイントのリスト
events: Array<CubismMotionEvent>; // イベントのリスト
}
// Namespace definition for compatibility.
import * as $ from './cubismmotioninternal';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMotionCurve = $.CubismMotionCurve;
export type CubismMotionCurve = $.CubismMotionCurve;
export const CubismMotionCurveTarget = $.CubismMotionCurveTarget;
export type CubismMotionCurveTarget = $.CubismMotionCurveTarget;
export const CubismMotionData = $.CubismMotionData;
export type CubismMotionData = $.CubismMotionData;
export const CubismMotionEvent = $.CubismMotionEvent;
export type CubismMotionEvent = $.CubismMotionEvent;
export const CubismMotionPoint = $.CubismMotionPoint;
export type CubismMotionPoint = $.CubismMotionPoint;
export const CubismMotionSegment = $.CubismMotionSegment;
export type CubismMotionSegment = $.CubismMotionSegment;
export const CubismMotionSegmentType = $.CubismMotionSegmentType;
export type CubismMotionSegmentType = $.CubismMotionSegmentType;
export type csmMotionSegmentEvaluationFunction =
$.csmMotionSegmentEvaluationFunction;
}

View File

@@ -0,0 +1,463 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { CSM_ASSERT, CubismLogWarning } from '../utils/cubismdebug';
import { CubismJson, JsonMap } from '../utils/cubismjson';
import { CubismMotionSegmentType } from './cubismmotioninternal';
// JSON keys
const Meta = 'Meta';
const Duration = 'Duration';
const Loop = 'Loop';
const AreBeziersRestricted = 'AreBeziersRestricted';
const CurveCount = 'CurveCount';
const Fps = 'Fps';
const TotalSegmentCount = 'TotalSegmentCount';
const TotalPointCount = 'TotalPointCount';
const Curves = 'Curves';
const Target = 'Target';
const Id = 'Id';
const FadeInTime = 'FadeInTime';
const FadeOutTime = 'FadeOutTime';
const Segments = 'Segments';
const UserData = 'UserData';
const UserDataCount = 'UserDataCount';
const TotalUserDataSize = 'TotalUserDataSize';
const Time = 'Time';
const Value = 'Value';
/**
* motion3.jsonのコンテナ。
*/
export class CubismMotionJson {
/**
* コンストラクタ
* @param buffer motion3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public constructor(buffer: ArrayBuffer, size: number) {
this._json = CubismJson.create(buffer, size);
}
/**
* デストラクタ相当の処理
*/
public release(): void {
CubismJson.delete(this._json);
}
/**
* モーションの長さを取得する
* @return モーションの長さ[秒]
*/
public getMotionDuration(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Duration)
.toFloat();
}
/**
* モーションのループ情報の取得
* @return true ループする
* @return false ループしない
*/
public isMotionLoop(): boolean {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Loop)
.toBoolean();
}
/**
* motion3.jsonファイルの整合性チェック
*
* @return 正常なファイルの場合はtrueを返す。
*/
hasConsistency(): boolean {
let result = true;
if (!this._json || !this._json.getRoot()) {
return false;
}
const actualCurveListSize = this._json
.getRoot()
.getValueByString(Curves)
.getVector().length;
let actualTotalSegmentCount = 0;
let actualTotalPointCount = 0;
// カウント処理
for (
let curvePosition = 0;
curvePosition < actualCurveListSize;
++curvePosition
) {
for (
let segmentPosition = 0;
segmentPosition < this.getMotionCurveSegmentCount(curvePosition);
) {
if (segmentPosition == 0) {
actualTotalPointCount += 1;
segmentPosition += 2;
}
const segment = this.getMotionCurveSegment(
curvePosition,
segmentPosition
) as CubismMotionSegmentType;
switch (segment) {
case CubismMotionSegmentType.CubismMotionSegmentType_Linear:
actualTotalPointCount += 1;
segmentPosition += 3;
break;
case CubismMotionSegmentType.CubismMotionSegmentType_Bezier:
actualTotalPointCount += 3;
segmentPosition += 7;
break;
case CubismMotionSegmentType.CubismMotionSegmentType_Stepped:
actualTotalPointCount += 1;
segmentPosition += 3;
break;
case CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped:
actualTotalPointCount += 1;
segmentPosition += 3;
break;
default:
CSM_ASSERT(0);
break;
}
++actualTotalSegmentCount;
}
}
// 個数チェック
if (actualCurveListSize != this.getMotionCurveCount()) {
CubismLogWarning('The number of curves does not match the metadata.');
result = false;
}
if (actualTotalSegmentCount != this.getMotionTotalSegmentCount()) {
CubismLogWarning('The number of segment does not match the metadata.');
result = false;
}
if (actualTotalPointCount != this.getMotionTotalPointCount()) {
CubismLogWarning('The number of point does not match the metadata.');
result = false;
}
return result;
}
public getEvaluationOptionFlag(flagType: EvaluationOptionFlag): boolean {
if (
EvaluationOptionFlag.EvaluationOptionFlag_AreBeziersRistricted == flagType
) {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(AreBeziersRestricted)
.toBoolean();
}
return false;
}
/**
* モーションカーブの個数の取得
* @return モーションカーブの個数
*/
public getMotionCurveCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(CurveCount)
.toInt();
}
/**
* モーションのフレームレートの取得
* @return フレームレート[FPS]
*/
public getMotionFps(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Fps)
.toFloat();
}
/**
* モーションのセグメントの総合計の取得
* @return モーションのセグメントの取得
*/
public getMotionTotalSegmentCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalSegmentCount)
.toInt();
}
/**
* モーションのカーブの制御店の総合計の取得
* @return モーションのカーブの制御点の総合計
*/
public getMotionTotalPointCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalPointCount)
.toInt();
}
/**
* モーションのフェードイン時間の存在
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionFadeInTime(): boolean {
return !this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeInTime)
.isNull();
}
/**
* モーションのフェードアウト時間の存在
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionFadeOutTime(): boolean {
return !this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeOutTime)
.isNull();
}
/**
* モーションのフェードイン時間の取得
* @return フェードイン時間[秒]
*/
public getMotionFadeInTime(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeInTime)
.toFloat();
}
/**
* モーションのフェードアウト時間の取得
* @return フェードアウト時間[秒]
*/
public getMotionFadeOutTime(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(FadeOutTime)
.toFloat();
}
/**
* モーションのカーブの種類の取得
* @param curveIndex カーブのインデックス
* @return カーブの種類
*/
public getMotionCurveTarget(curveIndex: number): string {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Target)
.getRawString();
}
/**
* モーションのカーブのIDの取得
* @param curveIndex カーブのインデックス
* @return カーブのID
*/
public getMotionCurveId(curveIndex: number): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Id)
.getRawString()
);
}
/**
* モーションのカーブのフェードイン時間の存在
* @param curveIndex カーブのインデックス
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionCurveFadeInTime(curveIndex: number): boolean {
return !this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeInTime)
.isNull();
}
/**
* モーションのカーブのフェードアウト時間の存在
* @param curveIndex カーブのインデックス
* @return true 存在する
* @return false 存在しない
*/
public isExistMotionCurveFadeOutTime(curveIndex: number): boolean {
return !this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeOutTime)
.isNull();
}
/**
* モーションのカーブのフェードイン時間の取得
* @param curveIndex カーブのインデックス
* @return フェードイン時間[秒]
*/
public getMotionCurveFadeInTime(curveIndex: number): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeInTime)
.toFloat();
}
/**
* モーションのカーブのフェードアウト時間の取得
* @param curveIndex カーブのインデックス
* @return フェードアウト時間[秒]
*/
public getMotionCurveFadeOutTime(curveIndex: number): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(FadeOutTime)
.toFloat();
}
/**
* モーションのカーブのセグメントの個数を取得する
* @param curveIndex カーブのインデックス
* @return モーションのカーブのセグメントの個数
*/
public getMotionCurveSegmentCount(curveIndex: number): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Segments)
.getVector().length;
}
/**
* モーションのカーブのセグメントの値の取得
* @param curveIndex カーブのインデックス
* @param segmentIndex セグメントのインデックス
* @return セグメントの値
*/
public getMotionCurveSegment(
curveIndex: number,
segmentIndex: number
): number {
return this._json
.getRoot()
.getValueByString(Curves)
.getValueByIndex(curveIndex)
.getValueByString(Segments)
.getValueByIndex(segmentIndex)
.toFloat();
}
/**
* イベントの個数の取得
* @return イベントの個数
*/
public getEventCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(UserDataCount)
.toInt();
}
/**
* イベントの総文字数の取得
* @return イベントの総文字数
*/
public getTotalEventValueSize(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalUserDataSize)
.toInt();
}
/**
* イベントの時間の取得
* @param userDataIndex イベントのインデックス
* @return イベントの時間[秒]
*/
public getEventTime(userDataIndex: number): number {
return this._json
.getRoot()
.getValueByString(UserData)
.getValueByIndex(userDataIndex)
.getValueByString(Time)
.toFloat();
}
/**
* イベントの取得
* @param userDataIndex イベントのインデックス
* @return イベントの文字列
*/
public getEventValue(userDataIndex: number): string {
return this._json
.getRoot()
.getValueByString(UserData)
.getValueByIndex(userDataIndex)
.getValueByString(Value)
.getRawString();
}
_json: CubismJson; // motion3.jsonのデータ
}
/**
* @brief ベジェカーブの解釈方法のフラグタイプ
*/
export enum EvaluationOptionFlag {
EvaluationOptionFlag_AreBeziersRistricted = 0 ///< ベジェハンドルの規制状態
}
// Namespace definition for compatibility.
import * as $ from './cubismmotionjson';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMotionJson = $.CubismMotionJson;
export type CubismMotionJson = $.CubismMotionJson;
}

View File

@@ -0,0 +1,126 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismModel } from '../model/cubismmodel';
import { ACubismMotion } from './acubismmotion';
import {
CubismMotionQueueEntryHandle,
CubismMotionQueueManager
} from './cubismmotionqueuemanager';
/**
* モーションの管理
*
* モーションの管理を行うクラス
*/
export class CubismMotionManager extends CubismMotionQueueManager {
/**
* コンストラクタ
*/
public constructor() {
super();
this._currentPriority = 0;
this._reservePriority = 0;
}
/**
* 再生中のモーションの優先度の取得
* @return モーションの優先度
*/
public getCurrentPriority(): number {
return this._currentPriority;
}
/**
* 予約中のモーションの優先度を取得する。
* @return モーションの優先度
*/
public getReservePriority(): number {
return this._reservePriority;
}
/**
* 予約中のモーションの優先度を設定する。
* @param val 優先度
*/
public setReservePriority(val: number): void {
this._reservePriority = val;
}
/**
* 優先度を設定してモーションを開始する。
*
* @param motion モーション
* @param autoDelete 再生が狩猟したモーションのインスタンスを削除するならtrue
* @param priority 優先度
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
*/
public startMotionPriority(
motion: ACubismMotion,
autoDelete: boolean,
priority: number
): CubismMotionQueueEntryHandle {
if (priority == this._reservePriority) {
this._reservePriority = 0; // 予約を解除
}
this._currentPriority = priority; // 再生中モーションの優先度を設定
return super.startMotion(motion, autoDelete);
}
/**
* モーションを更新して、モデルにパラメータ値を反映する。
*
* @param model 対象のモデル
* @param deltaTimeSeconds デルタ時間[秒]
* @return true 更新されている
* @return false 更新されていない
*/
public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean {
this._userTimeSeconds += deltaTimeSeconds;
const updated: boolean = super.doUpdateMotion(model, this._userTimeSeconds);
if (this.isFinished()) {
this._currentPriority = 0; // 再生中のモーションの優先度を解除
}
return updated;
}
/**
* モーションを予約する。
*
* @param priority 優先度
* @return true 予約できた
* @return false 予約できなかった
*/
public reserveMotion(priority: number): boolean {
if (
priority <= this._reservePriority ||
priority <= this._currentPriority
) {
return false;
}
this._reservePriority = priority;
return true;
}
_currentPriority: number; // 現在再生中のモーションの優先度
_reservePriority: number; // 再生予定のモーションの優先度。再生中は0になる。モーションファイルを別スレッドで読み込むときの機能。
}
// Namespace definition for compatibility.
import * as $ from './cubismmotionmanager';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMotionManager = $.CubismMotionManager;
export type CubismMotionManager = $.CubismMotionManager;
}

View File

@@ -0,0 +1,262 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ACubismMotion } from './acubismmotion';
import { CubismMotionQueueEntryHandle } from './cubismmotionqueuemanager';
/**
* CubismMotionQueueManagerで再生している各モーションの管理クラス。
*/
export class CubismMotionQueueEntry {
/**
* コンストラクタ
*/
public constructor() {
this._autoDelete = false;
this._motion = null;
this._available = true;
this._finished = false;
this._started = false;
this._startTimeSeconds = -1.0;
this._fadeInStartTimeSeconds = 0.0;
this._endTimeSeconds = -1.0;
this._stateTimeSeconds = 0.0;
this._stateWeight = 0.0;
this._lastEventCheckSeconds = 0.0;
this._motionQueueEntryHandle = this;
this._fadeOutSeconds = 0.0;
this._isTriggeredFadeOut = false;
}
/**
* デストラクタ相当の処理
*/
public release(): void {
if (this._autoDelete && this._motion) {
ACubismMotion.delete(this._motion); //
}
}
/**
* フェードアウト時間と開始判定の設定
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
*/
public setFadeOut(fadeOutSeconds: number): void {
this._fadeOutSeconds = fadeOutSeconds;
this._isTriggeredFadeOut = true;
}
/**
* フェードアウトの開始
* @param fadeOutSeconds フェードアウトにかかる時間[秒]
* @param userTimeSeconds デルタ時間の積算値[秒]
*/
public startFadeOut(fadeOutSeconds: number, userTimeSeconds: number): void {
const newEndTimeSeconds: number = userTimeSeconds + fadeOutSeconds;
this._isTriggeredFadeOut = true;
if (
this._endTimeSeconds < 0.0 ||
newEndTimeSeconds < this._endTimeSeconds
) {
this._endTimeSeconds = newEndTimeSeconds;
}
}
/**
* モーションの終了の確認
*
* @return true モーションが終了した
* @return false 終了していない
*/
public isFinished(): boolean {
return this._finished;
}
/**
* モーションの開始の確認
* @return true モーションが開始した
* @return false 開始していない
*/
public isStarted(): boolean {
return this._started;
}
/**
* モーションの開始時刻の取得
* @return モーションの開始時刻[秒]
*/
public getStartTime(): number {
return this._startTimeSeconds;
}
/**
* フェードインの開始時刻の取得
* @return フェードインの開始時刻[秒]
*/
public getFadeInStartTime(): number {
return this._fadeInStartTimeSeconds;
}
/**
* フェードインの終了時刻の取得
* @return フェードインの終了時刻の取得
*/
public getEndTime(): number {
return this._endTimeSeconds;
}
/**
* モーションの開始時刻の設定
* @param startTime モーションの開始時刻
*/
public setStartTime(startTime: number): void {
this._startTimeSeconds = startTime;
}
/**
* フェードインの開始時刻の設定
* @param startTime フェードインの開始時刻[秒]
*/
public setFadeInStartTime(startTime: number): void {
this._fadeInStartTimeSeconds = startTime;
}
/**
* フェードインの終了時刻の設定
* @param endTime フェードインの終了時刻[秒]
*/
public setEndTime(endTime: number): void {
this._endTimeSeconds = endTime;
}
/**
* モーションの終了の設定
* @param f trueならモーションの終了
*/
public setIsFinished(f: boolean): void {
this._finished = f;
}
/**
* モーション開始の設定
* @param f trueならモーションの開始
*/
public setIsStarted(f: boolean): void {
this._started = f;
}
/**
* モーションの有効性の確認
* @return true モーションは有効
* @return false モーションは無効
*/
public isAvailable(): boolean {
return this._available;
}
/**
* モーションの有効性の設定
* @param v trueならモーションは有効
*/
public setIsAvailable(v: boolean): void {
this._available = v;
}
/**
* モーションの状態の設定
* @param timeSeconds 現在時刻[秒]
* @param weight モーション尾重み
*/
public setState(timeSeconds: number, weight: number): void {
this._stateTimeSeconds = timeSeconds;
this._stateWeight = weight;
}
/**
* モーションの現在時刻の取得
* @return モーションの現在時刻[秒]
*/
public getStateTime(): number {
return this._stateTimeSeconds;
}
/**
* モーションの重みの取得
* @return モーションの重み
*/
public getStateWeight(): number {
return this._stateWeight;
}
/**
* 最後にイベントの発火をチェックした時間を取得
*
* @return 最後にイベントの発火をチェックした時間[秒]
*/
public getLastCheckEventSeconds(): number {
return this._lastEventCheckSeconds;
}
/**
* 最後にイベントをチェックした時間を設定
* @param checkSeconds 最後にイベントをチェックした時間[秒]
*/
public setLastCheckEventSeconds(checkSeconds: number): void {
this._lastEventCheckSeconds = checkSeconds;
}
/**
* フェードアウト開始判定の取得
* @return フェードアウト開始するかどうか
*/
public isTriggeredFadeOut(): boolean {
return this._isTriggeredFadeOut;
}
/**
* フェードアウト時間の取得
* @return フェードアウト時間[秒]
*/
public getFadeOutSeconds(): number {
return this._fadeOutSeconds;
}
/**
* モーションの取得
*
* @return モーション
*/
public getCubismMotion(): ACubismMotion {
return this._motion;
}
_autoDelete: boolean; // 自動削除
_motion: ACubismMotion; // モーション
_available: boolean; // 有効化フラグ
_finished: boolean; // 終了フラグ
_started: boolean; // 開始フラグ
_startTimeSeconds: number; // モーション再生開始時刻[秒]
_fadeInStartTimeSeconds: number; // フェードイン開始時刻(ループの時は初回のみ)[秒]
_endTimeSeconds: number; // 終了予定時刻[秒]
_stateTimeSeconds: number; // 時刻の状態[秒]
_stateWeight: number; // 重みの状態
_lastEventCheckSeconds: number; // 最終のMotion側のチェックした時間
private _fadeOutSeconds: number; // フェードアウト時間[秒]
private _isTriggeredFadeOut: boolean; // フェードアウト開始フラグ
_motionQueueEntryHandle: CubismMotionQueueEntryHandle; // インスタンスごとに一意の値を持つ識別番号
}
// Namespace definition for compatibility.
import * as $ from './cubismmotionqueueentry';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMotionQueueEntry = $.CubismMotionQueueEntry;
export type CubismMotionQueueEntry = $.CubismMotionQueueEntry;
}

View File

@@ -0,0 +1,329 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ACubismMotion } from './acubismmotion';
import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
import { CubismModel } from '../model/cubismmodel';
/**
* モーション再生の管理
*
* モーション再生の管理用クラス。CubismMotionモーションなどACubismMotionのサブクラスを再生するために使用する。
*
* @note 再生中に別のモーションが StartMotion()された場合は、新しいモーションに滑らかに変化し旧モーションは中断する。
* 表情用モーション、体用モーションなどを分けてモーション化した場合など、
* 複数のモーションを同時に再生させる場合は、複数のCubismMotionQueueManagerインスタンスを使用する。
*/
export class CubismMotionQueueManager {
/**
* コンストラクタ
*/
public constructor() {
this._userTimeSeconds = 0.0;
this._eventCallBack = null;
this._eventCustomData = null;
this._motions = new Array<CubismMotionQueueEntry>();
}
/**
* デストラクタ
*/
public release(): void {
for (let i = 0; i < this._motions.length; ++i) {
if (this._motions[i]) {
this._motions[i].release();
this._motions[i] = null;
}
}
this._motions = null;
}
/**
* 指定したモーションの開始
*
* 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。
*
* @param motion 開始するモーション
* @param autoDelete 再生が終了したモーションのインスタンスを削除するなら true
* @param userTimeSeconds Deprecated: デルタ時間の積算値[秒] 関数内で参照していないため使用は非推奨。
* @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
*/
public startMotion(
motion: ACubismMotion,
autoDelete: boolean,
userTimeSeconds?: number
): CubismMotionQueueEntryHandle {
if (motion == null) {
return InvalidMotionQueueEntryHandleValue;
}
let motionQueueEntry: CubismMotionQueueEntry = null;
// 既にモーションがあれば終了フラグを立てる
for (let i = 0; i < this._motions.length; ++i) {
motionQueueEntry = this._motions[i];
if (motionQueueEntry == null) {
continue;
}
motionQueueEntry.setFadeOut(motionQueueEntry._motion.getFadeOutTime()); // フェードアウト設定
}
motionQueueEntry = new CubismMotionQueueEntry(); // 終了時に破棄する
motionQueueEntry._autoDelete = autoDelete;
motionQueueEntry._motion = motion;
this._motions.push(motionQueueEntry);
return motionQueueEntry._motionQueueEntryHandle;
}
/**
* 全てのモーションの終了の確認
* @return true 全て終了している
* @return false 終了していない
*/
public isFinished(): boolean {
// ------- 処理を行う -------
// 既にモーションがあれば終了フラグを立てる
for (let i = 0; i < this._motions.length; ) {
let motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
if (motionQueueEntry == null) {
this._motions.splice(i, 1); // 削除
continue;
}
const motion: ACubismMotion = motionQueueEntry._motion;
if (motion == null) {
motionQueueEntry.release();
motionQueueEntry = null;
this._motions.splice(i, 1); // 削除
continue;
}
// ----- 終了済みの処理があれば削除する ------
if (!motionQueueEntry.isFinished()) {
return false;
} else {
i++;
}
}
return true;
}
/**
* 指定したモーションの終了の確認
* @param motionQueueEntryNumber モーションの識別番号
* @return true 全て終了している
* @return false 終了していない
*/
public isFinishedByHandle(
motionQueueEntryNumber: CubismMotionQueueEntryHandle
): boolean {
for (let i = 0; i < this._motions.length; i++) {
const motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
if (motionQueueEntry == null) {
continue;
}
if (
motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber &&
!motionQueueEntry.isFinished()
) {
return false;
}
}
return true;
}
/**
* 全てのモーションを停止する
*/
public stopAllMotions(): void {
// ------- 処理を行う -------
// 既にモーションがあれば終了フラグを立てる
for (let i = 0; i < this._motions.length; i++) {
const motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
if (motionQueueEntry == null) {
this._motions.splice(i, 1); // 削除
continue;
}
// ----- 終了済みの処理があれば削除する ------
motionQueueEntry.release();
this._motions.splice(i, 1); // 削除
continue;
}
}
/**
* @brief CubismMotionQueueEntryの配列の取得
*
* CubismMotionQueueEntryの配列を取得する。
*
* @return CubismMotionQueueEntryの配列へのポインタ
* NULL 見つからなかった
*/
public getCubismMotionQueueEntries(): Array<CubismMotionQueueEntry> {
return this._motions;
}
/**
* 指定したCubismMotionQueueEntryの取得
* @param motionQueueEntryNumber モーションの識別番号
* @return 指定したCubismMotionQueueEntry
* @return null 見つからなかった
*/
public getCubismMotionQueueEntry(
motionQueueEntryNumber: any
): CubismMotionQueueEntry {
//------- 処理を行う -------
for (let i = 0; i < this._motions.length; i++) {
const motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
if (motionQueueEntry == null) {
continue;
}
if (motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber) {
return motionQueueEntry;
}
}
return null;
}
/**
* イベントを受け取るCallbackの登録
*
* @param callback コールバック関数
* @param customData コールバックに返されるデータ
*/
public setEventCallback(
callback: CubismMotionEventFunction,
customData: any = null
): void {
this._eventCallBack = callback;
this._eventCustomData = customData;
}
/**
* モーションを更新して、モデルにパラメータ値を反映する。
*
* @param model 対象のモデル
* @param userTimeSeconds デルタ時間の積算値[秒]
* @return true モデルへパラメータ値の反映あり
* @return false モデルへパラメータ値の反映なし(モーションの変化なし)
*/
public doUpdateMotion(model: CubismModel, userTimeSeconds: number): boolean {
let updated = false;
// ------- 処理を行う --------
// 既にモーションがあれば終了フラグを立てる
for (let i = 0; i < this._motions.length; ) {
let motionQueueEntry: CubismMotionQueueEntry = this._motions[i];
if (motionQueueEntry == null) {
this._motions.splice(i, 1); // 削除
continue;
}
const motion: ACubismMotion = motionQueueEntry._motion;
if (motion == null) {
motionQueueEntry.release();
motionQueueEntry = null;
this._motions.splice(i, 1); // 削除
continue;
}
// ------ 値を反映する ------
motion.updateParameters(model, motionQueueEntry, userTimeSeconds);
updated = true;
// ------ ユーザトリガーイベントを検査する ----
const firedList: Array<string> = motion.getFiredEvent(
motionQueueEntry.getLastCheckEventSeconds() -
motionQueueEntry.getStartTime(),
userTimeSeconds - motionQueueEntry.getStartTime()
);
for (let i = 0; i < firedList.length; ++i) {
this._eventCallBack(this, firedList[i], this._eventCustomData);
}
motionQueueEntry.setLastCheckEventSeconds(userTimeSeconds);
// ------ 終了済みの処理があれば削除する ------
if (motionQueueEntry.isFinished()) {
motionQueueEntry.release();
motionQueueEntry = null;
this._motions.splice(i, 1); // 削除
} else {
if (motionQueueEntry.isTriggeredFadeOut()) {
motionQueueEntry.startFadeOut(
motionQueueEntry.getFadeOutSeconds(),
userTimeSeconds
);
}
i++;
}
}
return updated;
}
_userTimeSeconds: number; // デルタ時間の積算値[秒]
_motions: Array<CubismMotionQueueEntry>; // モーション
_eventCallBack: CubismMotionEventFunction; // コールバック関数
_eventCustomData: any; // コールバックに戻されるデータ
}
/**
* イベントのコールバック関数を定義
*
* イベントのコールバックに登録できる関数の型情報
* @param caller 発火したイベントを再生させたCubismMotionQueueManager
* @param eventValue 発火したイベントの文字列データ
* @param customData コールバックに返される登録時に指定されたデータ
*/
export interface CubismMotionEventFunction {
(caller: CubismMotionQueueManager, eventValue: string, customData: any): void;
}
/**
* モーションの識別番号
*
* モーションの識別番号の定義
*/
export declare type CubismMotionQueueEntryHandle = any;
export const InvalidMotionQueueEntryHandleValue: CubismMotionQueueEntryHandle =
-1;
// Namespace definition for compatibility.
import * as $ from './cubismmotionqueuemanager';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismMotionQueueManager = $.CubismMotionQueueManager;
export type CubismMotionQueueManager = $.CubismMotionQueueManager;
export const InvalidMotionQueueEntryHandleValue =
$.InvalidMotionQueueEntryHandleValue;
export type CubismMotionQueueEntryHandle = $.CubismMotionQueueEntryHandle;
export type CubismMotionEventFunction = $.CubismMotionEventFunction;
}

View File

@@ -0,0 +1,60 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
import { CubismPhysics } from '../physics/cubismphysics';
/**
* Updater for physics effects.
* Handles the management of physics simulation through the CubismPhysics class.
*/
export class CubismPhysicsUpdater extends ICubismUpdater {
private _physics: CubismPhysics;
/**
* Constructor
*
* @param physics CubismPhysics reference
*/
constructor(physics: CubismPhysics);
/**
* Constructor
*
* @param physics CubismPhysics reference
* @param executionOrder Order of operations
*/
constructor(physics: CubismPhysics, executionOrder: number);
constructor(physics: CubismPhysics, executionOrder?: number) {
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Physics);
this._physics = physics;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
this._physics.evaluate(model, deltaTimeSeconds);
}
}
// Namespace definition for compatibility.
import * as $ from './cubismphysicsupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismPhysicsUpdater = $.CubismPhysicsUpdater;
export type CubismPhysicsUpdater = $.CubismPhysicsUpdater;
}

View File

@@ -0,0 +1,60 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, CubismUpdateOrder } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
import { CubismPose } from '../effect/cubismpose';
/**
* Updater for pose effects.
* Handles the management of pose animation through the CubismPose class.
*/
export class CubismPoseUpdater extends ICubismUpdater {
private _pose: CubismPose;
/**
* Constructor
*
* @param pose CubismPose reference
*/
constructor(pose: CubismPose);
/**
* Constructor
*
* @param pose CubismPose reference
* @param executionOrder Order of operations
*/
constructor(pose: CubismPose, executionOrder: number);
constructor(pose: CubismPose, executionOrder?: number) {
super(executionOrder ?? CubismUpdateOrder.CubismUpdateOrder_Pose);
this._pose = pose;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
this._pose.updateParameters(model, deltaTimeSeconds);
}
}
// Namespace definition for compatibility.
import * as $ from './cubismposeupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismPoseUpdater = $.CubismPoseUpdater;
export type CubismPoseUpdater = $.CubismPoseUpdater;
}

View File

@@ -0,0 +1,180 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { ICubismUpdater, ICubismUpdaterChangeListener } from './icubismupdater';
import { CubismModel } from '../model/cubismmodel';
/**
* Scheduler for managing and updating ICubismUpdater instances.
* Handles the management of update order and execution through a sorted list.
*/
export class CubismUpdateScheduler implements ICubismUpdaterChangeListener {
private _cubismUpdatableList: ICubismUpdater[];
private _needsSort: boolean;
/**
* Constructor
*/
constructor() {
this._cubismUpdatableList = [];
this._needsSort = false;
}
/**
* Destructor equivalent - releases all updaters and removes listeners
*/
public release(): void {
// Remove all listeners before clearing
for (const updater of this._cubismUpdatableList) {
if (updater) {
updater.removeChangeListener(this);
}
}
// Clear the list - in TypeScript we don't need to manually delete objects
// as they will be garbage collected when no longer referenced
this._cubismUpdatableList.length = 0;
}
/**
* Adds ICubismUpdater to the update list.
* The list will be automatically sorted by execution order before the next update.
*
* @param updatable The ICubismUpdater instance to be added.
*/
public addUpdatableList(updatable: ICubismUpdater): void {
if (!updatable) {
return;
}
// Check for duplicate registration
if (this.hasUpdatable(updatable)) {
return; // Already exists, skip adding
}
this._cubismUpdatableList.push(updatable);
updatable.addChangeListener(this);
this._needsSort = true;
}
/**
* Removes ICubismUpdater from the update list.
*
* @param updatable The ICubismUpdater instance to be removed.
* @return true if the updater was found and removed, false otherwise.
*/
public removeUpdatableList(updatable: ICubismUpdater): boolean {
if (!updatable) {
return false;
}
const index = this._cubismUpdatableList.indexOf(updatable);
if (index >= 0) {
this._cubismUpdatableList.splice(index, 1);
updatable.removeChangeListener(this);
// Note: removal doesn't require re-sorting
return true;
}
return false;
}
/**
* Sorts the update list using the ICubismUpdater sort function.
*/
public sortUpdatableList(): void {
this._cubismUpdatableList.sort(ICubismUpdater.sortFunction);
this._needsSort = false;
}
/**
* Updates every element in the list.
* The list is automatically sorted by execution order before execution.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
public onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void {
if (!model) {
return;
}
// Automatically sort if needed to ensure execution order
if (this._needsSort) {
this.sortUpdatableList();
}
for (let i = 0; i < this._cubismUpdatableList.length; ++i) {
const updater = this._cubismUpdatableList[i];
if (updater) {
updater.onLateUpdate(model, deltaTimeSeconds);
}
}
}
/**
* Gets the number of updaters in the list.
*
* @return Number of updaters
*/
public getUpdatableCount(): number {
return this._cubismUpdatableList.length;
}
/**
* Gets the updater at the specified index.
*
* @param index Index of the updater to retrieve
* @return The updater at the specified index, or null if index is out of bounds
*/
public getUpdatable(index: number): ICubismUpdater | null {
if (index < 0 || index >= this._cubismUpdatableList.length) {
return null;
}
return this._cubismUpdatableList[index];
}
/**
* Checks if the specified updater exists in the list.
*
* @param updatable The updater to check for
* @return true if the updater exists in the list, false otherwise
*/
public hasUpdatable(updatable: ICubismUpdater): boolean {
return this._cubismUpdatableList.indexOf(updatable) >= 0;
}
/**
* Clears all updaters from the list.
*/
public clearUpdatableList(): void {
// Remove listeners before clearing
for (const updater of this._cubismUpdatableList) {
if (updater) {
updater.removeChangeListener(this);
}
}
this._cubismUpdatableList.length = 0;
this._needsSort = false;
}
/**
* Called when an updater's execution order has changed.
* Marks the list for re-sorting.
*
* @param updater The updater that was changed
*/
public onUpdaterChanged(updater: ICubismUpdater): void {
this._needsSort = true;
}
}
// Namespace definition for compatibility.
import * as $ from './cubismupdatescheduler';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismUpdateScheduler = $.CubismUpdateScheduler;
export type CubismUpdateScheduler = $.CubismUpdateScheduler;
}

View File

@@ -0,0 +1,126 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismModel } from '../model/cubismmodel';
/**
* Interface for listening to ICubismUpdater changes.
*/
export interface ICubismUpdaterChangeListener {
/**
* Called when an updater's execution order has changed.
*
* @param updater The updater that was changed
*/
onUpdaterChanged(updater: ICubismUpdater): void;
}
export enum CubismUpdateOrder {
CubismUpdateOrder_EyeBlink = 200,
CubismUpdateOrder_Expression = 300,
CubismUpdateOrder_Drag = 400,
CubismUpdateOrder_Breath = 500,
CubismUpdateOrder_Physics = 600,
CubismUpdateOrder_LipSync = 700,
CubismUpdateOrder_Pose = 800,
CubismUpdateOrder_Max = Number.MAX_SAFE_INTEGER
}
/**
* Abstract base class for motions.<br>
* Handles the management of motion playback through the CubismUpdateScheduler.
*/
export abstract class ICubismUpdater {
/**
* Comparison function used when sorting ICubismUpdater objects.
*
* @param left The first ICubismUpdater object to be compared.
* @param right The second ICubismUpdater object to be compared.
*
* @return negative if left should be placed before right,
* positive if right should be placed before left,
* zero if they are equal.
*/
static sortFunction(left: ICubismUpdater, right: ICubismUpdater): number {
if (!left || !right) {
if (!left && !right) return 0;
if (!left) return 1; // null/undefined elements go to end
if (!right) return -1;
}
return left.getExecutionOrder() - right.getExecutionOrder();
}
private _executionOrder: number;
private _changeListeners: ICubismUpdaterChangeListener[] = [];
/**
* Constructor
*/
constructor(executionOrder: number = 0) {
this._executionOrder = executionOrder;
}
/**
* Update process.
*
* @param model Model to update
* @param deltaTimeSeconds Delta time in seconds.
*/
abstract onLateUpdate(model: CubismModel, deltaTimeSeconds: number): void;
getExecutionOrder(): number {
return this._executionOrder;
}
setExecutionOrder(executionOrder: number): void {
if (this._executionOrder !== executionOrder) {
this._executionOrder = executionOrder;
this.notifyChangeListeners();
}
}
/**
* Adds a listener to be notified when this updater's properties change.
*
* @param listener The listener to add
*/
addChangeListener(listener: ICubismUpdaterChangeListener): void {
if (listener && this._changeListeners.indexOf(listener) === -1) {
this._changeListeners.push(listener);
}
}
/**
* Removes a listener from the notification list.
*
* @param listener The listener to remove
*/
removeChangeListener(listener: ICubismUpdaterChangeListener): void {
const index = this._changeListeners.indexOf(listener);
if (index >= 0) {
this._changeListeners.splice(index, 1);
}
}
/**
* Notifies all registered listeners that this updater has changed.
*/
private notifyChangeListeners(): void {
for (const listener of this._changeListeners) {
listener.onUpdaterChanged(this);
}
}
}
// Namespace definition for compatibility.
import * as $ from './icubismupdater';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const ICubismUpdater = $.ICubismUpdater;
export type ICubismUpdater = $.ICubismUpdater;
export type ICubismUpdaterChangeListener = $.ICubismUpdaterChangeListener;
}

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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
/**
* Interface class for providing parameter values.<br>
* Defines the base interface for classes that supply parameter values to the model.
*/
export abstract class IParameterProvider {
/**
* Constructor
*/
constructor() {}
/**
* Update process.
*
* @param deltaTimeSeconds Delta time in seconds (optional).
*
* @return true if the update is successful.
*/
abstract update(deltaTimeSeconds?: number): boolean;
/**
* Retrieves the current value of the parameter.
*
* @return The parameter value as a floating-point number.
*/
abstract getParameter(): number;
}
// Namespace definition for compatibility.
import * as $ from './iparameterprovider';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const IParameterProvider = $.IParameterProvider;
export type IParameterProvider = $.IParameterProvider;
}

File diff suppressed because it is too large Load Diff

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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismVector2 } from '../math/cubismvector2';
/**
* 物理演算の適用先の種類
*/
export enum CubismPhysicsTargetType {
CubismPhysicsTargetType_Parameter // パラメータに対して適用
}
/**
* 物理演算の入力の種類
*/
export enum CubismPhysicsSource {
CubismPhysicsSource_X, // X軸の位置から
CubismPhysicsSource_Y, // Y軸の位置から
CubismPhysicsSource_Angle // 角度から
}
/**
* @brief 物理演算で使用する外部の力
*
* 物理演算で使用する外部の力。
*/
export class PhysicsJsonEffectiveForces {
constructor() {
this.gravity = new CubismVector2(0, 0);
this.wind = new CubismVector2(0, 0);
}
gravity: CubismVector2; // 重力
wind: CubismVector2; // 風
}
/**
* 物理演算のパラメータ情報
*/
export class CubismPhysicsParameter {
id: CubismIdHandle; // パラメータ
targetType: CubismPhysicsTargetType; // 適用先の種類
}
/**
* 物理演算の正規化情報
*/
export class CubismPhysicsNormalization {
minimum: number; // 最大値
maximum: number; // 最小値
defalut: number; // デフォルト値
}
/**
* 物理演算の演算委使用する物理点の情報
*/
export class CubismPhysicsParticle {
constructor() {
this.initialPosition = new CubismVector2(0, 0);
this.position = new CubismVector2(0, 0);
this.lastPosition = new CubismVector2(0, 0);
this.lastGravity = new CubismVector2(0, 0);
this.force = new CubismVector2(0, 0);
this.velocity = new CubismVector2(0, 0);
}
initialPosition: CubismVector2; // 初期位置
mobility: number; // 動きやすさ
delay: number; // 遅れ
acceleration: number; // 加速度
radius: number; // 距離
position: CubismVector2; // 現在の位置
lastPosition: CubismVector2; // 最後の位置
lastGravity: CubismVector2; // 最後の重力
force: CubismVector2; // 現在かかっている力
velocity: CubismVector2; // 現在の速度
}
/**
* 物理演算の物理点の管理
*/
export class CubismPhysicsSubRig {
constructor() {
this.normalizationPosition = new CubismPhysicsNormalization();
this.normalizationAngle = new CubismPhysicsNormalization();
}
inputCount: number; // 入力の個数
outputCount: number; // 出力の個数
particleCount: number; // 物理点の個数
baseInputIndex: number; // 入力の最初のインデックス
baseOutputIndex: number; // 出力の最初のインデックス
baseParticleIndex: number; // 物理点の最初のインデックス
normalizationPosition: CubismPhysicsNormalization; // 正規化された位置
normalizationAngle: CubismPhysicsNormalization; // 正規化された角度
}
/**
* 正規化されたパラメータの取得関数の宣言
* @param targetTranslation // 演算結果の移動値
* @param targetAngle // 演算結果の角度
* @param value // パラメータの値
* @param parameterMinimunValue // パラメータの最小値
* @param parameterMaximumValue // パラメータの最大値
* @param parameterDefaultValue // パラメータのデフォルト値
* @param normalizationPosition // 正規化された位置
* @param normalizationAngle // 正規化された角度
* @param isInverted // 値が反転されているか?
* @param weight // 重み
*/
export interface normalizedPhysicsParameterValueGetter {
(
targetTranslation: CubismVector2,
targetAngle: { angle: number },
value: number,
parameterMinimunValue: number,
parameterMaximumValue: number,
parameterDefaultValue: number,
normalizationPosition: CubismPhysicsNormalization,
normalizationAngle: CubismPhysicsNormalization,
isInverted: boolean,
weight: number
): void;
}
/**
* 物理演算の値の取得関数の宣言
* @param translation 移動値
* @param particles 物理点のリスト
* @param isInverted 値が反映されているか
* @param parentGravity 重力
* @return 値
*/
export interface physicsValueGetter {
(
translation: CubismVector2,
particles: CubismPhysicsParticle[],
particleIndex: number,
isInverted: boolean,
parentGravity: CubismVector2
): number;
}
/**
* 物理演算のスケールの取得関数の宣言
* @param translationScale 移動値のスケール
* @param angleScale 角度のスケール
* @return スケール値
*/
export interface physicsScaleGetter {
(translationScale: CubismVector2, angleScale: number): number;
}
/**
* 物理演算の入力情報
*/
export class CubismPhysicsInput {
constructor() {
this.source = new CubismPhysicsParameter();
}
source: CubismPhysicsParameter; // 入力元のパラメータ
sourceParameterIndex: number; // 入力元のパラメータのインデックス
weight: number; // 重み
type: number; // 入力の種類
reflect: boolean; // 値が反転されているかどうか
getNormalizedParameterValue: normalizedPhysicsParameterValueGetter; // 正規化されたパラメータ値の取得関数
}
/**
* @brief 物理演算の出力情報
*
* 物理演算の出力情報。
*/
export class CubismPhysicsOutput {
constructor() {
this.destination = new CubismPhysicsParameter();
this.translationScale = new CubismVector2(0, 0);
}
destination: CubismPhysicsParameter; // 出力先のパラメータ
destinationParameterIndex: number; // 出力先のパラメータのインデックス
vertexIndex: number; // 振り子のインデックス
translationScale: CubismVector2; // 移動値のスケール
angleScale: number; // 角度のスケール
weight: number; // 重み
type: CubismPhysicsSource; // 出力の種類
reflect: boolean; // 値が反転されているかどうか
valueBelowMinimum: number; // 最小値を下回った時の値
valueExceededMaximum: number; // 最大値をこえた時の値
getValue: physicsValueGetter; // 物理演算の値の取得関数
getScale: physicsScaleGetter; // 物理演算のスケール値の取得関数
}
/**
* @brief 物理演算のデータ
*
* 物理演算のデータ。
*/
export class CubismPhysicsRig {
constructor() {
this.settings = new Array<CubismPhysicsSubRig>();
this.inputs = new Array<CubismPhysicsInput>();
this.outputs = new Array<CubismPhysicsOutput>();
this.particles = new Array<CubismPhysicsParticle>();
this.gravity = new CubismVector2(0, 0);
this.wind = new CubismVector2(0, 0);
this.fps = 0.0;
}
subRigCount: number; // 物理演算の物理点の個数
settings: Array<CubismPhysicsSubRig>; // 物理演算の物理点の管理のリスト
inputs: Array<CubismPhysicsInput>; // 物理演算の入力のリスト
outputs: Array<CubismPhysicsOutput>; // 物理演算の出力のリスト
particles: Array<CubismPhysicsParticle>; // 物理演算の物理点のリスト
gravity: CubismVector2; // 重力
wind: CubismVector2; // 風
fps: number; //物理演算動作FPS
}
// Namespace definition for compatibility.
import * as $ from './cubismphysicsinternal';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismPhysicsInput = $.CubismPhysicsInput;
export type CubismPhysicsInput = $.CubismPhysicsInput;
export const CubismPhysicsNormalization = $.CubismPhysicsNormalization;
export type CubismPhysicsNormalization = $.CubismPhysicsNormalization;
export const CubismPhysicsOutput = $.CubismPhysicsOutput;
export type CubismPhysicsOutput = $.CubismPhysicsOutput;
export const CubismPhysicsParameter = $.CubismPhysicsParameter;
export type CubismPhysicsParameter = $.CubismPhysicsParameter;
export const CubismPhysicsParticle = $.CubismPhysicsParticle;
export type CubismPhysicsParticle = $.CubismPhysicsParticle;
export const CubismPhysicsRig = $.CubismPhysicsRig;
export type CubismPhysicsRig = $.CubismPhysicsRig;
export const CubismPhysicsSource = $.CubismPhysicsSource;
export type CubismPhysicsSource = $.CubismPhysicsSource;
export const CubismPhysicsSubRig = $.CubismPhysicsSubRig;
export type CubismPhysicsSubRig = $.CubismPhysicsSubRig;
export const CubismPhysicsTargetType = $.CubismPhysicsTargetType;
export type CubismPhysicsTargetType = $.CubismPhysicsTargetType;
export const PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces;
export type PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces;
export type normalizedPhysicsParameterValueGetter =
$.normalizedPhysicsParameterValueGetter;
export type physicsScaleGetter = $.physicsScaleGetter;
export type physicsValueGetter = $.physicsValueGetter;
}

View File

@@ -0,0 +1,658 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismIdHandle } from '../id/cubismid';
import { CubismFramework } from '../live2dcubismframework';
import { CubismVector2 } from '../math/cubismvector2';
import { CubismJson } from '../utils/cubismjson';
// JSON keys
const Position = 'Position';
const X = 'X';
const Y = 'Y';
const Angle = 'Angle';
const Type = 'Type';
const Id = 'Id';
// Meta
const Meta = 'Meta';
const EffectiveForces = 'EffectiveForces';
const TotalInputCount = 'TotalInputCount';
const TotalOutputCount = 'TotalOutputCount';
const PhysicsSettingCount = 'PhysicsSettingCount';
const Gravity = 'Gravity';
const Wind = 'Wind';
const VertexCount = 'VertexCount';
const Fps = 'Fps';
// PhysicsSettings
const PhysicsSettings = 'PhysicsSettings';
const Normalization = 'Normalization';
const Minimum = 'Minimum';
const Maximum = 'Maximum';
const Default = 'Default';
const Reflect = 'Reflect';
const Weight = 'Weight';
// Input
const Input = 'Input';
const Source = 'Source';
// Output
const Output = 'Output';
const Scale = 'Scale';
const VertexIndex = 'VertexIndex';
const Destination = 'Destination';
// Particle
const Vertices = 'Vertices';
const Mobility = 'Mobility';
const Delay = 'Delay';
const Radius = 'Radius';
const Acceleration = 'Acceleration';
/**
* physics3.jsonのコンテナ。
*/
export class CubismPhysicsJson {
/**
* コンストラクタ
* @param buffer physics3.jsonが読み込まれているバッファ
* @param size バッファのサイズ
*/
public constructor(buffer: ArrayBuffer, size: number) {
this._json = CubismJson.create(buffer, size);
}
/**
* デストラクタ相当の処理
*/
public release(): void {
CubismJson.delete(this._json);
}
/**
* 重力の取得
* @return 重力
*/
public getGravity(): CubismVector2 {
const ret: CubismVector2 = new CubismVector2(0, 0);
ret.x = this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(EffectiveForces)
.getValueByString(Gravity)
.getValueByString(X)
.toFloat();
ret.y = this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(EffectiveForces)
.getValueByString(Gravity)
.getValueByString(Y)
.toFloat();
return ret;
}
/**
* 風の取得
* @return 風
*/
public getWind(): CubismVector2 {
const ret: CubismVector2 = new CubismVector2(0, 0);
ret.x = this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(EffectiveForces)
.getValueByString(Wind)
.getValueByString(X)
.toFloat();
ret.y = this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(EffectiveForces)
.getValueByString(Wind)
.getValueByString(Y)
.toFloat();
return ret;
}
/**
* 物理演算設定FPSの取得
* @return 物理演算設定FPS
*/
public getFps(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(Fps)
.toFloat(0.0);
}
/**
* 物理店の管理の個数の取得
* @return 物理店の管理の個数
*/
public getSubRigCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(PhysicsSettingCount)
.toInt();
}
/**
* 入力の総合計の取得
* @return 入力の総合計
*/
public getTotalInputCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalInputCount)
.toInt();
}
/**
* 出力の総合計の取得
* @return 出力の総合計
*/
public getTotalOutputCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(TotalOutputCount)
.toInt();
}
/**
* 物理点の個数の取得
* @return 物理点の個数
*/
public getVertexCount(): number {
return this._json
.getRoot()
.getValueByString(Meta)
.getValueByString(VertexCount)
.toInt();
}
/**
* 正規化された位置の最小値の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @return 正規化された位置の最小値
*/
public getNormalizationPositionMinimumValue(
physicsSettingIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Normalization)
.getValueByString(Position)
.getValueByString(Minimum)
.toFloat();
}
/**
* 正規化された位置の最大値の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @return 正規化された位置の最大値
*/
public getNormalizationPositionMaximumValue(
physicsSettingIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Normalization)
.getValueByString(Position)
.getValueByString(Maximum)
.toFloat();
}
/**
* 正規化された位置のデフォルト値の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @return 正規化された位置のデフォルト値
*/
public getNormalizationPositionDefaultValue(
physicsSettingIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Normalization)
.getValueByString(Position)
.getValueByString(Default)
.toFloat();
}
/**
* 正規化された角度の最小値の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @return 正規化された角度の最小値
*/
public getNormalizationAngleMinimumValue(
physicsSettingIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Normalization)
.getValueByString(Angle)
.getValueByString(Minimum)
.toFloat();
}
/**
* 正規化された角度の最大値の取得
* @param physicsSettingIndex
* @return 正規化された角度の最大値
*/
public getNormalizationAngleMaximumValue(
physicsSettingIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Normalization)
.getValueByString(Angle)
.getValueByString(Maximum)
.toFloat();
}
/**
* 正規化された角度のデフォルト値の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @return 正規化された角度のデフォルト値
*/
public getNormalizationAngleDefaultValue(
physicsSettingIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Normalization)
.getValueByString(Angle)
.getValueByString(Default)
.toFloat();
}
/**
* 入力の個数の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @return 入力の個数
*/
public getInputCount(physicsSettingIndex: number): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Input)
.getVector().length;
}
/**
* 入力の重みの取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param inputIndex 入力のインデックス
* @return 入力の重み
*/
public getInputWeight(
physicsSettingIndex: number,
inputIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Input)
.getValueByIndex(inputIndex)
.getValueByString(Weight)
.toFloat();
}
/**
* 入力の反転の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param inputIndex 入力のインデックス
* @return 入力の反転
*/
public getInputReflect(
physicsSettingIndex: number,
inputIndex: number
): boolean {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Input)
.getValueByIndex(inputIndex)
.getValueByString(Reflect)
.toBoolean();
}
/**
* 入力の種類の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param inputIndex 入力のインデックス
* @return 入力の種類
*/
public getInputType(physicsSettingIndex: number, inputIndex: number): string {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Input)
.getValueByIndex(inputIndex)
.getValueByString(Type)
.getRawString();
}
/**
* 入力元のIDの取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param inputIndex 入力のインデックス
* @return 入力元のID
*/
public getInputSourceId(
physicsSettingIndex: number,
inputIndex: number
): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Input)
.getValueByIndex(inputIndex)
.getValueByString(Source)
.getValueByString(Id)
.getRawString()
);
}
/**
* 出力の個数の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @return 出力の個数
*/
public getOutputCount(physicsSettingIndex: number): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Output)
.getVector().length;
}
/**
* 出力の物理点のインデックスの取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param outputIndex 出力のインデックス
* @return 出力の物理点のインデックス
*/
public getOutputVertexIndex(
physicsSettingIndex: number,
outputIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Output)
.getValueByIndex(outputIndex)
.getValueByString(VertexIndex)
.toInt();
}
/**
* 出力の角度のスケールを取得する
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param outputIndex 出力のインデックス
* @return 出力の角度のスケール
*/
public getOutputAngleScale(
physicsSettingIndex: number,
outputIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Output)
.getValueByIndex(outputIndex)
.getValueByString(Scale)
.toFloat();
}
/**
* 出力の重みの取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param outputIndex 出力のインデックス
* @return 出力の重み
*/
public getOutputWeight(
physicsSettingIndex: number,
outputIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Output)
.getValueByIndex(outputIndex)
.getValueByString(Weight)
.toFloat();
}
/**
* 出力先のIDの取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param outputIndex 出力のインデックス
* @return 出力先のID
*/
public getOutputDestinationId(
physicsSettingIndex: number,
outputIndex: number
): CubismIdHandle {
return CubismFramework.getIdManager().getId(
this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Output)
.getValueByIndex(outputIndex)
.getValueByString(Destination)
.getValueByString(Id)
.getRawString()
);
}
/**
* 出力の種類の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param outputIndex 出力のインデックス
* @return 出力の種類
*/
public getOutputType(
physicsSettingIndex: number,
outputIndex: number
): string {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Output)
.getValueByIndex(outputIndex)
.getValueByString(Type)
.getRawString();
}
/**
* 出力の反転の取得
* @param physicsSettingIndex 物理演算のインデックス
* @param outputIndex 出力のインデックス
* @return 出力の反転
*/
public getOutputReflect(
physicsSettingIndex: number,
outputIndex: number
): boolean {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Output)
.getValueByIndex(outputIndex)
.getValueByString(Reflect)
.toBoolean();
}
/**
* 物理点の個数の取得
* @param physicsSettingIndex 物理演算男設定のインデックス
* @return 物理点の個数
*/
public getParticleCount(physicsSettingIndex: number): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Vertices)
.getVector().length;
}
/**
* 物理点の動きやすさの取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param vertexIndex 物理点のインデックス
* @return 物理点の動きやすさ
*/
public getParticleMobility(
physicsSettingIndex: number,
vertexIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Vertices)
.getValueByIndex(vertexIndex)
.getValueByString(Mobility)
.toFloat();
}
/**
* 物理点の遅れの取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param vertexIndex 物理点のインデックス
* @return 物理点の遅れ
*/
public getParticleDelay(
physicsSettingIndex: number,
vertexIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Vertices)
.getValueByIndex(vertexIndex)
.getValueByString(Delay)
.toFloat();
}
/**
* 物理点の加速度の取得
* @param physicsSettingIndex 物理演算の設定
* @param vertexIndex 物理点のインデックス
* @return 物理点の加速度
*/
public getParticleAcceleration(
physicsSettingIndex: number,
vertexIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Vertices)
.getValueByIndex(vertexIndex)
.getValueByString(Acceleration)
.toFloat();
}
/**
* 物理点の距離の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param vertexIndex 物理点のインデックス
* @return 物理点の距離
*/
public getParticleRadius(
physicsSettingIndex: number,
vertexIndex: number
): number {
return this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Vertices)
.getValueByIndex(vertexIndex)
.getValueByString(Radius)
.toFloat();
}
/**
* 物理点の位置の取得
* @param physicsSettingIndex 物理演算の設定のインデックス
* @param vertexInde 物理点のインデックス
* @return 物理点の位置
*/
public getParticlePosition(
physicsSettingIndex: number,
vertexIndex: number
): CubismVector2 {
const ret: CubismVector2 = new CubismVector2(0, 0);
ret.x = this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Vertices)
.getValueByIndex(vertexIndex)
.getValueByString(Position)
.getValueByString(X)
.toFloat();
ret.y = this._json
.getRoot()
.getValueByString(PhysicsSettings)
.getValueByIndex(physicsSettingIndex)
.getValueByString(Vertices)
.getValueByIndex(vertexIndex)
.getValueByString(Position)
.getValueByString(Y)
.toFloat();
return ret;
}
_json: CubismJson; // physics3.jsonデータ
}
// Namespace definition for compatibility.
import * as $ from './cubismphysicsjson';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismPhysicsJson = $.CubismPhysicsJson;
export type CubismPhysicsJson = $.CubismPhysicsJson;
}

View File

@@ -0,0 +1,994 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { Constant } from '../live2dcubismframework';
import { csmRect } from '../type/csmrectf';
import { CubismMatrix44 } from '../math/cubismmatrix44';
import { CubismModel } from '../model/cubismmodel';
import { CubismClippingContext, CubismTextureColor } from './cubismrenderer';
import { CubismLogError, CubismLogWarning } from '../utils/cubismdebug';
const ColorChannelCount = 4; // 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4
const ClippingMaskMaxCountOnDefault = 36; // 通常のフレームバッファ一枚あたりのマスク最大数
const ClippingMaskMaxCountOnMultiRenderTexture = 32; // フレームバッファが2枚以上ある場合のフレームバッファ一枚あたりのマスク最大数
export type ClippingContextConstructor<
T_ClippingContext extends CubismClippingContext
> = new (
manager: CubismClippingManager<T_ClippingContext>,
drawableMasks: Int32Array,
drawableMaskCounts: number
) => T_ClippingContext;
export interface ICubismClippingManager {
getClippingMaskBufferSize(): number;
}
export abstract class CubismClippingManager<
T_ClippingContext extends CubismClippingContext
> implements ICubismClippingManager {
/**
* コンストラクタ
*/
public constructor(
clippingContextFactory: ClippingContextConstructor<T_ClippingContext>
) {
this._renderTextureCount = 0;
this._clippingMaskBufferSize = 256;
this._clippingContextListForMask = new Array<T_ClippingContext>();
this._clippingContextListForDraw = new Array<T_ClippingContext>();
this._clippingContextListForOffscreen = new Array<T_ClippingContext>();
this._tmpBoundsOnModel = new csmRect();
this._tmpMatrix = new CubismMatrix44();
this._tmpMatrixForMask = new CubismMatrix44();
this._tmpMatrixForDraw = new CubismMatrix44();
this._clearedMaskBufferFlags = new Array<boolean>();
this._clippingContexttConstructor = clippingContextFactory;
this._channelColors = [
new CubismTextureColor(1.0, 0.0, 0.0, 0.0),
new CubismTextureColor(0.0, 1.0, 0.0, 0.0),
new CubismTextureColor(0.0, 0.0, 1.0, 0.0),
new CubismTextureColor(0.0, 0.0, 0.0, 1.0)
];
}
/**
* デストラクタ相当の処理
*/
public release(): void {
for (let i = 0; i < this._clippingContextListForMask.length; i++) {
if (this._clippingContextListForMask[i]) {
this._clippingContextListForMask[i].release();
this._clippingContextListForMask[i] = void 0;
}
this._clippingContextListForMask[i] = null;
}
this._clippingContextListForMask = null;
// _clippingContextListForDrawは_clippingContextListForMaskにあるインスタンスを指している。上記の処理により要素ごとのDELETEは不要。
for (let i = 0; i < this._clippingContextListForDraw.length; i++) {
this._clippingContextListForDraw[i] = null;
}
this._clippingContextListForDraw = null;
for (let i = 0; i < this._channelColors.length; i++) {
this._channelColors[i] = null;
}
this._channelColors = null;
if (this._clearedMaskBufferFlags != null) {
this._clearedMaskBufferFlags.length = 0;
}
this._clearedMaskBufferFlags = null;
}
/**
* マネージャの初期化処理
* クリッピングマスクを使う描画オブジェクトの登録を行う
* @param model モデルのインスタンス
* @param renderTextureCount バッファの生成数
*/
public initializeForDrawable(
model: CubismModel,
renderTextureCount: number
): void {
// レンダーテクスチャの合計枚数の設定
// 1以上の整数でない場合はそれぞれ警告を出す
if (renderTextureCount % 1 != 0) {
CubismLogWarning(
'The number of render textures must be specified as an integer. The decimal point is rounded down and corrected to an integer.'
);
// 小数点以下を除去
renderTextureCount = ~~renderTextureCount;
}
if (renderTextureCount < 1) {
CubismLogWarning(
'The number of render textures must be an integer greater than or equal to 1. Set the number of render textures to 1.'
);
}
// 負の値が使われている場合は強制的に1枚と設定する
this._renderTextureCount = renderTextureCount < 1 ? 1 : renderTextureCount;
this._clearedMaskBufferFlags = new Array<boolean>(this._renderTextureCount);
// クリッピングマスクを使う描画オブジェクトをすべて登録する
// クリッピングマスクは、通常数個程度に限定して使うものとする
this._clippingContextListForDraw.length = model.getDrawableCount();
for (let i = 0; i < model.getDrawableCount(); i++) {
if (model.getDrawableMaskCounts()[i] <= 0) {
// クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない)
this._clippingContextListForDraw[i] = null;
continue;
}
// 既にあるClipContextと同じかチェックする
let clippingContext: T_ClippingContext = this.findSameClip(
model.getDrawableMasks()[i],
model.getDrawableMaskCounts()[i]
);
if (clippingContext == null) {
// 同一のマスクが存在していない場合は生成する
clippingContext = new this._clippingContexttConstructor(
this,
model.getDrawableMasks()[i],
model.getDrawableMaskCounts()[i]
);
this._clippingContextListForMask.push(clippingContext);
}
clippingContext.addClippedDrawable(i);
this._clippingContextListForDraw[i] = clippingContext;
}
}
/**
* オフスクリーン用の初期化処理
*
* @param model モデルのインスタンス
* @param maskBufferCount オフスクリーン用のマスクバッファの数
*/
public initializeForOffscreen(
model: CubismModel,
maskBufferCount: number
): void {
this._renderTextureCount = maskBufferCount;
// レンダーテクスチャのクリアフラグの設定
this._clearedMaskBufferFlags.length = this._renderTextureCount;
for (let i = 0; i < this._renderTextureCount; ++i) {
this._clearedMaskBufferFlags[i] = false;
}
//クリッピングマスクを使う描画オブジェクトを全て登録する
//クリッピングマスクは、通常数個程度に限定して使うものとする
this._clippingContextListForOffscreen.length = model.getOffscreenCount();
for (let i = 0; i < model.getOffscreenCount(); ++i) {
if (model.getOffscreenMaskCounts()[i] <= 0) {
//クリッピングマスクが使用されていないオフスクリーン(多くの場合使用しない)
this._clippingContextListForOffscreen.push(null);
continue;
}
// 既にあるClipContextと同じかチェックする
let cc = this.findSameClip(
model.getOffscreenMasks()[i],
model.getOffscreenMaskCounts()[i]
);
if (cc == null) {
// 同一のマスクが存在していない場合は生成する
cc = new this._clippingContexttConstructor(
this,
model.getOffscreenMasks()[i],
model.getOffscreenMaskCounts()[i]
);
this._clippingContextListForMask.push(cc);
}
cc.addClippedOffscreen(i);
this._clippingContextListForOffscreen[i] = cc;
}
}
/**
* 既にマスクを作っているかを確認
* 作っている様であれば該当するクリッピングマスクのインスタンスを返す
* 作っていなければNULLを返す
* @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのリスト
* @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数
* @return 該当するクリッピングマスクが存在すればインスタンスを返し、なければNULLを返す
*/
public findSameClip(
drawableMasks: Int32Array,
drawableMaskCounts: number
): T_ClippingContext {
// 作成済みClippingContextと一致するか確認
for (let i = 0; i < this._clippingContextListForMask.length; i++) {
const clippingContext: T_ClippingContext =
this._clippingContextListForMask[i];
const count: number = clippingContext._clippingIdCount;
// 個数が違う場合は別物
if (count != drawableMaskCounts) {
continue;
}
let sameCount = 0;
// 同じIDを持つか確認。配列の数が同じなので、一致した個数が同じなら同じ物を持つとする
for (let j = 0; j < count; j++) {
const clipId: number = clippingContext._clippingIdList[j];
for (let k = 0; k < count; k++) {
if (drawableMasks[k] == clipId) {
sameCount++;
break;
}
}
}
if (sameCount == count) {
return clippingContext;
}
}
return null; // 見つからなかった
}
/**
* 高精細マスク処理用の行列を計算する
* @param model モデルのインスタンス
* @param isRightHanded 処理が右手系であるか
*/
public setupMatrixForHighPrecision(
model: CubismModel,
isRightHanded: boolean
): void {
// 全てのクリッピングを用意する
// 同じクリップ複数の場合はまとめて一つのクリップを使う場合は1度だけ設定する
let usingClipCount = 0;
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.length;
clipIndex++
) {
// 1つのクリッピングマスクに関して
const cc: T_ClippingContext = this._clippingContextListForMask[clipIndex];
// このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
this.calcClippedDrawableTotalBounds(model, cc);
if (cc._isUsing) {
usingClipCount++; // 使用中としてカウント
}
}
// マスク行列作成処理
if (usingClipCount > 0) {
this.setupLayoutBounds(0);
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる
if (this._clearedMaskBufferFlags.length != this._renderTextureCount) {
this._clearedMaskBufferFlags.length = this._renderTextureCount;
for (let i = 0; i < this._renderTextureCount; i++) {
this._clearedMaskBufferFlags[i] = false;
}
} else {
// マスクのクリアフラグを毎フレーム開始時に初期化
for (let i = 0; i < this._renderTextureCount; i++) {
this._clearedMaskBufferFlags[i] = false;
}
}
// 実際にマスクを生成する
// 全てのマスクをどの様にレイアウトして描くかを決定し、ClipContext , ClippedDrawContext に記憶する
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.length;
clipIndex++
) {
// --- 実際に1つのマスクを描く ---
const clipContext: T_ClippingContext =
this._clippingContextListForMask[clipIndex];
const allClippedDrawRect: csmRect = clipContext._allClippedDrawRect; //このマスクを使う、全ての描画オブジェクトの論理座標上の囲み矩形
const layoutBoundsOnTex01 = clipContext._layoutBounds; //この中にマスクを収める
const margin = 0.05;
let scaleX = 0.0;
let scaleY = 0.0;
const ppu: number = model.getPixelsPerUnit();
const maskPixelSize: number = clipContext
.getClippingManager()
.getClippingMaskBufferSize();
const physicalMaskWidth: number =
layoutBoundsOnTex01.width * maskPixelSize;
const physicalMaskHeight: number =
layoutBoundsOnTex01.height * maskPixelSize;
this._tmpBoundsOnModel.setRect(allClippedDrawRect);
if (this._tmpBoundsOnModel.width * ppu > physicalMaskWidth) {
this._tmpBoundsOnModel.expand(allClippedDrawRect.width * margin, 0.0);
scaleX = layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
} else {
scaleX = ppu / physicalMaskWidth;
}
if (this._tmpBoundsOnModel.height * ppu > physicalMaskHeight) {
this._tmpBoundsOnModel.expand(
0.0,
allClippedDrawRect.height * margin
);
scaleY = layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
} else {
scaleY = ppu / physicalMaskHeight;
}
// マスク生成時に使う行列を求める
this.createMatrixForMask(
isRightHanded,
layoutBoundsOnTex01,
scaleX,
scaleY
);
clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray());
clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());
}
}
}
/**
* オフスクリーンの高精細マスク処理用の行列を計算する
*
* @param model モデルのインスタンス
* @param isRightHanded 処理が右手系であるか
* @param mvp モデルビュー投影行列
*/
public setupMatrixForOffscreenHighPrecision(
model: CubismModel,
isRightHanded: boolean,
mvp: CubismMatrix44
): void {
// 全てのクリッピングを用意する
// 同じクリップ(複数の場合はまとめて1つのクリップ)を使う場合は1度だけ設定する
let usingClipCount = 0;
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.length;
clipIndex++
) {
// 1つのクリッピングマスクに関して
const cc: T_ClippingContext = this._clippingContextListForMask[clipIndex];
// このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
this.calcClippedOffscreenTotalBounds(model, cc);
if (cc._isUsing) {
usingClipCount++; //使用中としてカウント
}
}
if (usingClipCount <= 0) {
return;
}
// マスク行列作成処理
this.setupLayoutBounds(0);
// サイズがレンダーテクスチャの枚数と合わない場合は合わせる
if (this._clearedMaskBufferFlags.length != this._renderTextureCount) {
this._clearedMaskBufferFlags.length = this._renderTextureCount;
for (let i = 0; i < this._renderTextureCount; ++i) {
this._clearedMaskBufferFlags[i] = false;
}
} else {
// マスクのクリアフラグを毎フレーム開始時に初期化
for (let i = 0; i < this._renderTextureCount; ++i) {
this._clearedMaskBufferFlags[i] = false;
}
}
// 実際にマスクを生成する
// 全てのマスクをどの様にレイアウトして描くかを決定し、ClipContext , ClippedDrawContext に記憶する
for (
let clipIndex = 0;
clipIndex < this._clippingContextListForMask.length;
clipIndex++
) {
// --- 実際に1つのマスクを描く ---
const clipContext = this._clippingContextListForMask[clipIndex];
const allClippedDrawRect = clipContext._allClippedDrawRect; //このマスクを使う、全ての描画オブジェクトの論理座標上の囲み矩形
const layoutBoundsOnTex01 = clipContext._layoutBounds; //この中にマスクを収める
const margin = 0.05;
let scaleX = 0.0;
let scaleY = 0.0;
const ppu = model.getPixelsPerUnit();
const maskPixel = clipContext
.getClippingManager()
.getClippingMaskBufferSize();
const physicalMaskWidth = layoutBoundsOnTex01.width * maskPixel;
const physicalMaskHeight = layoutBoundsOnTex01.height * maskPixel;
this._tmpBoundsOnModel.setRect(allClippedDrawRect);
if (this._tmpBoundsOnModel.width * ppu > physicalMaskWidth) {
this._tmpBoundsOnModel.expand(allClippedDrawRect.width * margin, 0.0);
scaleX = layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
} else {
scaleX = ppu / physicalMaskWidth;
}
if (this._tmpBoundsOnModel.height * ppu > physicalMaskHeight) {
this._tmpBoundsOnModel.expand(0.0, allClippedDrawRect.height * margin);
scaleY = layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
} else {
scaleY = ppu / physicalMaskHeight;
}
// マスク生成時に使う行列を求める
this.createMatrixForMask(
isRightHanded,
layoutBoundsOnTex01,
scaleX,
scaleY
);
clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray());
clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());
// clipContext * mvp^-1
const invertMvp = mvp.getInvert();
clipContext._matrixForDraw.multiplyByMatrix(invertMvp);
}
}
/**
* マスクを使う描画オブジェクトの全体の矩形を計算する。
*
* @param model モデルのインスタンス
* @param clippingContext クリッピングコンテキスト
*/
public calcClippedOffscreenTotalBounds(
model: CubismModel,
clippingContext: T_ClippingContext
): void {
// 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形
let clippedDrawTotalMinX = Number.MAX_VALUE,
clippedDrawTotalMinY = Number.MAX_VALUE;
let clippedDrawTotalMaxX = -Number.MAX_VALUE,
clippedDrawTotalMaxY = -Number.MAX_VALUE;
// このマスクが実際に必要か判定する
// このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある
const clippedOffscreenCount =
clippingContext._clippedOffscreenIndexList.length;
const clippedOffscreenChildDrawableIndexList = new Array<number>();
for (
let clippedOffscreenIndex = 0;
clippedOffscreenIndex < clippedOffscreenCount;
clippedOffscreenIndex++
) {
// マスクを使用する描画オブジェクトの描画される矩形を求める
const offscreenIndex =
clippingContext._clippedOffscreenIndexList[clippedOffscreenIndex];
this.getOffscreenChildDrawableIndexList(
model,
offscreenIndex,
clippedOffscreenChildDrawableIndexList
);
}
const childDrawableCount = clippedOffscreenChildDrawableIndexList.length;
for (
let childDrawableIndex = 0;
childDrawableIndex < childDrawableCount;
childDrawableIndex++
) {
const drawableVertexCount = model.getDrawableVertexCount(
clippedOffscreenChildDrawableIndexList[childDrawableIndex]
);
const drawableVertexes = model.getDrawableVertices(
clippedOffscreenChildDrawableIndexList[childDrawableIndex]
);
let minX = Number.MAX_VALUE,
minY = Number.MAX_VALUE;
let maxX = -Number.MAX_VALUE,
maxY = -Number.MAX_VALUE;
const loop = drawableVertexCount * Constant.vertexStep;
for (
let pi = Constant.vertexOffset;
pi < loop;
pi += Constant.vertexStep
) {
const x = drawableVertexes[pi];
const y = drawableVertexes[pi + 1];
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
if (minX == Number.MAX_VALUE) continue; //有効な点がひとつも取れなかったのでスキップする
// 全体の矩形に反映
if (minX < clippedDrawTotalMinX) clippedDrawTotalMinX = minX;
if (minY < clippedDrawTotalMinY) clippedDrawTotalMinY = minY;
if (maxX > clippedDrawTotalMaxX) clippedDrawTotalMaxX = maxX;
if (maxY > clippedDrawTotalMaxY) clippedDrawTotalMaxY = maxY;
}
if (clippedDrawTotalMinX == Number.MAX_VALUE) {
clippingContext._allClippedDrawRect.x = 0.0;
clippingContext._allClippedDrawRect.y = 0.0;
clippingContext._allClippedDrawRect.width = 0.0;
clippingContext._allClippedDrawRect.height = 0.0;
clippingContext._isUsing = false;
} else {
clippingContext._isUsing = true;
const w = clippedDrawTotalMaxX - clippedDrawTotalMinX;
const h = clippedDrawTotalMaxY - clippedDrawTotalMinY;
clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX;
clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY;
clippingContext._allClippedDrawRect.width = w;
clippingContext._allClippedDrawRect.height = h;
}
}
/**
* マスクを使う描画オブジェクトの全体の矩形を計算する。
*
* @param model モデルのインスタンス
* @param offscreenIndex オフスクリーンのインデックス
* @param childDrawableIndexList オフスクリーンの子Drawableのインデックスリスト
*/
public getOffscreenChildDrawableIndexList(
model: CubismModel,
offscreenIndex: number,
childDrawableIndexList: Array<number>
): void {
// 親オブジェクトを取得
const ownerIndex = model.getOffscreenOwnerIndices()[offscreenIndex];
// パーツのみ
this.getPartChildDrawableIndexList(
model,
ownerIndex,
childDrawableIndexList
);
}
/**
* パーツの子Drawableのインデックスリストを取得する。
*
* @param model モデルのインスタンス
* @param partIndex パーツのインデックス
* @param childDrawableIndexList パーツの子Drawableのインデックスリスト
*/
public getPartChildDrawableIndexList(
model: CubismModel,
partIndex: number,
childDrawableIndexList: Array<number>
): void {
const childDrawObjects =
model.getPartsHierarchy()[partIndex].childDrawObjects;
childDrawableIndexList.push(...childDrawObjects.drawableIndices);
for (let i = 0; i < childDrawObjects.offscreenIndices.length; ++i) {
this.getOffscreenChildDrawableIndexList(
model,
childDrawObjects.offscreenIndices[i],
childDrawableIndexList
);
}
}
/**
* マスク作成・描画用の行列を作成する。
* @param isRightHanded 座標を右手系として扱うかを指定
* @param layoutBoundsOnTex01 マスクを収める領域
* @param scaleX 描画オブジェクトの伸縮率
* @param scaleY 描画オブジェクトの伸縮率
*/
public createMatrixForMask(
isRightHanded: boolean,
layoutBoundsOnTex01: csmRect,
scaleX: number,
scaleY: number
): void {
this._tmpMatrix.loadIdentity();
{
// Layout0..1 を -1..1に変換
this._tmpMatrix.translateRelative(-1.0, -1.0);
this._tmpMatrix.scaleRelative(2.0, 2.0);
}
{
// view to Layout0..1
this._tmpMatrix.translateRelative(
layoutBoundsOnTex01.x,
layoutBoundsOnTex01.y
); //new = [translate]
this._tmpMatrix.scaleRelative(scaleX, scaleY); //new = [translate][scale]
this._tmpMatrix.translateRelative(
-this._tmpBoundsOnModel.x,
-this._tmpBoundsOnModel.y
); //new = [translate][scale][translate]
}
// tmpMatrixForMask が計算結果
this._tmpMatrixForMask.setMatrix(this._tmpMatrix.getArray());
this._tmpMatrix.loadIdentity();
{
this._tmpMatrix.translateRelative(
layoutBoundsOnTex01.x,
layoutBoundsOnTex01.y * (isRightHanded ? -1.0 : 1.0)
); //new = [translate]
this._tmpMatrix.scaleRelative(
scaleX,
scaleY * (isRightHanded ? -1.0 : 1.0)
); //new = [translate][scale]
this._tmpMatrix.translateRelative(
-this._tmpBoundsOnModel.x,
-this._tmpBoundsOnModel.y
); //new = [translate][scale][translate]
}
this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray());
}
/**
* クリッピングコンテキストを配置するレイアウト
* 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする
* マスクグループの数が4以下ならRGBA各チャンネルに一つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。
*
* @param usingClipCount 配置するクリッピングコンテキストの数
*/
public setupLayoutBounds(usingClipCount: number): void {
const useClippingMaskMaxCount =
this._renderTextureCount <= 1
? ClippingMaskMaxCountOnDefault
: ClippingMaskMaxCountOnMultiRenderTexture * this._renderTextureCount;
if (usingClipCount <= 0 || usingClipCount > useClippingMaskMaxCount) {
if (usingClipCount > useClippingMaskMaxCount) {
// マスクの制限数の警告を出す
CubismLogError(
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
usingClipCount - useClippingMaskMaxCount,
this._renderTextureCount,
usingClipCount
);
}
// この場合は一つのマスクターゲットを毎回クリアして使用する
for (
let index = 0;
index < this._clippingContextListForMask.length;
index++
) {
const clipContext: T_ClippingContext =
this._clippingContextListForMask[index];
clipContext._layoutChannelIndex = 0; // どうせ毎回消すので固定
clipContext._layoutBounds.x = 0.0;
clipContext._layoutBounds.y = 0.0;
clipContext._layoutBounds.width = 1.0;
clipContext._layoutBounds.height = 1.0;
clipContext._bufferIndex = 0;
}
return;
}
// レンダーテクスチャが1枚なら9分割する最大36枚
const layoutCountMaxValue = this._renderTextureCount <= 1 ? 9 : 8;
// 指定された数のレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトするデフォルトなら1
// マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。
let countPerSheetDiv: number = usingClipCount / this._renderTextureCount; // レンダーテクスチャ1枚あたり何枚割り当てるか。
const reduceLayoutTextureCount: number =
usingClipCount % this._renderTextureCount; // レイアウトの数を1枚減らすレンダーテクスチャの数この数だけのレンダーテクスチャが対象
// 1枚に割り当てるマスクの分割数を取りたいため、小数点は切り上げる
countPerSheetDiv = Math.ceil(countPerSheetDiv);
// RGBAを順番に使っていく
let divCount: number = countPerSheetDiv / ColorChannelCount; // 1チャンネルに配置する基本のマスク
const modCount: number = countPerSheetDiv % ColorChannelCount; // 余り、この番号のチャンネルまでに一つずつ配分する(インデックスではない)
// 小数点は切り捨てる
divCount = ~~divCount;
// RGBAそれぞれのチャンネルを用意していく0:R, 1:G, 2:B, 3:A
let curClipIndex = 0; // 順番に設定していく
for (
let renderTextureIndex = 0;
renderTextureIndex < this._renderTextureCount;
renderTextureIndex++
) {
for (
let channelIndex = 0;
channelIndex < ColorChannelCount;
channelIndex++
) {
// このチャンネルにレイアウトする数
// NOTE: レイアウト数 = 1チャンネルに配置する基本のマスク + 余りのマスクを置くチャンネルなら1つ追加
let layoutCount: number = divCount + (channelIndex < modCount ? 1 : 0);
// レイアウトの数を1枚減らす場合にそれを行うチャンネルを決定
// divが0の時は正常なインデックスの範囲内になるように調整
const 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) {
// 全てをそのまま使う
const clipContext: T_ClippingContext =
this._clippingContextListForMask[curClipIndex++];
clipContext._layoutChannelIndex = channelIndex;
clipContext._layoutBounds.x = 0.0;
clipContext._layoutBounds.y = 0.0;
clipContext._layoutBounds.width = 1.0;
clipContext._layoutBounds.height = 1.0;
clipContext._bufferIndex = renderTextureIndex;
} else if (layoutCount == 2) {
for (let i = 0; i < layoutCount; i++) {
let xpos: number = i % 2;
// 小数点は切り捨てる
xpos = ~~xpos;
const cc: T_ClippingContext =
this._clippingContextListForMask[curClipIndex++];
cc._layoutChannelIndex = channelIndex;
// UVを2つに分解して使う
cc._layoutBounds.x = xpos * 0.5;
cc._layoutBounds.y = 0.0;
cc._layoutBounds.width = 0.5;
cc._layoutBounds.height = 1.0;
cc._bufferIndex = renderTextureIndex;
}
} else if (layoutCount <= 4) {
// 4分割して使う
for (let i = 0; i < layoutCount; i++) {
let xpos: number = i % 2;
let ypos: number = i / 2;
// 小数点は切り捨てる
xpos = ~~xpos;
ypos = ~~ypos;
const cc = this._clippingContextListForMask[curClipIndex++];
cc._layoutChannelIndex = channelIndex;
cc._layoutBounds.x = xpos * 0.5;
cc._layoutBounds.y = ypos * 0.5;
cc._layoutBounds.width = 0.5;
cc._layoutBounds.height = 0.5;
cc._bufferIndex = renderTextureIndex;
}
} else if (layoutCount <= layoutCountMaxValue) {
// 9分割して使う
for (let i = 0; i < layoutCount; i++) {
let xpos = i % 3;
let ypos = i / 3;
// 小数点は切り捨てる
xpos = ~~xpos;
ypos = ~~ypos;
const cc: T_ClippingContext =
this._clippingContextListForMask[curClipIndex++];
cc._layoutChannelIndex = channelIndex;
cc._layoutBounds.x = xpos / 3.0;
cc._layoutBounds.y = ypos / 3.0;
cc._layoutBounds.width = 1.0 / 3.0;
cc._layoutBounds.height = 1.0 / 3.0;
cc._bufferIndex = renderTextureIndex;
}
} else {
// マスクの制限枚数を超えた場合の処理
CubismLogError(
'not supported mask count : {0}\n[Details] render texture count : {1}, mask count : {2}',
usingClipCount - useClippingMaskMaxCount,
this._renderTextureCount,
usingClipCount
);
// SetupShaderProgramでオーバーアクセスが発生するので仮で数値を入れる
// もちろん描画結果は正しいものではなくなる
for (let index = 0; index < layoutCount; index++) {
const cc: T_ClippingContext =
this._clippingContextListForMask[curClipIndex++];
cc._layoutChannelIndex = 0;
cc._layoutBounds.x = 0.0;
cc._layoutBounds.y = 0.0;
cc._layoutBounds.width = 1.0;
cc._layoutBounds.height = 1.0;
cc._bufferIndex = 0;
}
}
}
}
}
/**
* マスクされる描画オブジェクト群全体を囲む矩形(モデル座標系)を計算する
* @param model モデルのインスタンス
* @param clippingContext クリッピングマスクのコンテキスト
*/
public calcClippedDrawableTotalBounds(
model: CubismModel,
clippingContext: T_ClippingContext
): void {
// 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形
let clippedDrawTotalMinX: number = Number.MAX_VALUE;
let clippedDrawTotalMinY: number = Number.MAX_VALUE;
let clippedDrawTotalMaxX: number = Number.MIN_VALUE;
let clippedDrawTotalMaxY: number = Number.MIN_VALUE;
// このマスクが実際に必要か判定する
// このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある
const clippedDrawCount: number =
clippingContext._clippedDrawableIndexList.length;
for (
let clippedDrawableIndex = 0;
clippedDrawableIndex < clippedDrawCount;
clippedDrawableIndex++
) {
// マスクを使用する描画オブジェクトの描画される矩形を求める
const drawableIndex: number =
clippingContext._clippedDrawableIndexList[clippedDrawableIndex];
const drawableVertexCount: number =
model.getDrawableVertexCount(drawableIndex);
const drawableVertexes: Float32Array =
model.getDrawableVertices(drawableIndex);
let minX: number = Number.MAX_VALUE;
let minY: number = Number.MAX_VALUE;
let maxX: number = -Number.MAX_VALUE;
let maxY: number = -Number.MAX_VALUE;
const loop: number = drawableVertexCount * Constant.vertexStep;
for (
let pi: number = Constant.vertexOffset;
pi < loop;
pi += Constant.vertexStep
) {
const x: number = drawableVertexes[pi];
const y: number = drawableVertexes[pi + 1];
if (x < minX) {
minX = x;
}
if (x > maxX) {
maxX = x;
}
if (y < minY) {
minY = y;
}
if (y > maxY) {
maxY = y;
}
}
// 有効な点が一つも取れなかったのでスキップ
if (minX == Number.MAX_VALUE) {
continue;
}
// 全体の矩形に反映
if (minX < clippedDrawTotalMinX) {
clippedDrawTotalMinX = minX;
}
if (minY < clippedDrawTotalMinY) {
clippedDrawTotalMinY = minY;
}
if (maxX > clippedDrawTotalMaxX) {
clippedDrawTotalMaxX = maxX;
}
if (maxY > clippedDrawTotalMaxY) {
clippedDrawTotalMaxY = maxY;
}
if (clippedDrawTotalMinX == Number.MAX_VALUE) {
clippingContext._allClippedDrawRect.x = 0.0;
clippingContext._allClippedDrawRect.y = 0.0;
clippingContext._allClippedDrawRect.width = 0.0;
clippingContext._allClippedDrawRect.height = 0.0;
clippingContext._isUsing = false;
} else {
clippingContext._isUsing = true;
const w: number = clippedDrawTotalMaxX - clippedDrawTotalMinX;
const h: number = clippedDrawTotalMaxY - clippedDrawTotalMinY;
clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX;
clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY;
clippingContext._allClippedDrawRect.width = w;
clippingContext._allClippedDrawRect.height = h;
}
}
}
/**
* 画面描画に使用するクリッピングマスクのリストを取得する
* @return 画面描画に使用するクリッピングマスクのリスト
*/
public getClippingContextListForDraw(): Array<T_ClippingContext> {
return this._clippingContextListForDraw;
}
public getClippingContextListForOffscreen(): Array<T_ClippingContext> {
return this._clippingContextListForOffscreen;
}
/**
* クリッピングマスクバッファのサイズを取得する
* @return クリッピングマスクバッファのサイズ
*/
public getClippingMaskBufferSize(): number {
return this._clippingMaskBufferSize;
}
/**
* このバッファのレンダーテクスチャの枚数を取得する
* @return このバッファのレンダーテクスチャの枚数
*/
public getRenderTextureCount(): number {
return this._renderTextureCount;
}
/**
* カラーチャンネルRGBAのフラグを取得する
* @param channelNo カラーチャンネルRGBAの番号0:R, 1:G, 2:B, 3:A
*/
public getChannelFlagAsColor(channelNo: number): CubismTextureColor {
return this._channelColors[channelNo];
}
/**
* クリッピングマスクバッファのサイズを設定する
* @param size クリッピングマスクバッファのサイズ
*/
public setClippingMaskBufferSize(size: number): void {
this._clippingMaskBufferSize = size;
}
protected _clearedMaskBufferFlags: Array<boolean>; //マスクのクリアフラグの配列
protected _channelColors: Array<CubismTextureColor>;
protected _clippingContextListForMask: Array<T_ClippingContext>; // マスク用クリッピングコンテキストのリスト
protected _clippingContextListForDraw: Array<T_ClippingContext>; // 描画用クリッピングコンテキストのリスト
protected _clippingContextListForOffscreen: Array<T_ClippingContext>; // オフスクリーン用クリッピングコンテキストのリスト
protected _clippingMaskBufferSize: number; // クリッピングマスクのバッファサイズ(初期値:256
protected _renderTextureCount: number; // 生成するレンダーテクスチャの枚数
protected _tmpMatrix: CubismMatrix44; // マスク計算用の行列
protected _tmpMatrixForMask: CubismMatrix44; // マスク計算用の行列
protected _tmpMatrixForDraw: CubismMatrix44; // マスク計算用の行列
protected _tmpBoundsOnModel: csmRect; // マスク配置計算用の矩形
protected _clippingContexttConstructor: ClippingContextConstructor<T_ClippingContext>;
}

View File

@@ -0,0 +1,591 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { updateSize } from '../utils/cubismarrayutils';
import { CubismLogError } from '../utils/cubismdebug';
import { CubismRenderTarget_WebGL } from './cubismrendertarget_webgl';
/**
* フレームバッファなどのコンテナのクラス
*/
class CubismRenderTargetContainer {
/**
* Constructor
*
* @param colorBuffer カラーバッファ
* @param renderTexture レンダーテクスチャ
* @param inUse 使用中かどうか
*/
public constructor(
colorBuffer: WebGLTexture = null,
renderTexture: WebGLFramebuffer = null,
inUse: boolean = false
) {
this.colorBuffer = colorBuffer;
this.renderTexture = renderTexture;
this.inUse = inUse;
}
public clear(): void {
this.colorBuffer = null;
this.renderTexture = null;
this.inUse = false;
}
/**
* カラーバッファを取得
*
* @returns カラーバッファ
*/
public getColorBuffer(): WebGLTexture {
return this.colorBuffer;
}
/**
* レンダーテクスチャを取得
*
* @returns レンダーテクスチャ
*/
public getRenderTexture(): WebGLFramebuffer {
return this.renderTexture;
}
public colorBuffer: WebGLTexture; // colorBuffer
public renderTexture: WebGLFramebuffer; // renderTarget
public inUse: boolean; // Whether this container's render target is currently in use
}
/**
* WebGLContextごとのリソース管理を行う内部クラス
*/
class CubismWebGLContextManager {
constructor(gl: WebGLRenderingContext | WebGL2RenderingContext) {
this.gl = gl;
this.offscreenRenderTargetContainers =
new Array<CubismRenderTargetContainer>();
this.previousActiveRenderTextureMaxCount = 0;
this.currentActiveRenderTextureCount = 0;
this.hasResetThisFrame = false;
this.width = 0;
this.height = 0;
}
public release(): void {
if (this.offscreenRenderTargetContainers != null) {
for (
let index = 0;
index < this.offscreenRenderTargetContainers.length;
++index
) {
const container = this.offscreenRenderTargetContainers[index];
this.gl.deleteTexture(container.colorBuffer);
this.gl.deleteFramebuffer(container.renderTexture);
}
this.offscreenRenderTargetContainers.length = 0;
this.offscreenRenderTargetContainers = null;
}
}
public gl: WebGLRenderingContext | WebGL2RenderingContext; // WebGLContext
public offscreenRenderTargetContainers: Array<CubismRenderTargetContainer>; // オフスクリーン描画用レンダーターゲットのリスト
public previousActiveRenderTextureMaxCount: number; // 直前のアクティブなレンダーターゲットの最大数
public currentActiveRenderTextureCount: number; // 現在のアクティブなレンダーターゲットの数
public hasResetThisFrame: boolean; // 今フレームでリセットされたかどうか
public width: number; // 幅
public height: number; // 高さ
}
/**
* WebGL用オフスクリーン描画機能を管理するマネージャ
* オフスクリーン描画機能に必要なフレームバッファなどを含むコンテナを管理する。
* 複数のWebGLContextに対応。
*/
export class CubismWebGLOffscreenManager {
/**
* コンストラクタ
*/
private constructor() {
this._contextManagers = new Map<
WebGLRenderingContext | WebGL2RenderingContext,
CubismWebGLContextManager
>();
}
/**
* デストラクタ相当の処理
*/
public release(): void {
if (this._contextManagers != null) {
for (const manager of this._contextManagers.values()) {
manager.release();
}
this._contextManagers.clear();
this._contextManagers = null;
}
CubismWebGLOffscreenManager._instance = null;
}
/**
* インスタンスの取得
*
* @return インスタンス
*/
public static getInstance(): CubismWebGLOffscreenManager {
if (this._instance == null) {
this._instance = new CubismWebGLOffscreenManager();
}
return this._instance;
}
/**
* WebGLContextに対応するマネージャーを取得または作成
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @return WebGLContextManager
*/
private getContextManager(
gl: WebGLRenderingContext | WebGL2RenderingContext
): CubismWebGLContextManager {
if (!this._contextManagers.has(gl)) {
this._contextManagers.set(gl, new CubismWebGLContextManager(gl));
}
return this._contextManagers.get(gl);
}
/**
* 指定されたWebGLContextのマネージャーを削除
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
*/
public removeContext(
gl: WebGLRenderingContext | WebGL2RenderingContext
): void {
if (this._contextManagers.has(gl)) {
const manager = this._contextManagers.get(gl);
manager.release();
this._contextManagers.delete(gl);
}
}
/**
* 初期化処理
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @param width 幅
* @param height 高さ
*/
public initialize(
gl: WebGLRenderingContext | WebGL2RenderingContext,
width: number,
height: number
): void {
const contextManager = this.getContextManager(gl);
// initialize offscreenRenderTargetContainers
if (contextManager.offscreenRenderTargetContainers != null) {
for (
let index = 0;
index < contextManager.offscreenRenderTargetContainers.length;
++index
) {
const container = contextManager.offscreenRenderTargetContainers[index];
contextManager.gl.deleteTexture(container.colorBuffer);
contextManager.gl.deleteFramebuffer(container.renderTexture);
container.clear();
}
contextManager.offscreenRenderTargetContainers.length = 0;
} else {
contextManager.offscreenRenderTargetContainers =
new Array<CubismRenderTargetContainer>();
}
contextManager.width = width;
contextManager.height = height;
contextManager.previousActiveRenderTextureMaxCount = 0;
contextManager.currentActiveRenderTextureCount = 0;
contextManager.hasResetThisFrame = false;
}
/**
* モデルを描画する前に呼び出すフレーム開始時の処理を行う
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
*/
public beginFrameProcess(
gl: WebGLRenderingContext | WebGL2RenderingContext
): void {
const contextManager = this.getContextManager(gl);
if (contextManager.hasResetThisFrame) {
return;
}
contextManager.previousActiveRenderTextureMaxCount = 0;
contextManager.hasResetThisFrame = true;
}
/**
* モデルの描画が終わった後に呼び出すフレーム終了時の処理
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
*/
public endFrameProcess(
gl: WebGLRenderingContext | WebGL2RenderingContext
): void {
const contextManager = this.getContextManager(gl);
contextManager.hasResetThisFrame = false;
}
/**
* コンテナサイズの取得
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
*/
public getContainerSize(
gl: WebGLRenderingContext | WebGL2RenderingContext
): number {
const contextManager = this.getContextManager(gl);
if (contextManager.offscreenRenderTargetContainers == null) {
return 0;
}
return contextManager.offscreenRenderTargetContainers.length;
}
/**
* 使用可能なリソースコンテナの取得
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @param width 幅
* @param height 高さ
* @param previousFramebuffer 前のフレームバッファ
* @return 使用可能なリソースコンテナ
*/
public getOffscreenRenderTargetContainers(
gl: WebGLRenderingContext | WebGL2RenderingContext,
width: number,
height: number,
previousFramebuffer: WebGLFramebuffer
): CubismRenderTargetContainer {
const contextManager = this.getContextManager(gl);
// コンテナが初期化されていないか、サイズが変わったら初期化し直す
if (
contextManager.width != width ||
contextManager.height != height ||
contextManager.offscreenRenderTargetContainers == null
) {
this.initialize(gl, width, height);
}
// 使用数を更新
this.updateRenderTargetContainerCount(gl);
// 使われていないリソースコンテナがあればそれを返す
const container = this.getUnusedOffscreenRenderTargetContainer(gl);
if (container != null) {
return container;
}
// 使われていないリソースコンテナがなければ新たに作成する
const offscreenRenderTextureContainer =
this.createOffscreenRenderTargetContainer(
gl,
width,
height,
previousFramebuffer
);
return offscreenRenderTextureContainer;
}
/**
* リソースコンテナの使用状態を取得
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @param renderTexture WebGLFramebuffer
* @return 使用中はtrue、未使用の場合はfalse
*/
public getUsingRenderTextureState(
gl: WebGLRenderingContext | WebGL2RenderingContext,
renderTexture: WebGLFramebuffer
): boolean {
const contextManager = this.getContextManager(gl);
for (
let index = 0;
index < contextManager.offscreenRenderTargetContainers.length;
++index
) {
if (
contextManager.offscreenRenderTargetContainers[index].renderTexture ==
renderTexture
) {
return contextManager.offscreenRenderTargetContainers[index].inUse;
}
}
return true;
}
/**
* リソースコンテナの使用を開始する。
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @param renderTexture WebGLFramebuffer
*/
public startUsingRenderTexture(
gl: WebGLRenderingContext | WebGL2RenderingContext,
renderTexture: WebGLFramebuffer
): void {
const contextManager = this.getContextManager(gl);
for (
let index = 0;
index < contextManager.offscreenRenderTargetContainers.length;
++index
) {
if (
contextManager.offscreenRenderTargetContainers[index].renderTexture !=
renderTexture
) {
continue;
}
contextManager.offscreenRenderTargetContainers[index].inUse = true;
this.updateRenderTargetContainerCount(gl);
break;
}
}
/**
* リソースコンテナの使用を終了する。
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @param renderTexture WebGLFramebuffer
*/
public stopUsingRenderTexture(
gl: WebGLRenderingContext | WebGL2RenderingContext,
renderTexture: WebGLFramebuffer
): void {
const contextManager = this.getContextManager(gl);
for (
let index = 0;
index < contextManager.offscreenRenderTargetContainers.length;
++index
) {
if (
contextManager.offscreenRenderTargetContainers[index].renderTexture !=
renderTexture
) {
continue;
}
contextManager.offscreenRenderTargetContainers[index].inUse = false;
contextManager.currentActiveRenderTextureCount--;
if (contextManager.currentActiveRenderTextureCount < 0) {
contextManager.currentActiveRenderTextureCount = 0;
}
break;
}
}
/**
* リソースコンテナの使用を全て終了する。
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
*/
public stopUsingAllRenderTextures(
gl: WebGLRenderingContext | WebGL2RenderingContext
): void {
const contextManager = this.getContextManager(gl);
for (
let index = 0;
index < contextManager.offscreenRenderTargetContainers.length;
++index
) {
contextManager.offscreenRenderTargetContainers[index].inUse = false;
}
contextManager.currentActiveRenderTextureCount = 0;
}
/**
* 使用されていないリソースコンテナを解放する。
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
*/
public releaseStaleRenderTextures(
gl: WebGLRenderingContext | WebGL2RenderingContext
): void {
const contextManager = this.getContextManager(gl);
const listSize = contextManager.offscreenRenderTargetContainers.length;
if (contextManager.hasResetThisFrame || listSize === 0) {
// 使用する量が変化する場合は開放しない
return;
}
// 未使用な場所を開放して直前の最大数までリサイズする
let findPos = 0;
let resize = contextManager.previousActiveRenderTextureMaxCount;
for (
let i = listSize;
contextManager.previousActiveRenderTextureMaxCount < i;
--i
) {
const index = i - 1;
if (contextManager.offscreenRenderTargetContainers[index].inUse) {
// 空いている場所探して移動させる
let isFind = false;
for (
;
findPos < contextManager.previousActiveRenderTextureMaxCount;
++findPos
) {
if (!contextManager.offscreenRenderTargetContainers[findPos].inUse) {
const tempContainer =
contextManager.offscreenRenderTargetContainers[findPos];
contextManager.offscreenRenderTargetContainers[findPos] =
contextManager.offscreenRenderTargetContainers[index];
contextManager.offscreenRenderTargetContainers[findPos].inUse =
true;
contextManager.offscreenRenderTargetContainers[index] =
tempContainer;
contextManager.offscreenRenderTargetContainers[index].inUse = false;
isFind = true;
break;
}
}
if (!isFind) {
// 空いている場所が見つからなかったら現状のサイズでリサイズする
resize = i;
break;
}
}
const container = contextManager.offscreenRenderTargetContainers[index];
contextManager.gl.bindTexture(contextManager.gl.TEXTURE_2D, null);
contextManager.gl.deleteTexture(container.colorBuffer);
contextManager.gl.bindFramebuffer(contextManager.gl.FRAMEBUFFER, null);
contextManager.gl.deleteFramebuffer(container.renderTexture);
container.clear();
}
updateSize(contextManager.offscreenRenderTargetContainers, resize);
}
/**
* 直前のアクティブなレンダーターゲットの最大数を取得
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @returns 直前のアクティブなレンダーターゲットの最大数
*/
public getPreviousActiveRenderTextureCount(
gl: WebGLRenderingContext | WebGL2RenderingContext
): number {
const contextManager = this.getContextManager(gl);
return contextManager.previousActiveRenderTextureMaxCount;
}
/**
* 現在のアクティブなレンダーターゲットの数を取得
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @returns 現在のアクティブなレンダーターゲットの数
*/
public getCurrentActiveRenderTextureCount(
gl: WebGLRenderingContext | WebGL2RenderingContext
): number {
const contextManager = this.getContextManager(gl);
return contextManager.currentActiveRenderTextureCount;
}
/**
* 現在のアクティブなレンダーターゲットの数を更新
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
*/
public updateRenderTargetContainerCount(
gl: WebGLRenderingContext | WebGL2RenderingContext
): void {
const contextManager = this.getContextManager(gl);
++contextManager.currentActiveRenderTextureCount;
// 最大数更新
contextManager.previousActiveRenderTextureMaxCount =
contextManager.currentActiveRenderTextureCount >
contextManager.previousActiveRenderTextureMaxCount
? contextManager.currentActiveRenderTextureCount
: contextManager.previousActiveRenderTextureMaxCount;
}
/**
* 使用されていないリソースコンテナの取得
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @return 使用されていないリソースコンテナ
*/
public getUnusedOffscreenRenderTargetContainer(
gl: WebGLRenderingContext | WebGL2RenderingContext
): CubismRenderTargetContainer {
const contextManager = this.getContextManager(gl);
// 使われていないリソースコンテナがあればそれを返す
for (
let index = 0;
index < contextManager.offscreenRenderTargetContainers.length;
++index
) {
const container = contextManager.offscreenRenderTargetContainers[index];
if (container.inUse == false) {
container.inUse = true;
return container;
}
}
return null;
}
/**
* 新たにリソースコンテナを作成する。
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* @param width 幅
* @param height 高さ
* @param previousFramebuffer 前のフレームバッファ
* @return 作成されたリソースコンテナ
*/
public createOffscreenRenderTargetContainer(
gl: WebGLRenderingContext | WebGL2RenderingContext,
width: number,
height: number,
previousFramebuffer: WebGLFramebuffer
): CubismRenderTargetContainer {
const renderTarget = new CubismRenderTarget_WebGL();
if (
!renderTarget.createRenderTarget(gl, width, height, previousFramebuffer)
) {
CubismLogError('Failed to create offscreen render texture.');
return null;
}
const offscreenRenderTextureContainer = new CubismRenderTargetContainer(
renderTarget.getColorBuffer(),
renderTarget.getRenderTexture(),
true
);
const contextManager = this.getContextManager(gl);
contextManager.offscreenRenderTargetContainers.push(
offscreenRenderTextureContainer
);
return offscreenRenderTextureContainer;
}
private static _instance: CubismWebGLOffscreenManager; // オフスクリーン描画用レンダーターゲットマネージャ
private _contextManagers: Map<
WebGLRenderingContext | WebGL2RenderingContext,
CubismWebGLContextManager
>; // WebGLContextごとのマネージャー
}

View File

@@ -0,0 +1,240 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismRenderTarget_WebGL } from './cubismrendertarget_webgl';
import { CubismWebGLOffscreenManager } from './cubismoffscreenmanager';
import { CubismLogError } from '../utils/cubismdebug';
/**
* WebGL用オフスクリーンサーフェス
* マスクの描画及びオフスクリーン機能に必要なフレームバッファなどを管理する。
*/
export class CubismOffscreenRenderTarget_WebGL extends CubismRenderTarget_WebGL {
/**
* リソースコンテナマネージャを初期化する。
*
* @param displayBufferWidth レンダーターゲットの幅
* @param displayBufferHeight レンダーターゲットの高さ
*/
private initializeOffscreenManager(
gl: WebGLRenderingContext | WebGL2RenderingContext,
displayBufferWidth: number,
displayBufferHeight: number
): void {
this._gl = gl;
this._webGLOffscreenManager = CubismWebGLOffscreenManager.getInstance();
if (this._webGLOffscreenManager.getContainerSize(gl) === 0) {
this._webGLOffscreenManager.initialize(
gl,
displayBufferWidth,
displayBufferHeight
);
}
}
/**
* オフスクリーン描画用レンダーターゲットをセットする。
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* NOTE: Cubism 5.3以降のモデルが使用される場合はWebGL2RenderingContextを使用すること。
* @param displayBufferWidth レンダーターゲットの幅
* @param displayBufferHeight レンダーターゲットの高さ
* @param previousFramebuffer 前のフレームバッファ
*/
public setOffscreenRenderTarget(
gl: WebGLRenderingContext | WebGL2RenderingContext,
displayBufferWidth: number,
displayBufferHeight: number,
previousFramebuffer: WebGLFramebuffer
): void {
// マネージャがなければ初期化
if (this._webGLOffscreenManager == null) {
this.initializeOffscreenManager(
gl,
displayBufferWidth,
displayBufferHeight
);
}
// 使用可能なリソースコンテナを取得する
const offscreenRenderTargetContainer =
this._webGLOffscreenManager.getOffscreenRenderTargetContainers(
gl,
displayBufferWidth,
displayBufferHeight,
previousFramebuffer
);
if (offscreenRenderTargetContainer == null) {
CubismLogError('Failed to acquire offscreen render texture container.');
return;
}
this._colorBuffer = offscreenRenderTargetContainer.getColorBuffer();
this._renderTexture = offscreenRenderTargetContainer.getRenderTexture();
this._bufferWidth = displayBufferWidth;
this._bufferHeight = displayBufferHeight;
this._gl = gl;
if (this._renderTexture == null) {
this._renderTexture = previousFramebuffer;
CubismLogError('Failed to create offscreen render texture.');
}
return;
}
/**
* リソースコンテナの使用状態を取得
*
* @return 使用中はtrue、未使用の場合はfalse
*/
public getUsingRenderTextureState(): boolean {
if (this._webGLOffscreenManager == null || this._gl == null) {
return true;
}
return this._webGLOffscreenManager.getUsingRenderTextureState(
this._gl,
this._renderTexture
);
}
/**
* リソースコンテナの使用を開始する。
*/
public startUsingRenderTexture(): void {
if (this._webGLOffscreenManager == null || this._gl == null) {
return;
}
this._webGLOffscreenManager.startUsingRenderTexture(
this._gl,
this._renderTexture
);
}
/**
* リソースコンテナの使用を終了する。
*/
public stopUsingRenderTexture(): void {
if (this._webGLOffscreenManager == null || this._gl == null) {
return;
}
this._webGLOffscreenManager.stopUsingRenderTexture(
this._gl,
this._renderTexture
);
}
/**
* オフスクリーンのインデックスを設定する。
*
* @param offscreenIndex オフスクリーンのインデックス
*/
public setOffscreenIndex(offscreenIndex: number): void {
this._offscreenIndex = offscreenIndex;
}
/**
* オフスクリーンのインデックスを取得する。
*
* @return オフスクリーンのインデックス
*/
public getOffscreenIndex(): number {
return this._offscreenIndex;
}
/**
* 以前のオフスクリーン描画用レンダーターゲットを設定する。
*
* @param oldOffscreen 以前のオフスクリーン描画用レンダーターゲット
*/
public setOldOffscreen(
oldOffscreen: CubismOffscreenRenderTarget_WebGL
): void {
this._oldOffscreen = oldOffscreen;
}
/**
* 以前のオフスクリーン描画用レンダーターゲットを取得する。
*
* @return 以前のオフスクリーン描画用レンダーターゲット
*/
public getOldOffscreen(): CubismOffscreenRenderTarget_WebGL {
return this._oldOffscreen;
}
/**
* 親のオフスクリーン描画用レンダーターゲットを設定する。
*
* @param parentOffscreenRenderTarget 親のオフスクリーン描画用レンダーターゲット
*/
public setParentPartOffscreen(
parentOffscreenRenderTarget: CubismOffscreenRenderTarget_WebGL
): void {
this._parentOffscreenRenderTarget = parentOffscreenRenderTarget;
}
/**
* 親のオフスクリーン描画用レンダーターゲットを取得する。
*
* @return 親のオフスクリーン描画用レンダーターゲット
*/
public getParentPartOffscreen(): CubismOffscreenRenderTarget_WebGL {
return this._parentOffscreenRenderTarget;
}
/**
* コンストラクタ
*/
constructor() {
super();
this._offscreenIndex = -1;
this._parentOffscreenRenderTarget = null;
this._oldOffscreen = null;
this._webGLOffscreenManager = null;
}
public release(): void {
if (
this._webGLOffscreenManager != null &&
this._gl != null &&
this._renderTexture != null
) {
this._webGLOffscreenManager.stopUsingRenderTexture(
this._gl,
this._renderTexture
);
}
if (this._colorBuffer && this._gl) {
this._gl.deleteTexture(this._colorBuffer);
this._colorBuffer = null;
}
if (this._renderTexture && this._gl) {
this._gl.deleteFramebuffer(this._renderTexture);
this._renderTexture = null;
}
if (this._webGLOffscreenManager != null) {
this._webGLOffscreenManager = null;
}
this._oldOffscreen = null;
this._parentOffscreenRenderTarget = null;
}
private _offscreenIndex: number; // オフスクリーンのインデックス
private _parentOffscreenRenderTarget: CubismOffscreenRenderTarget_WebGL; // 親のオフスクリーン描画用レンダーターゲット
private _oldOffscreen: CubismOffscreenRenderTarget_WebGL; // 以前のオフスクリーン描画用レンダーターゲット
private _webGLOffscreenManager: CubismWebGLOffscreenManager; // オフスクリーン描画用レンダーターゲットマネージャ
protected _gl: WebGLRenderingContext | WebGL2RenderingContext; // WebGLコンテキスト
}

View File

@@ -0,0 +1,425 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismMath } from '../math/cubismmath';
import { CubismMatrix44 } from '../math/cubismmatrix44';
import { CubismModel } from '../model/cubismmodel';
import { csmRect } from '../type/csmrectf';
import { ICubismClippingManager } from './cubismclippingmanager';
import { CubismLogInfo } from '../utils/cubismdebug';
/**
* モデル描画を処理するレンダラ
*
* サブクラスに環境依存の描画命令を記述する。
*/
export abstract class CubismRenderer {
/**
* レンダラのインスタンスを生成して取得する
*
* @return レンダラのインスタンス
*/
public static create(): CubismRenderer {
return null;
}
/**
* レンダラのインスタンスを解放する
*/
public static delete(renderer: CubismRenderer): void {
renderer = null;
}
/**
* レンダラの初期化処理を実行する
* 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる
*
* @param model モデルのインスタンス
*/
public initialize(model: CubismModel): void {
this._model = model;
// ブレンドモード使用時は必ず高精細にする
if (model.isBlendModeEnabled()) {
this.useHighPrecisionMask(true);
CubismLogInfo(
'This model uses a high-resolution mask because it operates in blend mode.'
);
}
}
/**
* モデルを描画する
* @param shaderPath ブレンドモード用シェーダのパス
*/
public drawModel(shaderPath: string = null): void {
if (this.getModel() == null) return;
// NOTE: WebGL最適化のため、デフォルトではコメントアウト
//this.saveProfile();
this.doDrawModel(shaderPath);
// NOTE: WebGL最適化のため、デフォルトではコメントアウト
//this.restoreProfile();
}
/**
* Model-View-Projection 行列をセットする
* 配列は複製されるので、元の配列は外で破棄して良い
*
* @param matrix44 Model-View-Projection 行列
*/
public setMvpMatrix(matrix44: CubismMatrix44): void {
this._mvpMatrix4x4.setMatrix(matrix44.getArray());
}
/**
* Model-View-Projection 行列を取得する
*
* @return Model-View-Projection 行列
*/
public getMvpMatrix(): CubismMatrix44 {
return this._mvpMatrix4x4;
}
/**
* モデルの色をセットする
* 各色0.0~1.0の間で指定する1.0が標準の状態)
*
* @param red 赤チャンネルの値
* @param green 緑チャンネルの値
* @param blue 青チャンネルの値
* @param alpha αチャンネルの値
*/
public setModelColor(
red: number,
green: number,
blue: number,
alpha: number
): void {
this._modelColor.r = CubismMath.clamp(red, 0.0, 1.0);
this._modelColor.g = CubismMath.clamp(green, 0.0, 1.0);
this._modelColor.b = CubismMath.clamp(blue, 0.0, 1.0);
this._modelColor.a = CubismMath.clamp(alpha, 0.0, 1.0);
}
/**
* モデルの色を取得する
* 各色0.0~1.0の間で指定する(1.0が標準の状態)
*
* @return RGBAのカラー情報
*/
public getModelColor(): CubismTextureColor {
return JSON.parse(JSON.stringify(this._modelColor));
}
/**
* 透明度を考慮したモデルの色を計算する。
*
* @param opacity 透明度
*
* @return RGBAのカラー情報
*/
getModelColorWithOpacity(opacity: number): CubismTextureColor {
const modelColorRGBA: CubismTextureColor = this.getModelColor();
modelColorRGBA.a *= opacity;
if (this.isPremultipliedAlpha()) {
modelColorRGBA.r *= modelColorRGBA.a;
modelColorRGBA.g *= modelColorRGBA.a;
modelColorRGBA.b *= modelColorRGBA.a;
}
return modelColorRGBA;
}
/**
* 乗算済みαの有効・無効をセットする
* 有効にするならtrue、無効にするならfalseをセットする
*/
public setIsPremultipliedAlpha(enable: boolean): void {
this._isPremultipliedAlpha = enable;
}
/**
* 乗算済みαの有効・無効を取得する
* @return true 乗算済みのα有効
* false 乗算済みのα無効
*/
public isPremultipliedAlpha(): boolean {
return this._isPremultipliedAlpha;
}
/**
* カリング(片面描画)の有効・無効をセットする。
* 有効にするならtrue、無効にするならfalseをセットする
*/
public setIsCulling(culling: boolean): void {
this._isCulling = culling;
}
/**
* カリング(片面描画)の有効・無効を取得する。
*
* @return true カリング有効
* false カリング無効
*/
public isCulling(): boolean {
return this._isCulling;
}
/**
* テクスチャの異方性フィルタリングのパラメータをセットする
* パラメータ値の影響度はレンダラの実装に依存する
*
* @param n パラメータの値
*/
public setAnisotropy(n: number): void {
this._anisotropy = n;
}
/**
* テクスチャの異方性フィルタリングのパラメータをセットする
*
* @return 異方性フィルタリングのパラメータ
*/
public getAnisotropy(): number {
return this._anisotropy;
}
/**
* レンダリングするモデルを取得する
*
* @return レンダリングするモデル
*/
public getModel(): CubismModel {
return this._model;
}
/**
* マスク描画の方式を変更する。
* falseの場合、マスクを1枚のテクスチャに分割してレンダリングするデフォルト
* 高速だが、マスク個数の上限が36に限定され、質も荒くなる
* trueの場合、パーツ描画の前にその都度必要なマスクを描き直す
* レンダリング品質は高いが描画処理負荷は増す
*
* @param high 高精細マスクに切り替えるか?
*/
public useHighPrecisionMask(high: boolean): void {
this._useHighPrecisionMask = high;
}
/**
* マスクの描画方式を取得する
*
* @return true 高精細方式
* false デフォルト
*/
public isUsingHighPrecisionMask(): boolean {
return this._useHighPrecisionMask;
}
/**
* モデルを描画したバッファのサイズを設定
*
* @param[in] width -> モデルを描画したバッファの幅
* @param[in] height -> モデルを描画したバッファの高さ
*/
public setRenderTargetSize(width: number, height: number): void {
this._modelRenderTargetWidth = width;
this._modelRenderTargetHeight = height;
}
/**
* コンストラクタ
*/
protected constructor(width: number, height: number) {
this._modelRenderTargetWidth = width;
this._modelRenderTargetHeight = height;
this._isCulling = false;
this._isPremultipliedAlpha = false;
this._anisotropy = 0.0;
this._model = null;
this._modelColor = new CubismTextureColor();
this._useHighPrecisionMask = false;
// 単位行列に初期化
this._mvpMatrix4x4 = new CubismMatrix44();
this._mvpMatrix4x4.loadIdentity();
}
/**
* モデル描画直前のオフスクリーン設定を行う
*/
public abstract beforeDrawModelRenderTarget(): void;
/**
* モデル描画直後のオフスクリーン設定を行う
*/
public abstract afterDrawModelRenderTarget(): void;
/**
* モデル描画の実装
* @param shaderPath ブレンドモード用シェーダのパス
*/
public abstract doDrawModel(shaderPath: string): void;
/**
* モデル描画直前のレンダラのステートを保持する
*/
protected abstract saveProfile(): void;
/**
* モデル描画直前のレンダラのステートを復帰する
*/
protected abstract restoreProfile(): void;
/**
* レンダラが保持する静的なリソースを開放する
*/
public static staticRelease: any;
protected _mvpMatrix4x4: CubismMatrix44; // Model-View-Projection 行列
protected _modelColor: CubismTextureColor; // モデル自体のカラーRGBA
protected _isCulling: boolean; // カリングが有効ならtrue
protected _isPremultipliedAlpha: boolean; // 乗算済みαならtrue
protected _anisotropy: any; // テクスチャの異方性フィルタリングのパラメータ
protected _model: CubismModel; // レンダリング対象のモデル
protected _useHighPrecisionMask: boolean; // falseの場合、マスクを纏めて描画する trueの場合、マスクはパーツ描画ごとに書き直す
protected _modelRenderTargetWidth: number;
protected _modelRenderTargetHeight: number;
}
export enum CubismBlendMode {
CubismBlendMode_Normal = 0, // 通常
CubismBlendMode_Additive = 1, // 加算
CubismBlendMode_Multiplicative = 2 // 乗算
}
/**
* オブジェクトのタイプ
*/
export enum DrawableObjectType {
DrawableObjectType_Drawable = 0,
DrawableObjectType_Offscreen = 1
}
/**
* テクスチャの色をRGBAで扱うためのクラス
*/
export class CubismTextureColor {
/**
* コンストラクタ
*/
constructor(r = 1.0, g = 1.0, b = 1.0, a = 1.0) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
r: number; // 赤チャンネル
g: number; // 緑チャンネル
b: number; // 青チャンネル
a: number; // αチャンネル
}
/**
* クリッピングマスクのコンテキスト
*/
export abstract class CubismClippingContext {
/**
* 引数付きコンストラクタ
*/
public constructor(clippingDrawableIndices: Int32Array, clipCount: number) {
// クリップしているマスク用のDrawableのインデックスリスト
this._clippingIdList = clippingDrawableIndices;
// マスクの数
this._clippingIdCount = clipCount;
this._allClippedDrawRect = new csmRect();
this._layoutBounds = new csmRect();
this._clippedDrawableIndexList = [];
this._clippedOffscreenIndexList = [];
this._matrixForMask = new CubismMatrix44();
this._matrixForDraw = new CubismMatrix44();
this._bufferIndex = 0;
this._layoutChannelIndex = 0;
}
/**
* このマスクを管理するマネージャのインスタンスを取得する
* @return クリッピングマネージャのインスタンス
*/
public abstract getClippingManager(): ICubismClippingManager;
/**
* デストラクタ相当の処理
*/
public release(): void {
if (this._layoutBounds != null) {
this._layoutBounds = null;
}
if (this._allClippedDrawRect != null) {
this._allClippedDrawRect = null;
}
if (this._clippedDrawableIndexList != null) {
this._clippedDrawableIndexList = null;
}
if (this._clippedOffscreenIndexList != null) {
this._clippedOffscreenIndexList = null;
}
}
/**
* このマスクにクリップされる描画オブジェクトを追加する
*
* @param drawableIndex クリッピング対象に追加する描画オブジェクトのインデックス
*/
public addClippedDrawable(drawableIndex: number) {
this._clippedDrawableIndexList.push(drawableIndex);
}
/**
* このマスクにクリップされるオフスクリーンオブジェクトを追加する
*
* @param offscreenIndex クリッピング対象に追加するオフスクリーンオブジェクトのインデックス
*/
public addClippedOffscreen(offscreenIndex: number) {
this._clippedOffscreenIndexList.push(offscreenIndex);
}
public _isUsing: boolean; // 現在の描画状態でマスクの準備が必要ならtrue
public readonly _clippingIdList: Int32Array; // クリッピングマスクのIDリスト
public _clippingIdCount: number; // クリッピングマスクの数
public _layoutChannelIndex: number; // RGBAのいずれのチャンネルにこのクリップを配置するか0:R, 1:G, 2:B, 3:A
public _layoutBounds: csmRect; // マスク用チャンネルのどの領域にマスクを入れるかView座標-1~1, UVは0~1に直す
public _allClippedDrawRect: csmRect; // このクリッピングで、クリッピングされるすべての描画オブジェクトの囲み矩形(毎回更新)
public _matrixForMask: CubismMatrix44; // マスクの位置計算結果を保持する行列
public _matrixForDraw: CubismMatrix44; // 描画オブジェクトの位置計算結果を保持する行列
public _clippedDrawableIndexList: number[]; // このマスクにクリップされる描画オブジェクトのリスト
public _clippedOffscreenIndexList: number[]; // このマスクにクリップされるオフスクリーンオブジェクトのリスト
public _bufferIndex: number; // このマスクが割り当てられるレンダーテクスチャ(フレームバッファ)やカラーバッファのインデックス
}
// Namespace definition for compatibility.
import * as $ from './cubismrenderer';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismBlendMode = $.CubismBlendMode;
export type CubismBlendMode = $.CubismBlendMode;
export const CubismRenderer = $.CubismRenderer;
export type CubismRenderer = $.CubismRenderer;
export const CubismTextureColor = $.CubismTextureColor;
export type CubismTextureColor = $.CubismTextureColor;
}

View File

@@ -0,0 +1,289 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { CubismLogError } from '../utils/cubismdebug';
/**
* WebGL用オフスクリーンサーフェス
* マスクの描画に必要なフレームバッファなどを管理する。
*/
export class CubismRenderTarget_WebGL {
/**
* WebGL2RenderingContext.blitFramebuffer() でバッファのコピーを行う。
*
* @param src コピー元のオフスクリーンサーフェス
* @param dst コピー先のオフスクリーンサーフェス
*/
public static copyBuffer(
gl: WebGL2RenderingContext,
src: CubismRenderTarget_WebGL,
dst: CubismRenderTarget_WebGL
): void {
if (src == null || dst == null) {
return;
}
if (!(gl instanceof WebGL2RenderingContext)) {
throw new Error('WebGL2RenderingContext is required for buffer copy.');
}
const previousFramebuffer = gl.getParameter(
gl.FRAMEBUFFER_BINDING
) as WebGLFramebuffer;
// 各オフスクリーンサーフェスのレンダーテクスチャをバインド
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, src.getRenderTexture());
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dst.getRenderTexture());
// バッファのコピーを実行
gl.blitFramebuffer(
0,
0,
src.getBufferWidth(),
src.getBufferHeight(),
0,
0,
dst.getBufferWidth(),
dst.getBufferHeight(),
gl.COLOR_BUFFER_BIT,
gl.NEAREST
);
// コピー後、元のフレームバッファを復元
gl.bindFramebuffer(gl.FRAMEBUFFER, previousFramebuffer);
}
/**
* 描画を開始する。
*
* @param restoreFbo EndDraw時に復元するFBOを指定する。nullを指定すると、beginDraw時に現在のFBOを記憶しておく。
*/
public beginDraw(restoreFbo: WebGLFramebuffer = null): void {
if (this._renderTexture == null) {
console.error('_renderTexture is null');
return;
}
// バックバッファのサーフェイスを記憶しておく。
if (restoreFbo == null) {
this._oldFbo = this._gl.getParameter(this._gl.FRAMEBUFFER_BINDING);
} else {
this._oldFbo = restoreFbo;
}
// RenderTextureをactiveにセット
this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, this._renderTexture);
}
/**
* 描画を終了し、バックバッファのサーフェイスを復元する。
*/
public endDraw(): void {
// バックバッファのサーフェイスを復元
this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, this._oldFbo);
}
/**
* バインドされているカラーバッファのクリアを行う。
*
* @param r 赤の成分 (0.0 - 1.0)
* @param g 緑の成分 (0.0 - 1.0)
* @param b 青の成分 (0.0 - 1.0)
* @param a アルファの成分 (0.0 - 1.0)
*/
public clear(r: number, g: number, b: number, a: number): void {
// クリア処理
this._gl.clearColor(r, g, b, a);
this._gl.clear(this._gl.COLOR_BUFFER_BIT);
}
/**
* オフスクリーンサーフェスを作成する。
*
* @param gl WebGLRenderingContextまたはWebGL2RenderingContext
* NOTE: Cubism 5.3以降のモデルが使用される場合はWebGL2RenderingContextを使用すること。
* @param displayBufferWidth オフスクリーンサーフェスの幅
* @param displayBufferHeight オフスクリーンサーフェスの高さ
* @param previousFramebuffer 前のフレームバッファ
*
* @return 成功した場合はtrue、失敗した場合はfalse
*/
public createRenderTarget(
gl: WebGLRenderingContext | WebGL2RenderingContext,
displayBufferWidth: number,
displayBufferHeight: number,
previousFramebuffer: WebGLFramebuffer
): boolean {
this.destroyRenderTarget();
this._colorBuffer = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this._colorBuffer);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
displayBufferWidth,
displayBufferHeight,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.bindTexture(gl.TEXTURE_2D, null);
// フレームバッファを作成
const ret = gl.createFramebuffer();
if (ret == null) {
CubismLogError('Failed to create framebuffer');
return false;
}
// 作成したフレームバッファをバインド
gl.bindFramebuffer(gl.FRAMEBUFFER, ret);
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
this._colorBuffer,
0
);
// 状態をチェック
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
// フレームバッファが完全でない場合はエラーを出力して以前のフレームバッファを復元
if (status !== gl.FRAMEBUFFER_COMPLETE) {
CubismLogError('Framebuffer is not complete');
gl.bindFramebuffer(gl.FRAMEBUFFER, previousFramebuffer);
gl.deleteFramebuffer(ret);
this.destroyRenderTarget();
return false;
}
this._renderTexture = ret;
this._bufferWidth = displayBufferWidth;
this._bufferHeight = displayBufferHeight;
this._gl = gl;
return true;
}
/**
* レンダーターゲットを破棄する。
*/
public destroyRenderTarget(): void {
if (this._colorBuffer) {
this._gl.bindTexture(this._gl.TEXTURE_2D, null);
this._gl.deleteTexture(this._colorBuffer);
this._colorBuffer = null;
}
if (this._renderTexture) {
this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null);
this._gl.deleteFramebuffer(this._renderTexture);
this._renderTexture = null;
}
}
/**
* WebGLのコンテキストを取得する。
*
* @return WebGLRenderingContextまたはWebGL2RenderingContext
*/
public getGL(): WebGLRenderingContext | WebGL2RenderingContext {
return this._gl;
}
/**
* レンダーテクスチャを取得する。
*
* @return WebGLFramebuffer
*/
public getRenderTexture(): WebGLFramebuffer {
return this._renderTexture;
}
/**
* カラーバッファを取得する。
*
* @return WebGLTexture
*/
public getColorBuffer(): WebGLTexture {
return this._colorBuffer;
}
/**
* カラーバッファの幅を取得する。
*
* @return カラーバッファの幅
*/
public getBufferWidth(): number {
return this._bufferWidth;
}
/**
* カラーバッファの高さを取得する。
*
* @return カラーバッファの高さ
*/
public getBufferHeight(): number {
return this._bufferHeight;
}
/**
* オフスクリーンサーフェスが有効かどうかを確認する。
*
* @return 有効な場合はtrue、無効な場合はfalse
*/
public isValid(): boolean {
return this._renderTexture != null;
}
/**
* 以前のフレームバッファを取得する。
*
* @return 以前のフレームバッファ
*/
public getOldFBO(): WebGLFramebuffer {
return this._oldFbo;
}
/**
* コンストラクタ
*/
constructor() {
this._gl = null;
this._colorBuffer = null;
this._renderTexture = null;
this._bufferWidth = 0;
this._bufferHeight = 0;
this._oldFbo = null;
}
protected _gl: WebGLRenderingContext | WebGL2RenderingContext; // WebGLのコンテキスト
protected _colorBuffer: WebGLTexture; // カラーバッファ
protected _renderTexture: WebGLFramebuffer; // フレームバッファ
protected _bufferWidth: number; // カラーバッファの幅
protected _bufferHeight: number; // カラーバッファの高さ
private _oldFbo: WebGLFramebuffer; // 以前のフレームバッファ
}
// Namespace definition for compatibility.
import * as $ from './cubismrendertarget_webgl';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismOffscreenSurface_WebGL = $.CubismRenderTarget_WebGL;
export type CubismOffscreenSurface_WebGL = $.CubismRenderTarget_WebGL;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
/**
* 矩形形状座標・長さはfloat値を定義するクラス
*/
export class csmRect {
/**
* コンストラクタ
* @param x 左端X座標
* @param y 上端Y座標
* @param w 幅
* @param h 高さ
*/
public constructor(x?: number, y?: number, w?: number, h?: number) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
}
/**
* 矩形中央のX座標を取得する
*/
public getCenterX(): number {
return this.x + 0.5 * this.width;
}
/**
* 矩形中央のY座標を取得する
*/
public getCenterY(): number {
return this.y + 0.5 * this.height;
}
/**
* 右側のX座標を取得する
*/
public getRight(): number {
return this.x + this.width;
}
/**
* 下端のY座標を取得する
*/
public getBottom(): number {
return this.y + this.height;
}
/**
* 矩形に値をセットする
* @param r 矩形のインスタンス
*/
public setRect(r: csmRect): void {
this.x = r.x;
this.y = r.y;
this.width = r.width;
this.height = r.height;
}
/**
* 矩形中央を軸にして縦横を拡縮する
* @param w 幅方向に拡縮する量
* @param h 高さ方向に拡縮する量
*/
public expand(w: number, h: number) {
this.x -= w;
this.y -= h;
this.width += w * 2.0;
this.height += h * 2.0;
}
public x: number; // 左端X座標
public y: number; // 上端Y座標
public width: number; // 幅
public height: number; // 高さ
}
// Namespace definition for compatibility.
import * as $ from './csmrectf';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const csmRect = $.csmRect;
export type csmRect = $.csmRect;
}

View File

@@ -0,0 +1,42 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
/**
* Arrayのサイズを変更する。
* @param curArray
* @param newSize
* @param value
* @param callPlacementNew
*/
export function updateSize<T>(
curArray: Array<T>,
newSize: number,
value: any = null,
callPlacementNew: boolean = null
): void {
const curSize: number = curArray.length;
if (curSize < newSize) {
if (callPlacementNew) {
for (let i: number = curArray.length; i < newSize; i++) {
if (typeof value == 'function') {
// new
curArray[i] = JSON.parse(JSON.stringify(new value()));
} // プリミティブ型なので値渡し
else {
curArray[i] = value;
}
}
} else {
for (let i: number = curArray.length; i < newSize; i++) {
curArray[i] = value;
}
}
} else {
curArray.length = newSize;
}
}

View File

@@ -0,0 +1,162 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import {
CSM_LOG_LEVEL,
CSM_LOG_LEVEL_DEBUG,
CSM_LOG_LEVEL_ERROR,
CSM_LOG_LEVEL_INFO,
CSM_LOG_LEVEL_VERBOSE,
CSM_LOG_LEVEL_WARNING
} from '../cubismframeworkconfig';
import { CubismFramework, LogLevel } from '../live2dcubismframework';
export const CubismLogPrint = (level: LogLevel, fmt: string, args: any[]) => {
CubismDebug.print(level, '[CSM]' + fmt, args);
};
export const CubismLogPrintIn = (level: LogLevel, fmt: string, args: any[]) => {
CubismLogPrint(level, fmt + '\n', args);
};
export const CSM_ASSERT = (expr: any) => {
console.assert(expr);
};
export let CubismLogVerbose: (fmt: string, ...args: any[]) => void;
export let CubismLogDebug: (fmt: string, ...args: any[]) => void;
export let CubismLogInfo: (fmt: string, ...args: any[]) => void;
export let CubismLogWarning: (fmt: string, ...args: any[]) => void;
export let CubismLogError: (fmt: string, ...args: any[]) => void;
if (CSM_LOG_LEVEL <= CSM_LOG_LEVEL_VERBOSE) {
CubismLogVerbose = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Verbose, '[V]' + fmt, args);
};
CubismLogDebug = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Debug, '[D]' + fmt, args);
};
CubismLogInfo = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
};
CubismLogWarning = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
};
CubismLogError = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
};
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_DEBUG) {
CubismLogDebug = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Debug, '[D]' + fmt, args);
};
CubismLogInfo = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
};
CubismLogWarning = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
};
CubismLogError = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
};
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_INFO) {
CubismLogInfo = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
};
CubismLogWarning = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
};
CubismLogError = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
};
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_WARNING) {
CubismLogWarning = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
};
CubismLogError = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
};
} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_ERROR) {
CubismLogError = (fmt: string, ...args: any[]) => {
CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
};
}
/**
* デバッグ用のユーティリティクラス。
* ログの出力、バイトのダンプなど
*/
export class CubismDebug {
/**
* ログを出力する。第一引数にログレベルを設定する。
* CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。
*
* @param logLevel ログレベルの設定
* @param format 書式付き文字列
* @param args 可変長引数
*/
public static print(logLevel: LogLevel, format: string, args?: any[]): void {
// オプションで設定されたログ出力レベルを下回る場合はログに出さない
if (logLevel < CubismFramework.getLoggingLevel()) {
return;
}
const logPrint: Live2DCubismCore.csmLogFunction =
CubismFramework.coreLogFunction;
if (!logPrint) return;
const buffer: string = format.replace(/\{(\d+)\}/g, (m, k) => {
return args[k];
});
logPrint(buffer);
}
/**
* データから指定した長さだけダンプ出力する。
* CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。
*
* @param logLevel ログレベルの設定
* @param data ダンプするデータ
* @param length ダンプする長さ
*/
public static dumpBytes(
logLevel: LogLevel,
data: Uint8Array,
length: number
): void {
for (let i = 0; i < length; i++) {
if (i % 16 == 0 && i > 0) this.print(logLevel, '\n');
else if (i % 8 == 0 && i > 0) this.print(logLevel, ' ');
this.print(logLevel, '{0} ', [data[i] & 0xff]);
}
this.print(logLevel, '\n');
}
/**
* private コンストラクタ
*/
private constructor() {}
}
// Namespace definition for compatibility.
import * as $ from './cubismdebug';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismDebug = $.CubismDebug;
export type CubismDebug = $.CubismDebug;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import {
JsonArray,
JsonBoolean,
JsonFloat,
JsonMap,
JsonNullvalue,
JsonString,
Value
} from './cubismjson';
/**
* CubismJsonで実装されているJsonパーサを使用せず、
* TypeScript標準のJsonパーサなどを使用し出力された結果を
* Cubism SDKで定義されているJSONエレメントの要素に
* 置き換える処理をするクラス。
*/
export class CubismJsonExtension {
static parseJsonObject(obj: Value, map: JsonMap) {
Object.keys(obj).forEach(key => {
if (typeof obj[key] == 'boolean') {
const convValue = Boolean(obj[key]);
map.put(key, new JsonBoolean(convValue));
} else if (typeof obj[key] == 'string') {
const convValue = String(obj[key]);
map.put(key, new JsonString(convValue));
} else if (typeof obj[key] == 'number') {
const convValue = Number(obj[key]);
map.put(key, new JsonFloat(convValue));
} else if (obj[key] instanceof Array) {
// HACK: Array 単体で変換できないので unknown に変更してから Value にしている
map.put(
key,
CubismJsonExtension.parseJsonArray(obj[key] as unknown as Value)
);
} else if (obj[key] instanceof Object) {
map.put(
key,
CubismJsonExtension.parseJsonObject(obj[key], new JsonMap())
);
} else if (obj[key] == null) {
map.put(key, new JsonNullvalue());
} else {
// どれにも当てはまらない場合でも処理する
map.put(key, obj[key]);
}
});
return map;
}
protected static parseJsonArray(obj: Value) {
const arr = new JsonArray();
Object.keys(obj).forEach(key => {
const convKey = Number(key);
if (typeof convKey == 'number') {
if (typeof obj[key] == 'boolean') {
const convValue = Boolean(obj[key]);
arr.add(new JsonBoolean(convValue));
} else if (typeof obj[key] == 'string') {
const convValue = String(obj[key]);
arr.add(new JsonString(convValue));
} else if (typeof obj[key] == 'number') {
const convValue = Number(obj[key]);
arr.add(new JsonFloat(convValue));
} else if (obj[key] instanceof Array) {
// HACK: Array 単体で変換できないので unknown に変更してから Value にしている
arr.add(this.parseJsonArray(obj[key] as unknown as Value));
} else if (obj[key] instanceof Object) {
arr.add(this.parseJsonObject(obj[key], new JsonMap()));
} else if (obj[key] == null) {
arr.add(new JsonNullvalue());
} else {
// どれにも当てはまらない場合でも処理する
arr.add(obj[key]);
}
} else if (obj[key] instanceof Array) {
// HACK: Array 単体で変換できないので unknown に変更してから Value にしている
arr.add(this.parseJsonArray(obj[key] as unknown as Value));
} else if (obj[key] instanceof Object) {
arr.add(this.parseJsonObject(obj[key], new JsonMap()));
} else if (obj[key] == null) {
arr.add(new JsonNullvalue());
} else {
const convValue = Array(obj[key]);
// 配列ともObjectとも判定できなかった場合でも処理する
for (let i = 0; i < convValue.length; i++) {
arr.add(convValue[i]);
}
}
});
return arr;
}
}

View File

@@ -0,0 +1,129 @@
/**
* 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 https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
export class CubismString {
/**
* 標準出力の書式を適用した文字列を取得する。
* @param format 標準出力の書式指定文字列
* @param ...args 書式指定文字列に渡す文字列
* @return 書式を適用した文字列
*/
public static getFormatedString(format: string, ...args: any[]): string {
const ret: string = format;
return ret.replace(
/\{(\d+)\}/g,
(
m,
k // m="{0}", k="0"
) => {
return args[k];
}
);
}
/**
* textがstartWordで始まっているかどうかを返す
* @param test 検査対象の文字列
* @param startWord 比較対象の文字列
* @return true textがstartWordで始まっている
* @return false textがstartWordで始まっていない
*/
public static isStartWith(text: string, startWord: string): boolean {
let textIndex = 0;
let startWordIndex = 0;
while (startWord[startWordIndex] != '\0') {
if (
text[textIndex] == '\0' ||
text[textIndex++] != startWord[startWordIndex++]
) {
return false;
}
}
return false;
}
/**
* position位置の文字から数字を解析する。
*
* @param string 文字列
* @param length 文字列の長さ
* @param position 解析したい文字の位置
* @param outEndPos 一文字も読み込まなかった場合はエラー値(-1)が入る
* @return 解析結果の数値
*/
public static stringToFloat(
string: string,
length: number,
position: number,
outEndPos: number[]
): number {
let i: number = position;
let minus = false; // マイナスフラグ
let period = false;
let v1 = 0;
//負号の確認
let c: number = parseInt(string[i]);
if (c < 0) {
minus = true;
i++;
}
//整数部の確認
for (; i < length; i++) {
const c = string[i];
if (0 <= parseInt(c) && parseInt(c) <= 9) {
v1 = v1 * 10 + (parseInt(c) - 0);
} else if (c == '.') {
period = true;
i++;
break;
} else {
break;
}
}
//小数部の確認
if (period) {
let mul = 0.1;
for (; i < length; i++) {
c = parseFloat(string[i]) & 0xff;
if (0 <= c && c <= 9) {
v1 += mul * (c - 0);
} else {
break;
}
mul *= 0.1; //一桁下げる
if (!c) break;
}
}
if (i == position) {
//一文字も読み込まなかった場合
outEndPos[0] = -1; //エラー値が入るので呼び出し元で適切な処理を行う
return 0;
}
if (minus) v1 = -v1;
outEndPos[0] = i;
return v1;
}
/**
* コンストラクタ呼び出し不可な静的クラスにする。
*/
private constructor() {}
}
// Namespace definition for compatibility.
import * as $ from './cubismstring';
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Live2DCubismFramework {
export const CubismString = $.CubismString;
export type CubismString = $.CubismString;
}

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es6",
"moduleResolution": "node",
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"emitDecoratorMetadata": true,
"noImplicitAny": true,
"useUnknownInCatchVariables": true
},
"include": [
"src/**/*.ts",
"../Core/*.ts"
],
"exclude": [
"node_modules",
"dist"
]
}

View File

@@ -0,0 +1,67 @@
## Definitions
### Live2D Cubism Components
Cubism Web Samples is included in Live2D Cubism Components.
Cubism Web Samples は Live2D Cubism Components に含まれます。
Cubism Web Samples 包括在 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](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)
## Free Material License
Live2D models listed below are available under Free Material License.
* [Free Material License Agreement](https://www.live2d.com/eula/live2d-free-material-license-agreement_en.html)
* [無償提供マテリアルの使用許諾契約書](https://www.live2d.com/eula/live2d-free-material-license-agreement_jp.html)
* [无偿提供素材使用授权协议](https://www.live2d.com/eula/live2d-free-material-license-agreement_cn.html)
```
Samples/Resources/Haru
Samples/Resources/Hiyori
Samples/Resources/Mao
Samples/Resources/Mark
Samples/Resources/Natori
Samples/Resources/Ren
Samples/Resources/Rice
Samples/Resources/Wanko
```
If you use these models, you must agree to the terms of a contract set [here](https://www.live2d.com/eula/live2d-sample-model-terms_en.html) for each model.
---
Please contact us from [here](https://www.live2d.jp/contact/) for more license information.

View File

@@ -0,0 +1,41 @@
[English](NOTICE.md) / [日本語](NOTICE.ja.md)
---
# お知らせ
## [注意事項] Cubism 4 SDK for Web R1 以降へのアップデートに伴う注意
Cubism 4 SDK for Web R1 にてそれ以前のベータ版から正式版のリリースに伴い、
利便性向上のためパッケージ及びリポジトリの構造変更がおこなわれました。
この変更は Cubism 4 SDK for Native と構造を変えずに運用がなされるほか、
ユーザに管理が不必要なファイルが混在することを避けることが理由です。
構造の変更点、及び Cubism 4 SDK for Web beta2 以前のプロジェクトからの更新方法に関して、
[Cubism SDK Manual] に詳細を記載しています。アップデートを行う際は必ずご確認ください。
[Cubism SDK Manual]: https://docs.live2d.com/cubism-sdk-manual/warning-for-cubism4-web-r1-update/
## [注意事項] 依存パッケージでの宣言重複エラーについて (2023-02-23)
Cubism 4 SDK for Web Samples で利用されている依存パッケージのうち `@types/node` に起因する、
宣言重複のエラーが発生する場合があります。
こちらは以下のいずれかの手順で解決が可能であることを確認しております。
### 解決策1: npm-check-updatesを利用する方法
1. ターミナルで `/Samples/TypeScript/Demo` ディレクトリに移動する。
1. コマンド `npm i -g npm-check-updates` を実行する。
1. コマンド `ncu` を実行する。
### 解決策2: @types/node明示的にインストールし直す方法
1. ターミナルで `/Samples/TypeScript/Demo` ディレクトリに移動する。
1. コマンド `npm uninstall @types/node` を実行する。
1. コマンド `npm install @types/node` を実行する。
---
©Live2D

View File

@@ -0,0 +1,38 @@
[English](NOTICE.md) / [日本語](NOTICE.ja.md)
---
# Notices
## [Caution] Precautions for updating to Cubism 4 SDK for Web R1 or later
With the release of the official version of the Cubism 4 SDK for Web R1 from the previous beta version, the structure of packages and repositories has been changed to improve usability.
The reason for this change is to operate without changing the structure of Cubism 4 SDK for Native, and to avoid mixing files that do not require management by users.
Details on structural changes and how to update from projects prior to Cubism 4 SDK for Web beta2 are described in the [Cubism SDK Manual]. Please be sure to check it when updating.
[Cubism SDK Manual]: https://docs.live2d.com/cubism-sdk-manual/warning-for-cubism4-web-r1-update/
## [Caution] About the duplicate declaration error in dependent packages (2023-02-23)
A duplicate declaration error may occur due to `@types/node` among the dependency packages used in the Cubism 4 SDK for Web Samples.
We have confirmed that this can be resolved by one of the following procedures.
### Solution 1: Use npm-check-updates
1. Navigate to the `/Samples/TypeScript/Demo` directory in Terminal.
1. Execute the command `npm i -g npm-check-updates`.
1. Execute the command `ncu`.
### Solution 2: Reinstall @types/node explicitly
1. Navigate to the `/Samples/TypeScript/Demo` directory in Terminal.
1. Execute the command `npm uninstall @types/node`.
1. Execute the command `npm install @types/node`.
---
©Live2D

View File

@@ -0,0 +1,145 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Web Samples
Live2D Cubism Editor で出力したモデルを表示するアプリケーションのサンプル実装です。
Cubism Web Framework および Live2D Cubism Core と組み合わせて使用します。
## ライセンス
本 SDK を使用する前に、[ライセンス](LICENSE.md)をご確認ください。
## お知らせ
本 SDK を使用する前に、[お知らせ](NOTICE.ja.md)をご確認ください。
## Cubism 5.3新機能や過去バージョンとの互換性について
本 SDK はCubism 5.3に対応した製品です。
Cubism 5.3 Editorに搭載された新機能のSDK対応については [こちら](https://docs.live2d.com/cubism-sdk-manual/cubism-5-3-new-functions/)をご確認ください。
過去バージョンのCubism SDKとの互換性については [こちら](https://docs.live2d.com/cubism-sdk-manual/compatibility-with-cubism-5-3/)をご確認ください。
## ディレクトリ構成
```
.
├─ .vscode # Visual Studio Code 用プロジェクト設定ディレクトリ
├─ Core # Live2D Cubism Core が含まれるディレクトリ
├─ Framework # レンダリングやアニメーション機能などのソースコードが含まれるディレクトリ
└─ Samples
├─ Resources # モデルのファイルや画像などのリソースが含まれるディレクトリ
└─ TypeScript # TypeScript のサンプルプロジェクトが含まれるディレクトリ
```
## Live2D Cubism Core for Web
モデルをロードするためのライブラリです。
当リポジトリではCubism Coreを管理していません。
[こちら](https://www.live2d.com/download/cubism-sdk/download-web/)からCubism SDK for Webをダウンロードして、
Coreディレクトリのファイルをコピーしてください。
## 開発環境構築
1. [Node.js] と [Visual Studio Code] をインストールします
1. Visual Studio Code で **本 SDK のトップディレクトリ** を開き、推奨拡張機能をインストールします
* ポップアップ通知の他、拡張機能タブから `@recommended` を入力することで確認できます
### サンプルデモの動作確認
コマンドパレット(*View > Command Palette...*)で `>Tasks: Run Task` を入力することで、タスク一覧が表示されます。
1. タスク一覧から `npm: install - Samples/TypeScript/Demo` を選択して依存パッケージのダウンロードを行います
1. タスク一覧から `npm: build - Samples/TypeScript/Demo` を選択してサンプルデモのビルドを行います
1. タスク一覧から `npm: serve - Samples/TypeScript/Demo` を選択して動作確認用の簡易サーバを起動します
1. ブラウザの URL 欄に `http://localhost:5000` と入力してアクセスします
1. コマンドパレットから `>Tasks: Terminate Task` を入力して `npm: serve` を選択すると簡易サーバが終了します
その他のタスクに関してはサンプルプロジェクトの [README.md](Samples/TypeScript/README.ja.md) を参照ください。
NOTE: デバッグ用の設定は、`.vscode/tasks.json` に記述しています。
### プロジェクトのデバッグ
Visual Studio Code で **本 SDK のトップディレクトリ** を開き、 *F5* キーを押すと組み込みの JavaScript デバッガーChrome や Edge など)によるデバッグが開始されます。
Visual Studio Code 上でブレイクポイントを貼ってブラウザと連動してデバッグを行うことができます。
NOTE: デバッグ用の設定は、`.vscode/launch.json` に記述しています。
## SDKマニュアル
[Cubism SDK Manual](https://docs.live2d.com/cubism-sdk-manual/top/)
## 変更履歴
Samples : [CHANGELOG.md](CHANGELOG.md)
Framework : [CHANGELOG.md](Framework/CHANGELOG.md)
Core : [CHANGELOG.md](Core/CHANGELOG.md)
## 開発環境
### Node.js
* 25.8.2
* 24.14.1
## 動作確認環境
| プラットフォーム | ブラウザ | バージョン |
| --- | --- | --- |
| Android | Google Chrome | 145.0.7680.164 |
| Android | Microsoft Edge | 146.0.3856.71 |
| Android | Mozilla Firefox | 148.0.2 |
| iOS / iPadOS | Google Chrome | 147.0.7727.22 |
| iOS / iPadOS | Microsoft Edge | 146.0.3856.77 |
| iOS / iPadOS | Mozilla Firefox | 149.0 |
| iOS / iPadOS | Safari | 26.4 |
| macOS | Google Chrome | 146.0.7680.165 |
| macOS | Microsoft Edge | 146.0.3856.72 |
| macOS | Mozilla Firefox | 149.0 |
| macOS | Safari | 26.4 |
| Windows | Google Chrome | 146.0.7680.165 |
| Windows | Microsoft Edge | 146.0.3856.78 |
| Windows | Mozilla Firefox | 149.0 |
Note: 動作確認時のサーバの起動は `./Samples/TypeScript/Demo/package.json``serve` スクリプトを使用して行っています。
## プロジェクトへの貢献
プロジェクトに貢献する方法はたくさんあります。バグのログの記録、このGitHubでのプルリクエストの送信、Live2Dコミュニティでの問題の報告と提案の作成です。
### フォークとプルリクエスト
修正、改善、さらには新機能をもたらすかどうかにかかわらず、プルリクエストに感謝します。ただし、ラッパーは可能な限り軽量で浅くなるように設計されているため、バグ修正とメモリ/パフォーマンスの改善のみを行う必要があることに注意してください。メインリポジトリを可能な限りクリーンに保つために、必要に応じて個人用フォークと機能ブランチを作成してください。
### バグ
Live2Dコミュニティでは、問題のレポートと機能リクエストを定期的にチェックしています。バグレポートを提出する前に、Live2Dコミュニティで検索して、問題のレポートまたは機能リクエストがすでに投稿されているかどうかを確認してください。問題がすでに存在する場合は、関連するコメントを追記してください。
### 提案
SDKの将来についてのフィードバックにも関心があります。Live2Dコミュニティで提案や機能のリクエストを送信できます。このプロセスをより効果的にするために、それらをより明確に定義するのに役立つより多くの情報を含めるようお願いしています。
## フォーラム
ユーザー同士でCubism SDKの活用方法の提案や質問をしたい場合は、是非フォーラムをご活用ください。
- [Live2D 公式クリエイターズフォーラム](https://creatorsforum.live2d.com/)
- [Live2D Creator's Forum(English)](https://community.live2d.com/)

View File

@@ -0,0 +1,146 @@
[English](README.md) / [日本語](README.ja.md)
---
# Cubism Web Samples
This is a sample implementation of an application that displays models output by Live2D Cubism Editor.
It is used in conjunction with the Cubism Web Framework and Live2D Cubism Core.
## License
Please check the [license](LICENSE.md) before using this SDK.
## Notices
Please check the [notices](NOTICE.md) before using this SDK.
## Compatibility with Cubism 5.3 new features and previous Cubism SDK versions
This SDK is compatible with Cubism 5.3.
For SDK compatibility with new features in Cubism 5.3 Editor, please refer to [here](https://docs.live2d.com/en/cubism-sdk-manual/cubism-5-3-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-3/).
## Directory structure
```
.
├─ .vscode # Project settings directory for Visual Studio Code
├─ Core # Directory containing Live2D Cubism Core
├─ Framework # Directory containing source code such as rendering and animation functions
└─ Samples
├─ Resources # Directory containing resources such as model files and images
└─ TypeScript # Directory containing TypeScript sample projects
```
## Live2D Cubism Core for Web
A library for loading the model.
This repository does not manage Cubism Core.
Download the Cubism SDK for Web from [here](https://www.live2d.com/download/cubism-sdk/download-web/) and copy the files in the Core directory.
## Development environment construction
1. Install [Node.js] and [Visual Studio Code]
1. Open **the top directory of this SDK** in Visual Studio Code and install the recommended extensions
* In addition to pop-up notifications, you can check the others by typing `@recommended` from the Extensions tab
### Operation check of sample demo
Enter `>Tasks: Run Task` in the command palette (*View > Command Palette...*) to display the task list.
1. Select `npm: install - Samples/TypeScript/Demo` from the task list to download the dependent packages
1. Select `npm: build - Samples/TypeScript/Demo` from the task list to build the sample demo
1. Select `npm: serve - Samples/TypeScript/Demo` from the task list to start the simple server for operation check
1. Enter `http://localhost:5000` in the URL field of your browser to access it
1. Enter `>Tasks: Terminate Task` from the command palette and select `npm: serve` to terminate the simple server
For other tasks, see [README.md](Samples/TypeScript/README.md) of the sample project.
NOTE: Settings for debugging are described in `.vscode/tasks.json`.
### Project debugging
Open **the top directory of this SDK** in Visual Studio Code and press *F5* to start debugging with the built-in JavaScript Debugger (for example Chrome or Edge).
You can place breakpoints in Visual Studio Code and debug in the browser.
NOTE: Settings for debugging are described in `.vscode/launch.json`.
## SDK manual
[Cubism SDK Manual](https://docs.live2d.com/cubism-sdk-manual/top/)
## Changelog
Samples : [CHANGELOG.md](CHANGELOG.md)
Framework : [CHANGELOG.md](Framework/CHANGELOG.md)
Core : [CHANGELOG.md](Core/CHANGELOG.md)
## Development environment
### Node.js
* 25.8.2
* 24.14.1
## Operation environment
| Platform | Browser | Version |
| --- | --- | --- |
| Android | Google Chrome | 145.0.7680.164 |
| Android | Microsoft Edge | 146.0.3856.71 |
| Android | Mozilla Firefox | 148.0.2 |
| iOS / iPadOS | Google Chrome | 147.0.7727.22 |
| iOS / iPadOS | Microsoft Edge | 146.0.3856.77 |
| iOS / iPadOS | Mozilla Firefox | 149.0 |
| iOS / iPadOS | Safari | 26.4 |
| macOS | Google Chrome | 146.0.7680.165 |
| macOS | Microsoft Edge | 146.0.3856.72 |
| macOS | Mozilla Firefox | 149.0 |
| macOS | Safari | 26.4 |
| Windows | Google Chrome | 146.0.7680.165 |
| Windows | Microsoft Edge | 146.0.3856.78 |
| Windows | Mozilla Firefox | 149.0 |
Note: You can start the server for operation check by running the `serve` script of `./Samples/TypeScript/Demo/package.json`.
## 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. Note, however, that the wrapper is designed to be as lightweight and shallow as possible and should therefore only be subject to bug fixes and memory/performance improvements. 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/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,334 @@
{
"Version": 3,
"Parameters": [
{
"Id": "ParamAngleX",
"GroupId": "ParamGroupFace",
"Name": "角度 X"
},
{
"Id": "ParamAngleY",
"GroupId": "ParamGroupFace",
"Name": "角度 Y"
},
{
"Id": "ParamAngleZ",
"GroupId": "ParamGroupFace",
"Name": "角度 Z"
},
{
"Id": "ParamTere",
"GroupId": "ParamGroupFace",
"Name": "照れ"
},
{
"Id": "ParamFaceForm",
"GroupId": "ParamGroupFace",
"Name": "顔の拡縮"
},
{
"Id": "ParamEyeLOpen",
"GroupId": "ParamGroupEyes",
"Name": "左目 開閉"
},
{
"Id": "ParamEyeLSmile",
"GroupId": "ParamGroupEyes",
"Name": "左目 笑顔"
},
{
"Id": "ParamEyeROpen",
"GroupId": "ParamGroupEyes",
"Name": "右目 開閉"
},
{
"Id": "ParamEyeRSmile",
"GroupId": "ParamGroupEyes",
"Name": "右目 笑顔"
},
{
"Id": "ParamEyeForm",
"GroupId": "ParamGroupEyes",
"Name": "眼 変形"
},
{
"Id": "ParamEyeBallForm",
"GroupId": "ParamGroupEyes",
"Name": "目玉 収縮"
},
{
"Id": "ParamTear",
"GroupId": "ParamGroupEyes",
"Name": "涙"
},
{
"Id": "ParamEyeBallX",
"GroupId": "ParamGroupEyes",
"Name": "目玉 X"
},
{
"Id": "ParamEyeBallY",
"GroupId": "ParamGroupEyes",
"Name": "目玉 Y"
},
{
"Id": "ParamBrowLY",
"GroupId": "ParamGroup",
"Name": "左眉 上下"
},
{
"Id": "ParamBrowRY",
"GroupId": "ParamGroup",
"Name": "右眉 上下"
},
{
"Id": "ParamBrowLX",
"GroupId": "ParamGroup",
"Name": "左眉 左右"
},
{
"Id": "ParamBrowRX",
"GroupId": "ParamGroup",
"Name": "右眉 左右"
},
{
"Id": "ParamBrowLAngle",
"GroupId": "ParamGroup",
"Name": "左眉 角度"
},
{
"Id": "ParamBrowRAngle",
"GroupId": "ParamGroup",
"Name": "右眉 角度"
},
{
"Id": "ParamBrowLForm",
"GroupId": "ParamGroup",
"Name": "左眉 変形"
},
{
"Id": "ParamBrowRForm",
"GroupId": "ParamGroup",
"Name": "右眉 変形"
},
{
"Id": "ParamMouthForm",
"GroupId": "ParamGroupMouth",
"Name": "口 変形"
},
{
"Id": "ParamMouthOpenY",
"GroupId": "ParamGroupMouth",
"Name": "口 開閉"
},
{
"Id": "ParamScarf",
"GroupId": "ParamGroup2",
"Name": "スカーフ揺れ"
},
{
"Id": "ParamBodyAngleX",
"GroupId": "ParamGroup2",
"Name": "体の回転 X"
},
{
"Id": "ParamBodyAngleY",
"GroupId": "ParamGroup2",
"Name": "体の回転 Y"
},
{
"Id": "ParamBodyAngleZ",
"GroupId": "ParamGroup2",
"Name": "体の回転 Z"
},
{
"Id": "ParamBodyUpper",
"GroupId": "ParamGroup2",
"Name": "上体"
},
{
"Id": "ParamBreath",
"GroupId": "ParamGroup2",
"Name": "呼吸"
},
{
"Id": "ParamBustY",
"GroupId": "ParamGroup2",
"Name": "胸 揺れ"
},
{
"Id": "ParamArmLA",
"GroupId": "ParamGroupArms",
"Name": "左腕 A"
},
{
"Id": "ParamArmRA",
"GroupId": "ParamGroupArms",
"Name": "右腕 A"
},
{
"Id": "ParamArmLB",
"GroupId": "ParamGroupArms",
"Name": "右腕 B"
},
{
"Id": "ParamArmRB",
"GroupId": "ParamGroupArms",
"Name": "左腕 B"
},
{
"Id": "ParamHandChangeR",
"GroupId": "ParamGroupArms",
"Name": "右手切替"
},
{
"Id": "ParamHandAngleR",
"GroupId": "ParamGroupArms",
"Name": "右手首角度"
},
{
"Id": "ParamHandDhangeL",
"GroupId": "ParamGroupArms",
"Name": "左手切替"
},
{
"Id": "ParamHandAngleL",
"GroupId": "ParamGroupArms",
"Name": "左手首角度"
},
{
"Id": "ParamHairFront",
"GroupId": "ParamGroup3",
"Name": "髪揺れ 前"
},
{
"Id": "ParamHairSide",
"GroupId": "ParamGroup3",
"Name": "髪揺れ 横"
},
{
"Id": "ParamHairBack",
"GroupId": "ParamGroup3",
"Name": "髪揺れ 後"
}
],
"ParameterGroups": [
{
"Id": "ParamGroupFace",
"GroupId": "",
"Name": "顔"
},
{
"Id": "ParamGroupEyes",
"GroupId": "",
"Name": "目"
},
{
"Id": "ParamGroup",
"GroupId": "",
"Name": "眉毛"
},
{
"Id": "ParamGroupMouth",
"GroupId": "",
"Name": "口"
},
{
"Id": "ParamGroup2",
"GroupId": "",
"Name": "胴体"
},
{
"Id": "ParamGroupArms",
"GroupId": "",
"Name": "腕"
},
{
"Id": "ParamGroup3",
"GroupId": "",
"Name": "髪揺れ"
}
],
"Parts": [
{
"Id": "Part01Core",
"Name": "コアパーツ"
},
{
"Id": "Part01Hoho001",
"Name": "頬"
},
{
"Id": "Part01Brow001",
"Name": "まゆ毛"
},
{
"Id": "Part01Tear",
"Name": "涙"
},
{
"Id": "Part01EyeBall001",
"Name": "目玉"
},
{
"Id": "Part01Eye001",
"Name": "目"
},
{
"Id": "Part01Nose001",
"Name": "鼻"
},
{
"Id": "Part01Mouth001",
"Name": "口"
},
{
"Id": "Part01Face001",
"Name": "顔"
},
{
"Id": "Part01Ear001",
"Name": "耳"
},
{
"Id": "Part01Neck001",
"Name": "首"
},
{
"Id": "Part01HairFront001",
"Name": "前髪"
},
{
"Id": "Part01HairSide001",
"Name": "横髪"
},
{
"Id": "Part01HairBack001",
"Name": "後ろ髪"
},
{
"Id": "Part01ArmRB001",
"Name": "左腕 B"
},
{
"Id": "Part01ArmLB001",
"Name": "右腕 B"
},
{
"Id": "Part01ArmRA001",
"Name": "右腕 A"
},
{
"Id": "Part01ArmLA001",
"Name": "左腕 A"
},
{
"Id": "Part01Body001",
"Name": "制服"
},
{
"Id": "Part01Sketch",
"Name": "[ 下絵 ]"
}
]
}

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