Repository: bevyengine/bevy
File: examples/asset/generated_assets.rs (Rust)

Code before:

    commands.spawn((
        DirectionalLight::default(),
        Transform::default().looking_to(Dir3::new(Vec3::new(-1.0, -1.0, -1.0)).unwrap(), Dir3::Y),
    ));

    commands.spawn((
        // `add_async` creates a task that runs your async function. Once it completes, the asset is
        // added to the `Assets`. This is "deferred" meaning that the asset may take a frame to be
        // added after the task completes.
        Mesh3d(asset_server.add_async(generate_mesh(UVec2::new(100, 100), 1234))),
        // Another way to generate an asset is to add it directly to the `Assets`.
        MeshMaterial3d(materials.add(StandardMaterial::default())),
    ));

    // The last way to generate assets is to reserve a handle, and then use `Assets::insert` to
    // populate the asset later. In this example, the `generate_image` system runs to populate the
    // image
    let image_handle = images.reserve_handle();
    commands.insert_resource(HandleToGenerate(image_handle.clone()));
    commands.spawn(ImageNode::new(image_handle));
}

async fn generate_mesh(size: UVec2, seed: u64) -> Result<Mesh, std::io::Error> {
    // This mesh could take a while to generate!
    sleep(Duration::from_secs(3));

    let mut rng = StdRng::seed_from_u64(seed);
    let mut positions = vec![];
    for y in 0..size.y {
        for x in 0..size.x {
            positions.push(Vec3::new(
                x as f32 - size.x as f32 / 2.0,
                rng.random::<f32>() * 2.0,
                y as f32 - size.y as f32 / 2.0,
            ));
        }
    }

    let compute_index = |(x, y): (u32, u32)| x + y * size.x;
    let mut indices = vec![];
    for y in 0..(size.y - 1) {
        for x in 0..(size.x - 1) {
            indices.extend(
                [
                    (x, y),
                    (x, y + 1),
                    (x + 1, y),
                    (x + 1, y),
                    (x, y + 1),
                    (x + 1, y + 1),

Review comment (suggestion):
Personally I'd avoid the sleep - there's a non-zero chance that the user misunderstands and thinks this is required, or cut and pastes the example without removing it. I'm maybe too paranoid, but I've also seen users do exactly this!

Code after:

    // The last way to generate assets is to reserve a handle, and then use `Assets::insert` to
    // populate the asset later. In this example, the `generate_mesh_system` system runs to populate
    // the mesh.
    let mesh_handle = meshes.reserve_handle();
    commands.insert_resource(HandleToGenerate(mesh_handle.clone()));
    commands.spawn((
        Transform::from_xyz(2.0, 0.0, 0.0)
            .with_rotation(Quat::from_rotation_x(50.0f32.to_radians())),
        Mesh3d(mesh_handle),
        MeshMaterial3d(material_handle),
    ));
}

async fn generate_mesh_async() -> Result<Mesh, std::io::Error> {
    // This mesh could take a while to generate. It could even take several frames (though in this
    // example it should be ~instant).

    Ok(Mesh::from(Cone::new(1.0, 2.0)))
}

#[derive(Resource)]
struct HandleToGenerate(Handle<Mesh>);

/// This system runs once to populate the handle in [`HandleToGenerate`].
///
/// This generates a runtime mesh. Since it's a system, it can use other data in the world to
/// generate the asset!
fn generate_mesh_system(
    handle_to_generate: Res<HandleToGenerate>,
    mut meshes: ResMut<Assets<Mesh>>,
) {
    let mesh = Mesh::from(Torus::new(0.8, 1.2));
    meshes.insert(&handle_to_generate.0, mesh).unwrap();
}