Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CR] Add low light and night vision filters to tilesets #13715

Merged
merged 4 commits into from Oct 17, 2015

Conversation

Projects
None yet
4 participants
@DanmakuDan
Copy link
Contributor

commented Oct 4, 2015

(Updated Oct 11) This adds an extra set of tile textures that have been modified to be in grayscale, night vision, washed-out night vision, usable for low light situations ingame. The extra tiles are generated during tileset loading by copying the main tile surface and applying a color filter to it. The light level that was provided previously per tile is used in determining the tile texture to use. This should allow the tiles display to match closer to what ASCII was already showing based on light levels.

Note: characters will not show up with night vision currently, since the player tile is always considered lit, causing the player to appear very bright when using night vision goggles.

Outside view during daylight:
cata_daylight

Near dusk:
cata_dusk

Flashlight on:
cata_dusk_flashlight

Standing inside a furniture store during dusk:
cata_dusk_store

Night vision goggles at dusk:
cata_dusk_store_nv

Washed out color caused by using a flashlight with goggles:
cata_dusk_store_nv_flashlight

Washed out color caused by standing in daylight with goggles on:
cata_daylight_nv

@chaosvolt

This comment has been minimized.

Copy link
Contributor

commented Oct 5, 2015

Ah, so this doesn't require tileset authors to add anything, then?

@DanmakuDan

This comment has been minimized.

Copy link
Contributor Author

commented Oct 5, 2015

It's like a filter being applied to an image, and the post-processed image is stored as the textures used for rendering.

@chaosvolt

This comment has been minimized.

Copy link
Contributor

commented Oct 5, 2015

Ah, interesting. o.o

@BevapDin

This comment has been minimized.

Copy link
Contributor

commented Oct 5, 2015

Creating the surface requires different logic based on the endianness of the platform (see create_tile_surface), does accessing the pixels not need this?

Lots of (C-style) casting going on there. Would be easier to read if the pointer is stored:

const auto base_array = reinterpret_cast<unsigned char*>(surf->pixels);
const auto pixel_ptr = base_array + (y * w + x) * 4;
pix.r = pixel_ptr[0];
pix.g = pixel_ptr[1];
...

After all, the only thing that differs for the pix.(r|g|b|a) = lines is the color index 0...3.

@DanmakuDan

This comment has been minimized.

Copy link
Contributor Author

commented Oct 5, 2015

I was wondering about endianness earlier, but the create_tile_surface function already handles that using SDL's endian macro already. The resulting pixel format is always in the order R G B A. Alternatively, if that's not actually true, then I think it currently would affect a Mac build?

@DanmakuDan

This comment has been minimized.

Copy link
Contributor Author

commented Oct 5, 2015

Updated pointer logic, the endianness is already handled here:

SDL_Surface *cata_tiles::create_tile_surface()
{
    SDL_Surface *surface;
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        surface = SDL_CreateRGBSurface(0, tile_width, tile_height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
    #else
        surface = SDL_CreateRGBSurface(0, tile_width, tile_height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
    #endif
    if( surface == nullptr ) {
        dbg( D_ERROR ) << "Failed to create surface: " << SDL_GetError();
    }
    return surface;
}
@DanmakuDan

This comment has been minimized.

Copy link
Contributor Author

commented Oct 10, 2015

Currently adding night vision filters, setting back to WIP.

@DanmakuDan DanmakuDan changed the title Add low light display and generation for tilesets [WIP] Add low light display and generation for tilesets Oct 10, 2015

@DanmakuDan DanmakuDan force-pushed the DanmakuDan:low_light_tile_visuals branch to 10fdd21 Oct 11, 2015

@DanmakuDan

This comment has been minimized.

Copy link
Contributor Author

commented Oct 11, 2015

Night vision goggle filters have been added, and first post was updated.

@DanmakuDan DanmakuDan changed the title [WIP] Add low light display and generation for tilesets [CR] Add low light display and generation for tilesets Oct 11, 2015

@DanmakuDan DanmakuDan changed the title [CR] Add low light display and generation for tilesets [CR] Add low light and night vision filters to tilesets Oct 11, 2015

@chaosvolt

This comment has been minimized.

Copy link
Contributor

commented Oct 11, 2015

Wow that is impressive.

@@ -179,66 +193,212 @@ void cata_tiles::get_tile_information(std::string config_path, std::string &json
}
}

inline pixel cata_tiles::get_pixel_color(SDL_Surface *surf, int x, int y, int w)

This comment has been minimized.

Copy link
@kevingranade

kevingranade Oct 11, 2015

Member

These two pixel adaptors (translating from a SDL pixel to your idea of a pixel IIUC) don't need to be part of the cata_tiles class, you can make them static helper functions, which will limit their level of privledge and not expose them to callers of cata_tiles. This would also let you move the pixel struct definition into the .cpp file so it isn't shared outside the class either.

pixel_ptr[3] = static_cast<unsigned char>(pix.a);
}

void cata_tiles::convert_surface_to_grayscale(SDL_Surface *surf)

This comment has been minimized.

Copy link
@kevingranade

kevingranade Oct 11, 2015

Member

These three methods seem like good candidates to be deduplicated, you can extract the code for iterating over the pixel values, and just have the logic that is applied to each pixel be declared seperately.

@@ -217,6 +235,12 @@ class cata_tiles

/** Surface/Sprite rotation specifics */
SDL_Surface *create_tile_surface();
SDL_Surface *create_tile_surface(int w, int h);
void convert_surface_to_grayscale(SDL_Surface *surf);

This comment has been minimized.

Copy link
@kevingranade

kevingranade Oct 11, 2015

Member

As none of these new methods are called from outside cata_tiles, they should be private instead of protected.

@@ -319,8 +343,22 @@ class cata_tiles
/** Variables */
SDL_Renderer *renderer;
tile_map tile_values;
tile_map shadow_tile_values;

This comment has been minimized.

Copy link
@kevingranade

kevingranade Oct 11, 2015

Member

These new member variables should get the same treatment as the new methods, default to private instead of protected.

inline pixel cata_tiles::get_pixel_color(SDL_Surface *surf, int x, int y, int w)
{
pixel pix;
const auto pixelarray = reinterpret_cast<unsigned char *>(surf->pixels);

This comment has been minimized.

Copy link
@kevingranade

kevingranade Oct 11, 2015

Member

I'll have to poke at the SDL documentation a bit, but usually it provides a cleaner interface than this to manipulate data, there are probably some setter/getter macros or something that would encapsulate all this.

@DanmakuDan

This comment has been minimized.

Copy link
Contributor Author

commented Oct 13, 2015

I noticed some missing logic related to ascii sets loading, will be updating soon.

for (int y = 0; y < surf->h; y++) {
for (int x = 0; x < surf->w; x++) {
pixel pix = get_pixel_color(surf, x, y, surf->w);
switch (filter){

This comment has been minimized.

Copy link
@BevapDin

BevapDin Oct 15, 2015

Contributor

Technically, you don't need a COLOR_FILTER enum at all, you can just forward a reference to the function that should be applied to each pixel. And apply_color_filter can be a free function.

void apply_color_filter( SDL_Surface *surf, void (&pixel_converter)(pixel &) )
{
    for( ... ) {
        pixel pix = get_pixel_color( ... );
        pixel_converter( pix );
        set_pixel_color( ... );
    }
}

void init() {
    ...
    apply_color_filter( shadow_tile_atlas, color_pixel_grayscale );
    apply_color_filter( shadow_tile_atlas, color_pixel_overexposed );
    apply_color_filter( shadow_tile_atlas, color_pixel_nightvision );
    ...
}

If you keep the enum, please remove the default: case in the switch. It disables a very useful warning of the compiler: if the switch statement is over an enum type (as in this case), the compiler will warn you if some entries of the enum are not handled. This comes in handy when another entry is added to the enum, but the switch is not updated.

}
if( shadow_tile_tex != nullptr ) {
shadow_tile_values.push_back(shadow_tile_tex);
shadow_tilecount++;

This comment has been minimized.

Copy link
@BevapDin

BevapDin Oct 15, 2015

Contributor

Why this separate counter? It is only incremented when stuff is pushed to shadow_tile_values, so why not use shadow_tile_values.size() instead? Also the code that resets that counter is a bit inconsistent: the vector is cleared in clear(), but the counter is reset in reinit (right after calling clear). It's technically correct, but it's quite confusing.

@kevingranade kevingranade merged commit 5cd0c2d into CleverRaven:master Oct 17, 2015

1 check passed

default
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.