Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/pc/gfx/gfx_pc.c
7861 views
1
#include <math.h>
2
#include <stdint.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <stdbool.h>
6
#include <assert.h>
7
8
#if defined(TARGET_MACOS) || defined(TARGET_LINUX)
9
#include <libgen.h>
10
#endif
11
#ifdef TARGET_LINUX
12
#include <unistd.h>
13
#endif
14
#include <stdio.h>
15
#define STB_IMAGE_IMPLEMENTATION
16
#include <stb/stb_image.h>
17
18
#ifndef _LANGUAGE_C
19
#define _LANGUAGE_C
20
#endif
21
#include <PR/gbi.h>
22
23
#include "gfx_pc.h"
24
#include "gfx_cc.h"
25
#include "gfx_window_manager_api.h"
26
#include "gfx_rendering_api.h"
27
#include "gfx_screen_config.h"
28
29
#include "../../game/game_init.h"
30
#include "../../game/settings.h"
31
#include "../../engine/math_util.h"
32
33
#define SUPPORT_CHECK(x) assert(x)
34
35
// SCALE_M_N: upscale/downscale M-bit integer to N-bit
36
#define SCALE_5_8(VAL_) (((VAL_) * 0xFF) / 0x1F)
37
#define SCALE_8_5(VAL_) ((((VAL_) + 4) * 0x1F) / 0xFF)
38
#define SCALE_4_8(VAL_) ((VAL_) * 0x11)
39
#define SCALE_8_4(VAL_) ((VAL_) / 0x11)
40
#define SCALE_3_8(VAL_) ((VAL_) * 0x24)
41
#define SCALE_8_3(VAL_) ((VAL_) / 0x24)
42
43
#define ENCORE_COLOR(VAL_) MAX(min(VAL_, 31.0f), 0.0f)
44
#define ENCORE_COLOR_EXT(VAL_) MAX(min(VAL_, 255.0f), 0.0f)
45
#define ENCORE_R 4*i+0
46
#define ENCORE_G 4*i+1
47
#define ENCORE_B 4*i+2
48
#define ENCORE_A 4*i+3
49
50
#define SCREEN_WIDTH 320
51
#define SCREEN_HEIGHT 240
52
#define HALF_SCREEN_WIDTH (SCREEN_WIDTH / 2)
53
#define HALF_SCREEN_HEIGHT (SCREEN_HEIGHT / 2)
54
55
#define RATIO_X (gfx_current_dimensions.width / (2.0f * HALF_SCREEN_WIDTH))
56
#define RATIO_Y (gfx_current_dimensions.height / (2.0f * HALF_SCREEN_HEIGHT))
57
58
#define MAX_BUFFERED 256
59
#define MAX_LIGHTS 2
60
#define MAX_VERTICES 64
61
62
# define MAX_CACHED_TEXTURES 4096
63
# define HASH_SHIFT 0
64
65
#define HASHMAP_LEN (MAX_CACHED_TEXTURES * 2)
66
#define HASH_MASK (HASHMAP_LEN - 1)
67
68
struct RGBA {
69
uint8_t r, g, b, a;
70
};
71
72
struct XYWidthHeight {
73
uint16_t x, y, width, height;
74
};
75
76
struct LoadedVertex {
77
float x, y, z, w;
78
float u, v;
79
struct RGBA color;
80
uint8_t clip_rej;
81
};
82
83
struct TextureHashmapNode {
84
struct TextureHashmapNode *next;
85
86
const uint8_t *texture_addr;
87
uint8_t fmt, siz;
88
89
uint32_t texture_id;
90
uint8_t cms, cmt;
91
bool linear_filter;
92
};
93
static struct {
94
struct TextureHashmapNode *hashmap[HASHMAP_LEN];
95
struct TextureHashmapNode pool[MAX_CACHED_TEXTURES];
96
uint32_t pool_pos;
97
} gfx_texture_cache;
98
99
struct ColorCombiner {
100
uint32_t cc_id;
101
struct ShaderProgram *prg;
102
uint8_t shader_input_mapping[2][4];
103
};
104
105
static struct ColorCombiner color_combiner_pool[64];
106
static uint8_t color_combiner_pool_size;
107
108
static struct RSP {
109
float modelview_matrix_stack[11][4][4];
110
uint8_t modelview_matrix_stack_size;
111
112
float MP_matrix[4][4];
113
float P_matrix[4][4];
114
115
Light_t current_lights[MAX_LIGHTS + 1];
116
float current_lights_coeffs[MAX_LIGHTS][3];
117
float current_lookat_coeffs[2][3]; // lookat_x, lookat_y
118
Light_t lookat[2];
119
uint8_t current_num_lights; // includes ambient light
120
bool lights_changed;
121
122
uint32_t geometry_mode;
123
int16_t fog_mul, fog_offset;
124
125
struct {
126
// U0.16
127
uint16_t s, t;
128
} texture_scaling_factor;
129
130
struct LoadedVertex loaded_vertices[MAX_VERTICES + 4];
131
132
uint8_t saved_opcode;
133
uint8_t saved_tile;
134
uint16_t saved_uls, saved_ult;
135
int32_t saved_lrx, saved_lry, saved_ulx, saved_uly;
136
} rsp;
137
138
static struct RDP {
139
const uint8_t *palette;
140
struct {
141
const uint8_t *addr;
142
uint8_t siz;
143
uint8_t tile_number;
144
} texture_to_load;
145
struct {
146
const uint8_t *addr;
147
uint32_t size_bytes;
148
} loaded_texture[2];
149
struct {
150
uint8_t fmt;
151
uint8_t siz;
152
uint8_t cms, cmt;
153
uint16_t uls, ult, lrs, lrt; // U10.2
154
uint32_t line_size_bytes;
155
} texture_tile;
156
bool textures_changed[2];
157
158
uint32_t other_mode_l, other_mode_h;
159
uint32_t combine_mode;
160
161
struct RGBA env_color, prim_color, fog_color, fill_color;
162
struct XYWidthHeight viewport, scissor;
163
bool viewport_or_scissor_changed;
164
void *z_buf_address;
165
void *color_image_address;
166
} rdp;
167
168
static struct RenderingState {
169
bool depth_test;
170
bool depth_mask;
171
bool decal_mode;
172
bool alpha_blend;
173
struct XYWidthHeight viewport, scissor;
174
struct ShaderProgram *shader_program;
175
struct TextureHashmapNode *textures[2];
176
} rendering_state;
177
178
struct GfxDimensions gfx_current_dimensions;
179
180
static bool dropped_frame;
181
182
static float buf_vbo[MAX_BUFFERED * (26 * 3)]; // 3 vertices in a triangle and 26 floats per vtx
183
static size_t buf_vbo_len;
184
static size_t buf_vbo_num_tris;
185
186
static struct GfxWindowManagerAPI *gfx_wapi;
187
static struct GfxRenderingAPI *gfx_rapi;
188
189
// 4x4 pink-black checkerboard texture to indicate missing textures
190
#define MISSING_W 4
191
#define MISSING_H 4
192
static const uint8_t missing_texture[MISSING_W * MISSING_H * 4] = {
193
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
194
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
195
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
196
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
197
};
198
199
static void gfx_flush(void) {
200
if (buf_vbo_len > 0) {
201
int num = buf_vbo_num_tris;
202
gfx_rapi->draw_triangles(buf_vbo, buf_vbo_len, buf_vbo_num_tris);
203
buf_vbo_len = 0;
204
buf_vbo_num_tris = 0;
205
}
206
}
207
208
static struct ShaderProgram *gfx_lookup_or_create_shader_program(uint32_t shader_id) {
209
struct ShaderProgram *prg = gfx_rapi->lookup_shader(shader_id);
210
if (prg == NULL) {
211
gfx_rapi->unload_shader(rendering_state.shader_program);
212
prg = gfx_rapi->create_and_load_new_shader(shader_id);
213
rendering_state.shader_program = prg;
214
}
215
return prg;
216
}
217
218
static void gfx_generate_cc(struct ColorCombiner *comb, uint32_t cc_id) {
219
uint8_t c[2][4];
220
uint32_t shader_id = (cc_id >> 24) << 24;
221
uint8_t shader_input_mapping[2][4] = {{0}};
222
for (int i = 0; i < 4; i++) {
223
c[0][i] = (cc_id >> (i * 3)) & 7;
224
c[1][i] = (cc_id >> (12 + i * 3)) & 7;
225
}
226
for (int i = 0; i < 2; i++) {
227
if (c[i][0] == c[i][1] || c[i][2] == CC_0) {
228
c[i][0] = c[i][1] = c[i][2] = 0;
229
}
230
uint8_t input_number[8] = {0};
231
int next_input_number = SHADER_INPUT_1;
232
for (int j = 0; j < 4; j++) {
233
int val = 0;
234
switch (c[i][j]) {
235
case CC_0:
236
break;
237
case CC_TEXEL0:
238
val = SHADER_TEXEL0;
239
break;
240
case CC_TEXEL1:
241
val = SHADER_TEXEL1;
242
break;
243
case CC_TEXEL0A:
244
val = SHADER_TEXEL0A;
245
break;
246
case CC_PRIM:
247
case CC_SHADE:
248
case CC_ENV:
249
case CC_LOD:
250
if (input_number[c[i][j]] == 0) {
251
shader_input_mapping[i][next_input_number - 1] = c[i][j];
252
input_number[c[i][j]] = next_input_number++;
253
}
254
val = input_number[c[i][j]];
255
break;
256
}
257
shader_id |= val << (i * 12 + j * 3);
258
}
259
}
260
comb->cc_id = cc_id;
261
comb->prg = gfx_lookup_or_create_shader_program(shader_id);
262
memcpy(comb->shader_input_mapping, shader_input_mapping, sizeof(shader_input_mapping));
263
}
264
265
static struct ColorCombiner *gfx_lookup_or_create_color_combiner(uint32_t cc_id) {
266
static struct ColorCombiner *prev_combiner;
267
if (prev_combiner != NULL && prev_combiner->cc_id == cc_id) {
268
return prev_combiner;
269
}
270
271
for (size_t i = 0; i < color_combiner_pool_size; i++) {
272
if (color_combiner_pool[i].cc_id == cc_id) {
273
return prev_combiner = &color_combiner_pool[i];
274
}
275
}
276
gfx_flush();
277
struct ColorCombiner *comb = &color_combiner_pool[color_combiner_pool_size++];
278
gfx_generate_cc(comb, cc_id);
279
return prev_combiner = comb;
280
}
281
282
static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, const uint8_t *orig_addr, uint32_t fmt, uint32_t siz) {
283
size_t hash = (uintptr_t)orig_addr;
284
hash = (hash >> HASH_SHIFT) & HASH_MASK;
285
struct TextureHashmapNode **node = &gfx_texture_cache.hashmap[hash];
286
while (*node != NULL && *node - gfx_texture_cache.pool < (int)gfx_texture_cache.pool_pos) {
287
if ((*node)->texture_addr == orig_addr && (*node)->fmt == fmt && (*node)->siz == siz) {
288
gfx_rapi->select_texture(tile, (*node)->texture_id);
289
if (configEncoreMode)
290
(*node)->linear_filter = (get_palette() == 12);
291
*n = *node;
292
return true;
293
}
294
node = &(*node)->next;
295
}
296
if (gfx_texture_cache.pool_pos == sizeof(gfx_texture_cache.pool) / sizeof(struct TextureHashmapNode) || gReimportTextures) {
297
// Pool is full. We just invalidate everything and start over.
298
gfx_texture_cache.pool_pos = 0;
299
node = &gfx_texture_cache.hashmap[hash];
300
gReimportTextures = 0;
301
// puts("Clearing texture cache");
302
}
303
*node = &gfx_texture_cache.pool[gfx_texture_cache.pool_pos++];
304
if ((*node)->texture_addr == NULL) {
305
(*node)->texture_id = gfx_rapi->new_texture();
306
}
307
gfx_rapi->select_texture(tile, (*node)->texture_id);
308
gfx_rapi->set_sampler_parameters(tile, false, 0, 0);
309
(*node)->cms = 0;
310
(*node)->cmt = 0;
311
(*node)->linear_filter = (get_palette() == 12); // Use nearest neighbor on Wet Dry World
312
(*node)->next = NULL;
313
(*node)->texture_addr = orig_addr;
314
(*node)->fmt = fmt;
315
(*node)->siz = siz;
316
*n = *node;
317
return false;
318
}
319
320
#ifdef CUSTOM_TEXTURES
321
322
static bool import_texture_custom(const char *path) {
323
int w, h;
324
325
if (strstr(path, "mario_logo") != NULL) {
326
if (!configShowCapLogo)
327
return FALSE;
328
}
329
330
u8 *data = stbi_load(path, &w, &h, NULL, 4);
331
332
if (data == NULL)
333
{
334
gfx_rapi->upload_texture(missing_texture, MISSING_W, MISSING_H);
335
return TRUE;
336
}
337
338
// I'm so sorry for the mess you're about to witness. It was supposed to be a temporary thing but...
339
// TODO (Mors): Have a more proper implementation. Maybe implement a sort of scripting system with palette files?
340
if (((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT)
341
&& (rdp.texture_tile.fmt == G_IM_FMT_RGBA) && (rdp.texture_tile.siz == G_IM_SIZ_16b)
342
&& ((strstr(path, "actors") == NULL)
343
|| (strstr(path, "door") != NULL)
344
|| (strstr(path, "leaves") != NULL)
345
|| (strstr(path, "tree") != NULL)
346
|| (strstr(path, "star") != NULL)
347
|| (strstr(path, "wooden_signpost") != NULL)
348
|| (strstr(path, "manta") != NULL)
349
|| (strstr(path, "sushi") != NULL)
350
|| (strstr(path, "water_ring") != NULL)
351
|| (strstr(path, "segment2") != NULL)
352
|| (get_palette() == 19)
353
)) {
354
355
for (int i = 0; i < w*h; i++) {
356
int r = data[ENCORE_R];
357
int g = data[ENCORE_G];
358
int b = data[ENCORE_B];
359
// Return different palettes.
360
switch (get_palette()) {
361
362
case 1: // Castle grounds
363
// [ENCORE_R]
364
// [ENCORE_G]
365
data[ENCORE_B] = ENCORE_COLOR_EXT(b + r*0.1875f + g*0.375f);
366
break;
367
case 2: //Bob Omb Battlefield
368
data[ENCORE_R] = ENCORE_COLOR_EXT(r*1.125f + g*0.625f - b*0.625f);
369
// [ENCORE_G]
370
data[ENCORE_B] = ENCORE_COLOR_EXT(b*1.0625f - (r+g)*0.03125f);
371
break;
372
case 3: // Whomp's Fortress
373
data[ENCORE_R] = ENCORE_COLOR_EXT(r+g*0.5f);
374
// [ENCORE_G]
375
data[ENCORE_B] = ENCORE_COLOR_EXT(b+r*0.0625f+g*0.0625f);
376
break;
377
case 21: // Metal Cave
378
data[ENCORE_R] = ENCORE_COLOR_EXT(r+g*0.625f+b*0.0625f);
379
data[ENCORE_G] = ENCORE_COLOR_EXT(g+r*0.25f+b*0.03125f);
380
// [ENCORE_B]
381
break;
382
case 4: // Jolly Roger Bay
383
case 15: // Tick Tock Clock
384
case 22: // Wing Mario Over the Rainbows
385
data[ENCORE_R] = ENCORE_COLOR_EXT(r*0.75f)*0.75f;
386
data[ENCORE_G] = ENCORE_COLOR_EXT(g*0.5f+r*0.0625f+b*0.0625f)*0.75f;
387
data[ENCORE_B] = ENCORE_COLOR_EXT(b+r*0.5f+g*0.5f)*0.75f;
388
break;
389
case 5: // Cool Cool Mountain
390
// [ENCORE_R]
391
// [ENCORE_G]
392
data[ENCORE_B] = ENCORE_COLOR_EXT(b*0.96875f+r*0.5f+g*0.5f);
393
break;
394
case 6: // Big Boo's Haunt
395
case 19: // Sky
396
data[ENCORE_R] = ((r+g+b)/3.0f);
397
data[ENCORE_G] = ((r+g+b)/3.0f);
398
data[ENCORE_B] = ((r+g+b)/3.0f);
399
break;
400
case 7: // Hazy Maze Cave
401
data[ENCORE_R] = ENCORE_COLOR_EXT(r-g*0.5f+b*0.5f);
402
data[ENCORE_G] = ENCORE_COLOR_EXT((g-r*0.5f+b*0.5f)*1.5f-(r-g*0.5f+b*0.5f+b*0.75f+g*0.25f)*0.25f);
403
data[ENCORE_B] = ENCORE_COLOR_EXT(b*0.75f+g*0.25f);
404
break;
405
case 8: // Lethal Lava Land
406
case 23: // Vanish Cap area
407
case 10: // Dire Dire Docks
408
data[ENCORE_R] = ENCORE_COLOR_EXT(r+g*0.5f+b*0.5f);
409
data[ENCORE_G] = ENCORE_COLOR_EXT(g+r*0.0625f+b*0.0625f);
410
data[ENCORE_B] = (b*0.875f);
411
break;
412
case 9: // Shitting Sand Land
413
// [ENCORE_R]
414
// [ENCORE_G]
415
data[ENCORE_B] = ENCORE_COLOR_EXT(b*0.5+r*0.25+g*0.25f);
416
break;
417
case 11: // Snowman
418
case 24: // Poopoo level
419
data[ENCORE_R] = ENCORE_COLOR_EXT(r+b*0.25f);
420
// [ENCORE_G]
421
// [ENCORE_B]
422
break;
423
case 12: // Wet Dry World
424
data[ENCORE_R] = ENCORE_COLOR_EXT(round(sqrt(r/127.0f)*8)*24);
425
data[ENCORE_G] = ENCORE_COLOR_EXT(round(sqrt(g/127.0f)*8)*24);
426
data[ENCORE_B] = ENCORE_COLOR_EXT(round(sqrt(b/127.0f)*8)*24);
427
break;
428
case 13: // Donkey Slide
429
data[ENCORE_R] = ENCORE_COLOR_EXT(min(r+g*1.25f-b*1.5f, 255.0f*0.9375f));
430
// [ENCORE_G]
431
// [ENCORE_B]
432
break;
433
case 14: // Tiny Huge Island
434
if (g > r+b) {
435
// [ENCORE_R]
436
// [ENCORE_G]
437
data[ENCORE_B] = ENCORE_COLOR_EXT(b+r*0.5f+g*0.5f);
438
}
439
else if (r+g+b < 95)
440
{
441
data[ENCORE_R] = r/2.0f;
442
data[ENCORE_G] = ENCORE_COLOR_EXT((g+r*1.0625)/2.0f);
443
data[ENCORE_B] = ENCORE_COLOR_EXT((b+r*1.03125)/2.0f);
444
}
445
else
446
{
447
// [ENCORE_R]
448
// [ENCORE_G]
449
data[ENCORE_B] = ENCORE_COLOR_EXT(b+r*0.125f+g*0.25f);
450
}
451
break;
452
case 16: // Rainbow Ride
453
// [ENCORE_R]
454
// [ENCORE_G]
455
data[ENCORE_B] = ENCORE_COLOR_EXT(b+g*1.25f-r*0.5f);
456
break;
457
case 17: // Dank world
458
// [ENCORE_R]
459
data[ENCORE_G] = b;
460
data[ENCORE_B] = g;
461
break;
462
case 18: // Bowser in the Fire Sea
463
if (r > (g+b)*2) {
464
// [ENCORE_R]
465
data[ENCORE_G] = ENCORE_COLOR_EXT((g*0.875f+r*0.0625f+b*0.0625f)*0.3125f+g*0.5f);
466
data[ENCORE_B] = ENCORE_COLOR_EXT((b+r*0.75f+g*0.75f)*0.625f);
467
}
468
else
469
{
470
// [ENCORE_R]
471
data[ENCORE_G] = ENCORE_COLOR_EXT((g*0.875f+r*0.0625f+b*0.0625f)*0.875f);
472
data[ENCORE_B] = ENCORE_COLOR_EXT((b+r*0.75f+g*0.75f)*0.875f);
473
}
474
break;
475
case 20: // Secret Slide
476
// [ENCORE_R]
477
data[ENCORE_G] = (r+g+b)/3.0f;
478
data[ENCORE_B] = (r+g+b)/3.0f;
479
break;
480
case 25: // Secret Aquarium
481
data[ENCORE_R] = ENCORE_COLOR_EXT(r*0.875f);
482
data[ENCORE_G] = ENCORE_COLOR_EXT(g*0.875f+r*0.0625f+b*0.0625f);
483
data[ENCORE_B] = ENCORE_COLOR_EXT(b+r*0.75f+g*0.75f);
484
break;
485
case 26: // Ending (doesn't work? i think? cant be bothered to watch the credits again to see)
486
data[ENCORE_R] = ENCORE_COLOR_EXT((r+g+b)/2.0f);
487
data[ENCORE_G] = ENCORE_COLOR_EXT((r+g+b)/2.5f);
488
data[ENCORE_B] = ENCORE_COLOR_EXT((r+g+b)/3.0f);
489
break;
490
}
491
}
492
}
493
494
gfx_rapi->upload_texture(data, w, h);
495
stbi_image_free(data);
496
497
return TRUE;
498
}
499
500
#else
501
502
static void import_texture_rgba16(int tile) {
503
uint8_t rgba32_buf[8192];
504
uint8_t rgba32_buf_out[8192*4*4];
505
506
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes / 2; i++) {
507
uint16_t col16 = (rdp.loaded_texture[tile].addr[2 * i] << 8) | rdp.loaded_texture[tile].addr[2 * i + 1];
508
uint8_t a = col16 & 1;
509
uint8_t r = col16 >> 11;
510
uint8_t g = (col16 >> 6) & 0x1f;
511
uint8_t b = (col16 >> 1) & 0x1f;
512
char *path = rdp.loaded_texture[tile].addr;
513
if (((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT)
514
&& (rdp.texture_tile.fmt == G_IM_FMT_RGBA) && (rdp.texture_tile.siz == G_IM_SIZ_16b)) {
515
// I'm so sorry for the mess you're about to witness. It was supposed to be a temporary thing but...
516
// TODO (Mors): Have a more proper implementation. Maybe implement a sort of scripting system with palette files?
517
rgba32_buf[ENCORE_R] = SCALE_5_8(r);
518
rgba32_buf[ENCORE_G] = SCALE_5_8(g);
519
rgba32_buf[ENCORE_B] = SCALE_5_8(b);
520
switch (get_palette()) {
521
case 1: // Castle grounds
522
// [ENCORE_R]
523
// [ENCORE_G]
524
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b + r*0.1875f + g*0.375f));
525
break;
526
case 2: //Bob Omb Battlefield
527
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(r*1.125f + g*0.625f - b*0.625f));
528
// [ENCORE_G]
529
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b*1.0625f - (r+g)*0.03125f));
530
break;
531
case 3: // Whomp's Fortress
532
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(r+g*0.5f));
533
// [ENCORE_G]
534
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b+r*0.0625f+g*0.0625f));
535
break;
536
case 21: // Metal Cave
537
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(r+g*0.625f+b*0.0625f));
538
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR(g+r*0.25f+b*0.03125));
539
// [ENCORE_B]
540
break;
541
case 4: // Jolly Roger Bay
542
case 15: // Tick Tock Clock
543
case 22: // Wing Mario Over the Rainbows
544
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(r*0.75f)*0.75f);
545
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR(g*0.5f+r*0.0625f+b*0.0625f)*0.75f);
546
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b+r*0.5f+g*0.5f)*0.75f);
547
break;
548
case 5: // Cool Cool Mountain
549
// [ENCORE_R]
550
// [ENCORE_G]
551
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b*0.96875f+r*0.5f+g*0.5f));
552
break;
553
case 6: // Big Boo's Haunt
554
case 19: // Sky
555
rgba32_buf[ENCORE_R] = SCALE_5_8((r+g+b)/3.0f);
556
rgba32_buf[ENCORE_G] = SCALE_5_8((r+g+b)/3.0f);
557
rgba32_buf[ENCORE_B] = SCALE_5_8((r+g+b)/3.0f);
558
break;
559
case 7: // Hazy Maze Cave
560
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(r-g*0.5f+b*0.5f));
561
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR((g-r*0.5f+b*0.5f)*1.5f-(r-g*0.5f+b*0.5f+b*0.75f+g*0.25f)*0.25f));
562
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b*0.75+g*0.25));
563
break;
564
case 8: // Lethal Lava Land
565
case 23: // Vanish Cap area
566
case 10: // Dire Dire Docks
567
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(r+g*0.5f+b*0.5f));
568
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR(g+r*0.0625f+b*0.0625f));
569
rgba32_buf[ENCORE_B] = SCALE_5_8(b*0.875f);
570
break;
571
case 9: // Shitting Sand Land
572
// [ENCORE_R]
573
// [ENCORE_G]
574
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b*0.5+r*0.25+g*0.25f));
575
break;
576
case 11: // Snowman
577
case 24: // Poopoo level
578
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(r+b*0.25f));
579
// [ENCORE_G]
580
// [ENCORE_B]
581
break;
582
case 12: // Wet Dry World
583
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(round(sqrt(r/31.0f)*8)*4));
584
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR(round(sqrt(g/31.0f)*8)*4));
585
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(round(sqrt(b/31.0f)*8)*4));
586
break;
587
case 13: // Donkey Slide
588
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR(min(r+g*1.25f-b*1.5f, 31.0f*0.9375f)));
589
// [ENCORE_G]
590
// [ENCORE_B]
591
break;
592
case 14: // Tiny Huge Island
593
if (g > r+b) {
594
// [ENCORE_R]
595
// [ENCORE_G]
596
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b+r*0.5+g*0.5));
597
}
598
else if (r+g+b < 23)
599
{
600
rgba32_buf[ENCORE_R] = SCALE_5_8(r/2.0f);
601
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR((g+r*1.0625, 31)/2.0f));
602
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR((b+r*1.03125, 31)/2.0f));
603
}
604
else
605
{
606
// [ENCORE_R]
607
// [ENCORE_G]
608
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b+r*0.125+g*0.25f));
609
}
610
break;
611
case 16: // Rainbow Ride
612
// [ENCORE_R]
613
// [ENCORE_G]
614
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b+g*1.25f-r*0.5f));
615
break;
616
case 17: // Dank world
617
// [ENCORE_R]
618
rgba32_buf[ENCORE_G] = SCALE_5_8(b);
619
rgba32_buf[ENCORE_B] = SCALE_5_8(g);
620
break;
621
case 18: // Bowser in the Fire Sea
622
if (r > (g+b)*2) {
623
// [ENCORE_R]
624
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR((g*0.875f+r*0.0625f+b*0.0625f)*0.3125f+g*0.5f));
625
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR((b+r*0.75f+g*0.75f)*0.625f));
626
}
627
else
628
{
629
// [ENCORE_R]
630
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR((g*0.875f+r*0.0625f+b*0.0625f)*0.875f));
631
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR((b+r*0.75f+g*0.75f)*0.875f));
632
}
633
break;
634
case 20: // Secret Slide
635
// [ENCORE_R]
636
rgba32_buf[ENCORE_G] = SCALE_5_8((r+g+b)/3.0f);
637
rgba32_buf[ENCORE_B] = SCALE_5_8((r+g+b)/3.0f);
638
break;
639
case 25: // Secret Aquarium
640
rgba32_buf[ENCORE_R] = SCALE_5_8(r*0.875);
641
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR(g*0.875f+r*0.0625f+b*0.0625f));
642
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR(b+r*0.75+g*0.75));
643
break;
644
case 26: // Ending
645
rgba32_buf[ENCORE_R] = SCALE_5_8(ENCORE_COLOR((r+g+b)/2.0f));
646
rgba32_buf[ENCORE_G] = SCALE_5_8(ENCORE_COLOR((r+g+b)/2.5f));
647
rgba32_buf[ENCORE_B] = SCALE_5_8(ENCORE_COLOR((r+g+b)/3.0f));
648
break;
649
}
650
651
}
652
else {
653
rgba32_buf[ENCORE_R] = SCALE_5_8(r);
654
rgba32_buf[ENCORE_G] = SCALE_5_8(g);
655
rgba32_buf[ENCORE_B] = SCALE_5_8(b);
656
}
657
rgba32_buf[ENCORE_A] = a ? 255 : 0;
658
}
659
660
uint32_t width = rdp.texture_tile.line_size_bytes / 2;
661
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
662
663
gfx_rapi->upload_texture(rgba32_buf, width, height);
664
}
665
666
static void import_texture_rgba32(int tile) {
667
uint32_t width = rdp.texture_tile.line_size_bytes / 2;
668
uint32_t height = (rdp.loaded_texture[tile].size_bytes / 2) / rdp.texture_tile.line_size_bytes;
669
gfx_rapi->upload_texture(rdp.loaded_texture[tile].addr, width, height);
670
}
671
672
static void import_texture_ia4(int tile) {
673
uint8_t rgba32_buf[32768];
674
675
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes * 2; i++) {
676
uint8_t byte = rdp.loaded_texture[tile].addr[i / 2];
677
uint8_t part = (byte >> (4 - (i % 2) * 4)) & 0xf;
678
uint8_t intensity = part >> 1;
679
uint8_t alpha = part & 1;
680
uint8_t r = intensity;
681
uint8_t g = intensity;
682
uint8_t b = intensity;
683
rgba32_buf[ENCORE_R] = SCALE_3_8(r);
684
rgba32_buf[ENCORE_G] = SCALE_3_8(g);
685
rgba32_buf[ENCORE_B] = SCALE_3_8(b);
686
rgba32_buf[ENCORE_A] = alpha ? 255 : 0;
687
}
688
689
uint32_t width = rdp.texture_tile.line_size_bytes * 2;
690
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
691
692
gfx_rapi->upload_texture(rgba32_buf, width, height);
693
}
694
695
static void import_texture_ia8(int tile) {
696
uint8_t rgba32_buf[16384];
697
698
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes; i++) {
699
uint8_t intensity = rdp.loaded_texture[tile].addr[i] >> 4;
700
uint8_t alpha = rdp.loaded_texture[tile].addr[i] & 0xf;
701
uint8_t r = intensity;
702
uint8_t g = intensity;
703
uint8_t b = intensity;
704
rgba32_buf[ENCORE_R] = SCALE_4_8(r);
705
rgba32_buf[ENCORE_G] = SCALE_4_8(g);
706
rgba32_buf[ENCORE_B] = SCALE_4_8(b);
707
rgba32_buf[ENCORE_A] = SCALE_4_8(alpha);
708
}
709
710
uint32_t width = rdp.texture_tile.line_size_bytes;
711
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
712
713
gfx_rapi->upload_texture(rgba32_buf, width, height);
714
}
715
716
static void import_texture_ia16(int tile) {
717
uint8_t rgba32_buf[8192];
718
719
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes / 2; i++) {
720
uint8_t intensity = rdp.loaded_texture[tile].addr[2 * i];
721
uint8_t alpha = rdp.loaded_texture[tile].addr[2 * i + 1];
722
uint8_t r = intensity;
723
uint8_t g = intensity;
724
uint8_t b = intensity;
725
rgba32_buf[ENCORE_R] = r;
726
rgba32_buf[ENCORE_G] = g;
727
rgba32_buf[ENCORE_B] = b;
728
rgba32_buf[ENCORE_A] = alpha;
729
}
730
731
uint32_t width = rdp.texture_tile.line_size_bytes / 2;
732
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
733
734
gfx_rapi->upload_texture(rgba32_buf, width, height);
735
}
736
737
static void import_texture_i4(int tile) {
738
uint8_t rgba32_buf[32768];
739
740
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes * 2; i++) {
741
uint8_t byte = rdp.loaded_texture[tile].addr[i / 2];
742
uint8_t part = (byte >> (4 - (i % 2) * 4)) & 0xf;
743
uint8_t intensity = part;
744
uint8_t r = intensity;
745
uint8_t g = intensity;
746
uint8_t b = intensity;
747
rgba32_buf[ENCORE_R] = SCALE_4_8(r);
748
rgba32_buf[ENCORE_G] = SCALE_4_8(g);
749
rgba32_buf[ENCORE_B] = SCALE_4_8(b);
750
rgba32_buf[ENCORE_A] = 255;
751
}
752
753
uint32_t width = rdp.texture_tile.line_size_bytes * 2;
754
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
755
756
gfx_rapi->upload_texture(rgba32_buf, width, height);
757
}
758
759
static void import_texture_i8(int tile) {
760
uint8_t rgba32_buf[16384];
761
762
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes; i++) {
763
uint8_t intensity = rdp.loaded_texture[tile].addr[i];
764
uint8_t r = intensity;
765
uint8_t g = intensity;
766
uint8_t b = intensity;
767
rgba32_buf[ENCORE_R] = r;
768
rgba32_buf[ENCORE_G] = g;
769
rgba32_buf[ENCORE_B] = b;
770
rgba32_buf[ENCORE_A] = 255;
771
}
772
773
uint32_t width = rdp.texture_tile.line_size_bytes;
774
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
775
776
gfx_rapi->upload_texture(rgba32_buf, width, height);
777
}
778
779
780
static void import_texture_ci4(int tile) {
781
uint8_t rgba32_buf[32768];
782
783
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes * 2; i++) {
784
uint8_t byte = rdp.loaded_texture[tile].addr[i / 2];
785
uint8_t idx = (byte >> (4 - (i % 2) * 4)) & 0xf;
786
uint16_t col16 = (rdp.palette[idx * 2] << 8) | rdp.palette[idx * 2 + 1]; // Big endian load
787
uint8_t a = col16 & 1;
788
uint8_t r = col16 >> 11;
789
uint8_t g = (col16 >> 6) & 0x1f;
790
uint8_t b = (col16 >> 1) & 0x1f;
791
rgba32_buf[ENCORE_R] = SCALE_5_8(r);
792
rgba32_buf[ENCORE_G] = SCALE_5_8(g);
793
rgba32_buf[ENCORE_B] = SCALE_5_8(b);
794
rgba32_buf[ENCORE_A] = a ? 255 : 0;
795
}
796
797
uint32_t width = rdp.texture_tile.line_size_bytes * 2;
798
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
799
800
gfx_rapi->upload_texture(rgba32_buf, width, height);
801
}
802
803
static void import_texture_ci8(int tile) {
804
uint8_t rgba32_buf[16384];
805
806
for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes; i++) {
807
uint8_t idx = rdp.loaded_texture[tile].addr[i];
808
uint16_t col16 = (rdp.palette[idx * 2] << 8) | rdp.palette[idx * 2 + 1]; // Big endian load
809
uint8_t a = col16 & 1;
810
uint8_t r = col16 >> 11;
811
uint8_t g = (col16 >> 6) & 0x1f;
812
uint8_t b = (col16 >> 1) & 0x1f;
813
rgba32_buf[ENCORE_R] = SCALE_5_8(r);
814
rgba32_buf[ENCORE_G] = SCALE_5_8(g);
815
rgba32_buf[ENCORE_B] = SCALE_5_8(b);
816
rgba32_buf[ENCORE_A] = a ? 255 : 0;
817
}
818
819
uint32_t width = rdp.texture_tile.line_size_bytes;
820
uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes;
821
822
gfx_rapi->upload_texture(rgba32_buf, width, height);
823
}
824
825
#endif
826
827
// defined in pc_main.c
828
extern const char* GFX_DIR_PATH;
829
830
static void import_texture(int tile) {
831
uint8_t fmt = rdp.texture_tile.fmt;
832
uint8_t siz = rdp.texture_tile.siz;
833
834
if (gfx_texture_cache_lookup(tile, &rendering_state.textures[tile], rdp.loaded_texture[tile].addr, fmt, siz)) {
835
return;
836
}
837
838
// Load the textures
839
#ifdef CUSTOM_TEXTURES
840
char path[1024];
841
#if defined(TARGET_MACOS) || defined(TARGET_LINUX)
842
char exec_path[1024];
843
char* dir;
844
uint32_t size = sizeof(exec_path);
845
#ifdef TARGET_MACOS
846
_NSGetExecutablePath(exec_path, &size);
847
#else
848
readlink("/proc/self/exe", exec_path, size);
849
#endif
850
dir = dirname(exec_path);
851
const char gfx_path[1024];
852
snprintf(gfx_path, sizeof(gfx_path), "%s/gfx", dir);
853
const char* gfx_dir = GFX_DIR_PATH == NULL ? gfx_path : GFX_DIR_PATH;
854
#else
855
const char* gfx_dir = GFX_DIR_PATH == NULL ? "gfx" : GFX_DIR_PATH;
856
#endif
857
snprintf(path, sizeof(path), "%s/%s.png", gfx_dir, (const char*)rdp.loaded_texture[tile].addr);
858
859
import_texture_custom(path);
860
#else
861
if (fmt == G_IM_FMT_RGBA) {
862
if (siz == G_IM_SIZ_16b) {
863
import_texture_rgba16(tile);
864
} else if (siz == G_IM_SIZ_32b) {
865
import_texture_rgba32(tile);
866
} else {
867
abort();
868
}
869
} else if (fmt == G_IM_FMT_IA) {
870
if (siz == G_IM_SIZ_4b) {
871
import_texture_ia4(tile);
872
} else if (siz == G_IM_SIZ_8b) {
873
import_texture_ia8(tile);
874
} else if (siz == G_IM_SIZ_16b) {
875
import_texture_ia16(tile);
876
} else {
877
abort();
878
}
879
} else if (fmt == G_IM_FMT_CI) {
880
if (siz == G_IM_SIZ_4b) {
881
import_texture_ci4(tile);
882
} else if (siz == G_IM_SIZ_8b) {
883
import_texture_ci8(tile);
884
} else {
885
abort();
886
}
887
} else if (fmt == G_IM_FMT_I) {
888
if (siz == G_IM_SIZ_4b) {
889
import_texture_i4(tile);
890
} else if (siz == G_IM_SIZ_8b) {
891
import_texture_i8(tile);
892
} else {
893
abort();
894
}
895
} else {
896
abort();
897
}
898
#endif
899
}
900
901
static void gfx_normalize_vector(float v[3]) {
902
float s = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
903
v[0] /= s;
904
v[1] /= s;
905
v[2] /= s;
906
}
907
908
static void gfx_transposed_matrix_mul(float res[3], const float a[3], const float b[4][4]) {
909
res[0] = a[0] * b[0][0] + a[1] * b[0][1] + a[2] * b[0][2];
910
res[1] = a[0] * b[1][0] + a[1] * b[1][1] + a[2] * b[1][2];
911
res[2] = a[0] * b[2][0] + a[1] * b[2][1] + a[2] * b[2][2];
912
}
913
914
static void calculate_normal_dir(const Light_t *light, float coeffs[3]) {
915
float light_dir[3] = {
916
light->dir[0] / 127.0f,
917
light->dir[1] / 127.0f,
918
light->dir[2] / 127.0f
919
};
920
gfx_transposed_matrix_mul(coeffs, light_dir, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1]);
921
gfx_normalize_vector(coeffs);
922
}
923
924
static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4][4]) {
925
float tmp[4][4];
926
for (int i = 0; i < 4; i++) {
927
for (int j = 0; j < 4; j++) {
928
tmp[i][j] = a[i][0] * b[0][j] +
929
a[i][1] * b[1][j] +
930
a[i][2] * b[2][j] +
931
a[i][3] * b[3][j];
932
}
933
}
934
memcpy(res, tmp, sizeof(tmp));
935
}
936
937
static void gfx_sp_matrix(uint8_t parameters, const int32_t *addr) {
938
float matrix[4][4];
939
#ifndef GBI_FLOATS
940
// Original GBI where fixed point matrices are used
941
for (int i = 0; i < 4; i++) {
942
for (int j = 0; j < 4; j += 2) {
943
int32_t int_part = addr[i * 2 + j / 2];
944
uint32_t frac_part = addr[8 + i * 2 + j / 2];
945
matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f;
946
matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f;
947
}
948
}
949
#else
950
// For a modified GBI where fixed point values are replaced with floats
951
memcpy(matrix, addr, sizeof(matrix));
952
#endif
953
954
if (parameters & G_MTX_PROJECTION) {
955
if (parameters & G_MTX_LOAD) {
956
memcpy(rsp.P_matrix, matrix, sizeof(matrix));
957
} else {
958
gfx_matrix_mul(rsp.P_matrix, matrix, rsp.P_matrix);
959
}
960
} else { // G_MTX_MODELVIEW
961
if ((parameters & G_MTX_PUSH) && rsp.modelview_matrix_stack_size < 11) {
962
++rsp.modelview_matrix_stack_size;
963
memcpy(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 2], sizeof(matrix));
964
}
965
if (parameters & G_MTX_LOAD) {
966
memcpy(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], matrix, sizeof(matrix));
967
} else {
968
gfx_matrix_mul(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1]);
969
}
970
rsp.lights_changed = 1;
971
}
972
gfx_matrix_mul(rsp.MP_matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], rsp.P_matrix);
973
}
974
975
static void gfx_sp_pop_matrix(uint32_t count) {
976
while (count--) {
977
if (rsp.modelview_matrix_stack_size > 0) {
978
--rsp.modelview_matrix_stack_size;
979
if (rsp.modelview_matrix_stack_size > 0) {
980
gfx_matrix_mul(rsp.MP_matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], rsp.P_matrix);
981
}
982
}
983
}
984
}
985
986
static float gfx_adjust_x_for_aspect_ratio(float x) {
987
return x * (4.0f / 3.0f) / ((float)gfx_current_dimensions.width / (float)gfx_current_dimensions.height);
988
}
989
990
static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *vertices) {
991
for (size_t i = 0; i < n_vertices; i++, dest_index++) {
992
const Vtx_t *v = &vertices[i].v;
993
const Vtx_tn *vn = &vertices[i].n;
994
struct LoadedVertex *d = &rsp.loaded_vertices[dest_index];
995
996
float x = v->ob[0] * rsp.MP_matrix[0][0] + v->ob[1] * rsp.MP_matrix[1][0] + v->ob[2] * rsp.MP_matrix[2][0] + rsp.MP_matrix[3][0];
997
float y = v->ob[0] * rsp.MP_matrix[0][1] + v->ob[1] * rsp.MP_matrix[1][1] + v->ob[2] * rsp.MP_matrix[2][1] + rsp.MP_matrix[3][1];
998
float z = v->ob[0] * rsp.MP_matrix[0][2] + v->ob[1] * rsp.MP_matrix[1][2] + v->ob[2] * rsp.MP_matrix[2][2] + rsp.MP_matrix[3][2];
999
float w = v->ob[0] * rsp.MP_matrix[0][3] + v->ob[1] * rsp.MP_matrix[1][3] + v->ob[2] * rsp.MP_matrix[2][3] + rsp.MP_matrix[3][3];
1000
1001
x = gfx_adjust_x_for_aspect_ratio(x);
1002
1003
if (get_mirror() && (rsp.geometry_mode & G_ZBUFFER)) {
1004
x = -x;
1005
}
1006
1007
short U = v->tc[0] * rsp.texture_scaling_factor.s >> 16;
1008
short V = v->tc[1] * rsp.texture_scaling_factor.t >> 16;
1009
1010
if (configFXMode) {
1011
U = 1;
1012
V = 1;
1013
}
1014
1015
if (rsp.geometry_mode & G_LIGHTING) {
1016
if (rsp.lights_changed) {
1017
for (int i = 0; i < rsp.current_num_lights - 1; i++) {
1018
calculate_normal_dir(&rsp.current_lights[i], rsp.current_lights_coeffs[i]);
1019
}
1020
calculate_normal_dir(&rsp.lookat[0], rsp.current_lookat_coeffs[0]);
1021
calculate_normal_dir(&rsp.lookat[1], rsp.current_lookat_coeffs[1]);
1022
rsp.lights_changed = false;
1023
}
1024
1025
// Shadow colors
1026
int r = rsp.current_lights[rsp.current_num_lights - 1].col[0];
1027
int g = rsp.current_lights[rsp.current_num_lights - 1].col[1];
1028
int b = rsp.current_lights[rsp.current_num_lights - 1].col[2];
1029
1030
// Detect if these are one of Mario's colors
1031
bool mario_hat = (r == 0x02 && g == 0x02 && b == 0x02);
1032
bool mario_shirt = (r == 0x03 && g == 0x03 && b == 0x03);
1033
bool mario_overalls = (r == 0x04 && g == 0x04 && b == 0x04);
1034
bool mario_gloves = (r == 0x05 && g == 0x05 && b == 0x05);
1035
bool mario_shoes = (r == 0x06 && g == 0x06 && b == 0x06);
1036
bool mario_skin = (r == 0x07 && g == 0x07 && b == 0x07);
1037
bool mario_hair = (r == 0x08 && g == 0x08 && b == 0x08);
1038
1039
// Override them lazily
1040
if (mario_hat) {
1041
r = configColorCap[1][0];
1042
g = configColorCap[1][1];
1043
b = configColorCap[1][2];
1044
}
1045
if (mario_shirt) {
1046
r = configColorShirt[1][0];
1047
g = configColorShirt[1][1];
1048
b = configColorShirt[1][2];
1049
}
1050
if (mario_overalls) {
1051
r = configColorOveralls[1][0];
1052
g = configColorOveralls[1][1];
1053
b = configColorOveralls[1][2];
1054
}
1055
if (mario_gloves) {
1056
r = configColorGloves[1][0];
1057
g = configColorGloves[1][1];
1058
b = configColorGloves[1][2];
1059
}
1060
if (mario_shoes) {
1061
r = configColorShoes[1][0];
1062
g = configColorShoes[1][1];
1063
b = configColorShoes[1][2];
1064
}
1065
if (mario_skin) {
1066
r = configColorSkin[1][0];
1067
g = configColorSkin[1][1];
1068
b = configColorSkin[1][2];
1069
}
1070
if (mario_hair) {
1071
r = configColorHair[1][0];
1072
g = configColorHair[1][1];
1073
b = configColorHair[1][2];
1074
}
1075
1076
for (int i = 0; i < rsp.current_num_lights - 1; i++) {
1077
float intensity = 0;
1078
if (configDisableLighting) {
1079
intensity = 0.5f;
1080
}
1081
else {
1082
intensity += vn->n[0] * rsp.current_lights_coeffs[i][0];
1083
intensity += vn->n[1] * rsp.current_lights_coeffs[i][1];
1084
intensity += vn->n[2] * rsp.current_lights_coeffs[i][2];
1085
intensity /= 127.0f;
1086
}
1087
if (intensity > 0.0f) {
1088
// Light colors
1089
int lightr = rsp.current_lights[i].col[0];
1090
int lightg = rsp.current_lights[i].col[1];
1091
int lightb = rsp.current_lights[i].col[2];
1092
1093
// Override these too
1094
if (mario_hat) {
1095
r += intensity * configColorCap[0][0];
1096
g += intensity * configColorCap[0][1];
1097
b += intensity * configColorCap[0][2];
1098
}
1099
else if (mario_shirt) {
1100
r += intensity * configColorShirt[0][0];
1101
g += intensity * configColorShirt[0][1];
1102
b += intensity * configColorShirt[0][2];
1103
}
1104
else if (mario_overalls) {
1105
r += intensity * configColorOveralls[0][0];
1106
g += intensity * configColorOveralls[0][1];
1107
b += intensity * configColorOveralls[0][2];
1108
}
1109
else if (mario_gloves) {
1110
r += intensity * configColorGloves[0][0];
1111
g += intensity * configColorGloves[0][1];
1112
b += intensity * configColorGloves[0][2];
1113
}
1114
else if (mario_shoes) {
1115
r += intensity * configColorShoes[0][0];
1116
g += intensity * configColorShoes[0][1];
1117
b += intensity * configColorShoes[0][2];
1118
}
1119
else if (mario_skin) {
1120
r += intensity * configColorSkin[0][0];
1121
g += intensity * configColorSkin[0][1];
1122
b += intensity * configColorSkin[0][2];
1123
}
1124
else if (mario_hair) {
1125
r += intensity * configColorHair[0][0];
1126
g += intensity * configColorHair[0][1];
1127
b += intensity * configColorHair[0][2];
1128
}
1129
else {
1130
r += intensity * lightr;
1131
g += intensity * lightg;
1132
b += intensity * lightb;
1133
}
1134
}
1135
}
1136
1137
d->color.r = r > 255 ? 255 : r;
1138
d->color.g = g > 255 ? 255 : g;
1139
d->color.b = b > 255 ? 255 : b;
1140
1141
if (rsp.geometry_mode & G_TEXTURE_GEN) {
1142
float dotx = 0, doty = 0;
1143
dotx += vn->n[0] * rsp.current_lookat_coeffs[0][0];
1144
dotx += vn->n[1] * rsp.current_lookat_coeffs[0][1];
1145
dotx += vn->n[2] * rsp.current_lookat_coeffs[0][2];
1146
doty += vn->n[0] * rsp.current_lookat_coeffs[1][0];
1147
doty += vn->n[1] * rsp.current_lookat_coeffs[1][1];
1148
doty += vn->n[2] * rsp.current_lookat_coeffs[1][2];
1149
1150
if (configFXMode || configDisableLighting) {
1151
U = 1;
1152
V = 1;
1153
}
1154
else {
1155
U = (int32_t)((dotx / 127.0f + 1.0f) / 4.0f * rsp.texture_scaling_factor.s);
1156
V = (int32_t)((doty / 127.0f + 1.0f) / 4.0f * rsp.texture_scaling_factor.t);
1157
}
1158
}
1159
} else {
1160
d->color.r = v->cn[0];
1161
d->color.g = v->cn[1];
1162
d->color.b = v->cn[2];
1163
}
1164
1165
d->u = U;
1166
d->v = V;
1167
1168
// trivial clip rejection
1169
d->clip_rej = 0;
1170
if (x < -w) d->clip_rej |= 1;
1171
if (x > w) d->clip_rej |= 2;
1172
if (y < -w) d->clip_rej |= 4;
1173
if (y > w) d->clip_rej |= 8;
1174
if (z < -w) d->clip_rej |= 16;
1175
if (z > w) d->clip_rej |= 32;
1176
1177
d->x = x;
1178
d->y = y;
1179
d->z = z;
1180
d->w = w;
1181
1182
if (rsp.geometry_mode & G_FOG) {
1183
if (fabsf(w) < 0.001f) {
1184
// To avoid division by zero
1185
w = 0.001f;
1186
}
1187
1188
float winv = 1.0f / w;
1189
if (winv < 0.0f) {
1190
winv = 32767.0f;
1191
}
1192
1193
float fog_z = z * winv * rsp.fog_mul + rsp.fog_offset;
1194
if (fog_z < 0) fog_z = 0;
1195
if (fog_z > 255) fog_z = 255;
1196
d->color.a = fog_z; // Use alpha variable to store fog factor
1197
} else {
1198
d->color.a = v->cn[3];
1199
}
1200
}
1201
}
1202
1203
static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) {
1204
struct LoadedVertex *v1 = &rsp.loaded_vertices[vtx1_idx];
1205
struct LoadedVertex *v2 = &rsp.loaded_vertices[vtx2_idx];
1206
struct LoadedVertex *v3 = &rsp.loaded_vertices[vtx3_idx];
1207
struct LoadedVertex *v_arr[3] = {v1, v2, v3};
1208
1209
//if (rand()%2) return;
1210
1211
if (v1->clip_rej & v2->clip_rej & v3->clip_rej) {
1212
// The whole triangle lies outside the visible area
1213
return;
1214
}
1215
1216
if ((rsp.geometry_mode & G_CULL_BOTH) != 0) {
1217
float dx1 = v1->x / (v1->w) - v2->x / (v2->w);
1218
float dy1 = v1->y / (v1->w) - v2->y / (v2->w);
1219
float dx2 = v3->x / (v3->w) - v2->x / (v2->w);
1220
float dy2 = v3->y / (v3->w) - v2->y / (v2->w);
1221
float cross = dx1 * dy2 - dy1 * dx2;
1222
1223
if ((v1->w < 0) ^ (v2->w < 0) ^ (v3->w < 0)) {
1224
// If one vertex lies behind the eye, negating cross will give the correct result.
1225
// If all vertices lie behind the eye, the triangle will be rejected anyway.
1226
cross = -cross;
1227
}
1228
1229
switch (rsp.geometry_mode & G_CULL_BOTH) {
1230
case G_CULL_FRONT:
1231
if (get_mirror() && (rsp.geometry_mode & G_ZBUFFER)) {
1232
if (cross >= 0) return;
1233
}
1234
else {
1235
if (cross <= 0) return;
1236
}
1237
break;
1238
case G_CULL_BACK:
1239
if (get_mirror() && (rsp.geometry_mode & G_ZBUFFER)) {
1240
if (cross <= 0) return;
1241
}
1242
else {
1243
if (cross >= 0) return;
1244
}
1245
break;
1246
case G_CULL_BOTH:
1247
// Why is this even an option?
1248
return;
1249
}
1250
}
1251
1252
bool depth_test = (rsp.geometry_mode & G_ZBUFFER) == G_ZBUFFER;
1253
if (depth_test != rendering_state.depth_test) {
1254
gfx_flush();
1255
gfx_rapi->set_depth_test(depth_test);
1256
rendering_state.depth_test = depth_test;
1257
}
1258
1259
bool z_upd = (rdp.other_mode_l & Z_UPD) == Z_UPD;
1260
if (z_upd != rendering_state.depth_mask) {
1261
gfx_flush();
1262
gfx_rapi->set_depth_mask(z_upd);
1263
rendering_state.depth_mask = z_upd;
1264
}
1265
1266
bool zmode_decal = (rdp.other_mode_l & ZMODE_DEC) == ZMODE_DEC;
1267
if (zmode_decal != rendering_state.decal_mode) {
1268
gfx_flush();
1269
gfx_rapi->set_zmode_decal(zmode_decal);
1270
rendering_state.decal_mode = zmode_decal;
1271
}
1272
1273
if (rdp.viewport_or_scissor_changed) {
1274
if (memcmp(&rdp.viewport, &rendering_state.viewport, sizeof(rdp.viewport)) != 0) {
1275
gfx_flush();
1276
gfx_rapi->set_viewport(rdp.viewport.x, rdp.viewport.y, rdp.viewport.width, rdp.viewport.height);
1277
rendering_state.viewport = rdp.viewport;
1278
}
1279
if (memcmp(&rdp.scissor, &rendering_state.scissor, sizeof(rdp.scissor)) != 0) {
1280
gfx_flush();
1281
gfx_rapi->set_scissor(rdp.scissor.x, rdp.scissor.y, rdp.scissor.width, rdp.scissor.height);
1282
rendering_state.scissor = rdp.scissor;
1283
}
1284
rdp.viewport_or_scissor_changed = false;
1285
}
1286
1287
uint32_t cc_id = rdp.combine_mode;
1288
1289
uint32_t cycle_type = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE));
1290
bool use_alpha = (rdp.other_mode_l & (3U << 18)) == 0;
1291
bool use_fog = (rdp.other_mode_l >> 30) == G_BL_CLR_FOG;
1292
bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA;
1293
bool use_noise = (rdp.other_mode_l & G_AC_DITHER) == G_AC_DITHER && configNoiseType != 2;
1294
1295
if (texture_edge) {
1296
use_alpha = true;
1297
}
1298
1299
if (use_alpha) cc_id |= SHADER_OPT_ALPHA;
1300
if (use_fog) cc_id |= SHADER_OPT_FOG;
1301
if (texture_edge) cc_id |= SHADER_OPT_TEXTURE_EDGE;
1302
if (use_noise) cc_id |= SHADER_OPT_NOISE;
1303
1304
if (!use_alpha) {
1305
cc_id &= ~0xfff000;
1306
}
1307
1308
struct ColorCombiner *comb = gfx_lookup_or_create_color_combiner(cc_id);
1309
struct ShaderProgram *prg = comb->prg;
1310
if (prg != rendering_state.shader_program) {
1311
gfx_flush();
1312
gfx_rapi->unload_shader(rendering_state.shader_program);
1313
gfx_rapi->load_shader(prg);
1314
rendering_state.shader_program = prg;
1315
}
1316
if (use_alpha != rendering_state.alpha_blend) {
1317
gfx_flush();
1318
gfx_rapi->set_use_alpha(use_alpha);
1319
rendering_state.alpha_blend = use_alpha;
1320
}
1321
uint8_t num_inputs;
1322
bool used_textures[2];
1323
gfx_rapi->shader_get_info(prg, &num_inputs, used_textures);
1324
1325
for (int i = 0; i < 2; i++) {
1326
if (used_textures[i]) {
1327
if (rdp.textures_changed[i]) {
1328
gfx_flush();
1329
import_texture(i);
1330
rdp.textures_changed[i] = false;
1331
}
1332
bool linear_filter = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT || configHUDFiltering;
1333
if (linear_filter != rendering_state.textures[i]->linear_filter || rdp.texture_tile.cms != rendering_state.textures[i]->cms || rdp.texture_tile.cmt != rendering_state.textures[i]->cmt) {
1334
gfx_flush();
1335
gfx_rapi->set_sampler_parameters(i, linear_filter, rdp.texture_tile.cms, rdp.texture_tile.cmt);
1336
rendering_state.textures[i]->linear_filter = linear_filter;
1337
rendering_state.textures[i]->cms = rdp.texture_tile.cms;
1338
rendering_state.textures[i]->cmt = rdp.texture_tile.cmt;
1339
}
1340
}
1341
}
1342
1343
bool use_texture = used_textures[0] || used_textures[1];
1344
uint32_t tex_width = (rdp.texture_tile.lrs - rdp.texture_tile.uls + 4) / 4;
1345
uint32_t tex_height = (rdp.texture_tile.lrt - rdp.texture_tile.ult + 4) / 4;
1346
1347
bool z_is_from_0_to_1 = gfx_rapi->z_is_from_0_to_1();
1348
1349
for (int i = 0; i < 3; i++) {
1350
float z = v_arr[i]->z, w = v_arr[i]->w;
1351
if (z_is_from_0_to_1) {
1352
z = (z + w) / 2.0f;
1353
}
1354
buf_vbo[buf_vbo_len++] = v_arr[i]->x;
1355
buf_vbo[buf_vbo_len++] = v_arr[i]->y;
1356
buf_vbo[buf_vbo_len++] = z;
1357
buf_vbo[buf_vbo_len++] = w;
1358
1359
if (use_texture) {
1360
float u = (v_arr[i]->u - rdp.texture_tile.uls * 8) / 32.0f;
1361
float v = (v_arr[i]->v - rdp.texture_tile.ult * 8) / 32.0f;
1362
if ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT) {
1363
// Linear filter adds 0.5f to the coordinates
1364
u += 0.5f;
1365
v += 0.5f;
1366
}
1367
buf_vbo[buf_vbo_len++] = u / tex_width;
1368
buf_vbo[buf_vbo_len++] = v / tex_height;
1369
}
1370
1371
if (use_fog) {
1372
buf_vbo[buf_vbo_len++] = rdp.fog_color.r / 255.0f;
1373
buf_vbo[buf_vbo_len++] = rdp.fog_color.g / 255.0f;
1374
buf_vbo[buf_vbo_len++] = rdp.fog_color.b / 255.0f;
1375
buf_vbo[buf_vbo_len++] = v_arr[i]->color.a / 255.0f; // fog factor (not alpha)
1376
}
1377
1378
for (int j = 0; j < num_inputs; j++) {
1379
struct RGBA *color;
1380
struct RGBA tmp;
1381
for (int k = 0; k < 1 + (use_alpha ? 1 : 0); k++) {
1382
switch (comb->shader_input_mapping[k][j]) {
1383
case CC_PRIM:
1384
color = &rdp.prim_color;
1385
break;
1386
case CC_SHADE:
1387
switch (get_palette()) {
1388
default:
1389
color = &v_arr[i]->color;
1390
break;
1391
case 19: // Sky
1392
tmp.r = (v_arr[i]->color.r+v_arr[i]->color.g+v_arr[i]->color.b)/3;
1393
tmp.g = (v_arr[i]->color.r+v_arr[i]->color.g+v_arr[i]->color.b)/3;
1394
tmp.b = (v_arr[i]->color.r+v_arr[i]->color.g+v_arr[i]->color.b)/3;
1395
tmp.a = v_arr[i]->color.a;
1396
color = &tmp;
1397
break;
1398
}
1399
break;
1400
case CC_ENV:
1401
color = &rdp.env_color;
1402
break;
1403
case CC_LOD:
1404
{
1405
float distance_frac = (v1->w - 3000.0f) / 3000.0f;
1406
if (distance_frac < 0.0f) distance_frac = 0.0f;
1407
if (distance_frac > 1.0f) distance_frac = 1.0f;
1408
tmp.r = tmp.g = tmp.b = tmp.a = distance_frac * 255.0f;
1409
color = &tmp;
1410
break;
1411
}
1412
default:
1413
memset(&tmp, 0, sizeof(tmp));
1414
color = &tmp;
1415
break;
1416
}
1417
if (k == 0) {
1418
buf_vbo[buf_vbo_len++] = color->r / 255.0f;
1419
buf_vbo[buf_vbo_len++] = color->g / 255.0f;
1420
buf_vbo[buf_vbo_len++] = color->b / 255.0f;
1421
} else {
1422
if (use_fog && color == &v_arr[i]->color) {
1423
// Shade alpha is 100% for fog
1424
buf_vbo[buf_vbo_len++] = 1.0f;
1425
} else {
1426
buf_vbo[buf_vbo_len++] = color->a / 255.0f;
1427
}
1428
}
1429
}
1430
}
1431
/*struct RGBA *color = &v_arr[i]->color;
1432
buf_vbo[buf_vbo_len++] = color->r / 255.0f;
1433
buf_vbo[buf_vbo_len++] = color->g / 255.0f;
1434
buf_vbo[buf_vbo_len++] = color->b / 255.0f;
1435
buf_vbo[buf_vbo_len++] = color->a / 255.0f;*/
1436
}
1437
if (++buf_vbo_num_tris == MAX_BUFFERED) {
1438
gfx_flush();
1439
}
1440
}
1441
1442
static void gfx_sp_geometry_mode(uint32_t clear, uint32_t set) {
1443
rsp.geometry_mode &= ~clear;
1444
rsp.geometry_mode |= set;
1445
}
1446
1447
static void gfx_calc_and_set_viewport(const Vp_t *viewport) {
1448
// 2 bits fraction
1449
float width = 2.0f * viewport->vscale[0] / 4.0f;
1450
float height = 2.0f * viewport->vscale[1] / 4.0f;
1451
float x = (viewport->vtrans[0] / 4.0f) - width / 2.0f;
1452
float y = SCREEN_HEIGHT - ((viewport->vtrans[1] / 4.0f) + height / 2.0f);
1453
1454
width *= RATIO_X;
1455
height *= RATIO_Y;
1456
x *= RATIO_X;
1457
y *= RATIO_Y;
1458
1459
rdp.viewport.x = x;
1460
rdp.viewport.y = y;
1461
rdp.viewport.width = width;
1462
rdp.viewport.height = height;
1463
1464
rdp.viewport_or_scissor_changed = true;
1465
}
1466
1467
static void gfx_sp_movemem(uint8_t index, uint8_t offset, const void* data) {
1468
switch (index) {
1469
case G_MV_VIEWPORT:
1470
gfx_calc_and_set_viewport((const Vp_t *) data);
1471
break;
1472
#ifndef F3DEX_GBI_2
1473
case G_MV_LOOKATY:
1474
case G_MV_LOOKATX:
1475
memcpy(&rsp.lookat[(index - G_MV_LOOKATY) / 2], data, sizeof(Light_t));
1476
//rsp.lights_changed = 1;
1477
break;
1478
#endif
1479
#ifdef F3DEX_GBI_2
1480
case G_MV_LIGHT: {
1481
int lightidx = offset / 24 - 2;
1482
if (lightidx >= 0 && lightidx <= MAX_LIGHTS) { // skip lookat
1483
// NOTE: reads out of bounds if it is an ambient light
1484
memcpy(rsp.current_lights + lightidx, data, sizeof(Light_t));
1485
}
1486
else if (lightidx < 0) {
1487
int lookat_idx = offset / 24;
1488
if (lookat_idx == 0) {
1489
memcpy(&rsp.lookat[0], data, sizeof(Light_t));
1490
}
1491
else if (lookat_idx == 1) {
1492
memcpy(&rsp.lookat[1], data, sizeof(Light_t));
1493
}
1494
rsp.lights_changed = 1;
1495
}
1496
break;
1497
}
1498
#else
1499
case G_MV_L0:
1500
case G_MV_L1:
1501
case G_MV_L2:
1502
// NOTE: reads out of bounds if it is an ambient light
1503
memcpy(rsp.current_lights + (index - G_MV_L0) / 2, data, sizeof(Light_t));
1504
break;
1505
#endif
1506
}
1507
}
1508
1509
static void gfx_sp_moveword(uint8_t index, uint16_t offset, uint32_t data) {
1510
switch (index) {
1511
case G_MW_NUMLIGHT:
1512
#ifdef F3DEX_GBI_2
1513
rsp.current_num_lights = data / 24 + 1; // add ambient light
1514
#else
1515
// Ambient light is included
1516
// The 31th bit is a flag that lights should be recalculated
1517
rsp.current_num_lights = (data - 0x80000000U) / 32;
1518
#endif
1519
rsp.lights_changed = 1;
1520
break;
1521
case G_MW_FOG:
1522
rsp.fog_mul = (int16_t)(data >> 16);
1523
rsp.fog_offset = (int16_t)data;
1524
break;
1525
}
1526
}
1527
1528
static void gfx_sp_texture(uint16_t sc, uint16_t tc, uint8_t level, uint8_t tile, uint8_t on) {
1529
rsp.texture_scaling_factor.s = sc;
1530
rsp.texture_scaling_factor.t = tc;
1531
}
1532
1533
static void gfx_dp_set_scissor(uint32_t mode, uint32_t ulx, uint32_t uly, uint32_t lrx, uint32_t lry) {
1534
float x = ulx / 4.0f * RATIO_X;
1535
float y = (SCREEN_HEIGHT - lry / 4.0f) * RATIO_Y;
1536
float width = (lrx - ulx) / 4.0f * RATIO_X;
1537
float height = (lry - uly) / 4.0f * RATIO_Y;
1538
1539
rdp.scissor.x = x;
1540
rdp.scissor.y = y;
1541
rdp.scissor.width = width;
1542
rdp.scissor.height = height;
1543
1544
rdp.viewport_or_scissor_changed = true;
1545
}
1546
1547
static void gfx_dp_set_texture_image(uint32_t format, uint32_t size, uint32_t width, const void* addr) {
1548
rdp.texture_to_load.addr = addr;
1549
rdp.texture_to_load.siz = size;
1550
}
1551
1552
static void gfx_dp_set_tile(uint8_t fmt, uint32_t siz, uint32_t line, uint32_t tmem, uint8_t tile, uint32_t palette, uint32_t cmt, uint32_t maskt, uint32_t shiftt, uint32_t cms, uint32_t masks, uint32_t shifts) {
1553
if (tile == G_TX_RENDERTILE) {
1554
SUPPORT_CHECK(palette == 0); // palette should set upper 4 bits of color index in 4b mode
1555
rdp.texture_tile.fmt = fmt;
1556
rdp.texture_tile.siz = siz;
1557
rdp.texture_tile.cms = cms;
1558
rdp.texture_tile.cmt = cmt;
1559
rdp.texture_tile.line_size_bytes = line * 8;
1560
rdp.textures_changed[0] = true;
1561
rdp.textures_changed[1] = true;
1562
}
1563
1564
if (tile == G_TX_LOADTILE) {
1565
rdp.texture_to_load.tile_number = tmem / 256;
1566
}
1567
}
1568
1569
static void gfx_dp_set_tile_size(uint8_t tile, uint16_t uls, uint16_t ult, uint16_t lrs, uint16_t lrt) {
1570
if (tile == G_TX_RENDERTILE) {
1571
rdp.texture_tile.uls = uls;
1572
rdp.texture_tile.ult = ult;
1573
rdp.texture_tile.lrs = lrs;
1574
rdp.texture_tile.lrt = lrt;
1575
rdp.textures_changed[0] = true;
1576
rdp.textures_changed[1] = true;
1577
}
1578
}
1579
1580
static void gfx_dp_load_tlut(uint8_t tile, uint32_t high_index) {
1581
SUPPORT_CHECK(tile == G_TX_LOADTILE);
1582
SUPPORT_CHECK(rdp.texture_to_load.siz == G_IM_SIZ_16b);
1583
rdp.palette = rdp.texture_to_load.addr;
1584
}
1585
1586
static void gfx_dp_load_block(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t dxt) {
1587
if (tile == 1) return;
1588
SUPPORT_CHECK(tile == G_TX_LOADTILE);
1589
SUPPORT_CHECK(uls == 0);
1590
SUPPORT_CHECK(ult == 0);
1591
1592
// The lrs field rather seems to be number of pixels to load
1593
uint32_t word_size_shift;
1594
switch (rdp.texture_to_load.siz) {
1595
case G_IM_SIZ_4b:
1596
word_size_shift = 0; // Or -1? It's unused in SM64 anyway.
1597
break;
1598
case G_IM_SIZ_8b:
1599
word_size_shift = 0;
1600
break;
1601
case G_IM_SIZ_16b:
1602
word_size_shift = 1;
1603
break;
1604
case G_IM_SIZ_32b:
1605
word_size_shift = 2;
1606
break;
1607
}
1608
uint32_t size_bytes = (lrs + 1) << word_size_shift;
1609
rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = size_bytes;
1610
assert(size_bytes <= 4096 && "bug: too big texture");
1611
rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr;
1612
1613
rdp.textures_changed[rdp.texture_to_load.tile_number] = true;
1614
}
1615
1616
static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t lrt) {
1617
if (tile == 1) return;
1618
SUPPORT_CHECK(tile == G_TX_LOADTILE);
1619
SUPPORT_CHECK(uls == 0);
1620
SUPPORT_CHECK(ult == 0);
1621
1622
uint32_t word_size_shift;
1623
switch (rdp.texture_to_load.siz) {
1624
case G_IM_SIZ_4b:
1625
word_size_shift = 0;
1626
break;
1627
case G_IM_SIZ_8b:
1628
word_size_shift = 0;
1629
break;
1630
case G_IM_SIZ_16b:
1631
word_size_shift = 1;
1632
break;
1633
case G_IM_SIZ_32b:
1634
word_size_shift = 2;
1635
break;
1636
}
1637
1638
uint32_t size_bytes = (((lrs >> G_TEXTURE_IMAGE_FRAC) + 1) * ((lrt >> G_TEXTURE_IMAGE_FRAC) + 1)) << word_size_shift;
1639
rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = size_bytes;
1640
1641
assert(size_bytes <= 4096 && "bug: too big texture");
1642
rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr;
1643
rdp.texture_tile.uls = uls;
1644
rdp.texture_tile.ult = ult;
1645
rdp.texture_tile.lrs = lrs;
1646
rdp.texture_tile.lrt = lrt;
1647
1648
rdp.textures_changed[rdp.texture_to_load.tile_number] = true;
1649
}
1650
1651
1652
static uint8_t color_comb_component(uint32_t v) {
1653
switch (v) {
1654
case G_CCMUX_TEXEL0:
1655
return CC_TEXEL0;
1656
case G_CCMUX_TEXEL1:
1657
return CC_TEXEL1;
1658
case G_CCMUX_PRIMITIVE:
1659
return CC_PRIM;
1660
case G_CCMUX_SHADE:
1661
return CC_SHADE;
1662
case G_CCMUX_ENVIRONMENT:
1663
return CC_ENV;
1664
case G_CCMUX_TEXEL0_ALPHA:
1665
return CC_TEXEL0A;
1666
case G_CCMUX_LOD_FRACTION:
1667
return CC_LOD;
1668
default:
1669
return CC_0;
1670
}
1671
}
1672
1673
static inline uint32_t color_comb(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
1674
return color_comb_component(a) |
1675
(color_comb_component(b) << 3) |
1676
(color_comb_component(c) << 6) |
1677
(color_comb_component(d) << 9);
1678
}
1679
1680
static void gfx_dp_set_combine_mode(uint32_t rgb, uint32_t alpha) {
1681
rdp.combine_mode = rgb | (alpha << 12);
1682
}
1683
1684
static void gfx_dp_set_env_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
1685
rdp.env_color.r = r;
1686
rdp.env_color.g = g;
1687
rdp.env_color.b = b;
1688
rdp.env_color.a = a;
1689
}
1690
1691
static void gfx_dp_set_prim_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
1692
rdp.prim_color.r = r;
1693
rdp.prim_color.g = g;
1694
rdp.prim_color.b = b;
1695
rdp.prim_color.a = a;
1696
}
1697
1698
static void gfx_dp_set_fog_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
1699
1700
switch (get_palette()) {
1701
default:
1702
rdp.fog_color.r = r;
1703
rdp.fog_color.g = g;
1704
rdp.fog_color.b = b;
1705
break;
1706
case 2: // Bob-omb Battlefield
1707
rdp.fog_color.r = 195;
1708
rdp.fog_color.g = 229;
1709
rdp.fog_color.b = 255;
1710
break;
1711
case 4: // Jolly Roger Bay
1712
case 15: // Tick Tock Clock
1713
rdp.fog_color.r = 11;
1714
rdp.fog_color.g = 3;
1715
rdp.fog_color.b = 63;
1716
break;
1717
}
1718
rdp.fog_color.a = a;
1719
}
1720
1721
static void gfx_dp_set_fill_color(uint32_t packed_color) {
1722
uint16_t col16 = (uint16_t)packed_color;
1723
uint32_t r = col16 >> 11;
1724
uint32_t g = (col16 >> 6) & 0x1f;
1725
uint32_t b = (col16 >> 1) & 0x1f;
1726
uint32_t a = col16 & 1;
1727
rdp.fill_color.r = SCALE_5_8(r);
1728
rdp.fill_color.g = SCALE_5_8(g);
1729
rdp.fill_color.b = SCALE_5_8(b);
1730
rdp.fill_color.a = a * 255;
1731
}
1732
1733
static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) {
1734
uint32_t saved_other_mode_h = rdp.other_mode_h;
1735
uint32_t cycle_type = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE));
1736
1737
if (cycle_type == G_CYC_COPY) {
1738
rdp.other_mode_h = (rdp.other_mode_h & ~(3U << G_MDSFT_TEXTFILT)) | G_TF_POINT;
1739
}
1740
1741
// U10.2 coordinates
1742
float ulxf = ulx;
1743
float ulyf = uly;
1744
float lrxf = lrx;
1745
float lryf = lry;
1746
1747
ulxf = ulxf / (4.0f * HALF_SCREEN_WIDTH) - 1.0f;
1748
ulyf = -(ulyf / (4.0f * HALF_SCREEN_HEIGHT)) + 1.0f;
1749
lrxf = lrxf / (4.0f * HALF_SCREEN_WIDTH) - 1.0f;
1750
lryf = -(lryf / (4.0f * HALF_SCREEN_HEIGHT)) + 1.0f;
1751
1752
ulxf = gfx_adjust_x_for_aspect_ratio(ulxf);
1753
lrxf = gfx_adjust_x_for_aspect_ratio(lrxf);
1754
1755
struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0];
1756
struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1];
1757
struct LoadedVertex* lr = &rsp.loaded_vertices[MAX_VERTICES + 2];
1758
struct LoadedVertex* ur = &rsp.loaded_vertices[MAX_VERTICES + 3];
1759
1760
ul->x = ulxf;
1761
ul->y = ulyf;
1762
ul->z = -1.0f;
1763
ul->w = 1.0f;
1764
1765
ll->x = ulxf;
1766
ll->y = lryf;
1767
ll->z = -1.0f;
1768
ll->w = 1.0f;
1769
1770
lr->x = lrxf;
1771
lr->y = lryf;
1772
lr->z = -1.0f;
1773
lr->w = 1.0f;
1774
1775
ur->x = lrxf;
1776
ur->y = ulyf;
1777
ur->z = -1.0f;
1778
ur->w = 1.0f;
1779
1780
// The coordinates for texture rectangle shall bypass the viewport setting
1781
struct XYWidthHeight default_viewport = {0, 0, gfx_current_dimensions.width, gfx_current_dimensions.height};
1782
struct XYWidthHeight viewport_saved = rdp.viewport;
1783
uint32_t geometry_mode_saved = rsp.geometry_mode;
1784
1785
rdp.viewport = default_viewport;
1786
rdp.viewport_or_scissor_changed = true;
1787
rsp.geometry_mode = 0;
1788
1789
gfx_sp_tri1(MAX_VERTICES + 0, MAX_VERTICES + 1, MAX_VERTICES + 3);
1790
gfx_sp_tri1(MAX_VERTICES + 1, MAX_VERTICES + 2, MAX_VERTICES + 3);
1791
1792
rsp.geometry_mode = geometry_mode_saved;
1793
rdp.viewport = viewport_saved;
1794
rdp.viewport_or_scissor_changed = true;
1795
1796
if (cycle_type == G_CYC_COPY) {
1797
rdp.other_mode_h = saved_other_mode_h;
1798
}
1799
}
1800
1801
static void gfx_dp_texture_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry, uint8_t tile, int16_t uls, int16_t ult, int16_t dsdx, int16_t dtdy, bool flip) {
1802
uint32_t saved_combine_mode = rdp.combine_mode;
1803
if ((rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_COPY) {
1804
// Per RDP Command Summary Set Tile's shift s and this dsdx should be set to 4 texels
1805
// Divide by 4 to get 1 instead
1806
dsdx >>= 2;
1807
1808
// Color combiner is turned off in copy mode
1809
gfx_dp_set_combine_mode(color_comb(0, 0, 0, G_CCMUX_TEXEL0), color_comb(0, 0, 0, G_ACMUX_TEXEL0));
1810
1811
// Per documentation one extra pixel is added in this modes to each edge
1812
lrx += 1 << 2;
1813
lry += 1 << 2;
1814
}
1815
1816
// uls and ult are S10.5
1817
// dsdx and dtdy are S5.10
1818
// lrx, lry, ulx, uly are U10.2
1819
// lrs, lrt are S10.5
1820
if (flip) {
1821
dsdx = -dsdx;
1822
dtdy = -dtdy;
1823
}
1824
int16_t width = !flip ? lrx - ulx : lry - uly;
1825
int16_t height = !flip ? lry - uly : lrx - ulx;
1826
float lrs = ((uls << 7) + dsdx * width) >> 7;
1827
float lrt = ((ult << 7) + dtdy * height) >> 7;
1828
1829
struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0];
1830
struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1];
1831
struct LoadedVertex* lr = &rsp.loaded_vertices[MAX_VERTICES + 2];
1832
struct LoadedVertex* ur = &rsp.loaded_vertices[MAX_VERTICES + 3];
1833
ul->u = uls;
1834
ul->v = ult;
1835
lr->u = lrs;
1836
lr->v = lrt;
1837
if (!flip) {
1838
ll->u = uls;
1839
ll->v = lrt;
1840
ur->u = lrs;
1841
ur->v = ult;
1842
} else {
1843
ll->u = lrs;
1844
ll->v = ult;
1845
ur->u = uls;
1846
ur->v = lrt;
1847
}
1848
1849
gfx_draw_rectangle(ulx, uly, lrx, lry);
1850
rdp.combine_mode = saved_combine_mode;
1851
}
1852
1853
static void gfx_dp_fill_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) {
1854
if (rdp.color_image_address == rdp.z_buf_address) {
1855
// Don't clear Z buffer here since we already did it with glClear
1856
return;
1857
}
1858
uint32_t mode = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE));
1859
1860
if (mode == G_CYC_COPY || mode == G_CYC_FILL) {
1861
// Per documentation one extra pixel is added in this modes to each edge
1862
lrx += 1 << 2;
1863
lry += 1 << 2;
1864
}
1865
1866
for (int i = MAX_VERTICES; i < MAX_VERTICES + 4; i++) {
1867
struct LoadedVertex* v = &rsp.loaded_vertices[i];
1868
v->color = rdp.fill_color;
1869
}
1870
1871
uint32_t saved_combine_mode = rdp.combine_mode;
1872
gfx_dp_set_combine_mode(color_comb(0, 0, 0, G_CCMUX_SHADE), color_comb(0, 0, 0, G_ACMUX_SHADE));
1873
gfx_draw_rectangle(ulx, uly, lrx, lry);
1874
rdp.combine_mode = saved_combine_mode;
1875
}
1876
1877
static void gfx_dp_set_z_image(void *z_buf_address) {
1878
rdp.z_buf_address = z_buf_address;
1879
}
1880
1881
static void gfx_dp_set_color_image(uint32_t format, uint32_t size, uint32_t width, void* address) {
1882
rdp.color_image_address = address;
1883
}
1884
1885
static void gfx_sp_set_other_mode(uint32_t shift, uint32_t num_bits, uint64_t mode) {
1886
uint64_t mask = (((uint64_t)1 << num_bits) - 1) << shift;
1887
uint64_t om = rdp.other_mode_l | ((uint64_t)rdp.other_mode_h << 32);
1888
om = (om & ~mask) | mode;
1889
rdp.other_mode_l = (uint32_t)om;
1890
rdp.other_mode_h = (uint32_t)(om >> 32);
1891
}
1892
1893
static inline void *seg_addr(uintptr_t w1) {
1894
return (void *) w1;
1895
}
1896
1897
#define C0(pos, width) ((cmd->words.w0 >> (pos)) & ((1U << width) - 1))
1898
#define C1(pos, width) ((cmd->words.w1 >> (pos)) & ((1U << width) - 1))
1899
1900
static void gfx_run_dl(Gfx* cmd) {
1901
for (;;) {
1902
uint32_t opcode = cmd->words.w0 >> 24;
1903
1904
switch (opcode) {
1905
// RSP commands:
1906
case G_MTX:
1907
#ifdef F3DEX_GBI_2
1908
gfx_sp_matrix(C0(0, 8) ^ G_MTX_PUSH, (const int32_t *) seg_addr(cmd->words.w1));
1909
#else
1910
gfx_sp_matrix(C0(16, 8), (const int32_t *) seg_addr(cmd->words.w1));
1911
#endif
1912
break;
1913
case (uint8_t)G_POPMTX:
1914
#ifdef F3DEX_GBI_2
1915
gfx_sp_pop_matrix(cmd->words.w1 / 64);
1916
#else
1917
gfx_sp_pop_matrix(1);
1918
#endif
1919
break;
1920
case G_MOVEMEM:
1921
#ifdef F3DEX_GBI_2
1922
gfx_sp_movemem(C0(0, 8), C0(8, 8) * 8, seg_addr(cmd->words.w1));
1923
#else
1924
gfx_sp_movemem(C0(16, 8), 0, seg_addr(cmd->words.w1));
1925
#endif
1926
break;
1927
case (uint8_t)G_MOVEWORD:
1928
#ifdef F3DEX_GBI_2
1929
gfx_sp_moveword(C0(16, 8), C0(0, 16), cmd->words.w1);
1930
#else
1931
gfx_sp_moveword(C0(0, 8), C0(8, 16), cmd->words.w1);
1932
#endif
1933
break;
1934
case (uint8_t)G_TEXTURE:
1935
#ifdef F3DEX_GBI_2
1936
gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(1, 7));
1937
#else
1938
gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(0, 8));
1939
#endif
1940
break;
1941
case G_VTX:
1942
#ifdef F3DEX_GBI_2
1943
gfx_sp_vertex(C0(12, 8), C0(1, 7) - C0(12, 8), seg_addr(cmd->words.w1));
1944
#elif defined(F3DEX_GBI) || defined(F3DLP_GBI)
1945
gfx_sp_vertex(C0(10, 6), C0(16, 8) / 2, seg_addr(cmd->words.w1));
1946
#else
1947
gfx_sp_vertex((C0(0, 16)) / sizeof(Vtx), C0(16, 4), seg_addr(cmd->words.w1));
1948
#endif
1949
break;
1950
case G_DL:
1951
if (C0(16, 1) == 0) {
1952
// Push return address
1953
gfx_run_dl((Gfx *)seg_addr(cmd->words.w1));
1954
} else {
1955
cmd = (Gfx *)seg_addr(cmd->words.w1);
1956
--cmd; // increase after break
1957
}
1958
break;
1959
case (uint8_t)G_ENDDL:
1960
return;
1961
#ifdef F3DEX_GBI_2
1962
case G_GEOMETRYMODE:
1963
gfx_sp_geometry_mode(~C0(0, 24), cmd->words.w1);
1964
break;
1965
#else
1966
case (uint8_t)G_SETGEOMETRYMODE:
1967
gfx_sp_geometry_mode(0, cmd->words.w1);
1968
break;
1969
case (uint8_t)G_CLEARGEOMETRYMODE:
1970
gfx_sp_geometry_mode(cmd->words.w1, 0);
1971
break;
1972
#endif
1973
case (uint8_t)G_TRI1:
1974
#ifdef F3DEX_GBI_2
1975
gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2);
1976
#elif defined(F3DEX_GBI) || defined(F3DLP_GBI)
1977
gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2);
1978
#else
1979
gfx_sp_tri1(C1(16, 8) / 10, C1(8, 8) / 10, C1(0, 8) / 10);
1980
#endif
1981
break;
1982
#if defined(F3DEX_GBI) || defined(F3DLP_GBI)
1983
case (uint8_t)G_TRI2:
1984
gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2);
1985
gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2);
1986
break;
1987
#endif
1988
case (uint8_t)G_SETOTHERMODE_L:
1989
#ifdef F3DEX_GBI_2
1990
gfx_sp_set_other_mode(31 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, cmd->words.w1);
1991
#else
1992
gfx_sp_set_other_mode(C0(8, 8), C0(0, 8), cmd->words.w1);
1993
#endif
1994
break;
1995
case (uint8_t)G_SETOTHERMODE_H:
1996
#ifdef F3DEX_GBI_2
1997
gfx_sp_set_other_mode(63 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, (uint64_t) cmd->words.w1 << 32);
1998
#else
1999
gfx_sp_set_other_mode(C0(8, 8) + 32, C0(0, 8), (uint64_t) cmd->words.w1 << 32);
2000
#endif
2001
break;
2002
#ifdef F3D_OLD
2003
case (uint8_t)G_RDPHALF_2:
2004
#else
2005
case (uint8_t)G_RDPHALF_1:
2006
#endif
2007
switch (rsp.saved_opcode) {
2008
case G_TEXRECT:
2009
case G_TEXRECTFLIP:
2010
#ifdef F3DEX_GBI_2E
2011
rsp.saved_ulx = (int32_t)(C0(0, 24) << 8) >> 8;
2012
#endif
2013
rsp.saved_uls = (uint16_t)C1(16, 16);
2014
rsp.saved_ult = (uint16_t)C1(0, 16);
2015
break;
2016
#ifdef F3DEX_GBI_2E
2017
case G_FILLRECT:
2018
{
2019
int32_t ulx = (int32_t)(C0(0, 24) << 8) >> 8;
2020
int32_t uly = (int32_t)(C1(0, 24) << 8) >> 8;
2021
gfx_dp_fill_rectangle(ulx, uly, rsp.saved_lrx, rsp.saved_lry);
2022
rsp.saved_opcode = G_NOOP;
2023
break;
2024
}
2025
#endif
2026
}
2027
break;
2028
#ifdef F3D_OLD
2029
case (uint8_t)G_RDPHALF_CONT:
2030
#else
2031
case (uint8_t)G_RDPHALF_2:
2032
#endif
2033
switch (rsp.saved_opcode) {
2034
case G_TEXRECT:
2035
case G_TEXRECTFLIP:
2036
{
2037
uint8_t tile = rsp.saved_tile;
2038
int32_t ulx = rsp.saved_ulx, lrx = rsp.saved_lrx, lry = rsp.saved_lry;
2039
uint16_t uls = rsp.saved_uls, ult = rsp.saved_ult;
2040
#ifdef F3DEX_GBI_2E
2041
int32_t uly = (int32_t)(C0(0, 24) << 8) >> 8;
2042
#else
2043
int32_t uly = rsp.saved_uly;
2044
#endif
2045
uint16_t dsdx = (uint16_t)C1(16, 16);
2046
uint16_t dtdy = (uint16_t)C1(0, 16);
2047
gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, rsp.saved_opcode == G_TEXRECTFLIP);
2048
rsp.saved_opcode = G_NOOP;
2049
break;
2050
}
2051
}
2052
2053
// RDP Commands:
2054
case G_SETTIMG:
2055
gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), seg_addr(cmd->words.w1));
2056
break;
2057
case G_LOADBLOCK:
2058
gfx_dp_load_block(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12));
2059
break;
2060
case G_LOADTILE:
2061
gfx_dp_load_tile(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12));
2062
break;
2063
case G_SETTILE:
2064
gfx_dp_set_tile(C0(21, 3), C0(19, 2), C0(9, 9), C0(0, 9), C1(24, 3), C1(20, 4), C1(18, 2), C1(14, 4), C1(10, 4), C1(8, 2), C1(4, 4), C1(0, 4));
2065
break;
2066
case G_SETTILESIZE:
2067
gfx_dp_set_tile_size(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12));
2068
break;
2069
case G_LOADTLUT:
2070
gfx_dp_load_tlut(C1(24, 3), C1(14, 10));
2071
break;
2072
case G_SETENVCOLOR:
2073
gfx_dp_set_env_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8));
2074
break;
2075
case G_SETPRIMCOLOR:
2076
gfx_dp_set_prim_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8));
2077
break;
2078
case G_SETFOGCOLOR:
2079
gfx_dp_set_fog_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8));
2080
break;
2081
case G_SETFILLCOLOR:
2082
gfx_dp_set_fill_color(cmd->words.w1);
2083
break;
2084
case G_SETCOMBINE:
2085
gfx_dp_set_combine_mode(
2086
color_comb(C0(20, 4), C1(28, 4), C0(15, 5), C1(15, 3)),
2087
color_comb(C0(12, 3), C1(12, 3), C0(9, 3), C1(9, 3)));
2088
/*color_comb(C0(5, 4), C1(24, 4), C0(0, 5), C1(6, 3)),
2089
color_comb(C1(21, 3), C1(3, 3), C1(18, 3), C1(0, 3)));*/
2090
break;
2091
// G_SETPRIMCOLOR, G_CCMUX_PRIMITIVE, G_ACMUX_PRIMITIVE, is used by Goddard
2092
// G_CCMUX_TEXEL1, LOD_FRACTION is used in Bowser room 1
2093
case G_TEXRECT:
2094
case G_TEXRECTFLIP:
2095
{
2096
rsp.saved_opcode = opcode;
2097
#ifdef F3DEX_GBI_2E
2098
rsp.saved_lrx = (int32_t)(C0(0, 24) << 8) >> 8;
2099
rsp.saved_lry = (int32_t)(C1(0, 24) << 8) >> 8;
2100
rsp.saved_tile = (int32_t)C1(24, 3);
2101
#else
2102
rsp.saved_lrx = C0(12, 12);
2103
rsp.saved_lry = C0(0, 12);
2104
rsp.saved_tile = C1(24, 3);
2105
rsp.saved_ulx = C1(12, 12);
2106
rsp.saved_uly = C1(0, 12);
2107
#endif
2108
break;
2109
}
2110
case G_FILLRECT:
2111
#ifdef F3DEX_GBI_2E
2112
{
2113
rsp.saved_opcode = G_FILLRECT;
2114
rsp.saved_lrx = (int32_t)(C0(0, 24) << 8) >> 8;
2115
rsp.saved_lry = (int32_t)(C1(0, 24) << 8) >> 8;
2116
break;
2117
}
2118
#else
2119
gfx_dp_fill_rectangle(C1(12, 12), C1(0, 12), C0(12, 12), C0(0, 12));
2120
break;
2121
#endif
2122
case G_SETSCISSOR:
2123
gfx_dp_set_scissor(C1(24, 2), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12));
2124
break;
2125
case G_SETZIMG:
2126
gfx_dp_set_z_image(seg_addr(cmd->words.w1));
2127
break;
2128
case G_SETCIMG:
2129
gfx_dp_set_color_image(C0(21, 3), C0(19, 2), C0(0, 11), seg_addr(cmd->words.w1));
2130
break;
2131
}
2132
++cmd;
2133
}
2134
}
2135
2136
static void gfx_sp_reset() {
2137
rsp.modelview_matrix_stack_size = 1;
2138
rsp.current_num_lights = 2;
2139
rsp.lights_changed = true;
2140
memset(rsp.lookat, 0, sizeof(rsp.lookat));
2141
rsp.lookat[0].dir[0] = 0;
2142
rsp.lookat[0].dir[1] = 127;
2143
rsp.lookat[0].dir[2] = 0;
2144
rsp.lookat[1].dir[0] = 127;
2145
rsp.lookat[1].dir[1] = 0;
2146
rsp.lookat[1].dir[2] = 0;
2147
calculate_normal_dir(&rsp.lookat[0], rsp.current_lookat_coeffs[0]);
2148
calculate_normal_dir(&rsp.lookat[1], rsp.current_lookat_coeffs[1]);
2149
}
2150
2151
void gfx_get_dimensions(uint32_t *width, uint32_t *height) {
2152
gfx_wapi->get_dimensions(width, height);
2153
}
2154
2155
void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, const char *game_name, bool start_in_fullscreen) {
2156
gfx_wapi = wapi;
2157
gfx_rapi = rapi;
2158
gfx_wapi->init(game_name, start_in_fullscreen);
2159
gfx_rapi->init();
2160
2161
// Used in the 120 star TAS
2162
static uint32_t precomp_shaders[] = {
2163
0x01200200,
2164
0x00000045,
2165
0x00000200,
2166
0x01200a00,
2167
0x00000a00,
2168
0x01a00045,
2169
0x00000551,
2170
0x01045045,
2171
0x05a00a00,
2172
0x01200045,
2173
0x05045045,
2174
0x01045a00,
2175
0x01a00a00,
2176
0x0000038d,
2177
0x01081081,
2178
0x0120038d,
2179
0x03200045,
2180
0x03200a00,
2181
0x01a00a6f,
2182
0x01141045,
2183
0x07a00a00,
2184
0x05200200,
2185
0x03200200,
2186
0x09200200,
2187
0x0920038d,
2188
0x09200045
2189
};
2190
for (size_t i = 0; i < sizeof(precomp_shaders) / sizeof(uint32_t); i++) {
2191
gfx_lookup_or_create_shader_program(precomp_shaders[i]);
2192
}
2193
}
2194
2195
struct GfxRenderingAPI *gfx_get_current_rendering_api(void) {
2196
return gfx_rapi;
2197
}
2198
2199
bool gfx_begin_frame_render(void) {
2200
gfx_sp_reset();
2201
2202
if (!gfx_wapi->start_frame()) {
2203
dropped_frame = true;
2204
return false;
2205
}
2206
2207
dropped_frame = false;
2208
gfx_rapi->start_frame();
2209
return true;
2210
}
2211
2212
void gfx_end_frame_render(void) {
2213
if (!dropped_frame) {
2214
gfx_rapi->end_frame();
2215
}
2216
}
2217
2218
void gfx_swap_buffers_begin(void) {
2219
if (!dropped_frame) {
2220
gfx_wapi->swap_buffers_begin();
2221
}
2222
}
2223
2224
void gfx_start_frame(void) {
2225
gfx_wapi->handle_events();
2226
2227
uint32_t window_width, window_height;
2228
gfx_wapi->get_dimensions(&window_width, &window_height);
2229
2230
if (window_height == 0) {
2231
// Avoid division by zero
2232
window_height = 1;
2233
}
2234
2235
if (configInternalResolution > 0.0f) {
2236
2237
float aspect_ratio = (float)window_width / (float)window_height;
2238
2239
if (configAspectRatio == 1) {
2240
aspect_ratio = 4.0f / 3.0f;
2241
}
2242
else if (configAspectRatio == 2) {
2243
aspect_ratio = 16.0f / 9.0f;
2244
}
2245
2246
gfx_current_dimensions.height = (uint32_t)(240.0f * configInternalResolution);
2247
gfx_current_dimensions.width = (uint32_t)((float)gfx_current_dimensions.height * aspect_ratio);
2248
}
2249
else {
2250
if (configAspectRatio == 1 || configAspectRatio == 2) {
2251
2252
float target_aspect = (configAspectRatio == 1) ? (4.0f / 3.0f) : (16.0f / 9.0f);
2253
float window_aspect = (float)window_width / (float)window_height;
2254
2255
if (window_aspect > target_aspect) {
2256
gfx_current_dimensions.height = window_height;
2257
gfx_current_dimensions.width = (uint32_t)((float)window_height * target_aspect);
2258
}
2259
else if (window_aspect < target_aspect) {
2260
gfx_current_dimensions.width = window_width;
2261
gfx_current_dimensions.height = (uint32_t)((float)window_width / target_aspect);
2262
}
2263
else {
2264
gfx_current_dimensions.width = window_width;
2265
gfx_current_dimensions.height = window_height;
2266
}
2267
}
2268
else {
2269
2270
gfx_current_dimensions.width = window_width;
2271
gfx_current_dimensions.height = window_height;
2272
}
2273
}
2274
2275
if (gfx_current_dimensions.height == 0) {
2276
// Avoid division by zero
2277
gfx_current_dimensions.height = 1;
2278
}
2279
if (gfx_current_dimensions.width == 0) {
2280
// Avoid division by zero
2281
gfx_current_dimensions.width = 1;
2282
}
2283
2284
gfx_current_dimensions.aspect_ratio = (float)gfx_current_dimensions.width / (float)gfx_current_dimensions.height;
2285
}
2286
2287
void gfx_run(Gfx *commands) {
2288
if (!gfx_begin_frame_render()) {
2289
return;
2290
}
2291
2292
gfx_run_dl(commands);
2293
gfx_flush();
2294
gfx_end_frame_render();
2295
gfx_swap_buffers_begin();
2296
}
2297
2298
void gfx_end_frame(void) {
2299
if (!dropped_frame) {
2300
gfx_rapi->finish_render();
2301
gfx_wapi->swap_buffers_end();
2302
}
2303
}
2304
2305