1use crate::compound::CompoundData;
16use crate::core::NULL_INDEX;
17use crate::geometry::{Capsule, ShapeType, Sphere, SurfaceMaterial};
18use crate::height_field::HeightFieldData;
19use crate::hull::HullData;
20use crate::math_functions::{Aabb, Vec3, BOUNDS3_EMPTY, VEC3_ONE, VEC3_ZERO};
21use crate::mesh::MeshData;
22use crate::types::{Filter, QueryFilter};
23use std::rc::Rc;
24
25pub mod shape_flags {
27 pub const ENABLE_SENSOR_EVENTS: u8 = 0x01;
28 pub const ENABLE_CONTACT_EVENTS: u8 = 0x02;
29 pub const ENABLE_CUSTOM_FILTERING: u8 = 0x04;
30 pub const ENABLE_HIT_EVENTS: u8 = 0x08;
31 pub const ENABLE_PRE_SOLVE_EVENTS: u8 = 0x10;
32 pub const ENLARGED_AABB: u8 = 0x20;
33}
34
35#[derive(Debug, Clone)]
37pub enum ShapeGeometry {
38 Capsule(Capsule),
39 Sphere(Sphere),
40 Hull(Rc<HullData>),
42 Mesh {
44 data: MeshData,
45 scale: Vec3,
46 },
47 HeightField(HeightFieldData),
48 Compound(CompoundData),
49}
50
51impl ShapeGeometry {
52 pub fn shape_type(&self) -> ShapeType {
54 match self {
55 ShapeGeometry::Capsule(_) => ShapeType::Capsule,
56 ShapeGeometry::Sphere(_) => ShapeType::Sphere,
57 ShapeGeometry::Hull(_) => ShapeType::Hull,
58 ShapeGeometry::Mesh { .. } => ShapeType::Mesh,
59 ShapeGeometry::HeightField(_) => ShapeType::Height,
60 ShapeGeometry::Compound(_) => ShapeType::Compound,
61 }
62 }
63}
64
65impl Default for ShapeGeometry {
66 fn default() -> Self {
67 ShapeGeometry::Capsule(Capsule::default())
68 }
69}
70
71#[derive(Debug, Clone)]
77pub struct Shape {
78 pub id: i32,
79 pub body_id: i32,
80 pub prev_shape_id: i32,
81 pub next_shape_id: i32,
82 pub sensor_index: i32,
83 pub proxy_key: i32,
84 pub density: f32,
85 pub explosion_scale: f32,
86 pub aabb_margin: f32,
87
88 pub aabb: Aabb,
89 pub fat_aabb: Aabb,
90 pub local_centroid: Vec3,
91
92 pub material: SurfaceMaterial,
94 pub materials: Vec<SurfaceMaterial>,
97
98 pub filter: Filter,
99 pub user_data: u64,
100 pub user_shape: u64,
102
103 pub name_id: u32,
104 pub generation: u16,
105
106 pub flags: u8,
108
109 pub geometry: ShapeGeometry,
111}
112
113impl Shape {
114 pub fn shape_type(&self) -> ShapeType {
116 self.geometry.shape_type()
117 }
118
119 pub fn material_count(&self) -> i32 {
121 if self.materials.is_empty() {
122 1
123 } else {
124 self.materials.len() as i32
125 }
126 }
127
128 pub fn shape_materials(&self) -> &[SurfaceMaterial] {
133 if self.materials.is_empty() {
134 core::slice::from_ref(&self.material)
135 } else {
136 &self.materials
137 }
138 }
139
140 pub fn get_material(&self, index: i32) -> &SurfaceMaterial {
142 let mats = self.shape_materials();
143 &mats[index as usize]
144 }
145
146 pub fn shape_materials_mut(&mut self) -> &mut [SurfaceMaterial] {
148 if self.materials.is_empty() {
149 core::slice::from_mut(&mut self.material)
150 } else {
151 &mut self.materials
152 }
153 }
154
155 pub fn get_material_mut(&mut self, index: i32) -> &mut SurfaceMaterial {
157 &mut self.shape_materials_mut()[index as usize]
158 }
159
160 pub fn get_shape_user_material_id(&self, child_index: i32, triangle_index: i32) -> u64 {
167 use crate::compound::{get_compound_child, ChildGeometry, MAX_COMPOUND_MESH_MATERIALS};
168 use crate::height_field::get_height_field_material;
169 use crate::math_functions::clamp_int;
170 use crate::mesh::get_mesh_material_indices;
171
172 let mut material_index = 0i32;
173 match &self.geometry {
174 ShapeGeometry::Mesh { data, .. } => {
175 let indices = get_mesh_material_indices(data);
176 if !indices.is_empty() {
177 material_index = indices[triangle_index as usize] as i32;
178 }
179 }
180 ShapeGeometry::HeightField(height_field) => {
181 material_index = get_height_field_material(height_field, triangle_index);
182 }
183 ShapeGeometry::Compound(compound) => {
184 let child = get_compound_child(compound, child_index);
185 if let ChildGeometry::Mesh(mesh) = child.geometry {
186 let indices = get_mesh_material_indices(mesh.data);
187 let mesh_material_index = if !indices.is_empty() {
188 indices[triangle_index as usize] as i32
189 } else {
190 0
191 };
192 let mesh_material_index = clamp_int(
193 mesh_material_index,
194 0,
195 MAX_COMPOUND_MESH_MATERIALS as i32 - 1,
196 );
197 material_index = child.material_indices[mesh_material_index as usize];
198 } else {
199 material_index = child.material_indices[0];
200 }
201 }
202 _ => {}
203 }
204
205 let material_index = clamp_int(material_index, 0, self.material_count() - 1);
206 self.shape_materials()[material_index as usize].user_material_id
207 }
208}
209
210impl Default for Shape {
211 fn default() -> Self {
212 Shape {
213 id: NULL_INDEX,
214 body_id: NULL_INDEX,
215 prev_shape_id: NULL_INDEX,
216 next_shape_id: NULL_INDEX,
217 sensor_index: NULL_INDEX,
218 proxy_key: NULL_INDEX,
219 density: 0.0,
220 explosion_scale: 1.0,
221 aabb_margin: 0.0,
222 aabb: BOUNDS3_EMPTY,
223 fat_aabb: BOUNDS3_EMPTY,
224 local_centroid: VEC3_ZERO,
225 material: SurfaceMaterial::default(),
226 materials: Vec::new(),
227 filter: Filter::default(),
228 user_data: 0,
229 user_shape: 0,
230 name_id: 0,
231 generation: 0,
232 flags: 0,
233 geometry: ShapeGeometry::default(),
234 }
235 }
236}
237
238pub fn should_shapes_collide(filter_a: Filter, filter_b: Filter) -> bool {
240 if filter_a.group_index == filter_b.group_index && filter_a.group_index != 0 {
241 return filter_a.group_index > 0;
242 }
243
244 (filter_a.mask_bits & filter_b.category_bits) != 0
245 && (filter_a.category_bits & filter_b.mask_bits) != 0
246}
247
248pub fn should_query_collide(shape_filter: &Filter, query_filter: &QueryFilter) -> bool {
250 (shape_filter.category_bits & query_filter.mask_bits) != 0
251 && (shape_filter.mask_bits & query_filter.category_bits) != 0
252}
253
254pub fn mesh_geometry(data: MeshData) -> ShapeGeometry {
256 ShapeGeometry::Mesh {
257 data,
258 scale: VEC3_ONE,
259 }
260}
261
262mod accessors;
263mod api;
264mod dispatch;
265mod geometry_set;
266pub(crate) mod lifecycle;
267mod mutators;
268mod query;
269mod toi;
270mod wind;
271
272pub use accessors::*;
273pub use api::*;
274pub use dispatch::*;
275pub use geometry_set::*;
276pub use mutators::*;
277pub use query::*;
278pub use toi::*;
279pub use wind::*;