1use optic_core::{OpticError, OpticErrorKind, OpticResult};
2use cgmath::{InnerSpace, Matrix4, Vector3, Vector4};
3
4use crate::asset::attr::{
5 ATTRInfo, ATTRName, ColATTR, CustomATTR, DataType, Pos2DATTR, Pos3DATTR, Rot2DATTR, Rot3DATTR,
6 Scale2DATTR, Scale3DATTR,
7};
8
9#[derive(Clone, Debug)]
15struct AttrSlot {
16 offset: usize,
17 size: usize,
18 info: ATTRInfo,
19}
20
21fn build_slots(
22 pos: Option<&Pos3DATTR>,
23 rot: Option<&Rot3DATTR>,
24 scale: Option<&Scale3DATTR>,
25 col: Option<&ColATTR>,
26 custom: &[CustomATTR],
27) -> Vec<AttrSlot> {
28 let mut slots = Vec::new();
29 let mut offset = 0usize;
30
31 let mut push = |info: ATTRInfo| {
32 let size = info.elem_count * info.byte_count;
33 slots.push(AttrSlot { offset, size, info });
34 offset += size;
35 };
36
37 if let Some(a) = pos {
38 if !a.is_empty() {
39 push(a.info.clone());
40 }
41 }
42 if let Some(a) = rot {
43 if !a.is_empty() {
44 push(a.info.clone());
45 }
46 }
47 if let Some(a) = scale {
48 if !a.is_empty() {
49 push(a.info.clone());
50 }
51 }
52 if let Some(a) = col {
53 if !a.is_empty() {
54 push(a.info.clone());
55 }
56 }
57 for c in custom {
58 push(c.info.clone());
59 }
60
61 slots
62}
63
64fn interleave(slots: &[AttrSlot], attrs: &[&[u8]], count: usize) -> Vec<u8> {
65 let stride: usize = slots.iter().map(|s| s.size).sum();
66 let mut buf = vec![0u8; count * stride];
67 for i in 0..count {
68 for (slot, data) in slots.iter().zip(attrs.iter()) {
69 let start = i * slot.size;
70 let end = start + slot.size;
71 let dst = i * stride + slot.offset;
72 buf[dst..dst + slot.size].copy_from_slice(&data[start..end]);
73 }
74 }
75 buf
76}
77
78#[derive(Clone, Debug)]
81pub(crate) struct CustomSlot {
82 pub name: String,
83 pub byte_offset: usize,
84 pub byte_size: usize,
85 pub typ: optic_core::ATTRType,
86 pub elem_count: u32,
87}
88
89#[derive(Clone, Debug)]
90pub(crate) struct InstanceKind {
91 pub has_pos: bool,
92 pub has_rot: bool,
93 pub has_scale: bool,
94 pub has_col: bool,
95 pub custom_offsets: Vec<CustomSlot>,
96}
97
98pub struct InstanceDesc3D {
134 pub pos_attr: Pos3DATTR,
135 pub rot_attr: Rot3DATTR,
136 pub scale_attr: Scale3DATTR,
137 pub col_attr: ColATTR,
138 pub cus_attrs: Vec<CustomATTR>,
139}
140
141impl InstanceDesc3D {
142 pub fn empty() -> Self {
147 Self {
148 pos_attr: Pos3DATTR::empty(),
149 rot_attr: Rot3DATTR::empty(),
150 scale_attr: Scale3DATTR::empty(),
151 col_attr: ColATTR::empty(),
152 cus_attrs: Vec::new(),
153 }
154 }
155
156 pub fn from_positions(positions: &[Vector3<f32>]) -> Self {
160 let mut desc = Self::empty();
161 for p in positions {
162 desc.pos_attr.push([p.x, p.y, p.z]);
163 }
164 desc
165 }
166
167 pub fn from_transforms(transforms: &[Matrix4<f32>]) -> Self {
178 let mut desc = Self::empty();
179 for m in transforms {
180 desc.pos_attr.push([m[3][0], m[3][1], m[3][2]]);
181
182 let sx = Vector3::new(m[0][0], m[1][0], m[2][0]).magnitude();
183 let sy = Vector3::new(m[0][1], m[1][1], m[2][1]).magnitude();
184 let sz = Vector3::new(m[0][2], m[1][2], m[2][2]).magnitude();
185 desc.scale_attr.push([sx, sy, sz]);
186
187 let r00 = m[0][0] / sx;
188 let r01 = m[0][1] / sy;
189 let r02 = m[0][2] / sz;
190 let r10 = m[1][0] / sx;
191 let r11 = m[1][1] / sy;
192 let r12 = m[1][2] / sz;
193 let r20 = m[2][0] / sx;
194 let r21 = m[2][1] / sy;
195 let r22 = m[2][2] / sz;
196
197 let trace = r00 + r11 + r22;
198 if trace > 0.0 {
199 let s = (trace + 1.0).sqrt() * 2.0;
200 desc.rot_attr.push([(r21 - r12) / s, (r02 - r20) / s, (r10 - r01) / s, s / 4.0]);
201 } else if r00 > r11 && r00 > r22 {
202 let s = (1.0 + r00 - r11 - r22).sqrt() * 2.0;
203 desc.rot_attr.push([s / 4.0, (r01 + r10) / s, (r02 + r20) / s, (r21 - r12) / s]);
204 } else if r11 > r22 {
205 let s = (1.0 + r11 - r00 - r22).sqrt() * 2.0;
206 desc.rot_attr.push([(r01 + r10) / s, s / 4.0, (r12 + r21) / s, (r02 - r20) / s]);
207 } else {
208 let s = (1.0 + r22 - r00 - r11).sqrt() * 2.0;
209 desc.rot_attr.push([(r02 + r20) / s, (r12 + r21) / s, s / 4.0, (r10 - r01) / s]);
210 }
211 }
212 desc
213 }
214
215 pub fn attach_custom_attr(&mut self, attr: CustomATTR) -> &mut Self {
222 self.cus_attrs.push(attr);
223 self
224 }
225
226 pub fn ship(&self) -> OpticResult<InstanceBuffer> {
237 let count = self.resolve_count();
238 let has_any_attr = self.pos_attr.is_empty()
239 && self.rot_attr.is_empty()
240 && self.scale_attr.is_empty()
241 && self.col_attr.is_empty()
242 && self.cus_attrs.is_empty();
243
244 if has_any_attr {
245 return Err(OpticError::new(
246 OpticErrorKind::Custom,
247 "cannot ship an instance buffer with zero attributes populated",
248 ));
249 }
250
251 self.verify_counts()?;
252 self.verify_custom_names()?;
253
254 let slots = build_slots(
255 Some(&self.pos_attr),
256 Some(&self.rot_attr),
257 Some(&self.scale_attr),
258 Some(&self.col_attr),
259 &self.cus_attrs,
260 );
261
262 let stride: usize = slots.iter().map(|s| s.size).sum();
263 let instance_count = count.unwrap_or(0);
264
265 let mut raw: Vec<&[u8]> = Vec::new();
266 if !self.pos_attr.is_empty() {
267 raw.push(self.pos_attr.data.as_bytes());
268 }
269 if !self.rot_attr.is_empty() {
270 raw.push(self.rot_attr.data.as_bytes());
271 }
272 if !self.scale_attr.is_empty() {
273 raw.push(self.scale_attr.data.as_bytes());
274 }
275 if !self.col_attr.is_empty() {
276 raw.push(self.col_attr.data.as_bytes());
277 }
278 for c in &self.cus_attrs {
279 raw.push(&c.data);
280 }
281
282 let cpu_mirror = if instance_count > 0 {
283 interleave(&slots, &raw, instance_count)
284 } else {
285 Vec::new()
286 };
287
288 let layouts: Vec<(ATTRInfo, u32)> = slots.iter().enumerate().map(|(i, s)| (s.info.clone(), i as u32)).collect();
289
290 let mut custom_offsets = Vec::new();
291 for c in &self.cus_attrs {
292 let off = slots.iter()
293 .position(|s| s.info.name == c.info.name)
294 .map(|idx| slots[idx].offset)
295 .unwrap_or(0);
296 custom_offsets.push(CustomSlot {
297 name: match &c.info.name { ATTRName::Custom(n) => n.clone(), _ => String::new() },
298 byte_offset: off,
299 byte_size: c.info.elem_count * c.info.byte_count,
300 typ: c.info.typ.clone(),
301 elem_count: c.info.elem_count as u32,
302 });
303 }
304
305 let kind = InstanceKind {
306 has_pos: !self.pos_attr.is_empty(),
307 has_rot: !self.rot_attr.is_empty(),
308 has_scale: !self.scale_attr.is_empty(),
309 has_col: !self.col_attr.is_empty(),
310 custom_offsets,
311 };
312
313 let buf_id = create_instance_buffer();
314 if !cpu_mirror.is_empty() {
315 upload_instance_data(buf_id, &cpu_mirror);
316 }
317
318 let capacity = if instance_count > 0 { instance_count as u32 } else { 0 };
319
320 Ok(InstanceBuffer {
321 buf_id,
322 capacity,
323 count: instance_count as u32,
324 stride: stride as u32,
325 layouts,
326 cpu_mirror,
327 kind,
328 })
329 }
330
331 fn resolve_count(&self) -> Option<usize> {
332 for attr in [&self.pos_attr.data as &dyn AsCount, &self.rot_attr.data, &self.scale_attr.data, &self.col_attr.data] {
333 if !attr.is_empty() {
334 return Some(attr.len());
335 }
336 }
337 for c in &self.cus_attrs {
338 if !c.is_empty() {
339 let elem_size = c.info.elem_count * c.info.byte_count;
340 return Some(c.data.len() / elem_size);
341 }
342 }
343 None
344 }
345
346 fn verify_counts(&self) -> OpticResult<()> {
347 let count = match self.resolve_count() {
348 Some(c) => c,
349 None => return Ok(()),
350 };
351
352 macro_rules! check {
353 ($attr:expr, $name:expr) => {
354 if !$attr.is_empty() && $attr.data.len() != count {
355 return Err(OpticError::new(
356 OpticErrorKind::Custom,
357 &format!(
358 "instance attribute count mismatch: {} has {} elements, expected {}",
359 $name,
360 $attr.data.len(),
361 count
362 ),
363 ));
364 }
365 };
366 }
367 check!(self.pos_attr, "pos_attr");
368 check!(self.rot_attr, "rot_attr");
369 check!(self.scale_attr, "scale_attr");
370 check!(self.col_attr, "col_attr");
371
372 for c in &self.cus_attrs {
373 let elem_size = c.info.elem_count * c.info.byte_count;
374 let c_count = if elem_size > 0 { c.data.len() / elem_size } else { 0 };
375 if c_count != count {
376 let name = match &c.info.name { ATTRName::Custom(n) => n.clone(), _ => "unknown".into() };
377 return Err(OpticError::new(
378 OpticErrorKind::Custom,
379 &format!(
380 "instance attribute count mismatch: custom attr \"{name}\" has {c_count} elements, expected {count}"
381 ),
382 ));
383 }
384 }
385
386 Ok(())
387 }
388
389 fn verify_custom_names(&self) -> OpticResult<()> {
390 let reserved = ["iPos", "iRot", "iScale", "iColor"];
391 for c in &self.cus_attrs {
392 let name = match &c.info.name {
393 ATTRName::Custom(n) => n.as_str(),
394 _ => continue,
395 };
396 if reserved.contains(&name) {
397 return Err(OpticError::new(
398 OpticErrorKind::Custom,
399 &format!("custom attribute name \"{name}\" collides with reserved instance attribute name"),
400 ));
401 }
402 }
403 for i in 0..self.cus_attrs.len() {
404 for j in i + 1..self.cus_attrs.len() {
405 let ni = match &self.cus_attrs[i].info.name { ATTRName::Custom(n) => n, _ => continue };
406 let nj = match &self.cus_attrs[j].info.name { ATTRName::Custom(n) => n, _ => continue };
407 if ni == nj {
408 return Err(OpticError::new(
409 OpticErrorKind::Custom,
410 &format!("duplicate custom attribute name \"{ni}\""),
411 ));
412 }
413 }
414 }
415 Ok(())
416 }
417}
418
419pub struct InstanceDesc2D {
435 pub pos_attr: Pos2DATTR,
436 pub rot_attr: Rot2DATTR,
437 pub scale_attr: Scale2DATTR,
438 pub col_attr: ColATTR,
439 pub cus_attrs: Vec<CustomATTR>,
440}
441
442impl InstanceDesc2D {
443 pub fn empty() -> Self {
445 Self {
446 pos_attr: Pos2DATTR::empty(),
447 rot_attr: Rot2DATTR::empty(),
448 scale_attr: Scale2DATTR::empty(),
449 col_attr: ColATTR::empty(),
450 cus_attrs: Vec::new(),
451 }
452 }
453
454 pub fn attach_custom_attr(&mut self, attr: CustomATTR) -> &mut Self {
456 self.cus_attrs.push(attr);
457 self
458 }
459
460 pub fn ship(&self) -> OpticResult<InstanceBuffer> {
466 let has_any_attr = self.pos_attr.is_empty()
467 && self.rot_attr.is_empty()
468 && self.scale_attr.is_empty()
469 && self.col_attr.is_empty()
470 && self.cus_attrs.is_empty();
471
472 if has_any_attr {
473 return Err(OpticError::new(
474 OpticErrorKind::Custom,
475 "cannot ship an instance buffer with zero attributes populated",
476 ));
477 }
478
479 let mut slots = Vec::new();
480 let mut offset = 0usize;
481
482 let mut push = |info: ATTRInfo| {
483 let size = info.elem_count * info.byte_count;
484 slots.push(AttrSlot { offset, size, info: info.clone() });
485 offset += size;
486 };
487
488 if !self.pos_attr.is_empty() { push(self.pos_attr.info.clone()); }
489 if !self.rot_attr.is_empty() { push(self.rot_attr.info.clone()); }
490 if !self.scale_attr.is_empty() { push(self.scale_attr.info.clone()); }
491 if !self.col_attr.is_empty() { push(self.col_attr.info.clone()); }
492 for c in &self.cus_attrs { push(c.info.clone()); }
493
494 let stride = offset;
495 let count = self.resolve_count();
496 let instance_count = count.unwrap_or(0);
497
498 let mut raw: Vec<&[u8]> = Vec::new();
499 if !self.pos_attr.is_empty() { raw.push(self.pos_attr.data.as_bytes()); }
500 if !self.rot_attr.is_empty() { raw.push(self.rot_attr.data.as_bytes()); }
501 if !self.scale_attr.is_empty() { raw.push(self.scale_attr.data.as_bytes()); }
502 if !self.col_attr.is_empty() { raw.push(self.col_attr.data.as_bytes()); }
503 for c in &self.cus_attrs { raw.push(&c.data); }
504
505 let cpu_mirror = if instance_count > 0 {
506 interleave(&slots, &raw, instance_count)
507 } else {
508 Vec::new()
509 };
510
511 let layouts: Vec<(ATTRInfo, u32)> = slots.iter().enumerate().map(|(i, s)| (s.info.clone(), i as u32)).collect();
512
513 let mut custom_offsets = Vec::new();
514 for c in &self.cus_attrs {
515 let off = slots.iter()
516 .position(|s| s.info.name == c.info.name)
517 .map(|idx| slots[idx].offset)
518 .unwrap_or(0);
519 custom_offsets.push(CustomSlot {
520 name: match &c.info.name { ATTRName::Custom(n) => n.clone(), _ => String::new() },
521 byte_offset: off,
522 byte_size: c.info.elem_count * c.info.byte_count,
523 typ: c.info.typ.clone(),
524 elem_count: c.info.elem_count as u32,
525 });
526 }
527
528 let kind = InstanceKind {
529 has_pos: !self.pos_attr.is_empty(),
530 has_rot: !self.rot_attr.is_empty(),
531 has_scale: !self.scale_attr.is_empty(),
532 has_col: !self.col_attr.is_empty(),
533 custom_offsets,
534 };
535
536 let buf_id = create_instance_buffer();
537 if !cpu_mirror.is_empty() {
538 upload_instance_data(buf_id, &cpu_mirror);
539 }
540
541 let capacity = if instance_count > 0 { instance_count as u32 } else { 0 };
542
543 Ok(InstanceBuffer {
544 buf_id,
545 capacity,
546 count: instance_count as u32,
547 stride: stride as u32,
548 layouts,
549 cpu_mirror,
550 kind,
551 })
552 }
553
554 fn resolve_count(&self) -> Option<usize> {
555 for attr in [&self.pos_attr.data as &dyn AsCount, &self.col_attr.data] {
556 if !attr.is_empty() {
557 return Some(attr.len());
558 }
559 }
560 for c in &self.cus_attrs {
561 if !c.is_empty() {
562 let elem_size = c.info.elem_count * c.info.byte_count;
563 return Some(c.data.len() / elem_size);
564 }
565 }
566 None
567 }
568}
569
570pub struct InstanceBuffer {
635 pub(crate) buf_id: u32,
636 pub(crate) capacity: u32,
637 pub(crate) count: u32,
638 pub(crate) stride: u32,
639 pub layouts: Vec<(ATTRInfo, u32)>,
640 pub(crate) cpu_mirror: Vec<u8>,
641 pub(crate) kind: InstanceKind,
642}
643
644impl InstanceBuffer {
645 pub fn count(&self) -> u32 { self.count }
647
648 pub fn capacity(&self) -> u32 { self.capacity }
650
651 pub fn update_instance<D: DataType>(&mut self, index: u32, attr_index: usize, value: D) -> OpticResult<()> {
679 if index >= self.count {
680 return Err(OpticError::new(OpticErrorKind::Custom, &format!("instance index {index} out of bounds (count: {})", self.count)));
681 }
682 if attr_index >= self.layouts.len() {
683 return Err(OpticError::new(OpticErrorKind::Custom, &format!("attr index {attr_index} out of bounds (layout count: {})", self.layouts.len())));
684 }
685
686 let slot_info = &self.layouts[attr_index].0;
687 if slot_info.byte_count != D::BYTE_COUNT || slot_info.elem_count != D::ELEM_COUNT || slot_info.typ != D::ATTR_FORMAT {
688 return Err(OpticError::new(
689 OpticErrorKind::Custom,
690 &format!(
691 "type mismatch: attribute {} expects {:?}[{}], got {:?}[{}]",
692 slot_info.name.as_string(),
693 slot_info.typ,
694 slot_info.elem_count,
695 D::ATTR_FORMAT,
696 D::ELEM_COUNT,
697 ),
698 ));
699 }
700
701 let bytes = value.u8ify();
702 let off = index as usize * self.stride as usize + self.compute_attr_offset(attr_index);
703 let size = slot_info.elem_count * slot_info.byte_count;
704
705 if bytes.len() != size {
706 return Err(OpticError::new(OpticErrorKind::Custom, &format!(
707 "value byte size {} does not match attribute size {}", bytes.len(), size
708 )));
709 }
710
711 self.cpu_mirror[off..off + size].copy_from_slice(&bytes);
712 subfill_instance_data(self.buf_id, off, &bytes);
713
714 Ok(())
715 }
716
717 pub fn get_instance<D: DataType>(&self, index: u32, attr_index: usize) -> OpticResult<D> {
732 if index >= self.count {
733 return Err(OpticError::new(OpticErrorKind::Custom, &format!("instance index {index} out of bounds (count: {})", self.count)));
734 }
735 if attr_index >= self.layouts.len() {
736 return Err(OpticError::new(OpticErrorKind::Custom, &format!("attr index {attr_index} out of bounds (layout count: {})", self.layouts.len())));
737 }
738
739 let slot_info = &self.layouts[attr_index].0;
740 if slot_info.byte_count != D::BYTE_COUNT || slot_info.elem_count != D::ELEM_COUNT || slot_info.typ != D::ATTR_FORMAT {
741 return Err(OpticError::new(
742 OpticErrorKind::Custom,
743 &format!(
744 "type mismatch: attribute {} expects {:?}[{}], got {:?}[{}]",
745 slot_info.name.as_string(),
746 slot_info.typ,
747 slot_info.elem_count,
748 D::ATTR_FORMAT,
749 D::ELEM_COUNT,
750 ),
751 ));
752 }
753
754 let off = index as usize * self.stride as usize + self.compute_attr_offset(attr_index);
755 let size = slot_info.elem_count * slot_info.byte_count;
756 let raw = &self.cpu_mirror[off..off + size];
757
758 let d = deserialize::<D>(raw);
759 Ok(d)
760 }
761
762 pub fn update_custom<D: DataType>(&mut self, index: u32, name: &str, value: D) -> OpticResult<()> {
775 let slot = self.kind.custom_offsets.iter().find(|s| s.name == name)
776 .ok_or_else(|| OpticError::new(OpticErrorKind::Custom, &format!("custom attribute \"{name}\" not found")))?;
777
778 if slot.byte_size != D::BYTE_COUNT || slot.elem_count as usize != D::ELEM_COUNT || slot.typ != D::ATTR_FORMAT {
779 return Err(OpticError::new(
780 OpticErrorKind::Custom,
781 &format!(
782 "type mismatch for custom attribute \"{name}\": expected {:?}[{}], got {:?}[{}]",
783 slot.typ, slot.elem_count, D::ATTR_FORMAT, D::ELEM_COUNT,
784 ),
785 ));
786 }
787
788 let bytes = value.u8ify();
789 let off = index as usize * self.stride as usize + slot.byte_offset;
790
791 self.cpu_mirror[off..off + slot.byte_size].copy_from_slice(&bytes);
792 subfill_instance_data(self.buf_id, off, &bytes);
793
794 Ok(())
795 }
796
797 pub fn get_custom<D: DataType>(&self, index: u32, name: &str) -> OpticResult<D> {
805 let slot = self.kind.custom_offsets.iter().find(|s| s.name == name)
806 .ok_or_else(|| OpticError::new(OpticErrorKind::Custom, &format!("custom attribute \"{name}\" not found")))?;
807
808 if slot.byte_size != D::BYTE_COUNT || slot.elem_count as usize != D::ELEM_COUNT || slot.typ != D::ATTR_FORMAT {
809 return Err(OpticError::new(
810 OpticErrorKind::Custom,
811 &format!(
812 "type mismatch for custom attribute \"{name}\": expected {:?}[{}], got {:?}[{}]",
813 slot.typ, slot.elem_count, D::ATTR_FORMAT, D::ELEM_COUNT,
814 ),
815 ));
816 }
817
818 let off = index as usize * self.stride as usize + slot.byte_offset;
819 let raw = &self.cpu_mirror[off..off + slot.byte_size];
820 Ok(deserialize::<D>(raw))
821 }
822
823 pub fn set_position(&mut self, index: u32, pos: Vector3<f32>) -> OpticResult<()> {
834 if !self.kind.has_pos {
835 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no position attribute"));
836 }
837 let attr_index = 0;
838 self.update_instance(index, attr_index, [pos.x, pos.y, pos.z])
839 }
840
841 pub fn get_position(&self, index: u32) -> OpticResult<Vector3<f32>> {
850 if !self.kind.has_pos {
851 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no position attribute"));
852 }
853 let arr: [f32; 3] = self.get_instance(index, 0)?;
854 Ok(Vector3::new(arr[0], arr[1], arr[2]))
855 }
856
857 pub fn set_rotation(&mut self, index: u32, rot: Vector4<f32>) -> OpticResult<()> {
876 if !self.kind.has_rot {
877 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no rotation attribute"));
878 }
879 let attr_index = if self.kind.has_pos { 1 } else { 0 };
880 self.update_instance(index, attr_index, [rot.x, rot.y, rot.z, rot.w])
881 }
882
883 pub fn get_rotation(&self, index: u32) -> OpticResult<Vector4<f32>> {
892 if !self.kind.has_rot {
893 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no rotation attribute"));
894 }
895 let attr_index = if self.kind.has_pos { 1 } else { 0 };
896 let arr: [f32; 4] = self.get_instance(index, attr_index)?;
897 Ok(Vector4::new(arr[0], arr[1], arr[2], arr[3]))
898 }
899
900 pub fn set_scale(&mut self, index: u32, scale: Vector3<f32>) -> OpticResult<()> {
914 if !self.kind.has_scale {
915 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no scale attribute"));
916 }
917 let attr_index = if self.kind.has_pos { 1 } else { 0 };
918 let attr_index = if self.kind.has_rot { attr_index + 1 } else { attr_index };
919 self.update_instance(index, attr_index, [scale.x, scale.y, scale.z])
920 }
921
922 pub fn get_scale(&self, index: u32) -> OpticResult<Vector3<f32>> {
931 if !self.kind.has_scale {
932 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no scale attribute"));
933 }
934 let mut attr_index = 0u32;
935 if self.kind.has_pos { attr_index += 1; }
936 if self.kind.has_rot { attr_index += 1; }
937 let actual_idx = attr_index as usize;
938 let arr: [f32; 3] = self.get_instance(index, actual_idx)?;
939 Ok(Vector3::new(arr[0], arr[1], arr[2]))
940 }
941
942 pub fn set_color(&mut self, index: u32, color: optic_core::RGBA) -> OpticResult<()> {
956 if !self.kind.has_col {
957 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no color attribute"));
958 }
959 let rgba = [color.0, color.1, color.2, color.3];
960 let mut attr_index = 0u32;
961 if self.kind.has_pos { attr_index += 1; }
962 if self.kind.has_rot { attr_index += 1; }
963 if self.kind.has_scale { attr_index += 1; }
964 self.update_instance(index, attr_index as usize, rgba)
965 }
966
967 pub fn get_color(&self, index: u32) -> OpticResult<optic_core::RGBA> {
976 if !self.kind.has_col {
977 return Err(OpticError::new(OpticErrorKind::Custom, "instance buffer has no color attribute"));
978 }
979 let mut attr_index = 0u32;
980 if self.kind.has_pos { attr_index += 1; }
981 if self.kind.has_rot { attr_index += 1; }
982 if self.kind.has_scale { attr_index += 1; }
983 let rgba: [f32; 4] = self.get_instance(index, attr_index as usize)?;
984 Ok(optic_core::RGBA(rgba[0], rgba[1], rgba[2], rgba[3]))
985 }
986
987 pub fn set_instance_count(&mut self, new_count: u32) {
1019 if new_count > self.capacity {
1020 let new_cap = new_count.max(self.capacity * 2);
1021 self.reserve_internal(new_cap);
1022 }
1023 if new_count > self.count {
1024 let old_count = self.count as usize;
1025 let new_count_usize = new_count as usize;
1026 let stride = self.stride as usize;
1027 self.cpu_mirror.resize(new_count_usize * stride, 0u8);
1028 let default_slot = self.make_default_instance_bytes();
1029 for i in old_count..new_count_usize {
1030 let off = i * stride;
1031 self.cpu_mirror[off..off + stride].copy_from_slice(&default_slot);
1032 }
1033 }
1034 self.count = new_count;
1035 upload_instance_data(self.buf_id, &self.cpu_mirror);
1036 }
1037
1038 pub fn reserve(&mut self, additional: u32) {
1042 let needed = self.count + additional;
1043 if needed > self.capacity {
1044 let new_cap = needed.max(self.capacity * 2);
1045 self.reserve_internal(new_cap);
1046 }
1047 }
1048
1049 pub fn shrink_to_fit(&mut self) {
1055 if self.count < self.capacity {
1056 let new_cap = self.count;
1057 self.capacity = new_cap;
1058 realloc_instance_buffer(self.buf_id, self.cpu_mirror.len());
1059 }
1060 }
1061
1062 pub fn push_raw(&mut self, bytes: &[u8]) -> OpticResult<u32> {
1073 if bytes.len() != self.stride as usize {
1074 return Err(OpticError::new(
1075 OpticErrorKind::Custom,
1076 &format!("push_raw byte count {} does not match instance stride {}", bytes.len(), self.stride),
1077 ));
1078 }
1079 let idx = self.count;
1080 self.set_instance_count(self.count + 1);
1081 let off = idx as usize * self.stride as usize;
1082 self.cpu_mirror[off..off + self.stride as usize].copy_from_slice(bytes);
1083 subfill_instance_data(self.buf_id, off, bytes);
1084 Ok(idx)
1085 }
1086
1087 pub fn remove_instance(&mut self, index: u32) -> OpticResult<()> {
1099 if index >= self.count {
1100 return Err(OpticError::new(OpticErrorKind::Custom, &format!("remove index {index} out of bounds (count: {})", self.count)));
1101 }
1102 let last = self.count - 1;
1103 if index != last {
1104 let stride = self.stride as usize;
1105 let dst = index as usize * stride;
1106 let src = last as usize * stride;
1107 let len = stride;
1108 self.cpu_mirror.copy_within(src..src + len, dst);
1109 subfill_instance_data(self.buf_id, dst, &self.cpu_mirror[dst..dst + len]);
1110 }
1111 self.count = last;
1112 Ok(())
1113 }
1114
1115 pub fn remove_instance_ordered(&mut self, index: u32) -> OpticResult<()> {
1121 if index >= self.count {
1122 return Err(OpticError::new(OpticErrorKind::Custom, &format!("remove index {index} out of bounds (count: {})", self.count)));
1123 }
1124 let stride = self.stride as usize;
1125 let dst = index as usize * stride;
1126 let end = (self.count - 1) as usize * stride;
1127 let len = end - dst;
1128 if len > 0 {
1129 self.cpu_mirror.copy_within(dst + stride..end + stride, dst);
1130 }
1131 self.count -= 1;
1132 upload_instance_data(self.buf_id, &self.cpu_mirror[..self.count as usize * stride]);
1133 Ok(())
1134 }
1135
1136 pub fn write_all(&mut self, desc: &InstanceDesc3D) -> OpticResult<()> {
1147 let new_buf = desc.ship()?;
1148 self.buf_id = new_buf.buf_id;
1149 self.capacity = new_buf.capacity;
1150 self.count = new_buf.count;
1151 self.stride = new_buf.stride;
1152 self.layouts = new_buf.layouts;
1153 self.cpu_mirror = new_buf.cpu_mirror;
1154 self.kind = new_buf.kind;
1155 Ok(())
1156 }
1157
1158 pub fn write_range(&mut self, start: u32, bytes: &[u8]) -> OpticResult<()> {
1169 let stride = self.stride as usize;
1170 if bytes.len() % stride != 0 {
1171 return Err(OpticError::new(
1172 OpticErrorKind::Custom,
1173 &format!("write_range byte count {} is not a multiple of stride {}", bytes.len(), stride),
1174 ));
1175 }
1176 let instance_count = bytes.len() / stride;
1177 if start + instance_count as u32 > self.count {
1178 return Err(OpticError::new(
1179 OpticErrorKind::Custom,
1180 "write_range extends past the current instance count",
1181 ));
1182 }
1183 let off = start as usize * stride;
1184 self.cpu_mirror[off..off + bytes.len()].copy_from_slice(bytes);
1185 subfill_instance_data(self.buf_id, off, bytes);
1186 Ok(())
1187 }
1188
1189 fn compute_attr_offset(&self, attr_index: usize) -> usize {
1192 let mut offset = 0usize;
1193 for i in 0..attr_index {
1194 let si = &self.layouts[i].0;
1195 offset += si.elem_count * si.byte_count;
1196 }
1197 offset
1198 }
1199
1200 fn reserve_internal(&mut self, new_cap: u32) {
1201 let old_size = self.cpu_mirror.len();
1202 let new_size = new_cap as usize * self.stride as usize;
1203 self.cpu_mirror.resize(new_size, 0u8);
1204 let stride = self.stride as usize;
1205 let default_slot = self.make_default_instance_bytes();
1206 for i in (old_size / stride)..new_cap as usize {
1207 let off = i * stride;
1208 self.cpu_mirror[off..off + stride].copy_from_slice(&default_slot);
1209 }
1210 self.capacity = new_cap;
1211 realloc_instance_buffer(self.buf_id, new_size);
1212 upload_instance_data(self.buf_id, &self.cpu_mirror);
1213 }
1214
1215 fn make_default_instance_bytes(&self) -> Vec<u8> {
1216 let stride = self.stride as usize;
1217 let mut bytes = vec![0u8; stride];
1218 if self.kind.has_pos {
1219 }
1221 if self.kind.has_rot {
1222 let off = if self.kind.has_pos { 12 } else { 0 };
1223 bytes[off + 12..off + 16].copy_from_slice(&1.0f32.to_le_bytes());
1224 }
1225 if self.kind.has_scale {
1226 let mut off = 0usize;
1227 if self.kind.has_pos { off += 12; }
1228 if self.kind.has_rot { off += 16; }
1229 bytes[off..off + 4].copy_from_slice(&1.0f32.to_le_bytes());
1230 bytes[off + 4..off + 8].copy_from_slice(&1.0f32.to_le_bytes());
1231 bytes[off + 8..off + 12].copy_from_slice(&1.0f32.to_le_bytes());
1232 }
1233 if self.kind.has_col {
1234 let mut off = 0usize;
1235 if self.kind.has_pos { off += 12; }
1236 if self.kind.has_rot { off += 16; }
1237 if self.kind.has_scale { off += 12; }
1238 for i in 0..4 {
1239 bytes[off + i * 4..off + (i + 1) * 4].copy_from_slice(&1.0f32.to_le_bytes());
1240 }
1241 }
1242 bytes
1243 }
1244}
1245
1246fn create_instance_buffer() -> u32 {
1249 let mut id = 0u32;
1250 unsafe { gl::GenBuffers(1, &mut id); }
1251 id
1252}
1253
1254fn upload_instance_data(id: u32, data: &[u8]) {
1255 unsafe {
1256 gl::BindBuffer(gl::ARRAY_BUFFER, id);
1257 gl::BufferData(
1258 gl::ARRAY_BUFFER,
1259 data.len() as gl::types::GLsizeiptr,
1260 data.as_ptr() as *const std::ffi::c_void,
1261 gl::DYNAMIC_DRAW,
1262 );
1263 }
1264}
1265
1266fn subfill_instance_data(id: u32, offset: usize, data: &[u8]) {
1267 unsafe {
1268 gl::BindBuffer(gl::ARRAY_BUFFER, id);
1269 gl::BufferSubData(
1270 gl::ARRAY_BUFFER,
1271 offset as isize,
1272 data.len() as isize,
1273 data.as_ptr() as *const std::ffi::c_void,
1274 );
1275 }
1276}
1277
1278fn realloc_instance_buffer(id: u32, size: usize) {
1279 unsafe {
1280 gl::BindBuffer(gl::ARRAY_BUFFER, id);
1281 gl::BufferData(
1282 gl::ARRAY_BUFFER,
1283 size as gl::types::GLsizeiptr,
1284 std::ptr::null(),
1285 gl::DYNAMIC_DRAW,
1286 );
1287 }
1288}
1289
1290trait AsCount {
1293 fn len(&self) -> usize;
1294 fn is_empty(&self) -> bool;
1295}
1296
1297impl<T> AsCount for Vec<T> {
1298 fn len(&self) -> usize { self.len() }
1299 fn is_empty(&self) -> bool { self.is_empty() }
1300}
1301
1302impl AsCount for CustomATTR {
1303 fn len(&self) -> usize {
1304 if self.info.elem_count == 0 || self.info.byte_count == 0 { return 0; }
1305 self.data.len() / (self.info.elem_count * self.info.byte_count)
1306 }
1307 fn is_empty(&self) -> bool { self.data.is_empty() }
1308}
1309
1310fn deserialize<D: DataType>(bytes: &[u8]) -> D {
1313 unsafe {
1314 let ptr = bytes.as_ptr() as *const D;
1315 std::ptr::read_unaligned(ptr)
1316 }
1317}
1318
1319trait AsBytes {
1322 fn as_bytes(&self) -> &[u8];
1323}
1324
1325impl AsBytes for Vec<[f32; 3]> {
1326 fn as_bytes(&self) -> &[u8] {
1327 unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 12) }
1328 }
1329}
1330
1331impl AsBytes for Vec<[f32; 4]> {
1332 fn as_bytes(&self) -> &[u8] {
1333 unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 16) }
1334 }
1335}
1336
1337impl AsBytes for Vec<[f32; 2]> {
1338 fn as_bytes(&self) -> &[u8] {
1339 unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) }
1340 }
1341}
1342
1343impl AsBytes for Vec<f32> {
1344 fn as_bytes(&self) -> &[u8] {
1345 unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 4) }
1346 }
1347}