| 1 | // MIT License | 
|---|
| 2 |  | 
|---|
| 3 | // Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com | 
|---|
| 4 |  | 
|---|
| 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy | 
|---|
| 6 | // of this software and associated documentation files (the "Software"), to deal | 
|---|
| 7 | // in the Software without restriction, including without limitation the rights | 
|---|
| 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|---|
| 9 | // copies of the Software, and to permit persons to whom the Software is | 
|---|
| 10 | // furnished to do so, subject to the following conditions: | 
|---|
| 11 |  | 
|---|
| 12 | // The above copyright notice and this permission notice shall be included in all | 
|---|
| 13 | // copies or substantial portions of the Software. | 
|---|
| 14 |  | 
|---|
| 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|---|
| 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|---|
| 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
|---|
| 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|---|
| 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|---|
| 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
|---|
| 21 | // SOFTWARE. | 
|---|
| 22 |  | 
|---|
| 23 | #include "api.h" | 
|---|
| 24 | #include "core.h" | 
|---|
| 25 | #include "tilesheet.h" | 
|---|
| 26 |  | 
|---|
| 27 | #include <string.h> | 
|---|
| 28 | #include <stdlib.h> | 
|---|
| 29 | #include <math.h> | 
|---|
| 30 | #include <float.h> | 
|---|
| 31 |  | 
|---|
| 32 | #define TRANSPARENT_COLOR 255 | 
|---|
| 33 |  | 
|---|
| 34 | typedef void(*PixelFunc)(tic_mem* memory, s32 x, s32 y, u8 color); | 
|---|
| 35 |  | 
|---|
| 36 | static tic_tilesheet getTileSheetFromSegment(tic_mem* memory, u8 segment) | 
|---|
| 37 | { | 
|---|
| 38 | u8* src; | 
|---|
| 39 | switch (segment) { | 
|---|
| 40 | case 0: | 
|---|
| 41 | case 1: | 
|---|
| 42 | src = (u8*)&memory->ram->font; break; | 
|---|
| 43 | default: | 
|---|
| 44 | src = (u8*)&memory->ram->tiles.data; break; | 
|---|
| 45 | } | 
|---|
| 46 |  | 
|---|
| 47 | return tic_tilesheet_get(segment, src); | 
|---|
| 48 | } | 
|---|
| 49 |  | 
|---|
| 50 | static u8* getPalette(tic_mem* tic, u8* colors, u8 count) | 
|---|
| 51 | { | 
|---|
| 52 | static u8 mapping[TIC_PALETTE_SIZE]; | 
|---|
| 53 | for (s32 i = 0; i < TIC_PALETTE_SIZE; i++) mapping[i] = tic_tool_peek4(tic->ram->vram.mapping, i); | 
|---|
| 54 | for (s32 i = 0; i < count; i++) mapping[colors[i]] = TRANSPARENT_COLOR; | 
|---|
| 55 | return mapping; | 
|---|
| 56 | } | 
|---|
| 57 |  | 
|---|
| 58 | static inline u8 mapColor(tic_mem* tic, u8 color) | 
|---|
| 59 | { | 
|---|
| 60 | return tic_tool_peek4(tic->ram->vram.mapping, color & 0xf); | 
|---|
| 61 | } | 
|---|
| 62 |  | 
|---|
| 63 | static inline void setPixel(tic_core* core, s32 x, s32 y, u8 color) | 
|---|
| 64 | { | 
|---|
| 65 | const tic_vram* vram = &core->memory.ram->vram; | 
|---|
| 66 |  | 
|---|
| 67 | if (x < core->state.clip.l || y < core->state.clip.t || x >= core->state.clip.r || y >= core->state.clip.b) return; | 
|---|
| 68 |  | 
|---|
| 69 | tic_api_poke4((tic_mem*)core, y * TIC80_WIDTH + x, color); | 
|---|
| 70 | } | 
|---|
| 71 |  | 
|---|
| 72 | static inline void setPixelFast(tic_core* core, s32 x, s32 y, u8 color) | 
|---|
| 73 | { | 
|---|
| 74 | // does not do any CLIP checking, the caller needs to do that first | 
|---|
| 75 | tic_api_poke4((tic_mem*)core, y * TIC80_WIDTH + x, color); | 
|---|
| 76 | } | 
|---|
| 77 |  | 
|---|
| 78 | static u8 getPixel(tic_core* core, s32 x, s32 y) | 
|---|
| 79 | { | 
|---|
| 80 | return tic_api_peek4((tic_mem*)core, y * TIC80_WIDTH + x); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | #define EARLY_CLIP(x, y, width, height) \ | 
|---|
| 84 | ( \ | 
|---|
| 85 | (((y)+(height)-1) < core->state.clip.t) \ | 
|---|
| 86 | || (((x)+(width)-1) < core->state.clip.l) \ | 
|---|
| 87 | || ((y) >= core->state.clip.b) \ | 
|---|
| 88 | || ((x) >= core->state.clip.r) \ | 
|---|
| 89 | ) | 
|---|
| 90 |  | 
|---|
| 91 | static void drawHLine(tic_core* core, s32 x, s32 y, s32 width, u8 color) | 
|---|
| 92 | { | 
|---|
| 93 | const tic_vram* vram = &core->memory.ram->vram; | 
|---|
| 94 |  | 
|---|
| 95 | if (y < core->state.clip.t || core->state.clip.b <= y) return; | 
|---|
| 96 |  | 
|---|
| 97 | s32 xl = MAX(x, core->state.clip.l); | 
|---|
| 98 | s32 xr = MIN(x + width, core->state.clip.r); | 
|---|
| 99 | s32 start = y * TIC80_WIDTH; | 
|---|
| 100 |  | 
|---|
| 101 | for(s32 i = start + xl, end = start + xr; i < end; ++i) | 
|---|
| 102 | tic_api_poke4((tic_mem*)core, i, color); | 
|---|
| 103 | } | 
|---|
| 104 |  | 
|---|
| 105 | static void drawVLine(tic_core* core, s32 x, s32 y, s32 height, u8 color) | 
|---|
| 106 | { | 
|---|
| 107 | const tic_vram* vram = &core->memory.ram->vram; | 
|---|
| 108 |  | 
|---|
| 109 | if (x < core->state.clip.l || core->state.clip.r <= x) return; | 
|---|
| 110 |  | 
|---|
| 111 | s32 yl = y < 0 ? 0 : y; | 
|---|
| 112 | s32 yr = y + height >= TIC80_HEIGHT ? TIC80_HEIGHT : y + height; | 
|---|
| 113 |  | 
|---|
| 114 | for (s32 i = yl; i < yr; ++i) | 
|---|
| 115 | setPixel(core, x, i, color); | 
|---|
| 116 | } | 
|---|
| 117 |  | 
|---|
| 118 | static void drawRect(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color) | 
|---|
| 119 | { | 
|---|
| 120 | for (s32 i = y; i < y + height; ++i) | 
|---|
| 121 | drawHLine(core, x, i, width, color); | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | static void drawRectBorder(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color) | 
|---|
| 125 | { | 
|---|
| 126 | drawHLine(core, x, y, width, color); | 
|---|
| 127 | drawHLine(core, x, y + height - 1, width, color); | 
|---|
| 128 |  | 
|---|
| 129 | drawVLine(core, x, y, height, color); | 
|---|
| 130 | drawVLine(core, x + width - 1, y, height, color); | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | #define DRAW_TILE_BODY(X, Y) do {\ | 
|---|
| 134 | for(s32 py=sy; py < ey; py++, y++) \ | 
|---|
| 135 | { \ | 
|---|
| 136 | s32 xx = x; \ | 
|---|
| 137 | for(s32 px=sx; px < ex; px++, xx++) \ | 
|---|
| 138 | { \ | 
|---|
| 139 | u8 color = mapping[tic_tilesheet_gettilepix(tile, (X), (Y))];\ | 
|---|
| 140 | if(color != TRANSPARENT_COLOR) setPixelFast(core, xx, y, color); \ | 
|---|
| 141 | } \ | 
|---|
| 142 | } \ | 
|---|
| 143 | } while(0) | 
|---|
| 144 |  | 
|---|
| 145 | #define REVERT(X) (TIC_SPRITESIZE - 1 - (X)) | 
|---|
| 146 |  | 
|---|
| 147 | static void drawTile(tic_core* core, tic_tileptr* tile, s32 x, s32 y, u8* colors, s32 count, s32 scale, tic_flip flip, tic_rotate rotate) | 
|---|
| 148 | { | 
|---|
| 149 | const tic_vram* vram = &core->memory.ram->vram; | 
|---|
| 150 | u8* mapping = getPalette(&core->memory, colors, count); | 
|---|
| 151 |  | 
|---|
| 152 | rotate &= 3; | 
|---|
| 153 | u32 orientation = flip & 3; | 
|---|
| 154 |  | 
|---|
| 155 | if (rotate == tic_90_rotate) orientation ^= 1; | 
|---|
| 156 | else if (rotate == tic_180_rotate) orientation ^= 3; | 
|---|
| 157 | else if (rotate == tic_270_rotate) orientation ^= 2; | 
|---|
| 158 | if (rotate == tic_90_rotate || rotate == tic_270_rotate) orientation |= 2; | 
|---|
| 159 |  | 
|---|
| 160 | if (scale == 1) { | 
|---|
| 161 | // the most common path | 
|---|
| 162 | s32 sx, sy, ex, ey; | 
|---|
| 163 | sx = core->state.clip.l - x; if (sx < 0) sx = 0; | 
|---|
| 164 | sy = core->state.clip.t - y; if (sy < 0) sy = 0; | 
|---|
| 165 | ex = core->state.clip.r - x; if (ex > TIC_SPRITESIZE) ex = TIC_SPRITESIZE; | 
|---|
| 166 | ey = core->state.clip.b - y; if (ey > TIC_SPRITESIZE) ey = TIC_SPRITESIZE; | 
|---|
| 167 | y += sy; | 
|---|
| 168 | x += sx; | 
|---|
| 169 | switch (orientation) { | 
|---|
| 170 | case 4: DRAW_TILE_BODY(py, px); break; | 
|---|
| 171 | case 6: DRAW_TILE_BODY(REVERT(py), px); break; | 
|---|
| 172 | case 5: DRAW_TILE_BODY(py, REVERT(px)); break; | 
|---|
| 173 | case 7: DRAW_TILE_BODY(REVERT(py), REVERT(px)); break; | 
|---|
| 174 | case 0: DRAW_TILE_BODY(px, py); break; | 
|---|
| 175 | case 2: DRAW_TILE_BODY(px, REVERT(py)); break; | 
|---|
| 176 | case 1: DRAW_TILE_BODY(REVERT(px), py); break; | 
|---|
| 177 | case 3: DRAW_TILE_BODY(REVERT(px), REVERT(py)); break; | 
|---|
| 178 | } | 
|---|
| 179 | return; | 
|---|
| 180 | } | 
|---|
| 181 |  | 
|---|
| 182 | if (EARLY_CLIP(x, y, TIC_SPRITESIZE * scale, TIC_SPRITESIZE * scale)) return; | 
|---|
| 183 |  | 
|---|
| 184 | for (s32 py = 0; py < TIC_SPRITESIZE; py++, y += scale) | 
|---|
| 185 | { | 
|---|
| 186 | s32 xx = x; | 
|---|
| 187 | for (s32 px = 0; px < TIC_SPRITESIZE; px++, xx += scale) | 
|---|
| 188 | { | 
|---|
| 189 | s32 ix = orientation & 1 ? TIC_SPRITESIZE - px - 1 : px; | 
|---|
| 190 | s32 iy = orientation & 2 ? TIC_SPRITESIZE - py - 1 : py; | 
|---|
| 191 | if (orientation & 4) { | 
|---|
| 192 | s32 tmp = ix; ix = iy; iy = tmp; | 
|---|
| 193 | } | 
|---|
| 194 | u8 color = mapping[tic_tilesheet_gettilepix(tile, ix, iy)]; | 
|---|
| 195 | if (color != TRANSPARENT_COLOR) drawRect(core, xx, y, scale, scale, color); | 
|---|
| 196 | } | 
|---|
| 197 | } | 
|---|
| 198 | } | 
|---|
| 199 |  | 
|---|
| 200 | #undef DRAW_TILE_BODY | 
|---|
| 201 | #undef REVERT | 
|---|
| 202 |  | 
|---|
| 203 | static void drawSprite(tic_core* core, s32 index, s32 x, s32 y, s32 w, s32 h, u8* colors, s32 count, s32 scale, tic_flip flip, tic_rotate rotate) | 
|---|
| 204 | { | 
|---|
| 205 | const tic_vram* vram = &core->memory.ram->vram; | 
|---|
| 206 |  | 
|---|
| 207 | if (index < 0) | 
|---|
| 208 | return; | 
|---|
| 209 |  | 
|---|
| 210 | rotate &= 3; | 
|---|
| 211 | flip &= 3; | 
|---|
| 212 |  | 
|---|
| 213 | tic_tilesheet sheet = getTileSheetFromSegment(&core->memory, core->memory.ram->vram.blit.segment); | 
|---|
| 214 | if (w == 1 && h == 1) { | 
|---|
| 215 | tic_tileptr tile = tic_tilesheet_gettile(&sheet, index, false); | 
|---|
| 216 | drawTile(core, &tile, x, y, colors, count, scale, flip, rotate); | 
|---|
| 217 | } | 
|---|
| 218 | else | 
|---|
| 219 | { | 
|---|
| 220 | s32 step = TIC_SPRITESIZE * scale; | 
|---|
| 221 | s32 cols = sheet.segment->sheet_width; | 
|---|
| 222 |  | 
|---|
| 223 | const tic_flip vert_horz_flip = tic_horz_flip | tic_vert_flip; | 
|---|
| 224 |  | 
|---|
| 225 | if (EARLY_CLIP(x, y, w * step, h * step)) return; | 
|---|
| 226 |  | 
|---|
| 227 | for (s32 i = 0; i < w; i++) | 
|---|
| 228 | { | 
|---|
| 229 | for (s32 j = 0; j < h; j++) | 
|---|
| 230 | { | 
|---|
| 231 | s32 mx = i; | 
|---|
| 232 | s32 my = j; | 
|---|
| 233 |  | 
|---|
| 234 | if (flip == tic_horz_flip || flip == vert_horz_flip) mx = w - 1 - i; | 
|---|
| 235 | if (flip == tic_vert_flip || flip == vert_horz_flip) my = h - 1 - j; | 
|---|
| 236 |  | 
|---|
| 237 | if (rotate == tic_180_rotate) | 
|---|
| 238 | { | 
|---|
| 239 | mx = w - 1 - mx; | 
|---|
| 240 | my = h - 1 - my; | 
|---|
| 241 | } | 
|---|
| 242 | else if (rotate == tic_90_rotate) | 
|---|
| 243 | { | 
|---|
| 244 | if (flip == tic_no_flip || flip == vert_horz_flip) my = h - 1 - my; | 
|---|
| 245 | else mx = w - 1 - mx; | 
|---|
| 246 | } | 
|---|
| 247 | else if (rotate == tic_270_rotate) | 
|---|
| 248 | { | 
|---|
| 249 | if (flip == tic_no_flip || flip == vert_horz_flip) mx = w - 1 - mx; | 
|---|
| 250 | else my = h - 1 - my; | 
|---|
| 251 | } | 
|---|
| 252 |  | 
|---|
| 253 | enum { Cols = TIC_SPRITESHEET_SIZE / TIC_SPRITESIZE }; | 
|---|
| 254 |  | 
|---|
| 255 |  | 
|---|
| 256 | tic_tileptr tile = tic_tilesheet_gettile(&sheet, index + mx + my * cols, false); | 
|---|
| 257 | if (rotate == 0 || rotate == 2) | 
|---|
| 258 | drawTile(core, &tile, x + i * step, y + j * step, colors, count, scale, flip, rotate); | 
|---|
| 259 | else | 
|---|
| 260 | drawTile(core, &tile, x + j * step, y + i * step, colors, count, scale, flip, rotate); | 
|---|
| 261 | } | 
|---|
| 262 | } | 
|---|
| 263 | } | 
|---|
| 264 | } | 
|---|
| 265 |  | 
|---|
| 266 | static void drawMap(tic_core* core, const tic_map* src, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, u8* colors, s32 count, s32 scale, RemapFunc remap, void* data) | 
|---|
| 267 | { | 
|---|
| 268 | const s32 size = TIC_SPRITESIZE * scale; | 
|---|
| 269 |  | 
|---|
| 270 | tic_tilesheet sheet = getTileSheetFromSegment(&core->memory, core->memory.ram->vram.blit.segment); | 
|---|
| 271 |  | 
|---|
| 272 | for (s32 j = y, jj = sy; j < y + height; j++, jj += size) | 
|---|
| 273 | for (s32 i = x, ii = sx; i < x + width; i++, ii += size) | 
|---|
| 274 | { | 
|---|
| 275 | s32 mi = i; | 
|---|
| 276 | s32 mj = j; | 
|---|
| 277 |  | 
|---|
| 278 | while (mi < 0) mi += TIC_MAP_WIDTH; | 
|---|
| 279 | while (mj < 0) mj += TIC_MAP_HEIGHT; | 
|---|
| 280 | while (mi >= TIC_MAP_WIDTH) mi -= TIC_MAP_WIDTH; | 
|---|
| 281 | while (mj >= TIC_MAP_HEIGHT) mj -= TIC_MAP_HEIGHT; | 
|---|
| 282 |  | 
|---|
| 283 | s32 index = mi + mj * TIC_MAP_WIDTH; | 
|---|
| 284 | RemapResult retile = { *(src->data + index), tic_no_flip, tic_no_rotate }; | 
|---|
| 285 |  | 
|---|
| 286 | if (remap) | 
|---|
| 287 | remap(data, mi, mj, &retile); | 
|---|
| 288 |  | 
|---|
| 289 | tic_tileptr tile = tic_tilesheet_gettile(&sheet, retile.index, true); | 
|---|
| 290 | drawTile(core, &tile, ii, jj, colors, count, scale, retile.flip, retile.rotate); | 
|---|
| 291 | } | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 | static s32 drawChar(tic_core* core, tic_tileptr* font_char, s32 x, s32 y, s32 scale, bool fixed, u8* mapping) | 
|---|
| 295 | { | 
|---|
| 296 | const tic_vram* vram = &core->memory.ram->vram; | 
|---|
| 297 |  | 
|---|
| 298 | enum { Size = TIC_SPRITESIZE }; | 
|---|
| 299 |  | 
|---|
| 300 | s32 j = 0, start = 0, end = Size; | 
|---|
| 301 |  | 
|---|
| 302 | if (!fixed) { | 
|---|
| 303 | for (s32 i = 0; i < Size; i++) { | 
|---|
| 304 | for (j = 0; j < Size; j++) | 
|---|
| 305 | if (mapping[tic_tilesheet_gettilepix(font_char, i, j)] != TRANSPARENT_COLOR) break; | 
|---|
| 306 | if (j < Size) break; else start++; | 
|---|
| 307 | } | 
|---|
| 308 | for (s32 i = Size - 1; i >= start; i--) { | 
|---|
| 309 | for (j = 0; j < Size; j++) | 
|---|
| 310 | if (mapping[tic_tilesheet_gettilepix(font_char, i, j)] != TRANSPARENT_COLOR) break; | 
|---|
| 311 | if (j < Size) break; else end--; | 
|---|
| 312 | } | 
|---|
| 313 | } | 
|---|
| 314 | s32 width = end - start; | 
|---|
| 315 |  | 
|---|
| 316 | if (EARLY_CLIP(x, y, Size * scale, Size * scale)) return width; | 
|---|
| 317 |  | 
|---|
| 318 | s32 colStart = start, colStep = 1, rowStart = 0, rowStep = 1; | 
|---|
| 319 |  | 
|---|
| 320 | for (s32 i = 0, col = colStart, xs = x; i < width; i++, col += colStep, xs += scale) | 
|---|
| 321 | { | 
|---|
| 322 | for (s32 j = 0, row = rowStart, ys = y; j < Size; j++, row += rowStep, ys += scale) | 
|---|
| 323 | { | 
|---|
| 324 | u8 color = tic_tilesheet_gettilepix(font_char, col, row); | 
|---|
| 325 | if (mapping[color] != TRANSPARENT_COLOR) | 
|---|
| 326 | drawRect(core, xs, ys, scale, scale, mapping[color]); | 
|---|
| 327 | } | 
|---|
| 328 | } | 
|---|
| 329 | return width; | 
|---|
| 330 | } | 
|---|
| 331 |  | 
|---|
| 332 | static s32 drawText(tic_core* core, tic_tilesheet* font_face, const char* text, s32 x, s32 y, s32 width, s32 height, bool fixed, u8* mapping, s32 scale, bool alt) | 
|---|
| 333 | { | 
|---|
| 334 | s32 pos = x; | 
|---|
| 335 | s32 MAX = x; | 
|---|
| 336 | char sym = 0; | 
|---|
| 337 |  | 
|---|
| 338 | while ((sym = *text++)) | 
|---|
| 339 | { | 
|---|
| 340 | if (sym == '\n') | 
|---|
| 341 | { | 
|---|
| 342 | if (pos > MAX) | 
|---|
| 343 | MAX = pos; | 
|---|
| 344 |  | 
|---|
| 345 | pos = x; | 
|---|
| 346 | y += height * scale; | 
|---|
| 347 | } | 
|---|
| 348 | else { | 
|---|
| 349 | tic_tileptr font_char = tic_tilesheet_gettile(font_face, alt * TIC_FONT_CHARS + sym, true); | 
|---|
| 350 | s32 size = drawChar(core, &font_char, pos, y, scale, fixed, mapping); | 
|---|
| 351 | pos += ((!fixed && size) ? size + 1 : width) * scale; | 
|---|
| 352 | } | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 | return pos > MAX ? pos - x : MAX - x; | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | void tic_api_clip(tic_mem* memory, s32 x, s32 y, s32 width, s32 height) | 
|---|
| 359 | { | 
|---|
| 360 | tic_core* core = (tic_core*)memory; | 
|---|
| 361 | tic_vram* vram = &memory->ram->vram; | 
|---|
| 362 |  | 
|---|
| 363 | core->state.clip.l = x; | 
|---|
| 364 | core->state.clip.t = y; | 
|---|
| 365 | core->state.clip.r = x + width; | 
|---|
| 366 | core->state.clip.b = y + height; | 
|---|
| 367 |  | 
|---|
| 368 | if (core->state.clip.l < 0) core->state.clip.l = 0; | 
|---|
| 369 | if (core->state.clip.t < 0) core->state.clip.t = 0; | 
|---|
| 370 | if (core->state.clip.r > TIC80_WIDTH) core->state.clip.r = TIC80_WIDTH; | 
|---|
| 371 | if (core->state.clip.b > TIC80_HEIGHT) core->state.clip.b = TIC80_HEIGHT; | 
|---|
| 372 | } | 
|---|
| 373 |  | 
|---|
| 374 | void tic_api_rect(tic_mem* memory, s32 x, s32 y, s32 width, s32 height, u8 color) | 
|---|
| 375 | { | 
|---|
| 376 | tic_core* core = (tic_core*)memory; | 
|---|
| 377 |  | 
|---|
| 378 | drawRect(core, x, y, width, height, mapColor(memory, color)); | 
|---|
| 379 | } | 
|---|
| 380 |  | 
|---|
| 381 | static double ZBuffer[TIC80_WIDTH * TIC80_HEIGHT]; | 
|---|
| 382 |  | 
|---|
| 383 | void tic_api_cls(tic_mem* tic, u8 color) | 
|---|
| 384 | { | 
|---|
| 385 | tic_core* core = (tic_core*)tic; | 
|---|
| 386 | tic_vram* vram = &tic->ram->vram; | 
|---|
| 387 |  | 
|---|
| 388 | static const struct ClipRect EmptyClip = { 0, 0, TIC80_WIDTH, TIC80_HEIGHT }; | 
|---|
| 389 |  | 
|---|
| 390 | if (MEMCMP(core->state.clip, EmptyClip)) | 
|---|
| 391 | { | 
|---|
| 392 | memset(&vram->screen, (color & 0xf) | (color << TIC_PALETTE_BPP), sizeof(tic_screen)); | 
|---|
| 393 | ZEROMEM(ZBuffer); | 
|---|
| 394 | } | 
|---|
| 395 | else | 
|---|
| 396 | { | 
|---|
| 397 | for(s32 y = core->state.clip.t, start = y * TIC80_WIDTH; y < core->state.clip.b; ++y, start += TIC80_WIDTH) | 
|---|
| 398 | for(s32 x = core->state.clip.l, pixel = start + x; x < core->state.clip.r; ++x, ++pixel) | 
|---|
| 399 | { | 
|---|
| 400 | tic_api_poke4(tic, pixel, color); | 
|---|
| 401 | ZBuffer[pixel] = 0; | 
|---|
| 402 | } | 
|---|
| 403 | } | 
|---|
| 404 | } | 
|---|
| 405 |  | 
|---|
| 406 | s32 tic_api_font(tic_mem* memory, const char* text, s32 x, s32 y, u8* trans_colors, u8 trans_count, s32 w, s32 h, bool fixed, s32 scale, bool alt) | 
|---|
| 407 | { | 
|---|
| 408 | u8* mapping = getPalette(memory, trans_colors, trans_count); | 
|---|
| 409 |  | 
|---|
| 410 | // Compatibility : flip top and bottom of the spritesheet | 
|---|
| 411 | // to preserve tic_api_font's default target | 
|---|
| 412 | u8 segment = memory->ram->vram.blit.segment >> 1; | 
|---|
| 413 | u8 flipmask = 1; while (segment >>= 1) flipmask <<= 1; | 
|---|
| 414 |  | 
|---|
| 415 | tic_tilesheet font_face = getTileSheetFromSegment(memory, memory->ram->vram.blit.segment ^ flipmask); | 
|---|
| 416 | return drawText((tic_core*)memory, &font_face, text, x, y, w, h, fixed, mapping, scale, alt); | 
|---|
| 417 | } | 
|---|
| 418 |  | 
|---|
| 419 | s32 tic_api_print(tic_mem* memory, const char* text, s32 x, s32 y, u8 color, bool fixed, s32 scale, bool alt) | 
|---|
| 420 | { | 
|---|
| 421 | u8 mapping[] = { 255, color }; | 
|---|
| 422 | tic_tilesheet font_face = getTileSheetFromSegment(memory, 1); | 
|---|
| 423 |  | 
|---|
| 424 | const tic_font_data* font = alt ? &memory->ram->font.alt : &memory->ram->font.regular; | 
|---|
| 425 | s32 width = font->width; | 
|---|
| 426 |  | 
|---|
| 427 | // Compatibility : print uses reduced width for non-fixed space | 
|---|
| 428 | if (!fixed) width -= 2; | 
|---|
| 429 | return drawText((tic_core*)memory, &font_face, text, x, y, width, font->height, fixed, mapping, scale, alt); | 
|---|
| 430 | } | 
|---|
| 431 |  | 
|---|
| 432 | void tic_api_spr(tic_mem* memory, s32 index, s32 x, s32 y, s32 w, s32 h, u8* trans_colors, u8 trans_count, s32 scale, tic_flip flip, tic_rotate rotate) | 
|---|
| 433 | { | 
|---|
| 434 | drawSprite((tic_core*)memory, index, x, y, w, h, trans_colors, trans_count, scale, flip, rotate); | 
|---|
| 435 | } | 
|---|
| 436 |  | 
|---|
| 437 | static inline u8* getFlag(tic_mem* memory, s32 index, u8 flag) | 
|---|
| 438 | { | 
|---|
| 439 | static u8 stub = 0; | 
|---|
| 440 | if (index >= TIC_FLAGS || flag >= BITS_IN_BYTE) | 
|---|
| 441 | return &stub; | 
|---|
| 442 |  | 
|---|
| 443 | return memory->ram->flags.data + index; | 
|---|
| 444 | } | 
|---|
| 445 |  | 
|---|
| 446 | bool tic_api_fget(tic_mem* memory, s32 index, u8 flag) | 
|---|
| 447 | { | 
|---|
| 448 | return *getFlag(memory, index, flag) & (1 << flag); | 
|---|
| 449 | } | 
|---|
| 450 |  | 
|---|
| 451 | void tic_api_fset(tic_mem* memory, s32 index, u8 flag, bool value) | 
|---|
| 452 | { | 
|---|
| 453 | if (value) | 
|---|
| 454 | *getFlag(memory, index, flag) |= (1 << flag); | 
|---|
| 455 | else | 
|---|
| 456 | *getFlag(memory, index, flag) &= ~(1 << flag); | 
|---|
| 457 | } | 
|---|
| 458 |  | 
|---|
| 459 | u8 tic_api_pix(tic_mem* memory, s32 x, s32 y, u8 color, bool get) | 
|---|
| 460 | { | 
|---|
| 461 | tic_core* core = (tic_core*)memory; | 
|---|
| 462 |  | 
|---|
| 463 | if (get) return getPixel(core, x, y); | 
|---|
| 464 |  | 
|---|
| 465 | setPixel(core, x, y, mapColor(memory, color)); | 
|---|
| 466 | return 0; | 
|---|
| 467 | } | 
|---|
| 468 |  | 
|---|
| 469 | void tic_api_rectb(tic_mem* memory, s32 x, s32 y, s32 width, s32 height, u8 color) | 
|---|
| 470 | { | 
|---|
| 471 | tic_core* core = (tic_core*)memory; | 
|---|
| 472 |  | 
|---|
| 473 | drawRectBorder(core, x, y, width, height, mapColor(memory, color)); | 
|---|
| 474 | } | 
|---|
| 475 |  | 
|---|
| 476 | static struct | 
|---|
| 477 | { | 
|---|
| 478 | s16 Left[TIC80_HEIGHT]; | 
|---|
| 479 | s16 Right[TIC80_HEIGHT]; | 
|---|
| 480 | } SidesBuffer; | 
|---|
| 481 |  | 
|---|
| 482 | static void initSidesBuffer() | 
|---|
| 483 | { | 
|---|
| 484 | for (s32 i = 0; i < COUNT_OF(SidesBuffer.Left); i++) | 
|---|
| 485 | SidesBuffer.Left[i] = TIC80_WIDTH, SidesBuffer.Right[i] = -1; | 
|---|
| 486 | } | 
|---|
| 487 |  | 
|---|
| 488 | static void setSidePixel(s32 x, s32 y) | 
|---|
| 489 | { | 
|---|
| 490 | if (y >= 0 && y < TIC80_HEIGHT) | 
|---|
| 491 | { | 
|---|
| 492 | if (x < SidesBuffer.Left[y]) SidesBuffer.Left[y] = x; | 
|---|
| 493 | if (x > SidesBuffer.Right[y]) SidesBuffer.Right[y] = x; | 
|---|
| 494 | } | 
|---|
| 495 | } | 
|---|
| 496 |  | 
|---|
| 497 | static void drawEllipse(tic_mem* memory, s32 x0, s32 y0, s32 x1, s32 y1, u8 color, PixelFunc pix) | 
|---|
| 498 | { | 
|---|
| 499 | s64 a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1; /* values of diameter */ | 
|---|
| 500 | s64 dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a; /* error increment */ | 
|---|
| 501 | s64 err = dx + dy + b1 * a * a, e2; /* error of 1.step */ | 
|---|
| 502 |  | 
|---|
| 503 | if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped pos32s */ | 
|---|
| 504 | if (y0 > y1) y0 = y1; /* .. exchange them */ | 
|---|
| 505 | y0 += (b + 1) / 2; y1 = y0 - b1;   /* starting pixel */ | 
|---|
| 506 | a *= 8 * a; b1 = 8 * b * b; | 
|---|
| 507 |  | 
|---|
| 508 | do | 
|---|
| 509 | { | 
|---|
| 510 | pix(memory, x1, y0, color); /*   I. Quadrant */ | 
|---|
| 511 | pix(memory, x0, y0, color); /*  II. Quadrant */ | 
|---|
| 512 | pix(memory, x0, y1, color); /* III. Quadrant */ | 
|---|
| 513 | pix(memory, x1, y1, color); /*  IV. Quadrant */ | 
|---|
| 514 | e2 = 2 * err; | 
|---|
| 515 | if (e2 <= dy) { y0++; y1--; err += dy += a; }  /* y step */ | 
|---|
| 516 | if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x step */ | 
|---|
| 517 | } while (x0 <= x1); | 
|---|
| 518 |  | 
|---|
| 519 | while (y0-y1 < b) | 
|---|
| 520 | {  /* too early stop of flat ellipses a=1 */ | 
|---|
| 521 | pix(memory, x0 - 1, y0,    color); /* -> finish tip of ellipse */ | 
|---|
| 522 | pix(memory, x1 + 1, y0++,  color); | 
|---|
| 523 | pix(memory, x0 - 1, y1,    color); | 
|---|
| 524 | pix(memory, x1 + 1, y1--,  color); | 
|---|
| 525 | } | 
|---|
| 526 | } | 
|---|
| 527 |  | 
|---|
| 528 | static void setElliPixel(tic_mem* tic, s32 x, s32 y, u8 color) | 
|---|
| 529 | { | 
|---|
| 530 | setPixel((tic_core*)tic, x, y, color); | 
|---|
| 531 | } | 
|---|
| 532 |  | 
|---|
| 533 | static void setElliSide(tic_mem* tic, s32 x, s32 y, u8 color) | 
|---|
| 534 | { | 
|---|
| 535 | setSidePixel(x, y); | 
|---|
| 536 | } | 
|---|
| 537 |  | 
|---|
| 538 | static void drawSidesBuffer(tic_mem* memory, s32 y0, s32 y1, u8 color) | 
|---|
| 539 | { | 
|---|
| 540 | tic_vram* vram = &memory->ram->vram; | 
|---|
| 541 |  | 
|---|
| 542 | tic_core* core = (tic_core*)memory; | 
|---|
| 543 | s32 yt = MAX(core->state.clip.t, y0); | 
|---|
| 544 | s32 yb = MIN(core->state.clip.b, y1 + 1); | 
|---|
| 545 | u8 final_color = mapColor(&core->memory, color); | 
|---|
| 546 | for (s32 y = yt; y < yb; y++) | 
|---|
| 547 | { | 
|---|
| 548 | s32 xl = MAX(SidesBuffer.Left[y], core->state.clip.l); | 
|---|
| 549 | s32 xr = MIN(SidesBuffer.Right[y] + 1, core->state.clip.r); | 
|---|
| 550 | s32 start = y * TIC80_WIDTH; | 
|---|
| 551 |  | 
|---|
| 552 | for(s32 i = start + xl, end = start + xr; i < end; ++i) | 
|---|
| 553 | tic_api_poke4(memory, i, color); | 
|---|
| 554 | } | 
|---|
| 555 | } | 
|---|
| 556 |  | 
|---|
| 557 | void tic_api_circ(tic_mem* memory, s32 x, s32 y, s32 r, u8 color) | 
|---|
| 558 | { | 
|---|
| 559 | initSidesBuffer(); | 
|---|
| 560 | drawEllipse(memory, x - r, y - r, x + r, y + r, 0, setElliSide); | 
|---|
| 561 | drawSidesBuffer(memory, y - r, y + r + 1, color); | 
|---|
| 562 | } | 
|---|
| 563 |  | 
|---|
| 564 | void tic_api_circb(tic_mem* memory, s32 x, s32 y, s32 r, u8 color) | 
|---|
| 565 | { | 
|---|
| 566 | drawEllipse(memory, x - r, y - r, x + r, y + r, mapColor(memory, color), setElliPixel); | 
|---|
| 567 | } | 
|---|
| 568 |  | 
|---|
| 569 | void tic_api_elli(tic_mem* memory, s32 x, s32 y, s32 a, s32 b, u8 color) | 
|---|
| 570 | { | 
|---|
| 571 | initSidesBuffer(); | 
|---|
| 572 | drawEllipse(memory, x - a, y - b, x + a, y + b, 0, setElliSide); | 
|---|
| 573 | drawSidesBuffer(memory, y - b, y + b + 1, color); | 
|---|
| 574 | } | 
|---|
| 575 |  | 
|---|
| 576 | void tic_api_ellib(tic_mem* memory, s32 x, s32 y, s32 a, s32 b, u8 color) | 
|---|
| 577 | { | 
|---|
| 578 | drawEllipse(memory, x - a, y - b, x + a, y + b, mapColor(memory, color), setElliPixel); | 
|---|
| 579 | } | 
|---|
| 580 |  | 
|---|
| 581 | static inline float initLine(float *x0, float *x1, float *y0, float *y1) | 
|---|
| 582 | { | 
|---|
| 583 | if (*y0 > *y1) | 
|---|
| 584 | { | 
|---|
| 585 | SWAP(*x0, *x1, float); | 
|---|
| 586 | SWAP(*y0, *y1, float); | 
|---|
| 587 | } | 
|---|
| 588 |  | 
|---|
| 589 | float t = (*x1 - *x0) / (*y1 - *y0); | 
|---|
| 590 |  | 
|---|
| 591 | if(*y0 < 0) *x0 -= *y0 * t, *y0 = 0; | 
|---|
| 592 | if(*y1 > TIC80_WIDTH) *x1 += (TIC80_WIDTH - *y0) * t, *y1 = TIC80_WIDTH; | 
|---|
| 593 |  | 
|---|
| 594 | return t; | 
|---|
| 595 | } | 
|---|
| 596 |  | 
|---|
| 597 | static void drawLine(tic_mem* tic, float x0, float y0, float x1, float y1, u8 color) | 
|---|
| 598 | { | 
|---|
| 599 | if(fabs(x0 - x1) < fabs(y0 - y1)) | 
|---|
| 600 | for (float t = initLine(&x0, &x1, &y0, &y1); y0 < y1; y0++, x0 += t) | 
|---|
| 601 | setPixel((tic_core*)tic, x0, y0, color); | 
|---|
| 602 | else | 
|---|
| 603 | for (float t = initLine(&y0, &y1, &x0, &x1); x0 < x1; x0++, y0 += t) | 
|---|
| 604 | setPixel((tic_core*)tic, x0, y0, color); | 
|---|
| 605 |  | 
|---|
| 606 | setPixel((tic_core*)tic, x1, y1, color); | 
|---|
| 607 | } | 
|---|
| 608 |  | 
|---|
| 609 | typedef union | 
|---|
| 610 | { | 
|---|
| 611 | struct | 
|---|
| 612 | { | 
|---|
| 613 | double x, y; | 
|---|
| 614 | }; | 
|---|
| 615 |  | 
|---|
| 616 | double d[2]; | 
|---|
| 617 | } Vec2; | 
|---|
| 618 |  | 
|---|
| 619 | typedef union | 
|---|
| 620 | { | 
|---|
| 621 | struct | 
|---|
| 622 | { | 
|---|
| 623 | double x, y, z; | 
|---|
| 624 | }; | 
|---|
| 625 |  | 
|---|
| 626 | double d[3]; | 
|---|
| 627 | } Vec3; | 
|---|
| 628 |  | 
|---|
| 629 | typedef struct | 
|---|
| 630 | { | 
|---|
| 631 | void* data; | 
|---|
| 632 | const Vec2* v[3]; | 
|---|
| 633 | Vec3 w; | 
|---|
| 634 | } ShaderAttr; | 
|---|
| 635 |  | 
|---|
| 636 | typedef tic_color(*PixelShader)(const ShaderAttr* a, s32 pixel); | 
|---|
| 637 |  | 
|---|
| 638 | static inline double edgeFn(const Vec2* a, const Vec2* b, const Vec2* c) | 
|---|
| 639 | { | 
|---|
| 640 | return (b->x - a->x) * (c->y - a->y) - (b->y - a->y) * (c->x - a->x); | 
|---|
| 641 | } | 
|---|
| 642 |  | 
|---|
| 643 | static void drawTri(tic_mem* tic, const Vec2* v0, const Vec2* v1, const Vec2* v2, PixelShader shader, void* data) | 
|---|
| 644 | { | 
|---|
| 645 | ShaderAttr a = {data, v0, v1, v2}; | 
|---|
| 646 |  | 
|---|
| 647 | tic_core* core = (tic_core*)tic; | 
|---|
| 648 | const struct ClipRect* clip = &core->state.clip; | 
|---|
| 649 |  | 
|---|
| 650 | tic_point min = {floor(MIN3(a.v[0]->x, a.v[1]->x, a.v[2]->x)), floor(MIN3(a.v[0]->y, a.v[1]->y, a.v[2]->y))}; | 
|---|
| 651 | tic_point max = {ceil(MAX3(a.v[0]->x, a.v[1]->x, a.v[2]->x)), ceil(MAX3(a.v[0]->y, a.v[1]->y, a.v[2]->y))}; | 
|---|
| 652 |  | 
|---|
| 653 | min.x = MAX(min.x, clip->l); | 
|---|
| 654 | min.y = MAX(min.y, clip->t); | 
|---|
| 655 | max.x = MIN(max.x, clip->r); | 
|---|
| 656 | max.y = MIN(max.y, clip->b); | 
|---|
| 657 |  | 
|---|
| 658 | if(min.x >= max.x || min.y >= max.y) return; | 
|---|
| 659 |  | 
|---|
| 660 | double area = edgeFn(a.v[0], a.v[1], a.v[2]); | 
|---|
| 661 | if((s32)floor(area) == 0) return; | 
|---|
| 662 | if(area < 0.0) | 
|---|
| 663 | { | 
|---|
| 664 | SWAP(a.v[1], a.v[2], const Vec2*); | 
|---|
| 665 | area = -area; | 
|---|
| 666 | } | 
|---|
| 667 |  | 
|---|
| 668 | Vec2 d[3]; | 
|---|
| 669 | Vec3 s; | 
|---|
| 670 |  | 
|---|
| 671 | for(s32 i = 0; i != COUNT_OF(s.d); ++i) | 
|---|
| 672 | { | 
|---|
| 673 | // pixel center | 
|---|
| 674 | const double Center = 0.5 - FLT_EPSILON; | 
|---|
| 675 | Vec2 p = {min.x + Center, min.y + Center}; | 
|---|
| 676 |  | 
|---|
| 677 | s32 c = (i + 1) % 3, n = (i + 2) % 3; | 
|---|
| 678 |  | 
|---|
| 679 | d[i].x = (a.v[c]->y - a.v[n]->y) / area; | 
|---|
| 680 | d[i].y = (a.v[n]->x - a.v[c]->x) / area; | 
|---|
| 681 | s.d[i] = edgeFn(a.v[c], a.v[n], &p) / area; | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | for(s32 y = min.y, start = min.y * TIC80_WIDTH + min.x; y < max.y; ++y, start += TIC80_WIDTH) | 
|---|
| 685 | { | 
|---|
| 686 | for(s32 i = 0; i != COUNT_OF(a.w.d); ++i) | 
|---|
| 687 | a.w.d[i] = s.d[i]; | 
|---|
| 688 |  | 
|---|
| 689 | for(s32 x = min.x, pixel = start; x < max.x; ++x, ++pixel) | 
|---|
| 690 | { | 
|---|
| 691 | if(a.w.x > -DBL_EPSILON && a.w.y > -DBL_EPSILON && a.w.z > -DBL_EPSILON) | 
|---|
| 692 | { | 
|---|
| 693 | u8 color = shader(&a, pixel); | 
|---|
| 694 | if(color != TRANSPARENT_COLOR) | 
|---|
| 695 | tic_api_poke4(tic, pixel, color); | 
|---|
| 696 | } | 
|---|
| 697 |  | 
|---|
| 698 | for(s32 i = 0; i != COUNT_OF(a.w.d); ++i) | 
|---|
| 699 | a.w.d[i] += d[i].x; | 
|---|
| 700 | } | 
|---|
| 701 |  | 
|---|
| 702 | for(s32 i = 0; i != COUNT_OF(s.d); ++i) | 
|---|
| 703 | s.d[i] += d[i].y; | 
|---|
| 704 | } | 
|---|
| 705 | } | 
|---|
| 706 |  | 
|---|
| 707 | static tic_color triColorShader(const ShaderAttr* a, s32 pixel){return *(u8*)a->data;} | 
|---|
| 708 |  | 
|---|
| 709 | void tic_api_tri(tic_mem* tic, float x1, float y1, float x2, float y2, float x3, float y3, u8 color) | 
|---|
| 710 | { | 
|---|
| 711 | color = mapColor(tic, color); | 
|---|
| 712 | drawTri(tic, | 
|---|
| 713 | &(Vec2){x1, y1}, | 
|---|
| 714 | &(Vec2){x2, y2}, | 
|---|
| 715 | &(Vec2){x3, y3}, | 
|---|
| 716 | triColorShader, &color); | 
|---|
| 717 | } | 
|---|
| 718 |  | 
|---|
| 719 | void tic_api_trib(tic_mem* tic, float x1, float y1, float x2, float y2, float x3, float y3, u8 color) | 
|---|
| 720 | { | 
|---|
| 721 | tic_core* core = (tic_core*)tic; | 
|---|
| 722 |  | 
|---|
| 723 | u8 finalColor = mapColor(tic, color); | 
|---|
| 724 |  | 
|---|
| 725 | drawLine(tic, x1, y1, x2, y2, finalColor); | 
|---|
| 726 | drawLine(tic, x2, y2, x3, y3, finalColor); | 
|---|
| 727 | drawLine(tic, x3, y3, x1, y1, finalColor); | 
|---|
| 728 | } | 
|---|
| 729 |  | 
|---|
| 730 | typedef struct | 
|---|
| 731 | { | 
|---|
| 732 | Vec2 _; | 
|---|
| 733 | Vec3 d; | 
|---|
| 734 | }TexVert; | 
|---|
| 735 |  | 
|---|
| 736 | typedef struct | 
|---|
| 737 | { | 
|---|
| 738 | tic_tilesheet sheet; | 
|---|
| 739 | u8* mapping; | 
|---|
| 740 | const u8* map; | 
|---|
| 741 | const tic_vram* vram; | 
|---|
| 742 | bool depth; | 
|---|
| 743 | } TexData; | 
|---|
| 744 |  | 
|---|
| 745 | static inline bool shaderStart(const ShaderAttr* a, Vec3* vars, s32 pixel) | 
|---|
| 746 | { | 
|---|
| 747 | TexData* data = a->data; | 
|---|
| 748 |  | 
|---|
| 749 | if(data->depth) | 
|---|
| 750 | { | 
|---|
| 751 | vars->z = 0; | 
|---|
| 752 | for(s32 i = 0; i != COUNT_OF(a->v); ++i) | 
|---|
| 753 | { | 
|---|
| 754 | const TexVert* t = (TexVert*)a->v[i]; | 
|---|
| 755 | vars->z += a->w.d[i] * t->d.z; | 
|---|
| 756 | } | 
|---|
| 757 |  | 
|---|
| 758 | if(ZBuffer[pixel] < vars->z); | 
|---|
| 759 | else return false; | 
|---|
| 760 | } | 
|---|
| 761 |  | 
|---|
| 762 | vars->x = vars->y = 0; | 
|---|
| 763 | for(s32 i = 0; i != COUNT_OF(a->v); ++i) | 
|---|
| 764 | { | 
|---|
| 765 | const TexVert* t = (TexVert*)a->v[i]; | 
|---|
| 766 | vars->x += a->w.d[i] * t->d.x; | 
|---|
| 767 | vars->y += a->w.d[i] * t->d.y; | 
|---|
| 768 | } | 
|---|
| 769 |  | 
|---|
| 770 | if(data->depth) | 
|---|
| 771 | vars->x /= vars->z, | 
|---|
| 772 | vars->y /= vars->z; | 
|---|
| 773 |  | 
|---|
| 774 | return true; | 
|---|
| 775 | } | 
|---|
| 776 |  | 
|---|
| 777 | static inline tic_color shaderEnd(const ShaderAttr* a, const Vec3* vars, s32 pixel, tic_color color) | 
|---|
| 778 | { | 
|---|
| 779 | TexData* data = a->data; | 
|---|
| 780 |  | 
|---|
| 781 | if(data->depth && color != TRANSPARENT_COLOR) | 
|---|
| 782 | ZBuffer[pixel] = vars->z; | 
|---|
| 783 |  | 
|---|
| 784 | return color; | 
|---|
| 785 | } | 
|---|
| 786 |  | 
|---|
| 787 | static tic_color triTexMapShader(const ShaderAttr* a, s32 pixel) | 
|---|
| 788 | { | 
|---|
| 789 | TexData* data = a->data; | 
|---|
| 790 |  | 
|---|
| 791 | Vec3 vars; | 
|---|
| 792 | if(!shaderStart(a, &vars, pixel)) | 
|---|
| 793 | return TRANSPARENT_COLOR; | 
|---|
| 794 |  | 
|---|
| 795 | enum { MapWidth = TIC_MAP_WIDTH * TIC_SPRITESIZE, MapHeight = TIC_MAP_HEIGHT * TIC_SPRITESIZE, | 
|---|
| 796 | WMask = TIC_SPRITESIZE - 1, HMask = TIC_SPRITESIZE - 1 }; | 
|---|
| 797 |  | 
|---|
| 798 | s32 iu = tic_modulo(vars.x, MapWidth); | 
|---|
| 799 | s32 iv = tic_modulo(vars.y, MapHeight); | 
|---|
| 800 |  | 
|---|
| 801 | u8 idx = data->map[(iv >> 3) * TIC_MAP_WIDTH + (iu >> 3)]; | 
|---|
| 802 | tic_tileptr tile = tic_tilesheet_gettile(&data->sheet, idx, true); | 
|---|
| 803 |  | 
|---|
| 804 | return shaderEnd(a, &vars, pixel, data->mapping[tic_tilesheet_gettilepix(&tile, iu & WMask, iv & HMask)]); | 
|---|
| 805 | } | 
|---|
| 806 |  | 
|---|
| 807 | static tic_color triTexTileShader(const ShaderAttr* a, s32 pixel) | 
|---|
| 808 | { | 
|---|
| 809 | TexData* data = a->data; | 
|---|
| 810 |  | 
|---|
| 811 | Vec3 vars; | 
|---|
| 812 | if(!shaderStart(a, &vars, pixel)) | 
|---|
| 813 | return TRANSPARENT_COLOR; | 
|---|
| 814 |  | 
|---|
| 815 | enum { WMask = TIC_SPRITESHEET_SIZE - 1, HMask = TIC_SPRITESHEET_SIZE * TIC_SPRITE_BANKS - 1 }; | 
|---|
| 816 |  | 
|---|
| 817 | return shaderEnd(a, &vars, pixel, data->mapping[tic_tilesheet_getpix(&data->sheet, (s32)vars.x & WMask, (s32)vars.y & HMask)]); | 
|---|
| 818 | } | 
|---|
| 819 |  | 
|---|
| 820 | static tic_color triTexVbankShader(const ShaderAttr* a, s32 pixel) | 
|---|
| 821 | { | 
|---|
| 822 | TexData* data = a->data; | 
|---|
| 823 |  | 
|---|
| 824 | Vec3 vars; | 
|---|
| 825 | if(!shaderStart(a, &vars, pixel)) | 
|---|
| 826 | return TRANSPARENT_COLOR; | 
|---|
| 827 |  | 
|---|
| 828 | s32 iu = tic_modulo(vars.x, TIC80_WIDTH); | 
|---|
| 829 | s32 iv = tic_modulo(vars.y, TIC80_HEIGHT); | 
|---|
| 830 |  | 
|---|
| 831 | return shaderEnd(a, &vars, pixel, data->mapping[tic_tool_peek4(data->vram->data, iv * TIC80_WIDTH + iu)]); | 
|---|
| 832 | } | 
|---|
| 833 |  | 
|---|
| 834 | void tic_api_ttri(tic_mem* tic, | 
|---|
| 835 | float x1, float y1, | 
|---|
| 836 | float x2, float y2, | 
|---|
| 837 | float x3, float y3, | 
|---|
| 838 | float u1, float v1, | 
|---|
| 839 | float u2, float v2, | 
|---|
| 840 | float u3, float v3, | 
|---|
| 841 | tic_texture_src texsrc, u8* colors, s32 count, | 
|---|
| 842 | float z1, float z2, float z3, bool depth) | 
|---|
| 843 | { | 
|---|
| 844 | TexData texData = | 
|---|
| 845 | { | 
|---|
| 846 | .sheet = getTileSheetFromSegment(tic, tic->ram->vram.blit.segment), | 
|---|
| 847 | .mapping = getPalette(tic, colors, count), | 
|---|
| 848 | .map = tic->ram->map.data, | 
|---|
| 849 | .vram = &((tic_core*)tic)->state.vbank.mem, | 
|---|
| 850 | .depth = depth, | 
|---|
| 851 | }; | 
|---|
| 852 |  | 
|---|
| 853 | TexVert t[] = | 
|---|
| 854 | { | 
|---|
| 855 | {x1, y1, u1, v1, z1}, | 
|---|
| 856 | {x2, y2, u2, v2, z2}, | 
|---|
| 857 | {x3, y3, u3, v3, z3}, | 
|---|
| 858 | }; | 
|---|
| 859 |  | 
|---|
| 860 | if(depth) | 
|---|
| 861 | for(s32 i = 0; i != COUNT_OF(t); ++i) | 
|---|
| 862 | t[i].d.x /= t[i].d.z, | 
|---|
| 863 | t[i].d.y /= t[i].d.z, | 
|---|
| 864 | t[i].d.z = 1.0 / t[i].d.z; | 
|---|
| 865 |  | 
|---|
| 866 | static const PixelShader Shaders[] = | 
|---|
| 867 | { | 
|---|
| 868 | [tic_tiles_texture] = triTexTileShader, | 
|---|
| 869 | [tic_map_texture]   = triTexMapShader, | 
|---|
| 870 | [tic_vbank_texture] = triTexVbankShader, | 
|---|
| 871 | }; | 
|---|
| 872 |  | 
|---|
| 873 | if(texsrc >= 0 && texsrc < COUNT_OF(Shaders)) | 
|---|
| 874 | drawTri(tic, | 
|---|
| 875 | (const Vec2*)&t[0], | 
|---|
| 876 | (const Vec2*)&t[1], | 
|---|
| 877 | (const Vec2*)&t[2], | 
|---|
| 878 | Shaders[texsrc], &texData); | 
|---|
| 879 | } | 
|---|
| 880 |  | 
|---|
| 881 | void tic_api_map(tic_mem* memory, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, u8* colors, u8 count, s32 scale, RemapFunc remap, void* data) | 
|---|
| 882 | { | 
|---|
| 883 | drawMap((tic_core*)memory, &memory->ram->map, x, y, width, height, sx, sy, colors, count, scale, remap, data); | 
|---|
| 884 | } | 
|---|
| 885 |  | 
|---|
| 886 | void tic_api_mset(tic_mem* memory, s32 x, s32 y, u8 value) | 
|---|
| 887 | { | 
|---|
| 888 | if (x < 0 || x >= TIC_MAP_WIDTH || y < 0 || y >= TIC_MAP_HEIGHT) return; | 
|---|
| 889 |  | 
|---|
| 890 | tic_map* src = &memory->ram->map; | 
|---|
| 891 | *(src->data + y * TIC_MAP_WIDTH + x) = value; | 
|---|
| 892 | } | 
|---|
| 893 |  | 
|---|
| 894 | u8 tic_api_mget(tic_mem* memory, s32 x, s32 y) | 
|---|
| 895 | { | 
|---|
| 896 | if (x < 0 || x >= TIC_MAP_WIDTH || y < 0 || y >= TIC_MAP_HEIGHT) return 0; | 
|---|
| 897 |  | 
|---|
| 898 | const tic_map* src = &memory->ram->map; | 
|---|
| 899 | return *(src->data + y * TIC_MAP_WIDTH + x); | 
|---|
| 900 | } | 
|---|
| 901 |  | 
|---|
| 902 | void tic_api_line(tic_mem* memory, float x0, float y0, float x1, float y1, u8 color) | 
|---|
| 903 | { | 
|---|
| 904 | drawLine(memory, x0, y0, x1, y1, mapColor(memory, color)); | 
|---|
| 905 | } | 
|---|
| 906 |  | 
|---|
| 907 | #if defined(BUILD_DEPRECATED) | 
|---|
| 908 | #include "draw_dep.c" | 
|---|
| 909 | #endif | 
|---|
| 910 |  | 
|---|