Initial commit
This commit is contained in:
		
						commit
						8ab8cdedbd
					
				
					 10 changed files with 704 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								.envrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.envrc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					use flake
 | 
				
			||||||
							
								
								
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					/target
 | 
				
			||||||
 | 
					*.svg
 | 
				
			||||||
 | 
					*.data
 | 
				
			||||||
 | 
					*.old
 | 
				
			||||||
 | 
					/bin
 | 
				
			||||||
							
								
								
									
										16
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					# This file is automatically @generated by Cargo.
 | 
				
			||||||
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
 | 
					version = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "byteorder"
 | 
				
			||||||
 | 
					version = "1.5.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "live-link-face"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "byteorder",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										18
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "live-link-face"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					authors = ["Klink"]
 | 
				
			||||||
 | 
					description = "Live Link Face packets encoder/decoder"
 | 
				
			||||||
 | 
					repository = "https://git.zhitno.st/klink/live-link-face"
 | 
				
			||||||
 | 
					keywords = ["unreal", "livelink", "face-tracking"]
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					byteorder = "1.5.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[profile.release]
 | 
				
			||||||
 | 
					opt-level = 'z'     # Optimize for size
 | 
				
			||||||
 | 
					lto = true          # Enable link-time optimization
 | 
				
			||||||
 | 
					codegen-units = 1   # Reduce number of codegen units to increase optimizations
 | 
				
			||||||
 | 
					panic = 'abort'     # Abort on panic
 | 
				
			||||||
 | 
					strip = true        # Strip symbols from binary*
 | 
				
			||||||
							
								
								
									
										8
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					Copyright (C) 2024 Klink
 | 
				
			||||||
 | 
					This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice, this permission notice and the word "NIGGER" shall be included in all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>
 | 
				
			||||||
							
								
								
									
										11
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					# Live Link Face Rust
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Live Link Face Rust is a library for decoding/encoding UDP dadagrams sent by Live Link Face App.
 | 
				
			||||||
 | 
					You get `LiveLinkFace` struct from which you can get any blend shape:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					let face_data = LiveLinkFace::decode(&BUF); // UDP datagram; decode returns tuple of (bool, LiveLinkFace)
 | 
				
			||||||
 | 
					assert!(face_data.0); // 'true' means that there is face data. false - returns empty LiveLinkFace object
 | 
				
			||||||
 | 
					let eye_blink_left = face_data.1.get_blendshape(FaceBlendShape::EyeBlinkLeft);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This library is inspired by https://github.com/JimWest/PyLiveLinkFace
 | 
				
			||||||
							
								
								
									
										270
									
								
								examples/listener.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								examples/listener.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,270 @@
 | 
				
			||||||
 | 
					use live_link_face::{LiveLinkFace, FaceBlendShape};
 | 
				
			||||||
 | 
					use std::io::{prelude::*, stdout};
 | 
				
			||||||
 | 
					use std::net::UdpSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() -> std::io::Result<()> {
 | 
				
			||||||
 | 
					    let socket = UdpSocket::bind("[::]:3000")?; // for UDP4/6
 | 
				
			||||||
 | 
					    let mut buf = [0; 1024];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        // Receives a single datagram message on the socket.
 | 
				
			||||||
 | 
					        // If `buf` is too small to hold
 | 
				
			||||||
 | 
					        // the message, it will be cut off.
 | 
				
			||||||
 | 
					        let (amt, _src) = socket.recv_from(&mut buf)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Redeclare `buf` as slice of the received data
 | 
				
			||||||
 | 
					        // and send data back to origin.
 | 
				
			||||||
 | 
					        let buf = &mut buf[..amt];
 | 
				
			||||||
 | 
					        let face = LiveLinkFace::decode(buf);
 | 
				
			||||||
 | 
					        shitty_print(face.1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn shitty_print(face_data: LiveLinkFace) {
 | 
				
			||||||
 | 
					    print!("{}[2J", 27 as char);
 | 
				
			||||||
 | 
					    stdout().flush().unwrap();
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeBlinkLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeBlinkLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookDownLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookDownLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookInLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookInLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookOutLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookOutLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookUpLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookUpLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeSquintLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeSquintLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeWideLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeWideLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeBlinkRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeBlinkRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookDownRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookDownRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookInRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookInRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookOutRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookOutRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeLookUpRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeLookUpRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeSquintRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeSquintRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "EyeWideRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::EyeWideRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "JawForward: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::JawForward)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "JawLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::JawLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "JawRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::JawRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "JawOpen: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::JawOpen)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthClose: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthClose)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthFunnel: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthFunnel)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthPucker: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthPucker)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthSmileLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthSmileLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthSmileRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthSmileRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthFrownLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthFrownLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthFrownRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthFrownRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthDimpleLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthDimpleLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthDimpleRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthDimpleRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthStretchLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthStretchLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthStretchRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthStretchRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthRollLower: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthRollLower)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthRollUpper: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthRollUpper)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthShrugLower: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthShrugLower)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthShrugUpper: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthShrugUpper)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthPressLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthPressLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthPressRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthPressRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthLowerDownLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthLowerDownLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthLowerDownRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthLowerDownRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthUpperUpLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthUpperUpLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "MouthUpperUpRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::MouthUpperUpRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "BrowDownLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::BrowDownLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "BrowDownRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::BrowDownRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "BrowInnerUp: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::BrowInnerUp)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "BrowOuterUpLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::BrowOuterUpLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "BrowOuterUpRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::BrowOuterUpRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "CheekPuff: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::CheekPuff)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "CheekSquintLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::CheekSquintLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "CheekSquintRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::CheekSquintRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "NoseSneerLeft: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::NoseSneerLeft)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "NoseSneerRight: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::NoseSneerRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "TongueOut: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::TongueOut)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "HeadYaw: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::HeadYaw)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "HeadPitch: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::HeadPitch)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "HeadRoll: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::HeadRoll)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "LeftEyeYaw: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::LeftEyeYaw)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "LeftEyePitch: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::LeftEyePitch)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "LeftEyeRoll: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::LeftEyeRoll)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "RightEyeYaw: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::RightEyeYaw)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "RightEyePitch: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::RightEyePitch)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "RightEyeRoll: {}",
 | 
				
			||||||
 | 
					        face_data.get_blendshape(FaceBlendShape::RightEyeRoll)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										93
									
								
								flake.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								flake.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,93 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "nodes": {
 | 
				
			||||||
 | 
					    "flake-utils": {
 | 
				
			||||||
 | 
					      "inputs": {
 | 
				
			||||||
 | 
					        "systems": "systems"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "locked": {
 | 
				
			||||||
 | 
					        "lastModified": 1705309234,
 | 
				
			||||||
 | 
					        "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
 | 
				
			||||||
 | 
					        "owner": "numtide",
 | 
				
			||||||
 | 
					        "repo": "flake-utils",
 | 
				
			||||||
 | 
					        "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "original": {
 | 
				
			||||||
 | 
					        "owner": "numtide",
 | 
				
			||||||
 | 
					        "repo": "flake-utils",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "naersk": {
 | 
				
			||||||
 | 
					      "inputs": {
 | 
				
			||||||
 | 
					        "nixpkgs": "nixpkgs"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "locked": {
 | 
				
			||||||
 | 
					        "lastModified": 1698420672,
 | 
				
			||||||
 | 
					        "narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
 | 
				
			||||||
 | 
					        "owner": "nix-community",
 | 
				
			||||||
 | 
					        "repo": "naersk",
 | 
				
			||||||
 | 
					        "rev": "aeb58d5e8faead8980a807c840232697982d47b9",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "original": {
 | 
				
			||||||
 | 
					        "owner": "nix-community",
 | 
				
			||||||
 | 
					        "repo": "naersk",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "nixpkgs": {
 | 
				
			||||||
 | 
					      "locked": {
 | 
				
			||||||
 | 
					        "lastModified": 1708296515,
 | 
				
			||||||
 | 
					        "narHash": "sha256-FyF489fYNAUy7b6dkYV6rGPyzp+4tThhr80KNAaF/yY=",
 | 
				
			||||||
 | 
					        "path": "/nix/store/2p9qcybzmx1rfayjw866rz8xljbw45g9-source",
 | 
				
			||||||
 | 
					        "rev": "b98a4e1746acceb92c509bc496ef3d0e5ad8d4aa",
 | 
				
			||||||
 | 
					        "type": "path"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "original": {
 | 
				
			||||||
 | 
					        "id": "nixpkgs",
 | 
				
			||||||
 | 
					        "type": "indirect"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "nixpkgs_2": {
 | 
				
			||||||
 | 
					      "locked": {
 | 
				
			||||||
 | 
					        "lastModified": 1708407374,
 | 
				
			||||||
 | 
					        "narHash": "sha256-EECzarm+uqnNDCwaGg/ppXCO11qibZ1iigORShkkDf0=",
 | 
				
			||||||
 | 
					        "owner": "NixOS",
 | 
				
			||||||
 | 
					        "repo": "nixpkgs",
 | 
				
			||||||
 | 
					        "rev": "f33dd27a47ebdf11dc8a5eb05e7c8fbdaf89e73f",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "original": {
 | 
				
			||||||
 | 
					        "owner": "NixOS",
 | 
				
			||||||
 | 
					        "ref": "nixpkgs-unstable",
 | 
				
			||||||
 | 
					        "repo": "nixpkgs",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "root": {
 | 
				
			||||||
 | 
					      "inputs": {
 | 
				
			||||||
 | 
					        "flake-utils": "flake-utils",
 | 
				
			||||||
 | 
					        "naersk": "naersk",
 | 
				
			||||||
 | 
					        "nixpkgs": "nixpkgs_2"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "systems": {
 | 
				
			||||||
 | 
					      "locked": {
 | 
				
			||||||
 | 
					        "lastModified": 1681028828,
 | 
				
			||||||
 | 
					        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
 | 
				
			||||||
 | 
					        "owner": "nix-systems",
 | 
				
			||||||
 | 
					        "repo": "default",
 | 
				
			||||||
 | 
					        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "original": {
 | 
				
			||||||
 | 
					        "owner": "nix-systems",
 | 
				
			||||||
 | 
					        "repo": "default",
 | 
				
			||||||
 | 
					        "type": "github"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "root": "root",
 | 
				
			||||||
 | 
					  "version": 7
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								flake.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								flake.nix
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,42 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  inputs = {
 | 
				
			||||||
 | 
					    flake-utils.url = "github:numtide/flake-utils";
 | 
				
			||||||
 | 
					    naersk.url = "github:nix-community/naersk";
 | 
				
			||||||
 | 
					    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  outputs = { self, flake-utils, naersk, nixpkgs }:
 | 
				
			||||||
 | 
					    flake-utils.lib.eachDefaultSystem (system:
 | 
				
			||||||
 | 
					      let
 | 
				
			||||||
 | 
					        pkgs = (import nixpkgs) { inherit system; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        naersk' = pkgs.callPackage naersk { };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      in rec {
 | 
				
			||||||
 | 
					        # For `nix build` & `nix run`:
 | 
				
			||||||
 | 
					        packages.default = naersk'.buildPackage {
 | 
				
			||||||
 | 
					          src = ./.;
 | 
				
			||||||
 | 
					          nativeBuildInputs = with pkgs; [ pkg-config openssl ];
 | 
				
			||||||
 | 
					          GIT_HASH = "000000000000000000000000000000";
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # For `nix develop`:
 | 
				
			||||||
 | 
					        devShells.default = pkgs.mkShell {
 | 
				
			||||||
 | 
					          nativeBuildInputs = with pkgs; [
 | 
				
			||||||
 | 
					            rustc
 | 
				
			||||||
 | 
					            cargo
 | 
				
			||||||
 | 
					            cargo-watch
 | 
				
			||||||
 | 
					            clippy
 | 
				
			||||||
 | 
					            rustfmt
 | 
				
			||||||
 | 
					            rust-analyzer
 | 
				
			||||||
 | 
					            pkg-config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            openssl
 | 
				
			||||||
 | 
					          ];
 | 
				
			||||||
 | 
					          env = {
 | 
				
			||||||
 | 
					            RUST_BACKTRACE = 1;
 | 
				
			||||||
 | 
					            RUST_LOG = "debug";
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										240
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,240 @@
 | 
				
			||||||
 | 
					use byteorder::{BigEndian, ByteOrder, LittleEndian};
 | 
				
			||||||
 | 
					use std::collections::VecDeque;
 | 
				
			||||||
 | 
					use std::time::{SystemTime, UNIX_EPOCH};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const FILTER_SIZE: usize = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(dead_code)]
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq)]
 | 
				
			||||||
 | 
					pub enum FaceBlendShape {
 | 
				
			||||||
 | 
					    EyeBlinkLeft = 0,
 | 
				
			||||||
 | 
					    EyeLookDownLeft = 1,
 | 
				
			||||||
 | 
					    EyeLookInLeft = 2,
 | 
				
			||||||
 | 
					    EyeLookOutLeft = 3,
 | 
				
			||||||
 | 
					    EyeLookUpLeft = 4,
 | 
				
			||||||
 | 
					    EyeSquintLeft = 5,
 | 
				
			||||||
 | 
					    EyeWideLeft = 6,
 | 
				
			||||||
 | 
					    EyeBlinkRight = 7,
 | 
				
			||||||
 | 
					    EyeLookDownRight = 8,
 | 
				
			||||||
 | 
					    EyeLookInRight = 9,
 | 
				
			||||||
 | 
					    EyeLookOutRight = 10,
 | 
				
			||||||
 | 
					    EyeLookUpRight = 11,
 | 
				
			||||||
 | 
					    EyeSquintRight = 12,
 | 
				
			||||||
 | 
					    EyeWideRight = 13,
 | 
				
			||||||
 | 
					    JawForward = 14,
 | 
				
			||||||
 | 
					    JawLeft = 15,
 | 
				
			||||||
 | 
					    JawRight = 16,
 | 
				
			||||||
 | 
					    JawOpen = 17,
 | 
				
			||||||
 | 
					    MouthClose = 18,
 | 
				
			||||||
 | 
					    MouthFunnel = 19,
 | 
				
			||||||
 | 
					    MouthPucker = 20,
 | 
				
			||||||
 | 
					    MouthLeft = 21,
 | 
				
			||||||
 | 
					    MouthRight = 22,
 | 
				
			||||||
 | 
					    MouthSmileLeft = 23,
 | 
				
			||||||
 | 
					    MouthSmileRight = 24,
 | 
				
			||||||
 | 
					    MouthFrownLeft = 25,
 | 
				
			||||||
 | 
					    MouthFrownRight = 26,
 | 
				
			||||||
 | 
					    MouthDimpleLeft = 27,
 | 
				
			||||||
 | 
					    MouthDimpleRight = 28,
 | 
				
			||||||
 | 
					    MouthStretchLeft = 29,
 | 
				
			||||||
 | 
					    MouthStretchRight = 30,
 | 
				
			||||||
 | 
					    MouthRollLower = 31,
 | 
				
			||||||
 | 
					    MouthRollUpper = 32,
 | 
				
			||||||
 | 
					    MouthShrugLower = 33,
 | 
				
			||||||
 | 
					    MouthShrugUpper = 34,
 | 
				
			||||||
 | 
					    MouthPressLeft = 35,
 | 
				
			||||||
 | 
					    MouthPressRight = 36,
 | 
				
			||||||
 | 
					    MouthLowerDownLeft = 37,
 | 
				
			||||||
 | 
					    MouthLowerDownRight = 38,
 | 
				
			||||||
 | 
					    MouthUpperUpLeft = 39,
 | 
				
			||||||
 | 
					    MouthUpperUpRight = 40,
 | 
				
			||||||
 | 
					    BrowDownLeft = 41,
 | 
				
			||||||
 | 
					    BrowDownRight = 42,
 | 
				
			||||||
 | 
					    BrowInnerUp = 43,
 | 
				
			||||||
 | 
					    BrowOuterUpLeft = 44,
 | 
				
			||||||
 | 
					    BrowOuterUpRight = 45,
 | 
				
			||||||
 | 
					    CheekPuff = 46,
 | 
				
			||||||
 | 
					    CheekSquintLeft = 47,
 | 
				
			||||||
 | 
					    CheekSquintRight = 48,
 | 
				
			||||||
 | 
					    NoseSneerLeft = 49,
 | 
				
			||||||
 | 
					    NoseSneerRight = 50,
 | 
				
			||||||
 | 
					    TongueOut = 51,
 | 
				
			||||||
 | 
					    HeadYaw = 52,
 | 
				
			||||||
 | 
					    HeadPitch = 53,
 | 
				
			||||||
 | 
					    HeadRoll = 54,
 | 
				
			||||||
 | 
					    LeftEyeYaw = 55,
 | 
				
			||||||
 | 
					    LeftEyePitch = 56,
 | 
				
			||||||
 | 
					    LeftEyeRoll = 57,
 | 
				
			||||||
 | 
					    RightEyeYaw = 58,
 | 
				
			||||||
 | 
					    RightEyePitch = 59,
 | 
				
			||||||
 | 
					    RightEyeRoll = 60,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct LiveLinkFace {
 | 
				
			||||||
 | 
					    pub uuid: String,
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub fps: i32,
 | 
				
			||||||
 | 
					    version: u32,
 | 
				
			||||||
 | 
					    frames: u32,
 | 
				
			||||||
 | 
					    sub_frame: i32,
 | 
				
			||||||
 | 
					    denominator: i32,
 | 
				
			||||||
 | 
					    blend_shapes: Vec<f32>,
 | 
				
			||||||
 | 
					    old_blend_shapes: Vec<VecDeque<f32>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl LiveLinkFace {
 | 
				
			||||||
 | 
					    pub fn new(name: String, uuid: String, fps: i32) -> Self {
 | 
				
			||||||
 | 
					        let now = SystemTime::now()
 | 
				
			||||||
 | 
					            .duration_since(UNIX_EPOCH)
 | 
				
			||||||
 | 
					            .expect("Time went backwards");
 | 
				
			||||||
 | 
					        let frames = now.as_secs() as u32;
 | 
				
			||||||
 | 
					        let sub_frame = 1056060032; // I don't know why
 | 
				
			||||||
 | 
					        let denominator = fps / 60; // 1 most of the time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut old_blend_shapes: Vec<VecDeque<f32>> = Vec::with_capacity(61);
 | 
				
			||||||
 | 
					        for _ in 0..61 {
 | 
				
			||||||
 | 
					            old_blend_shapes.push(VecDeque::with_capacity(FILTER_SIZE));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LiveLinkFace {
 | 
				
			||||||
 | 
					            uuid,
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            fps,
 | 
				
			||||||
 | 
					            version: 6,
 | 
				
			||||||
 | 
					            frames,
 | 
				
			||||||
 | 
					            sub_frame,
 | 
				
			||||||
 | 
					            denominator,
 | 
				
			||||||
 | 
					            blend_shapes: vec![0.0; 61],
 | 
				
			||||||
 | 
					            old_blend_shapes,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn encode(&self) -> Vec<u8> {
 | 
				
			||||||
 | 
					        let mut encoded: Vec<u8> = Vec::new();
 | 
				
			||||||
 | 
					        encoded.extend(&(self.version).to_le_bytes());
 | 
				
			||||||
 | 
					        encoded.extend(self.uuid.as_bytes());
 | 
				
			||||||
 | 
					        encoded.extend(&(self.name.len() as i32).to_be_bytes());
 | 
				
			||||||
 | 
					        encoded.extend(self.name.as_bytes());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Prepare frame data
 | 
				
			||||||
 | 
					        let now = SystemTime::now()
 | 
				
			||||||
 | 
					            .duration_since(UNIX_EPOCH)
 | 
				
			||||||
 | 
					            .expect("Time went backwards");
 | 
				
			||||||
 | 
					        let frame_time = now.as_secs() as u32;
 | 
				
			||||||
 | 
					        let frame_rate = (self.fps, self.denominator);
 | 
				
			||||||
 | 
					        let blend_shapes_data = self.blend_shapes.to_vec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        encoded.extend(&(frame_time).to_be_bytes());
 | 
				
			||||||
 | 
					        encoded.extend(&(self.sub_frame).to_be_bytes());
 | 
				
			||||||
 | 
					        encoded.extend(&(frame_rate).0.to_be_bytes());
 | 
				
			||||||
 | 
					        encoded.extend(&(frame_rate).1.to_be_bytes());
 | 
				
			||||||
 | 
					        encoded.extend(&(blend_shapes_data.len() as i32).to_be_bytes());
 | 
				
			||||||
 | 
					        for f in &blend_shapes_data {
 | 
				
			||||||
 | 
					            encoded.extend(&f.to_be_bytes());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        encoded
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn get_blendshape(&self, index: FaceBlendShape) -> f32 {
 | 
				
			||||||
 | 
					        self.blend_shapes[index as usize]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_blendshape(&mut self, index: FaceBlendShape, value: f32, no_filter: bool) {
 | 
				
			||||||
 | 
					        let idx = index as usize;
 | 
				
			||||||
 | 
					        if no_filter {
 | 
				
			||||||
 | 
					            self.blend_shapes[idx] = value;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            self.old_blend_shapes[idx].push_back(value);
 | 
				
			||||||
 | 
					            let filtered_value: f32 = self.old_blend_shapes[idx].iter().sum::<f32>()
 | 
				
			||||||
 | 
					                / self.old_blend_shapes[idx].len() as f32;
 | 
				
			||||||
 | 
					            self.blend_shapes[idx] = filtered_value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn decode(data: &[u8]) -> (bool, LiveLinkFace) {
 | 
				
			||||||
 | 
					        let version = LittleEndian::read_i16(&data[0..4]);
 | 
				
			||||||
 | 
					        let uuid = String::from_utf8_lossy(&data[4..41]).to_string();
 | 
				
			||||||
 | 
					        let name_length = BigEndian::read_i32(&data[41..45]) as usize;
 | 
				
			||||||
 | 
					        let name_end_pos = 45 + name_length;
 | 
				
			||||||
 | 
					        let name = String::from_utf8_lossy(&data[45..name_end_pos]).to_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if data.len() > name_end_pos + 16 {
 | 
				
			||||||
 | 
					            let frame_number = BigEndian::read_i32(&data[name_end_pos..name_end_pos + 4]) as usize;
 | 
				
			||||||
 | 
					            let sub_frame = BigEndian::read_i32(&data[name_end_pos + 4..name_end_pos + 8]);
 | 
				
			||||||
 | 
					            let fps = BigEndian::read_i32(&data[name_end_pos + 8..name_end_pos + 12]);
 | 
				
			||||||
 | 
					            let denominator = BigEndian::read_i32(&data[name_end_pos + 12..name_end_pos + 16]);
 | 
				
			||||||
 | 
					            let (int_bytes, _rest) = data[70..74].split_at(std::mem::size_of::<i8>());
 | 
				
			||||||
 | 
					            let data_length = i8::from_be_bytes(int_bytes.try_into().unwrap());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if data_length != 61 {
 | 
				
			||||||
 | 
					                println!(
 | 
				
			||||||
 | 
					                    "Blendshape data is malformed. got: {}; requred: 61",
 | 
				
			||||||
 | 
					                    data_length
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut blend_shapes: [f32; 61] = [0.0; 61];
 | 
				
			||||||
 | 
					            for i in 0..61 {
 | 
				
			||||||
 | 
					                blend_shapes[i] = BigEndian::read_f32(
 | 
				
			||||||
 | 
					                    &data[name_end_pos + 17 + i * 4..name_end_pos + 17 + (i + 1) * 4],
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // println!("vata: {:?}", vata);
 | 
				
			||||||
 | 
					            let mut live_link_face = LiveLinkFace::new(name, uuid, fps);
 | 
				
			||||||
 | 
					            live_link_face.version = version as u32;
 | 
				
			||||||
 | 
					            live_link_face.frames = frame_number as u32;
 | 
				
			||||||
 | 
					            live_link_face.sub_frame = sub_frame;
 | 
				
			||||||
 | 
					            live_link_face.denominator = denominator;
 | 
				
			||||||
 | 
					            live_link_face.blend_shapes = blend_shapes.to_vec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return (true, live_link_face);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					            false,
 | 
				
			||||||
 | 
					            LiveLinkFace::new("Default".to_string(), "Default".to_string(), 60),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use crate::{LiveLinkFace, FaceBlendShape};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const BUF: [u8;315] = [
 | 
				
			||||||
 | 
					            0x06, 0x00, 0x00, 0x00, 0x24, 0x30, 0x30, 0x46, 0x44, 0x45, 0x31, 0x43, 0x33, 0x2D,
 | 
				
			||||||
 | 
					            0x46, 0x41, 0x38, 0x35, 0x2D, 0x34, 0x30, 0x36, 0x37, 0x2D, 0x39, 0x37, 0x44, 0x38,
 | 
				
			||||||
 | 
					            0x2D, 0x31, 0x31, 0x33, 0x37, 0x43, 0x41, 0x31, 0x38, 0x33, 0x30, 0x35, 0x45, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x09, 0x46, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x63, 0x6B, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0xFD, 0x2F, 0x3E, 0x02, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01,
 | 
				
			||||||
 | 
					            0x3D, 0x39, 0xC0, 0x37, 0x00, 0x3D, 0x95, 0x3A, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x39, 0xC0, 0x95, 0xF3, 0x3D, 0x95, 0x2D, 0x4E, 0x3D, 0x02, 0xA5, 0x4E, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x39, 0x7B, 0x26, 0xB0, 0x3B, 0x0D, 0x5D, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x3B,
 | 
				
			||||||
 | 
					            0x1D, 0xB8, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x3B, 0xE3, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xFD, 0xE5,
 | 
				
			||||||
 | 
					            0x7A, 0x3B, 0x09, 0x19, 0x78, 0x3B, 0x64, 0x88, 0xAE, 0x3B, 0x2E, 0x7D, 0xE2, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x3A, 0xD8, 0xD2, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
					            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD,
 | 
				
			||||||
 | 
					            0xE8, 0xCC, 0x08, 0xBE, 0x39, 0x6A, 0x4C, 0x3C, 0x36, 0xE9, 0x45, 0xBC, 0x9D, 0xC5,
 | 
				
			||||||
 | 
					            0x24, 0x3D, 0x2A, 0x20, 0x39, 0xBA, 0xE9, 0xF6, 0xC7, 0xBC, 0x9B, 0x6C, 0xD2, 0x3D,
 | 
				
			||||||
 | 
					            0x2A, 0x1E, 0x4B, 0x3A, 0xA1, 0xB0, 0x42,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn decode_buf() {
 | 
				
			||||||
 | 
					        let face_data = LiveLinkFace::decode(&BUF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(face_data.0);
 | 
				
			||||||
 | 
					        assert_eq!(face_data.1.get_blendshape(FaceBlendShape::EyeBlinkLeft), 0.00036662072);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue