Commit 623cbb87 authored by Shu's avatar Shu 🍠
Browse files

World pass VK and GL Mipmap load

parent 15f5b090
......@@ -106,10 +106,12 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons
actions |= Actions::FillMode;
}
ImGui::Text("Textures '%s'", options.renderer.textures.c_str()); // MAYBE: select
if (ImGui::SliderFloat("LOD", &options.renderer.mipMapLOD, -1, 1) |
ImGui::SliderInt("Anisotropy", &options.renderer.anisotropy, 0, 8)) {
if (ImGui::SliderInt("Quality", &options.renderer.textureQuality, 0, 200, "%d%%") |
ImGui::SliderInt("Sharpness", &options.renderer.textureSharpness, 0, 8)) {
actions |= Actions::RendererTextures;
}
if(ImGui::IsItemHovered())
ImGui::SetTooltip("Better texture quality especially at low angles");
ImGui::End();
}
......
......@@ -14,7 +14,7 @@ std::unique_ptr<TextureArray> (*TextureArray::loadFunc)(const std::vector<std::s
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
std::optional<Image::properties> Image::Read(const std::string& imagepath, std::vector<unsigned char>& data) {
std::optional<Image::properties> Image::Read(const std::string& imagepath, std::vector<unsigned char>& data, bool srgb) {
unsigned char header[124];
properties info;
......@@ -56,15 +56,18 @@ std::optional<Image::properties> Image::Read(const std::string& imagepath, std::
switch(fourCC)
{
case FOURCC_DXT3:
info.format = Format::BC2;
info.format = srgb ? Format::BC2 : Format::BC2_UNORM;
break;
case FOURCC_DXT5:
info.format = Format::BC3;
info.format = srgb ? Format::BC3 : Format::BC3_UNORM;
break;
//MAYBE: VK_FORMAT_BC6H_SFLOAT_BLOCK
default:
return {};
}
//FIXME: miplevels with size < block size (2 last) are corrupted
const uint maxMipmapLevels = 1 + std::floor(std::log2(std::max(info.size.height, info.size.width))) - 2;
info.mipmapLevels = std::min(maxMipmapLevels, info.mipmapLevels);
return info;
}
\ No newline at end of file
......@@ -20,8 +20,10 @@ public:
enum class Format {
/// DXT3 RGBA SRGB
BC2 = 136,
BC2_UNORM = 135,
/// DXT5 RGBA SRGB
BC3 = 138,
BC3_UNORM = 137,
// MAYBE: R8G8B8A8
// MAYBE: For HDR BC6H_SFLOAT = 144,
};
......@@ -93,7 +95,7 @@ public:
return requirement(props, Layout::SHADER_READ_ONLY, Usage::SAMPLED, Aspect::COLOR, 1, arraySize, cube); }
};
static std::optional<properties> Read(const std::string&, std::vector<unsigned char>& data);
static std::optional<properties> Read(const std::string&, std::vector<unsigned char>& data, bool srgb = true);
};
/// Const image (single texture2D) with sampler
......@@ -112,6 +114,7 @@ public:
Wrap wrap = Wrap::MIRRORED_REPEAT;
int anisotropy = 0;
bool mipmap = true;
float mipmapLod = 0;
};
/// Only supports dds files
......
......@@ -14,6 +14,9 @@ using namespace render::gl;
constexpr auto GL_MAJOR = 4;
constexpr auto GL_MINOR = 6;
#define CONTENT_DIR "content/"
#define TEXTURES_DIR CONTENT_DIR "textures/"
Renderer::Renderer(const renderOptions& options):
IndicatorCubeBuffer(Indicator::CUBE.first, Indicator::CUBE.second) {
glGenVertexArrays(1, &VertexArrayID);
......@@ -25,7 +28,7 @@ Renderer::Renderer(const renderOptions& options):
IndicatorPass = std::make_unique<pass::ColorProgram>();
FogColor = glm::vec3(options.clear_color.x, options.clear_color.y, options.clear_color.z);
loadTextures(options.textures, options.mipMapLOD, options.anisotropy);
loadTextures(options.textures, options.getMipmapLodBias(), options.getAnisotropy());
}
Renderer::~Renderer() {
......@@ -107,10 +110,12 @@ std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> Rend
};
}
size_t Renderer::drawIndicatorCube(glm::mat4 model) {
std::function<size_t(glm::mat4)> Renderer::beginIndicatorPass() {
IndicatorPass->useIt();
return [&](glm::mat4 model) {
IndicatorPass->setup(this, model);
return IndicatorCubeBuffer.draw();
};
}
void Renderer::postProcess() {
......@@ -136,21 +141,25 @@ void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD, f
}
void Renderer::unloadTextures() {
glDeleteTextures(1, &HOSAtlas);
glDeleteTextures(1, &NormalAtlas);
glDeleteTextures(1, &TextureAtlas);
HOSAtlas.reset();
NormalAtlas.reset();
TextureAtlas.reset();
}
void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) {
std::vector<std::string> terrainTextures;
auto makePaths = [&](const std::string& suffix) {
terrainTextures.clear();
for(const auto& texture: world::materials::textures) {
terrainTextures.emplace_back(texturePath + "/terrain/" + texture);
terrainTextures.emplace_back(TEXTURES_DIR + texturePath + "/terrain/" + texture + suffix + ".dds");
}
const auto ani = anisotropy >= 1 ? (1 << (static_cast<int>(anisotropy)-1)) : 0;
TextureAtlas = pass::Program::loadTextureArray(terrainTextures, "", mipMapLOD, ani);
NormalAtlas = pass::Program::loadTextureArray(terrainTextures, ".nrm", mipMapLOD, ani);
HOSAtlas = pass::Program::loadTextureArray(terrainTextures, ".hos", mipMapLOD, ani);
return terrainTextures;
};
auto sampling = Texture::sampling{true, true, Texture::Wrap::REPEAT, anisotropy, true, mipMapLOD};
TextureAtlas = TextureArray::LoadFromFiles(makePaths(""), sampling);
NormalAtlas = TextureArray::LoadFromFiles(makePaths(".nrm"), sampling);
HOSAtlas = TextureArray::LoadFromFiles(makePaths(".hos"), sampling);
Skybox = pass::Program::loadTextureCube(texturePath + "/sky/Space_tray");
Skybox = TextureCube::LoadFromFiles(TEXTURES_DIR + texturePath + "/sky/Space_tray.cube", {});
}
void Renderer::lookFrom(const Camera& camera) {
......
......@@ -7,6 +7,7 @@
#include "pass/SkyProgram.hpp"
#include "pass/ColorProgram.hpp"
#include "api/Models.hpp"
#include "api/Images.hpp"
namespace render::gl {
......@@ -28,22 +29,22 @@ public:
}
GLuint getTextureAtlas() const {
return TextureAtlas;
return TextureAtlas->getId();
}
GLuint getNormalAtlas() const {
return NormalAtlas;
return NormalAtlas->getId();
}
GLuint getHOSAtlas() const {
return HOSAtlas;
return HOSAtlas->getId();
}
GLuint getSkyTexture() const {
return Skybox;
return Skybox->getId();
}
void beginFrame() override;
std::function<size_t(render::LodModel *const, glm::mat4, glm::vec4, float)> beginWorldPass() override;
std::function<size_t(render::Model *const, const std::vector<glm::mat4>&)> beginEntityPass() override;
size_t drawIndicatorCube(glm::mat4 model) override;
std::function<size_t(glm::mat4)> beginIndicatorPass() override;
void postProcess() override;
void endFrame() override;
void swapBuffer(Window&) override;
......@@ -73,10 +74,10 @@ private:
glm::mat4 ProjectionMatrix;
glm::mat4 ViewMatrix;
GLuint TextureAtlas;
GLuint NormalAtlas;
GLuint HOSAtlas;
GLuint Skybox;
std::unique_ptr<TextureArray> TextureAtlas;
std::unique_ptr<TextureArray> NormalAtlas;
std::unique_ptr<TextureArray> HOSAtlas;
std::unique_ptr<TextureCube> Skybox;
/// Draw skybox
bool SkyEnable;
......
#include "Texture.hpp"
#include "texture.hpp"
using namespace render::gl;
GLuint Texture::CreatePtr(const std::string &name, bool linear) {
return loadDDS("content/textures/" + name + ".dds", linear);
}
#pragma once
#include <string>
#include <GLFW/glfw3.h>
namespace render::gl {
class Texture {
public:
static GLuint CreatePtr(const std::string &name, bool linear);
};
}
......@@ -4,7 +4,7 @@
#include <GL/gl3w.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include "Texture.hpp"
#include <string>
using namespace render::gl;
......@@ -12,7 +12,7 @@ UI::UI(GLFWwindow *window): render::UI() {
// Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 130");
aim = Texture::CreatePtr("ui/Aim", false);
aim = Texture::LoadFromFile("content/textures/ui/Aim.dds", {false, false, Texture::Wrap::MIRRORED_REPEAT, 0, false});
}
UI::~UI() {
ImGui_ImplGlfw_Shutdown();
......@@ -22,7 +22,7 @@ UI::~UI() {
UI::Actions UI::draw(config::client::options &o, state::state &s, const state::reports &r) {
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
return render::UI::draw(o, s, r, aim);
return render::UI::draw(o, s, r, aim->getId());
}
void UI::render() {
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
......
#pragma once
#include "../UI.hpp"
#include "api/Images.hpp"
struct GLFWwindow;
class Window;
......@@ -19,6 +20,6 @@ public:
void render() override;
private:
intptr_t aim;
std::unique_ptr<Texture> aim;
};
}
\ No newline at end of file
#include "Images.hpp"
#include "../../../../core/utils/logger.hpp"
#include <algorithm>
#include <string>
using namespace render::gl;
Image::~Image() {
glDeleteTextures(1, &id);
}
GLenum getGLFormat(render::Image::Format format) {
switch (format) {
case render::Image::Format::BC2:
case render::Image::Format::BC2_UNORM:
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
case render::Image::Format::BC3:
case render::Image::Format::BC3_UNORM:
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
default:
return 0;
}
}
GLuint createImage(const Image::requirement& req, render::data_view data) {
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glTextureParameteri(textureID, GL_TEXTURE_MAX_LEVEL, req.mipmapLevels-1);
GLenum format = getGLFormat(req.format);
unsigned int blockSize = /*(format == GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) ? 8 :*/ 16;
unsigned int offset = 0;
int width = req.size.width;
int height = req.size.height;
/* load the mipmaps */
for (unsigned int level = 0; level < req.mipmapLevels && (width || height); ++level) {
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
assert(data.size >= offset + size);
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, static_cast<const uint8_t*>(data.ptr) + offset);
offset += size;
width /= 2;
height /= 2;
// Deal with Non-Power-Of-Two textures. This code is not included in the webpage to reduce clutter.
if(width < 1) width = 1;
if(height < 1) height = 1;
}
return textureID;
}
void applySampler(GLuint textureID, const Texture::sampling& props) {
auto getFilter = [](bool linear, bool mipmap) { return linear ?
(mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) : (mipmap ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST); };
auto wrap = [](Texture::Wrap wrap) {
switch (wrap) {
case Texture::Wrap::CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
case Texture::Wrap::CLAMP_TO_BORDER:
return GL_CLAMP_TO_BORDER;
case Texture::Wrap::MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
case Texture::Wrap::REPEAT:
default:
return GL_REPEAT;
}
}(props.wrap);
glTextureParameteri(textureID, GL_TEXTURE_MAG_FILTER, getFilter(props.magLinear, props.mipmap));
glTextureParameteri(textureID, GL_TEXTURE_MIN_FILTER, getFilter(props.minLinear, props.mipmap));
glTextureParameterf(textureID, GL_TEXTURE_LOD_BIAS, props.mipmapLod);
glTextureParameterf(textureID, GL_TEXTURE_MAX_ANISOTROPY, props.anisotropy);
glTextureParameteri(textureID, GL_TEXTURE_WRAP_S, wrap);
glTextureParameteri(textureID, GL_TEXTURE_WRAP_T, wrap);
glTextureParameteri(textureID, GL_TEXTURE_WRAP_R, wrap);
}
std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sampling& props) {
std::vector<unsigned char> data;
auto header = [&] {
if (auto header = render::Image::Read(path, data)) {
return header.value();
}
FATAL("Cannot read texture");
}();
auto id = createImage(requirement::Texture(header), data);
if(!id) {
FATAL("Cannot create texture image");
}
applySampler(id, props);
return std::unique_ptr<Texture>(new Texture(id));
}
std::unique_ptr<TextureCube> TextureCube::LoadFromFiles(const std::array<std::string, 6>& paths, const sampling& props) {
std::vector<unsigned char> data;
auto header = [&] {
if (auto header = render::Image::Read(paths.front(), data)) {
return header.value();
}
FATAL("Cannot read first texture");
}();
auto req = requirement::Texture(header);
GLuint textureID;
{
GLenum format = getGLFormat(header.format);
glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &textureID);
glTextureStorage2D(textureID, 1, format, header.size.width, header.size.height);
ushort layer = 0;
for (auto imagepath = paths.begin(); imagepath != paths.end(); ++imagepath, ++layer) {
data.clear();
if (!render::Image::Read(*imagepath, data).has_value()) {
FATAL("Cannot read texture");
}
GLuint subTextureID = createImage(req, data);
glCopyImageSubData(subTextureID, GL_TEXTURE_2D, 0, 0, 0, 0, textureID, GL_TEXTURE_CUBE_MAP, 0, 0, 0, layer, header.size.width, header.size.height, 1);
glDeleteTextures(1, &subTextureID);
}
}
glTextureParameteri(textureID, GL_TEXTURE_MAX_LEVEL, header.mipmapLevels-1);
applySampler(textureID, props);
return std::unique_ptr<TextureCube>(new TextureCube(textureID));
}
std::unique_ptr<TextureCube> TextureCube::LoadFromFiles(const std::string& prefix, const sampling& props) {
const std::array<std::string, 6> faces {
"right",
"left",
"top",
"bottom",
"front",
"back"
};
std::array<std::string, 6> paths;
std::transform(faces.begin(), faces.end(), paths.begin(),
[prefix](const std::string &face) -> std::string { return prefix + "." + face + ".dds"; });
return LoadFromFiles(paths, props);
}
std::unique_ptr<TextureArray> TextureArray::LoadFromFiles(const std::vector<std::string>& paths, const sampling& props) {
std::vector<unsigned char> data;
auto header = [&] {
if (auto header = render::Image::Read(paths.front(), data)) {
return header.value();
}
FATAL("Cannot read first texture");
}();
auto req = requirement::Texture(header);
GLuint textureID;
{
GLenum format = getGLFormat(header.format);
glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &textureID);
glTextureStorage3D(textureID, header.mipmapLevels, format, header.size.width, header.size.height, paths.size());
ushort layer = 0;
for (auto imagepath = paths.begin(); imagepath != paths.end(); ++imagepath, ++layer) {
data.clear();
if (!render::Image::Read(*imagepath, data).has_value()) {
FATAL("Cannot read texture");
}
GLuint subTextureID = createImage(req, data);
auto width = header.size.width;
auto height = header.size.height;
for (uint level = 0; level < header.mipmapLevels; level++) {
glCopyImageSubData(subTextureID, GL_TEXTURE_2D, level, 0, 0, 0, textureID, GL_TEXTURE_2D_ARRAY, level, 0, 0, layer, width, height, 1);
width /= 2;
height /= 2;
if(width < 1) width = 1;
if(height < 1) height = 1;
}
glDeleteTextures(1, &subTextureID);
}
}
glTextureParameteri(textureID, GL_TEXTURE_MAX_LEVEL, header.mipmapLevels-1);
applySampler(textureID, props);
return std::unique_ptr<TextureArray>(new TextureArray(paths.size(), textureID));
}
\ No newline at end of file
#pragma once
#include "../../api/Images.hpp"
#include <GL/gl3w.h>
namespace render::gl {
class Image: public render::Image {
public:
virtual ~Image();
constexpr GLuint getId() const { return id; }
protected:
Image(GLuint id): id(id) { }
GLuint id;
};
class Texture: public render::Texture, public Image {
public:
static std::unique_ptr<Texture> LoadFromFile(const std::string&, const sampling&);
protected:
Texture(GLuint id): gl::Image(id) { }
};
class TextureCube: public render::TextureCube, public Texture {
public:
/// Looks for .right.dds, .left.dds, .top.dds, .bottom.dds, .front.dds, .back.dds
static std::unique_ptr<TextureCube> LoadFromFiles(const std::string& prefix, const sampling&);
static std::unique_ptr<TextureCube> LoadFromFiles(const std::array<std::string, 6>& paths, const sampling&);
protected:
TextureCube(GLuint id): Texture(id) { }
};
class TextureArray: public render::TextureArray, public Texture {
public:
static std::unique_ptr<TextureArray> LoadFromFiles(const std::vector<std::string>&, const sampling&);
protected:
TextureArray(uint32_t size, GLuint id): render::TextureArray(size), Texture(id) { }
};
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
#include <algorithm>
#include <stdexcept>
#include "../texture.hpp"
#include "../api/Images.hpp"
#define CONTENT_DIR "content/"
#define SHADER_DIR CONTENT_DIR "shaders/"
......@@ -45,28 +45,27 @@ void Program::useIt() {
glUseProgram(ProgramID);
}
/*
GLuint Program::loadTexture(const std::string& name, bool linear) {
return loadDDS(TEXTURES_DIR + name + ".dds", linear);
auto img = render::gl::Texture::LoadFromFile(TEXTURES_DIR + name + ".dds", {linear, linear});
auto id = img->getId();
std::free(img.release());
return id;
}
GLuint Program::loadTextureArray(const std::vector<std::string> &names, const std::string& suffix, float mipMapLOD, float anisotropy) {
std::vector<std::string> paths;
std::transform(names.begin(), names.end(), std::back_inserter(paths),
[suffix](const std::string &name) -> std::string { return TEXTURES_DIR + name + suffix + ".dds"; });
return loadDDSArray(paths, mipMapLOD, anisotropy);
}
GLuint Program::loadTextureCube(const std::string &name) {
std::array<std::string, 6> faces {
"right",
"left",
"top",
"bottom",
"front",
"back"
};
std::array<std::string, 6> paths;
std::transform(faces.begin(), faces.end(), paths.begin(),
[name](const std::string &face) -> std::string { return TEXTURES_DIR + name + ".cube." + face + ".dds"; });
return loadDDSCube(paths);
auto img = render::gl::TextureArray::LoadFromFiles(paths, {true, true, render::Texture::Wrap::REPEAT, anisotropy, true, mipMapLOD});
auto id = img->getId();
std::free(img.release());
return id;
}
GLuint Program::loadTextureCube(const std::string &name) {
auto img = render::gl::TextureCube::LoadFromFiles(TEXTURES_DIR + name + ".cube", {});
auto id = img->getId();
std::free(img.release());
return id;
}*/
\ No newline at end of file
......@@ -23,10 +23,6 @@ namespace pass {
/// Bind program
void useIt();
static GLuint loadTexture(const std::string &name, bool linear = true);
static GLuint loadTextureArray(const std::vector<std::string> &names, const std::string &suffix = "", float mipMapLOD = 0, float anisotropy = 0);
static GLuint loadTextureCube(const std::string &name);
protected:
/// Get shaders name prefix
virtual std::string getName() const = 0;
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <cassert>
#include <cmath>
#include <vector>
#include <iostream>
#include <array>
#include <GL/gl3w.h>
#include <GLFW/glfw3.h>
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
GLuint loadDDS(const std::string& imagepath, bool linear){
unsigned char header[124];
FILE *fp;
/* try to open the file */
fp = fopen(imagepath.c_str(), "rb");
if (fp == NULL){
printf("%s could not be opened.\n", imagepath.c_str()); getchar();
return 0;
}
/* verify the type of file */