|
|
|
@@ -39,6 +39,7 @@ typedef struct { |
|
|
|
SDL_Renderer* renderer; |
|
|
|
SDL_Surface* background; |
|
|
|
SDL_Surface* background_line; |
|
|
|
SDL_Surface* foreground; |
|
|
|
SDL_Surface* sprite; |
|
|
|
SDL_Surface* target; |
|
|
|
} sdl_render_data; |
|
|
|
@@ -110,12 +111,30 @@ static int sdl_render_init(nes_Renderer* rend) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (0 == status) { |
|
|
|
data->foreground = SDL_CreateRGBSurfaceWithFormat( |
|
|
|
0, nes_ppu_render_w, nes_ppu_render_h, |
|
|
|
8, SDL_PIXELFORMAT_INDEX8 |
|
|
|
); |
|
|
|
|
|
|
|
if (NULL == data->foreground) { |
|
|
|
fprintf(stderr, "SDL: Failed to create foreground\n"); |
|
|
|
SDL_FreeSurface(data->background_line); |
|
|
|
SDL_FreeSurface(data->background); |
|
|
|
SDL_DestroyRenderer(data->renderer); |
|
|
|
SDL_DestroyWindow(data->window); |
|
|
|
SDL_Quit(); |
|
|
|
status = -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (0 == status) { |
|
|
|
data->sprite = SDL_CreateRGBSurfaceWithFormat( |
|
|
|
0, 8, 16, 8, SDL_PIXELFORMAT_INDEX8 |
|
|
|
); |
|
|
|
if (NULL == data->sprite) { |
|
|
|
fprintf(stderr, "SDL: Failed to create sprite\n"); |
|
|
|
SDL_FreeSurface(data->foreground); |
|
|
|
SDL_FreeSurface(data->background_line); |
|
|
|
SDL_FreeSurface(data->background); |
|
|
|
SDL_DestroyRenderer(data->renderer); |
|
|
|
@@ -133,6 +152,7 @@ static int sdl_render_init(nes_Renderer* rend) { |
|
|
|
if (NULL == data->target) { |
|
|
|
fprintf(stderr, "SDL: Failed to create target\n"); |
|
|
|
SDL_FreeSurface(data->sprite); |
|
|
|
SDL_FreeSurface(data->foreground); |
|
|
|
SDL_FreeSurface(data->background_line); |
|
|
|
SDL_FreeSurface(data->background); |
|
|
|
SDL_DestroyRenderer(data->renderer); |
|
|
|
@@ -147,6 +167,10 @@ static int sdl_render_init(nes_Renderer* rend) { |
|
|
|
nes_palette, 0U, 64U); |
|
|
|
SDL_SetColorKey(data->background, SDL_TRUE, 0xFFU); |
|
|
|
|
|
|
|
SDL_SetPaletteColors(data->foreground->format->palette, |
|
|
|
nes_palette, 0U, 64U); |
|
|
|
SDL_SetColorKey(data->foreground, SDL_TRUE, 0xFFU); |
|
|
|
|
|
|
|
SDL_SetPaletteColors(data->sprite->format->palette, |
|
|
|
nes_palette, 0U, 64U); |
|
|
|
SDL_SetColorKey(data->sprite, SDL_TRUE, 0xFFU); |
|
|
|
@@ -161,6 +185,7 @@ static void sdl_render_done(nes_Renderer* rend) { |
|
|
|
sdl_render_data* data = (sdl_render_data*)rend->data; |
|
|
|
SDL_FreeSurface(data->target); |
|
|
|
SDL_FreeSurface(data->sprite); |
|
|
|
SDL_FreeSurface(data->foreground); |
|
|
|
SDL_FreeSurface(data->background_line); |
|
|
|
SDL_FreeSurface(data->background); |
|
|
|
SDL_DestroyRenderer(data->renderer); |
|
|
|
@@ -168,6 +193,52 @@ static void sdl_render_done(nes_Renderer* rend) { |
|
|
|
SDL_Quit(); |
|
|
|
} |
|
|
|
|
|
|
|
static inline void render_sprite_line( |
|
|
|
const nes_ppu* ppu, int index, int y, const uint8_t* pal, |
|
|
|
uint8_t* dst, int start, int end) { |
|
|
|
uint8_t* sprite = chr_mem(ppu, index * 16U); |
|
|
|
uint8_t lo = sprite[0U + y] << start; |
|
|
|
uint8_t hi = sprite[8U + y] << start; |
|
|
|
for (int x = start; x < end; ++x) { |
|
|
|
int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7)); |
|
|
|
if (pal_idx) *dst = pal[pal_idx]; |
|
|
|
dst++; |
|
|
|
hi <<= 1; |
|
|
|
lo <<= 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static inline void render_sprite_line_flip( |
|
|
|
const nes_ppu* ppu, int index, int y, const uint8_t* pal, |
|
|
|
uint8_t* dst, int start, int end) { |
|
|
|
uint8_t* sprite = chr_mem(ppu, index * 16U); |
|
|
|
uint8_t lo = sprite[0U + y] >> start; |
|
|
|
uint8_t hi = sprite[8U + y] >> start; |
|
|
|
for (int x = start; x < end; ++x) { |
|
|
|
int pal_idx = (((hi & 1) << 1) | (lo & 1)); |
|
|
|
if (pal_idx) *dst = pal[pal_idx]; |
|
|
|
dst++; |
|
|
|
hi >>= 1; |
|
|
|
lo >>= 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static inline void render_bg_sprite_line( |
|
|
|
const nes_ppu* ppu, int index, int y, const uint8_t* pal, |
|
|
|
uint8_t* dst, int start, int end) { |
|
|
|
uint8_t* sprite = chr_mem(ppu, index * 16U); |
|
|
|
uint8_t lo = sprite[0U + y] << (start % 8); |
|
|
|
uint8_t hi = sprite[8U + y] << (start % 8); |
|
|
|
for (int x = start; x < end; ++x) { |
|
|
|
int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7)); |
|
|
|
*dst = (pal_idx ? pal[pal_idx] : 0xFFU); |
|
|
|
dst++; |
|
|
|
hi <<= 1; |
|
|
|
lo <<= 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
static void render_bg_sprite(const nes_ppu* ppu, int index, |
|
|
|
const uint8_t* pal, |
|
|
|
void* loc, int pitch) { |
|
|
|
@@ -216,7 +287,62 @@ static void render_background_area(const nes_ppu* ppu, int page, |
|
|
|
index_line += nes_ppu_blocks_w; |
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
static inline void render_bg_scanline_area( |
|
|
|
const nes_ppu* ppu, int page, |
|
|
|
uint8_t* dst, int x, int y, int w) { |
|
|
|
int block_x = x / 8; |
|
|
|
int line_end = x + w; |
|
|
|
int block_y = y / 8; |
|
|
|
y = y % 8; |
|
|
|
int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0; |
|
|
|
const uint8_t* indexes = nes_map_vram_addr(ppu->mapper, |
|
|
|
page << 10); |
|
|
|
const uint8_t* attrs = indexes + 960U; |
|
|
|
const uint8_t* index = indexes + |
|
|
|
(block_y * nes_ppu_blocks_w) + |
|
|
|
block_x; |
|
|
|
|
|
|
|
while (x < line_end) { |
|
|
|
int attr_idx = ((block_y / 4) * 8) + (block_x / 4); |
|
|
|
int shift = 2 * ((block_y & 0b10) | |
|
|
|
((block_x & 0b10) >> 1)); |
|
|
|
int pal_idx = (attrs[attr_idx] >> shift) & 3; |
|
|
|
const uint8_t* pal = &ppu->palette[pal_idx * 4]; |
|
|
|
|
|
|
|
int end = (x + 8) & ~7; |
|
|
|
if (end > line_end) end = line_end; |
|
|
|
|
|
|
|
render_bg_sprite_line(ppu, bank + *index, y, |
|
|
|
pal, dst, x, end); |
|
|
|
|
|
|
|
++index; |
|
|
|
++block_x; |
|
|
|
dst += (end - x); |
|
|
|
x = end; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void render_bg_scanline(const nes_ppu* ppu, int scanline, |
|
|
|
uint8_t* dst) { |
|
|
|
// TODO: Handle column 0 flag |
|
|
|
|
|
|
|
int page = (ppu->control & ppu_Control_Nametable_Mask); |
|
|
|
int x = ppu->scroll_x; |
|
|
|
int y = ppu->scroll_y + scanline; |
|
|
|
|
|
|
|
if (y >= nes_ppu_render_h) { |
|
|
|
y -= nes_ppu_render_h; |
|
|
|
page ^= 0b10; |
|
|
|
} |
|
|
|
|
|
|
|
int w = (nes_ppu_render_w - x); |
|
|
|
render_bg_scanline_area(ppu, page, dst, x, y, w); |
|
|
|
render_bg_scanline_area(ppu, page ^ 1, dst + w, 0, y, x); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
static void render_background_line(const nes_ppu* ppu, int line, |
|
|
|
void* buffer, int pitch) { |
|
|
|
// TODO: Handle column 0 flag |
|
|
|
@@ -246,27 +372,6 @@ static void render_background_line(const nes_ppu* ppu, int line, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
uint8_t y; |
|
|
|
uint8_t index; |
|
|
|
uint8_t attr; |
|
|
|
uint8_t x; |
|
|
|
} oam_sprite; |
|
|
|
|
|
|
|
typedef enum { |
|
|
|
oam_Attr_Pal_Mask = 0b00000011, |
|
|
|
oam_Attr_Background = 0b00100000, |
|
|
|
oam_Attr_Flip_X = 0b01000000, |
|
|
|
oam_Attr_Flip_Y = 0b10000000, |
|
|
|
} oam_Attribute; |
|
|
|
|
|
|
|
typedef enum { |
|
|
|
oam_Index_Bank = 0b00000001, |
|
|
|
oam_Index_Tile_Mask = 0b11111110, |
|
|
|
} oam_Index; |
|
|
|
|
|
|
|
|
|
|
|
static void render_sprite(nes_ppu* ppu, int index, |
|
|
|
const uint8_t* pal, uint8_t attr, |
|
|
|
void* loc, int pitch) { |
|
|
|
@@ -299,7 +404,62 @@ static void render_sprite(nes_ppu* ppu, int index, |
|
|
|
++sprite; |
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
static void render_line_sprites(nes_ppu* ppu, uint8_t* dst_line, |
|
|
|
int scanline, int background) { |
|
|
|
int bank = (ppu->control & ppu_Control_Sprite_Bank) ? |
|
|
|
0x100 : 0; |
|
|
|
for ( int i_sprite = nes_ppu_oam_sprite_count - 1; |
|
|
|
i_sprite >= 0; --i_sprite) { |
|
|
|
const oam_sprite* sprite = &ppu->oam[i_sprite]; |
|
|
|
if ((sprite->attr & oam_Attr_Background) ^ background) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ( !(ppu->mask & ppu_Mask_Left_Sprite) && |
|
|
|
sprite->x < 8) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
int y_pos = (sprite->y + 1); |
|
|
|
int y = scanline - y_pos; |
|
|
|
if (0 > y) continue; |
|
|
|
int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8; |
|
|
|
if (y >= h) continue; |
|
|
|
|
|
|
|
int index = sprite->index; |
|
|
|
if (ppu->control & ppu_Control_Sprite_Size) { |
|
|
|
bank = (index & 1) ? 0x100 : 0; |
|
|
|
index &= 0xFEU; |
|
|
|
} |
|
|
|
index += bank; |
|
|
|
|
|
|
|
if (ppu->control & ppu_Control_Sprite_Size) { |
|
|
|
if (y >= 8) { |
|
|
|
index ^= 1; |
|
|
|
y -= 8; |
|
|
|
} |
|
|
|
if (sprite->attr & oam_Attr_Flip_Y) { |
|
|
|
index ^= 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int pal_idx = (sprite->attr & oam_Attr_Pal_Mask); |
|
|
|
const uint8_t* pal = &ppu->palette[16 + (pal_idx * 4)]; |
|
|
|
|
|
|
|
if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y; |
|
|
|
int end = nes_ppu_render_w - sprite->x; |
|
|
|
if (end > 8) end = 8; |
|
|
|
if (sprite->attr & oam_Attr_Flip_X) { |
|
|
|
render_sprite_line_flip(ppu, index, y, pal, |
|
|
|
dst_line + sprite->x, 0, end); |
|
|
|
} else { |
|
|
|
render_sprite_line(ppu, index, y, pal, |
|
|
|
dst_line + sprite->x, 0, end); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
static void render_sprites(nes_ppu* ppu, |
|
|
|
SDL_Surface* buffer, |
|
|
|
SDL_Surface* target, |
|
|
|
@@ -363,30 +523,30 @@ static void render_sprites(nes_ppu* ppu, |
|
|
|
target, &target_rect); |
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
// Check sprite (0 only) collision on a scanline |
|
|
|
// This assumes that we've verified that this sprite |
|
|
|
// intersects with this scanline. |
|
|
|
// Scanline is 0-239 from inside the rendering window |
|
|
|
// (though we should never see this called with 0). |
|
|
|
static int eval_sprite_line(const nes_ppu* ppu, int line, |
|
|
|
static int eval_sprite_line(const nes_ppu* ppu, int y, |
|
|
|
const oam_sprite* sprite, |
|
|
|
const uint8_t* chr, |
|
|
|
const uint8_t* back) { |
|
|
|
int hit_pos = -1; |
|
|
|
|
|
|
|
int y = line - (sprite->y + 1); |
|
|
|
|
|
|
|
if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y; |
|
|
|
uint8_t lo = chr[0U + y]; |
|
|
|
uint8_t hi = chr[8U + y]; |
|
|
|
|
|
|
|
back += sprite->x; |
|
|
|
for (int x = sprite->x; x < nes_ppu_render_w; ++x) { |
|
|
|
int end = nes_ppu_render_w; |
|
|
|
if (end > sprite->x + 8) end = sprite->x + 8; |
|
|
|
for (int x = sprite->x; x < end; ++x) { |
|
|
|
int pal_idx = (sprite->attr & oam_Attr_Flip_X) ? |
|
|
|
(((hi & 1) << 1) | (lo & 1)) : |
|
|
|
( ((hi & 0x80) >> 6) | |
|
|
|
((lo & 0x80) >> 7)); |
|
|
|
(((hi & 0x80) >> 6) | ((lo & 0x80) >> 7)); |
|
|
|
if (pal_idx && *back != 0xFFU) { |
|
|
|
hit_pos = x; |
|
|
|
break; |
|
|
|
@@ -404,6 +564,7 @@ static int eval_sprite_line(const nes_ppu* ppu, int line, |
|
|
|
return hit_pos; |
|
|
|
} |
|
|
|
|
|
|
|
#if 0 |
|
|
|
static void update_sprite_hit(nes_ppu* ppu, int block_line, |
|
|
|
const void* back_pixels, |
|
|
|
int back_pitch) { |
|
|
|
@@ -498,6 +659,102 @@ static void render_block_line(nes_ppu* ppu, int line, |
|
|
|
data->background, &render_rect); |
|
|
|
*/ |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
static void update_scanline_hit(nes_ppu* ppu, uint8_t* back_line, |
|
|
|
int scanline) { |
|
|
|
const oam_sprite* sprite = &ppu->oam[0]; |
|
|
|
int bank = (ppu->control & ppu_Control_Sprite_Bank) ? |
|
|
|
0x100 : 0; |
|
|
|
|
|
|
|
if ( !(ppu->mask & ppu_Mask_Left_Sprite) && |
|
|
|
sprite->x < 8) { |
|
|
|
return; |
|
|
|
} |
|
|
|
int y_pos = (sprite->y + 1); |
|
|
|
int y = scanline - y_pos; |
|
|
|
if (0 > y) return; |
|
|
|
int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8; |
|
|
|
if (y >= h) return; |
|
|
|
|
|
|
|
int index = sprite->index; |
|
|
|
if (ppu->control & ppu_Control_Sprite_Size) { |
|
|
|
bank = (index & 1) ? 0x100 : 0; |
|
|
|
index &= 0xFEU; |
|
|
|
} |
|
|
|
index += bank; |
|
|
|
|
|
|
|
if (ppu->control & ppu_Control_Sprite_Size) { |
|
|
|
if (y >= 8) { |
|
|
|
index ^= 1; |
|
|
|
y -= 8; |
|
|
|
} |
|
|
|
if (sprite->attr & oam_Attr_Flip_Y) { |
|
|
|
index ^= 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const uint8_t* chr = chr_mem(ppu, index * 16U); |
|
|
|
int hit = eval_sprite_line(ppu, y, sprite, chr, back_line); |
|
|
|
|
|
|
|
if (hit >= 0) { |
|
|
|
REND_LOG("Upcoming hit @ %d, %d\n", scanline + 1, hit); |
|
|
|
REND_LOG("(Currently @ %d, %d)\n", ppu->scanline, ppu->cycle); |
|
|
|
ppu->hit_line = scanline; |
|
|
|
ppu->hit_dot = hit; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void render_scanline(nes_ppu* ppu, int line, |
|
|
|
sdl_render_data* data) { |
|
|
|
if (!(ppu->mask & (ppu_Mask_Sprite | ppu_Mask_Back))) { |
|
|
|
// Do nothing if BOTH are disabled. |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
SDL_Rect src_rect = { |
|
|
|
.x = 0, |
|
|
|
.y = 0, |
|
|
|
.w = nes_ppu_render_w, |
|
|
|
.h = 1, |
|
|
|
}; |
|
|
|
|
|
|
|
SDL_Rect dst_rect = { |
|
|
|
.x = 0, |
|
|
|
.y = line, |
|
|
|
.w = nes_ppu_render_w, |
|
|
|
.h = 1, |
|
|
|
}; |
|
|
|
|
|
|
|
uint8_t* foreground = data->foreground->pixels; |
|
|
|
uint8_t* background = data->background->pixels; |
|
|
|
|
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
memset(foreground, 0xFFU, nes_ppu_render_w); |
|
|
|
render_line_sprites(ppu, foreground, line, |
|
|
|
oam_Attr_Background); |
|
|
|
SDL_BlitSurface(data->foreground, &src_rect, |
|
|
|
data->target, &dst_rect); |
|
|
|
} |
|
|
|
|
|
|
|
// We check for hits if EITHER layer is enabled. |
|
|
|
render_bg_scanline(ppu, line, background); |
|
|
|
if (ppu->hit_line <= 0) { |
|
|
|
update_scanline_hit(ppu, background, line); |
|
|
|
} |
|
|
|
|
|
|
|
if (ppu->mask & ppu_Mask_Back) { |
|
|
|
SDL_BlitSurface(data->background, &src_rect, |
|
|
|
data->target, &dst_rect); |
|
|
|
} |
|
|
|
|
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
memset(foreground, 0xFFU, nes_ppu_render_w); |
|
|
|
render_line_sprites(ppu, foreground, line, 0); |
|
|
|
SDL_BlitSurface(data->foreground, &src_rect, |
|
|
|
data->target, &dst_rect); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { |
|
|
|
int status = 0; |
|
|
|
@@ -513,9 +770,21 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { |
|
|
|
SDL_FillRect(data->target, NULL, ((int)ext.r << 16) | |
|
|
|
((int)ext.g << 8) | |
|
|
|
ext.b); |
|
|
|
/* |
|
|
|
memset(data->foreground->pixels, 0xFFU, |
|
|
|
data->foreground->pitch * data->foreground->h); |
|
|
|
|
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
render_sprites(ppu, data->sprite, data->target, |
|
|
|
oam_Attr_Background); |
|
|
|
|
|
|
|
render_sprites(ppu, data->sprite, data->foreground, 0); |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|
} else if (ppu->scanline < nes_ppu_prerender + |
|
|
|
nes_ppu_height) { |
|
|
|
/* |
|
|
|
int line = (ppu->scanline - (int)nes_ppu_prerender) / 8; |
|
|
|
|
|
|
|
REND_LOG("Scanline %3d -> Line %2d @ X %d\n", ppu->scanline, line, ppu->scroll_x); |
|
|
|
@@ -524,26 +793,44 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { |
|
|
|
if (ppu->mask & ppu_Mask_Back) { |
|
|
|
render_block_line(ppu, line, data); |
|
|
|
} |
|
|
|
*/ |
|
|
|
REND_LOG("Scanline %3d : X %d Y %d\n", ppu->scanline, ppu->scroll_x, ppu->scroll_y); |
|
|
|
int line = ppu->scanline - (int)nes_ppu_prerender; |
|
|
|
render_scanline(ppu, line, data); |
|
|
|
|
|
|
|
} else { |
|
|
|
REND_LOG("Scanline %3d -> Postrender\n", ppu->scanline); |
|
|
|
|
|
|
|
/* |
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
render_sprites(ppu, data->sprite, data->target, |
|
|
|
oam_Attr_Background); |
|
|
|
} |
|
|
|
*/ |
|
|
|
/* |
|
|
|
if (ppu->mask & ppu_Mask_Back) { |
|
|
|
// Render final partial line |
|
|
|
if (0 != (ppu->scroll_y % 8)) { |
|
|
|
render_block_line(ppu, nes_ppu_blocks_h, data); |
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
// TODO: Render background sprites that start this block |
|
|
|
} |
|
|
|
if (ppu->mask & ppu_Mask_Back) { |
|
|
|
render_block_line(ppu, nes_ppu_blocks_h, data); |
|
|
|
} |
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
// TODO: Render foreground sprites that end this block |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
SDL_BlitSurface(data->background, NULL, |
|
|
|
data->target, NULL); |
|
|
|
} |
|
|
|
|
|
|
|
if (ppu->mask & ppu_Mask_Sprite) { |
|
|
|
render_sprites(ppu, data->sprite, data->target, 0); |
|
|
|
SDL_BlitSurface(data->foreground, NULL, |
|
|
|
data->target, NULL); |
|
|
|
} |
|
|
|
*/ |
|
|
|
SDL_Texture* texture = SDL_CreateTextureFromSurface( |
|
|
|
data->renderer, data->target |
|
|
|
); |
|
|
|
|