1use super::{create_shape_internal, destroy_shape_internal, ChainShape};
11use crate::body::get_body_full_id;
12use crate::collision::{ChainSegment, Segment, ShapeGeometry};
13use crate::core::NULL_INDEX;
14use crate::id::{BodyId, ChainId, ShapeId};
15use crate::math_functions::is_valid_float;
16use crate::types::{default_shape_def, ChainDef, SurfaceMaterial};
17use crate::world::World;
18
19pub fn chain_is_valid(world: &World, id: ChainId) -> bool {
22 let chain_id = id.index1 - 1;
23 if chain_id < 0 || world.chain_shapes.len() as i32 <= chain_id {
24 return false;
25 }
26
27 let chain = &world.chain_shapes[chain_id as usize];
28 if chain.id == NULL_INDEX {
29 return false;
31 }
32
33 debug_assert!(chain.id == chain_id);
34
35 id.generation == chain.generation
36}
37
38pub fn get_chain_index(world: &World, chain_id: ChainId) -> i32 {
41 let index = chain_id.index1 - 1;
42 debug_assert!((index as usize) < world.chain_shapes.len());
43 let chain = &world.chain_shapes[index as usize];
44 debug_assert!(chain.id == index && chain.generation == chain_id.generation);
45 index
46}
47
48pub fn create_chain(world: &mut World, body_id: BodyId, def: &ChainDef) -> ChainId {
51 debug_assert!(def.internal_value == crate::core::SECRET_COOKIE);
52 debug_assert!(def.points.len() >= 4);
53 debug_assert!(def.materials.len() == 1 || def.materials.len() == def.points.len());
54
55 debug_assert!(!world.locked);
56 if world.locked {
57 return ChainId::default();
58 }
59
60 let body_index = get_body_full_id(world, body_id);
61 let transform =
62 crate::body::get_body_transform_quick(world, &world.bodies[body_index as usize]);
63
64 let chain_id = world.chain_id_pool.alloc_id();
65
66 if chain_id == world.chain_shapes.len() as i32 {
67 world.chain_shapes.push(ChainShape {
68 id: NULL_INDEX,
69 body_id: NULL_INDEX,
70 next_chain_id: NULL_INDEX,
71 shape_indices: Vec::new(),
72 materials: Vec::new(),
73 generation: 0,
74 });
75 } else {
76 debug_assert!(world.chain_shapes[chain_id as usize].id == NULL_INDEX);
77 }
78
79 let material_count = def.materials.len();
80 for material in def.materials.iter() {
81 debug_assert!(is_valid_float(material.friction) && material.friction >= 0.0);
82 debug_assert!(is_valid_float(material.restitution) && material.restitution >= 0.0);
83 debug_assert!(
84 is_valid_float(material.rolling_resistance) && material.rolling_resistance >= 0.0
85 );
86 debug_assert!(is_valid_float(material.tangent_speed));
87 }
88
89 let head_chain_id = world.bodies[body_index as usize].head_chain_id;
90 {
91 let chain_shape = &mut world.chain_shapes[chain_id as usize];
92 chain_shape.id = chain_id;
93 chain_shape.body_id = world.bodies[body_index as usize].id;
94 chain_shape.next_chain_id = head_chain_id;
95 chain_shape.generation += 1;
96 chain_shape.materials = def.materials.clone();
97 }
98
99 world.bodies[body_index as usize].head_chain_id = chain_id;
100
101 let mut shape_def = default_shape_def();
102 shape_def.user_data = def.user_data;
103 shape_def.filter = def.filter;
104 shape_def.enable_sensor_events = def.enable_sensor_events;
105 shape_def.enable_contact_events = false;
106 shape_def.enable_hit_events = false;
107
108 let points = &def.points;
109 let n = points.len();
110
111 let material_for = |index: usize| -> SurfaceMaterial {
114 if material_count == 1 {
115 def.materials[0]
116 } else {
117 def.materials[index]
118 }
119 };
120
121 let make_segment = |g1: usize, p1: usize, p2: usize, g2: usize| ChainSegment {
122 ghost1: points[g1],
123 segment: Segment {
124 point1: points[p1],
125 point2: points[p2],
126 },
127 ghost2: points[g2],
128 chain_id,
129 };
130
131 let mut shape_indices: Vec<i32>;
132
133 if def.is_loop {
134 shape_indices = Vec::with_capacity(n);
135
136 let mut prev_index = n - 1;
137 for i in 0..(n - 2) {
138 let chain_segment = make_segment(prev_index, i, i + 1, i + 2);
139 prev_index = i;
140
141 shape_def.material = material_for(i);
142 let shape_id = create_shape_internal(
143 world,
144 body_index,
145 transform,
146 &shape_def,
147 ShapeGeometry::ChainSegment(chain_segment),
148 );
149 shape_indices.push(shape_id);
150 }
151
152 {
153 let chain_segment = make_segment(n - 3, n - 2, n - 1, 0);
154 shape_def.material = material_for(n - 2);
155 let shape_id = create_shape_internal(
156 world,
157 body_index,
158 transform,
159 &shape_def,
160 ShapeGeometry::ChainSegment(chain_segment),
161 );
162 shape_indices.push(shape_id);
163 }
164
165 {
166 let chain_segment = make_segment(n - 2, n - 1, 0, 1);
167 shape_def.material = material_for(n - 1);
168 let shape_id = create_shape_internal(
169 world,
170 body_index,
171 transform,
172 &shape_def,
173 ShapeGeometry::ChainSegment(chain_segment),
174 );
175 shape_indices.push(shape_id);
176 }
177 } else {
178 shape_indices = Vec::with_capacity(n - 3);
179
180 for i in 0..(n - 3) {
181 let chain_segment = make_segment(i, i + 1, i + 2, i + 3);
182
183 shape_def.material = material_for(i + 1);
185 let shape_id = create_shape_internal(
186 world,
187 body_index,
188 transform,
189 &shape_def,
190 ShapeGeometry::ChainSegment(chain_segment),
191 );
192 shape_indices.push(shape_id);
193 }
194 }
195
196 world.chain_shapes[chain_id as usize].shape_indices = shape_indices;
197
198 let id = ChainId {
199 index1: chain_id + 1,
200 world0: world.world_id,
201 generation: world.chain_shapes[chain_id as usize].generation,
202 };
203
204 crate::recording::record_op(world, |rec, _| {
206 crate::recording::write_create_chain(rec, body_id, def, id)
207 });
208
209 id
210}
211
212pub fn destroy_chain(world: &mut World, chain_id: ChainId) {
215 crate::recording::record_op(world, |rec, _| {
216 crate::recording::write_destroy_chain(rec, chain_id)
217 });
218 debug_assert!(!world.locked);
219 if world.locked {
220 return;
221 }
222
223 let chain_index = get_chain_index(world, chain_id);
224 let body_id = world.chain_shapes[chain_index as usize].body_id;
225
226 let mut found = false;
228 if world.bodies[body_id as usize].head_chain_id == chain_index {
229 world.bodies[body_id as usize].head_chain_id =
230 world.chain_shapes[chain_index as usize].next_chain_id;
231 found = true;
232 } else {
233 let mut prev_chain_id = world.bodies[body_id as usize].head_chain_id;
234 while prev_chain_id != NULL_INDEX {
235 let next = world.chain_shapes[prev_chain_id as usize].next_chain_id;
236 if next == chain_index {
237 world.chain_shapes[prev_chain_id as usize].next_chain_id =
238 world.chain_shapes[chain_index as usize].next_chain_id;
239 found = true;
240 break;
241 }
242 prev_chain_id = next;
243 }
244 }
245
246 debug_assert!(found);
247 if !found {
248 return;
249 }
250
251 let shape_indices = std::mem::take(&mut world.chain_shapes[chain_index as usize].shape_indices);
252 for shape_id in shape_indices {
253 let wake_bodies = true;
254 destroy_shape_internal(world, shape_id, body_id, wake_bodies);
255 }
256
257 world.chain_shapes[chain_index as usize].materials = Vec::new();
259
260 world.chain_id_pool.free_id(chain_index);
262 world.chain_shapes[chain_index as usize].id = NULL_INDEX;
263
264 world.validate_solver_sets();
265}
266
267pub fn chain_get_segment_count(world: &World, chain_id: ChainId) -> i32 {
269 debug_assert!(!world.locked);
270 if world.locked {
271 return 0;
272 }
273
274 let chain_index = get_chain_index(world, chain_id);
275 world.chain_shapes[chain_index as usize].shape_indices.len() as i32
276}
277
278pub fn chain_get_segments(world: &World, chain_id: ChainId, capacity: usize) -> Vec<ShapeId> {
281 debug_assert!(!world.locked);
282 if world.locked {
283 return Vec::new();
284 }
285
286 let chain_index = get_chain_index(world, chain_id);
287 let chain = &world.chain_shapes[chain_index as usize];
288
289 let count = chain.shape_indices.len().min(capacity);
290 chain.shape_indices[..count]
291 .iter()
292 .map(|&shape_id| ShapeId {
293 index1: shape_id + 1,
294 world0: world.world_id,
295 generation: world.shapes[shape_id as usize].generation,
296 })
297 .collect()
298}
299
300pub fn chain_get_surface_material_count(world: &World, chain_id: ChainId) -> i32 {
303 let chain_index = get_chain_index(world, chain_id);
304 world.chain_shapes[chain_index as usize].materials.len() as i32
305}
306
307pub fn chain_set_surface_material(
310 world: &mut World,
311 chain_id: ChainId,
312 material: SurfaceMaterial,
313 material_index: usize,
314) {
315 crate::recording::record_op(world, |rec, _| {
316 crate::recording::write_chain_set_surface_material(
317 rec,
318 chain_id,
319 material,
320 material_index as i32,
321 )
322 });
323 debug_assert!(!world.locked);
324 if world.locked {
325 return;
326 }
327
328 let chain_index = get_chain_index(world, chain_id);
329 debug_assert!(material_index < world.chain_shapes[chain_index as usize].materials.len());
330 world.chain_shapes[chain_index as usize].materials[material_index] = material;
331
332 let (material_count, count) = {
333 let chain = &world.chain_shapes[chain_index as usize];
334 (chain.materials.len(), chain.shape_indices.len())
335 };
336 debug_assert!(material_count == 1 || material_count == count);
337
338 if material_count == 1 {
339 for i in 0..count {
340 let shape_id = world.chain_shapes[chain_index as usize].shape_indices[i];
341 world.shapes[shape_id as usize].material = material;
342 }
343 } else {
344 let shape_id = world.chain_shapes[chain_index as usize].shape_indices[material_index];
345 world.shapes[shape_id as usize].material = material;
346 }
347}
348
349pub fn chain_get_surface_material(
351 world: &World,
352 chain_id: ChainId,
353 segment_index: usize,
354) -> SurfaceMaterial {
355 let chain_index = get_chain_index(world, chain_id);
356 let chain = &world.chain_shapes[chain_index as usize];
357 debug_assert!(segment_index < chain.shape_indices.len());
362 if chain.materials.len() == 1 {
363 chain.materials[0]
364 } else {
365 chain.materials[segment_index]
366 }
367}