Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/large_scenes/bevy_city/src/assets.rs
30636 views
1
use bevy::{dev_tools::world_asset_helpers::merge_all_mesh_3d, prelude::*};
2
use rand::RngExt;
3
4
const BASE_URL: &str = "https://github.com/bevyengine/bevy_asset_files/raw/main/kenney";
5
6
pub fn strip_base_url(path: String) -> String {
7
path.strip_prefix(BASE_URL)
8
.map(|s| s.trim_start_matches('/').to_string())
9
.unwrap_or(path)
10
}
11
12
#[derive(Resource)]
13
pub struct CityAssets {
14
pub untyped_assets: Vec<UntypedHandle>,
15
pub cars: Vec<Handle<WorldAsset>>,
16
pub car_meshes: Vec<Handle<Mesh>>,
17
pub car_material: Handle<StandardMaterial>,
18
pub crossroad: Handle<WorldAsset>,
19
pub road_straight: Handle<WorldAsset>,
20
pub high_density: Buildings,
21
pub medium_density: Buildings,
22
pub low_density: Buildings,
23
pub ground_tile: (
24
Handle<Mesh>,
25
Handle<StandardMaterial>,
26
Handle<StandardMaterial>,
27
),
28
pub tree_small: Handle<WorldAsset>,
29
pub tree_large: Handle<WorldAsset>,
30
pub path_stones_long: Handle<WorldAsset>,
31
pub fence: Handle<WorldAsset>,
32
}
33
34
impl CityAssets {
35
pub fn get_random_car<R: RngExt>(
36
&self,
37
rng: &mut R,
38
) -> (Mesh3d, MeshMaterial3d<StandardMaterial>) {
39
let mesh = self.car_meshes[rng.random_range(0..self.car_meshes.len())].clone();
40
(Mesh3d(mesh), MeshMaterial3d(self.car_material.clone()))
41
}
42
}
43
44
pub struct Buildings {
45
meshes: Vec<Handle<Mesh>>,
46
materials: Vec<Handle<StandardMaterial>>,
47
}
48
49
impl Buildings {
50
pub fn get_random_building<R: RngExt>(
51
&self,
52
rng: &mut R,
53
) -> (Mesh3d, MeshMaterial3d<StandardMaterial>) {
54
let mesh = self.meshes[rng.random_range(0..self.meshes.len())].clone();
55
let material = self.materials[rng.random_range(0..self.materials.len())].clone();
56
(Mesh3d(mesh), MeshMaterial3d(material))
57
}
58
}
59
60
pub fn load_assets(
61
mut commands: Commands,
62
asset_server: Res<AssetServer>,
63
mut materials: ResMut<Assets<StandardMaterial>>,
64
) {
65
let base_url = BASE_URL;
66
67
let mut untyped_assets = vec![];
68
/// Wraps asset_server.load_asset to automatically track all the assets that are being loaded
69
macro_rules! load_asset {
70
($path:expr) => {{
71
let handle = asset_server.load($path);
72
untyped_assets.push(handle.clone().untyped());
73
handle
74
}};
75
}
76
77
let car_texture: Handle<Image> =
78
load_asset!(format!("{base_url}/car-kit/Textures/colormap.png"));
79
let car_material = materials.add(StandardMaterial {
80
base_color_texture: Some(car_texture),
81
..Default::default()
82
});
83
84
let cars = {
85
// TODO generate color variations
86
[
87
"hatchback-sports",
88
"suv",
89
"suv-luxury",
90
"sedan",
91
"sedan-sports",
92
"truck",
93
"truck-flat",
94
"van",
95
"delivery",
96
"delivery-flat",
97
"taxi",
98
"garbage-truck",
99
"ambulance",
100
"police",
101
"firetruck",
102
]
103
.iter()
104
.map(|t| {
105
load_asset!(GltfAssetLabel::Scene(0).from_asset(format!("{base_url}/car-kit/{t}.glb")))
106
})
107
.collect::<Vec<_>>()
108
};
109
110
let crossroad = load_asset!(GltfAssetLabel::Scene(0)
111
.from_asset(format!("{base_url}/city-kit-roads/road-crossroad-path.glb")));
112
let road_straight =
113
load_asset!(GltfAssetLabel::Scene(0)
114
.from_asset(format!("{base_url}/city-kit-roads/road-straight.glb")));
115
116
let high_density = {
117
let materials = ["colormap", "variation-a", "variation-b"]
118
.iter()
119
.map(|variation| {
120
materials.add(StandardMaterial {
121
base_color_texture: Some(load_asset!(format!(
122
"{base_url}/city-kit-commercial/Textures/{variation}.png"
123
))),
124
..Default::default()
125
})
126
})
127
.collect::<Vec<_>>();
128
129
let mut meshes = ["a", "b", "c", "d", "e"]
130
.iter()
131
.map(|t| {
132
load_asset!(GltfAssetLabel::Primitive {
133
mesh: 0,
134
primitive: 0,
135
}
136
.from_asset(format!(
137
"{base_url}/city-kit-commercial/building-skyscraper-{t}.glb"
138
)))
139
})
140
.collect::<Vec<_>>();
141
meshes.extend(["m", "l"].iter().map(|t| {
142
load_asset!(GltfAssetLabel::Primitive {
143
mesh: 0,
144
primitive: 0,
145
}
146
.from_asset(format!("{base_url}/city-kit-commercial/building-{t}.glb")))
147
}));
148
149
Buildings { meshes, materials }
150
};
151
152
let medium_density = {
153
let materials = ["colormap", "variation-a", "variation-b"]
154
.iter()
155
.map(|variation| {
156
materials.add(StandardMaterial {
157
base_color_texture: Some(load_asset!(format!(
158
"{base_url}/city-kit-commercial/Textures/{variation}.png"
159
))),
160
..Default::default()
161
})
162
})
163
.collect::<Vec<_>>();
164
let meshes = ["a", "b", "c", "d", "f", "g", "h"]
165
.iter()
166
.map(|t| {
167
load_asset!(GltfAssetLabel::Primitive {
168
mesh: 0,
169
primitive: 0,
170
}
171
.from_asset(format!("{base_url}/city-kit-commercial/building-{t}.glb")))
172
})
173
.collect::<Vec<_>>();
174
175
Buildings { meshes, materials }
176
};
177
let low_density = {
178
let materials = ["colormap", "variation-a", "variation-b", "variation-c"]
179
.iter()
180
.map(|variation| {
181
materials.add(StandardMaterial {
182
base_color_texture: Some(load_asset!(format!(
183
"{base_url}/city-kit-suburban/Textures/{variation}.png"
184
))),
185
..Default::default()
186
})
187
})
188
.collect::<Vec<_>>();
189
let meshes = ["b", "c", "d", "e", "f", "g", "h", "i", "k", "l", "o", "u"]
190
.iter()
191
.map(|t| {
192
load_asset!(GltfAssetLabel::Primitive {
193
mesh: 0,
194
primitive: 0,
195
}
196
.from_asset(format!(
197
"{base_url}/city-kit-suburban/building-type-{t}.glb"
198
)))
199
})
200
.collect::<Vec<_>>();
201
202
Buildings { meshes, materials }
203
};
204
205
let ground_tile = {
206
let mesh = load_asset!(GltfAssetLabel::Primitive {
207
mesh: 0,
208
primitive: 0,
209
}
210
.from_asset(format!("{base_url}/city-kit-roads/tile-low.glb")));
211
let default_material: Handle<StandardMaterial> = load_asset!(format!(
212
"{base_url}/city-kit-roads/tile-low.glb#{}/std",
213
GltfAssetLabel::DefaultMaterial
214
));
215
let grass_material =
216
materials.add(StandardMaterial::from_color(Color::srgb_u8(97, 203, 139)));
217
218
(mesh, default_material, grass_material)
219
};
220
221
let tree_small: Handle<WorldAsset> =
222
load_asset!(GltfAssetLabel::Scene(0)
223
.from_asset(format!("{base_url}/city-kit-suburban/tree-small.glb")));
224
let tree_large: Handle<WorldAsset> =
225
load_asset!(GltfAssetLabel::Scene(0)
226
.from_asset(format!("{base_url}/city-kit-suburban/tree-large.glb")));
227
228
let path_stones_long: Handle<WorldAsset> = load_asset!(GltfAssetLabel::Scene(0)
229
.from_asset(format!("{base_url}/city-kit-suburban/path-stones-long.glb")));
230
231
let fence: Handle<WorldAsset> = load_asset!(
232
GltfAssetLabel::Scene(0).from_asset(format!("{base_url}/city-kit-suburban/fence.glb"))
233
);
234
235
commands.insert_resource(CityAssets {
236
untyped_assets,
237
cars,
238
car_meshes: vec![],
239
car_material,
240
crossroad,
241
road_straight,
242
high_density,
243
medium_density,
244
low_density,
245
ground_tile,
246
tree_small,
247
tree_large,
248
path_stones_long,
249
fence,
250
});
251
}
252
253
/// Merge the meshes of all the cars gltf into a single mesh per car.
254
///
255
/// The asset pack we are using uses a separate mesh for each tire of the car and some also have
256
/// doors as separate meshes. This is useful if you want to animate these element individually but
257
/// in this scene we don't need to do that. Having multiple meshes for a single car means we need
258
/// to run transform propagation on all these meshes and it will also generate even more indirect
259
/// commands for each of those meshes.
260
pub fn merge_car_meshes(
261
city_assets: &mut CityAssets,
262
world_assets: &mut Assets<WorldAsset>,
263
meshes: &mut Assets<Mesh>,
264
) {
265
for car_scene in &city_assets.cars {
266
let Some(merged) = merge_all_mesh_3d(world_assets, meshes, car_scene) else {
267
continue;
268
};
269
city_assets.car_meshes.push(meshes.add(merged));
270
}
271
}
272
273