Merge remote-tracking branch 'origin/main' into sdl-try-2

This commit is contained in:
Stepanov Igor 2025-10-04 23:24:45 +03:00
commit 9dad26232a
264 changed files with 8609 additions and 2047 deletions

View file

@ -38,6 +38,10 @@ bool Font::isPrintableChar(uint codepoint) const {
}
}
int FontMetrics::calcWidth(std::wstring_view text, size_t offset, size_t length) const {
return std::min(text.length() - offset, length) * glyphInterval;
}
int Font::calcWidth(std::wstring_view text, size_t length) const {
return calcWidth(text, 0, length);
}
@ -57,6 +61,14 @@ static inline void draw_glyph(
const FontStyle& style
) {
for (int i = 0; i <= style.bold; i++) {
glm::vec4 color;
if (style.color == glm::vec4(1, 1, 1, 1)) {
color = batch.getColor();
} else {
color = style.color;
}
batch.sprite(
pos.x + (offset.x + i / (right.x/glyphInterval/2.0f)) * right.x,
pos.y + offset.y * right.y,
@ -65,7 +77,7 @@ static inline void draw_glyph(
-0.15f * style.italic,
16,
c,
batch.getColor() * style.color
color
);
}
}
@ -81,6 +93,15 @@ static inline void draw_glyph(
const FontStyle& style
) {
for (int i = 0; i <= style.bold; i++) {
glm::vec4 color;
if (style.color == glm::vec4(1, 1, 1, 1)) {
color = batch.getColor();
} else {
color = style.color;
}
batch.sprite(
pos + right * (offset.x + i) + up * offset.y,
up, right / glyphInterval,
@ -88,7 +109,7 @@ static inline void draw_glyph(
0.5f,
16,
c,
batch.getColor() * style.color
color
);
}
}

View file

@ -4,7 +4,9 @@
#include <string>
#include <vector>
#include <glm/glm.hpp>
#include "typedefs.hpp"
#include "FontMetics.hpp"
class Texture;
class Batch2D;
@ -90,4 +92,8 @@ public:
) const;
const Texture* getPage(int page) const;
FontMetrics getMetrics() const {
return {lineHeight, yoffset, glyphInterval};
}
};

View file

@ -0,0 +1,11 @@
#pragma once
#include <string>
struct FontMetrics {
int lineHeight;
int yoffset;
int glyphInterval = 8;
int calcWidth(std::wstring_view text, size_t offset=0, size_t length=-1) const;
};

View file

@ -1,5 +1,6 @@
#include "ImageData.hpp"
#include <glm/glm.hpp>
#include <assert.h>
#include <stdexcept>
#include <cstring>
@ -187,6 +188,41 @@ void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color
}
}
template<uint channels>
static void draw_rect(ImageData& image, int dstX, int dstY, int width, int height, const glm::ivec4& color) {
ubyte* data = image.getData();
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
int x1 = glm::min(glm::max(dstX, 0), imageWidth - 1);
int y1 = glm::min(glm::max(dstY, 0), imageHeight - 1);
int x2 = glm::min(glm::max(dstX + width, 0), imageWidth - 1);
int y2 = glm::min(glm::max(dstY + height, 0), imageHeight - 1);
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
int index = (y * imageWidth + x) * channels;
for (int i = 0; i < channels; i++) {
data[index + i] = color[i];
}
}
}
}
void ImageData::drawRect(int x, int y, int width, int height, const glm::ivec4& color) {
switch (format) {
case ImageFormat::rgb888:
draw_rect<3>(*this, x, y, width, height, color);
break;
case ImageFormat::rgba8888:
draw_rect<4>(*this, x, y, width, height, color);
break;
default:
break;
}
}
void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
ubyte* source = image.getData();
uint srcwidth = image.getWidth();

View file

@ -28,6 +28,7 @@ public:
void flipY();
void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
void drawRect(int x, int y, int width, int height, const glm::ivec4& color);
void blit(const ImageData& image, int x, int y);
void extrude(int x, int y, int w, int h);
void fixAlphaColor();

View file

@ -128,15 +128,21 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi
}
static GLuint compile_program(
const Shader::Source& vertexSource, const Shader::Source& fragmentSource
const Shader::Source& vertexSource,
const Shader::Source& fragmentSource,
const std::vector<std::string>& defines
) {
auto& preprocessor = *Shader::preprocessor;
auto vertexCode = std::move(
preprocessor.process(vertexSource.file, vertexSource.code).code
preprocessor
.process(vertexSource.file, vertexSource.code, false, defines)
.code
);
auto fragmentCode = std::move(
preprocessor.process(fragmentSource.file, fragmentSource.code).code
preprocessor
.process(fragmentSource.file, fragmentSource.code, false, defines)
.code
);
const GLchar* vCode = vertexCode.c_str();
@ -166,8 +172,8 @@ static GLuint compile_program(
return program;
}
void Shader::recompile() {
GLuint newProgram = compile_program(vertexSource, fragmentSource);
void Shader::recompile(const std::vector<std::string>& defines) {
GLuint newProgram = compile_program(vertexSource, fragmentSource, defines);
glDeleteProgram(id);
id = newProgram;
uniformLocations.clear();
@ -178,7 +184,7 @@ std::unique_ptr<Shader> Shader::create(
Source&& vertexSource, Source&& fragmentSource
) {
return std::make_unique<Shader>(
compile_program(vertexSource, fragmentSource),
compile_program(vertexSource, fragmentSource, {}),
std::move(vertexSource),
std::move(fragmentSource)
);

View file

@ -4,6 +4,7 @@
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
#include <glm/glm.hpp>
@ -50,7 +51,7 @@ public:
void uniform4v(const std::string& name, int length, const float* v);
/// @brief Re-preprocess source code and re-compile shader program
void recompile();
void recompile(const std::vector<std::string>& defines);
/// @brief Create shader program using vertex and fragment shaders source.
/// @return linked shader program containing vertex and fragment shaders

View file

@ -1,47 +0,0 @@
#include "ShadowMap.hpp"
#include <GL/glew.h>
ShadowMap::ShadowMap(int resolution) : resolution(resolution) {
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
float border[4] {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR, border);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
ShadowMap::~ShadowMap() {
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &depthMap);
}
void ShadowMap::bind() {
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_DEPTH_BUFFER_BIT);
}
void ShadowMap::unbind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
uint ShadowMap::getDepthMap() const {
return depthMap;
}
int ShadowMap::getResolution() const {
return resolution;
}

View file

@ -1,18 +0,0 @@
#pragma once
#include "typedefs.hpp"
class ShadowMap {
public:
ShadowMap(int resolution);
~ShadowMap();
void bind();
void unbind();
uint getDepthMap() const;
int getResolution() const;
private:
uint fbo;
uint depthMap;
int resolution;
};

View file

@ -0,0 +1,203 @@
#include "Shadows.hpp"
#include <GL/glew.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/norm.hpp>
#include "assets/Assets.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/commons.hpp"
#include "world/Level.hpp"
#include "world/Weather.hpp"
#include "world/World.hpp"
using namespace advanced_pipeline;
inline constexpr int MIN_SHADOW_MAP_RES = 512;
inline constexpr GLenum TEXTURE_MAIN = GL_TEXTURE0;
class ShadowMap {
public:
ShadowMap(int resolution) : resolution(resolution) {
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(
GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE
);
float border[4] {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0
);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
~ShadowMap() {
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &depthMap);
}
void bind(){
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_DEPTH_BUFFER_BIT);
}
void unbind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
uint getDepthMap() const {
return depthMap;
}
int getResolution() const {
return resolution;
}
private:
uint fbo;
uint depthMap;
int resolution;
};
Shadows::Shadows(const Level& level) : level(level) {}
Shadows::~Shadows() = default;
void Shadows::setQuality(int quality) {
int resolution = MIN_SHADOW_MAP_RES << quality;
if (quality > 0 && !shadows) {
shadowMap = std::make_unique<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(resolution);
shadows = true;
} else if (quality == 0 && shadows) {
shadowMap.reset();
wideShadowMap.reset();
shadows = false;
}
if (shadows && shadowMap->getResolution() != resolution) {
shadowMap = std::make_unique<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(resolution);
}
this->quality = quality;
}
void Shadows::setup(Shader& shader, const Weather& weather) {
if (shadows) {
const auto& worldInfo = level.getWorld()->getInfo();
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
float shadowsOpacity = 1.0f - cloudsIntensity;
shadowsOpacity *= glm::sqrt(glm::abs(
glm::mod((worldInfo.daytime + 0.5f) * 2.0f, 1.0f) * 2.0f - 1.0f
));
shader.uniform1i("u_screen", 0);
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
shader.uniform3f("u_sunDir", shadowCamera.front);
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
shader.uniform1f("u_shadowsOpacity", shadowsOpacity); // TODO: make it configurable
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
glActiveTexture(TEXTURE_MAIN);
}
}
void Shadows::refresh(const Camera& camera, const DrawContext& pctx, std::function<void(Camera&)> renderShadowPass) {
static int frameid = 0;
if (shadows) {
if (frameid % 2 == 0) {
generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f, renderShadowPass);
} else {
generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f, renderShadowPass);
}
}
frameid++;
}
void Shadows::generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale,
std::function<void(Camera&)> renderShadowPass
) {
auto world = level.getWorld();
const auto& worldInfo = world->getInfo();
int resolution = shadowMap.getResolution();
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
float shadowMapSize = resolution * shadowMapScale;
glm::vec3 basePos = glm::floor(camera.position / 4.0f) * 4.0f;
glm::vec3 prevPos = shadowCamera.position;
shadowCamera = Camera(
glm::distance2(prevPos, basePos) > 25.0f ? basePos : prevPos,
shadowMapSize
);
shadowCamera.near = 0.1f;
shadowCamera.far = 1000.0f;
shadowCamera.perspective = false;
shadowCamera.setAspectRatio(1.0f);
float t = worldInfo.daytime - 0.25f;
if (t < 0.0f) {
t += 1.0f;
}
t = fmod(t, 0.5f);
float sunCycleStep = 1.0f / 500.0f;
float sunAngle = glm::radians(
90.0f -
((static_cast<int>(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f
);
float sunAltitude = glm::pi<float>() * 0.25f;
shadowCamera.rotate(
-glm::cos(sunAngle + glm::pi<float>() * 0.5f) * sunAltitude,
sunAngle - glm::pi<float>() * 0.5f,
glm::radians(0.0f)
);
shadowCamera.position -= shadowCamera.front * 500.0f;
shadowCamera.position += shadowCamera.up * 0.0f;
shadowCamera.position += camera.front * 0.0f;
auto view = shadowCamera.getView();
auto currentPos = shadowCamera.position;
auto topRight = shadowCamera.right + shadowCamera.up;
auto min = view * glm::vec4(currentPos - topRight * shadowMapSize * 0.5f, 1.0f);
auto max = view * glm::vec4(currentPos + topRight * shadowMapSize * 0.5f, 1.0f);
shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f));
{
auto sctx = pctx.sub();
sctx.setDepthTest(true);
sctx.setCullFace(true);
sctx.setViewport({resolution, resolution});
shadowMap.bind();
if (renderShadowPass) {
renderShadowPass(shadowCamera);
}
shadowMap.unbind();
}
}

View file

@ -0,0 +1,46 @@
#pragma once
#include <memory>
#include <functional>
#include "typedefs.hpp"
#include "window/Camera.hpp"
class Shader;
class Level;
class Assets;
struct Weather;
class DrawContext;
struct EngineSettings;
class ShadowMap;
class Shadows {
public:
Shadows(const Level& level);
~Shadows();
void setup(Shader& shader, const Weather& weather);
void setQuality(int quality);
void refresh(
const Camera& camera,
const DrawContext& pctx,
std::function<void(Camera&)> renderShadowPass
);
private:
const Level& level;
bool shadows = false;
Camera shadowCamera;
Camera wideShadowCamera;
std::unique_ptr<ShadowMap> shadowMap;
std::unique_ptr<ShadowMap> wideShadowMap;
int quality = 0;
void generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale,
std::function<void(Camera&)> renderShadowPass
);
};

View file

@ -25,6 +25,9 @@ void TextureAnimator::update(float delta) {
std::unordered_set<uint> changedTextures;
for (auto& elem : animations) {
if (elem.frames.empty()) {
continue;
}
elem.timer += delta;
size_t frameNum = elem.currentFrame;
Frame frame = elem.frames[elem.currentFrame];