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