Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manually created meshes are oriented wrong on pipelined-rendering #3030

Closed
sweepline opened this issue Oct 26, 2021 · 6 comments
Closed

Manually created meshes are oriented wrong on pipelined-rendering #3030

sweepline opened this issue Oct 26, 2021 · 6 comments
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior

Comments

@sweepline
Copy link

Bevy version

running on commit 9f47697

Operating system & version

Arch Linux

What you did

Generate a Mesh with a single triangle, using render2 and pbr2.

A snippet can be seen here.

    vertices.push([0.0, 0.0, 0.0]);
    vertices.push([50.0, 0.0, 0.0]);
    vertices.push([0.0, 100.0, 0.0]);
    indices.push(0);
    indices.push(1);
    indices.push(2);

Using a camera with the following transform to look at it:

Transform::from_xyz(0.0, 30.0, 100.0).looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y),

What you expected to happen

I expected the triangle to be perpendicular to the X,Z plane and having a face towards +Z. Like so:
triangle_main

An minimal example of this can be found here: https://github.com/sweepline/bevy-mesh-bug/tree/working-main

What actually happened

The triangle is positioned flatly on the X,Z plane facing towards +Y. Like so:
triangle_pipelined

An minimal example of this can be found here: https://github.com/sweepline/bevy-mesh-bug/tree/master

@sweepline sweepline added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Oct 26, 2021
@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen and removed S-Needs-Triage This issue needs to be labelled labels Oct 26, 2021
@alice-i-cecile alice-i-cecile added this to the Bevy 0.6 milestone Oct 26, 2021
@wilk10
Copy link
Contributor

wilk10 commented Oct 27, 2021

Thanks for reporting this!

From what i can see, in the second example the normals are not set. If they are set to the same values as the first example, the triangle renders in the same position.

So i don't think it's related to pipelined-rendering.

On 0.5 they cannot be omitted (it will panic), while on pipelined-rendering if not provided the mesh.attribute(Mesh::ATTRIBUTE_NORMAL) is None and then the vertex buffer data returned by mesh.get_vertex_buffer_data() is very much different.

So perhaps the issue is with the expected behaviour when the normals are not set by the user.

@bjorn3
Copy link
Contributor

bjorn3 commented Oct 27, 2021

Missing normals should only affect the shading and not the shape of objects, right? The screenshots clearly show that the actual shape of the objects is different. It is rotated.

@sweepline
Copy link
Author

sweepline commented Oct 28, 2021

Okay so I've figured it out i think. In bevy/pipelined/bevy_pbr2/src/render/mod.rs the VertexBufferLayout has a stride of 32, this is of course not true when I don't insert the normal as @wilk10 said. So yes its an issue of the engine not reporting that a required attribute is missing or crashing like on main.

@sweepline
Copy link
Author

sweepline commented Oct 28, 2021

This makes me wonder how we make a shader that functions on meshes with different sets of attributes, this happens in the shaders from_world which is used for initialization right? I could do this on the old renderer quite easily (check #3008).

@wilk10
Copy link
Contributor

wilk10 commented Oct 28, 2021

Okay so I've figured it out i think. In bevy/pipelined/bevy_pbr2/src/render/mod.rs the VertexBufferLayout has a stride of 32, this is of course not true when I don't insert the normal as @wilk10 said. So yes its an issue of the engine not reporting that a required attribute is missing or crashing like on main.

Yeh i definitely agree, it should keep on panicking if they're not set, or have correct defaults.

For reference on latest the panic is:

thread 'main' panicked at 'Attribute Vertex_Normal is required by shader, but not supplied by mesh. 
Either remove the attribute from the shader or supply the attribute (Vertex_Normal) to the mesh.', 
/Users/wilk/.cargo/git/checkouts/bevy-f7ffde730c324c74/db55bf5/crates/bevy_render/src/pipeline/pipeline_compiler.rs:231:17
stack backtrace:
   0: rust_begin_unwind
             at /rustc/fdf65053e99e8966f9bd83b5a8491326cb33d638/library/std/src/panicking.rs:517:5
   1: std::panicking::begin_panic_fmt
             at /rustc/fdf65053e99e8966f9bd83b5a8491326cb33d638/library/std/src/panicking.rs:460:5
   2: bevy_render::pipeline::pipeline_compiler::PipelineCompiler::compile_pipeline
             at /Users/wilk/.cargo/git/checkouts/bevy-f7ffde730c324c74/db55bf5/crates/bevy_render/src/pipeline/pipeline_compiler.rs:231:17
   3: bevy_render::draw::DrawContext::set_pipeline
             at /Users/wilk/.cargo/git/checkouts/bevy-f7ffde730c324c74/db55bf5/crates/bevy_render/src/draw.rs:200:13
   4: bevy_render::pipeline::render_pipelines::draw_render_pipelines_system
             at /Users/wilk/.cargo/git/checkouts/bevy-f7ffde730c324c74/db55bf5/crates/bevy_render/src/pipeline/render_pipelines.rs:147:13
   5: core::ops::function::Fn::call
             at /rustc/fdf65053e99e8966f9bd83b5a8491326cb33d638/library/core/src/ops/function.rs:70:5
   6: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &F>::call_mut
             at /rustc/fdf65053e99e8966f9bd83b5a8491326cb33d638/library/core/src/ops/function.rs:247:13
   7: <Func as bevy_ecs::system::into_system::SystemParamFunction<(),Out,(F0,F1,F2,F3,F4),()>>::run
             at /Users/wilk/.cargo/git/checkouts/bevy-f7ffde730c324c74/db55bf5/crates/bevy_ecs/src/system/into_system.rs:207:21

@cart cart removed this from the Bevy 0.6 milestone Jan 8, 2022
@robojeb
Copy link
Contributor

robojeb commented Jan 20, 2022

Just to add, it might be worth updating the documentation for Mesh to indicate which attributes need to be provided.
I just ran into this issue and spent a whole night wondering what was up when my math printed by debug was all correct.

It seems the minimum to be set is ATTRIBUTE_POSITION, ATTRIBUTE_NORMAL and ATTRIBUTE_UV_0 or else you encounter problems.

The docs on main only show setting ATTRIBUTE_POSITION in the example for creating a custom mesh.

@alice-i-cecile alice-i-cecile added this to the Bevy 0.6.1 milestone Jan 22, 2022
@alice-i-cecile alice-i-cecile removed this from the Bevy 0.6.1 milestone Feb 10, 2022
@bors bors bot closed this as completed in e369a8a Feb 23, 2022
kurtkuehnert pushed a commit to kurtkuehnert/bevy that referenced this issue Mar 6, 2022
This PR makes a number of changes to how meshes and vertex attributes are handled, which the goal of enabling easy and flexible custom vertex attributes:
* Reworks the `Mesh` type to use the newly added `VertexAttribute` internally
  * `VertexAttribute` defines the name, a unique `VertexAttributeId`, and a `VertexFormat`
  *  `VertexAttributeId` is used to produce consistent sort orders for vertex buffer generation, replacing the more expensive and often surprising "name based sorting"
  * Meshes can be used to generate a `MeshVertexBufferLayout`, which defines the layout of the gpu buffer produced by the mesh. `MeshVertexBufferLayouts` can then be used to generate actual `VertexBufferLayouts` according to the requirements of a specific pipeline. This decoupling of "mesh layout" vs "pipeline vertex buffer layout" is what enables custom attributes. We don't need to standardize _mesh layouts_ or contort meshes to meet the needs of a specific pipeline. As long as the mesh has what the pipeline needs, it will work transparently.
* Mesh-based pipelines now specialize on `&MeshVertexBufferLayout` via the new `SpecializedMeshPipeline` trait (which behaves like `SpecializedPipeline`, but adds `&MeshVertexBufferLayout`). The integrity of the pipeline cache is maintained because the `MeshVertexBufferLayout` is treated as part of the key (which is fully abstracted from implementers of the trait ... no need to add any additional info to the specialization key).
* Hashing `MeshVertexBufferLayout` is too expensive to do for every entity, every frame. To make this scalable, I added a generalized "pre-hashing" solution to `bevy_utils`: `Hashed<T>` keys and `PreHashMap<K, V>` (which uses `Hashed<T>` internally) . Why didn't I just do the quick and dirty in-place "pre-compute hash and use that u64 as a key in a hashmap" that we've done in the past? Because its wrong! Hashes by themselves aren't enough because two different values can produce the same hash. Re-hashing a hash is even worse! I decided to build a generalized solution because this pattern has come up in the past and we've chosen to do the wrong thing. Now we can do the right thing! This did unfortunately require pulling in `hashbrown` and using that in `bevy_utils`, because avoiding re-hashes requires the `raw_entry_mut` api, which isn't stabilized yet (and may never be ... `entry_ref` has favor now, but also isn't available yet). If std's HashMap ever provides the tools we need, we can move back to that. Note that adding `hashbrown` doesn't increase our dependency count because it was already in our tree. I will probably break these changes out into their own PR.
* Specializing on `MeshVertexBufferLayout` has one non-obvious behavior: it can produce identical pipelines for two different MeshVertexBufferLayouts. To optimize the number of active pipelines / reduce re-binds while drawing, I de-duplicate pipelines post-specialization using the final `VertexBufferLayout` as the key.  For example, consider a pipeline that needs the layout `(position, normal)` and is specialized using two meshes: `(position, normal, uv)` and `(position, normal, other_vec2)`. If both of these meshes result in `(position, normal)` specializations, we can use the same pipeline! Now we do. Cool!

To briefly illustrate, this is what the relevant section of `MeshPipeline`'s specialization code looks like now:

```rust
impl SpecializedMeshPipeline for MeshPipeline {
    type Key = MeshPipelineKey;

    fn specialize(
        &self,
        key: Self::Key,
        layout: &MeshVertexBufferLayout,
    ) -> RenderPipelineDescriptor {
        let mut vertex_attributes = vec![
            Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
            Mesh::ATTRIBUTE_NORMAL.at_shader_location(1),
            Mesh::ATTRIBUTE_UV_0.at_shader_location(2),
        ];

        let mut shader_defs = Vec::new();
        if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
            shader_defs.push(String::from("VERTEX_TANGENTS"));
            vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
        }

        let vertex_buffer_layout = layout
            .get_layout(&vertex_attributes)
            .expect("Mesh is missing a vertex attribute");
```

Notice that this is _much_ simpler than it was before. And now any mesh with any layout can be used with this pipeline, provided it has vertex postions, normals, and uvs. We even got to remove `HAS_TANGENTS` from MeshPipelineKey and `has_tangents` from `GpuMesh`, because that information is redundant with `MeshVertexBufferLayout`.

This is still a draft because I still need to:

* Add more docs
* Experiment with adding error handling to mesh pipeline specialization (which would print errors at runtime when a mesh is missing a vertex attribute required by a pipeline). If it doesn't tank perf, we'll keep it.
* Consider breaking out the PreHash / hashbrown changes into a separate PR.
* Add an example illustrating this change
* Verify that the "mesh-specialized pipeline de-duplication code" works properly

Please dont yell at me for not doing these things yet :) Just trying to get this in peoples' hands asap.

Alternative to bevyengine#3120
Fixes bevyengine#3030

Co-authored-by: Carter Anderson <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants