├── live2d_mini_rust ├── resources │ └── .keep ├── Cargo.toml └── src │ └── main.rs ├── live2d_mini ├── .gitignore ├── src │ ├── lib.rs │ ├── vector2.rs │ ├── address.rs │ ├── pose_json.rs │ ├── motion_json.rs │ ├── constant_flag.rs │ ├── model_json.rs │ ├── dynamic_flag.rs │ ├── physic_json.rs │ ├── part.rs │ ├── parameter.rs │ ├── model.rs │ ├── drawable.rs │ ├── animation.rs │ ├── model_resource.rs │ └── physic.rs └── Cargo.toml ├── .gitignore ├── .gitmodules ├── Cargo.toml └── README.md /live2d_mini_rust/resources/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /live2d_mini/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /resources/**/* 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | live2d_mini_rust/resources/**/* 4 | !.keep 5 | .DS_Store 6 | .vscode 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "live2d_mini_sys"] 2 | path = live2d_mini_sys 3 | url = https://github.com/ahogappa0613/live2d_mini_sys 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "live2d_mini", 5 | "live2d_mini_sys", 6 | "live2d_mini_rust", 7 | ] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # これは何 2 | * Live2Dのライブラリをrustから読んでみる 3 | # 技術的な話 4 | * Live2Dのリファレンス実装はC++ 5 | * Live2Dはレンダリングエンジンに実装を依存していないので、自由に選べる 6 | * 今回はレンダリングエンジンにminiquadを採用 7 | 8 | # ビルド 9 | * M1のみ確認 10 | * resouces/以下にLive2Dモデルデータを配置 11 | * 動作確認はHiyoriのデータを使っている 12 | * live2d_mini_sys/Core以下にLive2Dのライブラリを配置 13 | -------------------------------------------------------------------------------- /live2d_mini/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | mod animation; 3 | mod constant_flag; 4 | mod drawable; 5 | mod dynamic_flag; 6 | pub mod model; 7 | mod model_json; 8 | pub mod model_resource; 9 | mod motion_json; 10 | mod parameter; 11 | mod part; 12 | mod physic_json; 13 | mod pose_json; 14 | mod vector2; 15 | mod physic; 16 | -------------------------------------------------------------------------------- /live2d_mini/src/vector2.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy)] 2 | pub struct Live2DVector2(pub(crate) live2d_mini_sys::csmVector2); 3 | impl Live2DVector2 { 4 | #[inline] 5 | pub fn x(&self) -> f32 { 6 | self.0.X 7 | } 8 | 9 | #[inline] 10 | pub fn y(&self) -> f32 { 11 | self.0.Y 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /live2d_mini/src/address.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::{dealloc, Layout}; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq)] 4 | pub struct Live2DAddress { 5 | pub(crate) ptr: *mut u8, 6 | pub(crate) layout: Layout, 7 | } 8 | 9 | impl Drop for Live2DAddress { 10 | fn drop(&mut self) { 11 | unsafe { dealloc(self.ptr, self.layout) }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /live2d_mini_rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "live2d_mini_rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | live2d_mini_sys = { path = "../live2d_mini_sys" } 8 | live2d_mini = {path = "../live2d_mini"} 9 | 10 | miniquad = "0.3.15" 11 | image = "0.24.5" 12 | serde_json = "1.0.93" 13 | serde = { version = "1.0.152", features = ["derive"] } 14 | -------------------------------------------------------------------------------- /live2d_mini/src/pose_json.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use serde::*; 4 | 5 | #[derive(Debug, Deserialize)] 6 | pub struct PoseJson { 7 | pub Type: String, 8 | pub FadeInTime: f32, 9 | pub Groups: Vec>, 10 | } 11 | 12 | #[derive(Debug, Deserialize)] 13 | 14 | pub struct Group { 15 | pub Id: String, 16 | pub Link: Vec, 17 | } 18 | -------------------------------------------------------------------------------- /live2d_mini/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "live2d_mini" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [dependencies] 8 | live2d_mini_sys = { path = "../live2d_mini_sys" } 9 | serde_json = "1.0" 10 | serde = { version = "1.0", features = ["derive"] } 11 | 12 | miniquad = { version = "0.3.3"} 13 | image = "0.24" 14 | -------------------------------------------------------------------------------- /live2d_mini/src/motion_json.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use serde::*; 4 | 5 | #[derive(Debug, Deserialize)] 6 | pub struct MotionJson { 7 | pub Version: i32, 8 | pub Meta: Meta, 9 | pub Curves: Vec, 10 | } 11 | 12 | #[derive(Debug, Deserialize)] 13 | pub struct Meta { 14 | pub Duration: f32, 15 | pub Fps: f32, 16 | pub Loop: bool, 17 | pub AreBeziersRestricted: bool, 18 | pub CurveCount: usize, 19 | pub TotalSegmentCount: i32, 20 | pub TotalPointCount: i32, 21 | pub UserDataCount: i32, 22 | pub TotalUserDataSize: i32, 23 | } 24 | 25 | #[derive(Debug, Deserialize)] 26 | pub struct Curve { 27 | pub Target: String, 28 | pub Id: String, 29 | pub Segments: Vec, 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /live2d_mini/src/constant_flag.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 2 | pub struct Live2DConstantFlag(live2d_mini_sys::csmFlags); 3 | 4 | impl Live2DConstantFlag { 5 | #[inline] 6 | pub fn is_csm_blend_additive(&self) -> bool { 7 | self.0 as u32 & live2d_mini_sys::csmBlendAdditive == live2d_mini_sys::csmBlendAdditive 8 | } 9 | 10 | #[inline] 11 | pub fn is_csm_blend_multiplicative(&self) -> bool { 12 | self.0 as u32 & live2d_mini_sys::csmBlendMultiplicative 13 | == live2d_mini_sys::csmBlendMultiplicative 14 | } 15 | 16 | /// trueは両面描画=カリングなし 17 | #[inline] 18 | pub fn is_csm_is_double_sided(&self) -> bool { 19 | self.0 as u32 & live2d_mini_sys::csmIsDoubleSided == live2d_mini_sys::csmIsDoubleSided 20 | } 21 | 22 | #[inline] 23 | pub fn is_csm_is_inverted_mask(&self) -> bool { 24 | self.0 as u32 & live2d_mini_sys::csmIsInvertedMask == live2d_mini_sys::csmIsInvertedMask 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /live2d_mini/src/model_json.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | use serde::*; 3 | 4 | #[derive(Debug, Deserialize)] 5 | pub struct ModelJson { 6 | pub Version: i32, 7 | pub FileReferences: FileReferences, 8 | pub Groups: Option>, 9 | pub HitAreas: Option>, 10 | } 11 | 12 | #[derive(Debug, Deserialize)] 13 | pub struct FileReferences { 14 | pub Moc: String, 15 | pub Textures: Vec, 16 | pub Physics: Option, 17 | pub Pose: Option, 18 | pub UserData: Option, 19 | pub DisplayInfo: String, 20 | pub Motions: Option, 21 | } 22 | 23 | #[derive(Debug, Deserialize)] 24 | pub struct Motions { 25 | pub Idle: Vec, 26 | pub TapBody: Vec, 27 | } 28 | 29 | #[derive(Debug, Deserialize)] 30 | pub struct Motion { 31 | pub File: String, 32 | pub FadeInTime: f32, 33 | pub FadeOutTime: f32, 34 | } 35 | 36 | #[derive(Debug, Deserialize)] 37 | pub struct Group { 38 | pub Target: String, 39 | pub Name: String, 40 | pub Ids: Vec, 41 | } 42 | 43 | #[derive(Debug, Deserialize)] 44 | pub struct HitArea { 45 | pub Id: String, 46 | pub Name: String, 47 | } 48 | -------------------------------------------------------------------------------- /live2d_mini/src/dynamic_flag.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 2 | pub struct Live2DDynamicFlag(live2d_mini_sys::csmFlags); 3 | 4 | impl Live2DDynamicFlag { 5 | #[inline] 6 | pub fn is_csm_is_visible(&self) -> bool { 7 | if (self.0 as u32 & live2d_mini_sys::csmIsVisible) == live2d_mini_sys::csmIsVisible { 8 | true 9 | } else { 10 | false 11 | } 12 | } 13 | 14 | #[inline] 15 | pub fn is_csm_visibility_did_change(&self) -> bool { 16 | if (self.0 as u32 & live2d_mini_sys::csmVisibilityDidChange) 17 | == live2d_mini_sys::csmVisibilityDidChange 18 | { 19 | true 20 | } else { 21 | false 22 | } 23 | } 24 | 25 | #[inline] 26 | pub fn is_csm_opacity_did_change(&self) -> bool { 27 | if (self.0 as u32 & live2d_mini_sys::csmOpacityDidChange) 28 | == live2d_mini_sys::csmOpacityDidChange 29 | { 30 | true 31 | } else { 32 | false 33 | } 34 | } 35 | 36 | #[inline] 37 | pub fn is_csm_draw_order_did_change(&self) -> bool { 38 | if (self.0 as u32 & live2d_mini_sys::csmDrawOrderDidChange) 39 | == live2d_mini_sys::csmDrawOrderDidChange 40 | { 41 | true 42 | } else { 43 | false 44 | } 45 | } 46 | 47 | #[inline] 48 | pub fn is_csm_vertex_positions_did_change(&self) -> bool { 49 | if (self.0 as u32 & live2d_mini_sys::csmVertexPositionsDidChange) 50 | == live2d_mini_sys::csmVertexPositionsDidChange 51 | { 52 | true 53 | } else { 54 | false 55 | } 56 | } 57 | 58 | #[inline] 59 | pub fn is_csm_blend_color_did_change(&self) -> bool { 60 | if (self.0 as u32 & live2d_mini_sys::csmBlendColorDidChange) 61 | == live2d_mini_sys::csmBlendColorDidChange 62 | { 63 | true 64 | } else { 65 | false 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /live2d_mini/src/physic_json.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use serde::*; 4 | 5 | #[derive(Debug, Deserialize)] 6 | pub struct PhysicJson { 7 | pub Version: i32, 8 | pub Meta: Meta, 9 | /// このvectorの数はSubRigCountとして扱われる 10 | pub PhysicsSettings: Vec, 11 | } 12 | 13 | #[derive(Debug, Deserialize)] 14 | pub struct Vec2 { 15 | pub X: f32, 16 | pub Y: f32, 17 | } 18 | 19 | #[derive(Debug, Deserialize)] 20 | pub struct Meta { 21 | pub PhysicsSettingCount: i32, 22 | pub TotalInputCount: i32, 23 | pub TotalOutputCount: i32, 24 | pub VertexCount: i32, 25 | pub EffectiveForces: EffectiveForces, 26 | pub PhysicsDictionary: Vec, 27 | pub Fps: Option, 28 | } 29 | 30 | #[derive(Debug, Deserialize)] 31 | pub struct EffectiveForces { 32 | pub Gravity: Vec2, 33 | pub Wind: Vec2, 34 | } 35 | 36 | #[derive(Debug, Deserialize)] 37 | pub struct PhysicsDiction { 38 | pub Id: String, 39 | pub Name: String, 40 | } 41 | 42 | #[derive(Debug, Deserialize)] 43 | pub struct PhysicsSetting { 44 | pub Id: String, 45 | pub Input: Vec, 46 | /// output_count 47 | pub Output: Vec, 48 | pub Vertices: Vec, 49 | pub Normalization: Normalize, 50 | } 51 | 52 | #[derive(Debug, Deserialize)] 53 | pub struct InputParam { 54 | pub Source: InputSource, 55 | pub Weight: f32, 56 | pub Type: String, 57 | pub Reflect: bool, 58 | } 59 | 60 | #[derive(Debug, Deserialize)] 61 | pub struct InputSource { 62 | pub Target: String, 63 | pub Id: String, 64 | } 65 | 66 | #[derive(Debug, Deserialize)] 67 | pub struct OutputParam { 68 | pub Destination: OutputDestination, 69 | pub VertexIndex: usize, 70 | pub Scale: f32, 71 | pub Weight: f32, 72 | pub Type: String, 73 | pub Reflect: bool, 74 | } 75 | 76 | #[derive(Debug, Deserialize)] 77 | pub struct OutputDestination { 78 | pub Target: String, 79 | pub Id: String, 80 | } 81 | 82 | #[derive(Debug, Deserialize)] 83 | pub struct Vertice { 84 | pub Position: Vec2, 85 | pub Mobility: f32, 86 | pub Delay: f32, 87 | pub Acceleration: f32, 88 | pub Radius: f32, 89 | } 90 | 91 | #[derive(Debug, Deserialize)] 92 | pub struct Normalize { 93 | pub Position: NormalizePosition, 94 | pub Angle: NormalizeAngle, 95 | } 96 | 97 | #[derive(Debug, Deserialize)] 98 | pub struct NormalizePosition { 99 | pub Minimum: f32, 100 | pub Default: f32, 101 | pub Maximum: f32, 102 | } 103 | 104 | #[derive(Debug, Deserialize)] 105 | pub struct NormalizeAngle { 106 | pub Minimum: f32, 107 | pub Default: f32, 108 | pub Maximum: f32, 109 | } 110 | -------------------------------------------------------------------------------- /live2d_mini/src/part.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CStr, os::raw::c_char}; 2 | 3 | use crate::model_resource::Live2DModelResource; 4 | 5 | #[derive(Debug, PartialEq)] 6 | pub struct Live2DPart<'a> { 7 | id: &'a *const c_char, 8 | opacitiy: &'a f32, 9 | parent_part_index: &'a i32, 10 | } 11 | 12 | impl<'a> Live2DPart<'a> { 13 | #[inline] 14 | pub fn id(&self) -> &str { 15 | unsafe { CStr::from_ptr(*self.id).to_str().expect("id error") } 16 | } 17 | 18 | #[inline] 19 | pub fn opacitiy(&self) -> &f32 { 20 | self.opacitiy 21 | } 22 | 23 | #[inline] 24 | pub fn parent_part_index(&self) -> &i32 { 25 | self.parent_part_index 26 | } 27 | } 28 | 29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 30 | pub struct Live2DPartIter<'a> { 31 | pub(crate) pos: usize, 32 | pub(crate) len: usize, 33 | 34 | pub(crate) inner: &'a Live2DModelResource, 35 | } 36 | 37 | impl<'a> Iterator for Live2DPartIter<'a> { 38 | type Item = Live2DPart<'a>; 39 | 40 | fn next(&mut self) -> Option { 41 | if self.pos >= self.len { 42 | return None; 43 | } else { 44 | self.pos += 1; 45 | 46 | unsafe { 47 | Some(Live2DPart { 48 | id: self.inner.csm_get_part_ids().get_unchecked(self.pos - 1), 49 | opacitiy: self 50 | .inner 51 | .csm_get_part_opacities() 52 | .get_unchecked(self.pos - 1), 53 | parent_part_index: self 54 | .inner 55 | .csm_get_part_parent_part_indices() 56 | .get_unchecked(self.pos - 1), 57 | }) 58 | } 59 | } 60 | } 61 | } 62 | 63 | #[derive(Debug, PartialEq)] 64 | pub struct Live2DPartMut<'a> { 65 | id: &'a *const c_char, 66 | pub opacitiy: &'a mut f32, 67 | pub parent_part_index: &'a i32, 68 | } 69 | impl<'a> Live2DPartMut<'a> { 70 | #[inline] 71 | pub fn id(&self) -> &str { 72 | unsafe { CStr::from_ptr(*self.id).to_str().expect("id error") } 73 | } 74 | } 75 | 76 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 77 | pub struct Live2DPartIterMut<'a> { 78 | pub(crate) pos: usize, 79 | pub(crate) len: usize, 80 | 81 | pub(crate) inner: &'a Live2DModelResource, 82 | } 83 | 84 | impl<'a> Iterator for Live2DPartIterMut<'a> { 85 | type Item = Live2DPartMut<'a>; 86 | 87 | fn next(&mut self) -> Option { 88 | if self.pos >= self.len { 89 | return None; 90 | } else { 91 | self.pos += 1; 92 | 93 | unsafe { 94 | Some(Live2DPartMut { 95 | id: self.inner.csm_get_part_ids().get_unchecked(self.pos - 1), 96 | opacitiy: self 97 | .inner 98 | .csm_get_part_opacities() 99 | .get_unchecked_mut(self.pos - 1), 100 | parent_part_index: self 101 | .inner 102 | .csm_get_part_parent_part_indices() 103 | .get_unchecked(self.pos - 1), 104 | }) 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /live2d_mini/src/parameter.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CStr, os::raw::c_char}; 2 | 3 | use crate::model_resource::Live2DModelResource; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq)] 6 | pub struct Live2DParameter<'a> { 7 | pub id: &'a *const c_char, 8 | pub minimum_value: &'a f32, 9 | pub maximum_value: &'a f32, 10 | pub default_value: &'a f32, 11 | pub value: &'a f32, 12 | } 13 | 14 | impl<'a> Live2DParameter<'a> { 15 | #[inline] 16 | pub fn id(&self) -> &str { 17 | unsafe { CStr::from_ptr(*self.id).to_str().expect("id error") } 18 | } 19 | } 20 | 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 22 | pub struct Live2DParameterIter<'a> { 23 | pub(crate) pos: usize, 24 | pub(crate) len: usize, 25 | 26 | pub(crate) inner: &'a Live2DModelResource, 27 | } 28 | 29 | impl<'a> Iterator for Live2DParameterIter<'a> { 30 | type Item = Live2DParameter<'a>; 31 | 32 | fn next(&mut self) -> Option { 33 | if self.pos >= self.len { 34 | return None; 35 | } else { 36 | self.pos += 1; 37 | 38 | unsafe { 39 | Some(Live2DParameter { 40 | id: self 41 | .inner 42 | .csm_get_parameter_ids() 43 | .get_unchecked(self.pos - 1), 44 | minimum_value: self 45 | .inner 46 | .csm_get_parameter_minimum_values() 47 | .get_unchecked(self.pos - 1), 48 | maximum_value: self 49 | .inner 50 | .csm_get_parameter_maximum_values() 51 | .get_unchecked(self.pos - 1), 52 | default_value: self 53 | .inner 54 | .csm_get_parameter_default_values() 55 | .get_unchecked(self.pos - 1), 56 | value: self 57 | .inner 58 | .csm_get_parameter_values() 59 | .get_unchecked(self.pos - 1), 60 | }) 61 | } 62 | } 63 | } 64 | } 65 | 66 | #[derive(Debug, PartialEq)] 67 | pub struct Live2DParameterMut<'a> { 68 | pub id: &'a *const c_char, 69 | pub minimum_value: &'a f32, 70 | pub maximum_value: &'a f32, 71 | pub default_value: &'a f32, 72 | pub value: &'a mut f32, 73 | } 74 | 75 | impl<'a> Live2DParameterMut<'a> { 76 | #[inline] 77 | pub fn id(&self) -> &str { 78 | unsafe { CStr::from_ptr(*self.id).to_str().expect("id error") } 79 | } 80 | } 81 | 82 | #[derive(Debug, PartialEq, Eq)] 83 | pub struct Live2DParameterIterMut<'a> { 84 | pub(crate) pos: usize, 85 | pub(crate) len: usize, 86 | 87 | pub(crate) inner: &'a Live2DModelResource, 88 | } 89 | 90 | impl<'a> Iterator for Live2DParameterIterMut<'a> { 91 | type Item = Live2DParameterMut<'a>; 92 | 93 | fn next(&mut self) -> Option { 94 | if self.pos >= self.len { 95 | return None; 96 | } else { 97 | self.pos += 1; 98 | 99 | unsafe { 100 | Some(Live2DParameterMut { 101 | id: self 102 | .inner 103 | .csm_get_parameter_ids() 104 | .get_unchecked(self.pos - 1), 105 | minimum_value: self 106 | .inner 107 | .csm_get_parameter_minimum_values() 108 | .get_unchecked(self.pos - 1), 109 | maximum_value: self 110 | .inner 111 | .csm_get_parameter_maximum_values() 112 | .get_unchecked(self.pos - 1), 113 | default_value: self 114 | .inner 115 | .csm_get_parameter_default_values() 116 | .get_unchecked(self.pos - 1), 117 | value: self 118 | .inner 119 | .csm_get_mut_parameter_values() 120 | .get_unchecked_mut(self.pos - 1), 121 | }) 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /live2d_mini/src/model.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::BufReader; 3 | use std::path::Path; 4 | 5 | use crate::animation::*; 6 | use crate::model_json; 7 | use crate::motion_json; 8 | use crate::physic_json; 9 | 10 | use image::RgbaImage; 11 | 12 | use crate::model_resource::Live2DModelResource; 13 | use crate::physic::Physics; 14 | 15 | #[derive(Debug)] 16 | pub struct Live2DModel { 17 | pub resource: Live2DModelResource, 18 | pub animations: Vec, 19 | pub textures: Vec, 20 | pub physics: Option, 21 | 22 | /// 再生するアニメーションの番号 23 | animation_index: Option, 24 | } 25 | 26 | impl<'a> Live2DModel { 27 | pub fn new

(path: P) -> Self 28 | where 29 | P: AsRef, 30 | { 31 | let path = Path::new(path.as_ref()); 32 | 33 | let current_dir = path.parent().expect("cannot find parents"); 34 | 35 | let file = File::open(path).expect(&format!("cannot open file: {:?}", path.to_str())); 36 | let reader = BufReader::new(file); 37 | 38 | let model_json: model_json::ModelJson = 39 | serde_json::from_reader(reader).expect("deselialize error"); 40 | 41 | let textures = model_json 42 | .FileReferences 43 | .Textures 44 | .iter() 45 | .map(|path| { 46 | image::io::Reader::open(current_dir.join(path)) 47 | .expect("not find image") 48 | .decode() 49 | .expect("decode faild") 50 | .flipv() 51 | .to_rgba8() 52 | }) 53 | .collect::>(); 54 | 55 | let resource = Live2DModelResource::new(current_dir.join(model_json.FileReferences.Moc)) 56 | .expect("moc load error"); 57 | // let file = 58 | // File::open(current_dir.join(model_json.FileReferences.Pose.expect(""))).expect(""); 59 | // let reader = BufReader::new(file); 60 | // let u: pose_json::PoseJson = serde_json::from_reader(reader).expect(""); 61 | 62 | let physics = if let Some(physics_path) = model_json.FileReferences.Physics { 63 | let file = File::open(current_dir.join(physics_path)).expect("open error"); 64 | let reader = BufReader::new(file); 65 | let physic_json: physic_json::PhysicJson = 66 | serde_json::from_reader(reader).expect("load error"); 67 | let mut raw_physics = Physics::new(physic_json); 68 | raw_physics.initialize(); 69 | Some(raw_physics) 70 | } else { 71 | None 72 | }; 73 | 74 | let motions = model_json 75 | .FileReferences 76 | .Motions 77 | .expect("") 78 | .Idle 79 | .iter() 80 | .map(|idle| { 81 | let file = File::open(current_dir.join(&idle.File)).expect("file open error"); 82 | let reader = BufReader::new(file); 83 | serde_json::from_reader(reader).expect("deselialize error") 84 | }) 85 | .collect::>(); 86 | 87 | let animations = motions 88 | .iter() 89 | .map(|motion| Animation::new(motion)) 90 | .collect::>(); 91 | 92 | Live2DModel { 93 | resource, 94 | animations, 95 | textures, 96 | physics, 97 | animation_index: None, 98 | } 99 | } 100 | 101 | pub fn animation(&mut self, time: f32) { 102 | if let Some(anime) = self 103 | .animations 104 | .get_mut(self.animation_index.expect("no set animation")) 105 | { 106 | anime.evaluate_animation(&mut self.resource, time) 107 | } else { 108 | panic!("not find animation") 109 | } 110 | } 111 | 112 | pub fn get_animation(&self) -> Option<&Animation> { 113 | self.animations 114 | .get(self.animation_index.expect("no set animation")) 115 | } 116 | 117 | pub fn get_mut_animation(&mut self) -> Option<&mut Animation> { 118 | self.animations 119 | .get_mut(self.animation_index.expect("no set animation")) 120 | } 121 | 122 | pub fn evaluate_physic(&mut self, delta_time: f32) { 123 | if let Some(physic) = self.physics.as_mut() { 124 | physic.evaluate(&mut self.resource, delta_time) 125 | } 126 | } 127 | 128 | /// indexを設定した値にし 129 | /// 再生時間を0にする 130 | pub fn reset_animation(&mut self, index: usize) { 131 | self.animation_index = Some(index); 132 | self.animation(0.0); 133 | self.replace_default_values(); 134 | self.resource.update(); 135 | } 136 | 137 | fn replace_default_values(&self) { 138 | for (value, default_value) in self 139 | .resource 140 | .csm_get_mut_parameter_values() 141 | .iter_mut() 142 | .zip(self.resource.csm_get_parameter_default_values()) 143 | { 144 | *value = *default_value; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /live2d_mini/src/drawable.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CStr, os::raw::c_char}; 2 | 3 | use crate::constant_flag::*; 4 | use crate::dynamic_flag::*; 5 | use crate::model_resource::*; 6 | 7 | use crate::vector2::*; 8 | 9 | #[derive(Debug, Clone, Copy, PartialEq)] 10 | pub struct Live2DDrawable<'a> { 11 | id: &'a *const c_char, 12 | constant_flag: &'a Live2DConstantFlag, 13 | dynamic_flag: &'a Live2DDynamicFlag, 14 | texture_index: &'a i32, 15 | draw_order: &'a i32, 16 | render_order: &'a i32, 17 | opacitiy: &'a f32, 18 | mask_count: &'a i32, 19 | vertex_count: &'a i32, 20 | index_count: &'a i32, 21 | 22 | masks: &'a *const i32, 23 | indices: &'a *const u16, 24 | vertex_positions: &'a *const Live2DVector2, 25 | vertex_uvs: &'a *const Live2DVector2, 26 | } 27 | 28 | impl<'a> Live2DDrawable<'a> { 29 | #[inline] 30 | pub fn id(&self) -> &str { 31 | unsafe { CStr::from_ptr(*self.id).to_str().expect("id error") } 32 | } 33 | 34 | #[inline] 35 | pub fn constant_flag(&self) -> &Live2DConstantFlag { 36 | self.constant_flag 37 | } 38 | 39 | #[inline] 40 | pub fn dynamic_flag(&self) -> &Live2DDynamicFlag { 41 | self.dynamic_flag 42 | } 43 | 44 | #[inline] 45 | pub fn texture_index(&self) -> &i32 { 46 | self.texture_index 47 | } 48 | 49 | #[inline] 50 | pub fn draw_order(&self) -> &i32 { 51 | self.draw_order 52 | } 53 | 54 | #[inline] 55 | pub fn render_order(&self) -> &i32 { 56 | self.render_order 57 | } 58 | 59 | #[inline] 60 | pub fn opacitiy(&self) -> &f32 { 61 | self.opacitiy 62 | } 63 | 64 | #[inline] 65 | pub fn mask_count(&self) -> &i32 { 66 | self.mask_count 67 | } 68 | 69 | #[inline] 70 | pub fn vertex_count(&self) -> &i32 { 71 | self.vertex_count 72 | } 73 | 74 | #[inline] 75 | pub fn index_count(&self) -> &i32 { 76 | self.index_count 77 | } 78 | 79 | #[inline] 80 | pub fn masks(&self) -> &[i32] { 81 | unsafe { std::slice::from_raw_parts(*self.masks, *self.mask_count() as usize) } 82 | } 83 | 84 | #[inline] 85 | pub fn indices(&self) -> Option<&[u16]> { 86 | if *self.index_count() == 0 { 87 | return None; 88 | } 89 | 90 | unsafe { 91 | Some(std::slice::from_raw_parts( 92 | *self.indices, 93 | *self.index_count() as usize, 94 | )) 95 | } 96 | } 97 | 98 | #[inline] 99 | pub fn vertex_positions(&self) -> &[Live2DVector2] { 100 | unsafe { std::slice::from_raw_parts(*self.vertex_positions, *self.vertex_count() as usize) } 101 | } 102 | 103 | #[inline] 104 | pub fn vertex_uvs(&self) -> &[Live2DVector2] { 105 | unsafe { std::slice::from_raw_parts(*self.vertex_uvs, *self.vertex_count() as usize) } 106 | } 107 | } 108 | 109 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 110 | pub struct Live2DDrawableIter<'a> { 111 | pub(crate) pos: usize, 112 | pub(crate) len: usize, 113 | 114 | pub(crate) inner: &'a Live2DModelResource, 115 | } 116 | 117 | impl<'a> Iterator for Live2DDrawableIter<'a> { 118 | type Item = Live2DDrawable<'a>; 119 | 120 | fn next(&mut self) -> Option { 121 | if self.pos >= self.len { 122 | return None; 123 | } else { 124 | let pos = self.pos; 125 | 126 | self.pos += 1; 127 | 128 | unsafe { 129 | Some(Live2DDrawable { 130 | id: self.inner.csm_get_drawable_ids().get_unchecked(pos), 131 | constant_flag: self 132 | .inner 133 | .csm_get_drawable_constant_flags() 134 | .get_unchecked(pos), 135 | texture_index: self 136 | .inner 137 | .csm_get_drawable_texture_indices() 138 | .get_unchecked(pos), 139 | draw_order: self.inner.csm_get_drawable_draw_orders().get_unchecked(pos), 140 | opacitiy: self.inner.csm_get_drawable_opacities().get_unchecked(pos), 141 | mask_count: self.inner.csm_get_drawable_mask_counts().get_unchecked(pos), 142 | vertex_count: self 143 | .inner 144 | .csm_get_drawable_vertex_counts() 145 | .get_unchecked(pos), 146 | index_count: self 147 | .inner 148 | .csm_get_drawable_index_counts() 149 | .get_unchecked(pos), 150 | render_order: self 151 | .inner 152 | .csm_get_drawable_render_orders() 153 | .get_unchecked(pos), 154 | dynamic_flag: self 155 | .inner 156 | .csm_get_drawable_dynamic_flags() 157 | .get_unchecked(pos), 158 | 159 | masks: self.inner.csm_get_drawable_masks().get_unchecked(pos), 160 | indices: self.inner.csm_get_drawable_indices().get_unchecked(pos), 161 | vertex_positions: self 162 | .inner 163 | .csm_get_drawable_vertex_positions() 164 | .get_unchecked(pos), 165 | vertex_uvs: self.inner.csm_get_drawable_vertex_uvs().get_unchecked(pos), 166 | }) 167 | } 168 | } 169 | } 170 | } 171 | 172 | #[derive(Debug, Clone)] 173 | pub struct Live2DSortedDrawableIter<'a> { 174 | pub(crate) sorted_indices: Vec, 175 | 176 | pub(crate) inner: &'a Live2DModelResource, 177 | } 178 | 179 | impl<'a> Iterator for Live2DSortedDrawableIter<'a> { 180 | type Item = Live2DDrawable<'a>; 181 | 182 | fn next(&mut self) -> Option { 183 | match self.sorted_indices.pop() { 184 | Some(index) => unsafe { 185 | Some(Live2DDrawable { 186 | id: self.inner.csm_get_drawable_ids().get_unchecked(index), 187 | constant_flag: self 188 | .inner 189 | .csm_get_drawable_constant_flags() 190 | .get_unchecked(index), 191 | texture_index: self 192 | .inner 193 | .csm_get_drawable_texture_indices() 194 | .get_unchecked(index), 195 | draw_order: self 196 | .inner 197 | .csm_get_drawable_draw_orders() 198 | .get_unchecked(index), 199 | opacitiy: self.inner.csm_get_drawable_opacities().get_unchecked(index), 200 | mask_count: self 201 | .inner 202 | .csm_get_drawable_mask_counts() 203 | .get_unchecked(index), 204 | vertex_count: self 205 | .inner 206 | .csm_get_drawable_vertex_counts() 207 | .get_unchecked(index), 208 | index_count: self 209 | .inner 210 | .csm_get_drawable_index_counts() 211 | .get_unchecked(index), 212 | render_order: self 213 | .inner 214 | .csm_get_drawable_render_orders() 215 | .get_unchecked(index), 216 | dynamic_flag: self 217 | .inner 218 | .csm_get_drawable_dynamic_flags() 219 | .get_unchecked(index), 220 | 221 | masks: self.inner.csm_get_drawable_masks().get_unchecked(index), 222 | indices: self.inner.csm_get_drawable_indices().get_unchecked(index), 223 | vertex_positions: self 224 | .inner 225 | .csm_get_drawable_vertex_positions() 226 | .get_unchecked(index), 227 | vertex_uvs: self 228 | .inner 229 | .csm_get_drawable_vertex_uvs() 230 | .get_unchecked(index), 231 | }) 232 | }, 233 | None => return None, 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /live2d_mini_rust/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fs::File, io::BufReader, path::Path, rc::Rc}; 2 | 3 | use image::EncodableLayout; 4 | use miniquad::*; 5 | 6 | #[derive(Debug)] 7 | #[repr(C)] 8 | struct Vec2 { 9 | x: f32, 10 | y: f32, 11 | } 12 | #[derive(Debug)] 13 | #[repr(C)] 14 | struct Vertex { 15 | pos: Vec2, 16 | uv: Vec2, 17 | } 18 | 19 | struct Stage { 20 | pipeline: Pipeline, 21 | bindings: Vec, 22 | model: live2d_mini::model::Live2DModel, 23 | // vertex_vec: Vec, 24 | max: Vec, 25 | // anime: live2d_mini::animation::Animation, 26 | start_time: f64, 27 | last_frame: f64, 28 | textures: Vec, // model: live2d_mini::model_resource::Live2DModelResource, 29 | canvas_info: live2d_mini::model_resource::Live2DCanvasInfo, 30 | } 31 | impl Stage { 32 | pub fn new(ctx: &mut Context) -> Self { 33 | let mut model = live2d_mini::model::Live2DModel::new( 34 | "./live2d_mini_rust/resources/Hiyori/Hiyori.model3.json", 35 | ); 36 | let textures = model 37 | .textures 38 | .iter() 39 | .map(|tex| { 40 | Texture::from_rgba8(ctx, tex.width() as _, tex.height() as _, tex.as_bytes()) 41 | }) 42 | .collect::>(); 43 | 44 | let mut indices4 = vec![]; 45 | let mut bindings_vec = vec![]; 46 | 47 | model.reset_animation(1); 48 | model.evaluate_physic(0.01); 49 | model.resource.update(); 50 | 51 | // dbg!(&model.physics); 52 | 53 | for (index, drawable) in model.resource.iter_sorted_drawables().enumerate() { 54 | if drawable.dynamic_flag().is_csm_is_visible() && drawable.indices().is_some() { 55 | let mut vertices4 = vec![]; 56 | for (pos, uv) in drawable 57 | .vertex_positions() 58 | .iter() 59 | .zip(drawable.vertex_uvs()) 60 | { 61 | vertices4.push(Vertex { 62 | pos: Vec2 { 63 | x: pos.x(), 64 | y: pos.y(), 65 | }, 66 | uv: Vec2 { 67 | x: uv.x(), 68 | y: uv.y(), 69 | }, 70 | }); 71 | } 72 | 73 | let buf = Buffer::immutable(ctx, BufferType::VertexBuffer, &vertices4); 74 | 75 | bindings_vec.push(Bindings { 76 | vertex_buffers: vec![buf], 77 | index_buffer: Buffer::immutable( 78 | ctx, 79 | BufferType::IndexBuffer, 80 | drawable.indices().unwrap_or(&[]), 81 | ), 82 | images: vec![textures[*drawable.texture_index() as usize]], 83 | }); 84 | 85 | indices4.push(drawable.indices().unwrap_or(&[]).len()); 86 | } 87 | } 88 | 89 | let shader1 = Shader::new(ctx, shader::VERTEX, shader::FRAGMENT, shader::meta()).unwrap(); 90 | 91 | let mut param = PipelineParams::default(); 92 | let state = BlendState::new( 93 | Equation::Add, 94 | BlendFactor::Value(BlendValue::SourceAlpha), 95 | BlendFactor::OneMinusValue(BlendValue::SourceAlpha), 96 | ); 97 | 98 | param.color_blend = Some(state); 99 | param.alpha_blend = Some(state); 100 | param.cull_face = CullFace::Back; 101 | let pipeline1 = Pipeline::with_params( 102 | ctx, 103 | &[BufferLayout::default()], 104 | &[ 105 | VertexAttribute::with_buffer("pos", VertexFormat::Float2, 0), 106 | VertexAttribute::with_buffer("uv", VertexFormat::Float2, 0), 107 | ], 108 | shader1, 109 | param, 110 | ); 111 | 112 | let time = miniquad::date::now(); 113 | let canvas_info = model.resource.csm_read_canvas_info(); 114 | 115 | // dbg!("----------------------------------------------------------------------------"); 116 | Stage { 117 | pipeline: pipeline1, 118 | bindings: bindings_vec, 119 | model, 120 | // vertex_vec: 121 | max: indices4, 122 | // anime: anime1, 123 | start_time: time, 124 | last_frame: 0.0, 125 | textures, // model, 126 | canvas_info, 127 | } 128 | } 129 | } 130 | 131 | impl<'a> EventHandler for Stage { 132 | fn update(&mut self, ctx: &mut Context) { 133 | self.last_frame += 0.02; 134 | 135 | if self.last_frame > self.model.get_animation().unwrap().duration.into() { 136 | self.last_frame = 0.0; 137 | self.model 138 | .get_mut_animation() 139 | .unwrap() 140 | .reset_evaluate_indeies(); 141 | } 142 | // self.model.evaluate_physic(0.01); 143 | self.model.animation(self.last_frame as f32); 144 | self.model.evaluate_physic(0.01); 145 | 146 | // self.model.resource.csm_update_model(); 147 | 148 | self.model.resource.update(); 149 | 150 | let mut indices4 = vec![]; 151 | let mut bindings_vec = vec![]; 152 | 153 | for drawable in self.model.resource.iter_sorted_drawables() { 154 | if drawable.dynamic_flag().is_csm_is_visible() && drawable.indices().is_some() { 155 | // dbg!(&drawable.id()); 156 | let mut vertices4 = vec![]; 157 | for (pos, uv) in drawable 158 | .vertex_positions() 159 | .iter() 160 | .zip(drawable.vertex_uvs()) 161 | { 162 | vertices4.push(Vertex { 163 | pos: Vec2 { 164 | x: pos.x(), 165 | y: pos.y(), 166 | }, 167 | uv: Vec2 { 168 | x: uv.x(), 169 | y: uv.y(), 170 | }, 171 | }); 172 | } 173 | 174 | let buf = Buffer::immutable(ctx, BufferType::VertexBuffer, &vertices4); 175 | 176 | bindings_vec.push(Bindings { 177 | vertex_buffers: vec![buf], 178 | index_buffer: Buffer::immutable( 179 | ctx, 180 | BufferType::IndexBuffer, 181 | drawable.indices().unwrap(), 182 | ), 183 | images: vec![self.textures[*drawable.texture_index() as usize]], 184 | }); 185 | 186 | indices4.push(drawable.indices().unwrap().len()); 187 | } 188 | } 189 | 190 | // 全部取っ替える 191 | for binding in self.bindings.iter_mut() { 192 | binding.index_buffer.delete(); 193 | binding.vertex_buffers[0].delete(); 194 | } 195 | 196 | self.bindings = bindings_vec; 197 | self.max = indices4; 198 | } 199 | 200 | fn draw(&mut self, ctx: &mut Context) { 201 | ctx.begin_default_pass(PassAction::default()); 202 | ctx.apply_pipeline(&self.pipeline); 203 | 204 | let (w, h) = ctx.screen_size(); 205 | let mut projection = [ 206 | 1.0f32, 0., 0., 0., 0., 1.0, 0., 0., 0., 0., 1.0, 0., 0., 0., 0., 1.0, 207 | ]; 208 | 209 | if self.canvas_info.get_canvas_with() > 1.0 && w < h { 210 | projection[0] = 1.0; 211 | projection[5] = w / h; 212 | } else { 213 | projection[0] = h / w; 214 | projection[5] = 1.0; 215 | } 216 | 217 | ctx.apply_uniforms(&projection); 218 | 219 | for (index, bind) in self.bindings.iter().enumerate() { 220 | ctx.apply_bindings(bind); 221 | 222 | ctx.draw(0, self.max[index] as _, 1); 223 | } 224 | ctx.end_render_pass(); 225 | 226 | ctx.commit_frame(); 227 | } 228 | } 229 | fn main() { 230 | miniquad::start( 231 | conf::Conf { 232 | window_title: "live2d_mini_rs_demo".to_string(), 233 | window_width: 1398, 234 | window_height: 847, 235 | fullscreen: false, 236 | high_dpi: true, 237 | ..Default::default() 238 | }, 239 | |mut ctx| Box::new(Stage::new(&mut ctx)), 240 | ); 241 | } 242 | 243 | mod shader { 244 | use miniquad::*; 245 | 246 | pub const VERTEX: &str = r#"#version 100 247 | attribute vec2 pos; 248 | attribute vec2 uv; 249 | uniform mat4 offset; 250 | varying lowp vec2 texcoord; 251 | void main() { 252 | gl_Position = vec4(pos, 0, 1) * offset; 253 | texcoord = uv; 254 | }"#; 255 | 256 | pub const FRAGMENT: &str = r#"#version 100 257 | varying lowp vec2 texcoord; 258 | uniform sampler2D tex1; 259 | void main() { 260 | gl_FragColor = texture2D(tex1, texcoord); 261 | }"#; 262 | 263 | pub fn meta() -> ShaderMeta { 264 | ShaderMeta { 265 | images: vec!["tex1".to_string()], 266 | uniforms: UniformBlockLayout { 267 | uniforms: vec![UniformDesc::new("offset", UniformType::Mat4)], 268 | }, 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /live2d_mini/src/animation.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::model_resource::Live2DModelResource; 4 | use crate::motion_json; 5 | 6 | #[derive(Debug, Clone, Copy, PartialEq)] 7 | /// アニメーションカーブの種類 8 | pub enum AnimationCurveType { 9 | Linear(AnimationPoint, AnimationPoint), 10 | Bezier( 11 | AnimationPoint, 12 | AnimationPoint, 13 | AnimationPoint, 14 | AnimationPoint, 15 | ), 16 | Stepped(AnimationPoint, AnimationPoint), 17 | InverseStepped(AnimationPoint, AnimationPoint), 18 | } 19 | 20 | impl AnimationCurveType { 21 | #[inline] 22 | pub fn last_point(&self) -> AnimationPoint { 23 | match self { 24 | AnimationCurveType::Linear(_, l) => l.clone(), 25 | AnimationCurveType::Bezier(_, _, _, l) => l.clone(), 26 | AnimationCurveType::Stepped(_, l) => l.clone(), 27 | AnimationCurveType::InverseStepped(_, l) => l.clone(), 28 | } 29 | } 30 | } 31 | 32 | #[derive(Debug, Clone, Copy)] 33 | /// アニメーションカーブの種類 34 | pub enum AnimationSegment { 35 | Linear, 36 | Bezier, 37 | Stepped, 38 | InverseStepped, 39 | } 40 | 41 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 42 | /// カーブで変化する値の種類 43 | pub enum AnimationType { 44 | ModelAnimationCurve, 45 | ParameterAnimationCurve, 46 | PartOpacityAnimationCurve, 47 | } 48 | 49 | #[derive(Debug, Clone, Copy, PartialEq)] 50 | pub struct AnimationPoint { 51 | pub time: f32, 52 | pub value: f32, 53 | } 54 | 55 | /// アニメーション一つの情報全て 56 | #[derive(Debug, Clone, PartialEq)] 57 | pub struct AnimationCurve { 58 | pub curve_type: AnimationType, 59 | pub segments: Vec, 60 | /// どこまで再生したか 61 | pub evaluated_index: usize, 62 | } 63 | 64 | #[derive(Debug, Clone, PartialEq, Default)] 65 | pub struct Animation { 66 | /// animationの最大時間 67 | pub duration: f32, 68 | /// アニメーションがループするか 69 | pub is_loop: bool, 70 | /// カーブの個数 71 | pub curve_count: usize, 72 | 73 | pub curves: HashMap, 74 | } 75 | 76 | impl Animation { 77 | pub fn new(json: &motion_json::MotionJson) -> Animation { 78 | let duration = json.Meta.Duration; 79 | let curve_count = json.Meta.CurveCount; 80 | let is_loop = json.Meta.Loop; 81 | let mut curves: HashMap = HashMap::new(); 82 | 83 | for curve in json.Curves.iter() { 84 | let id = curve.Id.clone(); 85 | let segments = match &*curve.Target { 86 | "Parameter" => AnimationCurve { 87 | curve_type: AnimationType::ParameterAnimationCurve, 88 | segments: parse_segments(&curve.Segments), 89 | evaluated_index: 0, 90 | }, 91 | "PartOpacity" => AnimationCurve { 92 | curve_type: AnimationType::PartOpacityAnimationCurve, 93 | segments: parse_segments(&curve.Segments), 94 | evaluated_index: 0, 95 | }, 96 | _ => panic!(), 97 | }; 98 | 99 | curves.insert(id, segments); 100 | } 101 | 102 | Animation { 103 | duration, 104 | is_loop, 105 | curve_count, 106 | curves, 107 | } 108 | } 109 | 110 | /// TODO 111 | pub fn add(json: &motion_json::MotionJson) { 112 | let _duration = json.Meta.Duration; 113 | let _curve_count = json.Meta.CurveCount; 114 | let _is_loop = json.Meta.Loop; 115 | let mut curves: HashMap = HashMap::new(); 116 | 117 | for curve in json.Curves.iter() { 118 | let id = curve.Id.clone(); 119 | let segments = match &*curve.Target { 120 | "Parameter" => AnimationCurve { 121 | curve_type: AnimationType::ParameterAnimationCurve, 122 | segments: parse_segments(&curve.Segments), 123 | evaluated_index: 0, 124 | }, 125 | "PartOpacity" => AnimationCurve { 126 | curve_type: AnimationType::PartOpacityAnimationCurve, 127 | segments: parse_segments(&curve.Segments), 128 | evaluated_index: 0, 129 | }, 130 | _ => panic!(), 131 | }; 132 | 133 | curves.insert(id, segments); 134 | } 135 | 136 | // Animation { 137 | // duration, 138 | // is_loop, 139 | // curve_count, 140 | // curves, 141 | // } 142 | } 143 | 144 | /// ある時間のアニメーションをmodel, parametor, opacityをそれぞれ実行する 145 | pub fn evaluate_animation(&mut self, model: &Live2DModelResource, time: f32) { 146 | // dbg!(&animation.curves); 147 | for (id, curve) in self.curves.iter_mut() { 148 | let mut value = curve.evaluate_curve(time); 149 | 150 | match curve.curve_type { 151 | AnimationType::ModelAnimationCurve => todo!(), 152 | AnimationType::ParameterAnimationCurve => { 153 | let target = model 154 | .iter_mut_parameters() 155 | .find(|part| part.id() == id) 156 | .expect("not find parameter"); 157 | if *target.maximum_value < value { 158 | value = *target.maximum_value; 159 | } 160 | if *target.minimum_value > value { 161 | value = *target.minimum_value; 162 | } 163 | *target.value = value; 164 | } 165 | AnimationType::PartOpacityAnimationCurve => { 166 | let target = model 167 | .iter_mut_parts() 168 | .find(|part| part.id() == id) 169 | .expect("not find part"); 170 | *target.opacitiy = value; 171 | } 172 | } 173 | } 174 | } 175 | 176 | pub fn reset_evaluate_indeies(&mut self) { 177 | self.curves 178 | .iter_mut() 179 | .for_each(|curve| curve.1.evaluated_index = 0); 180 | } 181 | } 182 | 183 | /// これなんとかしたい 184 | pub fn parse_segments(segments_vec: &Vec) -> Vec { 185 | let mut ret = vec![]; 186 | 187 | let mut index = 2; 188 | // 最初の点はどのcurve typeも固定 189 | let mut last_point = AnimationPoint { 190 | time: segments_vec[0], 191 | value: segments_vec[1], 192 | }; 193 | 194 | loop { 195 | if segments_vec.get(index).is_none() { 196 | break; 197 | } 198 | 199 | match segments_vec[index] { 200 | 1.0 => { 201 | ret.push(AnimationCurveType::Bezier( 202 | last_point, 203 | AnimationPoint { 204 | time: segments_vec[index + 1], 205 | value: segments_vec[index + 2], 206 | }, 207 | AnimationPoint { 208 | time: segments_vec[index + 3], 209 | value: segments_vec[index + 4], 210 | }, 211 | AnimationPoint { 212 | time: segments_vec[index + 5], 213 | value: segments_vec[index + 6], 214 | }, 215 | )); 216 | 217 | last_point = AnimationPoint { 218 | time: segments_vec[index + 5], 219 | value: segments_vec[index + 6], 220 | }; 221 | index += 7 222 | } 223 | 0.0 => { 224 | ret.push(AnimationCurveType::Linear( 225 | last_point, 226 | AnimationPoint { 227 | time: segments_vec[index + 1], 228 | value: segments_vec[index + 2], 229 | }, 230 | )); 231 | 232 | last_point = AnimationPoint { 233 | time: segments_vec[index + 1], 234 | value: segments_vec[index + 2], 235 | }; 236 | index += 3 237 | } 238 | _ => panic!(), 239 | }; 240 | } 241 | 242 | ret 243 | } 244 | 245 | impl AnimationCurve { 246 | pub fn evaluate_curve(&mut self, time: f32) -> f32 { 247 | let (evaluate_index, target_segment) = self 248 | .segments 249 | .iter() 250 | .skip(self.evaluated_index) 251 | .enumerate() 252 | .find(|(_, segment)| match segment { 253 | AnimationCurveType::Linear(p0, p1) => time >= p0.time && time <= p1.time, 254 | AnimationCurveType::Bezier(p0, _, _, p3) => time >= p0.time && time <= p3.time, 255 | AnimationCurveType::Stepped(p0, p1) => time >= p0.time && time <= p1.time, 256 | AnimationCurveType::InverseStepped(p0, p1) => time >= p0.time && time <= p1.time, 257 | }) 258 | .expect("not find segment"); 259 | 260 | self.evaluated_index = evaluate_index; 261 | target_segment.evaluate(time) 262 | } 263 | } 264 | 265 | impl AnimationCurveType { 266 | pub fn evaluate(&self, time: f32) -> f32 { 267 | match self { 268 | AnimationCurveType::Linear(p0, p1) => { 269 | let mut t = (time - p0.time) / (p1.time - p0.time); 270 | if t < 0.0 { 271 | t = 0.0; 272 | } 273 | 274 | p0.value + ((p1.value - p0.value) * t) 275 | } 276 | AnimationCurveType::Bezier(p0, p1, p2, p3) => { 277 | // 以下は古い方式 278 | // let mut t = (time - p0.time) / (p3.time - p0.time); 279 | // if t < 0.0 { 280 | // t = 0.0; 281 | // } 282 | 283 | // let p01 = Self::lerp_points(&p0, &p1, t); 284 | // let p12 = Self::lerp_points(&p1, &p2, t); 285 | // let p23 = Self::lerp_points(&p2, &p3, t); 286 | 287 | // let p012 = Self::lerp_points(&p01, &p12, t); 288 | // let p123 = Self::lerp_points(&p12, &p23, t); 289 | 290 | // Self::lerp_points(&p012, &p123, t).value 291 | 292 | // 最新方式 293 | let x = time; 294 | let x1 = p0.time; 295 | let x2 = p3.time; 296 | let cx1 = p1.time; 297 | let cx2 = p2.time; 298 | 299 | let a = x2 - 3.0 * cx2 + 3.0 * cx1 - x1; 300 | let b = 3.0 * cx2 - 6.0 * cx1 + 3.0 * x1; 301 | let c = 3.0 * cx1 - 3.0 * x1; 302 | let d = x1 - x; 303 | 304 | let t = Self::cardano_algorithm_for_bezier(a, b, c, d); 305 | 306 | let p01 = Self::lerp_points(&p0, &p1, t); 307 | let p12 = Self::lerp_points(&p1, &p2, t); 308 | let p23 = Self::lerp_points(&p2, &p3, t); 309 | 310 | let p012 = Self::lerp_points(&p01, &p12, t); 311 | let p123 = Self::lerp_points(&p12, &p23, t); 312 | 313 | Self::lerp_points(&p012, &p123, t).value 314 | } 315 | AnimationCurveType::Stepped(p0, _p1) => p0.value, 316 | AnimationCurveType::InverseStepped(_p0, p1) => p1.value, 317 | } 318 | } 319 | 320 | #[inline] 321 | fn lerp_points(a: &AnimationPoint, b: &AnimationPoint, t: f32) -> AnimationPoint { 322 | AnimationPoint { 323 | time: a.time + ((b.time - a.time) * t), 324 | value: a.value + ((b.value - a.value) * t), 325 | } 326 | } 327 | 328 | fn quadration_equation(a: f32, b: f32, c: f32) -> f32 { 329 | if a.abs() < std::f32::EPSILON { 330 | if b.abs() < std::f32::EPSILON { 331 | return -c; 332 | } 333 | return -c / b; 334 | } 335 | 336 | return -(b + (b * b - 4.0 * a * c).sqrt()) / (2.0 * a); 337 | } 338 | 339 | fn cardano_algorithm_for_bezier(a: f32, b: f32, c: f32, d: f32) -> f32 { 340 | if a.abs() < std::f32::EPSILON { 341 | return Self::quadration_equation(b, c, d).clamp(0.0, 1.0); 342 | } 343 | 344 | let ba = b / a; 345 | let ca = c / a; 346 | let da = d / a; 347 | 348 | let p = (3.0 * ca - ba * ba) / 3.0; 349 | let p3 = p / 3.0; 350 | let q = (2.0 * ba * ba * ba - 9.0 * ba * ca + 27.0 * da) / 27.0; 351 | let q2 = q / 2.0; 352 | let discriminant = q2 * q2 + p3 * p3 * p3; 353 | 354 | let center = 0.5; 355 | let threshold = center + 0.01; 356 | 357 | if discriminant < 0.0 { 358 | let mp3 = -p / 3.0; 359 | let mp33 = mp3 * mp3 * mp3; 360 | let r = mp33.sqrt(); 361 | let t = -q / (2.0 * r); 362 | let cosphi = t.clamp(-1.0, 1.0); 363 | let phi = cosphi.acos(); 364 | let crtr = r.cbrt(); 365 | let t1 = 2.0 * crtr; 366 | 367 | let root1 = t1 * (phi / 3.0).cos() - ba / 3.0; 368 | if (root1 - center).abs() < threshold { 369 | return root1.clamp(0.0, 1.0); 370 | } 371 | 372 | let root2 = t1 * ((phi + 2.0 * std::f32::consts::PI) / 3.0).cos() - ba / 3.0; 373 | if (root2 - center).abs() < threshold { 374 | return root2.clamp(0.0, 1.0); 375 | } 376 | 377 | let root3 = t1 * ((phi + 4.0 * std::f32::consts::PI) / 3.0).cos() - ba / 3.0; 378 | return root3.clamp(0.0, 1.0); 379 | } 380 | 381 | if discriminant == 0.0 { 382 | let u1 = if q2 < 0.0 { (-q2).cbrt() } else { -(q2.cbrt()) }; 383 | 384 | let root1 = 2.0 * u1 - ba / 3.0; 385 | if (root1 - center).abs() < threshold { 386 | return root1.clamp(0.0, 1.0); 387 | } 388 | 389 | let root2 = -u1 - ba / 3.0; 390 | if (root2 - center).abs() < threshold { 391 | return root2.clamp(0.0, 1.0); 392 | } 393 | } 394 | 395 | let sd = discriminant.sqrt(); 396 | let u1 = (sd - q2).cbrt(); 397 | let v1 = (sd + q2).cbrt(); 398 | let root1 = u1 - v1 - ba / 3.0; 399 | 400 | root1.clamp(0.0, 1.0) 401 | } 402 | } 403 | 404 | #[cfg(test)] 405 | mod tests { 406 | #[test] 407 | fn lerp_points_test() { 408 | use super::*; 409 | 410 | let a = AnimationPoint { 411 | time: 0.0, 412 | value: 18.0, 413 | }; 414 | let b = AnimationPoint { 415 | time: 0.210999995, 416 | value: 18.0, 417 | }; 418 | 419 | let t = AnimationCurveType::lerp_points(&a, &b, -0.0); 420 | 421 | assert_eq!( 422 | t, 423 | AnimationPoint { 424 | time: 0.0, 425 | value: 18.0 426 | } 427 | ); 428 | 429 | let a = AnimationPoint { 430 | time: 0.210999995, 431 | value: 18.0, 432 | }; 433 | let b = AnimationPoint { 434 | time: 0.421999991, 435 | value: 0.0, 436 | }; 437 | 438 | let t = AnimationCurveType::lerp_points(&a, &b, -0.0); 439 | 440 | assert_eq!( 441 | t, 442 | AnimationPoint { 443 | time: 0.210999995, 444 | value: 18.0 445 | } 446 | ); 447 | 448 | let a = AnimationPoint { 449 | time: 0.0, 450 | value: 1.0, 451 | }; 452 | let b = AnimationPoint { 453 | time: 0.333000004, 454 | value: 1.0, 455 | }; 456 | 457 | let t = AnimationCurveType::lerp_points(&a, &b, 0.00000154972076); 458 | 459 | assert_eq!( 460 | t, 461 | AnimationPoint { 462 | time: 5.16057014e-7, 463 | value: 1.0 464 | } 465 | ); 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /live2d_mini/src/model_resource.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::alloc_zeroed; 2 | use std::alloc::Layout; 3 | use std::alloc::LayoutError; 4 | use std::collections::HashMap; 5 | use std::fs::File; 6 | use std::io; 7 | use std::io::Read; 8 | use std::os::raw::c_char; 9 | use std::path::Path; 10 | 11 | use crate::address::*; 12 | use crate::constant_flag::Live2DConstantFlag; 13 | use crate::drawable::*; 14 | use crate::dynamic_flag::Live2DDynamicFlag; 15 | use crate::parameter::*; 16 | use crate::part::*; 17 | 18 | use crate::vector2::Live2DVector2; 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct Live2DVector4(live2d_mini_sys::csmVector4); 22 | impl Live2DVector4 { 23 | #[inline] 24 | pub fn x(&self) -> f32 { 25 | self.0.X 26 | } 27 | 28 | #[inline] 29 | pub fn y(&self) -> f32 { 30 | self.0.Y 31 | } 32 | 33 | #[inline] 34 | pub fn z(&self) -> f32 { 35 | self.0.Z 36 | } 37 | 38 | #[inline] 39 | pub fn w(&self) -> f32 { 40 | self.0.W 41 | } 42 | 43 | #[inline] 44 | pub fn r(&self) -> f32 { 45 | self.x() 46 | } 47 | 48 | #[inline] 49 | pub fn g(&self) -> f32 { 50 | self.y() 51 | } 52 | 53 | #[inline] 54 | pub fn b(&self) -> f32 { 55 | self.z() 56 | } 57 | 58 | #[inline] 59 | pub fn a(&self) -> f32 { 60 | self.w() 61 | } 62 | } 63 | 64 | #[derive(Debug, Clone)] 65 | pub struct Live2DCanvasInfo { 66 | /// キャンバスサイズ 67 | pub out_size_in_pixels: Live2DVector2, 68 | /// キャンバスの中心点 69 | pub out_origin_in_pixels: Live2DVector2, 70 | /// モデルのユニットの大きさ 71 | pub out_pixels_per_unit: f32, 72 | } 73 | 74 | impl Live2DCanvasInfo { 75 | pub fn get_canvas_with(&self) -> f32 { 76 | self.out_size_in_pixels.x() / self.out_pixels_per_unit 77 | } 78 | 79 | pub fn get_canvas_height(&self) -> f32 { 80 | self.out_size_in_pixels.y() / self.out_pixels_per_unit 81 | } 82 | } 83 | 84 | #[derive(Debug, Clone, PartialEq, Eq)] 85 | pub struct Live2DModelResource { 86 | _model_address: Live2DAddress, 87 | _moc_address: Live2DAddress, 88 | 89 | model: *mut live2d_mini_sys::csmModel, 90 | not_exists_parameter_ids: HashMap, 91 | } 92 | 93 | impl Live2DModelResource { 94 | /// moc3ファイルを読み込んでLive2DModelを生成する 95 | pub fn new(moc_path: T) -> io::Result 96 | where 97 | T: AsRef, 98 | { 99 | let model_resource = Self::read_blob_aligned(moc_path.as_ref())?; 100 | 101 | Ok(model_resource) 102 | } 103 | 104 | pub fn update(&self) { 105 | self.csm_update_model(); 106 | self.csm_reset_drawable_dynamic_flags(); 107 | } 108 | 109 | pub fn iter_drawables<'a>(&'a self) -> Live2DDrawableIter<'a> { 110 | Live2DDrawableIter { 111 | pos: 0, 112 | len: self.csm_get_drawable_count(), 113 | 114 | inner: self, 115 | } 116 | } 117 | 118 | pub fn iter_sorted_drawables<'a>(&'a self) -> Live2DSortedDrawableIter<'a> { 119 | // なんとかする 120 | let mut work_vec = self 121 | .csm_get_drawable_render_orders() 122 | .iter() 123 | .enumerate() 124 | .map(|(index, order)| (index, order)) 125 | .collect::>(); 126 | 127 | work_vec.sort_by(|a, b| b.1.cmp(a.1)); 128 | 129 | let sorted_indices = work_vec 130 | .into_iter() 131 | .map(|(index, _)| index) 132 | .collect::>(); 133 | 134 | Live2DSortedDrawableIter { 135 | sorted_indices, 136 | 137 | inner: self, 138 | } 139 | } 140 | 141 | pub fn iter_parameters<'a>(&'a self) -> Live2DParameterIter<'a> { 142 | Live2DParameterIter { 143 | pos: 0, 144 | len: self.csm_get_parameter_count(), 145 | 146 | inner: self, 147 | } 148 | } 149 | 150 | pub fn iter_mut_parameters<'a>(&'a self) -> Live2DParameterIterMut<'a> { 151 | Live2DParameterIterMut { 152 | pos: 0, 153 | len: self.csm_get_parameter_count(), 154 | 155 | inner: self, 156 | } 157 | } 158 | 159 | pub fn iter_parts<'a>(&'a self) -> Live2DPartIter<'a> { 160 | Live2DPartIter { 161 | pos: 0, 162 | len: self.csm_get_part_count(), 163 | 164 | inner: self, 165 | } 166 | } 167 | 168 | pub fn iter_mut_parts<'a>(&'a self) -> Live2DPartIterMut<'a> { 169 | Live2DPartIterMut { 170 | pos: 0, 171 | len: self.csm_get_part_count(), 172 | 173 | inner: self, 174 | } 175 | } 176 | 177 | unsafe fn allocate_aligned(size: usize, align: usize) -> Result { 178 | let layout = Layout::from_size_align(size, align)?; 179 | Ok(Live2DAddress { 180 | ptr: alloc_zeroed(layout), 181 | layout, 182 | }) 183 | } 184 | 185 | fn read_blob_aligned(path: &Path) -> io::Result { 186 | let mut file = File::open(Path::new(path))?; 187 | let file_size = file.metadata()?.len() as usize; 188 | 189 | unsafe { 190 | // このアドレスを破棄の対象にする 191 | let moc_address = 192 | Self::allocate_aligned(file_size, live2d_mini_sys::csmAlignofMoc as _) 193 | .expect("allocate error"); 194 | let moc_slice = 195 | std::slice::from_raw_parts_mut(moc_address.ptr, moc_address.layout.size()); 196 | file.read(moc_slice)?; 197 | 198 | // moc3ファイルの整合性を確認する 199 | if live2d_mini_sys::csmHasMocConsistency(moc_address.ptr as _, file_size as _) == 0 { 200 | return Err(io::Error::new( 201 | io::ErrorKind::Other, 202 | "moc3 file is not consistent", 203 | )); 204 | } 205 | 206 | let moc = live2d_mini_sys::csmReviveMocInPlace(moc_address.ptr as _, file_size as _); 207 | 208 | let model_size = live2d_mini_sys::csmGetSizeofModel(moc); 209 | // このアドレスを破棄の対象にする 210 | let model_address = 211 | Self::allocate_aligned(model_size as _, live2d_mini_sys::csmAlignofModel as _) 212 | .expect("allocate error"); 213 | let model = 214 | live2d_mini_sys::csmInitializeModelInPlace(moc, model_address.ptr as _, model_size); 215 | 216 | Ok(Self { 217 | _model_address: model_address, 218 | _moc_address: moc_address, 219 | 220 | model, 221 | not_exists_parameter_ids: HashMap::new(), 222 | }) 223 | } 224 | } 225 | 226 | // parameter idからindexを取得する 227 | // idがないものが渡される可能性があるのでそれを考慮する 228 | pub fn get_parameter_index(&mut self, id: &str) -> usize { 229 | // dbg!(self.csm_get_parameter_count()); 230 | // jsonデータにあるパラメータか 231 | if let Some(index) = self.iter_parameters().position(|param| param.id() == id) { 232 | index 233 | } else { 234 | // 存在していないパラメータリストに存在しているか 235 | if let Some(index) = self.not_exists_parameter_ids.get(id) { 236 | *index 237 | } else { 238 | let index = self.csm_get_drawable_count() + self.not_exists_parameter_ids.len(); 239 | self.not_exists_parameter_ids.insert(id.to_string(), index); 240 | 241 | index 242 | } 243 | } 244 | } 245 | 246 | pub fn csm_read_canvas_info(&self) -> Live2DCanvasInfo { 247 | unsafe { 248 | let mut out_size_in_pixels: Live2DVector2 = std::mem::zeroed(); 249 | let mut out_origin_in_pixels: Live2DVector2 = std::mem::zeroed(); 250 | let mut out_pixels_per_unit: f32 = 0.0; 251 | live2d_mini_sys::csmReadCanvasInfo( 252 | self.model, 253 | &mut out_size_in_pixels.0, 254 | &mut out_origin_in_pixels.0, 255 | &mut out_pixels_per_unit, 256 | ); 257 | 258 | Live2DCanvasInfo { 259 | out_size_in_pixels, 260 | out_origin_in_pixels, 261 | out_pixels_per_unit, 262 | } 263 | } 264 | } 265 | 266 | /// 更新を適用する 267 | #[inline] 268 | pub fn csm_update_model(&self) { 269 | unsafe { live2d_mini_sys::csmUpdateModel(self.model) } 270 | } 271 | 272 | #[inline] 273 | pub fn csm_get_parameter_count(&self) -> usize { 274 | unsafe { 275 | let size = live2d_mini_sys::csmGetParameterCount(self.model); 276 | assert_ne!(size, -1); 277 | 278 | size as usize 279 | } 280 | } 281 | 282 | #[inline] 283 | pub fn csm_get_parameter_ids(&self) -> &[*const c_char] { 284 | unsafe { 285 | std::slice::from_raw_parts( 286 | live2d_mini_sys::csmGetParameterIds(self.model), 287 | self.csm_get_parameter_count(), 288 | ) 289 | } 290 | } 291 | 292 | #[inline] 293 | pub fn csm_get_parameter_types(&self) -> &[i32] { 294 | unsafe { 295 | std::slice::from_raw_parts( 296 | live2d_mini_sys::csmGetParameterTypes(self.model), 297 | self.csm_get_parameter_count(), 298 | ) 299 | } 300 | } 301 | 302 | #[inline] 303 | pub fn csm_get_parameter_minimum_values(&self) -> &[f32] { 304 | unsafe { 305 | std::slice::from_raw_parts( 306 | live2d_mini_sys::csmGetParameterMinimumValues(self.model), 307 | self.csm_get_parameter_count(), 308 | ) 309 | } 310 | } 311 | 312 | #[inline] 313 | pub fn csm_get_parameter_maximum_values(&self) -> &[f32] { 314 | unsafe { 315 | std::slice::from_raw_parts( 316 | live2d_mini_sys::csmGetParameterMaximumValues(self.model), 317 | self.csm_get_parameter_count(), 318 | ) 319 | } 320 | } 321 | 322 | #[inline] 323 | pub fn csm_get_parameter_default_values<'a>(&self) -> &[f32] { 324 | unsafe { 325 | std::slice::from_raw_parts( 326 | live2d_mini_sys::csmGetParameterDefaultValues(self.model), 327 | self.csm_get_drawable_count(), 328 | ) 329 | } 330 | } 331 | 332 | // ここに書き込むとmodelを操作できる 333 | #[inline] 334 | pub fn csm_get_mut_parameter_values<'a>(&self) -> &mut [f32] { 335 | unsafe { 336 | std::slice::from_raw_parts_mut( 337 | live2d_mini_sys::csmGetParameterValues(self.model), 338 | self.csm_get_parameter_count(), 339 | ) 340 | } 341 | } 342 | 343 | #[inline] 344 | pub fn csm_get_parameter_values<'a>(&self) -> &[f32] { 345 | unsafe { 346 | std::slice::from_raw_parts( 347 | live2d_mini_sys::csmGetParameterValues(self.model), 348 | self.csm_get_parameter_count(), 349 | ) 350 | } 351 | } 352 | 353 | #[inline] 354 | pub fn csm_get_parameter_key_counts<'a>(&self) -> &[i32] { 355 | unsafe { 356 | std::slice::from_raw_parts( 357 | live2d_mini_sys::csmGetParameterKeyCounts(self.model), 358 | self.csm_get_part_count(), 359 | ) 360 | } 361 | } 362 | 363 | #[inline] 364 | pub fn csm_get_parameter_key_values<'a>(&self) -> &[*const f32] { 365 | unsafe { 366 | std::slice::from_raw_parts( 367 | live2d_mini_sys::csmGetParameterKeyValues(self.model), 368 | self.csm_get_part_count(), 369 | ) 370 | } 371 | } 372 | 373 | #[inline] 374 | pub fn csm_get_part_count(&self) -> usize { 375 | unsafe { 376 | let size = live2d_mini_sys::csmGetPartCount(self.model); 377 | assert_ne!(size, -1); 378 | 379 | size as usize 380 | } 381 | } 382 | 383 | #[inline] 384 | pub fn csm_get_part_ids(&self) -> &[*const c_char] { 385 | unsafe { 386 | std::slice::from_raw_parts( 387 | live2d_mini_sys::csmGetPartIds(self.model), 388 | self.csm_get_part_count(), 389 | ) 390 | } 391 | } 392 | 393 | #[inline] 394 | pub fn csm_get_part_opacities(&self) -> &mut [f32] { 395 | unsafe { 396 | std::slice::from_raw_parts_mut( 397 | live2d_mini_sys::csmGetPartOpacities(self.model), 398 | self.csm_get_part_count(), 399 | ) 400 | } 401 | } 402 | 403 | #[inline] 404 | pub fn csm_get_part_parent_part_indices(&self) -> &[i32] { 405 | unsafe { 406 | std::slice::from_raw_parts( 407 | live2d_mini_sys::csmGetPartParentPartIndices(self.model), 408 | self.csm_get_part_count(), 409 | ) 410 | } 411 | } 412 | 413 | #[inline] 414 | pub fn csm_get_drawable_count(&self) -> usize { 415 | unsafe { 416 | let size = live2d_mini_sys::csmGetDrawableCount(self.model); 417 | assert_ne!(size, -1); 418 | 419 | size as usize 420 | } 421 | } 422 | 423 | #[inline] 424 | pub fn csm_get_drawable_ids<'a>(&self) -> &[*const c_char] { 425 | unsafe { 426 | std::slice::from_raw_parts( 427 | live2d_mini_sys::csmGetDrawableIds(self.model), 428 | self.csm_get_drawable_count(), 429 | ) 430 | } 431 | } 432 | 433 | #[inline] 434 | pub fn csm_get_drawable_constant_flags(&self) -> &[Live2DConstantFlag] { 435 | unsafe { 436 | std::slice::from_raw_parts( 437 | live2d_mini_sys::csmGetDrawableConstantFlags(self.model) 438 | as *const Live2DConstantFlag, 439 | self.csm_get_drawable_count(), 440 | ) 441 | } 442 | } 443 | 444 | #[inline] 445 | pub fn csm_get_drawable_dynamic_flags(&self) -> &[Live2DDynamicFlag] { 446 | unsafe { 447 | std::slice::from_raw_parts( 448 | live2d_mini_sys::csmGetDrawableDynamicFlags(self.model) as *const Live2DDynamicFlag, 449 | self.csm_get_drawable_count(), 450 | ) 451 | } 452 | } 453 | 454 | #[inline] 455 | pub fn csm_get_drawable_texture_indices(&self) -> &[i32] { 456 | unsafe { 457 | std::slice::from_raw_parts( 458 | live2d_mini_sys::csmGetDrawableTextureIndices(self.model), 459 | self.csm_get_drawable_count(), 460 | ) 461 | } 462 | } 463 | 464 | #[inline] 465 | pub fn csm_get_drawable_draw_orders(&self) -> &[i32] { 466 | unsafe { 467 | std::slice::from_raw_parts( 468 | live2d_mini_sys::csmGetDrawableDrawOrders(self.model), 469 | self.csm_get_drawable_count(), 470 | ) 471 | } 472 | } 473 | 474 | #[inline] 475 | pub fn csm_get_drawable_render_orders(&self) -> &[i32] { 476 | unsafe { 477 | std::slice::from_raw_parts( 478 | live2d_mini_sys::csmGetDrawableRenderOrders(self.model), 479 | self.csm_get_drawable_count(), 480 | ) 481 | } 482 | } 483 | 484 | #[inline] 485 | pub fn csm_get_drawable_opacities(&self) -> &[f32] { 486 | unsafe { 487 | std::slice::from_raw_parts( 488 | live2d_mini_sys::csmGetDrawableOpacities(self.model), 489 | self.csm_get_drawable_count(), 490 | ) 491 | } 492 | } 493 | 494 | #[inline] 495 | pub fn csm_get_drawable_mask_counts(&self) -> &[i32] { 496 | unsafe { 497 | std::slice::from_raw_parts( 498 | live2d_mini_sys::csmGetDrawableMaskCounts(self.model), 499 | self.csm_get_drawable_count(), 500 | ) 501 | } 502 | } 503 | 504 | //配列の配列をなんとかする 505 | #[inline] 506 | pub fn csm_get_drawable_masks(&self) -> &[*const i32] { 507 | unsafe { 508 | std::slice::from_raw_parts( 509 | live2d_mini_sys::csmGetDrawableMasks(self.model), 510 | self.csm_get_drawable_count(), 511 | ) 512 | } 513 | } 514 | 515 | #[inline] 516 | pub fn csm_get_drawable_vertex_counts(&self) -> &[i32] { 517 | unsafe { 518 | std::slice::from_raw_parts( 519 | live2d_mini_sys::csmGetDrawableVertexCounts(self.model), 520 | self.csm_get_drawable_count(), 521 | ) 522 | } 523 | } 524 | 525 | //配列の配列をなんとかする 526 | #[inline] 527 | pub fn csm_get_drawable_vertex_positions(&self) -> &[*const Live2DVector2] { 528 | unsafe { 529 | std::slice::from_raw_parts( 530 | live2d_mini_sys::csmGetDrawableVertexPositions(self.model) as _, 531 | self.csm_get_drawable_count(), 532 | ) 533 | } 534 | } 535 | 536 | //配列の配列をなんとかする 537 | #[inline] 538 | pub fn csm_get_drawable_vertex_uvs(&self) -> &[*const Live2DVector2] { 539 | unsafe { 540 | std::slice::from_raw_parts( 541 | live2d_mini_sys::csmGetDrawableVertexUvs(self.model) as _, 542 | self.csm_get_drawable_count(), 543 | ) 544 | } 545 | } 546 | 547 | #[inline] 548 | pub fn csm_get_drawable_index_counts(&self) -> &[i32] { 549 | unsafe { 550 | std::slice::from_raw_parts( 551 | live2d_mini_sys::csmGetDrawableIndexCounts(self.model), 552 | self.csm_get_drawable_count(), 553 | ) 554 | } 555 | } 556 | 557 | //配列の配列をなんとかする 558 | #[inline] 559 | pub fn csm_get_drawable_indices(&self) -> &[*const u16] { 560 | unsafe { 561 | std::slice::from_raw_parts( 562 | live2d_mini_sys::csmGetDrawableIndices(self.model), 563 | self.csm_get_drawable_count(), 564 | ) 565 | } 566 | } 567 | 568 | #[inline] 569 | pub fn csm_reset_drawable_dynamic_flags(&self) { 570 | unsafe { live2d_mini_sys::csmResetDrawableDynamicFlags(self.model) }; 571 | } 572 | 573 | #[inline] 574 | pub fn csm_get_drawable_multiply_colors(&self) -> &[*const Live2DVector4] { 575 | unsafe { 576 | std::slice::from_raw_parts( 577 | live2d_mini_sys::csmGetDrawableMultiplyColors(self.model) as _, 578 | self.csm_get_drawable_count(), 579 | ) 580 | } 581 | } 582 | 583 | #[inline] 584 | pub fn csm_get_drawable_screen_colors(&self) -> &[Live2DVector4] { 585 | unsafe { 586 | std::slice::from_raw_parts( 587 | live2d_mini_sys::csmGetDrawableScreenColors(self.model) as _, 588 | self.csm_get_drawable_count(), 589 | ) 590 | } 591 | } 592 | 593 | #[inline] 594 | pub fn csm_get_drawable_parent_part_indices(&self) -> &[i32] { 595 | unsafe { 596 | std::slice::from_raw_parts( 597 | live2d_mini_sys::csmGetDrawableParentPartIndices(self.model), 598 | self.csm_get_drawable_count(), 599 | ) 600 | } 601 | } 602 | } 603 | -------------------------------------------------------------------------------- /live2d_mini/src/physic.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | f32::consts::PI, 3 | ops::{Add, Div, DivAssign, Mul, MulAssign, Sub}, 4 | }; 5 | 6 | use crate::{model_resource::Live2DModelResource, physic_json}; 7 | 8 | #[derive(Debug, Default, Clone, Copy, PartialEq)] 9 | struct CubismVector2 { 10 | x: f32, 11 | y: f32, 12 | } 13 | 14 | impl CubismVector2 { 15 | pub fn normalize(&self) -> Self { 16 | let length = (self.x * self.x + self.y * self.y).powf(0.5f32); 17 | 18 | self.clone() / length 19 | } 20 | 21 | pub fn to_radian(&self, to: &CubismVector2) -> f32 { 22 | let q1 = to.y.atan2(to.x); 23 | let q2 = self.y.atan2(self.x); 24 | 25 | let mut ret = q1 - q2; 26 | while ret < -PI { 27 | ret += PI * 2.0; 28 | } 29 | 30 | while ret > PI { 31 | ret -= PI * 2.0; 32 | } 33 | 34 | ret 35 | } 36 | } 37 | 38 | impl Add for CubismVector2 { 39 | type Output = CubismVector2; 40 | 41 | fn add(self, rhs: Self) -> Self::Output { 42 | CubismVector2 { 43 | x: self.x + rhs.x, 44 | y: self.y + rhs.y, 45 | } 46 | } 47 | } 48 | 49 | impl Sub for CubismVector2 { 50 | type Output = CubismVector2; 51 | 52 | fn sub(self, rhs: Self) -> Self::Output { 53 | CubismVector2 { 54 | x: self.x - rhs.x, 55 | y: self.y - rhs.y, 56 | } 57 | } 58 | } 59 | 60 | impl Mul for CubismVector2 { 61 | type Output = CubismVector2; 62 | 63 | fn mul(self, rhs: f32) -> Self::Output { 64 | CubismVector2 { 65 | x: self.x * rhs, 66 | y: self.y * rhs, 67 | } 68 | } 69 | } 70 | 71 | impl Div for CubismVector2 { 72 | type Output = CubismVector2; 73 | 74 | fn div(self, rhs: f32) -> Self::Output { 75 | CubismVector2 { 76 | x: self.x / rhs, 77 | y: self.y / rhs, 78 | } 79 | } 80 | } 81 | 82 | impl MulAssign for CubismVector2 { 83 | fn mul_assign(&mut self, rhs: f32) { 84 | self.x *= rhs; 85 | self.y *= rhs; 86 | } 87 | } 88 | 89 | impl DivAssign for CubismVector2 { 90 | fn div_assign(&mut self, rhs: f32) { 91 | self.x /= rhs; 92 | self.y /= rhs; 93 | } 94 | } 95 | 96 | /** 97 | * @brief 物理演算の適用先の種類 98 | * 99 | * 物理演算の適用先の種類。 100 | */ 101 | #[derive(Debug, Clone, PartialEq)] 102 | enum CubismPhysicsTargetType { 103 | /// パラメータに対して適用 104 | CubismPhysicsTargetTypeParameter, 105 | } 106 | 107 | /** 108 | * @brief 物理演算の入力の種類 109 | * 110 | * 物理演算の入力の種類。 111 | */ 112 | #[derive(Debug, Clone, PartialEq)] 113 | enum CubismPhysicsSource { 114 | /// X軸の位置から 115 | CubismPhysicsSourceX, 116 | /// Y軸の位置から 117 | CubismPhysicsSourceY, 118 | /// 角度から 119 | CubismPhysicsSourceAngle, 120 | } 121 | 122 | /** 123 | * @brief 物理演算で使用する外部の力 124 | * 125 | * 物理演算で使用する外部の力。 126 | */ 127 | #[derive(Debug, Default, Clone)] 128 | struct PhysicsJsonEffectiveForces { 129 | /// 重力 130 | gravity: CubismVector2, 131 | /// 風 132 | wind: CubismVector2, 133 | } 134 | 135 | /** 136 | * @brief 物理演算のパラメータ情報 137 | * 138 | * 物理演算のパラメータ情報。 139 | */ 140 | #[derive(Debug, Clone, PartialEq)] 141 | struct CubismPhysicsParameter { 142 | /// パラメータID 143 | id: String, 144 | /// 適用先の種類 145 | target_type: CubismPhysicsTargetType, 146 | } 147 | 148 | /** 149 | * @brief 物理演算の正規化情報 150 | * 151 | * 物理演算の正規化情報。 152 | */ 153 | #[derive(Debug, Default, Clone, PartialEq)] 154 | struct CubismPhysicsNormalization { 155 | /// 最大値 156 | minimum: f32, 157 | /// 最小値 158 | maximum: f32, 159 | /// デフォルト値 160 | default: f32, 161 | } 162 | 163 | /** 164 | * @brief 物理演算の演算に使用する物理点の情報 165 | * 166 | * 物理演算の演算に使用する物理点の情報。 167 | */ 168 | #[derive(Debug, Default, Clone, Copy, PartialEq)] 169 | struct CubismPhysicsParticle { 170 | /// 初期位置 171 | pub initial_position: CubismVector2, 172 | /// 動きやすさ 173 | pub mobility: f32, 174 | /// 遅れ 175 | pub delay: f32, 176 | /// 加速度 177 | pub acceleration: f32, 178 | /// 距離 179 | pub radius: f32, 180 | /// 現在の位置 181 | pub position: CubismVector2, 182 | /// 最後の位置 183 | pub last_position: CubismVector2, 184 | /// 最後の重力 185 | pub last_gravity: CubismVector2, 186 | /// 現在かかっている力 187 | pub force: CubismVector2, 188 | /// 現在の速度 189 | pub velocity: CubismVector2, 190 | } 191 | 192 | /** 193 | * @brief 物理演算の物理点の管理 194 | * 195 | * 物理演算の物理点の管理。 196 | */ 197 | #[derive(Debug, Default, Clone, PartialEq)] 198 | struct CubismPhysicsSubRig { 199 | /// 入力の個数 200 | input_count: usize, 201 | /// 出力の個数 202 | output_count: usize, 203 | /// 物理点の個数 204 | particle_count: usize, 205 | /// 入力の最初のインデックス 206 | base_input_index: usize, 207 | /// 出力の最初のインデックス 208 | base_output_index: usize, 209 | /// 物理点の最初のインデックス 210 | base_particle_index: usize, 211 | /// 正規化された位置 212 | normalization_position: CubismPhysicsNormalization, 213 | /// 正規化された角度 214 | normalization_angle: CubismPhysicsNormalization, 215 | } 216 | 217 | /** 218 | * @brief 物理演算の入力情報 219 | * 220 | * 物理演算の入力情報。 221 | */ 222 | #[derive(Clone, Debug, PartialEq)] 223 | struct CubismPhysicsInput { 224 | /// 入力元のパラメータ 225 | source: CubismPhysicsParameter, 226 | /// 入力元のパラメータのインデックス 227 | source_parameter_index: Option, 228 | /// 重み 229 | weight: f32, 230 | /// 入力の種類 231 | input_type: CubismPhysicsSource, 232 | /// 値が反転されているかどうか 233 | reflect: bool, 234 | // 正規化されたパラメータ値の取得関数 235 | cubism_physics_input_type: CubismPhysicsInputType, 236 | } 237 | 238 | /** 239 | * @brief 物理演算の出力情報 240 | * 241 | * 物理演算の出力情報。 242 | */ 243 | #[derive(Debug, Clone, PartialEq)] 244 | struct CubismPhysicsOutput { 245 | /// 出力先のパラメータ 246 | destination: CubismPhysicsParameter, 247 | /// 出力先のパラメータのインデックス 248 | destination_parameter_index: Option, 249 | /// 振り子のインデックス 250 | vertex_index: usize, 251 | /// 移動値のスケール 252 | translation_scale: CubismVector2, 253 | /// 角度のスケール 254 | angle_scale: f32, 255 | /// 重み 256 | weight: f32, 257 | /// 出力の種類 258 | output_type: CubismPhysicsSource, 259 | /// 値が反転されているかどうか 260 | reflect: bool, 261 | /// 最小値を下回った時の値 262 | value_below_minimum: f32, 263 | /// 最大値をこえた時の値 264 | value_exceeded_maximum: f32, 265 | // 物理演算の値の取得関数 266 | get_value: GetOutputTranslation, 267 | // 物理演算のスケール値の取得関数 268 | get_scale: GetOutputScaleTranslationType, 269 | } 270 | 271 | /** 272 | * @brief 物理演算のデータ 273 | * 274 | * 物理演算のデータ。 275 | */ 276 | #[derive(Clone, Debug, PartialEq)] 277 | struct CubismPhysicsRig { 278 | /// 物理演算の物理点の個数 279 | /// Vec\::len() 280 | sub_rig_count: usize, 281 | /// 物理演算の物理点の管理のリスト 282 | settings: Vec, 283 | /// 物理演算の入力のリスト 284 | inputs: Vec, 285 | /// 物理演算の出力のリスト 286 | outputs: Vec, 287 | /// 物理演算の物理点のリスト 288 | particles: Vec, 289 | /// 重力 290 | gravity: CubismVector2, 291 | /// 風 292 | wind: CubismVector2, 293 | /// 物理演算動作FPS 294 | fps: Option, 295 | } 296 | 297 | #[derive(Clone, Debug, PartialEq)] 298 | pub struct Physics { 299 | /// 最新の振り子計算の結果 300 | current_rig_outputs: Vec>, 301 | /// 一つ前の振り子計算の結果 302 | previous_rig_outputs: Vec>, 303 | /// 物理演算が処理していない時間 304 | current_remain_time: f32, 305 | /// Evaluateで利用するパラメータのキャッシュ 306 | parameter_cache: Vec, 307 | parameter_cache_tmp: Option>, 308 | /// UpdateParticlesが動くときの入力をキャッシュ 309 | parameter_input_cache: Vec, 310 | /// 物理演算のデータ 311 | physics_rig: CubismPhysicsRig, 312 | } 313 | 314 | #[derive(Debug, Clone, PartialEq)] 315 | enum GetOutputScaleTranslationType { 316 | X, 317 | Y, 318 | Angle, 319 | } 320 | 321 | impl GetOutputScaleTranslationType { 322 | fn get_output_scale_translation( 323 | &self, 324 | translation_scale: CubismVector2, 325 | angle_scale: f32, 326 | ) -> f32 { 327 | match self { 328 | GetOutputScaleTranslationType::X => translation_scale.x, 329 | GetOutputScaleTranslationType::Y => translation_scale.y, 330 | GetOutputScaleTranslationType::Angle => angle_scale, 331 | } 332 | } 333 | } 334 | 335 | #[derive(Debug, Clone, PartialEq)] 336 | enum CubismPhysicsInputType { 337 | X, 338 | Y, 339 | Angle, 340 | } 341 | 342 | impl CubismPhysicsInputType { 343 | fn get_normalized_parameter_value( 344 | &self, 345 | target_translation: &mut CubismVector2, 346 | target_angle: &mut f32, 347 | value: f32, 348 | parameter_minimum: f32, 349 | parameter_maximum: f32, 350 | parameter_default: f32, 351 | normalization_position: &CubismPhysicsNormalization, 352 | normalization_angle: &CubismPhysicsNormalization, 353 | is_inverted: bool, 354 | weight: f32, 355 | ) -> () { 356 | match self { 357 | CubismPhysicsInputType::X => { 358 | target_translation.x += normalize_parameter_value( 359 | value, 360 | parameter_minimum, 361 | parameter_maximum, 362 | parameter_default, 363 | normalization_position.minimum, 364 | normalization_position.maximum, 365 | normalization_position.default, 366 | is_inverted, 367 | ) * weight; 368 | } 369 | CubismPhysicsInputType::Y => { 370 | target_translation.y += normalize_parameter_value( 371 | value, 372 | parameter_minimum, 373 | parameter_maximum, 374 | parameter_default, 375 | normalization_position.minimum, 376 | normalization_position.maximum, 377 | normalization_position.default, 378 | is_inverted, 379 | ) * weight; 380 | } 381 | CubismPhysicsInputType::Angle => { 382 | *target_angle += normalize_parameter_value( 383 | value, 384 | parameter_minimum, 385 | parameter_maximum, 386 | parameter_default, 387 | normalization_angle.minimum, 388 | normalization_angle.maximum, 389 | normalization_angle.default, 390 | is_inverted, 391 | ) * weight; 392 | } 393 | } 394 | } 395 | } 396 | 397 | #[derive(Debug, Clone, Copy, PartialEq)] 398 | enum GetOutputTranslation { 399 | X, 400 | Y, 401 | Angle, 402 | } 403 | 404 | impl GetOutputTranslation { 405 | pub fn get_value( 406 | &self, 407 | translation: CubismVector2, 408 | particles: Vec<&CubismPhysicsParticle>, 409 | particle_index: usize, 410 | is_inverted: bool, 411 | parent_gravity: CubismVector2, 412 | ) -> f32 { 413 | match self { 414 | GetOutputTranslation::X => { 415 | if is_inverted { 416 | translation.x * -1.0 417 | } else { 418 | translation.x 419 | } 420 | } 421 | GetOutputTranslation::Y => { 422 | if is_inverted { 423 | translation.y * -1.0 424 | } else { 425 | translation.y 426 | } 427 | } 428 | GetOutputTranslation::Angle => { 429 | let gravity = if particle_index >= 2 { 430 | particles[particle_index - 1].position - particles[particle_index - 2].position 431 | } else { 432 | parent_gravity * -1.0 433 | }; 434 | 435 | let output_value = gravity.to_radian(&translation); 436 | 437 | if is_inverted { 438 | output_value * -1.0 439 | } else { 440 | output_value 441 | } 442 | } 443 | } 444 | } 445 | } 446 | 447 | /// Constant of air resistance. 448 | const AIR_RESISTANCE: f32 = 5.0; 449 | 450 | /// Constant of maximum weight of input and output ratio. 451 | const MAXIMUM_WEIGHT: f32 = 100.0; 452 | 453 | /// Constant of threshold of movement. 454 | const MOVEMENT_THRESHOLD: f32 = 0.001; 455 | 456 | /// Constant of maximum allowed delta time 457 | const MAX_DELTA_TIME: f32 = 5.0; 458 | 459 | // L73 460 | fn normalize_parameter_value( 461 | value: f32, 462 | parameter_minimum: f32, 463 | parameter_maximum: f32, 464 | _parameter_default: f32, 465 | normalized_minimum: f32, 466 | normalized_maximum: f32, 467 | normalized_default: f32, 468 | is_inverted: bool, 469 | ) -> f32 { 470 | let mut result = 0.0; 471 | 472 | let max_value = parameter_maximum.max(parameter_minimum); 473 | // 補正する 474 | let value = if max_value < value { max_value } else { value }; 475 | 476 | let min_value = parameter_maximum.min(parameter_minimum); 477 | // 補正する 478 | let value = if min_value > value { min_value } else { value }; 479 | 480 | let min_norm_value = normalized_minimum.min(normalized_maximum); 481 | let max_norm_value = normalized_minimum.max(normalized_maximum); 482 | let middle_norm_value = normalized_default; 483 | 484 | let middle_value = get_default_value(min_value, max_value); 485 | let param_value = value - middle_value; 486 | 487 | match param_value { 488 | n if n > 0.0 => { 489 | let n_length = max_norm_value - middle_norm_value; 490 | let p_length = max_value - middle_value; 491 | if p_length != 0.0 { 492 | result = param_value * (n_length / p_length) + middle_norm_value; 493 | } 494 | } 495 | n if n < 0.0 => { 496 | let n_length = min_norm_value - middle_norm_value; 497 | let p_length = min_value - middle_value; 498 | if p_length != 0.0 { 499 | result = param_value * (n_length / p_length) + middle_norm_value; 500 | } 501 | } 502 | n if n == 0.0 => result = middle_norm_value, 503 | _ => unreachable!(), 504 | } 505 | 506 | if is_inverted { 507 | result 508 | } else { 509 | -result 510 | } 511 | } 512 | 513 | // L67 514 | fn get_default_value(min: f32, max: f32) -> f32 { 515 | let min_value = min.min(max); 516 | 517 | min_value + (get_range_value(min, max) / 2.0) 518 | } 519 | 520 | // L38 521 | fn get_range_value(min: f32, max: f32) -> f32 { 522 | let max_value = min.max(max); 523 | let min_value = min.min(max); 524 | 525 | (max_value - min_value).abs() 526 | } 527 | 528 | trait CubismPhysicsUtil { 529 | fn to_direction(&self) -> CubismVector2; 530 | } 531 | 532 | impl CubismPhysicsUtil for f32 { 533 | fn to_direction(&self) -> CubismVector2 { 534 | CubismVector2 { 535 | x: self.sin(), 536 | y: self.cos(), 537 | } 538 | } 539 | } 540 | 541 | // https://github.com/Live2D/CubismNativeFramework/blob/cbd4dfaa5ee95218ea3f9af30f8525c60b4a9b36/src/Physics/CubismPhysics.cpp 542 | impl<'a> Physics { 543 | pub fn new(json: physic_json::PhysicJson) -> Self { 544 | let mut settings: Vec = vec![]; 545 | let mut inputs: Vec = vec![]; 546 | let mut outputs: Vec = vec![]; 547 | let mut particles: Vec = vec![]; 548 | 549 | let mut current_rig_outputs = vec![]; 550 | let mut previous_rig_outputs = vec![]; 551 | 552 | let (mut base_input_index, mut base_output_index, mut base_particle_index) = (0, 0, 0); 553 | 554 | json.PhysicsSettings.iter().for_each(|setting| { 555 | // settings 556 | settings.push(CubismPhysicsSubRig { 557 | input_count: setting.Input.len(), 558 | output_count: setting.Output.len(), 559 | particle_count: setting.Vertices.len(), 560 | base_input_index, 561 | base_output_index, 562 | base_particle_index, 563 | normalization_position: CubismPhysicsNormalization { 564 | minimum: setting.Normalization.Position.Minimum, 565 | maximum: setting.Normalization.Position.Maximum, 566 | default: setting.Normalization.Position.Default, 567 | }, 568 | normalization_angle: CubismPhysicsNormalization { 569 | minimum: setting.Normalization.Angle.Minimum, 570 | maximum: setting.Normalization.Angle.Maximum, 571 | default: setting.Normalization.Angle.Default, 572 | }, 573 | }); 574 | 575 | // input 576 | for input in setting.Input.iter() { 577 | let weight = input.Weight; 578 | let reflect = input.Reflect; 579 | let id = input.Source.Id.clone(); 580 | 581 | inputs.push(match &*input.Type { 582 | "X" => CubismPhysicsInput { 583 | source: CubismPhysicsParameter { 584 | target_type: CubismPhysicsTargetType::CubismPhysicsTargetTypeParameter, 585 | id, 586 | }, 587 | source_parameter_index: None, 588 | weight, 589 | input_type: CubismPhysicsSource::CubismPhysicsSourceX, 590 | reflect, 591 | cubism_physics_input_type: CubismPhysicsInputType::X, 592 | }, 593 | "Y" => CubismPhysicsInput { 594 | source: CubismPhysicsParameter { 595 | target_type: CubismPhysicsTargetType::CubismPhysicsTargetTypeParameter, 596 | id, 597 | }, 598 | source_parameter_index: None, 599 | weight, 600 | input_type: CubismPhysicsSource::CubismPhysicsSourceY, 601 | reflect, 602 | cubism_physics_input_type: CubismPhysicsInputType::Y, 603 | }, 604 | "Angle" => CubismPhysicsInput { 605 | source: CubismPhysicsParameter { 606 | target_type: CubismPhysicsTargetType::CubismPhysicsTargetTypeParameter, 607 | id, 608 | }, 609 | source_parameter_index: None, 610 | weight, 611 | input_type: CubismPhysicsSource::CubismPhysicsSourceAngle, 612 | reflect, 613 | cubism_physics_input_type: CubismPhysicsInputType::Angle, 614 | }, 615 | 616 | _ => unreachable!(), 617 | }); 618 | } 619 | 620 | // output 621 | for output in setting.Output.iter() { 622 | let translation_scale = CubismVector2::default(); 623 | let weight = output.Weight; 624 | let reflect = output.Reflect; 625 | let angle_scale = output.Scale; 626 | let vertex_index = output.VertexIndex; 627 | let destination = CubismPhysicsParameter { 628 | id: output.Destination.Id.clone(), 629 | target_type: CubismPhysicsTargetType::CubismPhysicsTargetTypeParameter, 630 | }; 631 | 632 | outputs.push(match &*output.Type { 633 | "X" => CubismPhysicsOutput { 634 | destination, 635 | destination_parameter_index: None, 636 | vertex_index, 637 | translation_scale, 638 | angle_scale, 639 | weight, 640 | output_type: CubismPhysicsSource::CubismPhysicsSourceX, 641 | reflect, 642 | value_below_minimum: 0.0, 643 | value_exceeded_maximum: 0.0, 644 | get_value: GetOutputTranslation::X, 645 | get_scale: GetOutputScaleTranslationType::X, 646 | }, 647 | "Y" => CubismPhysicsOutput { 648 | destination, 649 | destination_parameter_index: None, 650 | vertex_index, 651 | translation_scale, 652 | angle_scale, 653 | weight, 654 | output_type: CubismPhysicsSource::CubismPhysicsSourceY, 655 | reflect, 656 | value_below_minimum: 0.0, 657 | value_exceeded_maximum: 0.0, 658 | get_value: GetOutputTranslation::Y, 659 | get_scale: GetOutputScaleTranslationType::Y, 660 | }, 661 | "Angle" => CubismPhysicsOutput { 662 | destination, 663 | destination_parameter_index: None, 664 | vertex_index, 665 | translation_scale, 666 | angle_scale, 667 | weight, 668 | output_type: CubismPhysicsSource::CubismPhysicsSourceAngle, 669 | reflect, 670 | value_below_minimum: 0.0, 671 | value_exceeded_maximum: 0.0, 672 | get_value: GetOutputTranslation::Angle, 673 | get_scale: GetOutputScaleTranslationType::Angle, 674 | }, 675 | 676 | _ => unreachable!(), 677 | }); 678 | } 679 | 680 | // particles 681 | for particle in setting.Vertices.iter() { 682 | particles.push(CubismPhysicsParticle { 683 | initial_position: CubismVector2::default(), //後で初期化する 684 | mobility: particle.Mobility, 685 | delay: particle.Delay, 686 | acceleration: particle.Acceleration, 687 | radius: particle.Radius, 688 | position: CubismVector2 { 689 | x: particle.Position.X, 690 | y: particle.Position.Y, 691 | }, 692 | last_position: CubismVector2::default(), //後で初期化する 693 | last_gravity: CubismVector2::default(), //後で初期化する 694 | force: CubismVector2::default(), //後で初期化する 695 | velocity: CubismVector2::default(), //後で初期化する 696 | }); 697 | } 698 | 699 | base_input_index += setting.Input.len(); 700 | base_output_index += setting.Output.len(); 701 | base_particle_index += setting.Vertices.len(); 702 | 703 | current_rig_outputs.push(Vec::from_iter(outputs.iter().map(|_| 0.0))); 704 | previous_rig_outputs.push(Vec::from_iter(outputs.iter().map(|_| 0.0))); 705 | }); 706 | 707 | Physics { 708 | current_rig_outputs, 709 | previous_rig_outputs, 710 | current_remain_time: 0.0, 711 | parameter_cache: vec![], 712 | parameter_cache_tmp: None, 713 | parameter_input_cache: vec![], 714 | physics_rig: CubismPhysicsRig { 715 | sub_rig_count: json.PhysicsSettings.len(), 716 | settings, 717 | inputs, 718 | outputs, 719 | particles, 720 | gravity: CubismVector2 { 721 | x: json.Meta.EffectiveForces.Gravity.X, 722 | y: json.Meta.EffectiveForces.Gravity.Y, 723 | }, 724 | wind: CubismVector2 { 725 | x: json.Meta.EffectiveForces.Wind.X, 726 | y: json.Meta.EffectiveForces.Wind.Y, 727 | }, 728 | fps: json.Meta.Fps, 729 | }, 730 | } 731 | } 732 | 733 | // L416 734 | pub fn initialize(&mut self) { 735 | for current_setting in self.physics_rig.settings.iter() { 736 | let mut prev_strand = self.physics_rig.particles[current_setting.base_particle_index]; 737 | // Initialize the top of particle. 738 | prev_strand.initial_position = CubismVector2::default(); 739 | prev_strand.last_position = CubismVector2::default(); 740 | prev_strand.last_gravity = CubismVector2 { x: 0.0, y: 1.0 }; 741 | prev_strand.velocity = CubismVector2::default(); 742 | prev_strand.force = CubismVector2::default(); 743 | 744 | // 同じVecで現在の位置のmutと一つ前の参照を取ることになるので 745 | // cloneするしかない 746 | let mut prev_strand = prev_strand.clone(); 747 | for particle in self 748 | .physics_rig 749 | .particles 750 | .iter_mut() 751 | .skip(current_setting.base_particle_index + 1) 752 | .take(current_setting.particle_count - 1) 753 | { 754 | let mut radius = CubismVector2::default(); 755 | radius.y = particle.radius; 756 | particle.initial_position = prev_strand.initial_position + radius; 757 | particle.last_position = particle.initial_position; 758 | particle.last_gravity = CubismVector2 { x: 0.0, y: 1.0 }; 759 | particle.velocity = CubismVector2::default(); 760 | particle.force = CubismVector2::default(); 761 | 762 | prev_strand = particle.clone(); 763 | } 764 | } 765 | } 766 | 767 | pub fn initialize2(&mut self) { 768 | for current_setting in self.physics_rig.settings.iter() { 769 | // let prev_strand = self 770 | // .physics_rig 771 | // .particles 772 | // .get_mut(current_setting.base_particle_index) 773 | // .unwrap(); 774 | // // Initialize the top of particle. 775 | // prev_strand.initial_position = CubismVector2::default(); 776 | // prev_strand.last_position = CubismVector2::default(); 777 | // prev_strand.last_gravity = CubismVector2 { x: 0.0, y: 1.0 }; 778 | // prev_strand.velocity = CubismVector2::default(); 779 | // prev_strand.force = CubismVector2::default(); 780 | 781 | // let mut prev_strand = prev_strand.clone(); 782 | // for particle in self 783 | // .physics_rig 784 | // .particles 785 | // .iter_mut() 786 | // .skip(current_setting.base_particle_index + 1) 787 | // .take(current_setting.particle_count - 1) 788 | // { 789 | // let mut radius = CubismVector2::default(); 790 | // radius.y = particle.radius; 791 | // particle.initial_position = prev_strand.initial_position + radius; 792 | // particle.last_position = particle.initial_position; 793 | // particle.last_gravity = CubismVector2 { x: 0.0, y: 1.0 }; 794 | // particle.velocity = CubismVector2::default(); 795 | // particle.force = CubismVector2::default(); 796 | 797 | // prev_strand = particle.clone(); 798 | // } 799 | // 上記コードをリファクタする 800 | 801 | for w in self.physics_rig.particles.windows(2) { 802 | let (prev_stand, next_stand) = (w[0], w[1]); 803 | } 804 | } 805 | } 806 | 807 | // L837 808 | fn interpolate(&mut self, model: &Live2DModelResource, weight: f32) { 809 | for setting_index in 0..self.physics_rig.sub_rig_count { 810 | let current_setting = &self.physics_rig.settings[setting_index]; 811 | // let current_output = &self.physics_rig.outputs[current_setting.base_output_index]; 812 | 813 | // Load input parameters. 814 | for (i, output) in self 815 | .physics_rig 816 | .outputs 817 | .iter_mut() 818 | .skip(current_setting.base_output_index) 819 | .take(current_setting.output_count) 820 | .enumerate() 821 | { 822 | if let Some(index) = output.destination_parameter_index { 823 | update_output_parameter_value( 824 | &mut model.csm_get_mut_parameter_values()[index], 825 | model.csm_get_parameter_minimum_values()[index], 826 | model.csm_get_parameter_maximum_values()[index], 827 | self.previous_rig_outputs[setting_index][i] * (1.0 - weight) 828 | + self.current_rig_outputs[setting_index][i] * weight, 829 | output, 830 | ) 831 | } else { 832 | continue; 833 | } 834 | } 835 | } 836 | } 837 | 838 | /// see: https://github.com/Live2D/CubismNativeFramework/blob/cbd4dfaa5ee95218ea3f9af30f8525c60b4a9b36/src/Physics/CubismPhysics.cpp#L661 839 | pub fn evaluate(&mut self, model: &mut Live2DModelResource, delta_time: f32) { 840 | // L856 841 | if 0.0 >= delta_time { 842 | return; 843 | } 844 | // dbg!(model.csm_get_parameter_values()); 845 | // dbg!(model.csm_get_parameter_default_values()); 846 | // L685 847 | self.current_remain_time += delta_time; 848 | if self.current_remain_time > MAX_DELTA_TIME { 849 | self.current_remain_time = 0.0; 850 | } 851 | 852 | // L696 853 | if self.parameter_cache.len() < model.csm_get_parameter_count() { 854 | self.parameter_cache = Vec::with_capacity(model.csm_get_parameter_count()); 855 | self.parameter_cache 856 | .resize(model.csm_get_parameter_count(), 0.0); 857 | } 858 | if self.parameter_input_cache.len() < model.csm_get_parameter_count() { 859 | self.parameter_input_cache = Vec::with_capacity(model.csm_get_parameter_count()); 860 | self.parameter_input_cache 861 | .resize(model.csm_get_parameter_count(), 0.0); 862 | for (i, param) in model.iter_parameters().enumerate() { 863 | self.parameter_input_cache[i] = *param.value; 864 | } 865 | } 866 | 867 | // L890 868 | let physics_delta_time = if let Some(fps) = self.physics_rig.fps { 869 | if fps > 0.0 { 870 | 1.0 / fps 871 | } else { 872 | delta_time 873 | } 874 | } else { 875 | delta_time 876 | }; 877 | 878 | // L899 879 | while self.current_remain_time >= physics_delta_time { 880 | // L902 881 | // copyRigOutputs _currentRigOutputs to _previousRigOutputs 882 | for setting_index in 0..self.physics_rig.sub_rig_count { 883 | let current_setting = &self.physics_rig.settings[setting_index]; 884 | // let current_output = &this.physics_rig.outputs[setting_index as usize]; 885 | for i in 0..current_setting.output_count { 886 | self.previous_rig_outputs[setting_index][i] = 887 | self.current_rig_outputs[setting_index][i]; 888 | } 889 | } 890 | 891 | // L916 892 | // 入力キャッシュとパラメータで線形補間してUpdateParticlesするタイミングでの入力を計算する。 893 | // _parameterCacheはグループ間での値の伝搬の役割があるので_parameterInputCacheとの分離が必要。 894 | let input_weight = physics_delta_time / self.current_remain_time; 895 | 896 | // L917 897 | for (j, param) in model.iter_parameters().enumerate() { 898 | self.parameter_cache[j] = self.parameter_input_cache[j] * (1.0 - input_weight) 899 | + *param.value * input_weight; 900 | self.parameter_input_cache[j] = self.parameter_cache[j]; 901 | } 902 | 903 | // L923 904 | for setting_index in 0..self.physics_rig.sub_rig_count { 905 | let mut total_angle = 0.0; 906 | let mut total_translation = CubismVector2::default(); 907 | let current_setting = &self.physics_rig.settings[setting_index]; 908 | // let current_input = &this.physics_rig.inputs[current_setting.base_input_index]; 909 | // let current_output = &this.physics_rig.outputs[current_setting.base_output_index]; 910 | // let currentParticles = &this.physics_rig.particles[current_setting.base_particle_index]; 911 | 912 | // Load input parameters. 913 | // L934 914 | for input in self 915 | .physics_rig 916 | .inputs 917 | .iter_mut() 918 | .skip(current_setting.base_input_index) 919 | .take(current_setting.input_count) 920 | { 921 | let weight = input.weight / MAXIMUM_WEIGHT; 922 | // L938 923 | if input.source_parameter_index.is_none() { 924 | input.source_parameter_index = 925 | Some(model.get_parameter_index(&input.source.id)); 926 | } 927 | 928 | // L943 929 | input 930 | .cubism_physics_input_type 931 | .get_normalized_parameter_value( 932 | &mut total_translation, 933 | &mut total_angle, 934 | self.parameter_cache[input.source_parameter_index.unwrap()], 935 | model.csm_get_parameter_minimum_values() 936 | [input.source_parameter_index.unwrap()], 937 | model.csm_get_parameter_maximum_values() 938 | [input.source_parameter_index.unwrap()], 939 | model.csm_get_parameter_default_values() 940 | [input.source_parameter_index.unwrap()], 941 | ¤t_setting.normalization_position, 942 | ¤t_setting.normalization_angle, 943 | input.reflect, 944 | weight, 945 | ); 946 | } 947 | 948 | // L957 949 | let rad_angle = (-total_angle).to_radians(); 950 | 951 | total_translation.x = 952 | total_translation.x * rad_angle.cos() - total_translation.y * rad_angle.sin(); 953 | total_translation.y = 954 | total_translation.x * rad_angle.sin() + total_translation.y * rad_angle.cos(); 955 | 956 | // L962 957 | // Calculate particles position. 958 | update_particles( 959 | &mut self 960 | .physics_rig 961 | .particles 962 | .iter_mut() 963 | .skip(current_setting.base_particle_index) 964 | .collect::<_>(), 965 | current_setting.particle_count, 966 | total_translation, 967 | total_angle, 968 | CubismVector2 { x: 0.0, y: 0.0 }, 969 | MOVEMENT_THRESHOLD * current_setting.normalization_position.maximum, 970 | physics_delta_time, 971 | AIR_RESISTANCE, 972 | ); 973 | 974 | // L974 975 | // Update output parameters. 976 | for (i, output) in self 977 | .physics_rig 978 | .outputs 979 | .iter_mut() 980 | .skip(current_setting.base_output_index) 981 | .take(current_setting.output_count) 982 | .enumerate() 983 | { 984 | let particle_index = output.vertex_index; 985 | // L797 986 | if output.destination_parameter_index.is_none() { 987 | output.destination_parameter_index = 988 | Some(model.get_parameter_index(&output.destination.id)); 989 | } 990 | 991 | if particle_index < 1 || particle_index >= current_setting.particle_count { 992 | continue; 993 | } 994 | 995 | let translation = { 996 | let index = current_setting.base_particle_index + particle_index; 997 | 998 | self.physics_rig.particles[index].position 999 | - self.physics_rig.particles[index - 1].position 1000 | // CubismVector2 { 1001 | // x: self.physics_rig.particles[index].position.x 1002 | // - self.physics_rig.particles[index - 1].position.x, 1003 | // y: self.physics_rig.particles[index].position.y 1004 | // - self.physics_rig.particles[index - 1].position.y, 1005 | // } 1006 | }; 1007 | 1008 | let output_value = output.get_value.get_value( 1009 | translation, 1010 | self.physics_rig 1011 | .particles 1012 | .iter() 1013 | .skip(current_setting.base_particle_index) 1014 | .collect::<_>(), 1015 | particle_index, 1016 | output.reflect, 1017 | CubismVector2 { x: 0.0, y: -1.0 }, 1018 | ); 1019 | 1020 | self.current_rig_outputs[setting_index][i] = output_value; 1021 | 1022 | update_output_parameter_value( 1023 | &mut self.parameter_cache[output.destination_parameter_index.unwrap()], 1024 | model.csm_get_parameter_minimum_values() 1025 | [output.destination_parameter_index.unwrap()], 1026 | model.csm_get_parameter_maximum_values() 1027 | [output.destination_parameter_index.unwrap()], 1028 | output_value, 1029 | output, 1030 | ); 1031 | } 1032 | } 1033 | 1034 | self.current_remain_time -= physics_delta_time; 1035 | } 1036 | let alpha = self.current_remain_time / physics_delta_time; 1037 | 1038 | self.interpolate(&model, alpha); 1039 | } 1040 | } 1041 | 1042 | fn update_particles2( 1043 | strand: &Vec, 1044 | strand_count: usize, 1045 | total_translation: CubismVector2, 1046 | total_angle: f32, 1047 | wind_direction: CubismVector2, 1048 | threshold_value: f32, 1049 | delta_time_seconds: f32, 1050 | air_resistance: f32, 1051 | ) -> Vec { 1052 | let mut ret = vec![]; 1053 | let total_redian = total_angle.to_radians(); 1054 | let current_gravity = total_redian.to_direction().normalize(); 1055 | 1056 | ret.push(CubismPhysicsParticle { 1057 | position: total_translation, 1058 | ..strand[0] 1059 | }); 1060 | 1061 | for w in strand.windows(2) { 1062 | let (prev, next) = (&w[0], &w[1]); 1063 | let last_position = next.position; 1064 | let radian = next.last_gravity.to_radian(¤t_gravity) / air_resistance; 1065 | 1066 | let mut direction = next.position - prev.position; 1067 | direction.x = (radian.cos() * direction.x) - (direction.y * radian.sin()); 1068 | direction.y = (radian.sin() * direction.x) + (direction.y * radian.cos()); 1069 | let delay = next.delay * delta_time_seconds * 30.0; 1070 | let velocity = CubismVector2 { 1071 | x: next.velocity.x * delay, 1072 | y: next.velocity.y * delay, 1073 | }; 1074 | 1075 | let force = ((current_gravity * next.acceleration) + wind_direction) * delay * delay; 1076 | let new_direction = (direction + velocity + force).normalize(); 1077 | let mut next_potison = prev.position + (new_direction * next.radius); 1078 | 1079 | if next_potison.x.abs() < threshold_value { 1080 | next_potison.x = 0.0; 1081 | } 1082 | 1083 | let next_velocity = if delay != 0.0 { 1084 | (next.position - next.last_position) / delay * next.mobility 1085 | } else { 1086 | next.velocity 1087 | }; 1088 | 1089 | let next_particle = CubismPhysicsParticle { 1090 | last_position, 1091 | force: CubismVector2::default(), 1092 | velocity: next_velocity, 1093 | ..next.clone() 1094 | }; 1095 | 1096 | ret.push(next_particle); 1097 | } 1098 | 1099 | ret 1100 | } 1101 | 1102 | // L277 1103 | fn update_particles( 1104 | strand: &mut Vec<&mut CubismPhysicsParticle>, 1105 | strand_count: usize, 1106 | total_translation: CubismVector2, 1107 | total_angle: f32, 1108 | wind_direction: CubismVector2, 1109 | threshold_value: f32, 1110 | delta_time_seconds: f32, 1111 | air_resistance: f32, 1112 | ) { 1113 | strand[0].position = total_translation; 1114 | let total_redian = total_angle.to_radians(); 1115 | let current_gravity = total_redian.to_direction().normalize(); 1116 | 1117 | for i in 1..strand_count { 1118 | // strand[i].force = (current_gravity * strand[i].acceleration) + wind_direction; 1119 | strand[i].last_position = strand[i].position; 1120 | let radian = strand[i].last_gravity.to_radian(¤t_gravity) / air_resistance; 1121 | // let mut direction = CubismVector2 { 1122 | // x: strand[i].position.x - strand[i - 1].position.x, 1123 | // y: strand[i].position.y - strand[i - 1].position.y, 1124 | // }; 1125 | 1126 | let mut direction = strand[i].position - strand[i - 1].position; 1127 | direction.x = (radian.cos() * direction.x) - (direction.y * radian.sin()); 1128 | direction.y = (radian.sin() * direction.x) + (direction.y * radian.cos()); 1129 | 1130 | // strand[i].position = strand[i - 1].position + direction; 1131 | 1132 | let delay = strand[i].delay * delta_time_seconds * 30.0; 1133 | let velocity = CubismVector2 { 1134 | x: strand[i].velocity.x * delay, 1135 | y: strand[i].velocity.y * delay, 1136 | }; 1137 | 1138 | // let force = strand[i].force * delay * delay; 1139 | let force = ((current_gravity * strand[i].acceleration) + wind_direction) * delay * delay; 1140 | // strand[i].position = strand[i].position + velocity + force; 1141 | // strand[i].position = strand[i - 1].position + direction + velocity + force; 1142 | 1143 | // let new_direction = (strand[i].position - strand[i - 1].position).normalize(); 1144 | let new_direction = (direction + velocity + force).normalize(); 1145 | strand[i].position = strand[i - 1].position + (new_direction * strand[i].radius); 1146 | 1147 | if strand[i].position.x.abs() < threshold_value { 1148 | strand[i].position.x = 0.0; 1149 | } 1150 | 1151 | if delay != 0.0 { 1152 | // strand[i].velocity.x = strand[i].position.x - strand[i].last_position.x; 1153 | // strand[i].velocity.y = strand[i].position.y - strand[i].last_position.y; 1154 | 1155 | // strand[i].velocity = strand[i].position - strand[i].last_position; 1156 | // strand[i].velocity /= delay; 1157 | // strand[i].velocity = strand[i].velocity * strand[i].mobility; 1158 | strand[i].velocity = 1159 | (strand[i].position - strand[i].last_position) / delay * strand[i].mobility; 1160 | } 1161 | 1162 | strand[i].force = CubismVector2::default(); 1163 | strand[i].last_gravity = current_gravity; 1164 | } 1165 | } 1166 | 1167 | // L350 1168 | fn update_output_parameter_value( 1169 | parameter_value: &mut f32, 1170 | parameter_value_minimum: f32, 1171 | parameter_value_maximum: f32, 1172 | translation: f32, 1173 | output: &mut CubismPhysicsOutput, 1174 | ) { 1175 | let output_scale = output 1176 | .get_scale 1177 | .get_output_scale_translation(output.translation_scale, output.angle_scale); 1178 | let mut value = translation * output_scale; 1179 | 1180 | if value < parameter_value_minimum { 1181 | if value < output.value_below_minimum { 1182 | output.value_below_minimum = value; 1183 | } 1184 | value = parameter_value_minimum; 1185 | } else if value > parameter_value_maximum { 1186 | if value > output.value_exceeded_maximum { 1187 | output.value_exceeded_maximum = value; 1188 | } 1189 | value = parameter_value_maximum; 1190 | } 1191 | 1192 | let weight = output.weight / MAXIMUM_WEIGHT; 1193 | 1194 | if weight >= 1.0 { 1195 | *parameter_value = value; 1196 | } else { 1197 | value = (*parameter_value * (1.0 - weight)) + (value + weight); 1198 | *parameter_value = value; 1199 | } 1200 | } 1201 | 1202 | #[cfg(test)] 1203 | mod tests { 1204 | #[test] 1205 | fn iter_skip_take() { 1206 | let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 1207 | 1208 | let mut vec_iter = vec.iter().skip(3).take(3); 1209 | 1210 | assert_eq!(vec_iter.next(), Some(&4)); 1211 | assert_eq!(vec_iter.next(), Some(&5)); 1212 | assert_eq!(vec_iter.next(), Some(&6)); 1213 | assert_eq!(vec_iter.next(), None); 1214 | } 1215 | 1216 | // L1053 1217 | #[test] 1218 | fn test_update_output_parameter_value() { 1219 | use super::*; 1220 | 1221 | let mut parameter_value = -1.0; 1222 | 1223 | let mut physic_output = CubismPhysicsOutput { 1224 | destination: CubismPhysicsParameter { 1225 | id: "ParamHairFront".to_string(), 1226 | target_type: CubismPhysicsTargetType::CubismPhysicsTargetTypeParameter, 1227 | }, 1228 | destination_parameter_index: Some(36), 1229 | vertex_index: 1, 1230 | translation_scale: CubismVector2 { x: 0.0, y: 0.0 }, 1231 | angle_scale: 1.52199996, 1232 | weight: 100.0, 1233 | output_type: CubismPhysicsSource::CubismPhysicsSourceAngle, 1234 | reflect: false, 1235 | value_below_minimum: 0.0, 1236 | value_exceeded_maximum: 0.0, 1237 | get_value: GetOutputTranslation::Angle, 1238 | get_scale: GetOutputScaleTranslationType::Angle, 1239 | }; 1240 | 1241 | update_output_parameter_value(&mut parameter_value, -1.0, 1.0, 0.0, &mut physic_output); 1242 | 1243 | assert_eq!(parameter_value, 0.0); 1244 | assert_eq!(physic_output.value_below_minimum, 0.0); 1245 | assert_eq!(physic_output.value_exceeded_maximum, 0.0); 1246 | 1247 | let mut physic_output = CubismPhysicsOutput { 1248 | destination: CubismPhysicsParameter { 1249 | id: "ParamHairFront".to_string(), 1250 | target_type: CubismPhysicsTargetType::CubismPhysicsTargetTypeParameter, 1251 | }, 1252 | destination_parameter_index: Some(50), 1253 | vertex_index: 2, 1254 | translation_scale: CubismVector2 { x: 0.0, y: 0.0 }, 1255 | angle_scale: 20.0, 1256 | weight: 100.0, 1257 | output_type: CubismPhysicsSource::CubismPhysicsSourceAngle, 1258 | reflect: false, 1259 | value_below_minimum: 0.0, 1260 | value_exceeded_maximum: 0.0, 1261 | get_value: GetOutputTranslation::Angle, 1262 | get_scale: GetOutputScaleTranslationType::Angle, 1263 | }; 1264 | 1265 | update_output_parameter_value( 1266 | &mut parameter_value, 1267 | -45.0, 1268 | 45.0, 1269 | 0.213295937, 1270 | &mut physic_output, 1271 | ); 1272 | 1273 | assert_eq!(parameter_value, 4.26591873); 1274 | assert_eq!(physic_output.value_below_minimum, 0.0); 1275 | assert_eq!(physic_output.value_exceeded_maximum, 0.0); 1276 | } 1277 | 1278 | #[test] 1279 | fn test_normalize_parameter_value() { 1280 | use super::*; 1281 | 1282 | assert_eq!( 1283 | normalize_parameter_value(1.31227696, -30.0, 30.0, 0.0, -10.0, 10.0, 0.0, false) 1284 | * 0.600000024, 1285 | -0.262455404 1286 | ); 1287 | 1288 | assert_eq!( 1289 | normalize_parameter_value(-3.26054907, -30.0, 30.0, 0.0, -10.0, 10.0, 0.0, false) 1290 | * 0.600000024, 1291 | 0.652109861 1292 | ); 1293 | 1294 | assert_eq!( 1295 | normalize_parameter_value(-1.99831104, -10.0, 10.0, 0.0, -10.0, 10.0, 0.0, false) 1296 | * 0.400000006, 1297 | 0.79932445 1298 | ); 1299 | 1300 | assert_eq!( 1301 | normalize_parameter_value(10000.0, -30.0, 30.0, 0.0, -10.0, 10.0, 0.0, false) 1302 | * 0.600000024, 1303 | -6.0 1304 | ); 1305 | } 1306 | 1307 | #[test] 1308 | fn test_update_particles() { 1309 | use super::*; 1310 | 1311 | let mut strand_1 = CubismPhysicsParticle { 1312 | initial_position: CubismVector2 { x: 0.0, y: 0.0 }, 1313 | mobility: 1.0, 1314 | delay: 1.0, 1315 | acceleration: 1.0, 1316 | radius: 0.0, 1317 | position: CubismVector2 { 1318 | x: -9.84807777, 1319 | y: -1.71010089, 1320 | }, 1321 | last_position: CubismVector2 { x: 0.0, y: 0.0 }, 1322 | last_gravity: CubismVector2 { x: 0.0, y: 1.0 }, 1323 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1324 | velocity: CubismVector2 { x: 0.0, y: 0.0 }, 1325 | }; 1326 | 1327 | let mut strand_2 = CubismPhysicsParticle { 1328 | initial_position: CubismVector2 { x: 0.0, y: 3.0 }, 1329 | mobility: 0.949999988, 1330 | delay: 0.899999976, 1331 | acceleration: 1.5, 1332 | radius: 3.0, 1333 | position: CubismVector2 { 1334 | x: -7.20117282, 1335 | y: -0.298047066, 1336 | }, 1337 | last_position: CubismVector2 { x: 0.0, y: 3.0 }, 1338 | last_gravity: CubismVector2 { 1339 | x: -0.173648208, 1340 | y: 0.984807789, 1341 | }, 1342 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1343 | velocity: CubismVector2 { 1344 | x: -25.3374615, 1345 | y: -11.6042404, 1346 | }, 1347 | }; 1348 | 1349 | let mut strand = vec![&mut strand_1, &mut strand_2]; 1350 | 1351 | update_particles( 1352 | &mut strand, 1353 | 2, 1354 | CubismVector2 { 1355 | x: -9.84807777, 1356 | y: -1.71010089, 1357 | }, 1358 | -10.0, 1359 | CubismVector2 { x: 0.0, y: 0.0 }, 1360 | 0.0100000007, 1361 | 0.00999999977, 1362 | 5.0, 1363 | ); 1364 | 1365 | assert_eq!( 1366 | strand_1, 1367 | CubismPhysicsParticle { 1368 | initial_position: CubismVector2 { x: 0.0, y: 0.0 }, 1369 | mobility: 1.0, 1370 | delay: 1.0, 1371 | acceleration: 1.0, 1372 | radius: 0.0, 1373 | position: CubismVector2 { 1374 | x: -9.84807777, 1375 | y: -1.71010089, 1376 | }, 1377 | last_position: CubismVector2 { x: 0.0, y: 0.0 }, 1378 | last_gravity: CubismVector2 { x: 0.0, y: 1.0 }, 1379 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1380 | velocity: CubismVector2 { x: 0.0, y: 0.0 }, 1381 | } 1382 | ); 1383 | 1384 | assert_eq!( 1385 | strand_2, 1386 | CubismPhysicsParticle { 1387 | initial_position: CubismVector2 { x: 0.0, y: 3.0 }, 1388 | mobility: 0.949999988, 1389 | delay: 0.899999976, 1390 | acceleration: 1.5, 1391 | radius: 3.0, 1392 | position: CubismVector2 { 1393 | x: -12.649684, 1394 | y: -2.78294802, 1395 | }, 1396 | last_position: CubismVector2 { 1397 | x: -7.20117282, 1398 | y: -0.298047066 1399 | }, 1400 | last_gravity: CubismVector2 { 1401 | x: -0.173648208, 1402 | y: 0.984807789, 1403 | }, 1404 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1405 | velocity: CubismVector2 { 1406 | x: -19.1706886, 1407 | y: -8.74317073, 1408 | }, 1409 | } 1410 | ); 1411 | } 1412 | 1413 | // #[test] 1414 | // fn radian_to_direction_test() { 1415 | // use super::*; 1416 | 1417 | // assert_eq!( 1418 | // radian_to_direction(-0.174532935), 1419 | // CubismVector2 { 1420 | // x: -0.1736482, 1421 | // y: 0.9848077 1422 | // } 1423 | // ); 1424 | // } 1425 | 1426 | #[test] 1427 | fn test_update_particles2() { 1428 | use super::*; 1429 | 1430 | let mut strand_1 = CubismPhysicsParticle { 1431 | initial_position: CubismVector2 { x: 0.0, y: 0.0 }, 1432 | mobility: 1.0, 1433 | delay: 1.0, 1434 | acceleration: 1.0, 1435 | radius: 0.0, 1436 | position: CubismVector2 { 1437 | x: -9.84807777, 1438 | y: -1.71010089, 1439 | }, 1440 | last_position: CubismVector2 { x: 0.0, y: 0.0 }, 1441 | last_gravity: CubismVector2 { x: 0.0, y: 1.0 }, 1442 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1443 | velocity: CubismVector2 { x: 0.0, y: 0.0 }, 1444 | }; 1445 | 1446 | let mut strand_2 = CubismPhysicsParticle { 1447 | initial_position: CubismVector2 { x: 0.0, y: 3.0 }, 1448 | mobility: 0.949999988, 1449 | delay: 0.899999976, 1450 | acceleration: 1.5, 1451 | radius: 3.0, 1452 | position: CubismVector2 { 1453 | x: -7.20117282, 1454 | y: -0.298047066, 1455 | }, 1456 | last_position: CubismVector2 { x: 0.0, y: 3.0 }, 1457 | last_gravity: CubismVector2 { 1458 | x: -0.173648208, 1459 | y: 0.984807789, 1460 | }, 1461 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1462 | velocity: CubismVector2 { 1463 | x: -25.3374615, 1464 | y: -11.6042404, 1465 | }, 1466 | }; 1467 | 1468 | let mut strand = vec![strand_1, strand_2]; 1469 | 1470 | update_particles2( 1471 | &strand, 1472 | 2, 1473 | CubismVector2 { 1474 | x: -9.84807777, 1475 | y: -1.71010089, 1476 | }, 1477 | -10.0, 1478 | CubismVector2 { x: 0.0, y: 0.0 }, 1479 | 0.0100000007, 1480 | 0.00999999977, 1481 | 5.0, 1482 | ); 1483 | 1484 | assert_eq!( 1485 | strand[0], 1486 | CubismPhysicsParticle { 1487 | initial_position: CubismVector2 { x: 0.0, y: 0.0 }, 1488 | mobility: 1.0, 1489 | delay: 1.0, 1490 | acceleration: 1.0, 1491 | radius: 0.0, 1492 | position: CubismVector2 { 1493 | x: -9.84807777, 1494 | y: -1.71010089, 1495 | }, 1496 | last_position: CubismVector2 { x: 0.0, y: 0.0 }, 1497 | last_gravity: CubismVector2 { x: 0.0, y: 1.0 }, 1498 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1499 | velocity: CubismVector2 { x: 0.0, y: 0.0 }, 1500 | } 1501 | ); 1502 | 1503 | assert_eq!( 1504 | strand[1], 1505 | CubismPhysicsParticle { 1506 | initial_position: CubismVector2 { x: 0.0, y: 3.0 }, 1507 | mobility: 0.949999988, 1508 | delay: 0.899999976, 1509 | acceleration: 1.5, 1510 | radius: 3.0, 1511 | position: CubismVector2 { 1512 | x: -12.649684, 1513 | y: -2.78294802, 1514 | }, 1515 | last_position: CubismVector2 { 1516 | x: -7.20117282, 1517 | y: -0.298047066 1518 | }, 1519 | last_gravity: CubismVector2 { 1520 | x: -0.173648208, 1521 | y: 0.984807789, 1522 | }, 1523 | force: CubismVector2 { x: 0.0, y: 0.0 }, 1524 | velocity: CubismVector2 { 1525 | x: -19.1706886, 1526 | y: -8.74317073, 1527 | }, 1528 | } 1529 | ); 1530 | } 1531 | } 1532 | --------------------------------------------------------------------------------