diff --git a/src/Lighting.c b/src/Lighting.c index 2e15c2dd3..bd1eab4ae 100644 --- a/src/Lighting.c +++ b/src/Lighting.c @@ -7,6 +7,11 @@ #include "Logger.h" #include "Event.h" #include "Game.h" +#include "String.h" +#include "Chat.h" +#include "ExtMath.h" +#include "Options.h" +cc_bool Lighting_Modern; struct _Lighting Lighting; #define Lighting_Pack(x, z) ((x) + World.Width * (z)) @@ -99,6 +104,156 @@ static void ClassicLighting_Refresh(void) { } +/*########################################################################################################################* +*----------------------------------------------------Modern lighting------------------------------------------------------* +*#########################################################################################################################*/ +/* TODO: Evil goodly hack, move chunk variables to World.h */ +int ModernLighting_ChunkCount; +int ModernLighting_ChunksX; +int ModernLighting_ChunksY; +int ModernLighting_ChunksZ; + +/* A 16x16 palette of sun and block light colors. */ +/* It is indexed by a byte where the leftmost 4 bits represent sunlight level and the rightmost 4 bits represent blocklight level */ +/* E.G. modernLighting_palette[0b_0010_0001] will give us the color for sun level 2 and block level 1 (lowest level is 0) */ +static PackedCol modernLighting_palette[MODERN_LIGHTING_LEVELS * MODERN_LIGHTING_LEVELS]; + +typedef cc_uint8* LightingChunk; +static cc_uint8* chunkLightingDataFlags; +#define CHUNK_UNCALCULATED 0 +#define CHUNK_CALCULATED 1 +#define CHUNK_ALL_DARK 2 +static LightingChunk* chunkLightingData; +static cc_uint8 allDarkChunkLightingData[CHUNK_SIZE_3]; + +#define Modern_MakePaletteIndex(sun, block) ((sun << MODERN_LIGHTING_SUN_SHIFT) | block) + +/* Fill in modernLighting_palette with values based on the current environment colors in lieu of recieving a palette from the server */ +static void ModernLighting_InitPalette(void) { + PackedCol darkestShadow, defaultBlockLight, blockColor, sunColor, invertedBlockColor, invertedSunColor, finalColor; + int sunLevel, blockLevel; + float blockLerp; + cc_uint8 R, G, B; + + defaultBlockLight = PackedCol_Make(255, 238, 204, 255); /* A very mildly orange tinted light color */ + darkestShadow = PackedCol_Lerp(Env.ShadowCol, 0, 0.75f); /* Use a darkened version of shadow color as the darkest color in sun ramp */ + + for (sunLevel = 0; sunLevel < MODERN_LIGHTING_LEVELS; sunLevel++) { + for (blockLevel = 0; blockLevel < MODERN_LIGHTING_LEVELS; blockLevel++) { + /* We want the brightest light level to be the sun env color, with all other 15 levels being interpolation */ + /* between shadow color and darkest shadow color */ + if (sunLevel == MODERN_LIGHTING_LEVELS-1) { + sunColor = Env.SunCol; + } + else { + sunColor = PackedCol_Lerp(darkestShadow, Env.ShadowCol, sunLevel / (float)(MODERN_LIGHTING_LEVELS - 2) ); + } + blockLerp = blockLevel / (float)(MODERN_LIGHTING_LEVELS-1); + blockLerp *= blockLerp; + blockLerp *= (MATH_PI / 2); + blockLerp = Math_Cos(blockLerp); + blockColor = PackedCol_Lerp(0, defaultBlockLight, 1 - blockLerp); + + /* With Screen blend mode, the values of the pixels in the two layers are inverted, multiplied, and then inverted again. */ + R = 255 - PackedCol_R(sunColor); + G = 255 - PackedCol_G(sunColor); + B = 255 - PackedCol_B(sunColor); + invertedSunColor = PackedCol_Make(R, G, B, 255); + R = 255 - PackedCol_R(blockColor); + G = 255 - PackedCol_G(blockColor); + B = 255 - PackedCol_B(blockColor); + invertedBlockColor = PackedCol_Make(R, G, B, 255); + + finalColor = PackedCol_Tint(invertedSunColor, invertedBlockColor); + R = 255 - PackedCol_R(finalColor); + G = 255 - PackedCol_G(finalColor); + B = 255 - PackedCol_B(finalColor); + modernLighting_palette[Modern_MakePaletteIndex(sunLevel, blockLevel)] = PackedCol_Make(R, G, B, 255); + } + } +} + +static void ModernLighting_AllocState(void) { + ModernLighting_InitPalette(); + ModernLighting_ChunksX = (World.Width + CHUNK_MAX) >> CHUNK_SHIFT; + ModernLighting_ChunksY = (World.Height + CHUNK_MAX) >> CHUNK_SHIFT; + ModernLighting_ChunksZ = (World.Length + CHUNK_MAX) >> CHUNK_SHIFT; + ModernLighting_ChunkCount = ModernLighting_ChunksX * ModernLighting_ChunksY * ModernLighting_ChunksZ; + + chunkLightingDataFlags = (cc_uint8*)Mem_TryAllocCleared(ModernLighting_ChunkCount, sizeof(cc_uint8)); + chunkLightingData = (LightingChunk*)Mem_TryAllocCleared(ModernLighting_ChunkCount, sizeof(LightingChunk)); +} +static void ModernLighting_FreeState(void) { + int i; + /* This function can be called multiple times without calling ModernLighting_AllocState, so... */ + if (chunkLightingDataFlags == NULL) { return; } + + for (i = 0; i < ModernLighting_ChunkCount; i++) { + if (chunkLightingDataFlags[i] > CHUNK_CALCULATED || chunkLightingDataFlags[i] == CHUNK_UNCALCULATED) { continue; } + Mem_Free(chunkLightingData[i]); + } + Mem_Free(chunkLightingDataFlags); + Mem_Free(chunkLightingData); + chunkLightingDataFlags = NULL; + chunkLightingData = NULL; +} + +/* Gives the index into array of chunk pointers based on chunk x y z */ +#define ChunkIndex(x, y, z) (((y) * ModernLighting_ChunksZ + (z)) * ModernLighting_ChunksX + (x)) +/* Gives the index into array of chunk data */ +#define ChunkDataIndex(x, y, z) (((y) * CHUNK_SIZE + (z)) * CHUNK_SIZE + (x)) + +static void CalculateChunkLighting(int chunkIndex) { + int i; + chunkLightingData[chunkIndex] = (cc_uint8*)Mem_TryAlloc(CHUNK_SIZE_3, sizeof(cc_uint8)); + + for (i = 0; i < CHUNK_SIZE_3; i++) { + chunkLightingData[chunkIndex][i] = i % 256; + } +} +static void ModernLighting_LightHint(void) { } /* ??? */ +static void ModernLighting_OnBlockChanged(void) { } +static void ModernLighting_Refresh(void) { + ModernLighting_InitPalette(); + /* Set all the chunk lighting data flags to CHUNK_UNCALCULATED? */ +} +static cc_bool ModernLighting_IsLit(int x, int y, int z) { return true; } +static cc_bool ModernLighting_IsLit_Fast(int x, int y, int z) { return true; } + +static PackedCol ModernLighting_Color(int x, int y, int z) { + if (!World_Contains(x, y, z)) return Env.SunCol; + //cc_uint8 thing = y % MODERN_LIGHTING_LEVELS; + //cc_uint8 thing2 = z % MODERN_LIGHTING_LEVELS; + //return y * z * x; + int cx, cy, cz; + int dx, dy, dz; + int chunkIndex; + int chunkDataIndex; + + cx = x / CHUNK_SIZE; + cy = y / CHUNK_SIZE; + cz = z / CHUNK_SIZE; + chunkIndex = ChunkIndex(cx, cy, cz); + + //cc_string msg; char msgBuffer[STRING_SIZE * 2]; String_InitArray(msg, msgBuffer); + //String_Format3(&msg, "Hi x %i, y %i, z %i", &cx, &cy, &cz); + //Logger_Log(&msg); + + if (chunkLightingDataFlags[chunkIndex] == CHUNK_UNCALCULATED) { + CalculateChunkLighting(chunkIndex); + chunkLightingDataFlags[chunkIndex] = CHUNK_CALCULATED; + } + /* Get coordinates into the chunk data*/ + dx = x % CHUNK_SIZE; + dy = y % CHUNK_SIZE; + dz = z % CHUNK_SIZE; + chunkDataIndex = ChunkDataIndex(dx, dy, dz); + cc_uint8 lightData = chunkLightingData[chunkIndex] + [chunkDataIndex]; + + return modernLighting_palette[lightData]; +} + /*########################################################################################################################* *----------------------------------------------------Lighting update------------------------------------------------------* *#########################################################################################################################*/ @@ -392,13 +547,46 @@ static void ClassicLighting_SetActive(void) { Lighting.AllocState = ClassicLighting_AllocState; Lighting.LightHint = ClassicLighting_LightHint; } +static void ModernLighting_SetActive(void) { + Lighting.OnBlockChanged = ModernLighting_OnBlockChanged; + Lighting.Refresh = ModernLighting_Refresh; + Lighting.IsLit = ModernLighting_IsLit; + Lighting.Color = ModernLighting_Color; + Lighting.Color_XSide = ModernLighting_Color; + Lighting.IsLit_Fast = ModernLighting_IsLit_Fast; + Lighting.Color_Sprite_Fast = ModernLighting_Color; + Lighting.Color_YMax_Fast = ModernLighting_Color; + Lighting.Color_YMin_Fast = ModernLighting_Color; + Lighting.Color_XSide_Fast = ModernLighting_Color; + Lighting.Color_ZSide_Fast = ModernLighting_Color; + + Lighting.FreeState = ModernLighting_FreeState; + Lighting.AllocState = ModernLighting_AllocState; + Lighting.LightHint = ModernLighting_LightHint; +} +static void Lighting_ApplyActive(void) { + if (Lighting_Modern) { + ModernLighting_SetActive(); + } + else { + ClassicLighting_SetActive(); + } +} +void Lighting_SwitchActive(void) { + Lighting.FreeState(); + Lighting_ApplyActive(); + Lighting.AllocState(); +} /*########################################################################################################################* *---------------------------------------------------Lighting component----------------------------------------------------* *#########################################################################################################################*/ -static void OnInit(void) { ClassicLighting_SetActive(); } +static void OnInit(void) { + if (!Game_ClassicMode) Lighting_Modern = Options_GetBool(OPT_MODERN_LIGHTING, false); + Lighting_ApplyActive(); +} static void OnReset(void) { Lighting.FreeState(); } static void OnNewMapLoaded(void) { Lighting.AllocState(); } diff --git a/src/Lighting.h b/src/Lighting.h index bbe823641..1c6c5e4ef 100644 --- a/src/Lighting.h +++ b/src/Lighting.h @@ -10,6 +10,12 @@ Copyright 2014-2022 ClassiCube | Licensed under BSD-3 */ struct IGameComponent; extern struct IGameComponent Lighting_Component; +/* Whether MC-style 16-level lighting should be used. */ +extern cc_bool Lighting_Modern; +/* How many unique "levels" of light there are when modern lighting is used. */ +#define MODERN_LIGHTING_LEVELS 16 +/* How many bits to shift sunlight level to the left when storing it in a byte along with blocklight level*/ +#define MODERN_LIGHTING_SUN_SHIFT 4 CC_VAR extern struct _Lighting { /* Releases/Frees the per-level lighting state */ @@ -49,4 +55,6 @@ CC_VAR extern struct _Lighting { PackedCol (*Color_XSide_Fast)(int x, int y, int z); PackedCol (*Color_ZSide_Fast)(int x, int y, int z); } Lighting; + +void Lighting_SwitchActive(void); #endif diff --git a/src/Menus.c b/src/Menus.c index abdd53b72..43f4d09d4 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -27,6 +27,7 @@ #include "Deflate.h" #include "Stream.h" #include "Builder.h" +#include "Lighting.h" #include "Logger.h" #include "Options.h" #include "Input.h" @@ -2739,6 +2740,13 @@ static void GraphicsOptionsScreen_SetSmooth(const cc_string* v) { Builder_ApplyActive(); MapRenderer_Refresh(); } +static void GraphicsOptionsScreen_GetModernLighting(cc_string* v) { Menu_GetBool(v, Lighting_Modern); } +static void GraphicsOptionsScreen_SetModernLighting(const cc_string* v) { + Lighting_Modern = Menu_SetBool(v, OPT_MODERN_LIGHTING); + Lighting_SwitchActive(); + Builder_ApplyActive(); + MapRenderer_Refresh(); +} static void GraphicsOptionsScreen_GetCamera(cc_string* v) { Menu_GetBool(v, Camera.Smooth); } static void GraphicsOptionsScreen_SetCamera(const cc_string* v) { Camera.Smooth = Menu_SetBool(v, OPT_CAMERA_SMOOTH); } @@ -2767,34 +2775,37 @@ static void GraphicsOptionsScreen_SetCameraMass(const cc_string* c) { Options_Set(OPT_CAMERA_MASS, c); } +#define GraphicsOptionsButtonCount 9 static void GraphicsOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { - static const struct MenuOptionDesc buttons[8] = { - { -1, -100, "Camera Mass", MenuOptionsScreen_Input, + static const struct MenuOptionDesc buttons[GraphicsOptionsButtonCount] = { + { -1, -150, "Camera Mass", MenuOptionsScreen_Input, GraphicsOptionsScreen_GetCameraMass, GraphicsOptionsScreen_SetCameraMass }, - { -1, -50, "FPS mode", MenuOptionsScreen_Enum, + { -1, -100, "FPS mode", MenuOptionsScreen_Enum, MenuOptionsScreen_GetFPS, MenuOptionsScreen_SetFPS }, - { -1, 0, "View distance", MenuOptionsScreen_Input, + { -1, -50, "View distance", MenuOptionsScreen_Input, GraphicsOptionsScreen_GetViewDist, GraphicsOptionsScreen_SetViewDist }, - { -1, 50, "Advanced lighting", MenuOptionsScreen_Bool, + { -1, 0, "Smooth lighting", MenuOptionsScreen_Bool, GraphicsOptionsScreen_GetSmooth, GraphicsOptionsScreen_SetSmooth }, + { -1, 50, "Modern lighting", MenuOptionsScreen_Bool, + GraphicsOptionsScreen_GetModernLighting, GraphicsOptionsScreen_SetModernLighting }, - { 1, -100, "Smooth camera", MenuOptionsScreen_Bool, + { 1, -150, "Smooth camera", MenuOptionsScreen_Bool, GraphicsOptionsScreen_GetCamera, GraphicsOptionsScreen_SetCamera }, - { 1, -50, "Names", MenuOptionsScreen_Enum, + { 1, -100, "Names", MenuOptionsScreen_Enum, GraphicsOptionsScreen_GetNames, GraphicsOptionsScreen_SetNames }, - { 1, 0, "Shadows", MenuOptionsScreen_Enum, + { 1, -50, "Shadows", MenuOptionsScreen_Enum, GraphicsOptionsScreen_GetShadows, GraphicsOptionsScreen_SetShadows }, - { 1, 50, "Mipmaps", MenuOptionsScreen_Bool, + { 1, 0, "Mipmaps", MenuOptionsScreen_Bool, GraphicsOptionsScreen_GetMipmaps, GraphicsOptionsScreen_SetMipmaps } }; - s->numCore = 8; - s->maxVertices += 8 * BUTTONWIDGET_MAX; + s->numCore = GraphicsOptionsButtonCount; + s->maxVertices += GraphicsOptionsButtonCount * BUTTONWIDGET_MAX; MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); } void GraphicsOptionsScreen_Show(void) { - static struct MenuInputDesc descs[8]; + static struct MenuInputDesc descs[GraphicsOptionsButtonCount]; static const char* extDescs[Array_Elems(descs)]; extDescs[0] = "&eChange the smoothness of the smooth camera."; @@ -2804,13 +2815,14 @@ void GraphicsOptionsScreen_Show(void) { "&eNoLimit: &fRenders as many frames as possible each second.\n" \ "&cNoLimit is pointless - it wastefully renders frames that you don't even see!"; extDescs[3] = "&cNote: &eSmooth lighting is still experimental and can heavily reduce performance."; - extDescs[5] = \ + extDescs[4] = "&cNote: &eModern lighting will reduce performance and increase memory usage."; + extDescs[6] = \ "&eNone: &fNo names of players are drawn.\n" \ "&eHovered: &fName of the targeted player is drawn see-through.\n" \ "&eAll: &fNames of all other players are drawn normally.\n" \ "&eAllHovered: &fAll names of players are drawn see-through.\n" \ "&eAllUnscaled: &fAll names of players are drawn see-through without scaling."; - extDescs[6] = \ + extDescs[7] = \ "&eNone: &fNo entity shadows are drawn.\n" \ "&eSnapToBlock: &fA square shadow is shown on block you are directly above.\n" \ "&eCircle: &fA circular shadow is shown across the blocks you are above.\n" \ diff --git a/src/Options.h b/src/Options.h index 51a57e815..0c0d55f01 100644 --- a/src/Options.h +++ b/src/Options.h @@ -23,6 +23,7 @@ Copyright 2014-2022 ClassiCube | Licensed under BSD-3 #define OPT_ENTITY_SHADOW "entityshadow" #define OPT_RENDER_TYPE "normal" #define OPT_SMOOTH_LIGHTING "gfx-smoothlighting" +#define OPT_MODERN_LIGHTING "gfx-modernlighting" #define OPT_MIPMAPS "gfx-mipmaps" #define OPT_CHAT_LOGGING "chat-logging" #define OPT_WINDOW_WIDTH "window-width"