use super::*;
/// A `ValueAccessor` is used to set/get/animate a component value.
/// `bool` and `i64` types will be set to target directly when using `animate`.
///
/// Example:
/// ```
/// let ball_mesh = ...;
/// let ball_entity = Entity::create("ball");
/// ball_entity.render().mesh().set(ball_mesh);
/// ball_entity
/// .transform()
/// .position() // ValueAccessor returned here
/// .set(Vec3::ZERO);
/// let duration = 1.0; // Duration is in seconds
/// ball_entity
/// .transform()
/// .position() // ValueAccessor returned here
/// .animate(Vec3::new(0.0, 2.5, -40.0), duration);
/// ```
pub struct ValueAccessorReadWriteAnimate<T> {
id: Entity,
component: ComponentType,
param: u32,
_marker: std::marker::PhantomData<T>,
}
/// Struct describing an animation request.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub struct AnimationRequest {
pub id: Entity,
pub component: ComponentType,
pub param: u32,
pub target: Value,
pub options: AnimationOptions,
pub enqueue: bool,
}
/// An `AnimationBuilder` is used to build an animation for a component value.
///
/// Set options on it such as curve / easing type, delay, target and finally submit the animation.
///
/// Note: bools and integers (also all kinds of entity references) will be set to their target directly,
/// however `delay` can be used to delay the set of the value.
pub struct AnimationBuilder<T> {
request: AnimationRequest,
_marker: std::marker::PhantomData<T>,
}
impl<T> AnimationBuilder<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Creates an `AnimationBuilder` from a `ValueAccessor`.
pub fn new(accessor: &ValueAccessorReadWriteAnimate<T>) -> Self {
Self {
_marker: std::marker::PhantomData,
request: AnimationRequest {
id: accessor.id,
component: accessor.component,
param: accessor.param,
options: AnimationOptions {
curve: AnimationCurve::Linear,
duration: 0.0,
loop_count: 1,
delay: 0.0,
smoothing: 0.0,
},
target: Value::none(),
enqueue: false,
},
}
}
/// Sets the delay of the animation (how long to wait before it starts)
pub fn delay(&mut self, seconds: f32) -> &mut Self {
self.request.options.delay = seconds;
self
}
/// Sets the `target` value that the animated value will approach.
pub fn target(&mut self, target: T, duration: f32) -> &mut Self {
self.request.target = <ValueConverter as ValueConverterTrait<T>>::into_value(target);
self.request.options.duration = duration;
self
}
/// Sets the curve shape of the value animation.
pub fn curve(&mut self, curve: AnimationCurve) -> &mut Self {
self.request.options.curve = curve;
self
}
/// Sets the number of times the animation will loop. 0 is currently undefined.
pub fn loop_count(&mut self, loop_count: u32) -> &mut Self {
self.request.options.loop_count = loop_count;
self
}
/// Sets the method of smoothing when the animation target changes.
pub fn smoothing(&mut self, smoothing: f32) -> &mut Self {
self.request.options.smoothing = smoothing;
self
}
/// Enqueues the animation instead of cancelling a currently on-going animation (if any).
pub fn enqueue(&mut self) -> &mut Self {
self.request.enqueue = true;
self
}
/// Finalizes the animation.
pub fn submit(&self) {
Animation::enqueue_request_in_global_queue(&self.request);
}
/// Finalizes the animation.
///
/// Also gives you the opportunity to pass in a `callback` that will be called
/// once the animation is finished. For this to work, you must call `entity_messenger().process_messages()`
/// from your `world_update` function.
pub fn submit_messenger(&self, callback: Option<Box<AnimationReplyFn<'static>>>) {
if let Some(callback) = callback {
EntityMessageDispatcher::global().animation_in_global_queue(&self.request, callback);
} else {
EntityMessenger::get()
.global_queue()
.animate_value_no_reply(&self.request);
}
}
#[allow(unused)]
#[inline]
fn build_request(&self) -> AnimationRequest {
self.request
}
}
impl<T> std::ops::Deref for AnimationBuilder<T>
where
ValueConverter: ValueConverterTrait<T>,
{
type Target = AnimationRequest;
#[inline]
fn deref(&self) -> &Self::Target {
&self.request
}
}
/// Value accessor with read-only access
pub struct ValueAccessorRead<T> {
va: ValueAccessorReadWriteAnimate<T>,
}
impl<T> ValueAccessorRead<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Create a new value accessor with read-only access
#[inline]
pub fn new(id: Entity, component: ComponentType, param: u32) -> Self {
Self {
va: ValueAccessorReadWriteAnimate::new(id, component, param),
}
}
/// Returns the current value.
#[inline]
pub fn get(&self) -> T {
self.va.get()
}
}
/// Value accessor with read-write-access
pub struct ValueAccessorReadWrite<T> {
va: ValueAccessorReadWriteAnimate<T>,
}
impl<T> ValueAccessorReadWrite<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Create a new value accessor with read-write access
#[inline]
pub fn new(id: Entity, component: ComponentType, param: u32) -> Self {
Self {
va: ValueAccessorReadWriteAnimate::new(id, component, param),
}
}
/// Sets the value to `v`.
#[inline]
pub fn set(&self, v: T) {
self.va.set(v);
}
/// Returns the current value.
#[inline]
pub fn get(&self) -> T {
self.va.get()
}
}
/// Value accessor with read-write-access to data objects.
pub struct ValueAccessorDataReadWrite<T> {
va: ValueAccessorReadWriteAnimate<T>,
}
impl<T> ValueAccessorDataReadWrite<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Create a new value accessor with read-write access
#[inline]
pub fn new(id: Entity, component: ComponentType, param: u32) -> Self {
Self {
va: ValueAccessorReadWriteAnimate::new(id, component, param),
}
}
/// Returns the current data handle.
#[inline]
fn get_handle(&self) -> DataHandle {
let v = World::get_entity_value(self.va.id, self.va.component, self.va.param);
// This will never retain
<ValueConverter as ValueConverterTrait<DataHandle>>::from_value(&v)
}
fn set_handle(&self, handle: DataHandle) {
World::set_entity_value(
self.va.id,
self.va.component,
self.va.param,
&Value::from_i64(handle.as_ffi() as i64),
);
}
/// Sets the value to `v`.
pub fn set(&self, v: T) {
let old_handle = self.get_handle();
self.va.set(v); // moved here thus drop will never be called here.
let new_handle = self.get_handle();
if old_handle.is_valid() && (old_handle != new_handle) {
World::destroy_data(old_handle); // We release the data we just overwrote.
}
}
/// Resets the value to an invalid data handle.
pub fn reset(&self) {
let old_handle = self.get_handle();
if old_handle.is_valid() {
World::destroy_data(old_handle); // We release the data we will overwrite.
}
self.set_handle(DataHandle::invalid());
}
/// Returns the current value.
#[inline]
pub fn get(&self) -> T {
self.va.get()
}
}
/// Trait for writing component values
pub trait ValueAccessorWriteTrait<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Sets the value to `v`.
fn set(&self, v: T);
}
/// Trait for reading component values
pub trait ValueAccessorReadTrait<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Returns the current value.
fn get(&self) -> T;
}
/// Trait for animating component values
pub trait ValueAccessorAnimateTrait<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Will linearly animate / transition the value from the current value to the
/// target `v` using `duration` (in seconds).
///
/// Will stop and continue from any ongoing animation.
///
/// Note: bools and integers (also all kinds of entity references) will be set to
/// their target directly, however delay can be used to delay the set of the value.
fn animate(&self, v: T, duration: f32);
/// Will linearly animate / transition the value from the current value to the
// target `v` using `duration` (in seconds) and apply a smooth filter to the output.
///
/// Will stop and continue from any ongoing animation.
/// A smoothing constant of 0 means no smoothing, higher value means more smoothing
/// (i.e. the value will take a longer time to change).
///
/// Useful if you want to smoothen out discontinuities when animating to new targets,
/// cancelling the old animation.
///
/// Note: bools and integers (also all kinds of entity references) will be set to their
/// target directly, however delay can be used to delay the set of the value.
fn animate_smooth(&self, v: T, duration: f32, smoothing: f32);
/// Starts setting up an animation of this value.
///
/// Call methods on the returned `AnimationBuilder` to configure it.
fn build_animation(&self) -> AnimationBuilder<T>;
}
impl<T> ValueAccessorReadWriteAnimate<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Create a new value accessor for a specific entity, component and parameter.
#[inline]
pub fn new(id: Entity, component: ComponentType, param: u32) -> Self
where
T: Sized,
{
Self {
id,
component,
param,
_marker: std::marker::PhantomData,
}
}
/// Sets the value to `v`.
#[inline]
pub fn set(&self, v: T) {
World::set_entity_value(
self.id,
self.component,
self.param,
&<ValueConverter as ValueConverterTrait<T>>::into_value(v),
);
}
/// Returns the current value.
#[inline]
pub fn get(&self) -> T {
let v = World::get_entity_value(self.id, self.component, self.param);
<ValueConverter as ValueConverterTrait<T>>::from_value(&v)
}
/// Will linearly animate / transition the value from the current value to the
/// target `v` using `duration` (in seconds).
///
/// Will stop and continue from any ongoing animation.
///
/// Note: bools and integers (also all kinds of entity references) will be set to
/// their target directly, however delay can be used to delay the set of the value.
pub fn animate(&self, v: T, duration: f32) {
AnimationBuilder::<T>::new(self)
.target(v, duration)
.submit();
}
/// Will linearly animate / transition the value from the current value to the
// target `v` using `duration` (in seconds) and apply a smooth filter to the output.
///
/// Will stop and continue from any ongoing animation.
/// A smoothing constant of 0 means no smoothing, higher value means more smoothing
/// (i.e. the value will take a longer time to change).
///
/// Useful if you want to smoothen out discontinuities when animating to new targets,
/// cancelling the old animation.
///
/// Note: bools and integers (also all kinds of entity references) will be set to their
/// target directly, however delay can be used to delay the set of the value.
pub fn animate_smooth(&self, v: T, duration: f32, smoothing: f32) {
AnimationBuilder::<T>::new(self)
.target(v, duration)
.smoothing(smoothing)
.submit();
}
/// Starts setting up an animation of this value.
///
/// Call methods on the returned `AnimationBuilder` to configure it.
pub fn build_animation(&self) -> AnimationBuilder<T> {
AnimationBuilder::<T>::new(self)
}
}
impl<T> ValueAccessorReadTrait<T> for ValueAccessorRead<T>
where
ValueConverter: ValueConverterTrait<T>,
{
#[inline]
fn get(&self) -> T {
self.va.get()
}
}
impl<T> ValueAccessorReadTrait<T> for ValueAccessorReadWrite<T>
where
ValueConverter: ValueConverterTrait<T>,
{
#[inline]
fn get(&self) -> T {
self.get()
}
}
/// Implementation of trait for reading component values
impl<T> ValueAccessorReadTrait<T> for ValueAccessorReadWriteAnimate<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Returns the current value.
#[inline]
fn get(&self) -> T {
self.get()
}
}
impl<T> ValueAccessorWriteTrait<T> for ValueAccessorReadWrite<T>
where
ValueConverter: ValueConverterTrait<T>,
{
#[inline]
fn set(&self, v: T) {
self.set(v);
}
}
/// Implementation of trait for writing component values
impl<T> ValueAccessorWriteTrait<T> for ValueAccessorReadWriteAnimate<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Sets the value to `v`.
#[inline]
fn set(&self, v: T) {
self.set(v);
}
}
/// Implementation of trait for animating component values
impl<T> ValueAccessorAnimateTrait<T> for ValueAccessorReadWriteAnimate<T>
where
ValueConverter: ValueConverterTrait<T>,
{
/// Will linearly animate / transition the value from the current value to the
/// target `v` using `duration` (in seconds).
///
/// Will stop and continue from any ongoing animation.
///
/// Note: bools and integers (also all kinds of entity references) will be set to
/// their target directly, however delay can be used to delay the set of the value.
fn animate(&self, v: T, duration: f32) {
self.animate(v, duration);
}
/// Will linearly animate / transition the value from the current value to the
// target `v` using `duration` (in seconds) and apply a smooth filter to the output.
///
/// Will stop and continue from any ongoing animation.
/// A smoothing constant of 0 means no smoothing, higher value means more smoothing
/// (i.e. the value will take a longer time to change).
///
/// Useful if you want to smoothen out discontinuities when animating to new targets,
/// cancelling the old animation.
///
/// Note: bools and integers (also all kinds of entity references) will be set to their
/// target directly, however delay can be used to delay the set of the value.
fn animate_smooth(&self, v: T, duration: f32, smoothing: f32) {
self.animate_smooth(v, duration, smoothing);
}
/// Starts setting up an animation of this value.
///
/// Call methods on the returned `AnimationBuilder` to configure it.
fn build_animation(&self) -> AnimationBuilder<T> {
self.build_animation()
}
}
#[macro_export]
/// Macro for implementing standard accessor trait
macro_rules! impl_world_accessor {
($(#[$attr: meta])* $component_type: ident, $param_name: ident, $param_type: ty, $accessor_name: ident, $visibility: ident) => {
$(#[$attr])*
#[inline]
pub fn $accessor_name(&self) -> $visibility<$param_type> {
use ffi::$component_type;
$visibility::new(self.id, ffi::ComponentType::$component_type, $component_type::$param_name.into())
}
};
}
#[macro_export]
/// Macro for implementing standard accessor trait, but with an index parameter.
macro_rules! impl_world_accessor_indexed {
($(#[$attr: meta])* $component_type: ident, $param_name: ident, $param_type: ty, $accessor_name: ident, $index_type: ty, $visibility: ident) => {
$(#[$attr])*
#[inline]
pub fn $accessor_name(&self, index: $index_type) -> $visibility<$param_type> {
use ffi::$component_type;
let param_index: u32 = $component_type::$param_name.into();
$visibility::new(self.id, ffi::ComponentType::$component_type, param_index + index as u32)
// TODO: If index is out of range, maybe do something?
}
};
}