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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
use crate::output::models::{
    OutputFogParams, OutputSampler, OutputStencil, OutputUniforms, OutputUniformsBlend,
    OutputUniformsCombine, OutputVBO,
};
use crate::output::texture_cache::TextureCacheId;
use fast3d_gbi::defines::color_combiner::CombineParams;
use fast3d_gbi::defines::{GeometryModes, WrapMode};
use std::hash::Hash;
use texture_cache::TextureCache;

use self::gfx::{BlendState, CompareFunction, Face};

pub mod gfx;
pub mod models;
pub mod texture_cache;

const TEXTURE_CACHE_MAX_SIZE: usize = 500;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ShaderId(pub ShaderConfig);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ShaderConfig {
    pub other_mode_h: u32,
    pub other_mode_l: u32,
    pub geometry_mode: GeometryModes,
    pub combine: CombineParams,
}

impl ShaderConfig {
    pub const EMPTY: Self = {
        Self {
            other_mode_h: 0,
            other_mode_l: 0,
            geometry_mode: GeometryModes::empty(),
            combine: CombineParams::ZERO,
        }
    };
}

#[derive(Debug, Clone)]
pub struct IntermediateDrawCall {
    // Shader Configuration
    pub shader_id: ShaderId,
    pub shader_config: ShaderConfig,

    // Textures
    pub texture_indices: [Option<TextureCacheId>; 2],

    // Samplers
    pub samplers: [Option<OutputSampler>; 2],

    // Stencil
    pub stencil: Option<OutputStencil>,

    // Viewport
    pub viewport: glam::Vec4,

    // Scissor
    pub scissor: [u32; 4],

    // Blend State
    pub blend_state: Option<BlendState>,

    // Cull Mode
    pub cull_mode: Option<Face>,

    // Uniforms
    pub uniforms: OutputUniforms,

    // Triangle Data
    pub vbo: OutputVBO,

    // Projection Matrix
    pub projection_matrix: glam::Mat4,

    // Fog Params
    pub fog: OutputFogParams,
}

impl IntermediateDrawCall {
    pub const EMPTY: Self = IntermediateDrawCall {
        shader_id: ShaderId(ShaderConfig::EMPTY),
        shader_config: ShaderConfig::EMPTY,
        texture_indices: [None; 2],
        samplers: [None; 2],
        stencil: None,
        viewport: glam::Vec4::ZERO,
        scissor: [0; 4],
        blend_state: None,
        cull_mode: None,
        uniforms: OutputUniforms::EMPTY,
        vbo: OutputVBO::EMPTY,
        projection_matrix: glam::Mat4::ZERO,
        fog: OutputFogParams::EMPTY,
    };
}

pub struct RenderData {
    pub texture_cache: TextureCache,
    pub draw_calls: Vec<IntermediateDrawCall>,
}

impl Default for RenderData {
    fn default() -> Self {
        Self::new()
    }
}

impl RenderData {
    pub fn new() -> Self {
        RenderData {
            texture_cache: TextureCache::new(TEXTURE_CACHE_MAX_SIZE),
            // start draw calls with a default draw call
            draw_calls: vec![IntermediateDrawCall::EMPTY],
        }
    }

    fn current_draw_call(&mut self) -> &mut IntermediateDrawCall {
        self.draw_calls.last_mut().unwrap()
    }

    fn new_draw_call(&mut self) {
        let draw_call = self.current_draw_call();
        let draw_call = draw_call.clone();
        self.draw_calls.push(draw_call);
    }

    // Public API

    pub fn clear_draw_calls(&mut self) {
        let draw_call = self.current_draw_call();
        let draw_call = draw_call.clone();
        self.draw_calls = vec![draw_call];
    }

    pub fn clear_textures(&mut self, index: usize) {
        let draw_call = self.current_draw_call();
        draw_call.texture_indices[index] = None;
    }

    pub fn set_program_params(
        &mut self,
        other_mode_h: u32,
        other_mode_l: u32,
        geometry_mode: GeometryModes,
        combine: CombineParams,
    ) {
        let draw_call = self.current_draw_call();
        let shader_config = ShaderConfig {
            other_mode_h,
            other_mode_l,
            geometry_mode,
            combine,
        };

        draw_call.shader_id = ShaderId(shader_config);
        draw_call.shader_config = shader_config;
    }

    pub fn set_texture(&mut self, tile: usize, cache_id: TextureCacheId) {
        let draw_call = self.current_draw_call();
        draw_call.texture_indices[tile] = Some(cache_id);
    }

    pub fn set_sampler_parameters(
        &mut self,
        tile: usize,
        linear_filter: bool,
        clamp_s: WrapMode,
        clamp_t: WrapMode,
    ) {
        let draw_call = self.current_draw_call();
        draw_call.samplers[tile] = Some(OutputSampler {
            tile,
            linear_filter,
            clamp_s,
            clamp_t,
        });
    }

    pub fn set_depth_stencil_params(
        &mut self,
        _depth_test_enabled: bool,
        depth_write_enabled: bool,
        depth_compare: CompareFunction,
        polygon_offset: bool,
    ) {
        let draw_call = self.current_draw_call();
        draw_call.stencil = Some(OutputStencil {
            depth_write_enabled,
            depth_compare,
            polygon_offset,
        });
    }

    pub fn set_projection_matrix(&mut self, matrix: glam::Mat4) {
        let draw_call = self.current_draw_call();
        draw_call.projection_matrix = matrix;
    }

    pub fn set_fog(&mut self, multiplier: i16, offset: i16) {
        let draw_call = self.current_draw_call();
        draw_call.fog = OutputFogParams { multiplier, offset };
    }

    pub fn set_viewport(&mut self, x: f32, y: f32, width: f32, height: f32) {
        let draw_call = self.current_draw_call();
        draw_call.viewport = glam::Vec4::new(x, y, width, height);
    }

    pub fn set_scissor(&mut self, x: u32, y: u32, width: u32, height: u32) {
        let draw_call = self.current_draw_call();
        draw_call.scissor = [x, y, width, height];
    }

    pub fn set_blend_state(&mut self, blend_state: Option<BlendState>) {
        let draw_call = self.current_draw_call();
        draw_call.blend_state = blend_state;
    }

    pub fn set_cull_mode(&mut self, cull_mode: Option<Face>) {
        let draw_call = self.current_draw_call();
        draw_call.cull_mode = cull_mode;
    }

    #[allow(clippy::too_many_arguments)]
    pub fn set_uniforms(
        &mut self,
        fog_color: glam::Vec4,
        blend_color: glam::Vec4,
        prim_color: glam::Vec4,
        env_color: glam::Vec4,
        key_center: glam::Vec3,
        key_scale: glam::Vec3,
        prim_lod: glam::Vec2,
        convert_k: [i32; 6],
    ) {
        let draw_call = self.current_draw_call();
        draw_call.uniforms = OutputUniforms {
            blend: OutputUniformsBlend {
                fog_color,
                blend_color,
            },
            combine: OutputUniformsCombine {
                prim_color,
                env_color,
                key_center,
                key_scale,
                prim_lod,
                convert_k4: convert_k[4] as f32 / 255.0,
                convert_k5: convert_k[5] as f32 / 255.0,
            },
        };
    }

    pub fn set_vbo(&mut self, vbo: Vec<u8>, num_tris: usize) {
        let draw_call = self.current_draw_call();
        draw_call.vbo = OutputVBO { vbo, num_tris };

        // start a new draw call that's a copy of the current one
        // we do this cause atm we only set properties on changes
        self.new_draw_call();
    }
}