Skip to main content

hotline_rs/
imdraw.rs

1use crate::gfx;
2use gfx::Buffer;
3use gfx::CmdBuf;
4
5use maths_rs::prelude::*;
6
7/// A coherent cpu/gpu buffer back by multiple gpu buffers to allow cpu writes while gpu is inflight 
8struct DynamicBuffer<D: gfx::Device> {
9    cpu_data: Vec<f32>,
10    gpu_data: Vec<D::Buffer>,
11    gpu_data_size: Vec<usize>,
12    vertex_count: u32
13}
14
15/// 2d vertex with position and colour
16#[repr(C)]
17struct ImDrawVertex2d {
18    _position: [f32; 2],
19    _color: [f32; 4],
20}
21
22/// 3d vertex with position and colour
23#[repr(C)]
24struct ImDrawVertex3d {
25    _position: [f32; 3],
26    _color: [f32; 4],
27}
28
29/// Information to create an instance of ImDraw
30pub struct ImDrawInfo {
31    pub initial_buffer_size_2d: usize,
32    pub initial_buffer_size_3d: usize
33}
34
35/// Immediate mode primitive drawing API struct
36pub struct ImDraw<D: gfx::Device> {
37    vertices_2d: DynamicBuffer<D>,
38    vertices_3d: DynamicBuffer<D>
39}
40
41/// Immediate mode primitive drawing API implementation
42impl<D> ImDraw<D> where D: gfx::Device {
43    fn new_buffer_2d_info(num_elements: usize) -> gfx::BufferInfo {
44        gfx::BufferInfo {
45            usage: gfx::BufferUsage::VERTEX,
46            cpu_access: gfx::CpuAccessFlags::WRITE,
47            format: gfx::Format::Unknown,
48            stride: std::mem::size_of::<ImDrawVertex2d>(),
49            num_elements,
50            initial_state: gfx::ResourceState::VertexConstantBuffer
51        }
52    }
53
54    fn new_buffer_3d_info(num_elements: usize) -> gfx::BufferInfo {
55        gfx::BufferInfo {
56            usage: gfx::BufferUsage::VERTEX,
57            cpu_access: gfx::CpuAccessFlags::WRITE,
58            format: gfx::Format::Unknown,
59            stride: std::mem::size_of::<ImDrawVertex3d>(),
60            num_elements,
61            initial_state: gfx::ResourceState::VertexConstantBuffer
62        }
63    }
64
65    pub fn create(info: &ImDrawInfo) -> Result<Self, super::Error> {
66        Ok(ImDraw {
67            vertices_2d: DynamicBuffer {
68                cpu_data: Vec::with_capacity(info.initial_buffer_size_2d),
69                gpu_data: Vec::new(),
70                gpu_data_size: Vec::new(),
71                vertex_count: 0
72            },
73            vertices_3d: DynamicBuffer {
74                cpu_data: Vec::with_capacity(info.initial_buffer_size_3d),
75                gpu_data: Vec::new(),
76                gpu_data_size: Vec::new(),
77                vertex_count: 0
78            },
79        })
80    }
81
82    pub fn add_vertex_2d(&mut self, v: Vec2f, col: Vec4f) {
83        // push position
84        for i in 0..2 {
85            self.vertices_2d.cpu_data.push(v[i])
86        }
87        // push colour
88        for i in 0..4 {
89            self.vertices_2d.cpu_data.push(col[i])
90        }
91    }
92
93    pub fn add_line_2d(&mut self, start: Vec2f, end: Vec2f, col: Vec4f) {
94        self.add_vertex_2d(start, col);
95        self.add_vertex_2d(end, col);
96    }
97
98    pub fn add_tri_2d(&mut self, p1: Vec2f, p2: Vec2f, p3: Vec2f, col: Vec4f) {
99        // edge 1
100        self.add_vertex_2d(p1, col);
101        self.add_vertex_2d(p2, col);
102        // edge 2
103        self.add_vertex_2d(p2, col);
104        self.add_vertex_2d(p3, col);
105        // edge 3
106        self.add_vertex_2d(p3, col);
107        self.add_vertex_2d(p1, col);
108    }
109
110    pub fn add_rect_2d(&mut self, p: Vec2f, size: Vec2f, col: Vec4f) {
111        let p1 = p + Vec2f::new(size.x, 0.0);
112        let p2 = p + size;
113        let p3 = p + Vec2f::new(0.0, size.y);
114        // edge 1
115        self.add_vertex_2d(p, col);
116        self.add_vertex_2d(p1, col);
117        // edge 2
118        self.add_vertex_2d(p1, col);
119        self.add_vertex_2d(p2, col);
120        // edge 3
121        self.add_vertex_2d(p2, col);
122        self.add_vertex_2d(p3, col);
123        // edge 4
124        self.add_vertex_2d(p3, col);
125        self.add_vertex_2d(p, col);
126    }
127
128    pub fn add_vertex_3d(&mut self, v: Vec3f, col: Vec4f) {
129        // push position
130        for i in 0..3 {
131            self.vertices_3d.cpu_data.push(v[i])
132        }
133        // push colour
134        for i in 0..4 {
135            self.vertices_3d.cpu_data.push(col[i])
136        }
137    }
138
139    pub fn add_line_3d(&mut self, start: Vec3f, end: Vec3f, col: Vec4f) {
140        self.add_vertex_3d(start, col);
141        self.add_vertex_3d(end, col);
142    }
143
144    pub fn add_point_3d(&mut self, pos: Vec3f, size: f32, col: Vec4f) {
145        self.add_line_3d(pos - Vec3f::unit_x() * size, pos + Vec3f::unit_x() * size, col);
146        self.add_line_3d(pos - Vec3f::unit_y() * size, pos + Vec3f::unit_y() * size, col);
147        self.add_line_3d(pos - Vec3f::unit_z() * size, pos + Vec3f::unit_z() * size, col);
148    }
149
150    pub fn add_circle_3d_xz(&mut self, pos: Vec3f, radius: f32, col: Vec4f) {
151        let segs = 16;
152        let step = (f32::pi() * 2.0) / segs as f32;
153        for i in 0..16 {
154            let ix = i as f32 * step;
155            let iy = (i + 1) as f32 * step;
156            self.add_line_3d(pos + Vec3f::new(f32::sin(ix), 0.0, f32::cos(ix)) * radius, 
157                pos + Vec3f::new(f32::sin(iy), 0.0, f32::cos(iy)) * radius, col);
158        }
159    }
160
161    /// Add a 3D aabb from `aabb_min` to `aabb_max` with designated colour `col`
162    pub fn add_aabb_3d(&mut self, aabb_min: Vec3f, aabb_max: Vec3f, col: Vec4f) {
163        self.add_line_3d(vec3f(aabb_min.x, aabb_min.y, aabb_min.z), vec3f(aabb_max.x, aabb_min.y, aabb_min.z), col);
164        self.add_line_3d(vec3f(aabb_min.x, aabb_min.y, aabb_min.z), vec3f(aabb_min.x, aabb_min.y, aabb_max.z), col);
165        self.add_line_3d(vec3f(aabb_min.x, aabb_min.y, aabb_max.z), vec3f(aabb_max.x, aabb_min.y, aabb_max.z), col);
166        self.add_line_3d(vec3f(aabb_max.x, aabb_min.y, aabb_max.z), vec3f(aabb_max.x, aabb_min.y, aabb_min.z), col);
167        self.add_line_3d(vec3f(aabb_min.x, aabb_max.y, aabb_min.z), vec3f(aabb_max.x, aabb_max.y, aabb_min.z), col);
168        self.add_line_3d(vec3f(aabb_min.x, aabb_max.y, aabb_min.z), vec3f(aabb_min.x, aabb_max.y, aabb_max.z), col);
169        self.add_line_3d(vec3f(aabb_min.x, aabb_max.y, aabb_max.z), vec3f(aabb_max.x, aabb_max.y, aabb_max.z), col);
170        self.add_line_3d(vec3f(aabb_max.x, aabb_max.y, aabb_max.z), vec3f(aabb_max.x, aabb_max.y, aabb_min.z), col);
171        self.add_line_3d(vec3f(aabb_min.x, aabb_min.y, aabb_min.z), vec3f(aabb_min.x, aabb_max.y, aabb_min.z), col);
172        self.add_line_3d(vec3f(aabb_max.x, aabb_min.y, aabb_min.z), vec3f(aabb_max.x, aabb_max.y, aabb_min.z), col);
173        self.add_line_3d(vec3f(aabb_max.x, aabb_min.y, aabb_max.z), vec3f(aabb_max.x, aabb_max.y, aabb_max.z), col);
174        self.add_line_3d(vec3f(aabb_min.x, aabb_min.y, aabb_max.z), vec3f(aabb_min.x, aabb_max.y, aabb_max.z), col);
175    }
176
177    /// Add a 3D obb from corners `obb` where the corners are formed of 0-3 front face, 4-7 back-face with designated colour `col`
178    pub fn add_obb_3d(&mut self, obb: Vec<Vec3f>, col: Vec4f) {
179        self.add_line_3d(vec3f(obb[0].x, obb[0].y, obb[0].z), vec3f(obb[1].x, obb[1].y, obb[1].z), col);
180        self.add_line_3d(vec3f(obb[1].x, obb[1].y, obb[1].z), vec3f(obb[2].x, obb[2].y, obb[2].z), col);
181        self.add_line_3d(vec3f(obb[2].x, obb[2].y, obb[2].z), vec3f(obb[3].x, obb[3].y, obb[3].z), col);
182        self.add_line_3d(vec3f(obb[3].x, obb[3].y, obb[3].z), vec3f(obb[0].x, obb[0].y, obb[0].z), col);
183        self.add_line_3d(vec3f(obb[4].x, obb[4].y, obb[4].z), vec3f(obb[5].x, obb[5].y, obb[5].z), col);
184        self.add_line_3d(vec3f(obb[5].x, obb[5].y, obb[5].z), vec3f(obb[6].x, obb[6].y, obb[6].z), col);
185        self.add_line_3d(vec3f(obb[6].x, obb[6].y, obb[6].z), vec3f(obb[7].x, obb[7].y, obb[7].z), col);
186        self.add_line_3d(vec3f(obb[7].x, obb[7].y, obb[7].z), vec3f(obb[4].x, obb[4].y, obb[4].z), col);
187        self.add_line_3d(vec3f(obb[4].x, obb[4].y, obb[4].z), vec3f(obb[0].x, obb[0].y, obb[0].z), col);
188        self.add_line_3d(vec3f(obb[5].x, obb[5].y, obb[5].z), vec3f(obb[1].x, obb[1].y, obb[1].z), col);
189        self.add_line_3d(vec3f(obb[6].x, obb[6].y, obb[6].z), vec3f(obb[2].x, obb[2].y, obb[2].z), col);
190        self.add_line_3d(vec3f(obb[7].x, obb[7].y, obb[7].z), vec3f(obb[3].x, obb[3].y, obb[3].z), col);
191    }
192
193    pub fn add_frustum(&mut self, view_proj: Mat4f, col: Vec4f) {
194        let corners = view_proj.get_frustum_corners();
195        let mut obb = Vec::new();
196        for c in corners {
197            obb.push(c);
198        }
199
200        self.add_line_3d(obb[0], obb[1], col);
201        self.add_line_3d(obb[1], obb[3], col);
202        self.add_line_3d(obb[3], obb[2], col);
203        self.add_line_3d(obb[2], obb[0], col);
204
205        self.add_line_3d(obb[4], obb[5], col);
206        self.add_line_3d(obb[5], obb[7], col);
207        self.add_line_3d(obb[7], obb[6], col);
208        self.add_line_3d(obb[6], obb[4], col);
209        
210        self.add_line_3d(obb[0], obb[4], col);
211        self.add_line_3d(obb[1], obb[5], col);
212        self.add_line_3d(obb[2], obb[6], col);
213        self.add_line_3d(obb[3], obb[7], col);
214    }
215
216    pub fn submit(&mut self, device: &mut D, buffer_index: usize) -> Result<(), super::Error> {
217        if !self.vertices_2d.cpu_data.is_empty() {
218            let num_elems = self.vertices_2d.cpu_data.len() / 6;
219            while buffer_index >= self.vertices_2d.gpu_data.len() {
220                // push a new buffer
221                self.vertices_2d.gpu_data.push(
222                    device.create_buffer::<u8>(&Self::new_buffer_2d_info(num_elems), None)?
223                );
224                self.vertices_2d.gpu_data_size.push(num_elems);
225            }
226            if num_elems > self.vertices_2d.gpu_data_size[buffer_index] {
227                // resize buffer
228                self.vertices_2d.gpu_data[buffer_index] = device.create_buffer::<u8>(
229                    &Self::new_buffer_2d_info(num_elems), None)?;
230            }
231            // update buffer
232            self.vertices_2d.gpu_data[buffer_index].update(0, self.vertices_2d.cpu_data.as_slice())?;
233            self.vertices_2d.gpu_data_size[buffer_index] = num_elems;
234            self.vertices_2d.vertex_count = num_elems as u32;
235            self.vertices_2d.cpu_data.clear();
236        }
237        if !self.vertices_3d.cpu_data.is_empty() {
238            let num_elems = self.vertices_3d.cpu_data.len() / 7;
239            while buffer_index >= self.vertices_3d.gpu_data.len() {
240                // push a new buffer
241                self.vertices_3d.gpu_data.push(
242                    device.create_buffer::<u8>(&Self::new_buffer_3d_info(num_elems), None)?
243                );
244                self.vertices_3d.gpu_data_size.push(num_elems);
245            }
246            if num_elems > self.vertices_3d.gpu_data_size[buffer_index] {
247                // resize buffer
248                self.vertices_3d.gpu_data[buffer_index] = device.create_buffer::<u8>(
249                    &Self::new_buffer_3d_info(num_elems), None)?;
250            }
251            // update buffer
252            self.vertices_3d.gpu_data[buffer_index].update(0, self.vertices_3d.cpu_data.as_slice())?;
253            self.vertices_3d.gpu_data_size[buffer_index] = num_elems;
254            self.vertices_3d.vertex_count = num_elems as u32;
255            self.vertices_3d.cpu_data.clear();
256        }
257        Ok(())     
258    }
259
260    pub fn draw_2d(&mut self, cmd: &D::CmdBuf, buffer_index: usize) {
261        if buffer_index < self.vertices_2d.gpu_data.len() {
262            cmd.set_vertex_buffer(&self.vertices_2d.gpu_data[buffer_index], 0);
263            cmd.draw_instanced(self.vertices_2d.vertex_count, 1, 0, 0);
264        }
265    }
266
267    pub fn draw_3d(&mut self, cmd: &D::CmdBuf, buffer_index: usize) {
268        if buffer_index < self.vertices_3d.gpu_data.len() {
269            cmd.set_vertex_buffer(&self.vertices_3d.gpu_data[buffer_index], 0);
270            cmd.draw_instanced(self.vertices_3d.vertex_count, 1, 0, 0);
271        }
272    }
273}