Skip to main content

Adding Goals

note

Since Lively is still in beta, the design is subject to change and should not be considered final!

A developer may desire to create a lifelike behavior that exhibits positional and rotational motion around an offset focal point, as if inspecting the properties of an object located there. Doing so requires the addition of an new goal type, which would encode the focal length to maintain the position of the focus, and the amount of rotational/translational movement allowed. The objectiveโ€™s ๐‘๐‘Ž๐‘™๐‘™ method would use these goals and a Perlin noise generator function to project the needed position and orientation in space to achieve the specified rotation around the focus at a given time and compute the radial and translational distance from those values, returning a cost value. The resulting objective would attempt to produce poses that adhered to this dynamic pattern as a function of time.

To demonstrate how to create additional lifelike behavior mentioned above, the developer will have to makes changes to the following files:

We will name the new objective: PerspectiveLivelinessObjective and the new goal: Cone.

    ...
[repr(C)]
#[derive(Serialize, Deserialize, Clone, Debug, Default)] #[cfg_attr(feature = "pybindings", pyclass)]
pub struct PerspectiveLivelinessObjective {
// Adds position liveliness to the specified link
pub name: String,
pub weight: f64,
pub link: String,
pub frequency: f64,

// Goal Value (shape of noise)
#[serde(skip)]
pub goal: Cone,
#[serde(skip)]
pub time: Option<f64>,

// Inaccessible
#[serde(skip)]
pub noise: Vector3<f64>,
#[serde(skip, default = "get_default_perlin")]
pub perlin: Perlin,
#[serde(skip, default = "get_default_offsets")]
pub offsets: [f64; 3],
}

impl PerspectiveLivelinessObjective {
pub fn new(name: String, weight: f64, link: String, frequency: f64) -> Self {
let mut rng: ThreadRng = thread_rng();
let seed: u32 = rng.gen();
let perlin: Perlin = Perlin::new().set_seed(seed);
let offsets: [f64; 3] = [
f64::from(rng.gen_range(0..1000)),
f64::from(rng.gen_range(0..1000)),
f64::from(rng.gen_range(0..1000)),
];
Self {
name,
weight,
link,
frequency,
goal: Cone::default(),
time: None,
noise: vector![0.0, 0.0, 0.0],
perlin,
offsets,
}
}
}

impl Callable<Cone> for PerspectiveLivelinessObjective {
fn call(&self, v: &Vars, state: &State) -> f64 {
//implementation ommitted
...
return ;
}

fn update(&mut self, time: f64) {
//implementation ommitted
}

fn set_goal(&mut self, goal: Cone) {
self.goal = goal;
}

fn set_weight(&mut self, weight: f64) {
self.weight = weight;
}
}

#[cfg(feature = "pybindings")]
#[pymethods]
impl PerspectiveLivelinessObjective {
#[new]
pub fn from_python(name:String,weight:f64,link:String,frequency:f64) -> Self {
PerspectiveLivelinessObjective::new(name,weight,link,frequency)
}

#[getter]
pub fn get_name(&self) -> PyResult<String> {
Ok(self.name.clone())
}

#[getter]
pub fn get_weight(&self) -> PyResult<f64> {
Ok(self.weight.clone())
}

#[getter]
pub fn get_link(&self) -> PyResult<String> {
Ok(self.link.clone())
}

#[getter]
pub fn get_frequency(&self) -> PyResult<f64> {
Ok(self.frequency.clone())
}
}
...