15

What do I have to do to to create a mesh for bevy with the following vertices:

let mut vertices : Vec<[f32; 3]> = Vec::new();

    vertices.push([0.0, 0.0, 0.0]);
    vertices.push([1.0, 2.0, 1.0]);
    vertices.push([2.0, 0.0, 0.0]);

I then want to spawn a MeshBundle like so

commands
    .spawn(MeshBundle {
        mesh: mesh,
        transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
        ..Default::default()
    });
Bruno Wallner
  • 383
  • 5
  • 13
  • 1
    Why is this Question getting donvoted? – Bruno Wallner Mar 17 '21 at 18:43
  • Probably because there is no question in your post, and there’s no indication of what your problem is, or why the examples of code you have aren’t sufficient. – mcarton Mar 20 '21 at 16:00
  • 15
    As a beginner wanting to develop procedural geometry, I believe I understand the gist of what this question is asking. I think, if the question lacks clarity, we should suggest edits rather than down-vote. – Jack Apr 08 '21 at 17:55
  • Also found it very insightful to see how the existing Bevy shapes have been constructed, for example the Box shape under: https://github.com/bevyengine/bevy/blob/main/crates/bevy_render/src/mesh/shape/mod.rs And as a bonus, if you create your own shapes in a similar fashion, they'll be very reusable. – b00t Apr 24 '23 at 07:46

2 Answers2

16

This answer has been updated for the latest bevy = "0.11" and uses the default shaders.

The code below demonstrates how to:

  1. Define vertex positions for a bevy::render::pipeline::PrimitiveTopology::TriangleList
  2. Assign vertex normals and uv coordinates to the vertices
  3. Create a triangle using the 3 vertices we defined

It is based on the built in shapes in bevy, which can be found here.

use bevy::prelude::*;
use bevy::render::mesh::{self, PrimitiveTopology};

fn main() {
    App::new()
        .insert_resource(Msaa::Sample4)
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);

    // Positions of the vertices
    // See https://bevy-cheatbook.github.io/features/coords.html
    mesh.insert_attribute(
        Mesh::ATTRIBUTE_POSITION,
        vec![[0., 0., 0.], [1., 2., 1.], [2., 0., 0.]],
    );

    // In this example, normals and UVs don't matter,
    // so we just use the same value for all of them
    mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0., 1., 0.]; 3]);
    mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0., 0.]; 3]);

    // A triangle using vertices 0, 2, and 1.
    // Note: order matters. [0, 1, 2] will be flipped upside down, and you won't see it from behind!
    mesh.set_indices(Some(mesh::Indices::U32(vec![0, 2, 1])));

    commands.spawn(PbrBundle {
        mesh: meshes.add(mesh),
        material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
        ..default()
    });

    commands.spawn(PointLightBundle {
        point_light: PointLight {
            intensity: 1500.0,
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_xyz(4.0, 8.0, 4.0),
        ..default()
    });

    commands.spawn(Camera3dBundle {
        transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}

You will have to define your own positions, uvs and normals according to your use case. Some shaders won't need all of these mesh attributes.

frankenapps
  • 5,800
  • 6
  • 28
  • 69
  • Optionally, for more complicated geometry, instead of setting normals manually with `mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL,...)`, you can do: `mesh.duplicate_vertices(); mesh.compute_flat_normals();` after `mesh.set_indices(...)`. Note the warnings about increasing the vertex count. – dim_voly Jan 17 '23 at 22:45
  • 1
    @dim_voly looking great, juste curious why you need to `duplicate_vertices()` ? – Philippe Paré Mar 30 '23 at 13:46
  • 1
    @PhilippeParé It's because by default the normals are interpolated. You do have the option in WGSL to disable this using `flat` [interpolation](https://www.w3.org/TR/WGSL/#interpolation), but then all meshes rendered using that shader would loose the interpolated normals as discussed [here](https://stackoverflow.com/questions/60022613/how-to-implement-flat-shading-in-opengl-without-duplicate-vertices). So if you want to mix flat and smooth shaded meshes using the same shader, duplicating vertices is the only option you have. – frankenapps Mar 30 '23 at 17:51
4

@frankenapps answer updated for bevy = 0.7

use bevy::prelude::*;
use bevy::render::mesh::{self, PrimitiveTopology};

fn main() {
    App::new()
        .insert_resource(Msaa { samples: 4 })
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup)
        .run();
}

/// set up a simple 3D scene
fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    ) {
    let vertices = [
        ([0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0]),
        ([1.0, 2.0, 1.0], [0.0, 1.0, 0.0], [1.0, 1.0]),
        ([2.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0]),
    ];

    let indices = mesh::Indices::U32(vec![0, 2, 1, 0, 3, 2]);

    let mut positions = Vec::new();
    let mut normals = Vec::new();
    let mut uvs = Vec::new();
    for (position, normal, uv) in vertices.iter() {
        positions.push(*position);
        normals.push(*normal);
        uvs.push(*uv);
    }

    let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
    mesh.set_indices(Some(indices));
    mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
    mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
    mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);

    // add entities to the world
    // plane
    commands.spawn_bundle(PbrBundle {
        mesh: meshes.add(mesh),
        material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
        ..default()
    });
    // light
    commands.spawn_bundle(PointLightBundle {
        point_light: PointLight {
            intensity: 1500.0,
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_xyz(4.0, 8.0, 4.0),
        ..default()
    });
    // camera
    commands.spawn_bundle(PerspectiveCameraBundle {
        transform: Transform::from_xyz(-2.0, 2.5, 5.0)
            .looking_at(Vec3::ZERO, Vec3::Y),
            ..default()
    });
}
Frieder Hannenheim
  • 1,144
  • 1
  • 7
  • 11
  • 1
    thanks for the contribution! fyi usually with api updates we'd submit an edit instead of creating a new answer.. I still gave you a vote though :) – chantey Oct 22 '22 at 11:33