use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use crate::{
graph::{EvalContext, ExprError},
Attribute, BoxedModifier, ExprHandle, Modifier, ModifierContext, Module, ShaderWriter,
};
#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
pub struct SetAttributeModifier {
pub attribute: Attribute,
pub value: ExprHandle,
}
impl SetAttributeModifier {
pub fn new(attribute: Attribute, value: ExprHandle) -> Self {
assert!(
attribute != Attribute::ID,
"The particle's ID is a read-only pseudo-attribute, cannot be assigned."
);
assert!(
attribute != Attribute::PARTICLE_COUNTER,
"The PARTICLE_COUNTER attribute is a read-only pseudo-attribute, cannot be assigned."
);
Self { attribute, value }
}
fn eval(
&self,
module: &mut Module,
context: &mut dyn EvalContext,
) -> Result<String, ExprError> {
let expr = module.try_get(self.value)?;
if let Some(value_type) = expr.value_type() {
let attr_value_type = self.attribute.value_type();
if value_type != attr_value_type {
return Err(ExprError::TypeError(format!(
"Mismatching expression type in SetAttributeModifer: attribute '{}' requires an expression producing a value of type {}, but a value of type {} was produced instead",
self.attribute.name().to_uppercase(), attr_value_type, value_type)));
}
}
let attr = module.attr(self.attribute);
let attr = context.eval(module, attr)?;
let expr = context.eval(module, self.value)?;
Ok(format!("{} = {};\n", attr, expr))
}
}
impl Modifier for SetAttributeModifier {
fn context(&self) -> ModifierContext {
ModifierContext::Init | ModifierContext::Update
}
fn attributes(&self) -> &[Attribute] {
std::slice::from_ref(&self.attribute)
}
fn boxed_clone(&self) -> BoxedModifier {
Box::new(*self)
}
fn apply(&self, module: &mut Module, context: &mut ShaderWriter) -> Result<(), ExprError> {
let code = self.eval(module, context)?;
context.main_code += &code;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
pub struct InheritAttributeModifier {
pub attribute: Attribute,
}
impl InheritAttributeModifier {
pub fn new(attribute: Attribute) -> Self {
Self { attribute }
}
fn eval(
&self,
module: &mut Module,
context: &mut dyn EvalContext,
) -> Result<String, ExprError> {
let attr = module.attr(self.attribute);
let attr = context.eval(module, attr)?;
Ok(format!(
"{} = parent_particle.{};\n",
attr,
self.attribute.name()
))
}
}
impl Modifier for InheritAttributeModifier {
fn context(&self) -> ModifierContext {
ModifierContext::Init
}
fn attributes(&self) -> &[Attribute] {
std::slice::from_ref(&self.attribute)
}
fn boxed_clone(&self) -> BoxedModifier {
Box::new(*self)
}
fn apply(&self, module: &mut Module, context: &mut ShaderWriter) -> Result<(), ExprError> {
let code = self.eval(module, context)?;
context.main_code += &code;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::SetAttributeModifier;
use crate::{
Attribute, ExprError, ModifierContext, Module, ParticleLayout, PropertyLayout, ShaderWriter,
};
#[test]
fn eval_validate() {
let mut module = Module::default();
let attr = Attribute::POSITION; let expr = module.lit(3.); let attr = SetAttributeModifier::new(attr, expr);
let property_layout = PropertyLayout::empty();
let particle_layout = ParticleLayout::empty();
let mut context =
ShaderWriter::new(ModifierContext::Init, &property_layout, &particle_layout);
assert!(matches!(
attr.eval(&mut module, &mut context),
Err(ExprError::TypeError(_))
));
}
}