blob: 513f921803203df1ede0e98817ba084f057098ff [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::{cell::Cell, iter};
use once_cell::unsync::OnceCell;
use crate::{
bones::{
skinnable::{self, Skinnable},
Tendon,
},
component::Component,
component_dirt::ComponentDirt,
container_component::ContainerComponent,
core::{Core, CoreContext, Object, ObjectRef, OnAdded, Property},
dyn_vec::DynVec,
math::Mat,
option_cell::OptionCell,
shapes::PathVertex,
status_code::StatusCode,
TransformComponent,
};
#[derive(Debug)]
pub struct Skin {
container_component: ContainerComponent,
xx: Property<f32>,
yx: Property<f32>,
xy: Property<f32>,
yy: Property<f32>,
tx: Property<f32>,
ty: Property<f32>,
world_transform: Cell<Mat>,
tendons: DynVec<Object<Tendon>>,
bone_transforms: OnceCell<Box<[Cell<Mat>]>>,
skinnable: OptionCell<Object>,
}
impl ObjectRef<'_, Skin> {
pub fn xx(&self) -> f32 {
self.xx.get()
}
pub fn set_xx(&self, xx: f32) {
self.xx.set(xx);
}
pub fn yx(&self) -> f32 {
self.yx.get()
}
pub fn set_yx(&self, yx: f32) {
self.yx.set(yx);
}
pub fn xy(&self) -> f32 {
self.xy.get()
}
pub fn set_xy(&self, xy: f32) {
self.xy.set(xy);
}
pub fn yy(&self) -> f32 {
self.yy.get()
}
pub fn set_yy(&self, yy: f32) {
self.yy.set(yy);
}
pub fn tx(&self) -> f32 {
self.tx.get()
}
pub fn set_tx(&self, tx: f32) {
self.tx.set(tx);
}
pub fn ty(&self) -> f32 {
self.ty.get()
}
pub fn set_ty(&self, ty: f32) {
self.ty.set(ty);
}
}
impl ObjectRef<'_, Skin> {
pub fn push_tendon(&self, tendon: Object<Tendon>) {
self.tendons.push(tendon);
}
pub fn deform(&self, vertices: impl Iterator<Item = Object<PathVertex>>) {
let bone_transforms =
self.bone_transforms.get().expect("bone_transforms should already be set");
for vertex in vertices {
vertex.as_ref().deform(self.world_transform.get(), bone_transforms);
}
}
pub fn build_dependencies(&self) {
for tendon in self.tendons.iter() {
let tendon = tendon.as_ref();
let bone = tendon.bone().expect("Tendon's bone must be set");
bone.as_ref().cast::<Component>().push_dependent(self.cast::<Component>().as_object());
}
self.bone_transforms
.set(iter::repeat(Cell::new(Mat::default())).take(self.tendons.len() + 1).collect())
.expect("bone_transform has been set twice");
}
pub fn on_dirty(&self, _dirt: ComponentDirt) {
let skinnable = self.skinnable.get().expect("Skinnable should already be set");
skinnable::as_ref(skinnable.as_ref()).mark_skin_dirty();
}
pub fn update(&self, _value: ComponentDirt) {
if let Some(bone_transforms) = self.bone_transforms.get() {
for (bone_transform, tendon) in bone_transforms.iter().skip(1).zip(self.tendons.iter())
{
let tendon = tendon.as_ref();
let bone =
tendon.bone().expect("Tendon's bone must be set").cast::<TransformComponent>();
bone_transform.set(bone.as_ref().world_transform() * tendon.inverse_bind());
}
}
}
}
impl Core for Skin {
parent_types![(container_component, ContainerComponent)];
properties![
(104, xx, set_xx),
(105, yx, set_yx),
(106, xy, set_xy),
(107, yy, set_yy),
(108, tx, set_tx),
(109, ty, set_ty),
container_component,
];
}
impl OnAdded for ObjectRef<'_, Skin> {
on_added!([on_added_dirty, import], ContainerComponent);
fn on_added_clean(&self, _context: &dyn CoreContext) -> StatusCode {
self.world_transform.set(Mat {
scale_x: self.xx(),
shear_y: self.xy(),
shear_x: self.yx(),
scale_y: self.yy(),
translate_x: self.tx(),
translate_y: self.ty(),
});
self.skinnable.set(
self.cast::<Component>().parent().and_then(|parent| skinnable::try_from(parent.into())),
);
if let Some(skinnable) = self.skinnable.get() {
skinnable::as_ref(skinnable.as_ref()).set_skin(self.as_object());
} else {
return StatusCode::MissingObject;
}
StatusCode::Ok
}
}
impl Default for Skin {
fn default() -> Self {
Self {
container_component: ContainerComponent::default(),
xx: Property::new(1.0),
yx: Property::new(0.0),
xy: Property::new(0.0),
yy: Property::new(1.0),
tx: Property::new(0.0),
ty: Property::new(0.0),
world_transform: Cell::new(Mat::default()),
tendons: DynVec::new(),
bone_transforms: OnceCell::new(),
skinnable: OptionCell::new(),
}
}
}