fix(render): Fix 3D bed positioning and implement dynamic lighting for custom models

This commit is contained in:
Игорь Федосеев 2026-10-07 19:18:16 +03:00
parent b3b69f997e
commit 527b3e4c7d
5 changed files with 219 additions and 2 deletions

View file

@ -0,0 +1,78 @@
#version 330
// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
in vec2 fragTexCoord;
in vec4 fragColor;
in vec3 fragNormal;
// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;
// Output fragment color
out vec4 finalColor;
#define MAX_LIGHTS 4
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
struct Light {
int enabled;
int type;
vec3 position;
vec3 target;
vec4 color;
};
uniform Light lights[MAX_LIGHTS];
uniform vec4 ambient;
uniform vec3 viewPos;
void main()
{
vec4 texelColor = texture(texture0, fragTexCoord);
if (texelColor.a < 0.1) discard;
vec3 normal = normalize(fragNormal);
vec3 viewD = normalize(viewPos - fragPosition);
vec3 lightTotal = ambient.rgb;
vec3 specularTotal = vec3(0.0);
for (int i = 0; i < MAX_LIGHTS; i++)
{
if (lights[i].enabled == 1)
{
vec3 lightDir = vec3(0.0);
float attenuation = 1.0;
if (lights[i].type == LIGHT_DIRECTIONAL)
{
lightDir = -normalize(lights[i].target - lights[i].position);
}
else // LIGHT_POINT
{
vec3 lightVec = lights[i].position - fragPosition;
float dist = length(lightVec);
lightDir = normalize(lightVec);
attenuation = 1.0 / (1.0 + 0.10 * dist + 0.03 * dist * dist);
}
float NdotL = max(dot(normal, lightDir), 0.0);
lightTotal += lights[i].color.rgb * NdotL * attenuation;
if (NdotL > 0.0)
{
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewD, reflectDir), 0.0), 16.0);
specularTotal += lights[i].color.rgb * spec * attenuation * 0.25;
}
}
}
vec4 tint = colDiffuse * fragColor;
vec3 result = (texelColor.rgb * tint.rgb) * lightTotal + specularTotal;
finalColor = vec4(result, texelColor.a * tint.a);
}

View file

@ -0,0 +1,30 @@
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
uniform mat4 matNormal;
// Output vertex attributes (to fragment shader)
out vec3 fragPosition;
out vec2 fragTexCoord;
out vec4 fragColor;
out vec3 fragNormal;
void main()
{
// Send vertex attributes to fragment shader
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0)));
// Calculate final vertex position
gl_Position = mvp*vec4(vertexPosition, 1.0);
}

View file

@ -137,6 +137,10 @@ void DebugSystem::applyWarp() {
m_game->startNewRun();
m_game->m_player.setGridPosition(2, 4);
m_game->m_player.setFacing(Direction::EAST);
} else if (w == "HUB_BED_FRONT") {
m_game->startNewRun();
m_game->m_player.setGridPosition(4, 3);
m_game->m_player.setFacing(Direction::SOUTH);
} else if (w == "EXPLORATION") {
m_game->startNewRun();
if (m_config.targetFloor > 1) {

View file

@ -1228,6 +1228,33 @@ void CyberRenderer::generateTextures() {
m_texAncientCeil = createAncientCeilTexture();
m_texAncientWood = createAncientWoodTexture();
// Load custom 3D model dynamic lighting shader
std::vector<std::pair<std::string, std::string>> lightingShaderPaths = {
{ "assets/shaders/lighting.vs", "assets/shaders/lighting.fs" },
{ "../assets/shaders/lighting.vs", "../assets/shaders/lighting.fs" },
{ "shaders/lighting.vs", "shaders/lighting.fs" }
};
m_modelLightingLoaded = false;
for (const auto& sp : lightingShaderPaths) {
if (FileExists(sp.first.c_str()) && FileExists(sp.second.c_str())) {
m_shaderModelLighting = LoadShader(sp.first.c_str(), sp.second.c_str());
if (m_shaderModelLighting.id != 0) {
m_shaderModelLighting.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(m_shaderModelLighting, "viewPos");
m_locLightViewPos = m_shaderModelLighting.locs[SHADER_LOC_VECTOR_VIEW];
m_locLightAmbient = GetShaderLocation(m_shaderModelLighting, "ambient");
for (int i = 0; i < 4; ++i) {
m_modelLights[i].enabled = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].enabled", i));
m_modelLights[i].type = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].type", i));
m_modelLights[i].position = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].position", i));
m_modelLights[i].target = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].target", i));
m_modelLights[i].color = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].color", i));
}
m_modelLightingLoaded = true;
break;
}
}
}
// Load custom imported 3D Bed model
std::vector<std::string> bedModelPaths = {
"assets/models/Bed.obj",
@ -1250,6 +1277,11 @@ void CyberRenderer::generateTextures() {
}
if (m_bedModelLoaded) {
if (m_modelLightingLoaded) {
for (int i = 0; i < m_bedModel.materialCount; ++i) {
m_bedModel.materials[i].shader = m_shaderModelLighting;
}
}
for (const auto& tp : bedTexPaths) {
if (FileExists(tp.c_str())) {
m_texBedModel = LoadTexture(tp.c_str());
@ -1268,6 +1300,11 @@ void CyberRenderer::generateTextures() {
void CyberRenderer::unloadTextures() {
if (!m_texturesLoaded) return;
if (m_modelLightingLoaded) {
UnloadShader(m_shaderModelLighting);
m_modelLightingLoaded = false;
}
if (m_bedModelLoaded) {
UnloadModel(m_bedModel);
if (m_texBedModel.id != 0) {
@ -1947,6 +1984,60 @@ void CyberRenderer::renderCombat3D(const Dungeon& dungeon, const Player& player,
endPostProcess(animTime);
}
void CyberRenderer::updateHubModelLighting(const Camera3D& camera, float animTime) {
if (!m_modelLightingLoaded) return;
// Camera view position for specular highlights
float camPos[3] = { camera.position.x, camera.position.y, camera.position.z };
SetShaderValue(m_shaderModelLighting, m_locLightViewPos, camPos, SHADER_UNIFORM_VEC3);
// Cyber-noir ambient illumination (subtle slate blue-gray base)
float ambient[4] = { 0.18f, 0.18f, 0.22f, 1.0f };
SetShaderValue(m_shaderModelLighting, m_locLightAmbient, ambient, SHADER_UNIFORM_VEC4);
struct HubLight {
int enabled;
int type;
Vector3 pos;
Vector3 target;
float color[4];
};
float deskFlicker = 0.90f + 0.10f * std::sin(animTime * 6.0f);
float portalPulse = 0.75f + 0.25f * std::sin(animTime * 3.5f);
HubLight lights[4] = {
// Light 0: Megacity Skyline Window glow (East wall) - cool neon cobalt/violet
{ 1, 1, { 11.0f, 1.8f, 6.0f }, { 0, 0, 0 }, { 0.35f, 0.40f, 0.85f, 1.0f } },
// Light 1: Workstation CRT monitors (West desk) - cyan flicker
{ 1, 1, { 1.3f, 1.3f, 5.0f }, { 0, 0, 0 }, { 0.15f * deskFlicker, 0.55f * deskFlicker, 0.85f * deskFlicker, 1.0f } },
// Light 2: Cyberspace Gateway Portal (North wall) - emerald pulse
{ 1, 1, { 6.0f, 1.8f, 1.0f }, { 0, 0, 0 }, { 0.10f * portalPulse, 0.80f * portalPulse, 0.40f * portalPulse, 1.0f } },
// Light 3: Ceiling warm ambient lamp (Above room center/bed approach) - warm tungsten
{ 1, 1, { 8.5f, 2.7f, 8.5f }, { 0, 0, 0 }, { 0.70f, 0.58f, 0.42f, 1.0f } }
};
for (int i = 0; i < 4; ++i) {
if (m_modelLights[i].enabled != -1) {
SetShaderValue(m_shaderModelLighting, m_modelLights[i].enabled, &lights[i].enabled, SHADER_UNIFORM_INT);
}
if (m_modelLights[i].type != -1) {
SetShaderValue(m_shaderModelLighting, m_modelLights[i].type, &lights[i].type, SHADER_UNIFORM_INT);
}
if (m_modelLights[i].position != -1) {
float p[3] = { lights[i].pos.x, lights[i].pos.y, lights[i].pos.z };
SetShaderValue(m_shaderModelLighting, m_modelLights[i].position, p, SHADER_UNIFORM_VEC3);
}
if (m_modelLights[i].target != -1) {
float t[3] = { lights[i].target.x, lights[i].target.y, lights[i].target.z };
SetShaderValue(m_shaderModelLighting, m_modelLights[i].target, t, SHADER_UNIFORM_VEC3);
}
if (m_modelLights[i].color != -1) {
SetShaderValue(m_shaderModelLighting, m_modelLights[i].color, lights[i].color, SHADER_UNIFORM_VEC4);
}
}
}
void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) {
if (!m_texturesLoaded) {
generateTextures();
@ -1963,6 +2054,8 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
updateHubModelLighting(m_camera, animTime);
BeginMode3D(m_camera);
const float tileSize = 2.0f;
@ -2136,7 +2229,7 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
// 8. BED (South-East corner: X in [8.5f, 11.2f], Z in [8.8f, 11.2f])
if (m_bedModelLoaded) {
// Draw user custom imported 3D Bed model
Vector3 customBedPos = { 9.8f, 0.0f, 10.1f };
Vector3 customBedPos = { 9.7f, 0.0f, 9.75f };
float customBedScale = 0.024f;
DrawModelEx(m_bedModel, customBedPos, Vector3{ 0.0f, 1.0f, 0.0f }, 0.0f,
Vector3{ customBedScale, customBedScale, customBedScale }, WHITE);

View file

@ -71,10 +71,22 @@ private:
Texture2D m_texWindowView{};
Texture2D m_texCyberGate{};
// Custom 3D Models
// Custom 3D Models & Dynamic Lighting
Model m_bedModel{};
Texture2D m_texBedModel{};
bool m_bedModelLoaded = false;
Shader m_shaderModelLighting{};
bool m_modelLightingLoaded = false;
int m_locLightAmbient = -1;
int m_locLightViewPos = -1;
struct ModelLightLocs {
int enabled = -1;
int type = -1;
int position = -1;
int target = -1;
int color = -1;
} m_modelLights[4];
void updateHubModelLighting(const Camera3D& camera, float animTime);
// Ancient Palace Storeroom Procedural Textures
Texture2D m_texAncientWall{};