Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/asset/processing/asset_processing.rs
6595 views
1
//! This example illustrates how to define custom `AssetLoader`s, `AssetTransformer`s, and `AssetSaver`s, how to configure them, and how to register asset processors.
2
3
use bevy::{
4
asset::{
5
embedded_asset,
6
io::{Reader, Writer},
7
processor::LoadTransformAndSave,
8
saver::{AssetSaver, SavedAsset},
9
transformer::{AssetTransformer, TransformedAsset},
10
AssetLoader, AsyncWriteExt, LoadContext,
11
},
12
prelude::*,
13
reflect::TypePath,
14
};
15
use serde::{Deserialize, Serialize};
16
use std::convert::Infallible;
17
use thiserror::Error;
18
19
fn main() {
20
App::new()
21
// Using the "processed" mode will configure the AssetPlugin to use asset processing.
22
// If you also enable the `asset_processor` cargo feature, this will run the AssetProcessor
23
// in the background, run them through configured asset processors, and write the results to
24
// the `imported_assets` folder. If you also enable the `file_watcher` cargo feature, changes to the
25
// source assets will be detected and they will be reprocessed.
26
//
27
// The AssetProcessor will create `.meta` files automatically for assets in the `assets` folder,
28
// which can then be used to configure how the asset will be processed.
29
.add_plugins((
30
DefaultPlugins.set(AssetPlugin {
31
mode: AssetMode::Processed,
32
// This is just overriding the default paths to scope this to the correct example folder
33
// You can generally skip this in your own projects
34
file_path: "examples/asset/processing/assets".to_string(),
35
processed_file_path: "examples/asset/processing/imported_assets/Default"
36
.to_string(),
37
..default()
38
}),
39
TextPlugin,
40
))
41
.add_systems(Startup, setup)
42
.add_systems(Update, print_text)
43
.run();
44
}
45
46
/// This [`TextPlugin`] defines two assets types:
47
/// * [`CoolText`]: a custom RON text format that supports dependencies and embedded dependencies
48
/// * [`Text`]: a "normal" plain text file
49
///
50
/// It also defines an asset processor that will load [`CoolText`], resolve embedded dependencies, and write the resulting
51
/// output to a "normal" plain text file. When the processed asset is loaded, it is loaded as a Text (plaintext) asset.
52
/// This illustrates that when you process an asset, you can change its type! However you don't _need_ to change the type.
53
struct TextPlugin;
54
55
impl Plugin for TextPlugin {
56
fn build(&self, app: &mut App) {
57
embedded_asset!(app, "examples/asset/processing/", "e.txt");
58
app.init_asset::<CoolText>()
59
.init_asset::<Text>()
60
.register_asset_loader(CoolTextLoader)
61
.register_asset_loader(TextLoader)
62
.register_asset_processor::<LoadTransformAndSave<CoolTextLoader, CoolTextTransformer, CoolTextSaver>>(
63
LoadTransformAndSave::new(CoolTextTransformer, CoolTextSaver),
64
)
65
.set_default_asset_processor::<LoadTransformAndSave<CoolTextLoader, CoolTextTransformer, CoolTextSaver>>("cool.ron");
66
}
67
}
68
69
#[derive(Asset, TypePath, Debug)]
70
struct Text(String);
71
72
#[derive(Default)]
73
struct TextLoader;
74
75
#[derive(Clone, Default, Serialize, Deserialize)]
76
struct TextSettings {
77
text_override: Option<String>,
78
}
79
80
impl AssetLoader for TextLoader {
81
type Asset = Text;
82
type Settings = TextSettings;
83
type Error = std::io::Error;
84
async fn load(
85
&self,
86
reader: &mut dyn Reader,
87
settings: &TextSettings,
88
_load_context: &mut LoadContext<'_>,
89
) -> Result<Text, Self::Error> {
90
let mut bytes = Vec::new();
91
reader.read_to_end(&mut bytes).await?;
92
let value = if let Some(ref text) = settings.text_override {
93
text.clone()
94
} else {
95
String::from_utf8(bytes).unwrap()
96
};
97
Ok(Text(value))
98
}
99
100
fn extensions(&self) -> &[&str] {
101
&["txt"]
102
}
103
}
104
105
#[derive(Serialize, Deserialize)]
106
struct CoolTextRon {
107
text: String,
108
dependencies: Vec<String>,
109
embedded_dependencies: Vec<String>,
110
dependencies_with_settings: Vec<(String, TextSettings)>,
111
}
112
113
#[derive(Asset, TypePath, Debug)]
114
struct CoolText {
115
text: String,
116
#[expect(
117
dead_code,
118
reason = "Used to show that our assets can hold handles to other assets"
119
)]
120
dependencies: Vec<Handle<Text>>,
121
}
122
123
#[derive(Default)]
124
struct CoolTextLoader;
125
126
#[derive(Debug, Error)]
127
enum CoolTextLoaderError {
128
#[error(transparent)]
129
Io(#[from] std::io::Error),
130
#[error(transparent)]
131
RonSpannedError(#[from] ron::error::SpannedError),
132
#[error(transparent)]
133
LoadDirectError(#[from] bevy::asset::LoadDirectError),
134
}
135
136
impl AssetLoader for CoolTextLoader {
137
type Asset = CoolText;
138
type Settings = ();
139
type Error = CoolTextLoaderError;
140
141
async fn load(
142
&self,
143
reader: &mut dyn Reader,
144
_settings: &Self::Settings,
145
load_context: &mut LoadContext<'_>,
146
) -> Result<CoolText, Self::Error> {
147
let mut bytes = Vec::new();
148
reader.read_to_end(&mut bytes).await?;
149
let ron: CoolTextRon = ron::de::from_bytes(&bytes)?;
150
let mut base_text = ron.text;
151
for embedded in ron.embedded_dependencies {
152
let loaded = load_context
153
.loader()
154
.immediate()
155
.load::<Text>(&embedded)
156
.await?;
157
base_text.push_str(&loaded.get().0);
158
}
159
for (path, settings_override) in ron.dependencies_with_settings {
160
let loaded = load_context
161
.loader()
162
.with_settings(move |settings| {
163
*settings = settings_override.clone();
164
})
165
.immediate()
166
.load::<Text>(&path)
167
.await?;
168
base_text.push_str(&loaded.get().0);
169
}
170
Ok(CoolText {
171
text: base_text,
172
dependencies: ron
173
.dependencies
174
.iter()
175
.map(|p| load_context.load(p))
176
.collect(),
177
})
178
}
179
180
fn extensions(&self) -> &[&str] {
181
&["cool.ron"]
182
}
183
}
184
185
#[derive(Default)]
186
struct CoolTextTransformer;
187
188
#[derive(Default, Serialize, Deserialize)]
189
struct CoolTextTransformerSettings {
190
appended: String,
191
}
192
193
impl AssetTransformer for CoolTextTransformer {
194
type AssetInput = CoolText;
195
type AssetOutput = CoolText;
196
type Settings = CoolTextTransformerSettings;
197
type Error = Infallible;
198
199
async fn transform<'a>(
200
&'a self,
201
mut asset: TransformedAsset<Self::AssetInput>,
202
settings: &'a Self::Settings,
203
) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> {
204
asset.text = format!("{}{}", asset.text, settings.appended);
205
Ok(asset)
206
}
207
}
208
209
struct CoolTextSaver;
210
211
impl AssetSaver for CoolTextSaver {
212
type Asset = CoolText;
213
type Settings = ();
214
type OutputLoader = TextLoader;
215
type Error = std::io::Error;
216
217
async fn save(
218
&self,
219
writer: &mut Writer,
220
asset: SavedAsset<'_, Self::Asset>,
221
_settings: &Self::Settings,
222
) -> Result<TextSettings, Self::Error> {
223
writer.write_all(asset.text.as_bytes()).await?;
224
Ok(TextSettings::default())
225
}
226
}
227
228
#[derive(Resource)]
229
struct TextAssets {
230
a: Handle<Text>,
231
b: Handle<Text>,
232
c: Handle<Text>,
233
d: Handle<Text>,
234
e: Handle<Text>,
235
}
236
237
fn setup(mut commands: Commands, assets: Res<AssetServer>) {
238
// This the final processed versions of `assets/a.cool.ron` and `assets/foo.c.cool.ron`
239
// Check out their counterparts in `imported_assets` to see what the outputs look like.
240
commands.insert_resource(TextAssets {
241
a: assets.load("a.cool.ron"),
242
b: assets.load("foo/b.cool.ron"),
243
c: assets.load("foo/c.cool.ron"),
244
d: assets.load("d.cool.ron"),
245
e: assets.load("embedded://asset_processing/e.txt"),
246
});
247
}
248
249
fn print_text(
250
handles: Res<TextAssets>,
251
texts: Res<Assets<Text>>,
252
mut asset_events: EventReader<AssetEvent<Text>>,
253
) {
254
if !asset_events.is_empty() {
255
// This prints the current values of the assets
256
// Hot-reloading is supported, so try modifying the source assets (and their meta files)!
257
println!("Current Values:");
258
println!(" a: {:?}", texts.get(&handles.a));
259
println!(" b: {:?}", texts.get(&handles.b));
260
println!(" c: {:?}", texts.get(&handles.c));
261
println!(" d: {:?}", texts.get(&handles.d));
262
println!(" e: {:?}", texts.get(&handles.e));
263
println!("(You can modify source assets and their .meta files to hot-reload changes!)");
264
println!();
265
asset_events.clear();
266
}
267
}
268
269