{"record":{"id":"edd10533d6745b9a","repo":"bevyengine/bevy","slug":"mesh-attribute-position-vertex-attributes-shoul","errorCode":null,"errorMessage":"`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`","messagePattern":"`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bevy_mesh/src/mesh.rs","lineNumber":1683,"sourceCode":"    /// attributes.\n    ///\n    /// FIXME: This should handle more cases since this is called as a part of gltf\n    /// mesh loading where we can't really blame users for loading meshes that might\n    /// not conform to the limitations here!\n    pub fn try_compute_flat_normals(&mut self) -> Result<(), MeshAccessError> {\n        assert!(\n            self.try_indices_option()?.is_none(),\n            \"`compute_flat_normals` can't work on indexed geometry. Consider calling either `Mesh::compute_smooth_normals` or `Mesh::duplicate_vertices` followed by `Mesh::compute_flat_normals`.\"\n        );\n        assert!(\n            matches!(self.primitive_topology, PrimitiveTopology::TriangleList),\n            \"`compute_flat_normals` can only work on `TriangleList`s\"\n        );\n\n        let positions = self\n            .try_attribute(Mesh::ATTRIBUTE_POSITION)?\n            .as_float3()\n            .expect(\"`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`\");\n\n        let normals: Vec<_> = positions\n            .as_chunks()\n            .0\n            .iter()\n            .flat_map(|&[a, b, c]| [triangle_normal(a, b, c); 3])\n            .collect();\n\n        self.try_insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals)\n    }\n\n    /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of an indexed mesh, smoothing normals for shared\n    /// vertices.\n    ///\n    /// This method weights normals by the angles of the corners of connected triangles, thus\n    /// eliminating triangle area and count as factors in the final normal. This does make it\n    /// somewhat slower than [`Mesh::compute_area_weighted_normals`] which does not need to\n    /// greedily normalize each triangle's normal or calculate corner angles.","sourceCodeStart":1665,"sourceCodeEnd":1701,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_mesh/src/mesh.rs#L1665-L1701","documentation":"Raised by Mesh::compute_flat_normals (crates/bevy_mesh/src/mesh.rs:1683). The function reads Mesh::ATTRIBUTE_POSITION through VertexAttributeValues::as_float3(), which returns Some only for the Float32x3 variant; any other storage format triggers this expect panic. Flat normals are computed per triangle, so positions must be exact 3-component floats.","triggerScenarios":"Calling compute_flat_normals() on a non-indexed TriangleList mesh whose ATTRIBUTE_POSITION was inserted with a format other than Float32x3 (e.g. Vec<[f32; 2]> -> Float32x2, Float32x4, or a packed integer format). The indexed/topology asserts earlier in the function fire first, so reaching this panic also implies those checks passed.","commonSituations":"Hand-built meshes where positions come from 2D data (heightmaps, Vec2 outlines) or from a pipeline that stores positions as vec4 (w=1) for math convenience; converting a raw GPU buffer with an unexpected stride into a Bevy Mesh.","solutions":["Insert positions as Vec<[f32; 3]> / Float32x3 from the start","Rewrite the existing attribute in place: take the old values, convert each to [f32; 3], and re-insert with mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, converted)","Build geometry through Bevy's MeshBuilder/primitive helpers, which always emit Float32x3 positions"],"exampleFix":"// before: positions stored as 2-component floats\nlet mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());\nmesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.0f32, 0.0]; 30]);\nmesh.compute_flat_normals(); // panic: expects float3\n\n// after: store positions as Float32x3\nlet mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());\nmesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.0f32, 0.0, 0.0]; 30]);\nmesh.compute_flat_normals();","handlingStrategy":"validation","validationCode":"fn positions_are_float3(mesh: &Mesh) -> bool {\n    mesh.attribute(Mesh::ATTRIBUTE_POSITION)\n        .is_some_and(|v| v.as_float3().is_some())\n}\n\nif positions_are_float3(&mesh) {\n    mesh.compute_flat_normals()?;\n}","typeGuard":"fn float3_position_mesh(mesh: &Mesh) -> Option<&Vec<[f32; 3]>> {\n    mesh.attribute(Mesh::ATTRIBUTE_POSITION)?.as_float3()\n}","tryCatchPattern":null,"preventionTips":["Always insert ATTRIBUTE_POSITION from Vec<[f32; 3]> data","Validate attribute formats once at asset ingestion, not at every compute_* call","Prefer Bevy primitive/MeshBuilder helpers that emit correct formats by construction"],"tags":["bevy","mesh","normals","vertex-attribute","format-mismatch","panic","float3"],"backgroundTag":"vertex-attribute-format-mismatch","analyzedSha":"396ca727080776bd313bb892423b7d94e03b81b4","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}