1use std::borrow::Cow;
2
3use crate::opengl_program::ShaderVersion;
4use fast3d::output::{ShaderConfig, ShaderId};
5use fast3d::{
6 output::{
7 gfx::{BlendComponent, BlendFactor, BlendOperation, BlendState, CompareFunction, Face},
8 models::{OutputFogParams, OutputSampler, OutputStencil, OutputTexture, OutputUniforms},
9 },
10 RenderData,
11};
12use fast3d_gbi::defines::WrapMode;
13use glam::Vec4Swizzles;
14use glium::buffer::{Buffer, BufferAny, BufferMode, BufferType};
15use glium::{
16 draw_parameters::{DepthClamp, PolygonOffset},
17 implement_uniform_block, implement_vertex,
18 index::{NoIndices, PrimitiveType},
19 program::ProgramCreationInput,
20 texture::{RawImage2d, Texture2d},
21 uniforms::{
22 MagnifySamplerFilter, MinifySamplerFilter, SamplerBehavior, SamplerWrapFunction,
23 UniformValue, Uniforms,
24 },
25 vertex::{AttributeType, VertexBufferAny},
26 BackfaceCullingMode, BlendingFunction, DepthTest, Display, DrawParameters, Frame,
27 LinearBlendingFactor, Program, Surface, VertexBuffer,
28};
29
30use super::opengl_program::OpenGLProgram;
31
32struct TextureData {
33 texture: Texture2d,
34 sampler: Option<SamplerBehavior>,
35}
36
37impl TextureData {
38 pub fn new(texture: Texture2d) -> Self {
39 Self {
40 texture,
41 sampler: None,
42 }
43 }
44}
45
46#[repr(C)]
47#[derive(Copy, Clone)]
48struct VertexUniforms {
49 screen_size: [f32; 2],
50 _padding: [f32; 2],
51 projection: [[f32; 4]; 4],
52}
53
54impl VertexUniforms {
55 pub fn new(screen_size: [f32; 2], projection: [[f32; 4]; 4]) -> Self {
56 Self {
57 screen_size,
58 _padding: [0.0; 2],
59 projection,
60 }
61 }
62}
63
64implement_uniform_block!(VertexUniforms, screen_size, projection);
65
66#[repr(C)]
67#[derive(Copy, Clone)]
68struct VertexWithFogUniforms {
69 screen_size: [f32; 2],
70 _padding: [f32; 2],
71 projection: [[f32; 4]; 4],
72 fog_multiplier: f32,
73 fog_offset: f32,
74}
75
76impl VertexWithFogUniforms {
77 pub fn new(
78 screen_size: [f32; 2],
79 projection: [[f32; 4]; 4],
80 fog_multiplier: f32,
81 fog_offset: f32,
82 ) -> Self {
83 Self {
84 screen_size,
85 _padding: [0.0; 2],
86 projection,
87 fog_multiplier,
88 fog_offset,
89 }
90 }
91}
92
93implement_uniform_block!(
94 VertexWithFogUniforms,
95 screen_size,
96 projection,
97 fog_multiplier,
98 fog_offset
99);
100
101#[repr(C)]
102#[derive(Copy, Clone)]
103struct BlendUniforms {
104 blend_color: [f32; 4],
105}
106
107implement_uniform_block!(BlendUniforms, blend_color);
108
109#[repr(C)]
110#[derive(Copy, Clone)]
111struct BlendWithFogUniforms {
112 blend_color: [f32; 4],
113 fog_color: [f32; 3],
114 _padding: f32,
115}
116
117implement_uniform_block!(BlendWithFogUniforms, blend_color, fog_color);
118
119#[repr(C)]
120#[derive(Copy, Clone)]
121struct CombineUniforms {
122 prim_color: [f32; 4],
123 env_color: [f32; 4],
124 key_center: [f32; 3],
125 _padding: f32,
126 key_scale: [f32; 3],
127 _padding2: f32,
128 prim_lod_frac: f32,
129 uk4: f32,
130 uk5: f32,
131}
132
133implement_uniform_block!(
134 CombineUniforms,
135 prim_color,
136 env_color,
137 key_center,
138 key_scale,
139 prim_lod_frac,
140 uk4,
141 uk5
142);
143
144#[repr(C)]
145#[derive(Copy, Clone)]
146struct FrameUniforms {
147 frame_count: i32,
148 frame_height: i32,
149}
150
151implement_uniform_block!(FrameUniforms, frame_count, frame_height);
152
153#[repr(C)]
154#[derive(Copy, Clone)]
155struct Vertex {
156 position: [f32; 4],
157 color: [f32; 4],
158}
159
160implement_vertex!(Vertex, position location(0), color location(1));
161
162#[repr(C)]
163#[derive(Copy, Clone)]
164struct VertexWithTexture {
165 position: [f32; 4],
166 color: [f32; 4],
167 tex_coord: [f32; 2],
168}
169
170implement_vertex!(VertexWithTexture, position location(0), color location(1), tex_coord location(2));
171
172#[derive(Default)]
173struct UniformVec<'a, 'b> {
174 pub uniforms: Vec<(&'a str, UniformValue<'b>)>,
175}
176
177impl Uniforms for UniformVec<'_, '_> {
178 fn visit_values<'a, F: FnMut(&str, UniformValue<'a>)>(&'a self, mut func: F) {
179 for uniform in &self.uniforms {
180 func(uniform.0, uniform.1);
181 }
182 }
183}
184
185pub struct GliumRenderer<'a> {
186 pub shader_cache: rustc_hash::FxHashMap<ShaderId, OpenGLProgram<Program>>,
187 current_shader: Option<ShaderId>,
188
189 textures: Vec<TextureData>,
190 active_texture: usize,
191 current_texture_ids: [usize; 2],
192
193 frame_count: i32,
194 current_height: i32,
195 screen_size: [u32; 2],
196
197 draw_params: DrawParameters<'a>,
198}
199
200fn blend_component_to_glium(component: BlendComponent) -> BlendingFunction {
201 match component.operation {
202 BlendOperation::Add => BlendingFunction::Addition {
203 source: blend_factor_to_glium(component.src_factor),
204 destination: blend_factor_to_glium(component.dst_factor),
205 },
206 BlendOperation::Subtract => BlendingFunction::Subtraction {
207 source: blend_factor_to_glium(component.src_factor),
208 destination: blend_factor_to_glium(component.dst_factor),
209 },
210 BlendOperation::ReverseSubtract => BlendingFunction::ReverseSubtraction {
211 source: blend_factor_to_glium(component.src_factor),
212 destination: blend_factor_to_glium(component.dst_factor),
213 },
214 BlendOperation::Min => BlendingFunction::Min,
215 BlendOperation::Max => BlendingFunction::Max,
216 }
217}
218
219fn blend_factor_to_glium(factor: BlendFactor) -> LinearBlendingFactor {
220 match factor {
221 BlendFactor::Zero => LinearBlendingFactor::Zero,
222 BlendFactor::One => LinearBlendingFactor::One,
223 BlendFactor::Src => LinearBlendingFactor::SourceColor,
224 BlendFactor::OneMinusSrc => LinearBlendingFactor::OneMinusSourceColor,
225 BlendFactor::SrcAlpha => LinearBlendingFactor::SourceAlpha,
226 BlendFactor::OneMinusSrcAlpha => LinearBlendingFactor::OneMinusSourceAlpha,
227 BlendFactor::Dst => LinearBlendingFactor::DestinationColor,
228 BlendFactor::OneMinusDst => LinearBlendingFactor::OneMinusDestinationColor,
229 BlendFactor::DstAlpha => LinearBlendingFactor::DestinationAlpha,
230 BlendFactor::OneMinusDstAlpha => LinearBlendingFactor::OneMinusDestinationAlpha,
231 BlendFactor::SrcAlphaSaturated => LinearBlendingFactor::SourceAlphaSaturate,
232 BlendFactor::Constant => LinearBlendingFactor::ConstantColor,
233 BlendFactor::OneMinusConstant => LinearBlendingFactor::OneMinusConstantColor,
234 }
235}
236
237fn clamp_to_glium(clamp: WrapMode) -> SamplerWrapFunction {
238 if clamp == WrapMode::Clamp {
239 return SamplerWrapFunction::Clamp;
240 } else if clamp == WrapMode::MirrorRepeat {
241 return SamplerWrapFunction::Mirror;
242 }
243
244 SamplerWrapFunction::Repeat
245}
246
247impl<'a> GliumRenderer<'a> {
248 pub fn new(screen_size: [u32; 2]) -> Self {
249 Self {
250 shader_cache: rustc_hash::FxHashMap::default(),
251 current_shader: None,
252
253 textures: Vec::new(),
254 active_texture: 0,
255 current_texture_ids: [0; 2],
256
257 frame_count: 0,
258 current_height: 0,
259 screen_size,
260
261 draw_params: DrawParameters {
262 ..Default::default()
263 },
264 }
265 }
266
267 pub fn start_frame(&mut self, target: &mut Frame) {
270 self.frame_count += 1;
271
272 target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
273
274 self.draw_params = DrawParameters {
275 ..Default::default()
276 };
277 }
278
279 pub fn resize(&mut self, screen_size: [u32; 2]) {
284 self.screen_size = screen_size;
285 }
286
287 pub fn render_rcp_output(
289 &mut self,
290 render_data: &mut RenderData,
291 display: &Display,
292 frame: &mut Frame,
293 ) {
294 for draw_call in render_data
297 .draw_calls
298 .iter()
299 .take(render_data.draw_calls.len() - 1)
300 {
301 assert!(!draw_call.vbo.vbo.is_empty());
302
303 self.set_cull_mode(draw_call.cull_mode);
304 self.set_depth_stencil_params(draw_call.stencil);
305 self.set_blend_state(draw_call.blend_state);
306 self.set_viewport(&draw_call.viewport);
307 self.set_scissor(draw_call.scissor);
308
309 self.select_program(display, draw_call.shader_id, draw_call.shader_config);
311
312 for (index, hash) in draw_call.texture_indices.iter().enumerate() {
314 if let Some(hash) = hash {
315 let texture = render_data.texture_cache.get_mut(*hash).unwrap();
316 self.bind_texture(display, index, texture);
317 }
318 }
319
320 for (index, sampler) in draw_call.samplers.iter().enumerate() {
322 if let Some(sampler) = sampler {
323 self.bind_sampler(index, sampler);
324 }
325 }
326
327 self.draw_triangles(
329 display,
330 frame,
331 draw_call.projection_matrix,
332 &draw_call.fog,
333 &draw_call.vbo.vbo,
334 draw_call.vbo.num_tris,
335 &draw_call.uniforms,
336 );
337 }
338 }
339
340 fn set_cull_mode(&mut self, cull_mode: Option<Face>) {
343 self.draw_params.backface_culling = match cull_mode {
344 Some(Face::Front) => BackfaceCullingMode::CullCounterClockwise,
345 Some(Face::Back) => BackfaceCullingMode::CullClockwise,
346 None => BackfaceCullingMode::CullingDisabled,
347 }
348 }
349
350 fn set_depth_stencil_params(&mut self, params: Option<OutputStencil>) {
351 self.draw_params.depth = if let Some(params) = params {
352 glium::Depth {
353 test: match params.depth_compare {
354 CompareFunction::Never => DepthTest::Ignore,
355 CompareFunction::Less => DepthTest::IfLess,
356 CompareFunction::Equal => DepthTest::IfEqual,
357 CompareFunction::LessEqual => DepthTest::IfLessOrEqual,
358 CompareFunction::Greater => DepthTest::IfMore,
359 CompareFunction::NotEqual => DepthTest::IfNotEqual,
360 CompareFunction::GreaterEqual => DepthTest::IfMoreOrEqual,
361 CompareFunction::Always => DepthTest::Overwrite,
362 },
363 write: params.depth_write_enabled,
364 clamp: DepthClamp::Clamp,
365 ..Default::default()
366 }
367 } else {
368 glium::Depth {
369 clamp: DepthClamp::Clamp,
370 ..Default::default()
371 }
372 };
373
374 self.draw_params.polygon_offset = if let Some(params) = params {
375 PolygonOffset {
376 factor: if params.polygon_offset { -2.0 } else { 0.0 },
377 units: if params.polygon_offset { 2.0 } else { 0.0 },
378 fill: true,
379 ..Default::default()
380 }
381 } else {
382 PolygonOffset {
383 ..Default::default()
384 }
385 };
386 }
387
388 fn set_blend_state(&mut self, blend_state: Option<BlendState>) {
389 self.draw_params.blend = if let Some(blend_state) = blend_state {
390 glium::Blend {
391 color: blend_component_to_glium(blend_state.color),
392 alpha: blend_component_to_glium(blend_state.alpha),
393 ..Default::default()
394 }
395 } else {
396 glium::Blend {
397 ..Default::default()
398 }
399 };
400 }
401
402 fn set_viewport(&mut self, viewport: &glam::Vec4) {
403 self.draw_params.viewport = Some(glium::Rect {
404 left: viewport.x as u32,
405 bottom: viewport.y as u32,
406 width: viewport.z as u32,
407 height: viewport.w as u32,
408 });
409
410 self.current_height = viewport.w as i32;
411 }
412
413 fn set_scissor(&mut self, scissor: [u32; 4]) {
414 self.draw_params.scissor = Some(glium::Rect {
415 left: scissor[0],
416 bottom: scissor[1],
417 width: scissor[2],
418 height: scissor[3],
419 });
420 }
421
422 fn select_program(
423 &mut self,
424 display: &Display,
425 shader_id: ShaderId,
426 shader_config: ShaderConfig,
427 ) {
428 if self.current_shader == Some(shader_id) {
430 return;
431 }
432
433 if self.current_shader.is_some() {
435 self.current_shader = None;
436 }
437
438 if self.shader_cache.contains_key(&shader_id) {
440 self.current_shader = Some(shader_id);
441 return;
442 }
443
444 let mut program = OpenGLProgram::new(shader_config);
446 program.init();
447 program.preprocess(&ShaderVersion::GLSL410); let source = ProgramCreationInput::SourceCode {
450 vertex_shader: &program.preprocessed_vertex,
451 fragment_shader: &program.preprocessed_frag,
452 geometry_shader: None,
453 tessellation_control_shader: None,
454 tessellation_evaluation_shader: None,
455 transform_feedback_varyings: None,
456 outputs_srgb: true, uses_point_size: false,
458 };
459
460 program.compiled_program = Some(Program::new(display, source).unwrap());
461
462 self.current_shader = Some(shader_id);
463 self.shader_cache.insert(shader_id, program);
464 }
465
466 fn bind_texture(&mut self, display: &Display, tile: usize, texture: &mut OutputTexture) {
467 if let Some(texture_id) = texture.device_id {
469 self.active_texture = tile;
471 self.current_texture_ids[tile] = texture_id as usize;
472
473 return;
474 }
475
476 let raw_texture =
478 RawImage2d::from_raw_rgba(texture.data.clone(), (texture.width, texture.height));
479 let native_texture = Texture2d::new(display, raw_texture).unwrap();
480
481 self.active_texture = tile;
482 self.current_texture_ids[tile] = self.textures.len();
483 texture.device_id = Some(self.textures.len() as u32);
484
485 self.textures.push(TextureData::new(native_texture));
486 }
487
488 fn bind_sampler(&mut self, tile: usize, sampler: &OutputSampler) {
489 if let Some(texture_data) = self.textures.get_mut(self.current_texture_ids[tile]) {
490 let wrap_s = clamp_to_glium(sampler.clamp_s);
491 let wrap_t = clamp_to_glium(sampler.clamp_t);
492
493 let native_sampler = SamplerBehavior {
494 minify_filter: if sampler.linear_filter {
495 MinifySamplerFilter::Linear
496 } else {
497 MinifySamplerFilter::Nearest
498 },
499 magnify_filter: if sampler.linear_filter {
500 MagnifySamplerFilter::Linear
501 } else {
502 MagnifySamplerFilter::Nearest
503 },
504 wrap_function: (wrap_s, wrap_t, SamplerWrapFunction::Repeat),
505 ..Default::default()
506 };
507
508 texture_data.sampler = Some(native_sampler);
509 }
510 }
511
512 #[allow(clippy::too_many_arguments)]
513 fn draw_triangles(
514 &self,
515 display: &Display,
516 target: &mut Frame,
517 projection_matrix: glam::Mat4,
518 fog: &OutputFogParams,
519 vbo: &[u8],
520 num_tris: usize,
521 uniforms: &OutputUniforms,
522 ) {
523 let program = self
525 .shader_cache
526 .get(&self.current_shader.unwrap())
527 .unwrap();
528
529 let mut vertex_format_data = vec![
531 (
532 Cow::Borrowed("aVtxPos"),
533 0,
534 -1,
535 AttributeType::F32F32F32F32,
536 false,
537 ),
538 (
539 Cow::Borrowed("aVtxColor"),
540 4 * ::std::mem::size_of::<f32>(),
541 -1,
542 AttributeType::F32F32F32F32,
543 false,
544 ),
545 ];
546
547 if program.get_define_bool("USE_TEXTURE0") || program.get_define_bool("USE_TEXTURE1") {
548 vertex_format_data.push((
549 Cow::Borrowed("aTexCoord"),
550 8 * ::std::mem::size_of::<f32>(),
551 -1,
552 AttributeType::F32F32,
553 false,
554 ));
555 }
556
557 let vertex_buffer = if program.get_define_bool("USE_TEXTURE0")
558 || program.get_define_bool("USE_TEXTURE1")
559 {
560 let vertex_array = unsafe {
561 std::slice::from_raw_parts(vbo.as_ptr() as *const VertexWithTexture, num_tris * 3)
562 };
563 let buffer = VertexBuffer::new(display, vertex_array).unwrap();
564 VertexBufferAny::from(buffer)
565 } else {
566 let vertex_array =
567 unsafe { std::slice::from_raw_parts(vbo.as_ptr() as *const Vertex, num_tris * 3) };
568 let buffer = VertexBuffer::new(display, vertex_array).unwrap();
569 VertexBufferAny::from(buffer)
570 };
571
572 let vtx_uniform_buf = if program.get_define_bool("USE_FOG") {
575 let data = VertexWithFogUniforms::new(
576 [self.screen_size[0] as f32, self.screen_size[1] as f32],
577 projection_matrix.to_cols_array_2d(),
578 fog.multiplier as f32,
579 fog.offset as f32,
580 );
581
582 let buffer = Buffer::new(
583 display,
584 &data,
585 BufferType::UniformBuffer,
586 BufferMode::Default,
587 )
588 .unwrap();
589 BufferAny::from(buffer)
590 } else {
591 let data = VertexUniforms::new(
592 [self.screen_size[0] as f32, self.screen_size[1] as f32],
593 projection_matrix.to_cols_array_2d(),
594 );
595
596 let buffer = Buffer::new(
597 display,
598 &data,
599 BufferType::UniformBuffer,
600 BufferMode::Default,
601 )
602 .unwrap();
603 BufferAny::from(buffer)
604 };
605
606 let blend_uniform_buf = if program.get_define_bool("USE_FOG") {
607 let data = BlendWithFogUniforms {
608 blend_color: uniforms.blend.blend_color.to_array(),
609 fog_color: uniforms.blend.fog_color.xyz().to_array(),
610 _padding: 0.0,
611 };
612
613 let buffer = Buffer::new(
614 display,
615 &data,
616 BufferType::UniformBuffer,
617 BufferMode::Default,
618 )
619 .unwrap();
620 BufferAny::from(buffer)
621 } else {
622 let data = BlendUniforms {
623 blend_color: uniforms.blend.blend_color.to_array(),
624 };
625
626 let buffer = Buffer::new(
627 display,
628 &data,
629 BufferType::UniformBuffer,
630 BufferMode::Default,
631 )
632 .unwrap();
633 BufferAny::from(buffer)
634 };
635
636 let combine_uniform_buf = {
637 let data = CombineUniforms {
638 prim_color: uniforms.combine.prim_color.to_array(),
639 env_color: uniforms.combine.env_color.to_array(),
640 _padding: 0.0,
641 key_center: uniforms.combine.key_center.to_array(),
642 key_scale: uniforms.combine.key_scale.to_array(),
643 _padding2: 0.0,
644 prim_lod_frac: uniforms.combine.prim_lod.x,
645 uk4: uniforms.combine.convert_k4,
646 uk5: uniforms.combine.convert_k5,
647 };
648
649 let buffer = Buffer::new(
650 display,
651 &data,
652 BufferType::UniformBuffer,
653 BufferMode::Default,
654 )
655 .unwrap();
656 BufferAny::from(buffer)
657 };
658
659 let frame_uniform_buf = if program.get_define_bool("USE_ALPHA")
660 && program.get_define_bool("ALPHA_COMPARE_DITHER")
661 {
662 let data = FrameUniforms {
663 frame_count: self.frame_count,
664 frame_height: self.current_height,
665 };
666
667 Some(
668 Buffer::new(
669 display,
670 &data,
671 BufferType::UniformBuffer,
672 BufferMode::Default,
673 )
674 .unwrap(),
675 )
676 } else {
677 None
678 };
679
680 let mut shader_uniforms = vec![
682 (
683 "Uniforms",
684 UniformValue::Block(vtx_uniform_buf.as_slice_any(), |_block| Ok(())),
685 ),
686 (
687 "BlendUniforms",
688 UniformValue::Block(blend_uniform_buf.as_slice_any(), |_block| Ok(())),
689 ),
690 (
691 "CombineUniforms",
692 UniformValue::Block(combine_uniform_buf.as_slice_any(), |_block| Ok(())),
693 ),
694 ];
695
696 if program.get_define_bool("USE_TEXTURE0") {
697 let texture = self.textures.get(self.current_texture_ids[0]).unwrap();
698 shader_uniforms.push((
699 "uTex0",
700 UniformValue::Texture2d(&texture.texture, texture.sampler),
701 ));
702 }
703
704 if program.get_define_bool("USE_TEXTURE1") {
705 let texture = self.textures.get(self.current_texture_ids[1]).unwrap();
706 shader_uniforms.push((
707 "uTex1",
708 UniformValue::Texture2d(&texture.texture, texture.sampler),
709 ));
710 }
711
712 if program.get_define_bool("USE_ALPHA") && program.get_define_bool("ALPHA_COMPARE_DITHER") {
713 let frame_uniform_buf = frame_uniform_buf.as_ref();
714 shader_uniforms.push((
715 "FrameUniforms",
716 UniformValue::Block(frame_uniform_buf.unwrap().as_slice_any(), |_block| Ok(())),
717 ));
718 }
719
720 target
722 .draw(
723 &vertex_buffer,
724 NoIndices(PrimitiveType::TrianglesList),
725 program.compiled_program.as_ref().unwrap(),
726 &UniformVec {
727 uniforms: shader_uniforms,
728 },
729 &self.draw_params,
730 )
731 .unwrap();
732 }
733}