Skip to main content

base_ui/graphics/
renderer.rs

1use super::shader::Shader;
2use crate::text::TextRenderer;
3use nalgebra_glm as glm;
4
5pub struct Renderer {
6    shader: Shader,
7    texture_shader: Shader,
8    text_renderer: TextRenderer,
9    screen_width: f32,
10    screen_height: f32,
11    background_color: [f32; 4],
12    vao: u32,
13    vbo: u32,
14    circle_vertices: Vec<f32>, // 미리 계산된 원의 버텍스들
15}
16
17const CIRCLE_SEGMENTS: usize = 32;
18
19impl Renderer {
20    pub fn new(font_data: Vec<u8>) -> Self {
21        let vertex_source = include_str!("shaders/basic.vert");
22        let fragment_source = include_str!("shaders/basic.frag");
23        let shader = Shader::new(vertex_source, fragment_source);
24        let texture_shader = Shader::new(
25            include_str!("shaders/texture.vert"),
26            include_str!("shaders/texture.frag")
27        );
28        let text_renderer = TextRenderer::new(font_data);
29
30        let mut vao = 0;
31        let mut vbo = 0;
32
33        unsafe {
34            gl::GenVertexArrays(1, &mut vao);
35            gl::GenBuffers(1, &mut vbo);
36
37            gl::BindVertexArray(vao);
38            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
39
40            gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, 0, std::ptr::null());
41            gl::EnableVertexAttribArray(0);
42        }
43
44        // 원의 버텍스들을 미리 계산
45        let mut circle_vertices = Vec::with_capacity(CIRCLE_SEGMENTS * 3 * 3);
46        for i in 0..CIRCLE_SEGMENTS {
47            let angle = (2.0 * std::f32::consts::PI * (i as f32)) / (CIRCLE_SEGMENTS as f32);
48            let x = angle.cos();
49            let y = angle.sin();
50            circle_vertices.extend_from_slice(&[x, y, 0.0]);
51        }
52
53        Self {
54            shader,
55            texture_shader,
56            text_renderer,
57            screen_width: 0.0,
58            screen_height: 0.0,
59            background_color: [0.1, 0.35, 0.33, 1.0],
60            vao,
61            vbo,
62            circle_vertices,
63        }
64    }
65
66    pub fn set_background_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
67        self.background_color = [r, g, b, a];
68    }
69
70    /// 매 프레임마다 호출하여 전체 화면을 다시 그리는 메서드
71    /// (배경 지우기, 카메라/투영 매트릭스 설정 등)
72    pub fn render(&mut self, screen_width: f32, screen_height: f32) {
73        // 화면 크기 업데이트
74        self.screen_width = screen_width;
75        self.screen_height = screen_height;
76
77        unsafe {
78            // 설정된 배경색으로 화면 지우기
79            gl::ClearColor(
80                self.background_color[0],
81                self.background_color[1],
82                self.background_color[2],
83                self.background_color[3]
84            );
85            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
86        }
87
88        // 셰이더 사용
89        self.shader.use_program();
90
91        // 예: Orthographic 투영 행렬을 만들고 셰이더 uniform에 설정
92        // (glm 사용 시 예시)
93        let projection = glm::ortho(0.0, screen_width, screen_height, 0.0, -1.0, 1.0);
94        self.shader.set_mat4("projection", &projection);
95    }
96
97    /// 텍스트 렌더링 객체에 접근 (폰트 그리기 등)
98    pub fn text_renderer(&self) -> &TextRenderer {
99        &self.text_renderer
100    }
101
102    /// Draws a filled rectangle at (x, y) with given width, height and color.
103    /// Assumes that self.screen_width and self.screen_height are updated.
104    pub fn draw_rect(&mut self, x: f32, y: f32, width: f32, height: f32, color: [f32; 4]) {
105        // 현재 바인딩된 shader를 저장
106        let current_shader = unsafe {
107            let mut current_program = 0;
108            gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
109            current_program
110        };
111
112        // 기본 셰이더로 전환하여 사각형 그리기
113        self.shader.use_program();
114        self.shader.set_vec4("uColor", &color);
115
116        // Convert screen coordinates to normalized device coordinates (NDC)
117        let x_ndc = (x / self.screen_width) * 2.0 - 1.0;
118        let y_ndc = 1.0 - (y / self.screen_height) * 2.0;
119        let x2_ndc = ((x + width) / self.screen_width) * 2.0 - 1.0;
120        let y2_ndc = 1.0 - ((y + height) / self.screen_height) * 2.0;
121
122        let vertices: [f32; 12] = [
123            x_ndc,
124            y_ndc,
125            0.0, // top-left
126            x2_ndc,
127            y_ndc,
128            0.0, // top-right
129            x2_ndc,
130            y2_ndc,
131            0.0, // bottom-right
132            x_ndc,
133            y2_ndc,
134            0.0, // bottom-left
135        ];
136        let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
137
138        unsafe {
139            let mut vao = 0;
140            let mut vbo = 0;
141            let mut ebo = 0;
142
143            gl::GenVertexArrays(1, &mut vao);
144            gl::GenBuffers(1, &mut vbo);
145            gl::GenBuffers(1, &mut ebo);
146
147            gl::BindVertexArray(vao);
148
149            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
150            gl::BufferData(
151                gl::ARRAY_BUFFER,
152                (vertices.len() * std::mem::size_of::<f32>()) as isize,
153                vertices.as_ptr() as *const _,
154                gl::STATIC_DRAW
155            );
156
157            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
158            gl::BufferData(
159                gl::ELEMENT_ARRAY_BUFFER,
160                (indices.len() * std::mem::size_of::<u32>()) as isize,
161                indices.as_ptr() as *const _,
162                gl::STATIC_DRAW
163            );
164
165            gl::VertexAttribPointer(
166                0,
167                3,
168                gl::FLOAT,
169                gl::FALSE,
170                (3 * std::mem::size_of::<f32>()) as i32,
171                std::ptr::null()
172            );
173            gl::EnableVertexAttribArray(0);
174
175            gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
176
177            // Cleanup
178            gl::DeleteVertexArrays(1, &vao);
179            gl::DeleteBuffers(1, &vbo);
180            gl::DeleteBuffers(1, &ebo);
181
182            // 이전 shader로 복원
183            if current_shader != 0 {
184                gl::UseProgram(current_shader as u32);
185            }
186        }
187    }
188
189    /// Add a new method for mutable access to the text renderer.
190    pub fn text_renderer_mut(&mut self) -> &mut TextRenderer {
191        &mut self.text_renderer
192    }
193
194    pub fn draw_textured_rect(
195        &self,
196        x: f32,
197        y: f32,
198        width: f32,
199        height: f32,
200        texture_id: u32,
201        opacity: f32
202    ) {
203        self.texture_shader.use_program();
204
205        // Set projection matrix
206        let projection = glm::ortho(0.0, self.screen_width, self.screen_height, 0.0, -1.0, 1.0);
207        self.texture_shader.set_mat4("projection", &projection);
208        self.texture_shader.set_float("uOpacity", opacity);
209
210        unsafe {
211            gl::ActiveTexture(gl::TEXTURE0);
212            gl::BindTexture(gl::TEXTURE_2D, texture_id);
213
214            let vertices: [f32; 20] = [
215                x,
216                y,
217                0.0,
218                0.0,
219                1.0,
220                x + width,
221                y,
222                0.0,
223                1.0,
224                1.0,
225                x + width,
226                y + height,
227                0.0,
228                1.0,
229                0.0,
230                x,
231                y + height,
232                0.0,
233                0.0,
234                0.0,
235            ];
236
237            let indices: [u32; 6] = [0, 1, 2, 2, 3, 0];
238
239            let mut vao = 0;
240            let mut vbo = 0;
241            let mut ebo = 0;
242
243            gl::GenVertexArrays(1, &mut vao);
244            gl::GenBuffers(1, &mut vbo);
245            gl::GenBuffers(1, &mut ebo);
246
247            gl::BindVertexArray(vao);
248
249            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
250            gl::BufferData(
251                gl::ARRAY_BUFFER,
252                (vertices.len() * std::mem::size_of::<f32>()) as isize,
253                vertices.as_ptr() as *const _,
254                gl::STATIC_DRAW
255            );
256
257            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
258            gl::BufferData(
259                gl::ELEMENT_ARRAY_BUFFER,
260                (indices.len() * std::mem::size_of::<u32>()) as isize,
261                indices.as_ptr() as *const _,
262                gl::STATIC_DRAW
263            );
264
265            // Position attribute
266            gl::VertexAttribPointer(
267                0,
268                3,
269                gl::FLOAT,
270                gl::FALSE,
271                (5 * std::mem::size_of::<f32>()) as i32,
272                std::ptr::null()
273            );
274            gl::EnableVertexAttribArray(0);
275
276            // Texture coord attribute
277            gl::VertexAttribPointer(
278                1,
279                2,
280                gl::FLOAT,
281                gl::FALSE,
282                (5 * std::mem::size_of::<f32>()) as i32,
283                (3 * std::mem::size_of::<f32>()) as *const _
284            );
285            gl::EnableVertexAttribArray(1);
286
287            gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, std::ptr::null());
288
289            gl::DeleteVertexArrays(1, &vao);
290            gl::DeleteBuffers(1, &vbo);
291            gl::DeleteBuffers(1, &ebo);
292        }
293    }
294
295    pub fn draw_triangle(&mut self, vertices: [(f32, f32); 3], color: [f32; 4]) {
296        let current_shader = unsafe {
297            let mut current_program = 0;
298            gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
299            current_program
300        };
301
302        self.shader.use_program();
303        self.shader.set_vec4("uColor", &color);
304
305        // Convert to NDC
306        let vertices_ndc: Vec<f32> = vertices
307            .iter()
308            .flat_map(|(x, y)| {
309                let x_ndc = (x / self.screen_width) * 2.0 - 1.0;
310                let y_ndc = 1.0 - (y / self.screen_height) * 2.0;
311                vec![x_ndc, y_ndc, 0.0]
312            })
313            .collect();
314
315        unsafe {
316            gl::BindVertexArray(self.vao);
317            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
318            gl::BufferData(
319                gl::ARRAY_BUFFER,
320                (vertices_ndc.len() * std::mem::size_of::<f32>()) as isize,
321                vertices_ndc.as_ptr() as *const _,
322                gl::STATIC_DRAW
323            );
324            gl::DrawArrays(gl::TRIANGLES, 0, 3);
325        }
326
327        // 이전 셰이더로 복원
328        unsafe {
329            gl::UseProgram(current_shader as u32);
330        }
331    }
332
333    pub fn draw_circle(&mut self, center_x: f32, center_y: f32, radius: f32, color: [f32; 4]) {
334        let current_shader = unsafe {
335            let mut current_program = 0;
336            gl::GetIntegerv(gl::CURRENT_PROGRAM, &mut current_program);
337            current_program
338        };
339
340        self.shader.use_program();
341        self.shader.set_vec4("uColor", &color);
342
343        // 스케일과 위치 변환을 적용한 버텍스들 생성
344        let vertices_transformed: Vec<f32> = self.circle_vertices
345            .chunks(3)
346            .flat_map(|v| {
347                let x = center_x + v[0] * radius;
348                let y = center_y + v[1] * radius;
349                vec![(x / self.screen_width) * 2.0 - 1.0, 1.0 - (y / self.screen_height) * 2.0, 0.0]
350            })
351            .collect();
352
353        unsafe {
354            gl::BindVertexArray(self.vao);
355            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
356            gl::BufferData(
357                gl::ARRAY_BUFFER,
358                (vertices_transformed.len() * std::mem::size_of::<f32>()) as isize,
359                vertices_transformed.as_ptr() as *const _,
360                gl::STATIC_DRAW
361            );
362            gl::DrawArrays(gl::TRIANGLE_FAN, 0, CIRCLE_SEGMENTS as i32);
363        }
364
365        unsafe {
366            gl::UseProgram(current_shader as u32);
367        }
368    }
369}