瀏覽代碼

'cache' module

Bigfoot71 1 月之前
父節點
當前提交
b244afea2f
共有 48 個文件被更改,包括 724 次插入1973 次删除
  1. 1 1
      CMakeLists.txt
  2. 6 6
      examples/animation.c
  3. 2 1
      examples/basic.c
  4. 3 1
      examples/billboards.c
  5. 11 11
      examples/bloom.c
  6. 1 1
      examples/directional.c
  7. 16 12
      examples/dof.c
  8. 11 11
      examples/emission.c
  9. 3 3
      examples/particles.c
  10. 12 11
      examples/pbr_car.c
  11. 4 4
      examples/pbr_musket.c
  12. 1 1
      examples/skybox.c
  13. 16 16
      examples/sponza.c
  14. 1 1
      examples/transparency.c
  15. 0 1
      include/r3d/r3d.h
  16. 1 0
      include/r3d/r3d_animation.h
  17. 9 9
      include/r3d/r3d_core.h
  18. 1 47
      include/r3d/r3d_culling.h
  19. 1 1
      include/r3d/r3d_curves.h
  20. 1 0
      include/r3d/r3d_decal.h
  21. 1 1
      include/r3d/r3d_draw.h
  22. 247 880
      include/r3d/r3d_environment.h
  23. 1 1
      include/r3d/r3d_lighting.h
  24. 1 1
      include/r3d/r3d_material.h
  25. 1 1
      include/r3d/r3d_mesh.h
  26. 1 1
      include/r3d/r3d_mesh_data.h
  27. 1 0
      include/r3d/r3d_model.h
  28. 1 1
      include/r3d/r3d_particles.h
  29. 8 0
      include/r3d/r3d_platform.h
  30. 1 1
      include/r3d/r3d_skeleton.h
  31. 1 1
      include/r3d/r3d_skybox.h
  32. 1 1
      include/r3d/r3d_utils.h
  33. 9 7
      shaders/deferred/ambient.frag
  34. 7 7
      shaders/scene/forward.frag
  35. 2 2
      shaders/scene/skybox.frag
  36. 44 0
      src/modules/r3d_cache.c
  37. 92 0
      src/modules/r3d_cache.h
  38. 8 7
      src/modules/r3d_shader.c
  39. 8 7
      src/modules/r3d_shader.h
  40. 16 101
      src/r3d_core.c
  41. 5 35
      src/r3d_culling.c
  42. 151 138
      src/r3d_draw.c
  43. 5 446
      src/r3d_environment.c
  44. 2 2
      src/r3d_model.c
  45. 4 4
      src/r3d_skybox.c
  46. 0 45
      src/r3d_state.c
  47. 0 140
      src/r3d_state.h
  48. 5 5
      src/r3d_utils.c

+ 1 - 1
CMakeLists.txt

@@ -200,6 +200,7 @@ add_library(${PROJECT_NAME}
     "${R3D_ROOT_PATH}/src/modules/r3d_target.c"
     "${R3D_ROOT_PATH}/src/modules/r3d_shader.c"
     "${R3D_ROOT_PATH}/src/modules/r3d_light.c"
+    "${R3D_ROOT_PATH}/src/modules/r3d_cache.c"
     "${R3D_ROOT_PATH}/src/modules/r3d_draw.c"
     "${R3D_ROOT_PATH}/src/r3d_animation.c"
     "${R3D_ROOT_PATH}/src/r3d_core.c"
@@ -215,7 +216,6 @@ add_library(${PROJECT_NAME}
     "${R3D_ROOT_PATH}/src/r3d_particles.c"
     "${R3D_ROOT_PATH}/src/r3d_skeleton.c"
     "${R3D_ROOT_PATH}/src/r3d_skybox.c"
-    "${R3D_ROOT_PATH}/src/r3d_state.c"
     "${R3D_ROOT_PATH}/src/r3d_utils.c"
 )
 

+ 6 - 6
examples/animation.c

@@ -27,15 +27,15 @@ const char* Init(void)
 
     /* --- Enable post-processing effects --- */
 
-    R3D_SetSSAO(true);
-    R3D_SetBloomIntensity(0.03f);
-    R3D_SetBloomMode(R3D_BLOOM_ADDITIVE);
-    R3D_SetTonemapMode(R3D_TONEMAP_ACES);
+    R3D_ENVIRONMENT_SET(ssao.enabled, true);
+    R3D_ENVIRONMENT_SET(bloom.intensity, 0.03f);
+    R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_ADDITIVE);
+    R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_ACES);
 
     /* --- Set background and ambient lighting colors --- */
 
-    R3D_SetBackgroundColor(BLACK);
-    R3D_SetAmbientColor((Color) { 7, 7, 7, 255 });
+    R3D_ENVIRONMENT_SET(background.color, BLACK);
+    R3D_ENVIRONMENT_SET(ambient.color, (Color) { 7, 7, 7, 255 });
 
     /* --- Generate a plane to serve as the ground and setup its material --- */
 

+ 2 - 1
examples/basic.c

@@ -1,4 +1,5 @@
 #include "./common.h"
+#include "r3d/r3d_environment.h"
 
 /* === Resources === */
 
@@ -24,7 +25,7 @@ const char* Init(void)
 
     /* --- Setup the scene lighting --- */
 
-    R3D_SetAmbientColor((Color) { 10, 10, 10, 255 });
+    R3D_ENVIRONMENT_SET(ambient.color, (Color) { 10, 10, 10, 255 });
 
     R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT);
     {

+ 3 - 1
examples/billboards.c

@@ -1,6 +1,8 @@
 #include "./common.h"
+#include "r3d/r3d_environment.h"
 #include "r3d/r3d_lighting.h"
 #include "r3d/r3d_material.h"
+#include "raylib.h"
 #include "raymath.h"
 
 /* === Resources === */
@@ -25,7 +27,7 @@ const char* Init(void)
 
     /* --- Set the background color --- */
 
-    R3D_SetBackgroundColor(SKYBLUE);
+    R3D_ENVIRONMENT_SET(background.color, SKYBLUE);
 
     /* --- Setup the ground mesh / material --- */
 

+ 11 - 11
examples/bloom.c

@@ -11,9 +11,9 @@ static float hueCube = 0.0f;
 
 /* === Local Functions === */
 
-static const char* getBloomModeName(R3D_Bloom mode)
+static const char* GetBloomModeName(void)
 {
-    switch (R3D_GetBloomMode()) {
+    switch (R3D_ENVIRONMENT_GET(bloom.mode)) {
         case R3D_BLOOM_DISABLED:
             return "Disabled";
         case R3D_BLOOM_MIX:
@@ -38,9 +38,9 @@ const char* Init(void)
 
     /* --- Setup the default bloom parameters --- */
 
-    R3D_SetTonemapMode(R3D_TONEMAP_ACES);
-    R3D_SetBloomMode(R3D_BLOOM_MIX);
-    R3D_SetBackgroundColor(BLACK);
+    R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_ACES);
+    R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_MIX);
+    R3D_ENVIRONMENT_SET(background.color, BLACK);
 
     /* --- Load a cube mesh and setup its material --- */
 
@@ -77,18 +77,18 @@ void Update(float delta)
                        (IsKeyPressedRepeat(KEY_LEFT) || IsKeyPressed(KEY_LEFT));
 
     if (intensityDir != 0) {
-        R3D_SetBloomIntensity(R3D_GetBloomIntensity() + intensityDir * 0.01f);
+        R3D_ENVIRONMENT_SET(bloom.intensity, R3D_ENVIRONMENT_GET(bloom.intensity) + intensityDir * 0.01f);
     }
 
     int radiusDir = (IsKeyPressedRepeat(KEY_UP) || IsKeyPressed(KEY_UP)) -
                     (IsKeyPressedRepeat(KEY_DOWN) || IsKeyPressed(KEY_DOWN));
 
     if (radiusDir != 0) {
-        R3D_SetBloomFilterRadius(R3D_GetBloomFilterRadius() + radiusDir);
+        R3D_ENVIRONMENT_SET(bloom.filterRadius, R3D_ENVIRONMENT_GET(bloom.filterRadius) + radiusDir * 0.1f);
     }
 
     if (IsKeyPressed(KEY_SPACE)) {
-        R3D_SetBloomMode((R3D_GetBloomMode() + 1) % (R3D_BLOOM_SCREEN + 1));
+        R3D_ENVIRONMENT_SET(bloom.mode, (R3D_ENVIRONMENT_GET(bloom.mode) + 1) % (R3D_BLOOM_SCREEN + 1));
     }
 }
 
@@ -103,15 +103,15 @@ void Draw(void)
     const char* infoStr;
     int infoLen;
 
-    infoStr = TextFormat("Mode: %s", getBloomModeName(R3D_GetBloomMode()));
+    infoStr = TextFormat("Mode: %s", GetBloomModeName());
     infoLen = MeasureText(infoStr, 20);
     DrawText(infoStr, GetScreenWidth() - infoLen - 10, 10, 20, LIME);
 
-    infoStr = TextFormat("Intensity: %.2f", R3D_GetBloomIntensity());
+    infoStr = TextFormat("Intensity: %.2f", R3D_ENVIRONMENT_GET(bloom.intensity));
     infoLen = MeasureText(infoStr, 20);
     DrawText(infoStr, GetScreenWidth() - infoLen - 10, 40, 20, LIME);
 
-    infoStr = TextFormat("Filter Radius: %i", R3D_GetBloomFilterRadius());
+    infoStr = TextFormat("Filter Radius: %.2f", R3D_ENVIRONMENT_GET(bloom.filterRadius));
     infoLen = MeasureText(infoStr, 20);
     DrawText(infoStr, GetScreenWidth() - infoLen - 10, 70, 20, LIME);
 }

+ 1 - 1
examples/directional.c

@@ -41,7 +41,7 @@ const char* Init(void)
 
     /* --- Setup the scene lighting --- */
 
-    R3D_SetAmbientColor((Color) { 10, 10, 10, 255 });
+    R3D_ENVIRONMENT_SET(ambient.color, (Color) { 10, 10, 10, 255 });
 
     R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR);
     {

+ 16 - 12
examples/dof.c

@@ -1,4 +1,5 @@
 #include "./common.h"
+#include "r3d/r3d_environment.h"
 #include "rcamera.h"
 #include <stdlib.h>
 #include <stdio.h>
@@ -24,11 +25,13 @@ const char* Init(void)
 
     /* --- Enable and configure DOF --- */
 
-    R3D_SetDofMode(R3D_DOF_ENABLED);
-    R3D_SetDofFocusPoint(2.0f);
-    R3D_SetDofFocusScale(3.0f);
-    R3D_SetDofMaxBlurSize(20.0f);
-    R3D_SetDofDebugMode(0);
+    R3D_ENVIRONMENT_SET(background.color, BLACK);
+
+    R3D_ENVIRONMENT_SET(dof.mode, R3D_DOF_ENABLED);
+    R3D_ENVIRONMENT_SET(dof.focusPoint, 2.0f);
+    R3D_ENVIRONMENT_SET(dof.focusScale, 3.0f);
+    R3D_ENVIRONMENT_SET(dof.maxBlurSize, 20.0f);
+    R3D_ENVIRONMENT_SET(dof.debugMode, false);
 
     /* --- Setup scene lighting --- */
 
@@ -87,19 +90,19 @@ void Update(float delta)
     float mouseWheel = GetMouseWheelMove();
 
     float focusPoint = 0.5f + (5.0f - (mousePosition.y / GetScreenHeight()) * 5.0f);
-    R3D_SetDofFocusPoint(focusPoint);
+    R3D_ENVIRONMENT_SET(dof.focusPoint, focusPoint);
 
     float focusScale = 0.5f + (5.0f - (mousePosition.x / GetScreenWidth()) * 5.0f);
-    R3D_SetDofFocusScale(focusScale);
+    R3D_ENVIRONMENT_SET(dof.focusScale, focusScale);
 
     if (mouseWheel != 0.0f) {
-        float maxBlurSize = R3D_GetDofMaxBlurSize();
+        float maxBlurSize = R3D_ENVIRONMENT_GET(dof.maxBlurSize);
         maxBlurSize += mouseWheel * 0.1f;
-        R3D_SetDofMaxBlurSize(maxBlurSize);
+        R3D_ENVIRONMENT_SET(dof.maxBlurSize, maxBlurSize);
     }
 
     if (IsKeyPressed(KEY_F1)) {
-        R3D_SetDofDebugMode(!R3D_GetDofDebugMode());
+        R3D_ENVIRONMENT_SET(dof.debugMode, !R3D_ENVIRONMENT_GET(dof.debugMode));
     }
 }
 
@@ -112,7 +115,6 @@ void Draw(void)
     /* --- Render R3D scene --- */
 
     R3D_Begin(camDefault);
-        R3D_SetBackgroundColor((Color){0, 0, 0, 255});
         R3D_DrawMeshInstancedEx(&meshSphere, &matDefault, instances, instanceColors, INSTANCE_COUNT);
     R3D_End();
 
@@ -120,7 +122,9 @@ void Draw(void)
 
     char dofText[128];
     snprintf(dofText, sizeof(dofText), "Focus Point: %.2f\nFocus Scale: %.2f\nMax Blur Size: %.2f\nDebug Mode: %d",
-        R3D_GetDofFocusPoint(), R3D_GetDofFocusScale(), R3D_GetDofMaxBlurSize(), R3D_GetDofDebugMode());
+        R3D_ENVIRONMENT_GET(dof.focusPoint), R3D_ENVIRONMENT_GET(dof.focusScale),
+        R3D_ENVIRONMENT_GET(dof.maxBlurSize), R3D_ENVIRONMENT_GET(dof.debugMode));
+
     DrawText(dofText, 10, 30, 20, WHITE);
 
     /* --- Print instructions --- */

+ 11 - 11
examples/emission.c

@@ -15,11 +15,11 @@ void ToggleLight(void)
 {
     if (R3D_IsLightActive(light)) {
         R3D_SetLightActive(light, false);
-        R3D_SetAmbientColor(BLACK);
+        R3D_ENVIRONMENT_SET(ambient.color, BLACK);
     }
     else {
         R3D_SetLightActive(light, true);
-        R3D_SetAmbientColor(DARKGRAY);
+        R3D_ENVIRONMENT_SET(ambient.color, DARKGRAY);
     }
 }
 
@@ -34,19 +34,19 @@ const char* Init(void)
 
     /* --- Configure the background color and ambient lighting --- */
 
-    R3D_SetBackgroundColor(BLACK);
-    R3D_SetAmbientColor(DARKGRAY);
+    R3D_ENVIRONMENT_SET(background.color, BLACK);
+    R3D_ENVIRONMENT_SET(ambient.color, DARKGRAY);
 
     /* --- Configure the post process parameters --- */
 
-    R3D_SetTonemapMode(R3D_TONEMAP_ACES);
-    R3D_SetTonemapExposure(0.8f);
-    R3D_SetTonemapWhite(2.5f);
+    R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_ACES);
+    R3D_ENVIRONMENT_SET(tonemap.exposure, 0.8f);
+    R3D_ENVIRONMENT_SET(tonemap.white, 2.5f);
 
-    R3D_SetBloomMode(R3D_BLOOM_ADDITIVE);
-    R3D_SetBloomSoftThreshold(0.2f);
-    R3D_SetBloomIntensity(0.2f);
-    R3D_SetBloomThreshold(0.6f);
+    R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_ADDITIVE);
+    R3D_ENVIRONMENT_SET(bloom.softThreshold, 0.2f);
+    R3D_ENVIRONMENT_SET(bloom.threshold, 0.6f);
+    R3D_ENVIRONMENT_SET(bloom.intensity, 0.2f);
 
     /* --- Loads the main model of the scene --- */
 

+ 3 - 3
examples/particles.c

@@ -21,12 +21,12 @@ const char* Init(void)
 
     /* --- Setup the background color and ambient light --- */
 
-    R3D_SetBackgroundColor((Color) { 4, 4, 4 });
-    R3D_SetAmbientColor(BLACK);
+    R3D_ENVIRONMENT_SET(background.color, (Color) { 4, 4, 4 });
+    R3D_ENVIRONMENT_SET(ambient.color, BLACK);
 
     /* --- Activate bloom in additive mode --- */
 
-    R3D_SetBloomMode(R3D_BLOOM_ADDITIVE);
+    R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_ADDITIVE);
 
     /* --- Gen a sphere as particle mesh --- */
 

+ 12 - 11
examples/pbr_car.c

@@ -1,4 +1,5 @@
 #include "./common.h"
+#include "r3d/r3d_skybox.h"
 
 /* === Resources === */
 
@@ -22,19 +23,19 @@ const char* Init(void)
 
     /* --- Setup background color and ambient light --- */
 
-    R3D_SetBackgroundColor(BLACK);
-    R3D_SetAmbientColor(DARKGRAY);
+    R3D_ENVIRONMENT_SET(background.color, BLACK);
+    R3D_ENVIRONMENT_SET(ambient.color, DARKGRAY);
 
     /* --- Setup post processing parameters --- */
 
-    R3D_SetSSR(true);
+    R3D_ENVIRONMENT_SET(ssr.enabled, true);
 
-    R3D_SetSSAO(true);
-    R3D_SetSSAORadius(2.0f);
+    R3D_ENVIRONMENT_SET(ssao.enabled, true);
+    R3D_ENVIRONMENT_SET(ssao.radius, 2.0f);
 
-    R3D_SetBloomIntensity(0.1f);
-    R3D_SetBloomMode(R3D_BLOOM_MIX);
-    R3D_SetTonemapMode(R3D_TONEMAP_ACES);
+    R3D_ENVIRONMENT_SET(bloom.intensity, 0.1f);
+    R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_MIX);
+    R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_ACES);
 
     /* --- Load the car model and apply scaling on import --- */
 
@@ -84,13 +85,13 @@ void Update(float delta)
     UpdateCamera(&camera, CAMERA_FREE);
 
     if (IsKeyPressed(KEY_O)) {
-        R3D_SetSSAO(!R3D_GetSSAO());
+        R3D_ENVIRONMENT_SET(ssao.enabled, !R3D_ENVIRONMENT_GET(ssao.enabled));
     }
 
     if (IsKeyPressed(KEY_T)) {
         showSkybox = !showSkybox;
-        if (showSkybox) R3D_EnableSkybox(skybox);
-        else R3D_DisableSkybox();
+        if (showSkybox) R3D_ENVIRONMENT_SET(background.sky, skybox);
+        else R3D_ENVIRONMENT_SET(background.sky, (R3D_Skybox) {0});
     }
 }
 

+ 4 - 4
examples/pbr_musket.c

@@ -20,9 +20,9 @@ const char* Init(void)
 
     /* --- Setup tonemapping --- */
 
-    R3D_SetTonemapMode(R3D_TONEMAP_ACES);
-    R3D_SetTonemapExposure(0.75f);
-    R3D_SetTonemapWhite(1.25f);
+    R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_ACES);
+    R3D_ENVIRONMENT_SET(tonemap.exposure, 0.75f);
+    R3D_ENVIRONMENT_SET(tonemap.white, 1.25f);
 
     /* --- Load model --- */
 
@@ -37,7 +37,7 @@ const char* Init(void)
     /* --- Load and enable the skybox --- */
 
     skybox = R3D_LoadSkybox(RESOURCES_PATH "sky/skybox2.png", CUBEMAP_LAYOUT_AUTO_DETECT);
-    R3D_EnableSkybox(skybox);
+    R3D_ENVIRONMENT_SET(background.sky, skybox);
 
     /* --- Setup the scene lighting --- */
 

+ 1 - 1
examples/skybox.c

@@ -36,7 +36,7 @@ const char* Init(void)
     /* --- Load and enable a skybox --- */
 
     skybox = R3D_LoadSkybox(RESOURCES_PATH "sky/skybox1.png", CUBEMAP_LAYOUT_AUTO_DETECT);
-    R3D_EnableSkybox(skybox);
+    R3D_ENVIRONMENT_SET(background.sky, skybox);
 
     /* --- Setup the camera --- */
 

+ 16 - 16
examples/sponza.c

@@ -20,17 +20,17 @@ const char* Init(void)
 
     /* --- Configure default post process settings --- */
 
-    R3D_SetSSAO(true);
-    R3D_SetSSAORadius(4.0f);
-    R3D_SetSSAOIntensity(1.25f);
-    R3D_SetSSAOPower(1.5f);
+    R3D_ENVIRONMENT_SET(ssao.enabled, true);
+    R3D_ENVIRONMENT_SET(ssao.radius, 4.0f);
+    R3D_ENVIRONMENT_SET(ssao.intensity, 1.25f);
+    R3D_ENVIRONMENT_SET(ssao.power, 1.5f);
 
-    R3D_SetBloomMode(R3D_BLOOM_MIX);
+    R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_MIX);
 
     /* --- Set default background and ambient color (when no skybox is activated) --- */
 
-    R3D_SetBackgroundColor(SKYBLUE);
-    R3D_SetAmbientColor(DARKGRAY);
+    R3D_ENVIRONMENT_SET(background.color, SKYBLUE);
+    R3D_ENVIRONMENT_SET(ambient.color, DARKGRAY);
 
     /* --- Load Sponza scene --- */
 
@@ -80,21 +80,21 @@ void Update(float delta)
     /* --- Skybox toggling --- */
 
     if (IsKeyPressed(KEY_ZERO)) {
-        if (sky) R3D_DisableSkybox();
-        else R3D_EnableSkybox(skybox);
+        if (sky) R3D_ENVIRONMENT_SET(background.sky, (R3D_Skybox) {0});
+        else R3D_ENVIRONMENT_SET(background.sky, skybox);
         sky = !sky;
     }
 
     /* --- SSAO toggling --- */
 
     if (IsKeyPressed(KEY_ONE)) {
-        R3D_SetSSAO(!R3D_GetSSAO());
+        R3D_ENVIRONMENT_SET(ssao.enabled, !R3D_ENVIRONMENT_GET(ssao.enabled));
     }
 
     /* --- Fog toggling --- */
 
     if (IsKeyPressed(KEY_TWO)) {
-        R3D_SetFogMode(R3D_GetFogMode() == R3D_FOG_DISABLED ? R3D_FOG_EXP : R3D_FOG_DISABLED);
+        R3D_ENVIRONMENT_SET(fog.mode, R3D_ENVIRONMENT_GET(fog.mode) == R3D_FOG_DISABLED ? R3D_FOG_EXP : R3D_FOG_DISABLED);
     }
 
     /* --- FXAA toggling --- */
@@ -108,12 +108,12 @@ void Update(float delta)
     /* --- Tonemapping setter --- */
 
     if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
-        R3D_Tonemap tonemap = R3D_GetTonemapMode();
-        R3D_SetTonemapMode((tonemap + R3D_TONEMAP_COUNT - 1) % R3D_TONEMAP_COUNT);
+        R3D_Tonemap tonemap = R3D_ENVIRONMENT_GET(tonemap.mode);
+        R3D_ENVIRONMENT_SET(tonemap.mode, (tonemap + R3D_TONEMAP_COUNT - 1) % R3D_TONEMAP_COUNT);
     }
     if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) {
-        R3D_Tonemap tonemap = R3D_GetTonemapMode();
-        R3D_SetTonemapMode((tonemap + 1) % R3D_TONEMAP_COUNT);
+        R3D_Tonemap tonemap = R3D_ENVIRONMENT_GET(tonemap.mode);
+        R3D_ENVIRONMENT_SET(tonemap.mode, (tonemap + 1) % R3D_TONEMAP_COUNT);
     }
 }
 
@@ -134,7 +134,7 @@ void Draw(void)
 
     /* --- Indicates which tonemapping is used --- */
 
-    R3D_Tonemap tonemap = R3D_GetTonemapMode();
+    R3D_Tonemap tonemap = R3D_ENVIRONMENT_GET(tonemap.mode);
 
     switch (tonemap) {
     case R3D_TONEMAP_LINEAR: {

+ 1 - 1
examples/transparency.c

@@ -61,7 +61,7 @@ const char* Init(void)
 
     /* --- Configure lighting --- */
 
-    R3D_SetAmbientColor((Color) { 10, 10, 10, 255 });
+    R3D_ENVIRONMENT_SET(ambient.color, (Color) { 10, 10, 10, 255 });
 
     R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT);
     {

+ 0 - 1
include/r3d/r3d.h

@@ -21,7 +21,6 @@
 #define R3D_H
 
 #include "r3d_animation.h"
-#include "r3d_api.h"
 #include "r3d_core.h"
 #include "r3d_culling.h"
 #include "r3d_curves.h"

+ 1 - 0
include/r3d/r3d_animation.h

@@ -9,6 +9,7 @@
 #ifndef R3D_ANIMATION_H
 #define R3D_ANIMATION_H
 
+#include "./r3d_platform.h"
 #include "./r3d_skeleton.h"
 #include <raylib.h>
 

+ 9 - 9
include/r3d/r3d_core.h

@@ -9,7 +9,7 @@
 #ifndef R3D_CORE_H
 #define R3D_CORE_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 #include <stdint.h>
 
@@ -94,7 +94,7 @@ extern "C" {
  * @param resHeight Height of the internal resolution.
  * @param flags Flags indicating internal behavior (modifiable via R3D_SetState).
  */
-R3DAPI void R3D_Init(int resWidth, int resHeight, unsigned int flags);
+R3DAPI void R3D_Init(int resWidth, int resHeight, R3D_Flags flags);
 
 /**
  * @brief Closes the rendering engine and deallocates all resources.
@@ -107,10 +107,10 @@ R3DAPI void R3D_Close(void);
 /**
  * @brief Checks if a specific internal state flag is set.
  * 
- * @param flag The state flag to check.
- * @return True if the flag is set, false otherwise.
+ * @param flags The state flags to check.
+ * @return True if the flags are set, false otherwise.
  */
-R3DAPI bool R3D_HasState(unsigned int flag);
+R3DAPI bool R3D_HasState(R3D_Flags flags);
 
 /**
  * @brief Sets internal state flags for the rendering engine.
@@ -120,7 +120,7 @@ R3DAPI bool R3D_HasState(unsigned int flag);
  * 
  * @param flags The flags to set.
  */
-R3DAPI void R3D_SetState(unsigned int flags);
+R3DAPI void R3D_SetState(R3D_Flags flags);
 
 /**
  * @brief Clears specific internal state flags.
@@ -130,7 +130,7 @@ R3DAPI void R3D_SetState(unsigned int flags);
  * 
  * @param flags The flags to clear.
  */
-R3DAPI void R3D_ClearState(unsigned int flags);
+R3DAPI void R3D_ClearState(R3D_Flags flags);
 
 /**
  * @brief Gets the current internal resolution.
@@ -188,9 +188,9 @@ R3DAPI R3D_Layer R3D_GetActiveLayers(void);
  *
  * Replaces the current set of active layers with the given bitfield.
  *
- * @param layers Bitfield representing the layers to activate.
+ * @param bitfield Bitfield representing the layers to activate.
  */
-R3DAPI void R3D_SetActiveLayers(R3D_Layer layers);
+R3DAPI void R3D_SetActiveLayers(R3D_Layer bitfield);
 
 /**
  * @brief Enable one or more layers without affecting other active layers.

+ 1 - 47
include/r3d/r3d_culling.h

@@ -9,7 +9,7 @@
 #ifndef R3D_CULLING_H
 #define R3D_CULLING_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 
 /**
@@ -105,52 +105,6 @@ R3DAPI bool R3D_IsAABBInFrustum(BoundingBox aabb);
  */
 R3DAPI bool R3D_IsOBBInFrustum(BoundingBox aabb, Matrix transform);
 
-/**
- * @brief Fast pre-filtering test for point inside frustum bounding box.
- *
- * Performs an AABB check using the frustum's bounding volume.
- * Useful for quick rejection before precise tests.
- *
- * @param position The 3D point to test.
- * @return `true` if inside the frustum AABB, `false` otherwise.
- *
- * @note May return false positives, never false negatives.
- * @warning Only checks against a loose AABB, not actual frustum planes.
- * @see R3D_IsPointInFrustum()
- */
-R3DAPI bool R3D_IsPointInFrustumBoundingBox(Vector3 position);
-
-/**
- * @brief Fast pre-filtering test for sphere inside frustum bounding box.
- *
- * Performs a quick check using the frustum's AABB to approximate intersection.
- *
- * @param position The center of the sphere.
- * @param radius Radius of the sphere.
- * @return `true` if possibly intersecting the frustum AABB, `false` otherwise.
- *
- * @note Faster but less accurate than full frustum testing.
- * @warning May produce false positives.
- * @see R3D_IsSphereInFrustum()
- */
-R3DAPI bool R3D_IsSphereInFrustumBoundingBox(Vector3 position, float radius);
-
-/**
- * @brief Fast pre-filtering test for AABB inside frustum bounding box.
- *
- * Performs a bounding box vs bounding box intersection to quickly eliminate non-visible objects.
- * Useful as an initial coarse check before calling full frustum tests.
- *
- * @param aabb The bounding box to test.
- * @return `true` if intersecting the frustum AABB, `false` otherwise.
- *
- * @note False positives possible, but never false negatives.
- * @warning Does not use actual frustum planes.
- * @note No OBB variant exists due to computational cost.
- * @see R3D_IsAABBInFrustum()
- */
-R3DAPI bool R3D_IsAABBInFrustumBoundingBox(BoundingBox aabb);
-
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 1 - 1
include/r3d/r3d_curves.h

@@ -9,7 +9,7 @@
 #ifndef R3D_CURVES_H
 #define R3D_CURVES_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 
 /**

+ 1 - 0
include/r3d/r3d_decal.h

@@ -9,6 +9,7 @@
 #ifndef R3D_DECAL_H
 #define R3D_DECAL_H
 
+#include "./r3d_platform.h"
 #include "./r3d_material.h"
 
 /**

+ 1 - 1
include/r3d/r3d_draw.h

@@ -10,9 +10,9 @@
 #define R3D_DRAW_H
 
 #include "./r3d_particles.h"
+#include "./r3d_platform.h"
 #include "./r3d_model.h"
 #include "./r3d_decal.h"
-#include "./r3d_api.h"
 #include <raylib.h>
 
 /**

+ 247 - 880
include/r3d/r3d_environment.h

@@ -9,16 +9,115 @@
 #ifndef R3D_ENVIRONMENT_H
 #define R3D_ENVIRONMENT_H
 
+#include "./r3d_platform.h"
 #include "./r3d_skybox.h"
-#include "./r3d_api.h"
 #include <raylib.h>
 
 /**
  * @defgroup Environment
- * @brief Mainly defines post process control.
+ * @brief Scene rendering and post-processing configuration.
+ *
+ * Controls background, lighting, and visual effects through the R3D_Environment structure.
+ * Includes skybox, ambient light, SSAO, bloom, SSR, fog, depth of field, tone mapping,
+ * and color grading.
+ *
+ * ## Usage
+ *
+ * ```c
+ * // Direct access
+ * R3D_Environment* env = R3D_GetEnvironment();
+ * env->bloom.intensity = 0.8f;
+ *
+ * // Quick macro update
+ * R3D_ENVIRONMENT_SET(bloom.intensity, 0.8f);
+ *
+ * // Batch configuration
+ * R3D_Environment custom = R3D_ENVIRONMENT_BASE;
+ * custom.bloom.mode = R3D_BLOOM_ADDITIVE;
+ * custom.ssao.enabled = true;
+ * R3D_SetEnvironment(&custom);
+ * ```
+ *
  * @{
  */
 
+// ========================================
+// CONSTANTS
+// ========================================
+
+/**
+ * @brief Default environment configuration.
+ *
+ * Initializes an R3D_Environment structure with sensible default values for all
+ * rendering parameters. Use this as a starting point for custom configurations.
+ */
+#define R3D_ENVIRONMENT_BASE                            \
+    R3D_LITERAL(R3D_Environment) {                      \
+        .background = {                                 \
+            .color = {80, 80, 80, 255},                 \
+            .energy = 1.0f,                             \
+            .sky = {0},                                 \
+            .rotation = {0.0f, 0.0f, 0.0f, 1.0f},       \
+        },                                              \
+        .ambient = {                                    \
+            .color = {0, 0, 0, 255},                    \
+            .energy = 1.0f,                             \
+            .reflect = 1.0f,                            \
+        },                                              \
+        .ssao = {                                       \
+            .intensity = 1.0f,                          \
+            .power = 1.0f,                              \
+            .radius = 0.5f,                             \
+            .bias = 0.025,                              \
+            .lightAffect = 0.0f,                        \
+            .iterations = 1,                            \
+            .enabled = false,                           \
+        },                                              \
+        .bloom = {                                      \
+            .mode = R3D_BLOOM_DISABLED,                 \
+            .levels = 0.5f,                             \
+            .intensity = 0.05f,                         \
+            .threshold = 0.0f,                          \
+            .softThreshold = 0.5f,                      \
+            .filterRadius = 1.0f,                       \
+        },                                              \
+        .ssr = {                                        \
+            .maxRaySteps = 64,                          \
+            .binarySearchSteps = 8,                     \
+            .rayMarchLength = 8.0f,                     \
+            .depthThickness = 0.2f,                     \
+            .depthTolerance = 0.005f,                   \
+            .edgeFadeStart = 0.7f,                      \
+            .edgeFadeEnd = 1.0f,                        \
+            .enabled = false,                           \
+        },                                              \
+        .fog = {                                        \
+            .mode = R3D_FOG_DISABLED,                   \
+            .color = {255, 255, 255, 255},              \
+            .start = 1.0f,                              \
+            .end = 50.0f,                               \
+            .density = 0.05f,                           \
+            .skyAffect = 0.5f,                          \
+        },                                              \
+        .dof = {                                        \
+            .mode = R3D_DOF_DISABLED,                   \
+            .focusPoint = 10.0f,                        \
+            .focusScale = 1.0f,                         \
+            .maxBlurSize = 20.0f,                       \
+            .debugMode = false,                         \
+        },                                              \
+        .tonemap = {                                    \
+            .mode = R3D_TONEMAP_LINEAR,                 \
+            .exposure = 1.0f,                           \
+            .white = 1.0f,                              \
+        },                                              \
+        .color = {                                      \
+            .brightness = 1.0f,                         \
+            .contrast = 1.0f,                           \
+            .saturation = 1.0f,                         \
+        },                                              \
+    }
+
 // ========================================
 // ENUMS TYPES
 // ========================================
@@ -26,965 +125,233 @@
 /**
  * @brief Bloom effect modes.
  *
- * Specifies different post-processing bloom techniques that can be applied
- * to the rendered scene. Bloom effects enhance the appearance of bright areas
- * by simulating light bleeding, contributing to a more cinematic and realistic look.
+ * Different blending methods for the bloom glow effect.
  */
- typedef enum R3D_Bloom {
-    R3D_BLOOM_DISABLED,     ///< Bloom effect is disabled. The scene is rendered without any glow enhancement.
-    R3D_BLOOM_MIX,          ///< Blends the bloom effect with the original scene using linear interpolation (Lerp).
-    R3D_BLOOM_ADDITIVE,     ///< Adds the bloom effect additively to the scene, intensifying bright regions.
-    R3D_BLOOM_SCREEN        ///< Combines the scene and bloom using screen blending, which brightens highlights
+typedef enum R3D_Bloom {
+    R3D_BLOOM_DISABLED,     ///< No bloom effect applied
+    R3D_BLOOM_MIX,          ///< Linear interpolation blend between scene and bloom
+    R3D_BLOOM_ADDITIVE,     ///< Additive blending, intensifying bright regions
+    R3D_BLOOM_SCREEN        ///< Screen blending for softer highlight enhancement
 } R3D_Bloom;
 
 /**
  * @brief Fog effect modes.
  *
- * Determines how fog is applied to the scene, affecting depth perception and atmosphere.
+ * Distance-based fog density distribution methods.
  */
 typedef enum R3D_Fog {
-    R3D_FOG_DISABLED, ///< Fog effect is disabled.
-    R3D_FOG_LINEAR,   ///< Fog density increases linearly with distance from the camera.
-    R3D_FOG_EXP2,     ///< Exponential fog (exp2), where density increases exponentially with distance.
-    R3D_FOG_EXP       ///< Exponential fog, similar to EXP2 but with a different rate of increase.
+    R3D_FOG_DISABLED,       ///< No fog effect
+    R3D_FOG_LINEAR,         ///< Linear density increase between start and end distances
+    R3D_FOG_EXP2,           ///< Exponential squared density (exp2), more realistic
+    R3D_FOG_EXP             ///< Simple exponential density increase
 } R3D_Fog;
 
 /**
- * @brief Depth of field effect modes.
- *
- * Controls how depth of field is applied to the scene, affecting the focus and blur of objects.
+ * @brief Depth of field modes.
  */
-typedef enum R3D_Dof {
-    R3D_DOF_DISABLED, ///< Depth of field effect is disabled.
-    R3D_DOF_ENABLED,  ///< Depth of field effect is enabled.
-} R3D_Dof;
+typedef enum R3D_DoF {
+    R3D_DOF_DISABLED,       ///< No depth of field effect
+    R3D_DOF_ENABLED         ///< Depth of field enabled with focus point and blur
+} R3D_DoF;
 
 /**
- * @brief Tone mapping modes.
+ * @brief Tone mapping algorithms.
  *
- * Controls how high dynamic range (HDR) colors are mapped to low dynamic range (LDR) for display.
+ * HDR to LDR color compression methods.
  */
 typedef enum R3D_Tonemap {
-    R3D_TONEMAP_LINEAR,   ///< Simple linear mapping of HDR values.
-    R3D_TONEMAP_REINHARD, ///< Reinhard tone mapping, a balanced method for compressing HDR values.
-    R3D_TONEMAP_FILMIC,   ///< Filmic tone mapping, mimicking the response of photographic film.
-    R3D_TONEMAP_ACES,     ///< ACES tone mapping, a high-quality cinematic rendering technique.
-    R3D_TONEMAP_AGX,      ///< AGX tone mapping, a modern technique designed to preserve both highlight and shadow details for HDR rendering.
-    R3D_TONEMAP_COUNT     ///< Number of tone mapping modes (used internally)
+    R3D_TONEMAP_LINEAR,     ///< Direct linear mapping (no compression)
+    R3D_TONEMAP_REINHARD,   ///< Reinhard operator, balanced HDR compression
+    R3D_TONEMAP_FILMIC,     ///< Film-like response curve
+    R3D_TONEMAP_ACES,       ///< Academy Color Encoding System (cinematic standard)
+    R3D_TONEMAP_AGX,        ///< Modern algorithm preserving highlights and shadows
+    R3D_TONEMAP_COUNT       ///< Internal: number of tonemap modes
 } R3D_Tonemap;
 
 // ========================================
-// PUBLIC API
+// STRUCT TYPES
 // ========================================
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// ----------------------------------------
-// ENVIRONMENT: Background And Ambient
-// ----------------------------------------
-
-/**
- * @brief Sets the background color when no skybox is enabled.
- *
- * This function defines the background color to be used when no skybox is active.
- * The color will be used for the clear color of the scene.
- *
- * @param color The color to set as the background color.
- */
-R3DAPI void R3D_SetBackgroundColor(Color color);
-
-/**
- * @brief Sets the ambient light color when no skybox is enabled.
- *
- * This function defines the ambient light color to be used when no skybox is active.
- * It affects the overall lighting of the scene when no skybox is present.
- *
- * @param color The color to set for ambient light.
- */
-R3DAPI void R3D_SetAmbientColor(Color color);
-
-/**
- * @brief Sets the energy level for ambient light when no skybox is enabled.
- *
- * This function defines the ambient light energy to be used when no skybox is active.
- * Applied multiplicatively to the base ambient color.
- *
- * @param energy The energy to set for ambient light.
- */
-R3DAPI void R3D_SetAmbientEnergy(float energy);
-
-/**
- * @brief Enables a skybox for the scene.
- *
- * This function enables a skybox in the scene, replacing the default background with
- * a 3D environment. The skybox is defined by the specified skybox asset.
- *
- * @param skybox The skybox to enable.
- */
-R3DAPI void R3D_EnableSkybox(R3D_Skybox skybox);
-
-/**
- * @brief Disables the skybox in the scene.
- *
- * This function disables the skybox, reverting back to the default background
- * color (or no background if none is set). It should be called to remove the skybox
- * from the scene.
- */
-R3DAPI void R3D_DisableSkybox(void);
-
-/**
- * @brief Sets the rotation of the skybox.
- *
- * This function allows you to specify the rotation of the skybox along the
- * pitch, yaw, and roll axes, which allows the skybox to be rotated in the scene.
- *
- * @param pitch The rotation angle around the X-axis (in degrees).
- * @param yaw The rotation angle around the Y-axis (in degrees).
- * @param roll The rotation angle around the Z-axis (in degrees).
- */
-R3DAPI void R3D_SetSkyboxRotation(float pitch, float yaw, float roll);
-
-/**
- * @brief Gets the current rotation of the skybox.
- *
- * This function returns the current rotation of the skybox as a vector containing
- * the pitch, yaw, and roll values in degrees.
- *
- * @return A vector containing the current pitch, yaw, and roll of the skybox.
- */
-R3DAPI Vector3 R3D_GetSkyboxRotation(void);
-
-/**
- * @brief Sets the intensity scaling values used for the environment's skybox.
- *
- * This function controls the intensity of both the rendered skybox as well as
- * the light that is generated from the skybox.
- *
- * @param background The intensity of the skybox rendered as the background.
- *                   A value of 0.0 will disable rendering the skybox but
- *                   allow any generated lighting to still be applied.
- * @param ambient The intensity of ambient light produced by the skybox.
- * @param reflection The intensity of reflections of the skybox in reflective materials.
- */
-R3DAPI void R3D_SetSkyboxIntensity(float background, float ambient, float reflection);
-
-/**
- * @brief Gets the intensity scaling values used for the environment's skybox.
- *
- * This function returns the intensity values for the rendered skybox as well
- * the light that is generated from the skybox.
- *
- * @param background Pointer to store the intensity value for the rendered skybox.
- * @param ambient Pointer to store the intensity value for ambient light produced by the skybox.
- * @param reflection Pointer to store the intensity value for reflections from the skybox.
- */
-R3DAPI void R3D_GetSkyboxIntensity(float* background, float* ambient, float* reflection);
-
-// ----------------------------------------
-// ENVIRONMENT: SSAO Config Functions
-// ----------------------------------------
-
-/**
- * @brief Enables or disables Screen Space Ambient Occlusion (SSAO).
- *
- * This function toggles the SSAO effect. When enabled, SSAO enhances the realism
- * of the scene by simulating ambient occlusion, darkening areas where objects
- * are close together or in corners.
- *
- * @param enabled Whether to enable or disable SSAO.
- *
- * Default: false
- */
-R3DAPI void R3D_SetSSAO(bool enabled);
-
-/**
- * @brief Gets the current state of SSAO.
- *
- * This function checks if SSAO is currently enabled or disabled.
- *
- * @return True if SSAO is enabled, false otherwise.
- */
-R3DAPI bool R3D_GetSSAO(void);
-
-/**
- * @brief Sets the radius for SSAO effect.
- *
- * This function sets the radius used by the SSAO effect to calculate occlusion.
- * A higher value will affect a larger area around each pixel, while a smaller value
- * will create sharper and more localized occlusion.
- *
- * @param value The radius value to set for SSAO.
- *
- * Default: 0.5
- */
-R3DAPI void R3D_SetSSAORadius(float value);
-
-/**
- * @brief Gets the current SSAO radius.
- *
- * This function retrieves the current radius value used by the SSAO effect.
- *
- * @return The radius value for SSAO.
- */
-R3DAPI float R3D_GetSSAORadius(void);
-
-/**
- * @brief Sets the bias for SSAO effect.
- *
- * This function sets the bias used by the SSAO effect to adjust how much occlusion
- * is applied to the scene. A higher value can reduce artifacts, but may also
- * result in less pronounced ambient occlusion.
- *
- * @param value The bias value for SSAO.
- *
- * Default: 0.025
- */
-R3DAPI void R3D_SetSSAOBias(float value);
-
-/**
- * @brief Gets the current SSAO bias.
- *
- * This function retrieves the current bias value used by the SSAO effect.
- *
- * @return The SSAO bias value.
- */
-R3DAPI float R3D_GetSSAOBias(void);
-
-/**
- * @brief Sets the number of blur iterations for the SSAO effect.
- *
- * This function sets the number of blur iterations applied to the SSAO effect.
- * By default, one iteration is performed, using a total of 12 samples for the
- * Gaussian blur. Increasing the number of iterations results in a smoother
- * ambient occlusion but may impact performance.
- *
- * @param value The number of blur iterations for SSAO.
- *
- * Default: 1
- */
-R3DAPI void R3D_SetSSAOIterations(int value);
-
 /**
- * @brief Gets the current number of blur iterations for the SSAO effect.
- *
- * This function retrieves the current number of blur iterations applied to the SSAO effect.
- *
- * @return The number of blur iterations for SSAO.
+ * @brief Background and skybox configuration.
  */
-R3DAPI int R3D_GetSSAOIterations(void);
+typedef struct R3D_EnvBackground {
+    Color color;            ///< Background color when skybox is disabled
+    float energy;           ///< Energy multiplier applied to background (skybox or color)
+    R3D_Skybox sky;         ///< Skybox asset (used if ID is non-zero)
+    Quaternion rotation;    ///< Skybox rotation (pitch, yaw, roll as quaternion)
+} R3D_EnvBackground;
 
 /**
- * @brief Sets the intensity multiplier for the SSAO effect.
- *
- * This function sets the the base multiplier used by the SSAO effect.
- * Higher values will result in darker occlusion.
- *
- * @param value The intensity multiplier for SSAO.
- *
- * Default: 1.0
+ * @brief Ambient lighting configuration.
  */
-R3DAPI void R3D_SetSSAOIntensity(float value);
+typedef struct R3D_EnvAmbient {
+    Color color;            ///< Ambient light color when skybox is disabled
+    float energy;           ///< Energy multiplier for ambient light (skybox or color)
+    float reflect;          ///< Reflection intensity from skybox (no effect if skybox disabled)
+} R3D_EnvAmbient;
 
 /**
- * @brief Gets the intensity multiplier for the SSAO effect.
- *
- * This function retrieves the intensity multiplier applied to the SSAO effect.
- *
- * @return The intensity multiplier for SSAO.
- */
-R3DAPI float R3D_GetSSAOIntensity(void);
-
-/**
- * @brief Sets the power factor for the SSAO effect.
- *
- * This function sets the exponential distributon applied to the SSAO effect.
- * Higher values will result in darker occlusion with an increasingly sharper
- * falloff compared to the SSAO intensity value.
- *
- * @param value The power factor for SSAO.
+ * @brief Screen Space Ambient Occlusion (SSAO) settings.
  *
- * Default: 1.0
+ * Darkens areas where surfaces are close together, such as corners and crevices.
  */
-R3DAPI void R3D_SetSSAOPower(float value);
+typedef struct R3D_EnvSSAO {
+    float intensity;        ///< Base occlusion strength multiplier (default: 1.0)
+    float power;            ///< Exponential falloff for sharper darkening (default: 1.0)
+    float radius;           ///< Sampling radius in world space (default: 0.5)
+    float bias;             ///< Depth bias to prevent self-shadowing artifacts (default: 0.025)
+    float lightAffect;      ///< How much SSAO affects direct lighting [0.0-1.0] (default: 0.0)
+    int iterations;         ///< Blur iterations for smoothing (default: 1)
+    bool enabled;           ///< Enable/disable SSAO effect (default: false)
+} R3D_EnvSSAO;
 
 /**
- * @brief Gets the power factor used for the SSAO effect.
+ * @brief Bloom post-processing settings.
  *
- * This function retrieves the exponential distributon value applied to the SSAO effect.
- *
- * @return The power factor for SSAO.
+ * Glow effect around bright areas in the scene.
  */
-R3DAPI float R3D_GetSSAOPower(void);
+typedef struct R3D_EnvBloom {
+    R3D_Bloom mode;         ///< Bloom blending mode (default: R3D_BLOOM_DISABLED)
+    float levels;           ///< Mipmap spread factor [0-1]: higher = wider glow (default: 0.5)
+    float intensity;        ///< Bloom strength multiplier (default: 0.05)
+    float threshold;        ///< Minimum brightness to trigger bloom (default: 0.0)
+    float softThreshold;    ///< Softness of brightness cutoff transition (default: 0.5)
+    float filterRadius;     ///< Blur filter radius during upscaling (default: 1.0)
+} R3D_EnvBloom;
 
 /**
- * @brief Controls the influence of SSAO on direct lighting.
- *
- * This function sets the amount of direct light attenuation from the SSAO effect.
- * Values greater than 0.0 will apply the SSAO effect to direct lighting with
- * increasing intensity at higher values. This is in addition to the typical
- * application to ambient light only.
+ * @brief Screen Space Reflections (SSR) settings.
  *
- * @param value SSAO effect intensity on direct light, in the range [0.0f, 1.0f].
- *
- * Default: 0.0
+ * Real-time reflections calculated in screen space.
  */
-R3DAPI void R3D_SetSSAOLightAffect(float value);
+typedef struct R3D_EnvSSR {
+    int maxRaySteps;            ///< Maximum ray marching iterations (default: 64)
+    int binarySearchSteps;      ///< Refinement steps for intersection (default: 8)
+    float rayMarchLength;       ///< Maximum ray distance in view space (default: 8.0)
+    float depthThickness;       ///< Depth tolerance for valid hits (default: 0.2)
+    float depthTolerance;       ///< Negative margin to prevent false negatives (default: 0.005)
+    float edgeFadeStart;        ///< Screen edge fade start [0-1] (default: 0.7)
+    float edgeFadeEnd;          ///< Screen edge fade end [0-1] (default: 1.0)
+    bool enabled;               ///< Enable/disable SSR (default: false)
+} R3D_EnvSSR;
 
 /**
- * @brief Gets the current direct lighting effect of SSAO.
- *
- * This function retrieves the value used for direct light attenuation from the SSAO effect.
- *
- * @return SSAO effect intensity on direct light, in the range [0.0f, 1.0f].
+ * @brief Fog atmospheric effect settings.
  */
-R3DAPI float R3D_GetSSAOLightAffect(void);
-
-// ----------------------------------------
-// ENVIRONMENT: Bloom Config Functions
-// ----------------------------------------
+typedef struct R3D_EnvFog {
+    R3D_Fog mode;           ///< Fog distribution mode (default: R3D_FOG_DISABLED)
+    Color color;            ///< Fog tint color (default: white)
+    float start;            ///< Linear mode: distance where fog begins (default: 1.0)
+    float end;              ///< Linear mode: distance of full fog density (default: 50.0)
+    float density;          ///< Exponential modes: fog thickness factor (default: 0.05)
+    float skyAffect;        ///< Fog influence on skybox [0-1] (default: 0.5)
+} R3D_EnvFog;
 
 /**
- * @brief Sets the bloom mode.
- *
- * This function configures the bloom effect mode, which determines how the bloom
- * effect is applied to the rendered scene.
+ * @brief Depth of Field (DoF) camera focus settings.
  *
- * @param mode The bloom mode to set.
- *
- * Default: R3D_BLOOM_DISABLED
+ * Blurs objects outside the focal plane.
  */
-R3DAPI void R3D_SetBloomMode(R3D_Bloom mode);
+typedef struct R3D_EnvDoF {
+    R3D_DoF mode;           ///< Enable/disable state (default: R3D_DOF_DISABLED)
+    float focusPoint;       ///< Focus distance in meters from camera (default: 10.0)
+    float focusScale;       ///< Depth of field depth: lower = shallower (default: 1.0)
+    float maxBlurSize;      ///< Maximum blur radius, similar to aperture (default: 20.0)
+    bool debugMode;         ///< Color-coded visualization: green=near, blue=far (default: false)
+} R3D_EnvDoF;
 
 /**
- * @brief Gets the current bloom mode.
- *
- * This function retrieves the bloom mode currently applied to the scene.
+ * @brief Tone mapping and exposure settings.
  *
- * @return The current bloom mode.
+ * Converts HDR colors to displayable LDR range.
  */
-R3DAPI R3D_Bloom R3D_GetBloomMode(void);
+typedef struct R3D_EnvTonemap {
+    R3D_Tonemap mode;       ///< Tone mapping algorithm (default: R3D_TONEMAP_LINEAR)
+    float exposure;         ///< Scene brightness multiplier (default: 1.0)
+    float white;            ///< Reference white point (not used for AGX) (default: 1.0)
+} R3D_EnvTonemap;
 
 /**
- * @brief Sets the number of mipmap levels used for the bloom effect.
- *
- * This function controls how many mipmap level are generated for use in the bloom effect.
- * More levels will give a smoother and more widely dispersed effect, while less mipmaps
- * can provide a tighter effect. Setting this value to 0 will result in the maximum
- * possible amount of levels to be used. Use of this function will rebuild the
- * mipmaps and may give a one time performance hit.
+ * @brief Color grading adjustments.
  *
- * @param value The number of mipmap level to be used for the bloom effect.
- *
- * Default: 7
+ * Final color correction applied after all other effects.
  */
-R3DAPI void R3D_SetBloomLevels(int value);
+typedef struct R3D_EnvColor {
+    float brightness;       ///< Overall brightness multiplier (default: 1.0)
+    float contrast;         ///< Contrast between dark and bright areas (default: 1.0)
+    float saturation;       ///< Color intensity (default: 1.0)
+} R3D_EnvColor;
 
 /**
- * @brief Gets the current amount of mipmap levels used for the bloom effect.
- *
- * This function retrieves the current amount of mipmap levels in use by the bloom effect.
+ * @brief Complete environment configuration structure.
  *
- * @return The number of mipmap level currently used for the bloom effect.
+ * Contains all rendering environment parameters: background, lighting, and post-processing effects.
+ * Initialize with R3D_ENVIRONMENT_BASE for default values.
  */
-R3DAPI int R3D_GetBloomLevels(void);
+typedef struct R3D_Environment {
+    R3D_EnvBackground background;   ///< Background and skybox settings
+    R3D_EnvAmbient    ambient;      ///< Ambient lighting configuration
+    R3D_EnvSSAO       ssao;         ///< Screen space ambient occlusion
+    R3D_EnvBloom      bloom;        ///< Bloom glow effect
+    R3D_EnvSSR        ssr;          ///< Screen space reflections
+    R3D_EnvFog        fog;          ///< Atmospheric fog
+    R3D_EnvDoF        dof;          ///< Depth of field focus effect
+    R3D_EnvTonemap    tonemap;      ///< HDR tone mapping
+    R3D_EnvColor      color;        ///< Color grading adjustments
+} R3D_Environment;
 
-/**
- * @brief Sets the bloom intensity.
- *
- * This function controls the strength of the bloom effect. Higher values result
- * in a more intense glow effect on bright areas of the scene.
- *
- * @param value The intensity value for bloom.
- *
- * Default: 0.05
- */
-R3DAPI void R3D_SetBloomIntensity(float value);
-
-/**
- * @brief Gets the current bloom intensity.
- *
- * This function retrieves the intensity value of the bloom effect.
- *
- * @return The current bloom intensity.
- */
-R3DAPI float R3D_GetBloomIntensity(void);
-
-/**
- * @brief Sets the bloom filter radius.
- *
- * Controls the radius of the blur filter applied during the upscaling stage
- * of the bloom effect. A larger radius results in a wider glow around bright
- * objects, creating a softer and more diffuse bloom. A value of 0 disables 
- * the filtering effect, preserving sharp bloom highlights.
- *
- * @param value The radius of the bloom filter (in pixels).
- *
- * Default: 1
- */
-R3DAPI void R3D_SetBloomFilterRadius(int value);
-
- /**
-  * @brief Gets the current bloom filter radius.
-  *
-  * Retrieves the current radius used for the bloom filter. This value determines
-  * how far the glow effect extends around bright areas in the scene.
-  *
-  * @return The current bloom filter radius.
-  */
-R3DAPI int R3D_GetBloomFilterRadius(void);
-
-/**
- * @brief Sets the bloom brightness threshold.
- *
- * Controls the brightness cutoff used during the downsampling stage of the
- * bloom effect. If the color channel with the brightest value is below the
- * set threshold the pixel will not be included in the bloom effect.
- *
- * @param value The lowest value to be included the bloom effect (in color value depending on implementation).
- *
- * Default: 0.0
- */
-R3DAPI void R3D_SetBloomThreshold(float value);
-
-/**
- * @brief Gets the bloom brightness threshold.
- *
- * Retrieves the current brightness cutoff used for the bloom effect. This value
- * determines if a pixel will be included in the bloom effect based on it's brightness.
- *
- * @return The current bloom brightness cutoff threshold.
- */
-R3DAPI float R3D_GetBloomThreshold(void);
-
-/**
- * @brief Sets the bloom brightness threshold's softness.
- *
- * Controls the softness of the cutoff between being include or excluded in the
- * bloom effect. A value of 0 will result in a hard transition between being
- * included or excluded, while larger values will give an increasingly
- * softer transition.
- *
- * @param value The value of of the bloom brightness threshold's softness.
- *
- * Default: 0.5
- */
-R3DAPI void R3D_SetBloomSoftThreshold(float value);
-
-/**
- * @brief Gets the current bloom brightness threshold's softness.
- *
- * Retrieves the softness of the brightness cutoff for the bloom effect.
- * This value determines the softness of the transition between being
- * included or excluded in the bloom effect
- *
- * @return The current bloom brightness threshold's softness.
- */
-R3DAPI float R3D_GetBloomSoftThreshold(void);
-
-// ----------------------------------------
-// ENVIRONMENT: SSR Config Functions
-// ----------------------------------------
-
-/**
- * @brief Enable or disable Screen Space Reflections (SSR).
- *
- * @param enabled Set to true to enable SSR, false to disable it.
- *
- * Default: false
- */
-R3DAPI void R3D_SetSSR(bool enabled);
-
-/**
- * @brief Check whether Screen Space Reflections (SSR) are enabled.
- *
- * @return true if SSR is enabled, false otherwise.
- */
-R3DAPI bool R3D_GetSSR(void);
-
-/**
- * @brief Set the maximum number of ray-marching steps for SSR.
- *
- * @param maxRaySteps The maximum number of steps taken while marching
- *        along the reflection ray. Higher values improve accuracy but
- *        increase GPU cost.
- *
- * Default: 64
- */
-R3DAPI void R3D_SetSSRMaxRaySteps(int maxRaySteps);
-
-/**
- * @brief Get the maximum number of ray-marching steps for SSR.
- *
- * @return The maximum ray-marching steps.
- */
-R3DAPI int R3D_GetSSRMaxRaySteps(void);
-
-/**
- * @brief Set the number of refinement steps for the binary search phase.
- *
- * @param binarySearchSteps The number of iterations used to refine
- *        the ray-surface intersection point after a hit is detected.
- *        More steps yield a more precise intersection.
- *
- * Default: 8
- */
-R3DAPI void R3D_SetSSRBinarySearchSteps(int binarySearchSteps);
-
-/**
- * @brief Get the number of refinement steps for the binary search phase.
- *
- * @return The number of binary search steps.
- */
-R3DAPI int R3D_GetSSRBinarySearchSteps(void);
-
-/**
- * @brief Set the maximum ray marching distance in view space units.
- *
- * @param rayMarchLength The maximum distance a reflection ray can travel.
- *        Larger values allow longer reflections but may cause artifacts.
- *
- * Default: 8.0
- */
-R3DAPI void R3D_SetSSRRayMarchLength(float rayMarchLength);
-
-/**
- * @brief Get the maximum ray marching distance.
- *
- * @return The maximum ray marching distance.
- */
-R3DAPI float R3D_GetSSRRayMarchLength(void);
-
-/**
- * @brief Set the SSR depth thickness tolerance.
- *
- * @param depthThickness The maximum depth difference allowed between
- *        the ray position and the scene depth to consider a valid hit.
- *        Larger values increase tolerance but can cause ghosting.
- *
- * Default: 0.2
- */
-R3DAPI void R3D_SetSSRDepthThickness(float depthThickness);
-
-/**
- * @brief Get the SSR depth thickness tolerance.
- *
- * @return The depth thickness value.
- */
-R3DAPI float R3D_GetSSRDepthThickness(void);
-
-/**
- * @brief Set the SSR depth tolerance.
- *
- * @param depthTolerance The negative margin allowed when comparing the
- *        ray position against the scene depth. This prevents false negatives
- *        due to floating-point errors or slight inconsistencies in depth
- *        reconstruction.
- *
- * In practice, a hit is accepted if:
- *    -depthTolerance <= depthDiff < depthThickness
- *
- * Smaller values increase strictness but may cause missed intersections,
- * while larger values reduce artifacts but can introduce ghosting.
- *
- * Default: 0.005
- */
-R3DAPI void R3D_SetSSRDepthTolerance(float depthTolerance);
-
-/**
- * @brief Get the SSR depth tolerance.
- *
- * @return The depth tolerance value.
- */
-R3DAPI float R3D_GetSSRDepthTolerance(void);
-
-/**
- * @brief Set the fade range near the screen edges to reduce artifacts.
- *
- * @param start Normalized distance from the screen center where edge fading begins (0.0–1.0).
- * @param end   Normalized distance where fading is complete (0.0–1.0).
- *
- * Pixels outside this range will have their reflections gradually
- * faded out to avoid hard cutoffs near the borders.
- *
- * Default: start = 0.7, end = 1.0
- */
-R3DAPI void R3D_SetSSRScreenEdgeFade(float start, float end);
-
-/**
- * @brief Get the screen edge fade range.
- *
- * @param start Pointer to receive the fade start value.
- * @param end   Pointer to receive the fade end value.
- */
-R3DAPI void R3D_GetSSRScreenEdgeFade(float* start, float* end);
-
-// ----------------------------------------
-// ENVIRONMENT: Fog Config Functions
-// ----------------------------------------
-
-/**
- * @brief Sets the fog mode.
- *
- * This function defines the type of fog effect applied to the scene.
- * Different modes may provide linear, exponential, or volumetric fog effects.
- *
- * @param mode The fog mode to set.
- *
- * Default: R3D_FOG_DISABLED
- */
-R3DAPI void R3D_SetFogMode(R3D_Fog mode);
-
-/**
- * @brief Gets the current fog mode.
- *
- * This function retrieves the fog mode currently applied to the scene.
- *
- * @return The current fog mode.
- */
-R3DAPI R3D_Fog R3D_GetFogMode(void);
-
-/**
- * @brief Sets the color of the fog.
- *
- * This function defines the color of the fog effect applied to the scene.
- * The fog color blends with objects as they are affected by fog.
- *
- * @param color The color to set for the fog.
- *
- * Default: WHITE
- */
-R3DAPI void R3D_SetFogColor(Color color);
-
-/**
- * @brief Gets the current fog color.
- *
- * This function retrieves the color currently used for the fog effect.
- *
- * @return The current fog color.
- */
-R3DAPI Color R3D_GetFogColor(void);
-
-/**
- * @brief Sets the start distance of the fog.
- *
- * This function defines the distance from the camera at which fog begins to appear.
- * Objects closer than this distance will not be affected by fog.
- *
- * @param value The start distance for the fog effect.
- *
- * Default: 1.0
- */
-R3DAPI void R3D_SetFogStart(float value);
-
-/**
- * @brief Gets the current fog start distance.
- *
- * This function retrieves the distance at which the fog begins to be applied.
- *
- * @return The current fog start distance.
- */
-R3DAPI float R3D_GetFogStart(void);
-
-/**
- * @brief Sets the end distance of the fog.
- *
- * This function defines the distance from the camera at which fog reaches full intensity.
- * Objects beyond this distance will be completely covered by fog.
- *
- * @param value The end distance for the fog effect.
- *
- * Default: 50.0
- */
-R3DAPI void R3D_SetFogEnd(float value);
-
-/**
- * @brief Gets the current fog end distance.
- *
- * This function retrieves the distance at which the fog is fully applied.
- *
- * @return The current fog end distance.
- */
-R3DAPI float R3D_GetFogEnd(void);
-
-/**
- * @brief Sets the density of the fog.
- *
- * This function controls how thick the fog appears. Higher values result in
- * denser fog, making objects fade out more quickly.
- *
- * @param value The density of the fog (higher values increase fog thickness).
- *
- * Default: 0.05
- */
-R3DAPI void R3D_SetFogDensity(float value);
-
-/**
- * @brief Gets the current fog density.
- *
- * This function retrieves the current density of the fog.
- *
- * @return The current fog density.
- */
-R3DAPI float R3D_GetFogDensity(void);
-
-/**
- * @brief Sets how much the fog affects the sky.
- *
- * This function controls the influence of fog on the sky color and visibility. 
- * A higher value makes the fog blend more strongly with the sky, reducing its clarity.
- *
- * @param value The fog effect on the sky, in the range [0.0f, 1.0f] 
- *              (0 = no effect, 1 = maximum blending).
- *
- * Default: 0.5
- */
-R3DAPI void R3D_SetFogSkyAffect(float value);
-
-/**
- * @brief Gets the current fog effect on the sky.
- *
- * This function retrieves the current influence of fog on the sky.
- *
- * @return The current fog-sky affect value, in the range [0.0f, 1.0f].
- */
-R3DAPI float R3D_GetFogSkyAffect(void);
-
-// ----------------------------------------
-// ENVIRONMENT: Depth of Field (DoF) Functions
-// ----------------------------------------
-
-/**
- * @brief Enables or disables the depth of field post-process.
- *
- * @param mode The depth of field mode to set.
- *
- * Default: R3D_DOF_DISABLED
- */
-R3DAPI void R3D_SetDofMode(R3D_Dof mode);
-
-/**
- * @brief Gets the current depth of field mode.
- *
- * @return The current depth of field mode.
- */
-R3DAPI R3D_Dof R3D_GetDofMode(void);
-
-/**
- * @brief Sets the focus point in world space.
- *
- * This function defines the distance (in meters) from the camera where
- * objects will be in perfect focus. Objects closer or farther will be blurred.
- *
- * @param value The focus point distance in meters.
- *
- * Default: 10.0
- */
-R3DAPI void R3D_SetDofFocusPoint(float value);
-
-/**
- * @brief Gets the current focus point.
- *
- * @return The focus point distance in meters.
- */
-R3DAPI float R3D_GetDofFocusPoint(void);
-
-/**
- * @brief Sets the focus scale.
- *
- * This function controls how shallow the depth of field effect is.
- * Lower values create a shallower depth of field with more blur,
- * while higher values create a deeper depth of field with less blur.
- *
- * @param value The focus scale value.
- *
- * Default: 1.0
- */
-R3DAPI void R3D_SetDofFocusScale(float value);
-
-/**
- * @brief Gets the current focus scale.
- *
- * @return The current focus scale value.
- */
-R3DAPI float R3D_GetDofFocusScale(void);
-
-/**
- * @brief Sets the maximum blur size.
- *
- * This function controls the maximum amount of blur applied to out-of-focus
- * areas. This value is similar to the lens aperture size, larger values
- * create more pronounced blur effects.
- *
- * @param value The maximum blur size value.
- *
- * Default: 20.0
- */
-R3DAPI void R3D_SetDofMaxBlurSize(float value);
-
-/**
- * @brief Gets the current maximum blur size.
- *
- * @return The current maximum blur size value.
- */
-R3DAPI float R3D_GetDofMaxBlurSize(void);
-
-/**
- * @brief Enables or disables depth-of-field debug mode.
- *
- * In debug mode, the scene uses color coding:
- * - Green: near blur
- * - Black: sharp areas
- * - Blue: far blur
- *
- * @param enabled true to enable, false to disable.
- *
- * Default: false
- */
-R3DAPI void R3D_SetDofDebugMode(bool enabled);
-
-/**
- * @brief Gets the current debug mode state.
- *
- * @return True if debug mode is enabled, false otherwise.
- */
-R3DAPI bool R3D_GetDofDebugMode(void);
-
-// ----------------------------------------
-// ENVIRONMENT: Tonemap Config Functions
-// ----------------------------------------
-
-/**
- * @brief Sets the tonemapping mode.
- *
- * This function defines the tonemapping algorithm applied to the final rendered image.
- * Different tonemap modes affect color balance, brightness compression, and overall
- * scene appearance.
- *
- * @param mode The tonemap mode to set.
- *
- * Default: R3D_TONEMAP_LINEAR
- */
-R3DAPI void R3D_SetTonemapMode(R3D_Tonemap mode);
-
-/**
- * @brief Gets the current tonemapping mode.
- *
- * This function retrieves the tonemap mode currently applied to the scene.
- *
- * @return The current tonemap mode.
- */
-R3DAPI R3D_Tonemap R3D_GetTonemapMode(void);
-
-/**
- * @brief Sets the exposure level for tonemapping.
- *
- * This function adjusts the exposure level used in tonemapping, affecting
- * the overall brightness of the rendered scene.
- *
- * @param value The exposure value (higher values make the scene brighter).
- *
- * Default: 1.0
- */
-R3DAPI void R3D_SetTonemapExposure(float value);
-
-/**
- * @brief Gets the current tonemap exposure level.
- *
- * This function retrieves the current exposure setting used in tonemapping.
- *
- * @return The current tonemap exposure value.
- */
-R3DAPI float R3D_GetTonemapExposure(void);
-
-/**
- * @brief Sets the white point for tonemapping.
- *
- * This function defines the reference white level, which determines how bright
- * areas of the scene are mapped to the final output.
- *
- * @param value The white point value.
- *
- * Default: 1.0
- */
-R3DAPI void R3D_SetTonemapWhite(float value);
-
-/**
- * @brief Gets the current tonemap white point.
- *
- * This function retrieves the white point setting used in tonemapping.
- *
- * @return The current tonemap white value.
- */
-R3DAPI float R3D_GetTonemapWhite(void);
-
-// ----------------------------------------
-// ENVIRONMENT: Color Adjustment Functions
-// ----------------------------------------
+// ========================================
+// HELPER MACROS
+// ========================================
 
 /**
- * @brief Sets the global brightness adjustment.
- *
- * This function controls the brightness of the final rendered image.
- * Higher values make the image brighter, while lower values darken it.
+ * @brief Quick read access to environment members.
  *
- * @param value The brightness adjustment value.
+ * @param member The environment member path (e.g., bloom.intensity)
+ * @return The current value of the specified member
  *
- * Default: 1.0
+ * Example: `float intensity = R3D_ENVIRONMENT_GET(bloom.intensity);`
  */
-R3DAPI void R3D_SetBrightness(float value);
+#define R3D_ENVIRONMENT_GET(member)         (R3D_GetEnvironment()->member)
 
 /**
- * @brief Gets the current brightness level.
+ * @brief Quick write access to environment members.
  *
- * This function retrieves the brightness setting applied to the scene.
+ * @param member The environment member path (e.g., bloom.intensity)
+ * @param ... The value to assign
  *
- * @return The current brightness value.
+ * Example: `R3D_ENVIRONMENT_SET(bloom.intensity, 0.05f);`
  */
-R3DAPI float R3D_GetBrightness(void);
+#define R3D_ENVIRONMENT_SET(member, ...)    ((R3D_GetEnvironment()->member) = (__VA_ARGS__))
 
-/**
- * @brief Sets the global contrast adjustment.
- *
- * This function controls the contrast of the final rendered image.
- * Higher values increase the difference between dark and bright areas.
- *
- * @param value The contrast adjustment value.
- *
- * Default: 1.0
- */
-R3DAPI void R3D_SetContrast(float value);
+// ========================================
+// PUBLIC API
+// ========================================
 
-/**
- * @brief Gets the current contrast level.
- *
- * This function retrieves the contrast setting applied to the scene.
- *
- * @return The current contrast value.
- */
-R3DAPI float R3D_GetContrast(void);
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 /**
- * @brief Sets the global saturation adjustment.
- *
- * This function controls the color intensity of the final rendered image.
- * Higher values make colors more vibrant, while lower values desaturate them.
+ * @brief Retrieves a pointer to the current environment configuration.
  *
- * @param value The saturation adjustment value.
+ * Provides direct read/write access to environment settings.
+ * Modifications take effect immediately.
  *
- * Default: 1.0
+ * @return Pointer to the active R3D_Environment structure
  */
-R3DAPI void R3D_SetSaturation(float value);
+R3DAPI R3D_Environment* R3D_GetEnvironment(void);
 
 /**
- * @brief Gets the current saturation level.
+ * @brief Replaces the entire environment configuration.
  *
- * This function retrieves the saturation setting applied to the scene.
+ * Copies all settings from the provided structure to the active environment.
+ * Useful for switching between presets or restoring saved states.
  *
- * @return The current saturation value.
+ * @param env Pointer to the R3D_Environment structure to copy from
  */
-R3DAPI float R3D_GetSaturation(void);
+R3DAPI void R3D_SetEnvironment(const R3D_Environment* env);
 
 #ifdef __cplusplus
 } // extern "C"

+ 1 - 1
include/r3d/r3d_lighting.h

@@ -9,7 +9,7 @@
 #ifndef R3D_LIGHTING_H
 #define R3D_LIGHTING_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 #include <stdint.h>
 

+ 1 - 1
include/r3d/r3d_material.h

@@ -9,7 +9,7 @@
 #ifndef R3D_MATERIAL_H
 #define R3D_MATERIAL_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 
 /**

+ 1 - 1
include/r3d/r3d_mesh.h

@@ -10,8 +10,8 @@
 #define R3D_MESH_H
 
 #include "./r3d_mesh_data.h"
+#include "./r3d_platform.h"
 #include "./r3d_core.h"
-#include "./r3d_api.h"
 #include <raylib.h>
 
 /**

+ 1 - 1
include/r3d/r3d_mesh_data.h

@@ -9,7 +9,7 @@
 #ifndef R3D_MESH_DATA_H
 #define R3D_MESH_DATA_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 #include <stdint.h>
 

+ 1 - 0
include/r3d/r3d_model.h

@@ -12,6 +12,7 @@
 #include "./r3d_animation.h"
 #include "./r3d_material.h"
 #include "./r3d_skeleton.h"
+#include "./r3d_platform.h"
 #include "./r3d_mesh.h"
 
 /**

+ 1 - 1
include/r3d/r3d_particles.h

@@ -9,8 +9,8 @@
 #ifndef R3D_PARTICLES_H
 #define R3D_PARTICLES_H
 
+#include "./r3d_platform.h"
 #include "./r3d_curves.h"
-#include "./r3d_api.h"
 #include <raylib.h>
 
 /**

+ 8 - 0
include/r3d/r3d_api.h → include/r3d/r3d_platform.h

@@ -28,4 +28,12 @@
 #   define R3DAPI extern
 #endif
 
+#ifndef R3D_LITERAL
+#   ifdef __cplusplus
+#       define R3D_LITERAL(type) type
+#   else
+#       define R3D_LITERAL(type) (type)
+#   endif
+#endif
+
 #endif // R3D_API_H

+ 1 - 1
include/r3d/r3d_skeleton.h

@@ -9,7 +9,7 @@
 #ifndef R3D_SKELETON_H
 #define R3D_SKELETON_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 
 /**

+ 1 - 1
include/r3d/r3d_skybox.h

@@ -9,7 +9,7 @@
 #ifndef R3D_SKYBOX_H
 #define R3D_SKYBOX_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 
 /**

+ 1 - 1
include/r3d/r3d_utils.h

@@ -9,7 +9,7 @@
 #ifndef R3D_UTILS_H
 #define R3D_UTILS_H
 
-#include "./r3d_api.h"
+#include "./r3d_platform.h"
 #include <raylib.h>
 
 /**

+ 9 - 7
shaders/deferred/ambient.frag

@@ -35,8 +35,8 @@ uniform samplerCube uCubeIrradiance;
 uniform samplerCube uCubePrefilter;
 uniform sampler2D uTexBrdfLut;
 uniform vec4 uQuatSkybox;
-uniform float uSkyboxAmbientIntensity;
-uniform float uSkyboxReflectIntensity;
+uniform float uAmbientEnergy;
+uniform float uReflectEnergy;
 
 uniform vec3 uViewPosition;
 uniform mat4 uMatInvProj;
@@ -100,7 +100,7 @@ void main()
 
     vec3 Nr = M_Rotate3D(N, uQuatSkybox);
     FragDiffuse = kD * texture(uCubeIrradiance, Nr).rgb;
-    FragDiffuse *= occlusion * uSkyboxAmbientIntensity;
+    FragDiffuse *= occlusion * uAmbientEnergy;
 
     /* Skybox reflection - IBL specular amélioré */
 
@@ -118,7 +118,7 @@ void main()
     float edgeFade = mix(1.0, pow(NdotV, 0.5), roughness);
     specular *= edgeFade;
 
-    FragSpecular = specular * uSkyboxReflectIntensity;
+    FragSpecular = specular * uReflectEnergy;
 }
 
 #else
@@ -132,7 +132,8 @@ noperspective in vec2 vTexCoord;
 uniform sampler2D uTexAlbedo;
 uniform sampler2D uTexSSAO;
 uniform sampler2D uTexORM;
-uniform vec3 uAmbientLight;
+uniform vec3 uAmbientColor;
+uniform float uAmbientEnergy;
 
 /* === Fragments === */
 
@@ -164,8 +165,9 @@ void main()
 
     vec3 F0 = PBR_ComputeF0(metalness, 0.5, albedo);
     vec3 kD = (1.0 - F0) * (1.0 - metalness);
-    vec3 ambient = kD * uAmbientLight;
-    ambient += F0 * uAmbientLight;
+    vec3 ambient = kD * uAmbientColor;
+    ambient += F0 * uAmbientColor;
+    ambient *= uAmbientEnergy;
     ambient *= occlusion;
 
     /* --- Output --- */

+ 7 - 7
shaders/scene/forward.frag

@@ -64,7 +64,7 @@ uniform float uOcclusion;
 uniform float uRoughness;
 uniform float uMetalness;
 
-uniform vec3 uAmbientLight;
+uniform vec3 uAmbientColor;
 uniform vec3 uEmissionColor;
 
 uniform samplerCube uCubeIrradiance;
@@ -72,8 +72,8 @@ uniform samplerCube uCubePrefilter;
 uniform sampler2D uTexBrdfLut;
 uniform vec4 uQuatSkybox;
 uniform bool uHasSkybox;
-uniform float uSkyboxAmbientIntensity;
-uniform float uSkyboxReflectIntensity;
+uniform float uAmbientEnergy;
+uniform float uReflectEnergy;
 
 uniform Light uLights[LIGHT_FORWARD_COUNT];
 
@@ -336,7 +336,7 @@ void main()
 
     /* Compute ambient - (IBL diffuse) */
 
-    vec3 ambient = uAmbientLight;
+    vec3 ambient = uAmbientColor;
 
     if (uHasSkybox)
     {
@@ -345,7 +345,7 @@ void main()
 
         vec3 Nr = M_Rotate3D(N, uQuatSkybox);
         ambient = kD * texture(uCubeIrradiance, Nr).rgb;
-        ambient *= uSkyboxAmbientIntensity;
+        ambient *= uAmbientEnergy;
     }
     else
     {
@@ -354,7 +354,7 @@ void main()
         //       but it's to at least simulate some specularity, otherwise the 
         //       result would look poor for metals...
         ambient = (1.0 - F0) * (1.0 - metalness) * ambient;
-        ambient += F0 * uAmbientLight;
+        ambient += F0 * uAmbientColor * uAmbientEnergy;
     }
 
     /* Compute ambient occlusion map */
@@ -384,7 +384,7 @@ void main()
         float edgeFade = mix(1.0, pow(cNdotV, 0.5), roughness);
         spec *= edgeFade;
 
-        specular += spec * uSkyboxReflectIntensity;
+        specular += spec * uReflectEnergy;
     }
 
     /* Compute the final diffuse color, including ambient and diffuse lighting contributions */

+ 2 - 2
shaders/scene/skybox.frag

@@ -11,11 +11,11 @@
 in vec3 vPosition;
 
 uniform samplerCube uCubeSky;
-uniform float uSkyIntensity;
+uniform float uSkyEnergy;
 
 layout(location = 0) out vec3 FragColor;
 
 void main()
 {
-    FragColor = texture(uCubeSky, vPosition).rgb * uSkyIntensity;
+    FragColor = texture(uCubeSky, vPosition).rgb * uSkyEnergy;
 }

+ 44 - 0
src/modules/r3d_cache.c

@@ -0,0 +1,44 @@
+/* r3d_draw.c -- Internal R3D cache module.
+ *
+ * Copyright (c) 2025 Le Juez Victor
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * For conditions of distribution and use, see accompanying LICENSE file.
+ */
+
+#include "./r3d_cache.h"
+#include "r3d/r3d_environment.h"
+#include <raymath.h>
+
+// ========================================
+// MODULE STATE
+// ========================================
+
+struct r3d_mod_cache R3D_MOD_CACHE;
+
+// ========================================
+// MODULE FUNCTIONS
+// ========================================
+
+bool r3d_mod_cache_init(R3D_Flags flags)
+{
+    R3D_MOD_CACHE.matCubeViews[0] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  1.0f,  0.0f,  0.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
+    R3D_MOD_CACHE.matCubeViews[1] = MatrixLookAt((Vector3) { 0 }, (Vector3) { -1.0f,  0.0f,  0.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
+    R3D_MOD_CACHE.matCubeViews[2] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f,  1.0f,  0.0f }, (Vector3) { 0.0f,  0.0f,  1.0f });
+    R3D_MOD_CACHE.matCubeViews[3] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f, -1.0f,  0.0f }, (Vector3) { 0.0f,  0.0f, -1.0f });
+    R3D_MOD_CACHE.matCubeViews[4] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f,  0.0f,  1.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
+    R3D_MOD_CACHE.matCubeViews[5] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f,  0.0f, -1.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
+
+    R3D_MOD_CACHE.environment = R3D_ENVIRONMENT_BASE;
+
+    R3D_MOD_CACHE.textureFilter = TEXTURE_FILTER_TRILINEAR;
+    R3D_MOD_CACHE.layers = R3D_LAYER_ALL;
+    R3D_MOD_CACHE.state = flags;
+
+    return true;
+}
+
+void r3d_mod_cache_quit(void)
+{
+    (void)0; // do nothing for now...
+}

+ 92 - 0
src/modules/r3d_cache.h

@@ -0,0 +1,92 @@
+/* r3d_cache.h -- Internal R3D cache module.
+ *
+ * Copyright (c) 2025 Le Juez Victor
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * For conditions of distribution and use, see accompanying LICENSE file.
+ */
+
+#ifndef R3D_MODULE_CACHE_H
+#define R3D_MODULE_CACHE_H
+
+#include <r3d/r3d_environment.h>
+#include <r3d/r3d_core.h>
+
+#include "../details/r3d_frustum.h"
+
+// ========================================
+// HELPER MACROS
+// ========================================
+
+/*
+ * Read access to cached module members.
+ * No added value, but it allows for consistency.
+ */
+#define R3D_CACHE_GET(member)                   \
+    (R3D_MOD_CACHE.member)
+
+/*
+ * Write access to cached module members.
+ * No added value, but it allows for consistency.
+ */
+#define R3D_CACHE_SET(member, value)            \
+    (R3D_MOD_CACHE.member) = (value)
+
+/*
+ * Check if all specified flags are set.
+ */
+#define R3D_CACHE_FLAGS_HAS(flags, mask)        \
+    (((R3D_MOD_CACHE.flags) & (mask)) == (mask))
+
+/*
+ * Set specified flags (bitwise OR).
+ */
+#define R3D_CACHE_FLAGS_ASSIGN(flags, mask) do {\
+    (R3D_MOD_CACHE.flags) |= (mask);            \
+} while (0)
+
+/*
+ * Clear specified flags (bitwise AND NOT).
+ */
+#define R3D_CACHE_FLAGS_CLEAR(flags, mask) do { \
+    (R3D_MOD_CACHE.flags) &= ~(mask);           \
+} while (0)
+
+// ========================================
+// MODULE STATE
+// ========================================
+
+/*
+ * Current viewport state including view frustum and transforms.
+ */
+typedef struct {
+    r3d_frustum_t frustum;          //< View frustum for culling
+    Vector3 viewPosition;           //< Camera position in world space
+    Matrix view, invView;           //< View matrix and its inverse
+    Matrix proj, invProj;           //< Projection matrix and its inverse
+    Matrix viewProj;                //< Combined view-projection matrix
+} r3d_viewport_t;
+
+/*
+ * Global cache for frequently accessed renderer state.
+ * Reduces parameter passing and provides centralized access to common data.
+ */
+extern struct r3d_mod_cache {
+    R3D_Environment environment;    //< Current environment settings
+    r3d_viewport_t viewport;        //< Current viewport state
+
+    TextureFilter textureFilter;    //< Default texture filter for model loading
+    Matrix matCubeViews[6];         //< Pre-computed view matrices for cubemap faces
+
+    R3D_Layer layers;               //< Active rendering layers
+    R3D_Flags state;                //< Renderer state flags
+} R3D_MOD_CACHE;
+
+// ========================================
+// MODULE FUNCTIONS
+// ========================================
+
+bool r3d_mod_cache_init(R3D_Flags flags);
+void r3d_mod_cache_quit(void);
+
+#endif // R3D_MODULE_CACHE_H

+ 8 - 7
src/modules/r3d_shader.c

@@ -308,15 +308,15 @@ void r3d_mod_shader_load_scene_forward(void)
     GET_LOCATION(scene.forward, uOcclusion);
     GET_LOCATION(scene.forward, uRoughness);
     GET_LOCATION(scene.forward, uMetalness);
-    GET_LOCATION(scene.forward, uAmbientLight);
+    GET_LOCATION(scene.forward, uAmbientColor);
     GET_LOCATION(scene.forward, uEmissionColor);
     GET_LOCATION(scene.forward, uCubeIrradiance);
     GET_LOCATION(scene.forward, uCubePrefilter);
     GET_LOCATION(scene.forward, uTexBrdfLut);
     GET_LOCATION(scene.forward, uQuatSkybox);
     GET_LOCATION(scene.forward, uHasSkybox);
-    GET_LOCATION(scene.forward, uSkyboxAmbientIntensity);
-    GET_LOCATION(scene.forward, uSkyboxReflectIntensity);
+    GET_LOCATION(scene.forward, uAmbientEnergy);
+    GET_LOCATION(scene.forward, uReflectEnergy);
     GET_LOCATION(scene.forward, uAlphaCutoff);
     GET_LOCATION(scene.forward, uViewPosition);
     GET_LOCATION(scene.forward, uFar);
@@ -376,7 +376,7 @@ void r3d_mod_shader_load_scene_skybox(void)
     GET_LOCATION(scene.skybox, uMatProj);
     GET_LOCATION(scene.skybox, uMatView);
     GET_LOCATION(scene.skybox, uRotation);
-    GET_LOCATION(scene.skybox, uSkyIntensity);
+    GET_LOCATION(scene.skybox, uSkyEnergy);
     GET_LOCATION(scene.skybox, uCubeSky);
 
     USE_SHADER(scene.skybox);
@@ -486,8 +486,8 @@ void r3d_mod_shader_load_deferred_ambient_ibl(void)
     GET_LOCATION(deferred.ambientIbl, uCubePrefilter);
     GET_LOCATION(deferred.ambientIbl, uTexBrdfLut);
     GET_LOCATION(deferred.ambientIbl, uQuatSkybox);
-    GET_LOCATION(deferred.ambientIbl, uSkyboxAmbientIntensity);
-    GET_LOCATION(deferred.ambientIbl, uSkyboxReflectIntensity);
+    GET_LOCATION(deferred.ambientIbl, uAmbientEnergy);
+    GET_LOCATION(deferred.ambientIbl, uReflectEnergy);
     GET_LOCATION(deferred.ambientIbl, uViewPosition);
     GET_LOCATION(deferred.ambientIbl, uMatInvProj);
     GET_LOCATION(deferred.ambientIbl, uMatInvView);
@@ -513,7 +513,8 @@ void r3d_mod_shader_load_deferred_ambient(void)
     GET_LOCATION(deferred.ambient, uTexAlbedo);
     GET_LOCATION(deferred.ambient, uTexSSAO);
     GET_LOCATION(deferred.ambient, uTexORM);
-    GET_LOCATION(deferred.ambient, uAmbientLight);
+    GET_LOCATION(deferred.ambient, uAmbientColor);
+    GET_LOCATION(deferred.ambient, uAmbientEnergy);
 
     USE_SHADER(deferred.ambient);
 

+ 8 - 7
src/modules/r3d_shader.h

@@ -327,15 +327,15 @@ typedef struct {
     r3d_shader_uniform_float_t uOcclusion;
     r3d_shader_uniform_float_t uRoughness;
     r3d_shader_uniform_float_t uMetalness;
-    r3d_shader_uniform_vec3_t uAmbientLight;
+    r3d_shader_uniform_vec3_t uAmbientColor;
     r3d_shader_uniform_vec3_t uEmissionColor;
     r3d_shader_uniform_samplerCube_t uCubeIrradiance;
     r3d_shader_uniform_samplerCube_t uCubePrefilter;
     r3d_shader_uniform_sampler2D_t uTexBrdfLut;
     r3d_shader_uniform_vec4_t uQuatSkybox;
     r3d_shader_uniform_int_t uHasSkybox;
-    r3d_shader_uniform_float_t uSkyboxAmbientIntensity;
-    r3d_shader_uniform_float_t uSkyboxReflectIntensity;
+    r3d_shader_uniform_float_t uAmbientEnergy;
+    r3d_shader_uniform_float_t uReflectEnergy;
     struct {
         r3d_shader_uniform_vec3_t color;
         r3d_shader_uniform_vec3_t position;
@@ -399,7 +399,7 @@ typedef struct {
     r3d_shader_uniform_mat4_t uMatProj;
     r3d_shader_uniform_mat4_t uMatView;
     r3d_shader_uniform_vec4_t uRotation;
-    r3d_shader_uniform_float_t uSkyIntensity;
+    r3d_shader_uniform_float_t uSkyEnergy;
     r3d_shader_uniform_samplerCube_t uCubeSky;
 } r3d_shader_scene_skybox_t;
 
@@ -414,8 +414,8 @@ typedef struct {
     r3d_shader_uniform_samplerCube_t uCubePrefilter;
     r3d_shader_uniform_sampler2D_t uTexBrdfLut;
     r3d_shader_uniform_vec4_t uQuatSkybox;
-    r3d_shader_uniform_float_t uSkyboxAmbientIntensity;
-    r3d_shader_uniform_float_t uSkyboxReflectIntensity;
+    r3d_shader_uniform_float_t uAmbientEnergy;
+    r3d_shader_uniform_float_t uReflectEnergy;
     r3d_shader_uniform_vec3_t uViewPosition;
     r3d_shader_uniform_mat4_t uMatInvProj;
     r3d_shader_uniform_mat4_t uMatInvView;
@@ -426,7 +426,8 @@ typedef struct {
     r3d_shader_uniform_sampler2D_t uTexAlbedo;
     r3d_shader_uniform_sampler2D_t uTexSSAO;
     r3d_shader_uniform_sampler2D_t uTexORM;
-    r3d_shader_uniform_vec3_t uAmbientLight;
+    r3d_shader_uniform_vec3_t uAmbientColor;
+    r3d_shader_uniform_float_t uAmbientEnergy;
 } r3d_shader_deferred_ambient_t;
 
 typedef struct {

+ 16 - 101
src/r3d_core.c

@@ -21,88 +21,22 @@
 #include "./modules/r3d_target.h"
 #include "./modules/r3d_shader.h"
 #include "./modules/r3d_light.h"
+#include "./modules/r3d_cache.h"
 #include "./modules/r3d_draw.h"
-#include "./r3d_state.h"
 
 // ========================================
 // PUBLIC API
 // ========================================
 
-void R3D_Init(int resWidth, int resHeight, unsigned int flags)
+void R3D_Init(int resWidth, int resHeight, R3D_Flags flags)
 {
-    // Set parameter flags
-    R3D.state.flags = flags;
-
-    // Environment data
-    R3D.env.backgroundColor = (Vector4) {0.2f, 0.2f, 0.2f, 1.0f};
-    R3D.env.ambientColor = (Color) {0, 0, 0, 255};
-    R3D.env.ambientEnergy = 1.0f;
-    R3D.env.ambientLight = (Vector3) {0.0f, 0.0f, 0.0f};
-    R3D.env.quatSky = QuaternionIdentity();
-    R3D.env.useSky = false;
-    R3D.env.skyBackgroundIntensity = 1.0f;
-    R3D.env.skyAmbientIntensity = 1.0f;
-    R3D.env.skyReflectIntensity = 1.0f;
-    R3D.env.ssaoEnabled = false;
-    R3D.env.ssaoRadius = 0.5f;
-    R3D.env.ssaoBias = 0.025f;
-    R3D.env.ssaoIterations = 1;
-    R3D.env.ssaoIntensity = 1.0f;
-    R3D.env.ssaoPower = 1.0f;
-    R3D.env.ssaoLightAffect = 0.0f;
-    R3D.env.bloomMode = R3D_BLOOM_DISABLED;
-    R3D.env.bloomLevels = 7;
-    R3D.env.bloomIntensity = 0.05f;
-    R3D.env.bloomFilterRadius = 1;
-    R3D.env.bloomThreshold = 0.0f;
-    R3D.env.bloomSoftThreshold = 0.5f;
-    R3D.env.fogMode = R3D_FOG_DISABLED;
-    R3D.env.ssrEnabled = false;
-    R3D.env.ssrMaxRaySteps = 64;
-    R3D.env.ssrBinarySearchSteps = 8;
-    R3D.env.ssrRayMarchLength = 8.0f;
-    R3D.env.ssrDepthThickness = 0.2f;
-    R3D.env.ssrDepthTolerance = 0.005f;
-    R3D.env.ssrEdgeFadeStart = 0.7f;
-    R3D.env.ssrEdgeFadeEnd = 1.0f;
-    R3D.env.fogColor = (Vector3) { 1.0f, 1.0f, 1.0f };
-    R3D.env.fogStart = 1.0f;
-    R3D.env.fogEnd = 50.0f;
-    R3D.env.fogDensity = 0.05f;
-    R3D.env.fogSkyAffect = 0.5f;
-    R3D.env.dofMode = R3D_DOF_DISABLED;
-    R3D.env.dofFocusPoint = 10.0f;
-    R3D.env.dofFocusScale = 1.0f;
-    R3D.env.dofMaxBlurSize = 20.0f;
-    R3D.env.dofDebugMode = false;
-    R3D.env.tonemapMode = R3D_TONEMAP_LINEAR;
-    R3D.env.tonemapExposure = 1.0f;
-    R3D.env.tonemapWhite = 1.0f;
-    R3D.env.brightness = 1.0f;
-    R3D.env.contrast = 1.0f;
-    R3D.env.saturation = 1.0f;
-
-    // Init default loading parameters
-    R3D.state.loading.textureFilter = TEXTURE_FILTER_TRILINEAR;
-
-    // Init default rendering layers
-    R3D.state.layers = R3D_LAYER_ALL;
-
-    // Init misc data
-    R3D.misc.matCubeViews[0] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  1.0f,  0.0f,  0.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
-    R3D.misc.matCubeViews[1] = MatrixLookAt((Vector3) { 0 }, (Vector3) { -1.0f,  0.0f,  0.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
-    R3D.misc.matCubeViews[2] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f,  1.0f,  0.0f }, (Vector3) { 0.0f,  0.0f,  1.0f });
-    R3D.misc.matCubeViews[3] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f, -1.0f,  0.0f }, (Vector3) { 0.0f,  0.0f, -1.0f });
-    R3D.misc.matCubeViews[4] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f,  0.0f,  1.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
-    R3D.misc.matCubeViews[5] = MatrixLookAt((Vector3) { 0 }, (Vector3) {  0.0f,  0.0f, -1.0f }, (Vector3) { 0.0f, -1.0f,  0.0f });
-
-    // Initialize modules
     r3d_mod_primitive_init();
     r3d_mod_texture_init();
     r3d_mod_storage_init();
     r3d_mod_target_init(resWidth, resHeight);
     r3d_mod_shader_init();
     r3d_mod_light_init();
+    r3d_mod_cache_init(flags);
     r3d_mod_draw_init();
 
     // Defines suitable clipping plane distances for r3d
@@ -117,42 +51,23 @@ void R3D_Close(void)
     r3d_mod_target_quit();
     r3d_mod_shader_quit();
     r3d_mod_light_quit();
+    r3d_mod_cache_quit();
     r3d_mod_draw_quit();
 }
 
-bool R3D_HasState(unsigned int flag)
+bool R3D_HasState(R3D_Flags flags)
 {
-    return R3D.state.flags & flag;
+    return R3D_CACHE_FLAGS_HAS(state, flags);
 }
 
-void R3D_SetState(unsigned int flags)
+void R3D_SetState(R3D_Flags flags)
 {
-    if (flags & R3D_FLAG_8_BIT_NORMALS) {
-        TraceLog(LOG_WARNING, "R3D: Cannot set 'R3D_FLAG_8_BIT_NORMALS'; this flag must be set during R3D initialization");
-        flags &= ~R3D_FLAG_8_BIT_NORMALS;
-    }
-
-    if (flags & R3D_FLAG_LOW_PRECISION_BUFFERS) {
-        TraceLog(LOG_WARNING, "R3D: Cannot set 'R3D_FLAG_LOW_PRECISION_BUFFERS'; this flag must be set during R3D initialization");
-        flags &= ~R3D_FLAG_LOW_PRECISION_BUFFERS;
-    }
-
-    R3D.state.flags |= flags;
+    R3D_CACHE_FLAGS_ASSIGN(state, flags);
 }
 
-void R3D_ClearState(unsigned int flags)
+void R3D_ClearState(R3D_Flags flags)
 {
-    if (flags & R3D_FLAG_8_BIT_NORMALS) {
-        TraceLog(LOG_WARNING, "R3D: Cannot clear 'R3D_FLAG_8_BIT_NORMALS'; this flag must be set during R3D initialization");
-        flags &= ~R3D_FLAG_8_BIT_NORMALS;
-    }
-
-    if (flags & R3D_FLAG_LOW_PRECISION_BUFFERS) {
-        TraceLog(LOG_WARNING, "R3D: Cannot clear 'R3D_FLAG_LOW_PRECISION_BUFFERS'; this flag must be set during R3D initialization");
-        flags &= ~R3D_FLAG_LOW_PRECISION_BUFFERS;
-    }
-
-    R3D.state.flags &= ~flags;
+    R3D_CACHE_FLAGS_CLEAR(state, flags);
 }
 
 void R3D_GetResolution(int* width, int* height)
@@ -179,25 +94,25 @@ void R3D_UpdateResolution(int width, int height)
 
 void R3D_SetTextureFilter(TextureFilter filter)
 {
-    R3D.state.loading.textureFilter = filter;
+    R3D_CACHE_SET(textureFilter, filter);
 }
 
 R3D_Layer R3D_GetActiveLayers(void)
 {
-    return R3D.state.layers;
+    return R3D_CACHE_GET(layers);
 }
 
-void R3D_SetActiveLayers(R3D_Layer layers)
+void R3D_SetActiveLayers(R3D_Layer bitfield)
 {
-    R3D.state.layers = layers;
+    R3D_CACHE_SET(layers, bitfield);
 }
 
 void R3D_EnableLayers(R3D_Layer bitfield)
 {
-    R3D.state.layers |= bitfield;
+    R3D_CACHE_FLAGS_ASSIGN(layers, bitfield);
 }
 
 void R3D_DisableLayers(R3D_Layer bitfield)
 {
-    R3D.state.layers &= ~bitfield;
+    R3D_CACHE_FLAGS_CLEAR(layers, bitfield);
 }

+ 5 - 35
src/r3d_culling.c

@@ -6,7 +6,7 @@
  * For conditions of distribution and use, see accompanying LICENSE file.
  */
 
-#include "./r3d_state.h"
+#include "./modules/r3d_cache.h"
 
 // ========================================
 // PUBLIC API
@@ -14,50 +14,20 @@
 
 bool R3D_IsPointInFrustum(Vector3 position)
 {
-	return r3d_frustum_is_point_in(&R3D.state.frustum.shape, &position);
+	return r3d_frustum_is_point_in(&R3D_CACHE_GET(viewport.frustum), &position);
 }
 
 bool R3D_IsSphereInFrustum(Vector3 position, float radius)
 {
-	return r3d_frustum_is_sphere_in(&R3D.state.frustum.shape, &position, radius);
+	return r3d_frustum_is_sphere_in(&R3D_CACHE_GET(viewport.frustum), &position, radius);
 }
 
 bool R3D_IsAABBInFrustum(BoundingBox aabb)
 {
-	return r3d_frustum_is_aabb_in(&R3D.state.frustum.shape, &aabb);
+	return r3d_frustum_is_aabb_in(&R3D_CACHE_GET(viewport.frustum), &aabb);
 }
 
 bool R3D_IsOBBInFrustum(BoundingBox aabb, Matrix transform)
 {
-	return r3d_frustum_is_obb_in(&R3D.state.frustum.shape, &aabb, &transform);
-}
-
-bool R3D_IsPointInFrustumBoundingBox(Vector3 position)
-{
-	const BoundingBox* box = &R3D.state.frustum.aabb;
-
-	return
-        position.x >= box->min.x && position.x <= box->max.x &&
-        position.y >= box->min.y && position.y <= box->max.y &&
-        position.z >= box->min.z && position.z <= box->max.z;
-}
-
-bool R3D_IsSphereInFrustumBoundingBox(Vector3 position, float radius)
-{
-	const BoundingBox* box = &R3D.state.frustum.aabb;
-
-    return
-        position.x + radius >= box->min.x && position.x - radius <= box->max.x &&
-        position.y + radius >= box->min.y && position.y - radius <= box->max.y &&
-        position.z + radius >= box->min.z && position.z - radius <= box->max.z;
-}
-
-bool R3D_IsAABBInFrustumBoundingBox(BoundingBox aabb)
-{
-	const BoundingBox* box = &R3D.state.frustum.aabb;
-
-    return
-        aabb.max.x >= box->min.x && aabb.min.x <= box->max.x &&
-        aabb.max.y >= box->min.y && aabb.min.y <= box->max.y &&
-        aabb.max.z >= box->min.z && aabb.min.z <= box->max.z;
+	return r3d_frustum_is_obb_in(&R3D_CACHE_GET(viewport.frustum), &aabb, &transform);
 }

+ 151 - 138
src/r3d_draw.c

@@ -22,8 +22,9 @@
 #include "./modules/r3d_target.h"
 #include "./modules/r3d_shader.h"
 #include "./modules/r3d_light.h"
+#include "./modules/r3d_cache.h"
+#include "r3d/r3d_environment.h"
 #include "./modules/r3d_draw.h"
-#include "./r3d_state.h"
 
 // ========================================
 // HELPER MACROS
@@ -62,7 +63,6 @@ static void upload_bone_matrices(const r3d_draw_call_t* call, int bindingSlot)
     r3d_storage_use(R3D_STORAGE_BONE_MATRICES, bindingSlot, bones, skeleton->boneCount);
 }
 
-
 static void raster_depth(const r3d_draw_call_t* call, bool shadow, const Matrix* matVP);
 static void raster_depth_cube(const r3d_draw_call_t* call, bool shadow, const Matrix* matVP);
 static void raster_decal(const r3d_draw_call_t* call, const Matrix* matVP);
@@ -113,98 +113,85 @@ void R3D_BeginEx(Camera3D camera, const RenderTexture* target)
 
     r3d_draw_clear();
 
-    /* --- Store camera position --- */
-
-    R3D.state.transform.viewPos = camera.position;
-
     /* --- Compute projection matrix --- */
 
+    Matrix view = MatrixLookAt(camera.position, camera.target, camera.up);
+    Matrix proj = R3D_MATRIX_IDENTITY;
+
     float aspect = r3d_mod_target_get_render_aspect();
 
     if (camera.projection == CAMERA_PERSPECTIVE) {
-        double top = rlGetCullDistanceNear() * tan(camera.fovy * 0.5 * DEG2RAD);
-        double right = top * aspect;
-        R3D.state.transform.proj = MatrixFrustum(
-            -right, right, -top, top,
-            rlGetCullDistanceNear(),
-            rlGetCullDistanceFar()
-        );
+        proj = MatrixPerspective(camera.fovy * DEG2RAD, aspect,
+            rlGetCullDistanceNear(), rlGetCullDistanceFar());
     }
     else if (camera.projection == CAMERA_ORTHOGRAPHIC) {
-        double top = camera.fovy / 2.0;
-        double right = top * aspect;
-        R3D.state.transform.proj = MatrixOrtho(
-            -right, right, -top, top,
-            rlGetCullDistanceNear(),
-            rlGetCullDistanceFar()
-        );
+        double top = camera.fovy / 2.0, right = top * aspect;
+        proj = MatrixOrtho(-right, right, -top, top,
+            rlGetCullDistanceNear(), rlGetCullDistanceFar());
     }
 
-    /* --- Compute view/proj matrices --- */
+    Matrix viewProj = r3d_matrix_multiply(&view, &proj);
 
-    R3D.state.transform.view = MatrixLookAt(camera.position, camera.target, camera.up);
-    R3D.state.transform.invProj = MatrixInvert(R3D.state.transform.proj);
-    R3D.state.transform.invView = MatrixInvert(R3D.state.transform.view);
-    R3D.state.transform.viewProj = r3d_matrix_multiply(&R3D.state.transform.view, &R3D.state.transform.proj);
+    /* --- Store viewport data to the cache --- */
 
-    /* --- Compute frustum --- */
+    R3D_CACHE_SET(viewport.frustum, r3d_frustum_create(viewProj));
+    R3D_CACHE_SET(viewport.viewPosition, camera.position);
 
-    R3D.state.frustum.aabb = r3d_frustum_get_bounding_box(R3D.state.transform.viewProj);
-    R3D.state.frustum.shape = r3d_frustum_create(R3D.state.transform.viewProj);
+    R3D_CACHE_SET(viewport.view, view);
+    R3D_CACHE_SET(viewport.proj, proj);
+    R3D_CACHE_SET(viewport.invView, MatrixInvert(view));
+    R3D_CACHE_SET(viewport.invProj, MatrixInvert(proj));
+    R3D_CACHE_SET(viewport.viewProj, viewProj);
 }
 
 void R3D_End(void)
 {
     /* --- Update and collect all visible lights then render shadow maps --- */
 
-    r3d_light_update_and_cull(&R3D.state.frustum.shape, R3D.state.transform.viewPos);
+    r3d_light_update_and_cull(&R3D_CACHE_GET(viewport.frustum), R3D_CACHE_GET(viewport.viewPosition));
 
     pass_scene_shadow();
 
     /* --- Cull and sort all draw calls before rendering --- */
 
-    if ((R3D.state.flags & R3D_FLAG_NO_FRUSTUM_CULLING) == 0) {
+    if (!R3D_CACHE_FLAGS_HAS(state, R3D_FLAG_NO_FRUSTUM_CULLING)) {
         for (int i = 0; i < R3D_DRAW_LIST_COUNT; i++) {
-            r3d_draw_cull_list(i, &R3D.state.frustum.shape);
+            r3d_draw_cull_list(i, &R3D_CACHE_GET(viewport.frustum));
         }
     }
 
-    if (R3D.state.flags & R3D_FLAG_OPAQUE_SORTING) {
-        r3d_draw_sort_list(R3D_DRAW_DEFERRED, R3D.state.transform.viewPos, R3D_DRAW_SORT_BACK_TO_FRONT);
-        r3d_draw_sort_list(R3D_DRAW_DEFERRED_INST, R3D.state.transform.viewPos, R3D_DRAW_SORT_BACK_TO_FRONT);
+    if (R3D_CACHE_FLAGS_HAS(state, R3D_FLAG_OPAQUE_SORTING)) {
+        r3d_draw_sort_list(R3D_DRAW_DEFERRED, R3D_CACHE_GET(viewport.viewPosition), R3D_DRAW_SORT_BACK_TO_FRONT);
+        r3d_draw_sort_list(R3D_DRAW_DEFERRED_INST, R3D_CACHE_GET(viewport.viewPosition), R3D_DRAW_SORT_BACK_TO_FRONT);
     }
 
-    if (R3D.state.flags & R3D_FLAG_TRANSPARENT_SORTING) {
-        r3d_draw_sort_list(R3D_DRAW_FORWARD, R3D.state.transform.viewPos, R3D_DRAW_SORT_FRONT_TO_BACK);
-        r3d_draw_sort_list(R3D_DRAW_FORWARD_INST, R3D.state.transform.viewPos, R3D_DRAW_SORT_FRONT_TO_BACK);
+    if (R3D_CACHE_FLAGS_HAS(state, R3D_FLAG_TRANSPARENT_SORTING)) {
+        r3d_draw_sort_list(R3D_DRAW_FORWARD, R3D_CACHE_GET(viewport.viewPosition), R3D_DRAW_SORT_FRONT_TO_BACK);
+        r3d_draw_sort_list(R3D_DRAW_FORWARD_INST, R3D_CACHE_GET(viewport.viewPosition), R3D_DRAW_SORT_FRONT_TO_BACK);
     }
 
     /* --- Opaque and decal rendering with deferred lighting and composition --- */
 
     r3d_target_t sceneTarget = R3D_TARGET_SCENE_0;
 
-    if (R3D_DRAW_HAS_DEFERRED)
-    {
+    if (R3D_DRAW_HAS_DEFERRED) {
         R3D_TARGET_CLEAR(R3D_TARGET_GBUFFER);
 
         pass_scene_geometry();
-
         if (R3D_DRAW_HAS_DECAL) {
             pass_scene_decals();
         }
 
         r3d_target_t ssaoSource = R3D_TARGET_INVALID;
-        if (R3D.env.ssaoEnabled) {
+        if (R3D_CACHE_GET(environment.ssao.enabled)) {
             ssaoSource = pass_prepare_ssao();
         }
 
         pass_deferred_ambient(ssaoSource);
         pass_deferred_lights(ssaoSource);
-
         pass_deferred_compose(sceneTarget);
     }
-    else
-    {
+    else {
         R3D_TARGET_CLEAR(R3D_TARGET_DEPTH);
     }
 
@@ -220,25 +207,25 @@ void R3D_End(void)
 
     sceneTarget = pass_post_setup(sceneTarget);
 
-    if (R3D.env.ssrEnabled) {
+    if (R3D_CACHE_GET(environment.ssr.enabled)) {
         sceneTarget = pass_post_ssr(sceneTarget);
     }
 
-    if (R3D.env.fogMode != R3D_FOG_DISABLED) {
+    if (R3D_CACHE_GET(environment.fog.mode) != R3D_FOG_DISABLED) {
         sceneTarget = pass_post_fog(sceneTarget);
     }
 
-    if (R3D.env.dofMode != R3D_DOF_DISABLED) {
+    if (R3D_CACHE_GET(environment.dof.mode) != R3D_DOF_DISABLED) {
         sceneTarget = pass_post_dof(sceneTarget);
     }
 
-    if (R3D.env.bloomMode != R3D_BLOOM_DISABLED) {
+    if (R3D_CACHE_GET(environment.bloom.mode) != R3D_BLOOM_DISABLED) {
         sceneTarget = pass_post_bloom(sceneTarget);
     }
 
     sceneTarget = pass_post_output(sceneTarget);
 
-    if (R3D.state.flags & R3D_FLAG_FXAA) {
+    if (R3D_CACHE_FLAGS_HAS(state, R3D_FLAG_FXAA)) {
         sceneTarget = pass_post_fxaa(sceneTarget);
     }
 
@@ -251,7 +238,7 @@ void R3D_End(void)
 
 void R3D_DrawMesh(const R3D_Mesh* mesh, const R3D_Material* material, Matrix transform)
 {
-    if ((mesh->layerMask & R3D.state.layers) == 0) {
+    if (!R3D_CACHE_FLAGS_HAS(layers, mesh->layerMask)) {
         return;
     }
 
@@ -285,7 +272,7 @@ void R3D_DrawMeshInstancedPro(const R3D_Mesh* mesh, const R3D_Material* material
                               const Color* instanceColors, int colorsStride,
                               int instanceCount)
 {
-    if ((mesh->layerMask & R3D.state.layers) == 0) {
+    if (!R3D_CACHE_FLAGS_HAS(layers, mesh->layerMask)) {
         return;
     }
 
@@ -347,8 +334,9 @@ void R3D_DrawModelPro(const R3D_Model* model, Matrix transform)
     for (int i = 0; i < model->meshCount; i++)
     {
         const R3D_Mesh* mesh = &model->meshes[i];
-        if ((mesh->layerMask & R3D.state.layers) == 0) {
-            continue;
+
+        if (!R3D_CACHE_FLAGS_HAS(layers, mesh->layerMask)) {
+            return;
         }
 
         r3d_draw_call_t drawCall = {0};
@@ -402,8 +390,9 @@ void R3D_DrawModelInstancedPro(const R3D_Model* model,
     for (int i = 0; i < model->meshCount; i++)
     {
         const R3D_Mesh* mesh = &model->meshes[i];
-        if ((mesh->layerMask & R3D.state.layers) == 0) {
-            continue;
+
+        if (!R3D_CACHE_FLAGS_HAS(layers, mesh->layerMask)) {
+            return;
         }
 
         r3d_draw_call_t drawCall = {0};
@@ -516,7 +505,7 @@ void raster_depth(const r3d_draw_call_t* call, bool shadow, const Matrix* matVP)
 
     R3D_SHADER_SET_INT(scene.depth, uBillboard, call->material.billboardMode);
     if (call->material.billboardMode != R3D_BILLBOARD_DISABLED) {
-        R3D_SHADER_SET_MAT4(scene.depth, uMatInvView, R3D.state.transform.invView);
+        R3D_SHADER_SET_MAT4(scene.depth, uMatInvView, R3D_CACHE_GET(viewport.invView));
     }
 
     /* --- Set texcoord offset/scale --- */
@@ -578,7 +567,7 @@ void raster_depth_cube(const r3d_draw_call_t* call, bool shadow, const Matrix* m
 
     R3D_SHADER_SET_INT(scene.depthCube, uBillboard, call->material.billboardMode);
     if (call->material.billboardMode != R3D_BILLBOARD_DISABLED) {
-        R3D_SHADER_SET_MAT4(scene.depthCube, uMatInvView, R3D.state.transform.invView);
+        R3D_SHADER_SET_MAT4(scene.depthCube, uMatInvView, R3D_CACHE_GET(viewport.invView));
     }
 
     /* --- Set texcoord offset/scale --- */
@@ -635,9 +624,9 @@ void raster_decal(const r3d_draw_call_t* call, const Matrix* matVP)
     R3D_SHADER_SET_MAT4(scene.decal, uMatNormal, matNormal);
     R3D_SHADER_SET_MAT4(scene.decal, uMatVP, *matVP);
 
-    R3D_SHADER_SET_MAT4(scene.decal, uMatInvView, R3D.state.transform.invView);
-    R3D_SHADER_SET_MAT4(scene.decal, uMatInvProj, R3D.state.transform.invProj);
-    R3D_SHADER_SET_MAT4(scene.decal, uMatProj, R3D.state.transform.proj);
+    R3D_SHADER_SET_MAT4(scene.decal, uMatInvView, R3D_CACHE_GET(viewport.invView));
+    R3D_SHADER_SET_MAT4(scene.decal, uMatInvProj, R3D_CACHE_GET(viewport.invProj));
+    R3D_SHADER_SET_MAT4(scene.decal, uMatProj, R3D_CACHE_GET(viewport.proj));
 
     /* --- Set factor material maps --- */
 
@@ -728,7 +717,7 @@ void raster_geometry(const r3d_draw_call_t* call, const Matrix* matVP)
 
     R3D_SHADER_SET_INT(scene.geometry, uBillboard, call->material.billboardMode);
     if (call->material.billboardMode != R3D_BILLBOARD_DISABLED) {
-        R3D_SHADER_SET_MAT4(scene.geometry, uMatInvView, R3D.state.transform.invView);
+        R3D_SHADER_SET_MAT4(scene.geometry, uMatInvView, R3D_CACHE_GET(viewport.invView));
     }
 
     /* --- Set factor material maps --- */
@@ -808,7 +797,7 @@ void raster_forward(const r3d_draw_call_t* call, const Matrix* matVP)
 
     R3D_SHADER_SET_INT(scene.forward, uBillboard, call->material.billboardMode);
     if (call->material.billboardMode != R3D_BILLBOARD_DISABLED) {
-        R3D_SHADER_SET_MAT4(scene.forward, uMatInvView, R3D.state.transform.invView);
+        R3D_SHADER_SET_MAT4(scene.forward, uMatInvView, R3D_CACHE_GET(viewport.invView));
     }
 
     /* --- Set factor material maps --- */
@@ -926,7 +915,7 @@ void pass_scene_geometry(void)
     glDisable(GL_BLEND);
 
     R3D_DRAW_FOR_EACH(call, R3D_DRAW_DEFERRED, R3D_DRAW_DEFERRED_INST) {
-        raster_geometry(call, &R3D.state.transform.viewProj);
+        raster_geometry(call, &R3D_CACHE_GET(viewport.viewProj));
     }
 
     // The bone matrices texture may have been bind during drawcalls, so UNBIND!
@@ -944,7 +933,7 @@ void pass_scene_decals(void)
     R3D_SHADER_BIND_SAMPLER_2D(scene.decal, uTexDepth, r3d_target_get(R3D_TARGET_DEPTH));
 
     R3D_DRAW_FOR_EACH(call, R3D_DRAW_DECAL, R3D_DRAW_DECAL_INST) {
-        raster_decal(call, &R3D.state.transform.viewProj);
+        raster_decal(call, &R3D_CACHE_GET(viewport.viewProj));
     }
 
     R3D_SHADER_UNBIND_SAMPLER_2D(scene.decal, uTexDepth);
@@ -963,14 +952,14 @@ r3d_target_t pass_prepare_ssao(void)
 
     R3D_SHADER_USE(prepare.ssao);
 
-    R3D_SHADER_SET_MAT4(prepare.ssao, uMatInvProj, R3D.state.transform.invProj);
-    R3D_SHADER_SET_MAT4(prepare.ssao, uMatProj, R3D.state.transform.proj);
-    R3D_SHADER_SET_MAT4(prepare.ssao, uMatView, R3D.state.transform.view);
+    R3D_SHADER_SET_MAT4(prepare.ssao, uMatInvProj, R3D_CACHE_GET(viewport.invProj));
+    R3D_SHADER_SET_MAT4(prepare.ssao, uMatProj, R3D_CACHE_GET(viewport.proj));
+    R3D_SHADER_SET_MAT4(prepare.ssao, uMatView, R3D_CACHE_GET(viewport.view));
 
-    R3D_SHADER_SET_FLOAT(prepare.ssao, uRadius, R3D.env.ssaoRadius);
-    R3D_SHADER_SET_FLOAT(prepare.ssao, uBias, R3D.env.ssaoBias);
-    R3D_SHADER_SET_FLOAT(prepare.ssao, uIntensity, R3D.env.ssaoIntensity);
-    R3D_SHADER_SET_FLOAT(prepare.ssao, uPower, R3D.env.ssaoPower);
+    R3D_SHADER_SET_FLOAT(prepare.ssao, uRadius,  R3D_CACHE_GET(environment.ssao.radius));
+    R3D_SHADER_SET_FLOAT(prepare.ssao, uBias, R3D_CACHE_GET(environment.ssao.bias));
+    R3D_SHADER_SET_FLOAT(prepare.ssao, uIntensity, R3D_CACHE_GET(environment.ssao.intensity));
+    R3D_SHADER_SET_FLOAT(prepare.ssao, uPower, R3D_CACHE_GET(environment.ssao.power));
 
     R3D_SHADER_BIND_SAMPLER_2D(prepare.ssao, uTexDepth, r3d_target_get(R3D_TARGET_DEPTH));
     R3D_SHADER_BIND_SAMPLER_2D(prepare.ssao, uTexNormal, r3d_target_get(R3D_TARGET_NORMAL));
@@ -990,9 +979,9 @@ r3d_target_t pass_prepare_ssao(void)
 
     R3D_SHADER_BIND_SAMPLER_2D(prepare.ssaoBlur, uTexNormal, r3d_target_get(R3D_TARGET_NORMAL));
     R3D_SHADER_BIND_SAMPLER_2D(prepare.ssaoBlur, uTexDepth, r3d_target_get(R3D_TARGET_DEPTH));
-    R3D_SHADER_SET_MAT4(prepare.ssaoBlur, uMatInvProj, R3D.state.transform.invProj);
+    R3D_SHADER_SET_MAT4(prepare.ssaoBlur, uMatInvProj, R3D_CACHE_GET(viewport.invProj));
 
-    for (int i = 0; i < R3D.env.ssaoIterations; i++)
+    for (int i = 0; i < R3D_CACHE_GET(environment.ssao.iterations); i++)
     {
         // Horizontal pass
         R3D_TARGET_BIND_AND_SWAP_SSAO(ssaoTarget);
@@ -1023,7 +1012,7 @@ void pass_deferred_ambient(r3d_target_t ssaoSource)
 
     /* --- Calculate skybox IBL contribution --- */
 
-    if (R3D.env.useSky)
+    if (R3D_CACHE_GET(environment.background.sky.cubemap.id) != 0)
     {
         R3D_TARGET_BIND(R3D_TARGET_LIGHTING);
         R3D_SHADER_USE(deferred.ambientIbl);
@@ -1033,16 +1022,16 @@ void pass_deferred_ambient(r3d_target_t ssaoSource)
         R3D_SHADER_BIND_SAMPLER_2D(deferred.ambientIbl, uTexDepth, r3d_target_get(R3D_TARGET_DEPTH));
         R3D_SHADER_BIND_SAMPLER_2D(deferred.ambientIbl, uTexSSAO, R3D_TEXTURE_SELECT(r3d_target_get(ssaoSource), WHITE));
         R3D_SHADER_BIND_SAMPLER_2D(deferred.ambientIbl, uTexORM, r3d_target_get(R3D_TARGET_ORM));
-        R3D_SHADER_BIND_SAMPLER_CUBE(deferred.ambientIbl, uCubeIrradiance, R3D.env.sky.irradiance.id);
-        R3D_SHADER_BIND_SAMPLER_CUBE(deferred.ambientIbl, uCubePrefilter, R3D.env.sky.prefilter.id);
+        R3D_SHADER_BIND_SAMPLER_CUBE(deferred.ambientIbl, uCubeIrradiance, R3D_CACHE_GET(environment.background.sky.irradiance.id));
+        R3D_SHADER_BIND_SAMPLER_CUBE(deferred.ambientIbl, uCubePrefilter, R3D_CACHE_GET(environment.background.sky.prefilter.id));
         R3D_SHADER_BIND_SAMPLER_2D(deferred.ambientIbl, uTexBrdfLut, r3d_texture_get(R3D_TEXTURE_IBL_BRDF_LUT));
 
-        R3D_SHADER_SET_VEC3(deferred.ambientIbl, uViewPosition, R3D.state.transform.viewPos);
-        R3D_SHADER_SET_MAT4(deferred.ambientIbl, uMatInvProj, R3D.state.transform.invProj);
-        R3D_SHADER_SET_MAT4(deferred.ambientIbl, uMatInvView, R3D.state.transform.invView);
-        R3D_SHADER_SET_VEC4(deferred.ambientIbl, uQuatSkybox, R3D.env.quatSky);
-        R3D_SHADER_SET_FLOAT(deferred.ambientIbl, uSkyboxAmbientIntensity, R3D.env.skyAmbientIntensity);
-        R3D_SHADER_SET_FLOAT(deferred.ambientIbl, uSkyboxReflectIntensity, R3D.env.skyReflectIntensity);
+        R3D_SHADER_SET_VEC3(deferred.ambientIbl, uViewPosition, R3D_CACHE_GET(viewport.viewPosition));
+        R3D_SHADER_SET_MAT4(deferred.ambientIbl, uMatInvProj, R3D_CACHE_GET(viewport.invProj));
+        R3D_SHADER_SET_MAT4(deferred.ambientIbl, uMatInvView, R3D_CACHE_GET(viewport.invView));
+        R3D_SHADER_SET_VEC4(deferred.ambientIbl, uQuatSkybox, R3D_CACHE_GET(environment.background.rotation));
+        R3D_SHADER_SET_FLOAT(deferred.ambientIbl, uAmbientEnergy, R3D_CACHE_GET(environment.ambient.energy));
+        R3D_SHADER_SET_FLOAT(deferred.ambientIbl, uReflectEnergy, R3D_CACHE_GET(environment.ambient.reflect));
 
         R3D_PRIMITIVE_DRAW_SCREEN();
 
@@ -1069,7 +1058,8 @@ void pass_deferred_ambient(r3d_target_t ssaoSource)
         R3D_SHADER_BIND_SAMPLER_2D(deferred.ambient, uTexSSAO, R3D_TEXTURE_SELECT(r3d_target_get(ssaoSource), WHITE));
         R3D_SHADER_BIND_SAMPLER_2D(deferred.ambient, uTexORM, r3d_target_get(R3D_TARGET_ORM));
 
-        R3D_SHADER_SET_VEC3(deferred.ambient, uAmbientLight, R3D.env.ambientLight);
+        R3D_SHADER_SET_COL3(deferred.ambient, uAmbientColor, R3D_CACHE_GET(environment.ambient.color));
+        R3D_SHADER_SET_FLOAT(deferred.ambient, uAmbientEnergy, R3D_CACHE_GET(environment.ambient.energy));
 
         R3D_PRIMITIVE_DRAW_SCREEN();
 
@@ -1105,10 +1095,10 @@ void pass_deferred_lights(r3d_target_t ssaoSource)
     R3D_SHADER_BIND_SAMPLER_2D(deferred.lighting, uTexSSAO, R3D_TEXTURE_SELECT(r3d_target_get(ssaoSource), WHITE));
     R3D_SHADER_BIND_SAMPLER_2D(deferred.lighting, uTexORM, r3d_target_get(R3D_TARGET_ORM));
 
-    R3D_SHADER_SET_VEC3(deferred.lighting, uViewPosition, R3D.state.transform.viewPos);
-    R3D_SHADER_SET_MAT4(deferred.lighting, uMatInvProj, R3D.state.transform.invProj);
-    R3D_SHADER_SET_MAT4(deferred.lighting, uMatInvView, R3D.state.transform.invView);
-    R3D_SHADER_SET_FLOAT(deferred.lighting, uSSAOLightAffect, R3D.env.ssaoLightAffect);
+    R3D_SHADER_SET_VEC3(deferred.lighting, uViewPosition, R3D_CACHE_GET(viewport.viewPosition));
+    R3D_SHADER_SET_MAT4(deferred.lighting, uMatInvProj, R3D_CACHE_GET(viewport.invProj));
+    R3D_SHADER_SET_MAT4(deferred.lighting, uMatInvView, R3D_CACHE_GET(viewport.invView));
+    R3D_SHADER_SET_FLOAT(deferred.lighting, uSSAOLightAffect, R3D_CACHE_GET(environment.ssao.lightAffect));
 
     /* --- Calculate lighting contributions --- */
 
@@ -1128,7 +1118,7 @@ void pass_deferred_lights(r3d_target_t ssaoSource)
             bool allInside = true;
             for (int i = 0; i < 8; i++) {
                 Vector4 corner = {(i & 1) ? max.x : min.x, (i & 2) ? max.y : min.y, (i & 4) ? max.z : min.z, 1.0f};
-                Vector4 clip = r3d_vector4_transform(corner, &R3D.state.transform.viewProj);
+                Vector4 clip = r3d_vector4_transform(corner, &R3D_CACHE_GET(viewport.viewProj));
                 if (clip.w <= 0.0f) { allInside = false; break; }
 
                 Vector2 ndc = Vector2Scale((Vector2){clip.x, clip.y}, 1.0f / clip.w);
@@ -1313,29 +1303,30 @@ void pass_scene_forward(r3d_target_t sceneTarget)
 
     glDepthFunc(GL_LEQUAL);
 
-    if (R3D.env.useSky) {
-        R3D_SHADER_BIND_SAMPLER_CUBE(scene.forward, uCubeIrradiance, R3D.env.sky.irradiance.id);
-        R3D_SHADER_BIND_SAMPLER_CUBE(scene.forward, uCubePrefilter, R3D.env.sky.prefilter.id);
+    if (R3D_CACHE_GET(environment.background.sky.cubemap.id) != 0) {
+        R3D_SHADER_BIND_SAMPLER_CUBE(scene.forward, uCubeIrradiance, R3D_CACHE_GET(environment.background.sky.irradiance.id));
+        R3D_SHADER_BIND_SAMPLER_CUBE(scene.forward, uCubePrefilter, R3D_CACHE_GET(environment.background.sky.prefilter.id));
         R3D_SHADER_BIND_SAMPLER_2D(scene.forward, uTexBrdfLut, r3d_texture_get(R3D_TEXTURE_IBL_BRDF_LUT));
 
-        R3D_SHADER_SET_VEC4(scene.forward, uQuatSkybox, R3D.env.quatSky);
+        R3D_SHADER_SET_FLOAT(scene.forward, uAmbientEnergy, R3D_CACHE_GET(environment.ambient.energy));
+        R3D_SHADER_SET_FLOAT(scene.forward, uReflectEnergy, R3D_CACHE_GET(environment.ambient.reflect));
+        R3D_SHADER_SET_VEC4(scene.forward, uQuatSkybox, R3D_CACHE_GET(environment.background.rotation));
         R3D_SHADER_SET_INT(scene.forward, uHasSkybox, true);
-        R3D_SHADER_SET_FLOAT(scene.forward, uSkyboxAmbientIntensity, R3D.env.skyAmbientIntensity);
-        R3D_SHADER_SET_FLOAT(scene.forward, uSkyboxReflectIntensity, R3D.env.skyReflectIntensity);
     }
     else {
-        R3D_SHADER_SET_VEC3(scene.forward, uAmbientLight, R3D.env.ambientLight);
+        R3D_SHADER_SET_FLOAT(scene.forward, uAmbientEnergy, R3D_CACHE_GET(environment.ambient.energy));
+        R3D_SHADER_SET_COL3(scene.forward, uAmbientColor, R3D_CACHE_GET(environment.ambient.color));
         R3D_SHADER_SET_INT(scene.forward, uHasSkybox, false);
     }
 
-    R3D_SHADER_SET_VEC3(scene.forward, uViewPosition, R3D.state.transform.viewPos);
+    R3D_SHADER_SET_VEC3(scene.forward, uViewPosition, R3D_CACHE_GET(viewport.viewPosition));
 
     R3D_DRAW_FOR_EACH(call, R3D_DRAW_FORWARD, R3D_DRAW_FORWARD_INST) {
         pass_scene_forward_send_lights(call);
-        raster_forward(call, &R3D.state.transform.viewProj);
+        raster_forward(call, &R3D_CACHE_GET(viewport.viewProj));
     }
 
-    if (R3D.env.useSky) {
+    if (R3D_CACHE_GET(environment.background.sky.cubemap.id) != 0) {
         R3D_SHADER_UNBIND_SAMPLER_CUBE(scene.forward, uCubeIrradiance);
         R3D_SHADER_UNBIND_SAMPLER_CUBE(scene.forward, uCubePrefilter);
         R3D_SHADER_UNBIND_SAMPLER_2D(scene.forward, uTexBrdfLut);
@@ -1359,23 +1350,31 @@ void pass_scene_background(r3d_target_t sceneTarget)
     glDepthMask(GL_FALSE);
     glDisable(GL_BLEND);
 
-    if (R3D.env.useSky) {
+    if (R3D_CACHE_GET(environment.background.sky.cubemap.id) != 0) {
         R3D_SHADER_USE(scene.skybox);
         glDisable(GL_CULL_FACE);
 
-        R3D_SHADER_BIND_SAMPLER_CUBE(scene.skybox, uCubeSky, R3D.env.sky.cubemap.id);
-        R3D_SHADER_SET_FLOAT(scene.skybox, uSkyIntensity, R3D.env.skyBackgroundIntensity);
-        R3D_SHADER_SET_MAT4(scene.skybox, uMatView, R3D.state.transform.view);
-        R3D_SHADER_SET_MAT4(scene.skybox, uMatProj, R3D.state.transform.proj);
-        R3D_SHADER_SET_VEC4(scene.skybox, uRotation, R3D.env.quatSky);
+        R3D_SHADER_BIND_SAMPLER_CUBE(scene.skybox, uCubeSky, R3D_CACHE_GET(environment.background.sky.cubemap.id));
+        R3D_SHADER_SET_FLOAT(scene.skybox, uSkyEnergy, R3D_CACHE_GET(environment.background.energy));
+        R3D_SHADER_SET_MAT4(scene.skybox, uMatView, R3D_CACHE_GET(viewport.view));
+        R3D_SHADER_SET_MAT4(scene.skybox, uMatProj, R3D_CACHE_GET(viewport.proj));
+        R3D_SHADER_SET_VEC4(scene.skybox, uRotation, R3D_CACHE_GET(environment.background.rotation));
 
         R3D_PRIMITIVE_DRAW_CUBE();
 
         R3D_SHADER_UNBIND_SAMPLER_CUBE(scene.skybox, uCubeSky);
     }
     else {
+        Color backgroundSDR = R3D_CACHE_GET(environment.background.color);
+        float backgroundNRG = R3D_CACHE_GET(environment.background.energy);
+        Vector4 backgroundHDR = {
+            .x = (float)backgroundSDR.r / 255 * backgroundNRG,
+            .y = (float)backgroundSDR.g / 255 * backgroundNRG,
+            .z = (float)backgroundSDR.b / 255 * backgroundNRG,
+            .w = 1.0f,
+        };
         R3D_SHADER_USE(scene.background);
-        R3D_SHADER_SET_VEC4(scene.background, uColor, R3D.env.backgroundColor);
+        R3D_SHADER_SET_VEC4(scene.background, uColor, backgroundHDR);
         R3D_PRIMITIVE_DRAW_SCREEN();
     }
 }
@@ -1401,19 +1400,19 @@ r3d_target_t pass_post_ssr(r3d_target_t sceneTarget)
     R3D_SHADER_BIND_SAMPLER_2D(post.ssr, uTexORM, r3d_target_get(R3D_TARGET_ORM));
     R3D_SHADER_BIND_SAMPLER_2D(post.ssr, uTexDepth, r3d_target_get(R3D_TARGET_DEPTH));
 
-    R3D_SHADER_SET_INT(post.ssr, uMaxRaySteps, R3D.env.ssrMaxRaySteps);
-    R3D_SHADER_SET_INT(post.ssr, uBinarySearchSteps, R3D.env.ssrBinarySearchSteps);
-    R3D_SHADER_SET_FLOAT(post.ssr, uRayMarchLength, R3D.env.ssrRayMarchLength);
-    R3D_SHADER_SET_FLOAT(post.ssr, uDepthThickness, R3D.env.ssrDepthThickness);
-    R3D_SHADER_SET_FLOAT(post.ssr, uDepthTolerance, R3D.env.ssrDepthTolerance);
-    R3D_SHADER_SET_FLOAT(post.ssr, uEdgeFadeStart, R3D.env.ssrEdgeFadeStart);
-    R3D_SHADER_SET_FLOAT(post.ssr, uEdgeFadeEnd, R3D.env.ssrEdgeFadeEnd);
+    R3D_SHADER_SET_INT(post.ssr, uMaxRaySteps, R3D_CACHE_GET(environment.ssr.maxRaySteps));
+    R3D_SHADER_SET_INT(post.ssr, uBinarySearchSteps, R3D_CACHE_GET(environment.ssr.binarySearchSteps));
+    R3D_SHADER_SET_FLOAT(post.ssr, uRayMarchLength, R3D_CACHE_GET(environment.ssr.rayMarchLength));
+    R3D_SHADER_SET_FLOAT(post.ssr, uDepthThickness, R3D_CACHE_GET(environment.ssr.depthThickness));
+    R3D_SHADER_SET_FLOAT(post.ssr, uDepthTolerance, R3D_CACHE_GET(environment.ssr.depthTolerance));
+    R3D_SHADER_SET_FLOAT(post.ssr, uEdgeFadeStart, R3D_CACHE_GET(environment.ssr.edgeFadeStart));
+    R3D_SHADER_SET_FLOAT(post.ssr, uEdgeFadeEnd, R3D_CACHE_GET(environment.ssr.edgeFadeEnd));
 
-    R3D_SHADER_SET_MAT4(post.ssr, uMatView, R3D.state.transform.view);
-    R3D_SHADER_SET_MAT4(post.ssr, uMatInvProj, R3D.state.transform.invProj);
-    R3D_SHADER_SET_MAT4(post.ssr, uMatInvView, R3D.state.transform.invView);
-    R3D_SHADER_SET_MAT4(post.ssr, uMatViewProj, R3D.state.transform.viewProj);
-    R3D_SHADER_SET_VEC3(post.ssr, uViewPosition, R3D.state.transform.viewPos);
+    R3D_SHADER_SET_MAT4(post.ssr, uMatView, R3D_CACHE_GET(viewport.view));
+    R3D_SHADER_SET_MAT4(post.ssr, uMatInvProj, R3D_CACHE_GET(viewport.invProj));
+    R3D_SHADER_SET_MAT4(post.ssr, uMatInvView, R3D_CACHE_GET(viewport.invView));
+    R3D_SHADER_SET_MAT4(post.ssr, uMatViewProj, R3D_CACHE_GET(viewport.viewProj));
+    R3D_SHADER_SET_VEC3(post.ssr, uViewPosition, R3D_CACHE_GET(viewport.viewPosition));
 
     R3D_PRIMITIVE_DRAW_SCREEN();
 
@@ -1436,12 +1435,12 @@ r3d_target_t pass_post_fog(r3d_target_t sceneTarget)
 
     R3D_SHADER_SET_FLOAT(post.fog, uNear, (float)rlGetCullDistanceNear());
     R3D_SHADER_SET_FLOAT(post.fog, uFar, (float)rlGetCullDistanceFar());
-    R3D_SHADER_SET_INT(post.fog, uFogMode, R3D.env.fogMode);
-    R3D_SHADER_SET_VEC3(post.fog, uFogColor, R3D.env.fogColor);
-    R3D_SHADER_SET_FLOAT(post.fog, uFogStart, R3D.env.fogStart);
-    R3D_SHADER_SET_FLOAT(post.fog, uFogEnd, R3D.env.fogEnd);
-    R3D_SHADER_SET_FLOAT(post.fog, uFogDensity, R3D.env.fogDensity);
-    R3D_SHADER_SET_FLOAT(post.fog, uSkyAffect, R3D.env.fogSkyAffect);
+    R3D_SHADER_SET_INT(post.fog, uFogMode, R3D_CACHE_GET(environment.fog.mode));
+    R3D_SHADER_SET_COL3(post.fog, uFogColor, R3D_CACHE_GET(environment.fog.color));
+    R3D_SHADER_SET_FLOAT(post.fog, uFogStart, R3D_CACHE_GET(environment.fog.start));
+    R3D_SHADER_SET_FLOAT(post.fog, uFogEnd, R3D_CACHE_GET(environment.fog.end));
+    R3D_SHADER_SET_FLOAT(post.fog, uFogDensity, R3D_CACHE_GET(environment.fog.density));
+    R3D_SHADER_SET_FLOAT(post.fog, uSkyAffect, R3D_CACHE_GET(environment.fog.skyAffect));
 
     R3D_PRIMITIVE_DRAW_SCREEN();
 
@@ -1462,10 +1461,10 @@ r3d_target_t pass_post_dof(r3d_target_t sceneTarget)
     R3D_SHADER_SET_VEC2(post.dof, uTexelSize, (Vector2) {R3D_TARGET_TEXEL_SIZE});
     R3D_SHADER_SET_FLOAT(post.dof, uNear, (float)rlGetCullDistanceNear());
     R3D_SHADER_SET_FLOAT(post.dof, uFar, (float)rlGetCullDistanceFar());
-    R3D_SHADER_SET_FLOAT(post.dof, uFocusPoint, R3D.env.dofFocusPoint);
-    R3D_SHADER_SET_FLOAT(post.dof, uFocusScale, R3D.env.dofFocusScale);
-    R3D_SHADER_SET_FLOAT(post.dof, uMaxBlurSize, R3D.env.dofMaxBlurSize);
-    R3D_SHADER_SET_INT(post.dof, uDebugMode, R3D.env.dofDebugMode);
+    R3D_SHADER_SET_FLOAT(post.dof, uFocusPoint, R3D_CACHE_GET(environment.dof.focusPoint));
+    R3D_SHADER_SET_FLOAT(post.dof, uFocusScale, R3D_CACHE_GET(environment.dof.focusScale));
+    R3D_SHADER_SET_FLOAT(post.dof, uMaxBlurSize, R3D_CACHE_GET(environment.dof.maxBlurSize));
+    R3D_SHADER_SET_INT(post.dof, uDebugMode, R3D_CACHE_GET(environment.dof.debugMode));
 
     R3D_PRIMITIVE_DRAW_SCREEN();
 
@@ -1487,6 +1486,20 @@ r3d_target_t pass_post_bloom(r3d_target_t sceneTarget)
 
     R3D_TARGET_BIND(R3D_TARGET_BLOOM);
 
+    /* --- Calculate bloom prefilter --- */
+
+    float threshold = R3D_CACHE_GET(environment.bloom.threshold);
+    float softThreshold = R3D_CACHE_GET(environment.bloom.threshold);
+
+    float knee = threshold * softThreshold;
+
+    Vector4 prefilter = {
+        prefilter.x = threshold,
+        prefilter.y = threshold - knee,
+        prefilter.z = 2.0f * knee,
+        prefilter.w = 0.25f / (knee + 0.00001f),
+    };
+
     /* --- Bloom: Karis average before downsampling --- */
 
     R3D_SHADER_USE(prepare.bloomDown);
@@ -1498,7 +1511,7 @@ r3d_target_t pass_post_bloom(r3d_target_t sceneTarget)
     R3D_SHADER_BIND_SAMPLER_2D(prepare.bloomDown, uTexture, sceneSourceID);
 
     R3D_SHADER_SET_VEC2(prepare.bloomDown, uTexelSize, (Vector2) {txSrcW, txSrcH});
-    R3D_SHADER_SET_VEC4(prepare.bloomDown, uPrefilter, R3D.env.bloomPrefilter);
+    R3D_SHADER_SET_VEC4(prepare.bloomDown, uPrefilter, prefilter);
     R3D_SHADER_SET_INT(prepare.bloomDown, uDstLevel, 0);
 
     R3D_PRIMITIVE_DRAW_SCREEN();
@@ -1547,8 +1560,8 @@ r3d_target_t pass_post_bloom(r3d_target_t sceneTarget)
 
         R3D_SHADER_SET_FLOAT(prepare.bloomUp, uSrcLevel, dstLevel + 1);
         R3D_SHADER_SET_VEC2(prepare.bloomUp, uFilterRadius, (Vector2) {
-            (float)R3D.env.bloomFilterRadius * txSrcW,
-            (float)R3D.env.bloomFilterRadius * txSrcH
+            R3D_CACHE_GET(environment.bloom.filterRadius) * txSrcW,
+            R3D_CACHE_GET(environment.bloom.filterRadius) * txSrcH
         });
 
         R3D_PRIMITIVE_DRAW_SCREEN();
@@ -1566,8 +1579,8 @@ r3d_target_t pass_post_bloom(r3d_target_t sceneTarget)
     R3D_SHADER_BIND_SAMPLER_2D(post.bloom, uTexColor, sceneSourceID);
     R3D_SHADER_BIND_SAMPLER_2D(post.bloom, uTexBloomBlur, r3d_target_get(R3D_TARGET_BLOOM));
 
-    R3D_SHADER_SET_INT(post.bloom, uBloomMode, R3D.env.bloomMode);
-    R3D_SHADER_SET_FLOAT(post.bloom, uBloomIntensity, R3D.env.bloomIntensity);
+    R3D_SHADER_SET_INT(post.bloom, uBloomMode, R3D_CACHE_GET(environment.bloom.mode));
+    R3D_SHADER_SET_FLOAT(post.bloom, uBloomIntensity, R3D_CACHE_GET(environment.bloom.intensity));
 
     R3D_PRIMITIVE_DRAW_SCREEN();
 
@@ -1584,12 +1597,12 @@ r3d_target_t pass_post_output(r3d_target_t sceneTarget)
 
     R3D_SHADER_BIND_SAMPLER_2D(post.output, uTexColor, r3d_target_get(sceneTarget));
 
-    R3D_SHADER_SET_FLOAT(post.output, uTonemapExposure, R3D.env.tonemapExposure);
-    R3D_SHADER_SET_FLOAT(post.output, uTonemapWhite, R3D.env.tonemapWhite);
-    R3D_SHADER_SET_INT(post.output, uTonemapMode, R3D.env.tonemapMode);
-    R3D_SHADER_SET_FLOAT(post.output, uBrightness, R3D.env.brightness);
-    R3D_SHADER_SET_FLOAT(post.output, uContrast, R3D.env.contrast);
-    R3D_SHADER_SET_FLOAT(post.output, uSaturation, R3D.env.saturation);
+    R3D_SHADER_SET_FLOAT(post.output, uTonemapExposure, R3D_CACHE_GET(environment.tonemap.exposure));
+    R3D_SHADER_SET_FLOAT(post.output, uTonemapWhite, R3D_CACHE_GET(environment.tonemap.white));
+    R3D_SHADER_SET_INT(post.output, uTonemapMode, R3D_CACHE_GET(environment.tonemap.mode));
+    R3D_SHADER_SET_FLOAT(post.output, uBrightness, R3D_CACHE_GET(environment.color.brightness));
+    R3D_SHADER_SET_FLOAT(post.output, uContrast, R3D_CACHE_GET(environment.color.contrast));
+    R3D_SHADER_SET_FLOAT(post.output, uSaturation, R3D_CACHE_GET(environment.color.saturation));
 
     R3D_PRIMITIVE_DRAW_SCREEN();
 

+ 5 - 446
src/r3d_environment.c

@@ -9,459 +9,18 @@
 #include <r3d/r3d_environment.h>
 #include <raymath.h>
 
-#include "./r3d_state.h"
+#include "./modules/r3d_cache.h"
 
 // ========================================
 // PUBLIC API
 // ========================================
 
-void R3D_SetBackgroundColor(Color color)
+R3DAPI R3D_Environment* R3D_GetEnvironment(void)
 {
-	R3D.env.backgroundColor.x = (float)color.r / 255;
-	R3D.env.backgroundColor.y = (float)color.g / 255;
-	R3D.env.backgroundColor.z = (float)color.b / 255;
-	R3D.env.backgroundColor.w = (float)color.a / 255;
+	return &R3D_CACHE_GET(environment);
 }
 
-void R3D_SetAmbientColor(Color color)
+R3DAPI void R3D_SetEnvironment(const R3D_Environment* env)
 {
-	R3D.env.ambientColor = color;
-	R3D.env.ambientLight.x = (float)color.r / 255 * R3D.env.ambientEnergy;
-	R3D.env.ambientLight.y = (float)color.g / 255 * R3D.env.ambientEnergy;
-	R3D.env.ambientLight.z = (float)color.b / 255 * R3D.env.ambientEnergy;
-}
-
-void R3D_SetAmbientEnergy(float energy)
-{
-	R3D.env.ambientEnergy = energy;
-	R3D.env.ambientLight.x = (float)R3D.env.ambientColor.r / 255 * energy;
-	R3D.env.ambientLight.y = (float)R3D.env.ambientColor.g / 255 * energy;
-	R3D.env.ambientLight.z = (float)R3D.env.ambientColor.b / 255 * energy;
-}
-
-void R3D_EnableSkybox(R3D_Skybox skybox)
-{
-	R3D.env.sky = skybox;
-	R3D.env.useSky = true;
-}
-
-void R3D_DisableSkybox(void)
-{
-	R3D.env.useSky = false;
-}
-
-void R3D_SetSkyboxRotation(float pitch, float yaw, float roll)
-{
-	R3D.env.quatSky = QuaternionFromEuler(pitch, yaw, roll);
-}
-
-Vector3 R3D_GetSkyboxRotation(void)
-{
-	return QuaternionToEuler(R3D.env.quatSky);
-}
-
-void R3D_SetSkyboxIntensity(float background, float ambient, float reflection)
-{
-	R3D.env.skyBackgroundIntensity = background;
-	R3D.env.skyAmbientIntensity = ambient;
-	R3D.env.skyReflectIntensity = reflection;
-}
-
-void R3D_GetSkyboxIntensity(float* background, float* ambient, float* reflection)
-{
-	if (background)	*background = R3D.env.skyBackgroundIntensity;
-	if (ambient) *ambient = R3D.env.skyAmbientIntensity;
-	if (reflection) *reflection = R3D.env.skyReflectIntensity;
-}
-
-void R3D_SetSSAO(bool enabled)
-{
-	R3D.env.ssaoEnabled = enabled;
-}
-
-bool R3D_GetSSAO(void)
-{
-	return R3D.env.ssaoEnabled;
-}
-
-void R3D_SetSSAORadius(float value)
-{
-	R3D.env.ssaoRadius = value;
-}
-
-float R3D_GetSSAORadius(void)
-{
-	return R3D.env.ssaoRadius;
-}
-
-void R3D_SetSSAOBias(float value)
-{
-	R3D.env.ssaoBias = value;
-}
-
-float R3D_GetSSAOBias(void)
-{
-	return R3D.env.ssaoBias;
-}
-
-void R3D_SetSSAOIterations(int value)
-{
-	R3D.env.ssaoIterations = value;
-}
-
-int R3D_GetSSAOIterations(void)
-{
-	return R3D.env.ssaoIterations;
-}
-
-void R3D_SetSSAOIntensity(float value)
-{
-	R3D.env.ssaoIntensity = value;
-}
-
-float R3D_GetSSAOIntensity(void)
-{
-	return R3D.env.ssaoIntensity;
-}
-
-void R3D_SetSSAOPower(float value)
-{
-	R3D.env.ssaoPower = value;
-}
-
-float R3D_GetSSAOPower(void)
-{
-	return R3D.env.ssaoPower;
-}
-
-void R3D_SetSSAOLightAffect(float value)
-{
-	R3D.env.ssaoLightAffect = value;
-}
-
-float R3D_GetSSAOLightAffect(void)
-{
-	return R3D.env.ssaoLightAffect;
-}
-
-void R3D_SetBloomMode(R3D_Bloom mode)
-{
-	R3D.env.bloomMode = mode;
-}
-
-R3D_Bloom R3D_GetBloomMode(void)
-{
-	return R3D.env.bloomMode;
-}
-
-void R3D_SetBloomLevels(int value)
-{
-	//R3D.env.bloomLevels = R3D.target.mipChainHs.count;
-}
-
-int R3D_GetBloomLevels(void)
-{
-	return R3D.env.bloomLevels;
-}
-
-void R3D_SetBloomIntensity(float value)
-{
-	R3D.env.bloomIntensity = value;
-}
-
-float R3D_GetBloomIntensity(void)
-{
-	return R3D.env.bloomIntensity;
-}
-
-void R3D_SetBloomFilterRadius(int value)
-{
-	R3D.env.bloomFilterRadius = value;
-}
-
-int R3D_GetBloomFilterRadius(void)
-{
-	return R3D.env.bloomFilterRadius;
-}
-
-void R3D_SetBloomThreshold(float value)
-{
-	R3D.env.bloomThreshold = value;
-
-	r3d_calculate_bloom_prefilter_data();
-}
-
-float R3D_GetBloomThreshold(void)
-{
-	return R3D.env.bloomThreshold;
-}
-
-void R3D_SetBloomSoftThreshold(float value)
-{
-	R3D.env.bloomSoftThreshold = value;
-
-	r3d_calculate_bloom_prefilter_data();
-}
-
-float R3D_GetBloomSoftThreshold(void)
-{
-	return R3D.env.bloomSoftThreshold;
-}
-
-void R3D_SetSSR(bool enabled)
-{
-	R3D.env.ssrEnabled = enabled;
-}
-
-bool R3D_GetSSR(void)
-{
-	return R3D.env.ssrEnabled;
-}
-
-void R3D_SetSSRMaxRaySteps(int maxRaySteps)
-{
-	R3D.env.ssrMaxRaySteps = maxRaySteps;
-}
-
-int R3D_GetSSRMaxRaySteps(void)
-{
-	return R3D.env.ssrMaxRaySteps;
-}
-
-void R3D_SetSSRBinarySearchSteps(int binarySearchSteps)
-{
-	R3D.env.ssrBinarySearchSteps = binarySearchSteps;
-}
-
-int R3D_GetSSRBinarySearchSteps(void)
-{
-	return R3D.env.ssrBinarySearchSteps;
-}
-
-void R3D_SetSSRRayMarchLength(float rayMarchLength)
-{
-	R3D.env.ssrRayMarchLength = rayMarchLength;
-}
-
-float R3D_GetSSRRayMarchLength(void)
-{
-	return R3D.env.ssrRayMarchLength;
-}
-
-void R3D_SetSSRDepthThickness(float depthThickness)
-{
-	R3D.env.ssrDepthThickness = depthThickness;
-}
-
-float R3D_GetSSRDepthThickness(void)
-{
-	return R3D.env.ssrDepthThickness;
-}
-
-void R3D_SetSSRDepthTolerance(float depthTolerance)
-{
-	R3D.env.ssrDepthTolerance = depthTolerance;
-}
-
-float R3D_GetSSRDepthTolerance(void)
-{
-	return R3D.env.ssrDepthTolerance;
-}
-
-void R3D_SetSSRScreenEdgeFade(float start, float end)
-{
-	R3D.env.ssrEdgeFadeStart = start;
-	R3D.env.ssrEdgeFadeEnd = end;
-}
-
-void R3D_GetSSRScreenEdgeFade(float* start, float* end)
-{
-	if (start) *start = R3D.env.ssrEdgeFadeStart;
-	if (end) *end = R3D.env.ssrEdgeFadeEnd;
-}
-
-void R3D_SetFogMode(R3D_Fog mode)
-{
-	R3D.env.fogMode = mode;
-}
-
-R3D_Fog R3D_GetFogMode(void)
-{
-	return R3D.env.fogMode;
-}
-
-void R3D_SetFogColor(Color color)
-{
-	R3D.env.fogColor.x = (float)color.r / 255;
-	R3D.env.fogColor.y = (float)color.g / 255;
-	R3D.env.fogColor.z = (float)color.b / 255;
-}
-
-Color R3D_GetFogColor(void)
-{
-	Color color = { 0 };
-	color.r = (unsigned char)(R3D.env.fogColor.x * 255);
-	color.g = (unsigned char)(R3D.env.fogColor.y * 255);
-	color.b = (unsigned char)(R3D.env.fogColor.z * 255);
-	color.a = 255;
-	return color;
-}
-
-void R3D_SetFogStart(float value)
-{
-	R3D.env.fogStart = value;
-}
-
-float R3D_GetFogStart(void)
-{
-	return R3D.env.fogStart;
-}
-
-void R3D_SetFogEnd(float value)
-{
-	R3D.env.fogEnd = value;
-}
-
-float R3D_GetFogEnd(void)
-{
-	return R3D.env.fogEnd;
-}
-
-void R3D_SetFogDensity(float value)
-{
-	R3D.env.fogDensity = value;
-}
-
-float R3D_GetFogDensity(void)
-{
-	return R3D.env.fogDensity;
-}
-
-void R3D_SetFogSkyAffect(float value)
-{
-	R3D.env.fogSkyAffect = value;
-}
-
-float R3D_GetFogSkyAffect(void)
-{
-	return R3D.env.fogSkyAffect;
-}
-
-void R3D_SetTonemapMode(R3D_Tonemap mode)
-{
-	R3D.env.tonemapMode = mode;
-
-	// NOTE: The output shader for this tonemap mode
-	//       will be loaded during the next output pass
-	//       in `R3D_End()`
-
-	//if (R3D.shader.screen.output[mode].id == 0) {
-	//	r3d_mod_shader_load_post_output(mode);
-	//}
-}
-
-R3D_Tonemap R3D_GetTonemapMode(void)
-{
-	return R3D.env.tonemapMode;
-}
-
-void R3D_SetTonemapExposure(float value)
-{
-	R3D.env.tonemapExposure = value;
-}
-
-float R3D_GetTonemapExposure(void)
-{
-	return R3D.env.tonemapExposure;
-}
-
-void R3D_SetTonemapWhite(float value)
-{
-	R3D.env.tonemapWhite = value;
-}
-
-float R3D_GetTonemapWhite(void)
-{
-	return R3D.env.tonemapExposure;
-}
-
-void R3D_SetBrightness(float value)
-{
-	R3D.env.brightness = value;
-}
-
-float R3D_GetBrightness(void)
-{
-	return R3D.env.brightness;
-}
-
-void R3D_SetContrast(float value)
-{
-	R3D.env.contrast = value;
-}
-
-float R3D_GetContrast(void)
-{
-	return R3D.env.contrast;
-}
-
-void R3D_SetSaturation(float value)
-{
-	R3D.env.saturation = value;
-}
-
-float R3D_GetSaturation(void)
-{
-	return R3D.env.saturation;
-}
-
-void R3D_SetDofMode(R3D_Dof mode)
-{
-	R3D.env.dofMode = mode;
-}
-
-R3D_Dof R3D_GetDofMode(void)
-{
-	return R3D.env.dofMode;
-}
-
-void R3D_SetDofFocusPoint(float value)
-{
-	R3D.env.dofFocusPoint = value;
-}
-
-float R3D_GetDofFocusPoint(void)
-{
-	return R3D.env.dofFocusPoint;
-}
-
-void R3D_SetDofFocusScale(float value)
-{
-	// clamp value between 0.0 and 10.0
-	R3D.env.dofFocusScale = Clamp(value, 0.0f, 10.0f);
-}
-
-float R3D_GetDofFocusScale(void)
-{
-	return R3D.env.dofFocusScale;
-}
-
-void R3D_SetDofMaxBlurSize(float value)
-{
-	R3D.env.dofMaxBlurSize = Clamp(value, 0.0f, 50.0f);
-}
-
-float R3D_GetDofMaxBlurSize(void)
-{
-	return R3D.env.dofMaxBlurSize;
-}
-
-void R3D_SetDofDebugMode(bool enabled)
-{
-	R3D.env.dofDebugMode = enabled;
-}
-
-bool R3D_GetDofDebugMode(void)
-{
-	return R3D.env.dofDebugMode;
+	R3D_CACHE_SET(environment, *env);
 }

+ 2 - 2
src/r3d_model.c

@@ -12,7 +12,7 @@
 #include <r3d/r3d_mesh.h>
 
 #include "./importer/r3d_importer.h"
-#include "./r3d_state.h"
+#include "./modules/r3d_cache.h"
 
 // ========================================
 // INTERNAL FUNCTIONS
@@ -20,7 +20,7 @@
 
 static bool import_model(r3d_importer_t* importer, R3D_Model* model)
 {
-    r3d_importer_texture_cache_t* textureCache = r3d_importer_load_texture_cache(importer, R3D.state.loading.textureFilter);
+    r3d_importer_texture_cache_t* textureCache = r3d_importer_load_texture_cache(importer, R3D_CACHE_GET(textureFilter));
     if (textureCache == NULL) {
         r3d_importer_destroy(importer);
         return false;

+ 4 - 4
src/r3d_skybox.c

@@ -27,7 +27,7 @@
 
 #include "./modules/r3d_primitive.h"
 #include "./modules/r3d_shader.h"
-#include "./r3d_state.h"
+#include "./modules/r3d_cache.h"
 
 // ========================================
 // INTERNAL FUNCTIONS
@@ -209,7 +209,7 @@ static TextureCubemap r3d_skybox_load_cubemap_from_panorama(Image image, int siz
     for (int i = 0; i < 6; i++) {
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemapId, 0);
         glClear(GL_DEPTH_BUFFER_BIT);
-        R3D_SHADER_SET_MAT4(prepare.cubemapFromEquirectangular, uMatView, R3D.misc.matCubeViews[i]);
+        R3D_SHADER_SET_MAT4(prepare.cubemapFromEquirectangular, uMatView, R3D_CACHE_GET(matCubeViews[i]));
         R3D_PRIMITIVE_DRAW_CUBE();
     }
 
@@ -293,7 +293,7 @@ static TextureCubemap r3d_skybox_generate_irradiance(TextureCubemap sky)
     for (int i = 0; i < 6; i++) {
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradianceId, 0);
         glClear(GL_DEPTH_BUFFER_BIT);
-        R3D_SHADER_SET_MAT4(prepare.cubemapIrradiance, uMatView, R3D.misc.matCubeViews[i]);
+        R3D_SHADER_SET_MAT4(prepare.cubemapIrradiance, uMatView, R3D_CACHE_GET(matCubeViews[i]));
         R3D_PRIMITIVE_DRAW_CUBE();
     }
 
@@ -373,7 +373,7 @@ static TextureCubemap r3d_skybox_generate_prefilter(TextureCubemap sky)
 
         // Render all faces of the cubemap
         for (int i = 0; i < 6; i++) {
-            R3D_SHADER_SET_MAT4(prepare.cubemapPrefilter, uMatView, R3D.misc.matCubeViews[i]);
+            R3D_SHADER_SET_MAT4(prepare.cubemapPrefilter, uMatView, R3D_CACHE_GET(matCubeViews[i]));
             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilterId, mip);
             glClear(GL_DEPTH_BUFFER_BIT);
             R3D_PRIMITIVE_DRAW_CUBE();

+ 0 - 45
src/r3d_state.c

@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2025 Le Juez Victor
- *
- * This software is provided "as-is", without any express or implied warranty. In no event
- * will the authors be held liable for any damages arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose, including commercial
- * applications, and to alter it and redistribute it freely, subject to the following restrictions:
- *
- *   1. The origin of this software must not be misrepresented; you must not claim that you
- *   wrote the original software. If you use this software in a product, an acknowledgment
- *   in the product documentation would be appreciated but is not required.
- *
- *   2. Altered source versions must be plainly marked as such, and must not be misrepresented
- *   as being the original software.
- *
- *   3. This notice may not be removed or altered from any source distribution.
- */
-
-#include "./r3d_state.h"
-
-#include <assert.h>
-#include <raylib.h>
-#include <raymath.h>
-#include <rlgl.h>
-#include <glad.h>
-
-/* === Assets === */
-
-#include <assets/brdf_lut_512_rg16_float.raw.h>
-
-/* === Global state definition === */
-
-struct R3D_State R3D = { 0 };
-
-/* === Helper functions === */
-
-void r3d_calculate_bloom_prefilter_data(void)
-{
-    float knee = R3D.env.bloomThreshold * R3D.env.bloomSoftThreshold;
-    R3D.env.bloomPrefilter.x = R3D.env.bloomThreshold;
-    R3D.env.bloomPrefilter.y = R3D.env.bloomPrefilter.x - knee;
-    R3D.env.bloomPrefilter.z = 2.0f * knee;
-    R3D.env.bloomPrefilter.w = 0.25f / (knee + 0.00001f);
-}

+ 0 - 140
src/r3d_state.h

@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 2025 Le Juez Victor
- *
- * This software is provided "as-is", without any express or implied warranty. In no event
- * will the authors be held liable for any damages arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose, including commercial
- * applications, and to alter it and redistribute it freely, subject to the following restrictions:
- *
- *   1. The origin of this software must not be misrepresented; you must not claim that you
- *   wrote the original software. If you use this software in a product, an acknowledgment
- *   in the product documentation would be appreciated but is not required.
- *
- *   2. Altered source versions must be plainly marked as such, and must not be misrepresented
- *   as being the original software.
- *
- *   3. This notice may not be removed or altered from any source distribution.
- */
-
-#ifndef R3D_STATE_H
-#define R3D_STATE_H
-
-#include <r3d/r3d_environment.h>
-#include <r3d/r3d_material.h>
-#include <r3d/r3d_core.h>
-#include <r3d/r3d_mesh.h>
-#include <glad.h>
-
-#include "./details/r3d_frustum.h"
-
-/* === Global R3D State === */
-
-extern struct R3D_State {
-
-    // Environment data
-    struct {
-
-        Vector4 backgroundColor;        // Used as default albedo color when skybox is disabled (scene pass)
-
-        Color ambientColor;             // Base color of ambient light used when skybox is disabled (light pass)
-        float ambientEnergy;            // Multiplicative energy for ambient light when skybox is disabled (light pass)
-        Vector3 ambientLight;           // Total ambient light contribution when skybox is disabled (light pass)
-                                        
-        Quaternion quatSky;             // Rotation of the skybox (scene / light passes)
-        R3D_Skybox sky;                 // Skybox textures (scene / light passes)
-        bool useSky;                    // Flag to indicate if skybox is enabled (light pass)
-        float skyBackgroundIntensity;   // Intensity of the visible background from the skybox (scene / light passes) 
-        float skyAmbientIntensity;      // Intensity of the ambient light from the skybox (light pass)
-        float skyReflectIntensity;      // Intensity of reflections from the skybox (light pass)
-                                        
-        bool ssaoEnabled;               // (pre-light pass)
-        float ssaoRadius;               // (pre-light pass)
-        float ssaoBias;                 // (pre-light pass)
-        int ssaoIterations;             // (pre-light pass)
-        float ssaoIntensity;            // (pre-light pass)
-        float ssaoPower;                // (light pass)
-        float ssaoLightAffect;          // (scene pass)
-                                        
-        R3D_Bloom bloomMode;            // (post pass)
-        float bloomIntensity;           // (post pass)
-        int bloomLevels;                // (gen pass)
-        int bloomFilterRadius;          // (gen pass)
-        float bloomThreshold;           // (gen pass)
-        float bloomSoftThreshold;       // (gen pass)
-        Vector4 bloomPrefilter;         // (gen pass)
-
-        bool ssrEnabled;                // (post pass)
-        int ssrMaxRaySteps;             // (post pass)
-        int ssrBinarySearchSteps;       // (post pass)
-        float ssrRayMarchLength;        // (post pass)
-        float ssrDepthThickness;        // (post pass)
-        float ssrDepthTolerance;        // (post pass)
-        float ssrEdgeFadeStart;         // (post pass)
-        float ssrEdgeFadeEnd;           // (post pass)
-                                        
-        R3D_Fog fogMode;                // (post pass)
-        Vector3 fogColor;               // (post pass)
-        float fogStart;                 // (post pass)
-        float fogEnd;                   // (post pass)
-        float fogDensity;               // (post pass)
-        float fogSkyAffect;             // (post pass)
-                                        
-        R3D_Tonemap tonemapMode;        // (post pass)
-        float tonemapExposure;          // (post pass)
-        float tonemapWhite;             // (post pass)
-                                        
-        float brightness;               // (post pass)
-        float contrast;                 // (post pass)
-        float saturation;               // (post pass)
-
-        R3D_Dof dofMode;                // (post pass)
-        float dofFocusPoint;            // (post pass)
-        float dofFocusScale;            // (post pass)
-        float dofMaxBlurSize;           // (post pass)
-        bool dofDebugMode;              // (post pass)
-
-    } env;
-
-    // State data
-    struct {
-
-        // Camera transformations
-        struct {
-            Matrix view, invView;
-            Matrix proj, invProj;
-            Matrix viewProj;
-            Vector3 viewPos;
-        } transform;
-
-        // Frustum data
-        struct {
-            r3d_frustum_t shape;
-            BoundingBox aabb;
-        } frustum;
-
-        // Loading param
-        struct {
-            TextureFilter textureFilter;       //< Texture filter used by R3D during model loading
-        } loading;
-
-        // Active layers
-        R3D_Layer layers;
-
-        // Miscellaneous flags
-        R3D_Flags flags;
-
-    } state;
-
-    // Misc data
-    struct {
-        Matrix matCubeViews[6];
-    } misc;
-
-} R3D;
-
-/* === Helper functions === */
-
-void r3d_calculate_bloom_prefilter_data(void);
-
-#endif // R3D_STATE_H

+ 5 - 5
src/r3d_utils.c

@@ -10,7 +10,7 @@
 
 #include "./modules/r3d_texture.h"
 #include "./modules/r3d_target.h"
-#include "./r3d_state.h"
+#include "./modules/r3d_cache.h"
 
 // ========================================
 // PUBLIC API
@@ -73,22 +73,22 @@ Texture2D R3D_GetBufferDepth(void)
 
 Matrix R3D_GetMatrixView(void)
 {
-    return R3D.state.transform.view;
+    return R3D_CACHE_GET(viewport.view);
 }
 
 Matrix R3D_GetMatrixInvView(void)
 {
-    return R3D.state.transform.invView;
+    return R3D_CACHE_GET(viewport.invView);
 }
 
 Matrix R3D_GetMatrixProjection(void)
 {
-    return R3D.state.transform.proj;
+    return R3D_CACHE_GET(viewport.proj);
 }
 
 Matrix R3D_GetMatrixInvProjection(void)
 {
-    return R3D.state.transform.invProj;
+    return R3D_CACHE_GET(viewport.invProj);
 }
 
 void R3D_DrawBufferAlbedo(float x, float y, float w, float h)