1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Copyright (c) 2024 Lily Lyons
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use fmod_sys::*;
use std::ffi::{c_float, c_int};

use crate::{Geometry, System, Vector};

impl System {
    /// [`Geometry`] creation function. This function will create a base geometry object which can then have polygons added to it.
    ///
    /// Polygons can be added to a geometry object using [`Geometry::add_polygon`]. For best efficiency, avoid overlapping of polygons and long thin polygons.
    ///
    /// A geometry object stores its polygons in a group to allow optimization for line testing, insertion and updating of geometry in real-time.
    /// Geometry objects also allow for efficient rotation, scaling and translation of groups of polygons.
    ///
    /// It is important to set the value of maxworldsize to an appropriate value using [`System::set_geometry_settings`].
    pub fn create_geometry(&self, max_polygons: c_int, max_vertices: c_int) -> Result<Geometry> {
        let mut geometry = std::ptr::null_mut();
        unsafe {
            FMOD_System_CreateGeometry(self.inner, max_polygons, max_vertices, &mut geometry)
                .to_result()?;
        }
        Ok(geometry.into())
    }

    /// Sets the maximum world size for the geometry engine for performance / precision reasons.
    ///
    /// FMOD uses an efficient spatial partitioning system to store polygons for ray casting purposes.
    /// The maximum size of the world should (`max_world_size`) be set to allow processing within a known range.
    /// Outside of this range, objects and polygons will not be processed as efficiently.
    /// Excessive world size settings can also cause loss of precision and efficiency.
    ///
    /// Setting `max_world_size` should be done first before creating any geometry.
    /// It can be done any time afterwards but may be slow in this case.
    pub fn set_geometry_settings(&self, max_world_size: c_float) -> Result<()> {
        unsafe { FMOD_System_SetGeometrySettings(self.inner, max_world_size).to_result() }
    }

    /// Retrieves the maximum world size for the geometry engine.
    ///
    /// FMOD uses an efficient spatial partitioning system to store polygons for ray casting purposes.
    /// The maximum size of the world should (`max_world_size`) be set to allow processing within a known range.
    /// Outside of this range, objects and polygons will not be processed as efficiently.
    /// Excessive world size settings can also cause loss of precision and efficiency.
    pub fn get_geometry_settings(&self) -> Result<c_float> {
        let mut max_world_size = 0.0;
        unsafe {
            FMOD_System_GetGeometrySettings(self.inner, &mut max_world_size).to_result()?;
        }
        Ok(max_world_size)
    }

    /// Creates a geometry object from a block of memory which contains pre-saved geometry data.
    ///
    /// This function avoids the need to manually create and add geometry for faster start time.
    pub fn load_geometry(&self, data: &[u8]) -> Result<Geometry> {
        let mut geometry = std::ptr::null_mut();
        unsafe {
            FMOD_System_LoadGeometry(
                self.inner,
                data.as_ptr().cast(),
                data.len() as c_int,
                &mut geometry,
            )
            .to_result()?;
        }
        Ok(geometry.into())
    }

    /// Calculates geometry occlusion between a listener and a sound source.
    ///
    /// If single sided polygons have been created, it is important to get the source and listener positions around the right way,
    /// as the occlusion from point A to point B may not be the same as the occlusion from point B to point A.
    pub fn get_geometry_occlusion(
        &self,
        listener: Vector,
        source: Vector,
    ) -> Result<(c_float, c_float)> {
        let mut direct = 0.0;
        let mut reverb = 0.0;
        unsafe {
            FMOD_System_GetGeometryOcclusion(
                self.inner,
                std::ptr::from_ref(&listener).cast(),
                std::ptr::from_ref(&source).cast(),
                &mut direct,
                &mut reverb,
            )
            .to_result()?;
        }
        Ok((direct, reverb))
    }
}