Skip to main content

fyrox_graphics_gl/
geometry_buffer.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::{buffer::GlBuffer, server::GlGraphicsServer, ToGlConstant};
22use fyrox_graphics::{
23    buffer::{BufferKind, GpuBufferDescriptor, GpuBufferTrait},
24    core::{array_as_u8_slice, math::TriangleDefinition},
25    error::FrameworkError,
26    geometry_buffer::{
27        AttributeKind, ElementsDescriptor, GpuGeometryBufferDescriptor, GpuGeometryBufferTrait,
28    },
29    ElementKind,
30};
31use glow::HasContext;
32use std::{cell::Cell, marker::PhantomData, rc::Weak};
33
34impl ToGlConstant for AttributeKind {
35    fn into_gl(self) -> u32 {
36        match self {
37            AttributeKind::Float => glow::FLOAT,
38            AttributeKind::UnsignedByte => glow::UNSIGNED_BYTE,
39            AttributeKind::UnsignedShort => glow::UNSIGNED_SHORT,
40            AttributeKind::UnsignedInt => glow::UNSIGNED_INT,
41        }
42    }
43}
44
45pub struct GlGeometryBuffer {
46    pub state: Weak<GlGraphicsServer>,
47    pub vertex_array_object: glow::VertexArray,
48    pub buffers: Vec<GlBuffer>,
49    pub element_buffer: GlBuffer,
50    pub element_count: Cell<usize>,
51    pub element_kind: ElementKind,
52    // Force compiler to not implement Send and Sync, because OpenGL is not thread-safe.
53    thread_mark: PhantomData<*const u8>,
54}
55
56impl GlGeometryBuffer {
57    pub fn new(
58        server: &GlGraphicsServer,
59        desc: GpuGeometryBufferDescriptor,
60    ) -> Result<Self, FrameworkError> {
61        let vao = unsafe { server.gl.create_vertex_array()? };
62
63        server.set_vertex_array_object(Some(vao));
64        #[cfg(not(target_arch = "wasm32"))]
65        if server.gl.supports_debug() && server.named_objects.get() {
66            unsafe {
67                server
68                    .gl
69                    .object_label(glow::VERTEX_ARRAY, vao.0.get(), Some(desc.name));
70            }
71        }
72
73        let element_buffer = GlBuffer::new(
74            server,
75            GpuBufferDescriptor {
76                name: &format!("{}ElementBuffer", desc.name),
77                size: 0,
78                kind: BufferKind::Index,
79                usage: desc.usage,
80            },
81        )?;
82
83        let (element_count, data) = match desc.elements {
84            ElementsDescriptor::Triangles(triangles) => {
85                (triangles.len(), array_as_u8_slice(triangles))
86            }
87            ElementsDescriptor::Lines(lines) => (lines.len(), array_as_u8_slice(lines)),
88            ElementsDescriptor::Points(points) => (points.len(), array_as_u8_slice(points)),
89        };
90
91        element_buffer.write_data(data)?;
92
93        let mut buffers = Vec::new();
94        for (i, buffer) in desc.buffers.iter().enumerate() {
95            unsafe {
96                let data_size = buffer.data.bytes.map(|bytes| bytes.len()).unwrap_or(0);
97
98                let native_buffer = GlBuffer::new(
99                    server,
100                    GpuBufferDescriptor {
101                        name: &format!("{}VertexBuffer{i}", desc.name),
102                        size: data_size,
103                        kind: BufferKind::Vertex,
104                        usage: buffer.usage,
105                    },
106                )?;
107
108                if let Some(data) = buffer.data.bytes {
109                    native_buffer.write_data(data)?;
110                }
111
112                let target = native_buffer.kind.into_gl();
113                server.gl.bind_buffer(target, Some(native_buffer.id));
114
115                let mut offset = 0usize;
116                for definition in buffer.attributes {
117                    server.gl.vertex_attrib_pointer_f32(
118                        definition.location,
119                        definition.component_count as i32,
120                        definition.kind.into_gl(),
121                        definition.normalized,
122                        buffer.data.element_size as i32,
123                        offset as i32,
124                    );
125                    server
126                        .gl
127                        .vertex_attrib_divisor(definition.location, definition.divisor);
128                    server.gl.enable_vertex_attrib_array(definition.location);
129
130                    offset += definition.kind.size() * definition.component_count;
131
132                    if offset > buffer.data.element_size {
133                        return Err(FrameworkError::InvalidAttributeDescriptor);
134                    }
135                }
136
137                buffers.push(native_buffer);
138            }
139        }
140
141        server.set_vertex_array_object(None);
142
143        Ok(GlGeometryBuffer {
144            state: server.weak(),
145            vertex_array_object: vao,
146            buffers,
147            element_buffer,
148            element_count: Cell::new(element_count),
149            element_kind: desc.elements.element_kind(),
150            thread_mark: PhantomData,
151        })
152    }
153
154    fn set_elements(&self, data: &[u8]) {
155        self.state
156            .upgrade()
157            .unwrap()
158            .set_vertex_array_object(Some(self.vertex_array_object));
159        self.element_buffer.write_data(data).unwrap()
160    }
161
162    pub fn mode(&self) -> u32 {
163        match self.element_kind {
164            ElementKind::Triangle => glow::TRIANGLES,
165            ElementKind::Line => glow::LINES,
166            ElementKind::Point => glow::POINTS,
167        }
168    }
169}
170
171impl GpuGeometryBufferTrait for GlGeometryBuffer {
172    fn set_buffer_data(&self, buffer: usize, data: &[u8]) {
173        self.state
174            .upgrade()
175            .unwrap()
176            .set_vertex_array_object(Some(self.vertex_array_object));
177        self.buffers[buffer]
178            .write_data(array_as_u8_slice(data))
179            .unwrap();
180    }
181
182    fn element_count(&self) -> usize {
183        self.element_count.get()
184    }
185
186    fn set_triangles(&self, triangles: &[TriangleDefinition]) {
187        assert_eq!(self.element_kind, ElementKind::Triangle);
188        self.element_count.set(triangles.len());
189        self.set_elements(array_as_u8_slice(triangles));
190    }
191
192    fn set_lines(&self, lines: &[[u32; 2]]) {
193        assert_eq!(self.element_kind, ElementKind::Line);
194        self.element_count.set(lines.len());
195        self.set_elements(array_as_u8_slice(lines));
196    }
197
198    fn set_points(&self, points: &[u32]) {
199        assert_eq!(self.element_kind, ElementKind::Point);
200        self.element_count.set(points.len());
201        self.set_elements(array_as_u8_slice(points));
202    }
203}
204
205impl Drop for GlGeometryBuffer {
206    fn drop(&mut self) {
207        if let Some(state) = self.state.upgrade() {
208            self.buffers.clear();
209            state.delete_vertex_array_object(self.vertex_array_object);
210        }
211    }
212}