diff --git a/demo/addons/gd_cubism/example/custom_efx_01.gd b/demo/addons/gd_cubism/example/custom_efx_01.gd deleted file mode 100644 index bf05ec2..0000000 --- a/demo/addons/gd_cubism/example/custom_efx_01.gd +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-License-Identifier: MIT -# SPDX-FileCopyrightText: 2023 MizunagiKB -extends GDCubismEffectCustom - - -var first_time: bool = true -var counter: float = 0.0 -var base_vct: Vector2 -var base_scale: Vector2 -@export var art_mesh_name: String = "ArtMesh278" -@export var vector_index_0: int = 0 -@export var vector_index_1: int = 1 - - -func _on_cubism_process(model: GDCubismUserModel, delta: float): - var dict_mesh: Dictionary = model.get_meshes() - - if dict_mesh.has(art_mesh_name) == false: - return - - var ary_mesh: ArrayMesh = dict_mesh[art_mesh_name] - var ary_surface: Array = ary_mesh.surface_get_arrays(0) - var mesh_vertex: PackedVector2Array = ary_surface[ArrayMesh.ARRAY_VERTEX] - - if first_time == true: - var fr = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_0] - var to = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_1] - base_vct = (to - fr) - base_scale = $Sprite2D.scale - first_time = false - - var fr = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_0] - var to = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_1] - var calc_vct = (to - fr) - var scale: float = calc_vct.length() / base_vct.length() - - $Sprite2D.position = fr - $Sprite2D.rotation = base_vct.normalized().cross(calc_vct.normalized()) - $Sprite2D.scale = base_scale * scale diff --git a/demo/addons/gd_cubism/example/custom_efx_02.gd b/demo/addons/gd_cubism/example/custom_efx_02.gd index 2468d9b..bf05ec2 100644 --- a/demo/addons/gd_cubism/example/custom_efx_02.gd +++ b/demo/addons/gd_cubism/example/custom_efx_02.gd @@ -3,84 +3,37 @@ extends GDCubismEffectCustom -@export var param_mouth_name: String = "ParamMouthA" -@export var min_db: float = 60.0 -@export var min_voice_freq: int = 0 -@export var max_voice_freq: int = 3200 -@export var history_size: int = 6 - -@export_category("Audio") -@export var audio_bus_name: String = "Voice" -@export var audio_efx_index: int = 0 - -var spectrum: AudioEffectSpectrumAnalyzerInstance -var param_mouth: GDCubismParameter -var ary_volume_history: Array -var history_position: int = 0 -var lipsync_ready: bool = false - - -func array_rebuild(): - if history_size != ary_volume_history.size(): - ary_volume_history.resize(history_size) - ary_volume_history.fill(0.0) - - -func array_avg() -> float: - var sum_v: float = 0.0 - for v in ary_volume_history: - sum_v += v - return sum_v / ary_volume_history.size() - - -func _on_cubism_init(model: GDCubismUserModel): - param_mouth = null - ary_volume_history.clear() - history_position = 0 - - var ary_param = model.get_parameters() - - for param in ary_param: - if param.id == param_mouth_name: - param_mouth = param - - var bus_index = AudioServer.get_bus_index(audio_bus_name) - spectrum = AudioServer.get_bus_effect_instance(bus_index, audio_efx_index) - - if spectrum == null: - lipsync_ready = false - else: - lipsync_ready = true - - -func _on_cubism_term(model: GDCubismUserModel): - lipsync_ready = false - param_mouth = null - ary_volume_history.clear() - history_position = 0 +var first_time: bool = true +var counter: float = 0.0 +var base_vct: Vector2 +var base_scale: Vector2 +@export var art_mesh_name: String = "ArtMesh278" +@export var vector_index_0: int = 0 +@export var vector_index_1: int = 1 func _on_cubism_process(model: GDCubismUserModel, delta: float): - if lipsync_ready == false: - return + var dict_mesh: Dictionary = model.get_meshes() - array_rebuild() - - var m: float = spectrum.get_magnitude_for_frequency_range( - min_voice_freq, - max_voice_freq - ).length() - var v = clamp((min_db + linear_to_db(m)) / min_db, 0.0, 1.0) - - ary_volume_history[history_position] = v - history_position += 1 - - if history_position >= ary_volume_history.size(): - history_position = 0 - - var avg_v: float = array_avg() + if dict_mesh.has(art_mesh_name) == false: + return - if param_mouth != null: - param_mouth.value = max(param_mouth.value - 0.2, 0.0) - if avg_v > 0.1: - param_mouth.value = 1.0 + var ary_mesh: ArrayMesh = dict_mesh[art_mesh_name] + var ary_surface: Array = ary_mesh.surface_get_arrays(0) + var mesh_vertex: PackedVector2Array = ary_surface[ArrayMesh.ARRAY_VERTEX] + + if first_time == true: + var fr = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_0] + var to = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_1] + base_vct = (to - fr) + base_scale = $Sprite2D.scale + first_time = false + + var fr = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_0] + var to = ary_surface[ArrayMesh.ARRAY_VERTEX][vector_index_1] + var calc_vct = (to - fr) + var scale: float = calc_vct.length() / base_vct.length() + + $Sprite2D.position = fr + $Sprite2D.rotation = base_vct.normalized().cross(calc_vct.normalized()) + $Sprite2D.scale = base_scale * scale diff --git a/demo/addons/gd_cubism/example/custom_efx_03.gd b/demo/addons/gd_cubism/example/custom_efx_03.gd new file mode 100644 index 0000000..2468d9b --- /dev/null +++ b/demo/addons/gd_cubism/example/custom_efx_03.gd @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2023 MizunagiKB +extends GDCubismEffectCustom + + +@export var param_mouth_name: String = "ParamMouthA" +@export var min_db: float = 60.0 +@export var min_voice_freq: int = 0 +@export var max_voice_freq: int = 3200 +@export var history_size: int = 6 + +@export_category("Audio") +@export var audio_bus_name: String = "Voice" +@export var audio_efx_index: int = 0 + +var spectrum: AudioEffectSpectrumAnalyzerInstance +var param_mouth: GDCubismParameter +var ary_volume_history: Array +var history_position: int = 0 +var lipsync_ready: bool = false + + +func array_rebuild(): + if history_size != ary_volume_history.size(): + ary_volume_history.resize(history_size) + ary_volume_history.fill(0.0) + + +func array_avg() -> float: + var sum_v: float = 0.0 + for v in ary_volume_history: + sum_v += v + return sum_v / ary_volume_history.size() + + +func _on_cubism_init(model: GDCubismUserModel): + param_mouth = null + ary_volume_history.clear() + history_position = 0 + + var ary_param = model.get_parameters() + + for param in ary_param: + if param.id == param_mouth_name: + param_mouth = param + + var bus_index = AudioServer.get_bus_index(audio_bus_name) + spectrum = AudioServer.get_bus_effect_instance(bus_index, audio_efx_index) + + if spectrum == null: + lipsync_ready = false + else: + lipsync_ready = true + + +func _on_cubism_term(model: GDCubismUserModel): + lipsync_ready = false + param_mouth = null + ary_volume_history.clear() + history_position = 0 + + +func _on_cubism_process(model: GDCubismUserModel, delta: float): + if lipsync_ready == false: + return + + array_rebuild() + + var m: float = spectrum.get_magnitude_for_frequency_range( + min_voice_freq, + max_voice_freq + ).length() + var v = clamp((min_db + linear_to_db(m)) / min_db, 0.0, 1.0) + + ary_volume_history[history_position] = v + history_position += 1 + + if history_position >= ary_volume_history.size(): + history_position = 0 + + var avg_v: float = array_avg() + + if param_mouth != null: + param_mouth.value = max(param_mouth.value - 0.2, 0.0) + if avg_v > 0.1: + param_mouth.value = 1.0 diff --git a/demo/addons/gd_cubism/example/demo_effect_custom_01.gd b/demo/addons/gd_cubism/example/demo_effect_custom_01.gd new file mode 100644 index 0000000..d37a1fa --- /dev/null +++ b/demo/addons/gd_cubism/example/demo_effect_custom_01.gd @@ -0,0 +1,59 @@ +extends Node2D + + +enum E_PARAM_MODE { + PROCESS, + SIGNAL +} + +var param_valid: bool = false +var param_mode: E_PARAM_MODE = E_PARAM_MODE.PROCESS +var l_eye: GDCubismParameter +var r_eye: GDCubismParameter + + +func _ready(): + pass + + +func _process(delta): + if param_mode == E_PARAM_MODE.PROCESS: + var model: GDCubismUserModel = $Sprite2D/GDCubismUserModel + + for _o in model.get_parameters(): + var o: GDCubismParameter = _o + if o.id == "ParamEyeLOpen": + o.value = 0.0 + if o.id == "ParamEyeROpen": + o.value = 0.0 + + +func _on_gd_cubism_effect_custom_cubism_init(model: GDCubismUserModel): + if param_mode == E_PARAM_MODE.SIGNAL: + for _o in model.get_parameters(): + var o: GDCubismParameter = _o + if o.id == "ParamEyeLOpen": + l_eye = o + if o.id == "ParamEyeROpen": + r_eye = o + param_valid = true + + +func _on_gd_cubism_effect_custom_cubism_term(model): + if param_mode == E_PARAM_MODE.SIGNAL: + param_valid = false + + +func _on_gd_cubism_effect_custom_cubism_prologue(model, delta): + pass # Replace with function body. + + +func _on_gd_cubism_effect_custom_cubism_process(model: GDCubismUserModel, delta: float): + if param_mode == E_PARAM_MODE.SIGNAL: + if param_valid == true: + l_eye.value = 0.0 + r_eye.value = 0.0 + + +func _on_gd_cubism_effect_custom_cubism_epilogue(model, delta): + pass # Replace with function body. diff --git a/demo/addons/gd_cubism/example/demo_effect_custom_01.tscn b/demo/addons/gd_cubism/example/demo_effect_custom_01.tscn index 5538d96..256ef52 100644 --- a/demo/addons/gd_cubism/example/demo_effect_custom_01.tscn +++ b/demo/addons/gd_cubism/example/demo_effect_custom_01.tscn @@ -1,9 +1,9 @@ -[gd_scene load_steps=3 format=3 uid="uid://drt0184vymus2"] +[gd_scene load_steps=2 format=3 uid="uid://0sa003phknvv"] -[ext_resource type="Script" path="res://addons/gd_cubism/example/custom_efx_01.gd" id="1_lkrys"] -[ext_resource type="Texture2D" uid="uid://c8o4yd28cp3jy" path="res://icon.svg" id="2_ur0ha"] +[ext_resource type="Script" path="res://addons/gd_cubism/example/demo_effect_custom_01.gd" id="1_2sdfp"] [node name="demo_effect_custom_01" type="Node2D"] +script = ExtResource("1_2sdfp") [node name="Sprite2D" type="Sprite2D" parent="."] position = Vector2(640, 512) @@ -16,11 +16,9 @@ size = Vector2i(1024, 1024) render_target_update_mode = 4 [node name="GDCubismEffectCustom" type="GDCubismEffectCustom" parent="Sprite2D/GDCubismUserModel"] -script = ExtResource("1_lkrys") -[node name="Sprite2D" type="Sprite2D" parent="Sprite2D/GDCubismUserModel/GDCubismEffectCustom"] -z_index = 1024 -scale = Vector2(0.5, 0.5) -texture = ExtResource("2_ur0ha") - -[connection signal="cubism_process" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" method="_on_cubism_process"] +[connection signal="cubism_epilogue" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="." method="_on_gd_cubism_effect_custom_cubism_epilogue"] +[connection signal="cubism_init" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="." method="_on_gd_cubism_effect_custom_cubism_init"] +[connection signal="cubism_process" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="." method="_on_gd_cubism_effect_custom_cubism_process"] +[connection signal="cubism_prologue" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="." method="_on_gd_cubism_effect_custom_cubism_prologue"] +[connection signal="cubism_term" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="." method="_on_gd_cubism_effect_custom_cubism_term"] diff --git a/demo/addons/gd_cubism/example/demo_effect_custom_02.tscn b/demo/addons/gd_cubism/example/demo_effect_custom_02.tscn index 200e4f5..e37b025 100644 --- a/demo/addons/gd_cubism/example/demo_effect_custom_02.tscn +++ b/demo/addons/gd_cubism/example/demo_effect_custom_02.tscn @@ -1,30 +1,26 @@ -[gd_scene load_steps=3 format=3 uid="uid://xk7rqvf532rj"] +[gd_scene load_steps=3 format=3 uid="uid://drt0184vymus2"] [ext_resource type="Script" path="res://addons/gd_cubism/example/custom_efx_02.gd" id="1_ittjg"] +[ext_resource type="Texture2D" uid="uid://c8o4yd28cp3jy" path="res://icon.svg" id="2_0d7x8"] -[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_w6cby"] -blend_mode = 4 -light_mode = 1 - -[node name="demo_effect_custom_02" type="Node2D"] +[node name="demo_effect_custom_01" type="Node2D"] [node name="Sprite2D" type="Sprite2D" parent="."] -material = SubResource("CanvasItemMaterial_w6cby") -position = Vector2(256, 256) +position = Vector2(640, 512) [node name="GDCubismUserModel" type="GDCubismUserModel" parent="Sprite2D"] disable_3d = true transparent_bg = true gui_disable_input = true +size = Vector2i(1024, 1024) render_target_update_mode = 4 [node name="GDCubismEffectCustom" type="GDCubismEffectCustom" parent="Sprite2D/GDCubismUserModel"] script = ExtResource("1_ittjg") -[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."] -autoplay = true -bus = &"Voice" +[node name="Sprite2D" type="Sprite2D" parent="Sprite2D/GDCubismUserModel/GDCubismEffectCustom"] +z_index = 1024 +scale = Vector2(0.5, 0.5) +texture = ExtResource("2_0d7x8") -[connection signal="cubism_init" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" method="_on_cubism_init"] [connection signal="cubism_process" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" method="_on_cubism_process"] -[connection signal="cubism_term" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" method="_on_cubism_term"] diff --git a/demo/addons/gd_cubism/example/demo_effect_custom_03.tscn b/demo/addons/gd_cubism/example/demo_effect_custom_03.tscn index 0302743..6a54649 100644 --- a/demo/addons/gd_cubism/example/demo_effect_custom_03.tscn +++ b/demo/addons/gd_cubism/example/demo_effect_custom_03.tscn @@ -1,16 +1,16 @@ -[gd_scene load_steps=3 format=3 uid="uid://dhfu0rwvwpprc"] +[gd_scene load_steps=3 format=3 uid="uid://xk7rqvf532rj"] -[ext_resource type="Script" path="res://addons/gd_cubism/example/demo_effect_custom_03.gd" id="1_j4tk3"] +[ext_resource type="Script" path="res://addons/gd_cubism/example/custom_efx_03.gd" id="1_lh0xb"] -[sub_resource type="ViewportTexture" id="ViewportTexture_q0fsk"] -viewport_path = NodePath("Sprite2D/GDCubismUserModel") +[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_w6cby"] +blend_mode = 4 +light_mode = 1 -[node name="demo_effect_custom_03" type="Node2D"] -script = ExtResource("1_j4tk3") +[node name="demo_effect_custom_02" type="Node2D"] [node name="Sprite2D" type="Sprite2D" parent="."] -position = Vector2(640, 360) -texture = SubResource("ViewportTexture_q0fsk") +material = SubResource("CanvasItemMaterial_w6cby") +position = Vector2(256, 256) [node name="GDCubismUserModel" type="GDCubismUserModel" parent="Sprite2D"] disable_3d = true @@ -18,4 +18,13 @@ transparent_bg = true gui_disable_input = true render_target_update_mode = 4 -[node name="GDCubismEffectTargetPoint" type="GDCubismEffectTargetPoint" parent="Sprite2D/GDCubismUserModel"] +[node name="GDCubismEffectCustom" type="GDCubismEffectCustom" parent="Sprite2D/GDCubismUserModel"] +script = ExtResource("1_lh0xb") + +[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."] +autoplay = true +bus = &"Voice" + +[connection signal="cubism_init" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" method="_on_cubism_init"] +[connection signal="cubism_process" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" method="_on_cubism_process"] +[connection signal="cubism_term" from="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" to="Sprite2D/GDCubismUserModel/GDCubismEffectCustom" method="_on_cubism_term"] diff --git a/demo/addons/gd_cubism/example/demo_effect_custom_03.gd b/demo/addons/gd_cubism/example/demo_effect_target_point.gd similarity index 100% rename from demo/addons/gd_cubism/example/demo_effect_custom_03.gd rename to demo/addons/gd_cubism/example/demo_effect_target_point.gd diff --git a/demo/addons/gd_cubism/example/demo_effect_target_point.tscn b/demo/addons/gd_cubism/example/demo_effect_target_point.tscn new file mode 100644 index 0000000..4ab4fca --- /dev/null +++ b/demo/addons/gd_cubism/example/demo_effect_target_point.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=2 format=3 uid="uid://dhfu0rwvwpprc"] + +[ext_resource type="Script" path="res://addons/gd_cubism/example/demo_effect_target_point.gd" id="1_j4tk3"] + +[node name="demo_effect_custom_03" type="Node2D"] +script = ExtResource("1_j4tk3") + +[node name="Sprite2D" type="Sprite2D" parent="."] +position = Vector2(640, 360) + +[node name="GDCubismUserModel" type="GDCubismUserModel" parent="Sprite2D"] +disable_3d = true +transparent_bg = true +gui_disable_input = true +render_target_update_mode = 4 + +[node name="GDCubismEffectTargetPoint" type="GDCubismEffectTargetPoint" parent="Sprite2D/GDCubismUserModel"] diff --git a/docs-src/modules/ROOT/examples/custom_animation_process.gd b/docs-src/modules/ROOT/examples/custom_animation_process.gd new file mode 100644 index 0000000..f8a09f6 --- /dev/null +++ b/docs-src/modules/ROOT/examples/custom_animation_process.gd @@ -0,0 +1,22 @@ +extends Node2D + + +var param_valid: bool = false +var l_eye: GDCubismParameter +var r_eye: GDCubismParameter + + +func _ready(): + pass + + +func _process(delta): + var model: GDCubismUserModel = $Sprite2D/GDCubismUserModel + + for _o in model.get_parameters(): + var o: GDCubismParameter = _o + if o.id == "ParamEyeLOpen": + o.value = 0.0 + if o.id == "ParamEyeROpen": + o.value = 0.0 + diff --git a/docs-src/modules/ROOT/examples/custom_animation_signal.gd b/docs-src/modules/ROOT/examples/custom_animation_signal.gd new file mode 100644 index 0000000..bab425c --- /dev/null +++ b/docs-src/modules/ROOT/examples/custom_animation_signal.gd @@ -0,0 +1,42 @@ +extends Node2D + + +var param_valid: bool = false +var l_eye: GDCubismParameter +var r_eye: GDCubismParameter + + +func _ready(): + pass + + +func _process(delta): + pass + + +func _on_gd_cubism_effect_custom_cubism_init(model: GDCubismUserModel): + for _o in model.get_parameters(): + var o: GDCubismParameter = _o + if o.id == "ParamEyeLOpen": + l_eye = o + if o.id == "ParamEyeROpen": + r_eye = o + param_valid = true + + +func _on_gd_cubism_effect_custom_cubism_term(model): + param_valid = false + + +func _on_gd_cubism_effect_custom_cubism_prologue(model, delta): + pass + + +func _on_gd_cubism_effect_custom_cubism_process(model: GDCubismUserModel, delta: float): + if param_valid == true: + l_eye.value = 0.0 + r_eye.value = 0.0 + + +func _on_gd_cubism_effect_custom_cubism_epilogue(model, delta): + pass diff --git a/docs-src/modules/ROOT/pages/en/api/gd_cubism_user_model.adoc b/docs-src/modules/ROOT/pages/en/api/gd_cubism_user_model.adoc index 9c77f3e..1764569 100644 --- a/docs-src/modules/ROOT/pages/en/api/gd_cubism_user_model.adoc +++ b/docs-src/modules/ROOT/pages/en/api/gd_cubism_user_model.adoc @@ -135,7 +135,7 @@ enum ParameterMode:: Specify this when playing the _Motion_ specified by the <> function. + When this is specified, operations by xref:./gd_cubism_parameter.adoc[] cannot be performed. * NONE_PARAMETER = 1 + -Specify this when using the xref:./gd_cubism_parameter.adoc[] obtained by the <> function to operate the Live2D model. + +Specify this when using the xref:./gd_cubism_parameter.adoc[] obtained by the <> function to operate the Live2D model. + When this is specified, motion playback cannot be performed by the <> function. @@ -427,13 +427,13 @@ for group in dict_motion.keys(): ---- -[[id-method-get_parameter]] -Array get_parameter():: +[[id-method-get_parameters]] +Array get_parameters():: Gets a class to operate the currently held Live2D model. -[[id-method-get_part_opacity]] -Array get_part_opacity():: +[[id-method-get_part_opacities]] +Array get_part_opacities():: Gets a class to operate the part transparency of the currently held Live2D model. diff --git a/docs-src/modules/ROOT/pages/en/build.adoc b/docs-src/modules/ROOT/pages/en/build.adoc index 03403ba..511b72e 100644 --- a/docs-src/modules/ROOT/pages/en/build.adoc +++ b/docs-src/modules/ROOT/pages/en/build.adoc @@ -150,6 +150,24 @@ The following items are required for building: * link:https://scons.org/pages/download.html[SCons 3.0+] build system. ==== +[CAUTION] +==== +There have been reports that using SCons 4.8 results in build failures. + +This issue lies with the Godot Engine and is expected to be resolved in future versions. + +* https://github.com/godotengine/godot/pull/94117 + +For now, please use SCons 4.7. + +[source] +---- +# You can address this by specifying the version when installing scons. +# If a version higher than 4.8 is installed, you can use this method to revert to 4.7. +pip install scons==4.7 +---- +==== + .Installing Visual Studio caveats **** @@ -163,10 +181,10 @@ link:https://docs.godotengine.org/en/stable/contributing/development/compiling/c When ready, run the build with the following command. -[source] +[source, console] -- -scons platform=windows arch=x86_64 target=template_debug -scons platform=windows arch=x86_64 target=template_release +scons platform=windows vsproj=yes arch=x86_64 target=template_debug +scons platform=windows vsproj=yes arch=x86_64 target=template_release -- When the build is completed, the following files will be generated under _demo/addons/gd_cubism/bin_. @@ -186,6 +204,25 @@ The following items are required for building: * link:https://scons.org/pages/download.html[SCons 3.0+] build system. ==== +[CAUTION] +==== +There have been reports that using SCons 4.8 results in build failures. + +This issue lies with the Godot Engine and is expected to be resolved in future versions. + +* https://github.com/godotengine/godot/pull/94117 + +For now, please use SCons 4.7. + +[source] +---- +# You can address this by specifying the version when installing scons. +# If a version higher than 4.8 is installed, you can use this method to revert to 4.7. +pip install scons==4.7 +---- +==== + + When ready, run the build with the following command. [source, bash] diff --git a/docs-src/modules/ROOT/pages/en/custom/animation.adoc b/docs-src/modules/ROOT/pages/en/custom/animation.adoc new file mode 100644 index 0000000..90537a1 --- /dev/null +++ b/docs-src/modules/ROOT/pages/en/custom/animation.adoc @@ -0,0 +1,171 @@ += Custom Animation +:encoding: utf-8 +:lang: en +:author: MizunagiKB +:copyright: 2024 MizunagiKB +:doctype: book +:source-highlighter: highlight.js +:icons: font +:experimental: +:stylesdir: ./docs/res/theme/css +:stylesheet: mizunagi-works.css +ifdef::env-github,env-vscode[] +:adocsuffix: .adoc +endif::env-github,env-vscode[] +ifndef::env-github,env-vscode[] +:adocsuffix: .html +endif::env-github,env-vscode[] + + +xref:ja/custom/animation.adoc[Japanese] / xref:en/custom/animation.adoc[English] + + +[NOTE] +==== +This document is currently under development. +This page is machine translated. +==== + + +== Introduction + +GDCubism is developed with the aim of simplifying the playback of Live2D models on the Godot Engine. + +Since rendering is performed via SubViewport, displaying the model requires some effort, but once displayed, it can be easily checked from the Godot Editor. + +While it is easy to display, it is somewhat unclear how to achieve finer control. For example, if you want to play a motion while changing only some parameters. + +Such operations can be achieved using GDScript; however, at present, they can only be discovered by reading the source code. + +Therefore, I have compiled this document as a reference. + + +=== Process Flow + +When controlling a Live2D model with GDScript, you will control the array obtained by the get_parameter function of GDCubismUserModel, but it will not be reflected in the Live2D model if you set it without thinking. + +The reason is that even if you try to overwrite the parameters, the set motion will overwrite them. + +To set it correctly, it is better to know how GDCubism performs rendering, so I will explain how GDCubism renders the Live2D model. (If you are not interested in the technical background, please skip this section) + + +==== Explanation of the Process + +GDCubismUserModel performs the following processes every frame. The general order is "Load Settings", "Update Live2D Information", and "Rendering". + +. Load Settings +.. Update motion parameters. +.. Update expression parameters. +.. Reflect parameters of GDCubismUserModel. +.. Reflect opacities of GDCubismUserModel. +. Update Live2D Information +.. Update physical parameters. +.. Update pose. +.. Update rendering information. +. Create Rendering Information +.. Calculate the number of SubViewport and Mesh required for rendering. +.. Generate SubViewport and ArrayMesh and assign them to the node tree. + +> In the actual program code (as of version 0.6), it is called as follows with _process as the trigger. + + +NOTE: _process is when playback_process_mode is set to IDLE. For PHYSICS, it is _physics_process, and for MANUAL, it is _advance. + + +* _GDCubismUserModel_._process +** _GDCubismUserModel_._update + +*** _InternalCubismModel_.pro_update +**** _InternalCubismModel_.effect_batch +***** Call of *cubism_prologue* signal +**** Restore parameter information of Live2D model +**** _motionManager.UpdateMotion +**** Save parameter information of Live2D model +**** _expressionManager.UpdateMotion +**** _InternalCubismModel_.GetModelOpacity + +*** _InternalCubismModel_.efx_update +**** If there is a change in the GDCubismEffect* node, call the efx_term, efx_init signalfootnote:id[Since it is called here, the signal is called halfway, so the call position may be changed in future versions.] +**** _InternalCubismModel_.effect_batch +***** Call of *cubism_process* signal + +*** Setting and getting parameter information +*** Setting and getting opacity + +*** _InternalCubismModel_.epi_update +**** Update physical parameters of Live2D model +**** Update pose of Live2D model +**** Update internal state of Live2D model +**** _InternalCubismModel_.effect_batch +***** Call of *cubism_epilogue* signal + +*** _InternalCubismModel_.update_node +**** _InternalCubismModel_.GetRenderer +**** Reflect rendering size +**** Reflect rendering position +**** Secure resources required for rendering +**** Set information required for rendering to GodotEngine + + +==== How to Add Arbitrary Processing + +From here, we will describe how to proceed with the actual processing. + + +===== How to Process in the _process Function + +The series of processes mentioned above are all performed when Godot Engine calls the _process of GDCubismUserModel. + +Therefore, if you want to change the parameters of the Live2D model a little, you can do it afterwards. The point to note is when Godot Engine calls the _process inside GDCubismUserModel. + +This depends on where GDCubismUserModel is in the node tree and where the script is set. To ensure it is reflected, set the playback_process_mode of GDCubismUserModel to MANUAL and call the advance function before operating the parameters. + +This prevents the parameter update from being overwritten before it is reflected. + +[source] +[source,,numlines] +---- +include::example$custom_animation_process.gd[] +---- + + +===== How to Process with GDCubismEffectCustom + +If you add GDCubismEffectCustom as a child element of GDCubismUserModel, you will be able to receive the cubism_prologue, cubism_process, and cubism_epilogue signals. + + +There are five types of signals that GDCubismEffectCustom can receive, but the ones related to the Live2D model are the following three. + +cubism_prologue:: +Called before the motion and expression applied to the Live2D model are updated. Even if you change the parameters when receiving this signal, they will be ignored. + + +Currently, you can operate on the parameters obtained by the get_parameters function of GDCubismUserModel, but the behavior may change in future versions. + +cubism_process:: +Called after the motion and expression applied to the Live2D model are updated. If you update the parameters when receiving this signal, you can overwrite arbitrary processing while playing the motion. + +For example, replacing the shape of the hand, removing the hat, or closing the eyes. + + +cubism_epilogue:: +It is almost the same as cubism_process, but the content of get_parameters of GDCubismUserModel reflects the motion and expression. + +[source] +[source,,numlines] +---- +include::example$custom_animation_signal.gd[] +---- + + +== About the Sample Code + +The following samples are provided using GDCubismEffectCustom. + +|demo_effect_custom_01.tscn +|This is a sample using _GDCubismEffectCustom_. + +|demo_effect_custom_02.tscn +|This is a sample using _GDCubismEffectCustom_. + +|demo_effect_custom_03.tscn +|This is a sample using _GDCubismEffectCustom_. It performs a simple lip sync, moving the mouth according to the volume. diff --git a/docs-src/modules/ROOT/pages/en/usage.adoc b/docs-src/modules/ROOT/pages/en/usage.adoc index 4d04f5d..79b55ac 100644 --- a/docs-src/modules/ROOT/pages/en/usage.adoc +++ b/docs-src/modules/ROOT/pages/en/usage.adoc @@ -153,9 +153,12 @@ If you want to try with your own model, please place the output referring to the |This is a sample using _GDCubismEffectCustom_. |demo_effect_custom_02.tscn -|This is a sample using _GDCubismEffectCustom_. It performs a simple lip sync, moving the mouth according to the volume. +|This is a sample using _GDCubismEffectCustom_. |demo_effect_custom_03.tscn +|This is a sample using _GDCubismEffectCustom_. It performs a simple lip sync, moving the mouth according to the volume. + +|demo_effect_custom_target_pooint.tscn |This is a sample using _GDCubismEffectTargetPoint_. The Live2D model will look in the direction of the mouse position. |demo_effect_hit_area.tscn diff --git a/docs-src/modules/ROOT/pages/ja/api/gd_cubism_user_model.adoc b/docs-src/modules/ROOT/pages/ja/api/gd_cubism_user_model.adoc index ed328fd..f342c9a 100644 --- a/docs-src/modules/ROOT/pages/ja/api/gd_cubism_user_model.adoc +++ b/docs-src/modules/ROOT/pages/ja/api/gd_cubism_user_model.adoc @@ -134,7 +134,7 @@ enum ParameterMode:: <> 関数で指定した _Motion_ を再生する時に指定します。 + この指定がされているときは xref:./gd_cubism_parameter.adoc[] による操作は行えません。 * NONE_PARAMETER = 1 + -<> 関数で取得した xref:./gd_cubism_parameter.adoc[] を使用して、Live2Dモデルを操作する場合に指定します。 + +<> 関数で取得した xref:./gd_cubism_parameter.adoc[] を使用して、Live2Dモデルを操作する場合に指定します。 + この指定がされているときは <> 関数でモーション再生は行えません。 @@ -425,13 +425,13 @@ for group in dict_motion.keys(): ---- -[[id-method-get_parameter]] -Array get_parameter():: +[[id-method-get_parameters]] +Array get_parameters():: 現在保持しているLive2Dモデルを操作するためのクラスを取得します。 -[[id-method-get_part_opacity]] -Array get_part_opacity():: +[[id-method-get_part_opacities]] +Array get_part_opacities():: 現在保持しているLive2Dモデルのパーツ透明度を操作するためのクラスを取得します。 diff --git a/docs-src/modules/ROOT/pages/ja/build.adoc b/docs-src/modules/ROOT/pages/ja/build.adoc index 2ed658e..54f8f83 100644 --- a/docs-src/modules/ROOT/pages/ja/build.adoc +++ b/docs-src/modules/ROOT/pages/ja/build.adoc @@ -43,7 +43,7 @@ git submodule update --init Cubism SDK for Native には Cusism SDK 以外に Cubism Native Framework が含まれています。 -ダウンロードしたものを以下のフォルダ構成になる様に展開してください。(ダウンロードしたものを thirdparty フォルダに配置してファイルを展開すればこの形になるはずです) +ダウンロードしたファイルを以下のフォルダ構成になる様に展開してください。(ダウンロードしたものを thirdparty フォルダに配置してファイルを展開すればこの形になります) [source] ---- @@ -152,6 +152,19 @@ CAUTION: ビルドはCubismSdkForNativeにあるFrameworkよりカスタムビ * link:https://scons.org/pages/download.html[SCons 3.0+] build system. ==== +[CAUTION] +==== +SCons 4.8 を使用すると VS Project が正常に生成出来ず、ビルドに失敗するという報告を受けています。 この問題は Godot Engine 側にあり、将来のバージョンで解消されると思いますが、現状は SCons を 4.7 で使用してください。 +https://github.com/godotengine/godot/pull/94117 + +[source] +---- +# scons をインストールする時にバージョン指定をする事で対応可能です。 +# 4.8 以上が導入されている場合もこの方法で 4.7 にする事が出来ます。 +pip install scons==4.7 +---- +==== + .Visual Studioのインストールに関する注意事項 **** @@ -167,8 +180,8 @@ link:https://docs.godotengine.org/en/stable/contributing/development/compiling/c [source, console] -- -scons platform=windows arch=x86_64 target=template_debug -scons platform=windows arch=x86_64 target=template_release +scons platform=windows vsproj=yes arch=x86_64 target=template_debug +scons platform=windows vsproj=yes arch=x86_64 target=template_release -- ビルドが完了すると以下のファイルが _demo/addons/gd_cubism/bin_ 以下に生成されます。 @@ -188,6 +201,20 @@ scons platform=windows arch=x86_64 target=template_release * link:https://scons.org/pages/download.html[SCons 3.0+] build system. ==== +[CAUTION] +==== +SCons 4.8 を使用するとビルドに失敗するという報告を受けています。 この問題は Godot Engine 側にあり、将来のバージョンで解消されると思いますが、現状は SCons を 4.7 で使用してください。 +https://github.com/godotengine/godot/pull/94117 + +[source] +---- +# scons をインストールする時にバージョン指定をする事で対応可能です。 +# 4.8 以上が導入されている場合もこの方法で 4.7 にする事が出来ます。 +pip install scons==4.7 +---- +==== + + 準備できたら以下の手順でビルドを行ってください。 [source, bash] diff --git a/docs-src/modules/ROOT/pages/ja/custom/animation.adoc b/docs-src/modules/ROOT/pages/ja/custom/animation.adoc new file mode 100644 index 0000000..618fc03 --- /dev/null +++ b/docs-src/modules/ROOT/pages/ja/custom/animation.adoc @@ -0,0 +1,170 @@ += Custom Animation +:encoding: utf-8 +:lang: en +:author: MizunagiKB +:copyright: 2024 MizunagiKB +:doctype: book +:source-highlighter: highlight.js +:icons: font +:experimental: +:stylesdir: ./docs/res/theme/css +:stylesheet: mizunagi-works.css +ifdef::env-github,env-vscode[] +:adocsuffix: .adoc +endif::env-github,env-vscode[] +ifndef::env-github,env-vscode[] +:adocsuffix: .html +endif::env-github,env-vscode[] + + +xref:ja/custom/animation.adoc[Japanese] / xref:en/custom/animation.adoc[English] + + +[NOTE] +==== +この文書は執筆中です。 +==== + + +== はじめに + +GDCubism は Godot Engine 上で Live2D モデルを再生する事を簡単に目的に開発されています。 + +SubViewport経由で描画を行なっているため、モデル表示までは少し手間ですが、一旦表示まで行えば Godot Editor 上から簡単に確認できる様にしています。 + +簡単に表示が行える一方で、より細かい制御を行いたい場合にどうすれば良いのかというのがややわかりづらいものになっています。例えば、モーションの再生をさせつつ一部のパラメータのみを変更したいといった場合です。 + +その様な操作は GDScript を使用する事で実現できるのですが、現状ソースコードを読む事でしか気がつけません。 + +そのため、資料としてこのドキュメントを執筆しました。 + + +=== 処理の流れについて + +GDScript で Live2D モデルを制御する場合、 GDCubismUserModel の get_parameter 関数で取得する配列を制御することになりますが、何も考えずに設定しても Live2D モデルにうまく反映されません。 + +その理由はパラメータを上書きしようとしても、設定されているモーションが上書きをしてしまう事が原因です。 + +うまく設定するには GDCubism がどの様に描画をおこなっているかを知るっておく方が良いかと思いますので、まずは GDCubism がどのように Live2D モデルを描画しているかについて説明します。(技術的な背景に興味がない方は読み飛ばしてください) + + +==== 処理についての説明 + +GDCubismUserModel は以下の処理を毎フレーム行います。順番の大枠は「設定値の読込」「Live2D情報の更新」「描画」となります。 + +. 設定値の読込 +.. モーションパラメータを更新。 +.. エクスプレッションパラメータを更新。 +.. GDCubismUserModel の parameters を反映。 +.. GDCubismUserModel の opacities を反映。 +. Live2D情報の更新 +.. 物理パラメータを更新。 +.. ポーズを更新。 +.. 描画情報を更新。 +. 描画情報の作成 +.. 描画に必要な SubViewport と Mesh 数を計算。 +.. SubViewport と ArrayMesh を生成し、ノードツリーに割り当て。 + +> 実際のプログラムコード(0.6時点)では、 _process をトリガとして以下の様に呼び出されています。 + +NOTE: _process は playback_process_mode が IDLE に設定されている場合です。 PHYSICS の場合は _physics_process、 MANUAL の場合は _advance となります。 + + +* _GDCubismUserModel_._process +** _GDCubismUserModel_._update + +*** _InternalCubismModel_.pro_update +**** _InternalCubismModel_.effect_batch +***** *cubism_prologue* シグナルの呼び出し +**** Live2D モデルのパラメータ情報を復元 +**** _motionManager.UpdateMotion +**** Live2D モデルのパラメータ情報を保存 +**** _expressionManager.UpdateMotion +**** _InternalCubismModel_.GetModelOpacity + +*** _InternalCubismModel_.efx_update +**** GDCubismEffect*ノードに変更があれば、 efx_term, efx_init シグナルの呼び出しfootnote:id[ここで呼び出されると、シグナルが中途半端に呼び出されてしまうため、今後のバージョンで呼び出し位置が変更される可能性があります。] +**** _InternalCubismModel_.effect_batch +***** *cubism_process* シグナルの呼び出し + +*** パラメータ情報の設定と取得 +*** 透明度の設定と取得 + +*** _InternalCubismModel_.epi_update +**** Live2D モデルの物理パラメータを更新 +**** Live2D モデルのポーズを更新 +**** Live2D モデルの内部状態を更新 +**** _InternalCubismModel_.effect_batch +***** *cubism_epilogue* シグナルの呼び出し + +*** _InternalCubismModel_.update_node +**** _InternalCubismModel_.GetRenderer +**** 描画サイズの反映 +**** 描画位置の反映 +**** 描画に必要なリソースの確保 +**** 描画に必要な情報を GodotEngine に設定 + + +==== 任意の処理を追加する方法 + +ここからは実際にどう処理すれば良いかについて記述していきます。 + + +===== _process 関数内で処理する方法 + +前述した一連の処理は、すべて Godot Engine が GDCubismUserModel の _process を呼び出すタイミングで行われます。 + +そのため、少しだけ Live2D モデルのパラメータを変更したい場合は、その後で行えばよいことになります。注意点としては Godot Engine 側が GDCubismUserModel 内の _process が呼び出されるのがいつなのかという事です。 + +これは GDCubismUserModel がノードツリーのどの場所にあり、スクリプトがどこに設定されているかによります。確実に反映させるには、 GDCubismUserModel の playback_process_mode を MANUAL にして、 advance 関数を呼び出してからパラメータを操作するようにしてください。 + +これにより、パラメータの更新が反映前に上書きされてしまうのを防ぐ事が出来ます。 + +[source] +[source,,numlines] +---- +include::example$custom_animation_process.gd[] +---- + + +===== GDCubismEffectCustom で処理する方法 + +GDCubismUserModel の子要素に GDCubismEffectCustom を追加すると、cubism_prologue, cubism_process, cubism_epilogue シグナルが受け取れるようになります。 + + +GDCubismEffectCustom が受け取る事ができるシグナルは5種類ありますが、そのうち Live2D モデルに関するものは以下の3種類となります。 + +cubism_prologue:: +Live2Dモデルに適用されたモーションとエクスプレッションの更新前に呼び出されます。このシグナルを受けった際にパラメータ変更を行なっても無視されてしまいます。 + + +現在は GDCubismUserModel の get_parameters 関数で取得したパラメータに対して操作出来てしまいますが、将来のバージョンで挙動が変わる可能性があります。 + +cubism_process:: +Live2Dモデルに適用されたモーションとエクスプレッションが更新された後に呼び出されます。このシグナルを受け取ったタイミングでパラメータを更新すると、モーションを再生させながら任意の処理を上書き出来ます。 + +例えば、手の形を差し替える、帽子を外す、目を閉じるといった処理です。 + + +cubism_epilogue:: +cubism_process とほとんど同じですが、GDCubismUserModel の get_parameters の内容がモーションとエクスプレッションを反映させた状態になっています。 + +[source] +[source,,numlines] +---- +include::example$custom_animation_signal.gd[] +---- + + +== サンプルコードについて + +GDCubismEffectCustom を使用したサンプルとして以下のものが用意されています。 + +|demo_effect_custom_01.tscn +|_GDCubismEffectCustom_ を使用したサンプルとなります。 + +|demo_effect_custom_02.tscn +|_GDCubismEffectCustom_ を使用したサンプルとなります。 + +|demo_effect_custom_03.tscn +|_GDCubismEffectCustom_ を使用したサンプルとなります。 +簡易的なリップシンクを行なって、音量にあわせて口を動かしています。 diff --git a/docs-src/modules/ROOT/pages/ja/usage.adoc b/docs-src/modules/ROOT/pages/ja/usage.adoc index 096a101..347f8e3 100644 --- a/docs-src/modules/ROOT/pages/ja/usage.adoc +++ b/docs-src/modules/ROOT/pages/ja/usage.adoc @@ -151,9 +151,12 @@ _demo_ フォルダには、サンプルとして以下のものが収められ |demo_effect_custom_02.tscn |_GDCubismEffectCustom_ を使用したサンプルとなります。 -簡易的なリップシンクを行なって、音量にあわせて口を動かしています。 |demo_effect_custom_03.tscn +|_GDCubismEffectCustom_ を使用したサンプルとなります。 +簡易的なリップシンクを行なって、音量にあわせて口を動かしています。 + +|demo_effect_custom_target_point.tscn |_GDCubismEffectTargetPoint_ を使用したサンプルとなります。 マウスの位置にあわせてLive2Dモデルが視線を向ける動作をします。 diff --git a/src/gd_cubism_user_model.cpp b/src/gd_cubism_user_model.cpp index f4d16f5..9e880ea 100644 --- a/src/gd_cubism_user_model.cpp +++ b/src/gd_cubism_user_model.cpp @@ -512,12 +512,18 @@ void GDCubismUserModel::_update(const float delta) { for(Csm::csmInt32 index = 0; index < this->ary_parameter.size(); index++ ) { Ref param = this->ary_parameter[index]; - if(param.is_null() != true) param->set_raw_value(); + if(param.is_null() != true) { + param->set_raw_value(); + param->get_raw_value(); + } } for(Csm::csmInt32 index = 0; index < this->ary_part_opacity.size(); index++ ) { Ref param = this->ary_part_opacity[index]; - if(param.is_null() != true) param->set_raw_value(); + if(param.is_null() != true) { + param->set_raw_value(); + param->get_raw_value(); + } } this->internal_model->epi_update(delta * this->speed_scale); @@ -532,17 +538,7 @@ void GDCubismUserModel::_update(const float delta) { } #else this->internal_model->update_node(); - #endif // COUNTERMEASURES_90017_90030 - - for(Csm::csmInt32 index = 0; index < this->ary_part_opacity.size(); index++ ) { - Ref param = this->ary_part_opacity[index]; - if(param.is_null() != true) param->get_raw_value(); - } - - for(Csm::csmInt32 index = 0; index < this->ary_parameter.size(); index++ ) { - Ref param = this->ary_parameter[index]; - if(param.is_null() != true) param->get_raw_value(); - } + #endif // COUNTERMEASURES_90017_90030 }