Skip to main content

fyroxed_base/scene/commands/
material.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::{
22    command::{CommandContext, CommandTrait},
23    fyrox::{
24        asset::ResourceData,
25        core::{log::Log, sstorage::ImmutableString},
26        material::{
27            shader::ShaderResource, Material, MaterialProperty, MaterialResource,
28            MaterialResourceBinding,
29        },
30    },
31};
32use std::path::{Path, PathBuf};
33
34fn try_save(path: Option<&Path>, material: &MaterialResource) {
35    if let Some(path) = path {
36        Log::verify(material.data_ref().save(path));
37    } else {
38        Log::warn("The edited material cannot be saved, because it does not have a path!")
39    }
40}
41
42#[derive(Debug)]
43pub struct SetMaterialBindingCommand {
44    material: MaterialResource,
45    name: ImmutableString,
46    binding: Option<MaterialResourceBinding>,
47    path: Option<PathBuf>,
48}
49
50impl SetMaterialBindingCommand {
51    pub fn new(
52        material: MaterialResource,
53        name: ImmutableString,
54        binding: MaterialResourceBinding,
55        path: Option<PathBuf>,
56    ) -> Self {
57        Self {
58            material,
59            name,
60            binding: Some(binding),
61            path,
62        }
63    }
64
65    fn swap(&mut self) {
66        let mut material = self.material.data_ref();
67
68        let old_value = material.binding_ref(self.name.clone()).cloned();
69        let new_value = std::mem::replace(&mut self.binding, old_value);
70        if let Some(new_value) = new_value {
71            material.bind(self.name.clone(), new_value);
72        } else {
73            material.unbind(self.name.clone());
74        }
75
76        drop(material);
77        try_save(self.path.as_deref(), &self.material);
78    }
79}
80
81impl CommandTrait for SetMaterialBindingCommand {
82    fn name(&mut self, _: &dyn CommandContext) -> String {
83        format!("Set Material {} Property Value", self.name)
84    }
85
86    fn execute(&mut self, _: &mut dyn CommandContext) {
87        self.swap();
88    }
89
90    fn revert(&mut self, _: &mut dyn CommandContext) {
91        self.swap();
92    }
93}
94
95#[derive(Debug)]
96pub struct SetMaterialPropertyGroupPropertyValueCommand {
97    material: MaterialResource,
98    group_name: ImmutableString,
99    property_name: ImmutableString,
100    value: Option<MaterialProperty>,
101    path: Option<PathBuf>,
102}
103
104impl SetMaterialPropertyGroupPropertyValueCommand {
105    pub fn new(
106        material: MaterialResource,
107        group_name: ImmutableString,
108        property_name: ImmutableString,
109        value: MaterialProperty,
110        path: Option<PathBuf>,
111    ) -> Self {
112        Self {
113            material,
114            group_name,
115            property_name,
116            value: Some(value),
117            path,
118        }
119    }
120
121    fn swap(&mut self) {
122        let mut material = self.material.data_ref();
123
124        let group = material.try_get_or_insert_property_group(self.group_name.clone());
125        let old_value = group.property_ref(self.property_name.clone()).cloned();
126        let new_value = std::mem::replace(&mut self.value, old_value);
127        if let Some(new_value) = new_value {
128            group.set_property(self.property_name.clone(), new_value);
129        } else {
130            group.unset_property(self.property_name.clone());
131        }
132
133        drop(material);
134        try_save(self.path.as_deref(), &self.material);
135    }
136}
137
138impl CommandTrait for SetMaterialPropertyGroupPropertyValueCommand {
139    fn name(&mut self, _: &dyn CommandContext) -> String {
140        format!("Set Material {} Property Value", self.property_name)
141    }
142
143    fn execute(&mut self, _: &mut dyn CommandContext) {
144        self.swap();
145    }
146
147    fn revert(&mut self, _: &mut dyn CommandContext) {
148        self.swap();
149    }
150}
151
152#[derive(Debug)]
153enum SetMaterialShaderCommandState {
154    Undefined,
155    NonExecuted { new_shader: ShaderResource },
156    Executed { old_material: Material },
157    Reverted { new_material: Material },
158}
159
160#[derive(Debug)]
161pub struct SetMaterialShaderCommand {
162    material: MaterialResource,
163    path: Option<PathBuf>,
164    state: SetMaterialShaderCommandState,
165}
166
167impl SetMaterialShaderCommand {
168    pub fn new(material: MaterialResource, shader: ShaderResource, path: Option<PathBuf>) -> Self {
169        Self {
170            material,
171            state: SetMaterialShaderCommandState::NonExecuted { new_shader: shader },
172            path,
173        }
174    }
175
176    fn swap(&mut self, _context: &mut dyn CommandContext) {
177        match std::mem::replace(&mut self.state, SetMaterialShaderCommandState::Undefined) {
178            SetMaterialShaderCommandState::Undefined => {
179                unreachable!()
180            }
181            SetMaterialShaderCommandState::NonExecuted { new_shader } => {
182                let mut material = self.material.data_ref();
183
184                let old_material =
185                    std::mem::replace(&mut *material, Material::from_shader(new_shader));
186
187                self.state = SetMaterialShaderCommandState::Executed { old_material };
188            }
189            SetMaterialShaderCommandState::Executed { old_material } => {
190                let mut material = self.material.data_ref();
191
192                let new_material = std::mem::replace(&mut *material, old_material);
193
194                self.state = SetMaterialShaderCommandState::Reverted { new_material };
195            }
196            SetMaterialShaderCommandState::Reverted { new_material } => {
197                let mut material = self.material.data_ref();
198
199                let old_material = std::mem::replace(&mut *material, new_material);
200
201                self.state = SetMaterialShaderCommandState::Executed { old_material };
202            }
203        }
204
205        try_save(self.path.as_deref(), &self.material);
206    }
207}
208
209impl CommandTrait for SetMaterialShaderCommand {
210    fn name(&mut self, _: &dyn CommandContext) -> String {
211        "Set Material Shader".to_owned()
212    }
213
214    fn execute(&mut self, ctx: &mut dyn CommandContext) {
215        self.swap(ctx);
216    }
217
218    fn revert(&mut self, ctx: &mut dyn CommandContext) {
219        self.swap(ctx);
220    }
221}