1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-20 21:43:06 +01:00
Files
OpenRCT2/src/openrct2/drawing/LightFX.cpp
Hielke Morsink 55979a3fff Remove and replace C typedefs
`typedef struct/union/enum name { ... } name_again;` is not needed whe compiling C++, moving the name at the back to be in front of the object and removing `typedef` makes it usable the very same way.
This also replaces typedefs with the using keyword. They have better readability, especially for function pointer types, and would allow more flexibility when used with templates.
2018-02-14 09:42:26 +01:00

1019 lines
38 KiB
C++

#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#ifdef __ENABLE_LIGHTFX__
#include <algorithm>
#include <cmath>
#include "../common.h"
#include "../config/Config.h"
#include "../Game.h"
#include "../interface/Viewport.h"
#include "../interface/Window.h"
#include "../paint/tile_element/TileElement.h"
#include "../util/Util.h"
#include "../world/Climate.h"
#include "Drawing.h"
#include "LightFX.h"
static uint8 _bakedLightTexture_lantern_0[32*32];
static uint8 _bakedLightTexture_lantern_1[64*64];
static uint8 _bakedLightTexture_lantern_2[128*128];
static uint8 _bakedLightTexture_lantern_3[256*256];
static uint8 _bakedLightTexture_spot_0[32 * 32];
static uint8 _bakedLightTexture_spot_1[64 * 64];
static uint8 _bakedLightTexture_spot_2[128 * 128];
static uint8 _bakedLightTexture_spot_3[256 * 256];
static rct_drawpixelinfo _pixelInfo;
static bool _lightfxAvailable = false;
static void* _light_rendered_buffer_back = nullptr;
static void* _light_rendered_buffer_front = nullptr;
static uint32 _lightPolution_back = 0;
static uint32 _lightPolution_front = 0;
struct lightlist_entry {
sint16 x, y, z;
uint8 lightType;
uint8 lightIntensity;
uint32 lightID;
uint16 lightIDqualifier;
uint8 lightLinger;
uint8 pad[1];
};
static lightlist_entry _LightListA[16000];
static lightlist_entry _LightListB[16000];
static lightlist_entry *_LightListBack;
static lightlist_entry *_LightListFront;
static uint32 LightListCurrentCountBack;
static uint32 LightListCurrentCountFront;
static sint16 _current_view_x_front = 0;
static sint16 _current_view_y_front = 0;
static uint8 _current_view_rotation_front = 0;
static uint8 _current_view_zoom_front = 0;
static sint16 _current_view_x_back = 0;
static sint16 _current_view_y_back = 0;
static uint8 _current_view_rotation_back = 0;
static uint8 _current_view_zoom_back = 0;
static uint8 _current_view_zoom_back_delay = 0;
static rct_palette gPalette_light;
static uint8 calc_light_intensity_lantern(sint32 x, sint32 y) {
double distance = (double)(x * x + y * y);
double light = 0.03 + std::pow(10.0 / (1.0 + distance / 100.0), 0.55);
light *= std::min(1.0, std::max(0.0, 2.0 - std::sqrt(distance) / 64));
light *= 0.1f;
return (uint8)(std::min(255.0, light * 255.0));
}
static uint8 calc_light_intensity_spot(sint32 x, sint32 y) {
double distance = (double)(x * x + y * y);
double light = 0.3 + std::pow(10.0 / (1.0 + distance / 100.0), 0.75);
light *= std::min(1.0, std::max(0.0, 2.0 - std::sqrt(distance) / 64));
light *= 0.5f;
return (uint8)(std::min(255.0, light * 255.0)) >> 4;
}
static void calc_rescale_light_half( uint8 *target, uint8 *source,uint32 targetWidth, uint32 targetHeight) {
uint8 *parcerRead = source;
uint8 *parcerWrite = target;
for (uint32 y = 0; y < targetHeight; y++) {
for (uint32 x = 0; x < targetWidth; x++) {
*parcerWrite = (*parcerRead);
parcerWrite++;
parcerRead += 2;
}
parcerRead += targetWidth * 2;
}
}
void lightfx_set_available(bool available)
{
_lightfxAvailable = available;
}
bool lightfx_is_available()
{
return _lightfxAvailable && gConfigGeneral.enable_light_fx != 0;
}
void lightfx_init()
{
_LightListBack = _LightListA;
_LightListFront = _LightListB;
memset(_bakedLightTexture_lantern_0, 0xFF, 32 * 32);
memset(_bakedLightTexture_lantern_1, 0xFF, 64 * 64);
memset(_bakedLightTexture_lantern_2, 0xFF, 128 * 128);
memset(_bakedLightTexture_lantern_3, 0xFF, 256 * 256);
uint8 *parcer = _bakedLightTexture_lantern_3;
for (sint32 y = 0; y < 256; y++) {
for (sint32 x = 0; x < 256; x++) {
*parcer = calc_light_intensity_lantern(x - 128, y - 128);
parcer++;
}
}
parcer = _bakedLightTexture_spot_3;
for (sint32 y = 0; y < 256; y++) {
for (sint32 x = 0; x < 256; x++) {
*parcer = calc_light_intensity_spot(x - 128, y - 128);
parcer++;
}
}
calc_rescale_light_half(_bakedLightTexture_lantern_2, _bakedLightTexture_lantern_3, 128, 128);
calc_rescale_light_half(_bakedLightTexture_lantern_1, _bakedLightTexture_lantern_2, 64, 64);
calc_rescale_light_half(_bakedLightTexture_lantern_0, _bakedLightTexture_lantern_1, 32, 32);
calc_rescale_light_half(_bakedLightTexture_spot_2, _bakedLightTexture_spot_3, 128, 128);
calc_rescale_light_half(_bakedLightTexture_spot_1, _bakedLightTexture_spot_2, 64, 64);
calc_rescale_light_half(_bakedLightTexture_spot_0, _bakedLightTexture_spot_1, 32, 32);
}
void lightfx_update_buffers(rct_drawpixelinfo *info)
{
_light_rendered_buffer_front = realloc(_light_rendered_buffer_front, info->width * info->height);
_light_rendered_buffer_back = realloc(_light_rendered_buffer_back, info->width * info->height);
memcpy(&_pixelInfo, info, sizeof(rct_drawpixelinfo));
}
extern void viewport_paint_setup();
void lightfx_prepare_light_list()
{
for (uint32 light = 0; light < LightListCurrentCountFront; light++) {
lightlist_entry *entry = &_LightListFront[light];
if (entry->z == 0x7FFF) {
entry->lightIntensity = 0xFF;
continue;
}
LocationXYZ16 coord_3d = {
/* .x = */ entry->x,
/* .y = */ entry->y,
/* .z = */ entry->z
};
LocationXY16 coord_2d = coordinate_3d_to_2d(&coord_3d, _current_view_rotation_front);
entry->x = coord_2d.x;// - (_current_view_x_front);
entry->y = coord_2d.y;// - (_current_view_y_front);
sint32 posOnScreenX = entry->x - _current_view_x_front;
sint32 posOnScreenY = entry->y - _current_view_y_front;
posOnScreenX >>= _current_view_zoom_front;
posOnScreenY >>= _current_view_zoom_front;
if ((posOnScreenX < -128) ||
(posOnScreenY < -128) ||
(posOnScreenX > _pixelInfo.width + 128) ||
(posOnScreenY > _pixelInfo.height + 128)) {
entry->lightType = LIGHTFX_LIGHT_TYPE_NONE;
continue;
}
// entry->x >>= _current_view_zoom_front;
// entry->y >>= _current_view_zoom_front;
uint32 lightIntensityOccluded = 0x0;
sint32 dirVecX = 707;
sint32 dirVecY = 707;
switch (_current_view_rotation_front) {
case 0:
dirVecX = 707;
dirVecY = 707;
break;
case 1:
dirVecX = -707;
dirVecY = 707;
break;
case 2:
dirVecX = -707;
dirVecY = -707;
break;
case 3:
dirVecX = 707;
dirVecY = -707;
break;
default:
dirVecX = 0;
dirVecY = 0;
break;
}
#ifdef LIGHTFX_UNKNOWN_PART_1
sint32 tileOffsetX = 0;
sint32 tileOffsetY = 0;
switch (_current_view_rotation_front) {
case 0:
tileOffsetX = 0;
tileOffsetY = 0;
break;
case 1:
tileOffsetX = 16;
tileOffsetY = 0;
break;
case 2:
tileOffsetX = 32;
tileOffsetY = 32;
break;
case 3:
tileOffsetX = 0;
tileOffsetY = 16;
break;
}
sint32 mapFrontDiv = 1 << _current_view_zoom_front;
static sint16 offsetPattern[26] = { 0, 0, -4, 0, 0, -3, 4, 0, 0, 3,
-2, -1, -1, -1, 2, 1, 1, 1,
-3, -2, -3, 2, 3, -2, 3, 2 };
#endif //LIGHTFX_UNKNOWN_PART_1
if (true) {
sint32 totalSamplePoints = 5;
sint32 startSamplePoint = 1;
// sint32 lastSampleCount = 0;
if ((entry->lightIDqualifier & 0xF) == LIGHTFX_LIGHT_QUALIFIER_MAP) {
startSamplePoint = 0;
totalSamplePoints = 1;
}
for (sint32 pat = startSamplePoint; pat < totalSamplePoints; pat++) {
LocationXY16 mapCoord = { 0 };
rct_tile_element *tileElement = nullptr;
sint32 interactionType = 0;
rct_window *w = window_get_main();
if (w != nullptr) {
// get_map_coordinates_from_pos(entry->x + offsetPattern[pat*2] / mapFrontDiv, entry->y + offsetPattern[pat*2+1] / mapFrontDiv, VIEWPORT_INTERACTION_MASK_NONE, &mapCoord.x, &mapCoord.y, &interactionType, &tileElement, NULL);
#ifdef LIGHTFX_UNKNOWN_PART_1
_unk9AC154 = ~VIEWPORT_INTERACTION_MASK_SPRITE & 0xFFFF;
_viewportDpi1.zoom = _current_view_zoom_front;
_viewportDpi1.x = entry->x + offsetPattern[0 + pat * 2] / mapFrontDiv;
_viewportDpi1.y = entry->y + offsetPattern[1 + pat * 2] / mapFrontDiv;
rct_drawpixelinfo* dpi = &_viewportDpi2;
dpi->x = _viewportDpi1.x;
dpi->y = _viewportDpi1.y;
dpi->zoom_level = _viewportDpi1.zoom;
dpi->height = 1;
dpi->width = 1;
gPaintSession.EndOfPaintStructArray = 0xF1A4CC;
gPaintSession.Unk140E9A8 = dpi;
painter_setup();
viewport_paint_setup();
paint_session_arrange(gPaintSession);
sub_68862C();
// log_warning("[%i, %i]", dpi->x, dpi->y);
mapCoord.x = _interactionMapX + tileOffsetX;
mapCoord.y = _interactionMapY + tileOffsetY;
interactionType = _interactionSpriteType;
tileElement = RCT2_GLOBAL(0x9AC150, rct_tile_element*);
#endif //LIGHTFX_UNKNOWN_PART_1
//RCT2_GLOBAL(0x9AC154, uint16_t) = VIEWPORT_INTERACTION_MASK_NONE;
//RCT2_GLOBAL(0x9AC148, uint8_t) = 0;
//RCT2_GLOBAL(0x9AC138 + 4, int16_t) = screenX;
//RCT2_GLOBAL(0x9AC138 + 6, int16_t) = screenY;
//if (screenX >= 0 && screenX < (sint32)myviewport->width && screenY >= 0 && screenY < (sint32)myviewport->height)
//{
// screenX <<= myviewport->zoom;
// screenY <<= myviewport->zoom;
// screenX += (sint32)myviewport->view_x;
// screenY += (sint32)myviewport->view_y;
// RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_ZOOM, uint16_t) = myviewport->zoom;
// screenX &= (0xFFFF << myviewport->zoom) & 0xFFFF;
// screenY &= (0xFFFF << myviewport->zoom) & 0xFFFF;
// RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, int16_t) = screenX;
// RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_Y, int16_t) = screenY;
// rct_drawpixelinfo* dpi = RCT2_ADDRESS(RCT2_ADDRESS_VIEWPORT_DPI, rct_drawpixelinfo);
// dpi->y = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_Y, int16_t);
// dpi->height = 1;
// dpi->zoom_level = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_ZOOM, uint16_t);
// dpi->x = RCT2_GLOBAL(RCT2_ADDRESS_VIEWPORT_PAINT_X, int16_t);
// dpi->width = 1;
// RCT2_GLOBAL(0xEE7880, uint32_t) = 0xF1A4CC;
// RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*) = dpi;
// painter_setup();
// viewport_paint_setup();
// paint_session_arrange(gPaintSession);
// sub_68862C();
//}
}
sint32 minDist = 0;
sint32 baseHeight = -999;
if (interactionType != VIEWPORT_INTERACTION_ITEM_SPRITE && tileElement) {
baseHeight = tileElement->base_height;
}
minDist = ((baseHeight * 8) - coord_3d.z) / 2;
sint32 deltaX = mapCoord.x - coord_3d.x;
sint32 deltaY = mapCoord.y - coord_3d.y;
sint32 projDot = (dirVecX * deltaX + dirVecY * deltaY) / 1000;
projDot = std::max(minDist, projDot);
if (projDot < 5) {
lightIntensityOccluded += 100;
}
else {
lightIntensityOccluded += std::max(0, 200 - (projDot * 20));
}
// log_warning("light %i [%i, %i, %i], [%i, %i] minDist to %i: %i; projdot: %i", light, coord_3d.x, coord_3d.y, coord_3d.z, mapCoord.x, mapCoord.y, baseHeight, minDist, projDot);
if (pat == 0) {
if (lightIntensityOccluded == 100)
break;
if (_current_view_zoom_front > 2)
break;
totalSamplePoints += 4;
}
else if (pat == 4) {
if (_current_view_zoom_front > 1)
break;
if (lightIntensityOccluded == 0 || lightIntensityOccluded == 500)
break;
// lastSampleCount = lightIntensityOccluded / 500;
// break;
totalSamplePoints += 4;
}
else if (pat == 8) {
break;
// if (_current_view_zoom_front > 0)
// break;
// sint32 newSampleCount = lightIntensityOccluded / 900;
// if (abs(newSampleCount - lastSampleCount) < 10)
// break;
// totalSamplePoints += 4;
}
}
totalSamplePoints -= startSamplePoint;
// lightIntensityOccluded = totalSamplePoints * 100;
// log_warning("sample-count: %i, occlusion: %i", totalSamplePoints, lightIntensityOccluded);
if (lightIntensityOccluded == 0) {
entry->lightType = LIGHTFX_LIGHT_TYPE_NONE;
continue;
}
// log_warning("sample-count: %i, occlusion: %i", totalSamplePoints, lightIntensityOccluded / totalSamplePoints);
entry->lightIntensity = std::min<uint32>(0xFF, (entry->lightIntensity * lightIntensityOccluded) / (totalSamplePoints * 100));
entry->lightIntensity = std::max<uint32>(0x00, entry->lightIntensity - _current_view_zoom_front * 5);
}
if (_current_view_zoom_front > 0) {
if ((entry->lightType & 0x3) < _current_view_zoom_front) {
entry->lightType = LIGHTFX_LIGHT_TYPE_NONE;
continue;
}
entry->lightType -= _current_view_zoom_front;
}
}
}
void lightfx_swap_buffers()
{
void *tmp = _light_rendered_buffer_back;
_light_rendered_buffer_back = _light_rendered_buffer_front;
_light_rendered_buffer_front = tmp;
tmp = _light_rendered_buffer_back;
_light_rendered_buffer_back = _light_rendered_buffer_front;
_light_rendered_buffer_front = tmp;
tmp = _LightListBack;
_LightListBack = _LightListFront;
_LightListFront = (lightlist_entry *)tmp;
LightListCurrentCountFront = LightListCurrentCountBack;
LightListCurrentCountBack = 0x0;
uint32 uTmp = _lightPolution_back;
_lightPolution_back = _lightPolution_front;
_lightPolution_front = uTmp;
_current_view_x_front = _current_view_x_back;
_current_view_y_front = _current_view_y_back;
_current_view_rotation_front = _current_view_rotation_back;
_current_view_zoom_front = _current_view_zoom_back_delay;
_current_view_zoom_back_delay = _current_view_zoom_back;
}
void lightfx_update_viewport_settings()
{
rct_window *mainWindow = window_get_main();
if (mainWindow) {
rct_viewport * viewport = window_get_viewport(mainWindow);
_current_view_x_back = viewport->view_x;
_current_view_y_back = viewport->view_y;
_current_view_rotation_back = get_current_rotation();
_current_view_zoom_back = viewport->zoom;
}
}
void lightfx_render_lights_to_frontbuffer()
{
if (_light_rendered_buffer_front == nullptr) {
return;
}
memset(_light_rendered_buffer_front, 0, _pixelInfo.width * _pixelInfo.height);
_lightPolution_back = 0;
// log_warning("%i lights", LightListCurrentCountFront);
for (uint32 light = 0; light < LightListCurrentCountFront; light++) {
const uint8 *bufReadBase = nullptr;
uint8 *bufWriteBase = (uint8 *)_light_rendered_buffer_front;
uint32 bufReadWidth, bufReadHeight;
sint32 bufWriteX, bufWriteY;
sint32 bufWriteWidth, bufWriteHeight;
uint32 bufReadSkip, bufWriteSkip;
lightlist_entry * entry = &_LightListFront[light];
sint32 inRectCentreX = entry->x;
sint32 inRectCentreY = entry->y;
if (entry->z != 0x7FFF) {
inRectCentreX -= _current_view_x_front;
inRectCentreY -= _current_view_y_front;
inRectCentreX >>= _current_view_zoom_front;
inRectCentreY >>= _current_view_zoom_front;
}
switch (entry->lightType) {
case LIGHTFX_LIGHT_TYPE_LANTERN_0:
bufReadWidth = 32;
bufReadHeight = 32;
bufReadBase = _bakedLightTexture_lantern_0;
break;
case LIGHTFX_LIGHT_TYPE_LANTERN_1:
bufReadWidth = 64;
bufReadHeight = 64;
bufReadBase = _bakedLightTexture_lantern_1;
break;
case LIGHTFX_LIGHT_TYPE_LANTERN_2:
bufReadWidth = 128;
bufReadHeight = 128;
bufReadBase = _bakedLightTexture_lantern_2;
break;
case LIGHTFX_LIGHT_TYPE_LANTERN_3:
bufReadWidth = 256;
bufReadHeight = 256;
bufReadBase = _bakedLightTexture_lantern_3;
break;
case LIGHTFX_LIGHT_TYPE_SPOT_0:
bufReadWidth = 32;
bufReadHeight = 32;
bufReadBase = _bakedLightTexture_spot_0;
break;
case LIGHTFX_LIGHT_TYPE_SPOT_1:
bufReadWidth = 64;
bufReadHeight = 64;
bufReadBase = _bakedLightTexture_spot_1;
break;
case LIGHTFX_LIGHT_TYPE_SPOT_2:
bufReadWidth = 128;
bufReadHeight = 128;
bufReadBase = _bakedLightTexture_spot_2;
break;
case LIGHTFX_LIGHT_TYPE_SPOT_3:
bufReadWidth = 256;
bufReadHeight = 256;
bufReadBase = _bakedLightTexture_spot_3;
break;
default:
continue;
}
bufWriteX = inRectCentreX - bufReadWidth / 2;
bufWriteY = inRectCentreY - bufReadHeight / 2;
bufWriteWidth = bufReadWidth;
bufWriteHeight = bufReadHeight;
if (bufWriteX < 0) {
bufReadBase += -bufWriteX;
bufWriteWidth += bufWriteX;
}
else {
bufWriteBase += bufWriteX;
}
if (bufWriteWidth <= 0)
continue;
if (bufWriteY < 0) {
bufReadBase += -bufWriteY * bufReadWidth;
bufWriteHeight += bufWriteY;
}
else {
bufWriteBase += bufWriteY * _pixelInfo.width;
}
if (bufWriteHeight <= 0)
continue;
sint32 rightEdge = bufWriteX + bufWriteWidth;
sint32 bottomEdge = bufWriteY + bufWriteHeight;
if (rightEdge > _pixelInfo.width) {
bufWriteWidth -= rightEdge - _pixelInfo.width;
}
if (bottomEdge > _pixelInfo.height) {
bufWriteHeight -= bottomEdge - _pixelInfo.height;
}
if (bufWriteWidth <= 0)
continue;
if (bufWriteHeight <= 0)
continue;
_lightPolution_back += (bufWriteWidth * bufWriteHeight) / 256;
bufReadSkip = bufReadWidth - bufWriteWidth;
bufWriteSkip = _pixelInfo.width - bufWriteWidth;
if (entry->lightIntensity == 0xFF) {
for (sint32 y = 0; y < bufWriteHeight; y++) {
for (sint32 x = 0; x < bufWriteWidth; x++) {
*bufWriteBase = std::min(0xFF, *bufWriteBase + *bufReadBase);
bufWriteBase++;
bufReadBase++;
}
bufWriteBase += bufWriteSkip;
bufReadBase += bufReadSkip;
}
}
else {
for (sint32 y = 0; y < bufWriteHeight; y++) {
for (sint32 x = 0; x < bufWriteWidth; x++) {
*bufWriteBase = std::min(0xFF, *bufWriteBase + (((*bufReadBase) * (1 + entry->lightIntensity)) >> 8));
bufWriteBase++;
bufReadBase++;
}
bufWriteBase += bufWriteSkip;
bufReadBase += bufReadSkip;
}
}
}
}
void* lightfx_get_front_buffer()
{
return _light_rendered_buffer_front;
}
const rct_palette * lightfx_get_palette()
{
return &gPalette_light;
}
void lightfx_add_3d_light(uint32 lightID, uint16 lightIDqualifier, sint16 x, sint16 y, uint16 z, uint8 lightType)
{
if (LightListCurrentCountBack == 15999) {
return;
}
// log_warning("%i lights in back", LightListCurrentCountBack);
for (uint32 i = 0; i < LightListCurrentCountBack; i++) {
lightlist_entry *entry = &_LightListBack[i];
if (entry->lightID != lightID)
continue;
if (entry->lightIDqualifier != lightIDqualifier)
continue;
entry->x = x;
entry->y = y;
entry->z = z;
entry->lightType = lightType;
entry->lightIntensity = 0xFF;
entry->lightID = lightID;
entry->lightIDqualifier = lightIDqualifier;
entry->lightLinger = 1;
return;
}
lightlist_entry *entry = &_LightListBack[LightListCurrentCountBack++];
entry->x = x;
entry->y = y;
entry->z = z;
entry->lightType = lightType;
entry->lightIntensity = 0xFF;
entry->lightID = lightID;
entry->lightIDqualifier = lightIDqualifier;
entry->lightLinger = 1;
// log_warning("new 3d light");
}
void lightfx_add_3d_light_magic_from_drawing_tile(LocationXY16 mapPosition, sint16 offsetX, sint16 offsetY, sint16 offsetZ, uint8 lightType)
{
sint16 x = mapPosition.x + offsetX;
sint16 y = mapPosition.y + offsetY;
switch (get_current_rotation()) {
case 0:
x += 16;
y += 16;
break;
case 1:
x += 16;
y += 16;
break;
case 2:
x += 16;
y -= 16;
break;
case 3:
x += 16;
y -= 16;
break;
default:
return;
}
lightfx_add_3d_light((x << 16) | y, (offsetZ << 8) | LIGHTFX_LIGHT_QUALIFIER_MAP, x, y, offsetZ, lightType);
}
uint32 lightfx_get_light_polution()
{
return _lightPolution_front;
}
void lightfx_add_lights_magic_vehicles()
{
uint16 spriteIndex = gSpriteListHead[SPRITE_LIST_TRAIN];
while (spriteIndex != SPRITE_INDEX_NULL) {
rct_vehicle * vehicle = &(get_sprite(spriteIndex)->vehicle);
uint16 vehicleID = spriteIndex;
spriteIndex = vehicle->next;
rct_vehicle *mother_vehicle = vehicle;
if (mother_vehicle->ride_subtype == RIDE_ENTRY_INDEX_NULL) {
continue;
}
for (uint16 q = vehicleID; q != SPRITE_INDEX_NULL; ) {
vehicle = GET_VEHICLE(q);
vehicleID = q;
if (vehicle->next_vehicle_on_train == q)
break;
q = vehicle->next_vehicle_on_train;
sint16 place_x, place_y, place_z;
place_x = vehicle->x;
place_y = vehicle->y;
place_z = vehicle->z;
static constexpr const sint16 offsetLookup[32] = { 10, 10, 9, 8, 7, 6, 4, 2, 0, -2, -4, -6, -7, -8, -9, -10, -10, -10, -9, -8, -7, -6, -4, -2, 0, 2, 4, 6, 7, 8, 9, 10 };
Ride *ride = get_ride(vehicle->ride);
switch (ride->type) {
case RIDE_TYPE_OBSERVATION_TOWER:
lightfx_add_3d_light(vehicleID, 0x0000 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, vehicle->x, vehicle->y + 16, vehicle->z, LIGHTFX_LIGHT_TYPE_SPOT_3);
lightfx_add_3d_light(vehicleID, 0x0100 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, vehicle->x + 16, vehicle->y, vehicle->z, LIGHTFX_LIGHT_TYPE_SPOT_3);
lightfx_add_3d_light(vehicleID, 0x0200 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, vehicle->x - 16, vehicle->y, vehicle->z, LIGHTFX_LIGHT_TYPE_SPOT_3);
lightfx_add_3d_light(vehicleID, 0x0300 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, vehicle->x, vehicle->y - 16, vehicle->z, LIGHTFX_LIGHT_TYPE_SPOT_3);
break;
case RIDE_TYPE_MINE_TRAIN_COASTER:
case RIDE_TYPE_GHOST_TRAIN:
if (vehicle == vehicle_get_head(vehicle)) {
place_x -= offsetLookup[(vehicle->sprite_direction + 0) % 32] * 2;
place_y -= offsetLookup[(vehicle->sprite_direction + 8) % 32] * 2;
lightfx_add_3d_light(vehicleID, 0x0000 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z, LIGHTFX_LIGHT_TYPE_SPOT_3);
}
break;
case RIDE_TYPE_CHAIRLIFT:
lightfx_add_3d_light(vehicleID, 0x0000 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z - 16, LIGHTFX_LIGHT_TYPE_LANTERN_2);
break;
case RIDE_TYPE_BOAT_HIRE:
case RIDE_TYPE_CAR_RIDE:
case RIDE_TYPE_GO_KARTS:
case RIDE_TYPE_DODGEMS:
case RIDE_TYPE_MINI_HELICOPTERS:
case RIDE_TYPE_MONORAIL_CYCLES:
case RIDE_TYPE_SUBMARINE_RIDE:
case RIDE_TYPE_SPLASH_BOATS:
case RIDE_TYPE_WATER_COASTER: {
rct_vehicle *vehicle_draw = vehicle_get_head(vehicle);
if (vehicle_draw->next_vehicle_on_train != SPRITE_INDEX_NULL) {
vehicle_draw = GET_VEHICLE(vehicle_draw->next_vehicle_on_train);
}
place_x = vehicle_draw->x;
place_y = vehicle_draw->y;
place_z = vehicle_draw->z;
place_x -= offsetLookup[(vehicle_draw->sprite_direction + 0) % 32];
place_y -= offsetLookup[(vehicle_draw->sprite_direction + 8) % 32];
lightfx_add_3d_light(vehicleID, 0x0000 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z, LIGHTFX_LIGHT_TYPE_SPOT_2);
place_x -= offsetLookup[(vehicle_draw->sprite_direction + 0) % 32];
place_y -= offsetLookup[(vehicle_draw->sprite_direction + 8) % 32];
lightfx_add_3d_light(vehicleID, 0x0100 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z, LIGHTFX_LIGHT_TYPE_SPOT_2);
break;
}
case RIDE_TYPE_MONORAIL:
lightfx_add_3d_light(vehicleID, 0x0000 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, vehicle->x, vehicle->y, vehicle->z + 12, LIGHTFX_LIGHT_TYPE_SPOT_2);
if (vehicle == vehicle_get_head(vehicle)) {
place_x -= offsetLookup[(vehicle->sprite_direction + 0) % 32] * 2;
place_y -= offsetLookup[(vehicle->sprite_direction + 8) % 32] * 2;
lightfx_add_3d_light(vehicleID, 0x0100 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z + 10, LIGHTFX_LIGHT_TYPE_LANTERN_3);
place_x -= offsetLookup[(vehicle->sprite_direction + 0) % 32] * 3;
place_y -= offsetLookup[(vehicle->sprite_direction + 8) % 32] * 3;
lightfx_add_3d_light(vehicleID, 0x0200 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z + 2, LIGHTFX_LIGHT_TYPE_LANTERN_3);
}
if (vehicle == vehicle_get_tail(vehicle)) {
place_x += offsetLookup[(vehicle->sprite_direction + 0) % 32] * 2;
place_y += offsetLookup[(vehicle->sprite_direction + 8) % 32] * 2;
lightfx_add_3d_light(vehicleID, 0x0300 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z + 10, LIGHTFX_LIGHT_TYPE_LANTERN_3);
place_x += offsetLookup[(vehicle->sprite_direction + 0) % 32] * 2;
place_y += offsetLookup[(vehicle->sprite_direction + 8) % 32] * 2;
lightfx_add_3d_light(vehicleID, 0x0400 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z + 2, LIGHTFX_LIGHT_TYPE_LANTERN_3);
}
break;
case RIDE_TYPE_MINIATURE_RAILWAY:
if (vehicle == vehicle_get_head(vehicle)) {
place_x -= offsetLookup[(vehicle->sprite_direction + 0) % 32] * 2;
place_y -= offsetLookup[(vehicle->sprite_direction + 8) % 32] * 2;
lightfx_add_3d_light(vehicleID, 0x0100 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z + 10, LIGHTFX_LIGHT_TYPE_LANTERN_3);
place_x -= offsetLookup[(vehicle->sprite_direction + 0) % 32] * 2;
place_y -= offsetLookup[(vehicle->sprite_direction + 8) % 32] * 2;
lightfx_add_3d_light(vehicleID, 0x0200 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z + 2, LIGHTFX_LIGHT_TYPE_LANTERN_3);
}
else {
lightfx_add_3d_light(vehicleID, 0x0000 | LIGHTFX_LIGHT_QUALIFIER_SPRITE, place_x, place_y, place_z + 10, LIGHTFX_LIGHT_TYPE_LANTERN_3);
}
break;
default:
break;
};
}
}
}
void lightfx_apply_palette_filter(uint8 i, uint8 *r, uint8 *g, uint8 *b)
{
float night = (float)(pow(gDayNightCycle, 1.5));
float natLightR = 1.0f;
float natLightG = 1.0f;
float natLightB = 1.0f;
float elecMultR = 1.0f;
float elecMultG = 0.95f;
float elecMultB = 0.45f;
static float wetness = 0.0f;
static float fogginess = 0.0f;
static float lightPolution = 0.0f;
float sunLight = std::max(0.0f, std::min(1.0f, 2.0f - night * 3.0f));
// Night version
natLightR = flerp(natLightR * 4.0f, 0.635f, (float)(std::pow(night, 0.035f + sunLight * 10.50f)));
natLightG = flerp(natLightG * 4.0f, 0.650f, (float)(std::pow(night, 0.100f + sunLight * 5.50f)));
natLightB = flerp(natLightB * 4.0f, 0.850f, (float)(std::pow(night, 0.200f + sunLight * 1.5f)));
float overExpose = 0.0f;
float lightAvg = (natLightR + natLightG + natLightB) / 3.0f;
#ifdef LIGHTFX_UNKNOWN_PART_2
float lightMax = (natLightR + natLightG + natLightB) / 3.0f;
#endif // LIGHTFX_UNKNOWN_PART_2
// overExpose += ((lightMax - lightAvg) / lightMax) * 0.01f;
if (gClimateCurrent.Temperature > 20) {
float offset = ((float)(gClimateCurrent.Temperature - 20)) * 0.04f;
offset *= 1.0f - night;
lightAvg /= 1.0f + offset;
// overExpose += offset * 0.1f;
}
#ifdef LIGHTFX_UNKNOWN_PART_2
lightAvg += (lightMax - lightAvg) * 0.6f;
#endif // LIGHTFX_UNKNOWN_PART_2
if (lightAvg > 1.0f) {
natLightR /= lightAvg;
natLightG /= lightAvg;
natLightB /= lightAvg;
}
natLightR *= 1.0f + overExpose;
natLightG *= 1.0f + overExpose;
natLightB *= 1.0f + overExpose;
overExpose *= 255.0f;
float targetFogginess = (float)(gClimateCurrent.RainLevel) / 8.0f;
targetFogginess += (night * night) * 0.15f;
if (gClimateCurrent.Temperature < 10) {
targetFogginess += ((float)(10 - gClimateCurrent.Temperature)) * 0.01f;
}
fogginess -= (fogginess - targetFogginess) * 0.00001f;
wetness *= 0.999995f;
wetness += fogginess * 0.001f;
wetness = std::min(wetness, 1.0f);
float boost = 1.0f;
float envFog = fogginess;
float lightFog = envFog;
float addLightNatR = 0.0f;
float addLightNatG = 0.0f;
float addLightNatB = 0.0f;
float reduceColourNat = 1.0f;
float reduceColourLit = 1.0f;
reduceColourLit *= night / (float)std::pow(std::max(1.01f, 0.4f + lightAvg), 2.0);
float targetLightPollution = reduceColourLit * std::max(0.0f, 0.0f + 0.000001f * (float)lightfx_get_light_polution());
lightPolution -= (lightPolution - targetLightPollution) * 0.001f;
// lightPollution /= 1.0f + fogginess * 1.0f;
natLightR /= 1.0f + lightPolution * 20.0f;
natLightG /= 1.0f + lightPolution * 20.0f;
natLightB /= 1.0f + lightPolution * 20.0f;
natLightR += elecMultR * 0.6f * lightPolution;
natLightG += elecMultG * 0.6f * lightPolution;
natLightB += elecMultB * 0.6f * lightPolution;
natLightR /= 1.0f + lightPolution;
natLightG /= 1.0f + lightPolution;
natLightB /= 1.0f + lightPolution;
reduceColourLit += (float)(gClimateCurrent.RainLevel) / 2.0f;
reduceColourNat /= 1.0f + fogginess;
reduceColourLit /= 1.0f + fogginess;
lightFog *= reduceColourLit;
reduceColourNat *= 1.0f - envFog;
reduceColourLit *= 1.0f - lightFog;
float fogR = 35.5f * natLightR * 1.3f;
float fogG = 45.0f * natLightG * 1.3f;
float fogB = 50.0f * natLightB * 1.3f;
lightFog *= 10.0f;
float wetnessBoost = 1.0f;//1.0f + wetness * wetness * 0.1f;
if (night >= 0 && gClimateLightningFlash != 1) {
*r = lerp(*r, soft_light(*r, 8), night);
*g = lerp(*g, soft_light(*g, 8), night);
*b = lerp(*b, soft_light(*b, 128), night);
// if (i == 32)
// boost = 300000.0f;
if ((i % 32) == 0)
boost = 1.01f * wetnessBoost;
else if ((i % 16) < 7)
boost = 1.001f * wetnessBoost;
if (i > 230 && i < 232)
boost = ((float)(*b)) / 64.0f;
if (false) {
// This experiment shifts the colour of pixels as-if they are wet, but it is not a pretty solution at all
if ((i % 16)) {
float iVal = ((float)((i + 12) % 16)) / 16.0f;
float eff = (wetness * ((float)std::pow(iVal, 1.5) * 0.85f));
reduceColourNat *= 1.0f - eff;
addLightNatR += fogR * eff * 3.95f;
addLightNatG += fogR * eff * 3.95f;
addLightNatB += fogR * eff * 3.95f;
}
}
addLightNatR *= 1.0f - envFog;
addLightNatG *= 1.0f - envFog;
addLightNatB *= 1.0f - envFog;
*r = (uint8)(std::min(255.0f, std::max(0.0f, (-overExpose + (float)(*r) * reduceColourNat * natLightR + envFog * fogR + addLightNatR))));
*g = (uint8)(std::min(255.0f, std::max(0.0f, (-overExpose + (float)(*g) * reduceColourNat * natLightG + envFog * fogG + addLightNatG))));
*b = (uint8)(std::min(255.0f, std::max(0.0f, (-overExpose + (float)(*b) * reduceColourNat * natLightB + envFog * fogB + addLightNatB))));
rct_palette_entry * dstEntry = &gPalette_light.entries[i];
dstEntry->red = (uint8)(std::min<float>(0xFF, ((float)(*r) * reduceColourLit * boost + lightFog) * elecMultR));
dstEntry->green = (uint8)(std::min<float>(0xFF, ((float)(*g) * reduceColourLit * boost + lightFog) * elecMultG));
dstEntry->blue = (uint8)(std::min<float>(0xFF, ((float)(*b) * reduceColourLit * boost + lightFog) * elecMultB));
}
}
static uint8 mix_light(uint32 a, uint32 b, uint32 intensity)
{
intensity = intensity * 6;
uint32 bMul = (b * intensity) >> 8;
uint32 ab = a + bMul;
uint8 result = std::min<uint32>(255, ab);
return result;
}
void lightfx_render_to_texture(
void * dstPixels,
uint32 dstPitch,
uint8 * bits,
uint32 width,
uint32 height,
const uint32 * palette,
const uint32 * lightPalette)
{
lightfx_update_viewport_settings();
lightfx_swap_buffers();
lightfx_prepare_light_list();
lightfx_render_lights_to_frontbuffer();
uint8 * lightBits = (uint8 *)lightfx_get_front_buffer();
if (lightBits == nullptr) {
return;
}
for (uint32 y = 0; y < height; y++) {
uintptr_t dstOffset = (uintptr_t)(y * dstPitch);
uint32 * dst = (uint32 *)((uintptr_t)dstPixels + dstOffset);
for (uint32 x = 0; x < width; x++) {
uint8 * src = &bits[y * width + x];
uint32 darkColour = palette[*src];
uint32 lightColour = lightPalette[*src];
uint8 lightIntensity = lightBits[y * width + x];
uint32 colour = 0;
if (lightIntensity == 0) {
colour = darkColour;
} else {
colour |= mix_light((darkColour >> 0) & 0xFF, (lightColour >> 0) & 0xFF, lightIntensity);
colour |= mix_light((darkColour >> 8) & 0xFF, (lightColour >> 8) & 0xFF, lightIntensity) << 8;
colour |= mix_light((darkColour >> 16) & 0xFF, (lightColour >> 16) & 0xFF, lightIntensity) << 16;
colour |= mix_light((darkColour >> 24) & 0xFF, (lightColour >> 24) & 0xFF, lightIntensity) << 24;
}
*dst++ = colour;
}
}
}
#endif // __ENABLE_LIGHTFX__