Commit d95b9eef authored by Shu's avatar Shu 🍠
Browse files

Render API

parent a3d9a157
......@@ -27,9 +27,7 @@ void Client::run(server_handle* const localHandle) {
pipeline->LightInvDir = glm::normalize(glm::vec3(-.5f, 2, -2));
render::Renderer::Get()->loadUI(window);
auto world = world::client::Load(options.connection, localHandle, options.world);
world->setContouring(contouring::load(options.contouring.idx, options.contouring.data));
auto world = world::client::Load(options.connection, localHandle, options.world, options.contouring);
state.contouring = world->getContouring();
//TODO: loop
......@@ -115,10 +113,8 @@ void Client::run(server_handle* const localHandle) {
if(actions && render::UI::Actions::Control) {
player.setOptions(options.control);
}
if(actions && render::UI::Actions::ChangeContouring) {
state.contouring = NULL;
world->setContouring(contouring::load(options.contouring.idx, options.contouring.data));
state.contouring = world->getContouring();
if(actions && render::UI::Actions::FillMode) {
//TODO: pipeline->setFillMode(options.renderer.wireframe);
}
}
{ // Rendering
......@@ -134,10 +130,10 @@ void Client::run(server_handle* const localHandle) {
const auto offset = state.position.raw_as_long();
{ // Chunks
const auto pass = pipeline->beginWorldPass();
const auto draw = [&](glm::mat4 model, buffer::Abstract *const buffer, const contouring::Abstract::area_info &area, const voxel_pos &pos) {
const auto draw = [&](glm::mat4 model, render::LodModel *const buffer, const contouring::Abstract::area_info &area, const voxel_pos &pos) {
pipeline->setCurvature(glm::vec4(pos, std::get<1>(area)), std::get<2>(area));
reports.models_count++;
reports.tris_count += buffer->draw(pass(model));
reports.tris_count += pass(buffer, model);
};
if (options.culling > 0) {
std::vector<glm::vec3> occlusion;
......@@ -188,5 +184,5 @@ void Client::run(server_handle* const localHandle) {
render::Renderer::Unload();
window.destroy();
contouring::save(options.contouring.idx, state.contouring, options.contouring.data);
options.contouring = state.contouring->getOptions();
}
\ No newline at end of file
......@@ -6,7 +6,7 @@
#include <iomanip>
#include "world/Universe.hpp"
#include "render/Renderer.hpp"
#include "contouring/index.hpp"
#include "contouring/Abstract.hpp"
#include "control/Camera.hpp"
namespace config::client {
......@@ -50,10 +50,7 @@ public:
renderer.voxel.curv_depth = config["render"]["curvature_depth"].value_or(renderer.voxel.curv_depth);
culling = config["render"]["culling"].value_or(culling);
contouring.idx = contouring::idxByName(config["contouring"]["mode"].value_or(std::string("")));
for(const auto& name: contouring::names) {
contouring.data.emplace(name, config["contouring"]["options"][name].value_or(std::string("")));
}
contouring = config["contouring"].value_or(std::string(""));
camera.far = config["camera"]["far"].value_or(camera.far);
camera.near = config["camera"]["near"].value_or(camera.near);
......@@ -120,14 +117,7 @@ public:
{"curvature_depth", renderer.voxel.curv_depth},
{"culling", culling}
}));
config.insert_or_assign("contouring", toml::table({
{"mode", contouring::names[contouring.idx]},
{"options", toml::table()}
}));
for(const auto& [key, val]: contouring.data) {
if(!val.empty())
config["contouring"]["options"].as_table()->insert_or_assign(key, val);
}
config.insert_or_assign("contouring", contouring);
config.insert_or_assign("camera", toml::table({
{"far", camera.far},
{"near", camera.near},
......@@ -199,10 +189,7 @@ public:
render::renderOptions renderer;
int culling = 0;
struct {
int idx;
std::map<std::string, std::string> data;
} contouring;
std::string contouring;
Controllable::options control;
Camera::options camera;
......
#pragma once
#include "../render/gl/buffer/Abstract.hpp"
#include "../../core/world/forward.h"
#include "../../core/geometry/Frustum.hpp"
#include "../../core/geometry/Ray.hpp"
#include "../../core/geometry/Faces.hpp"
namespace render { class LodModel; }
/// Mesh creation (from world to render)
namespace contouring {
/// Generating mesh from world data
......@@ -35,7 +35,7 @@ namespace contouring {
virtual size_t getQueueSize() = 0;
using area_info = std::tuple<area_pos, voxel_pos::value_type, float>;
using draw_call = const std::function<void(glm::mat4, buffer::Abstract *const, const area_info&, const voxel_pos&)> &;
using draw_call = const std::function<void(glm::mat4, render::LodModel *const, const area_info&, const voxel_pos&)> &;
/// Get buffers in frustum with model matrices
/// @note buffers invalidated after update
......
#pragma once
#include "Abstract.hpp"
namespace contouring {
/// Useless contouring
class Dummy: public Abstract {
public:
Dummy(): Abstract() { }
virtual ~Dummy() { }
void update(const voxel_pos &, const world::client::area_map &) override {}
void onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override {}
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) override {}
void onGui() override { }
std::string getOptions() const override { return ""; }
std::pair<float, float> getFarRange() const override { return std::make_pair(0, 0); }
size_t getQueueSize() override { return 0; }
void getModels(draw_call, const std::optional<geometry::Frustum> &, const glm::llvec3 &, int) override {}
void getModels(draw_call, const glm::ifvec3 &, float, const std::vector<glm::vec3> &, const glm::llvec3 &, int) override {}
};
}
\ No newline at end of file
......@@ -41,14 +41,15 @@ namespace contouring {
#if TRACY_ENABLE
tracy::SetThreadName("Contouring");
#endif
std::vector<buffer::VertexData> tmp;
std::vector<render::VertexData> tmp;
while (running) {
std::pair<area_<chunk_pos>, surrounding::corners> ctx;
loadQueue.wait();
if (loadQueue.pop(ctx)) {
ZoneScopedN("ProcessContouring");
buffer::LodShortIndexed::LodData data;
render::LodModel::LodData data;
render(ctx.second, data, tmp);
//TODO: direct upload with vulkan
loadedQueue.emplace(ctx.first, data);
}
}
......@@ -121,12 +122,12 @@ namespace contouring {
void FlatDualMC::update(const voxel_pos& pos, const world::client::area_map& areas) {
ZoneScopedN("Ct");
std::pair<area_<chunk_pos>, buffer::LodShortIndexed::LodData> out;
std::pair<area_<chunk_pos>, render::LodModel::LodData> out;
TracyPlot("CtLoad", static_cast<int64_t>(loadQueue.size()));
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
TracyPlot("CtLoaded", static_cast<int64_t>(loadedQueue.size()));
for(auto handle = loadedQueue.extractor(); handle.first(out);) {
const auto buffer = out.second.first.empty() ? NULL : new buffer::LodShortIndexed(GL_TRIANGLES, out.second);
const auto buffer = out.second.first.empty() ? NULL : render::LodModel::Create(out.second).release();
auto &bfs = buffers[out.first.first].second; //NOTE: buffer.first uninitialized (will be set in clear())
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
if(it->second != NULL)
......@@ -163,7 +164,7 @@ namespace contouring {
it = bfs.erase(it);
} else {
if(it->second != NULL) {
static_cast<buffer::LodShortIndexed*>(it->second)->setLevel(std::clamp<size_t>((1+lod_quality-distRatio)*levelMax*(1+lod_strength), 0, levelMax));
it->second->setLevel(std::clamp<size_t>((1+lod_quality-distRatio)*levelMax*(1+lod_strength), 0, levelMax));
buffer_count++;
}
++it;
......@@ -212,7 +213,7 @@ namespace contouring {
}
}
void FlatDualMC::render(const surrounding::corners &surrounding, buffer::LodShortIndexed::LodData &out, std::vector<buffer::VertexData> &tmp) const {
void FlatDualMC::render(const surrounding::corners &surrounding, render::LodModel::LodData &out, std::vector<render::VertexData> &tmp) const {
const int SIZE = CHUNK_LENGTH + 3;
std::array<dualmc::DualMC<float>::Point, SIZE * SIZE * SIZE> grid;
{
......@@ -255,7 +256,7 @@ namespace contouring {
tmp.reserve(dmc_vertices.size());
constexpr auto HALF_MANTISSA = 10;
std::transform(dmc_vertices.begin(), dmc_vertices.end(), std::back_inserter(tmp), [](const dualmc::Vertex &v) {
return buffer::VertexData(glm::vec3(meshopt_quantizeFloat(v.x, HALF_MANTISSA),meshopt_quantizeFloat(v.y, HALF_MANTISSA),
return render::VertexData(glm::vec3(meshopt_quantizeFloat(v.x, HALF_MANTISSA),meshopt_quantizeFloat(v.y, HALF_MANTISSA),
meshopt_quantizeFloat(v.z, HALF_MANTISSA)), v.w, glm::vec3(0));
});
......@@ -285,8 +286,8 @@ namespace contouring {
}
out.second = simplify_lod(data.indices, tmp, loadedLevels);
std::transform(tmp.begin(), tmp.end(), std::back_inserter(data.vertices), [](const buffer::VertexData &v) {
return buffer::PackedVertexData(meshopt_quantizeHalf(v.Position.x), meshopt_quantizeHalf(v.Position.y),
std::transform(tmp.begin(), tmp.end(), std::back_inserter(data.vertices), [](const render::VertexData &v) {
return render::PackedVertexData(meshopt_quantizeHalf(v.Position.x), meshopt_quantizeHalf(v.Position.y),
meshopt_quantizeHalf(v.Position.z), v.Material,
meshopt_quantizeHalf(v.Normal.x), meshopt_quantizeHalf(v.Normal.y),
meshopt_quantizeHalf(v.Normal.z));
......
......@@ -5,14 +5,14 @@
#include "../../core/data/safe_queue.hpp"
#include "../../core/data/safe_priority_queue.hpp"
#include "../render/gl/buffer/LodShortIndexed.hpp"
#include "../render/api/Models.hpp"
#include "../../core/data/math.hpp"
#include <thread>
using namespace data;
namespace contouring {
/// Dual Marching Cube 1:1 contouring
class FlatDualMC: public Abstract {
class FlatDualMC final: public Abstract {
public:
FlatDualMC(const std::string&);
virtual ~FlatDualMC();
......@@ -39,10 +39,11 @@ namespace contouring {
void getModels(draw_call draw, const glm::ifvec3 &from, float far, const std::vector<glm::vec3> &occlusion, const glm::llvec3 &offset, int density) override;
protected:
robin_hood::unordered_map<area_id, robin_hood::pair<area_info, robin_hood::unordered_map<chunk_pos, buffer::Abstract *>>> buffers;
//FIXME: use unique_ptr
robin_hood::unordered_map<area_id, robin_hood::pair<area_info, robin_hood::unordered_map<chunk_pos, render::LodModel*>>> buffers;
safe_priority_queue_map<area_<chunk_pos>, surrounding::corners, int, area_hash> loadQueue;
safe_queue<std::pair<area_<chunk_pos>, buffer::LodShortIndexed::LodData>> loadedQueue;
safe_queue<std::pair<area_<chunk_pos>, render::LodModel::LodData>> loadedQueue;
bool running = true;
std::vector<std::thread> workers;
......@@ -61,6 +62,6 @@ namespace contouring {
std::vector<std::pair<float, float>> loadedLevels;
void render(const surrounding::corners &surrounding, buffer::LodShortIndexed::LodData& out, std::vector<buffer::VertexData>& tmp) const;
void render(const surrounding::corners &surrounding, render::LodModel::LodData& out, std::vector<render::VertexData>& tmp) const;
};
}
#pragma once
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include <meshoptimizer.h>
#include "../data/geometry/Faces.hpp"
#include "../render/buffer/VertexData.hpp"
using namespace geometry;
namespace contouring::box {
static const auto BOX_FACES = 6;
static const std::vector<glm::vec3> g_quad_vertices = {
glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.5f, -0.5f, 0.5f), glm::vec3(0.5f, -0.5f, -0.5f),
glm::vec3(0.5f, -0.5f, -0.5f), glm::vec3(0.5f, 0.5f, -0.5f), glm::vec3(0.5f, 0.5f, 0.5f)};
static const glm::vec3 g_cube_normals[BOX_FACES] = {
glm::vec3(1, 0, 0), glm::vec3(-1, 0, 0), glm::vec3(0, 1, 0), glm::vec3(0, -1, 0), glm::vec3(0, 0, 1), glm::vec3(0, 0, -1)};
static const glm::mat4 g_cube_rotate[BOX_FACES] = {
glm::mat4(1),
glm::rotate<float>(glm::mat4(1), glm::pi<float>(), glm::vec3(0, 1, 0)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 0, 1)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 0, -1)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, -1, 0)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 1, 0)),
};
static void addQuad(std::vector<buffer::PackedVertexData> & out, glm::vec3 position, ushort texture, Face face, glm::vec3 size) {
for (auto vertex : g_quad_vertices) {
const auto p = glm::vec3(g_cube_rotate[static_cast<int>(face)] * glm::vec4(vertex, 1)) * size + position;
out.emplace_back(meshopt_quantizeHalf(p.x), meshopt_quantizeHalf(p.y),
meshopt_quantizeHalf(p.z), texture,
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].x), meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].y),
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].z));
}
}
static void addCube(std::vector<buffer::PackedVertexData>& out, glm::vec3 position, uint texture, Faces faces = Faces::All, glm::vec3 size = glm::vec3(1)) {
if (faces && Faces::Right)
addQuad(out, position, texture, Face::Right, size);
if(faces && Faces::Left)
addQuad(out, position, texture, Face::Left, size);
if (faces && Faces::Up)
addQuad(out, position, texture, Face::Up, size);
if (faces && Faces::Down)
addQuad(out, position, texture, Face::Down, size);
if (faces && Faces::Forward)
addQuad(out, position, texture, Face::Forward, size);
if (faces && Faces::Backward)
addQuad(out, position, texture, Face::Backward, size);
}
}
#include "index.hpp"
#include "Dummy.hpp"
#include "FlatDualMC.hpp"
namespace contouring {
int idxByName(const std::string& name) {
for (size_t i = 0; i < names.size(); i++) {
if(name == names[i])
return (int)i;
}
return 0;
}
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string>& data) {
switch (idx) {
case 2:
return std::make_shared<Dummy>();
default:
return std::make_shared<FlatDualMC>(data.at(names[0]));
}
}
void save(int idx, std::shared_ptr<Abstract>& ct, std::map<std::string, std::string>& data) {
data.insert_or_assign(names[idx], ct->getOptions());
}
}
\ No newline at end of file
#pragma once
#include "Abstract.hpp"
#include <map>
namespace contouring {
static const std::array<std::string, 2> names = {"FlatDualMC", "Dummy"};
int idxByName(const std::string &name);
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string> &data);
void save(int idx, std::shared_ptr<Abstract> &ct, std::map<std::string, std::string> &data);
}
\ No newline at end of file
#pragma once
#include <meshoptimizer.h>
inline void optimize_fetch(buffer::ShortIndexed::Data& out) {
inline void optimize_fetch(render::Model::Data& out) {
ZoneScopedN("Optimize");
std::vector<unsigned int> remap(out.indices.size());
size_t vertex_count = meshopt_optimizeVertexFetch(out.vertices.data(), out.indices.data(), out.indices.size(), out.vertices.data(), out.vertices.size(), sizeof(buffer::PackedVertexData));
out.vertices.resize(vertex_count, buffer::PackedVertexData(0, 0, 0, 0, 0, 0, 0));
size_t vertex_count = meshopt_optimizeVertexFetch(out.vertices.data(), out.indices.data(), out.indices.size(), out.vertices.data(), out.vertices.size(), sizeof(render::PackedVertexData));
out.vertices.resize(vertex_count, render::PackedVertexData(0, 0, 0, 0, 0, 0, 0));
}
//MAYBE: when networking meshopt_encodeVertexBuffer
template<typename I>
inline void simplify_buffer(std::vector<I> &out, const std::vector<I> &indices, const std::vector<buffer::VertexData>& vertices, float threshold = .2f, float target_error = 1e-2f) {
inline void simplify_buffer(std::vector<I> &out, const std::vector<I> &indices, const std::vector<render::VertexData>& vertices, float threshold = .2f, float target_error = 1e-2f) {
out.resize(indices.size());
out.resize(meshopt_simplify(out.data(), indices.data(), indices.size(), &vertices.front().Position.x, vertices.size(), sizeof(buffer::VertexData), indices.size() * threshold, target_error));
out.resize(meshopt_simplify(out.data(), indices.data(), indices.size(), &vertices.front().Position.x, vertices.size(), sizeof(render::VertexData), indices.size() * threshold, target_error));
}
#include <iostream>
template <typename I>
inline std::vector<size_t> simplify_lod(std::vector<I> &indices, const std::vector<buffer::VertexData> &vertices, const std::vector<std::pair<float, float>> &levels)
inline std::vector<size_t> simplify_lod(std::vector<I> &indices, const std::vector<render::VertexData> &vertices, const std::vector<std::pair<float, float>> &levels)
{
ZoneScopedN("LOD");
typename std::vector<I> full(indices);
......
#pragma once
#include "../../core/flags.hpp"
#include "buffer/Abstract.hpp"
#include <cassert>
#include <memory>
#include <string>
......@@ -11,6 +10,8 @@
class Window;
class Camera;
namespace render {
class Model;
class LodModel;
/// Pass options
struct passOptions {
......@@ -63,9 +64,9 @@ public:
/// Start new frame and setup
virtual void beginFrame() = 0;
/// Get started world program
virtual std::function<buffer::params(glm::mat4)> beginWorldPass() = 0;
virtual std::function<size_t(render::LodModel *const, glm::mat4)> beginWorldPass() = 0;
/// Get started entity program
virtual std::function<buffer::params(const std::vector<glm::mat4> &)> beginEntityPass() = 0;
virtual std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> beginEntityPass() = 0;
/// Draw cube indicator
virtual size_t drawIndicatorCube(glm::mat4 model) = 0;
/// Apply postprocessing
......
......@@ -102,8 +102,9 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons
if (ImGui::ColorEdit3("Fog color", &options.renderer.clear_color[0])) {
actions |= Actions::ClearColor;
}
if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe))
glPolygonMode(GL_FRONT_AND_BACK, options.renderer.wireframe ? GL_LINE : GL_FILL);
if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe)) {
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)) {
......@@ -130,23 +131,6 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons
if (options.debugMenu.contouring) {
ImGui::Begin("Debug: Contouring", &options.debugMenu.contouring, ImGuiWindowFlags_AlwaysAutoResize);
if (ImGui::BeginCombo("Contouring", contouring::names[options.contouring.idx].c_str())) {
for (size_t i = 0; i < contouring::names.size(); i++) {
const bool is_selected = (options.contouring.idx == (int)i);
if (ImGui::Selectable(contouring::names[i].c_str(), is_selected)) {
actions |= Actions::ChangeContouring;
contouring::save(options.contouring.idx, state.contouring, options.contouring.data);
options.contouring.idx = i;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
if(ImGui::Button("Reload")) {
actions |= Actions::ChangeContouring;
contouring::save(options.contouring.idx, state.contouring, options.contouring.data);
}
ImGui::SliderInt("Culling", &options.culling, -1, 10, options.culling > 0 ? "Occlusion %d" : (options.culling < 0 ? "None" : "Frustum"));
state.contouring->onGui();
ImGui::End();
......
......@@ -31,7 +31,7 @@ public:
World = 1 << 5,
Camera = 1 << 6,
Control = 1 << 7,
ChangeContouring = 1 << 8,
FillMode = 1 << 8,
};
friend inline void operator|=(Actions& a, Actions b) {
a = static_cast<Actions>(static_cast<int>(a) | static_cast<int>(b));
......
#include "Buffers.hpp"
using namespace render;
std::unique_ptr<WritableBuffer> (*WritableBuffer::createFunc)(size_t, Usage, const data_view) = nullptr;
std::unique_ptr<FastBuffer> (*FastBuffer::createFunc)(size_t, Usage, const data_view) = nullptr;
std::unique_ptr<ShortIndexedVertexBuffer> (*ShortIndexedVertexBuffer::createFunc)(const data_view, const data_view) = nullptr;
\ No newline at end of file
#pragma once
#include "common.hpp"
namespace render {
/// Abstract raw data buffer
class Buffer {
public:
virtual ~Buffer() { }
// NOTE: matches VkBufferUsageFlags
enum class Usage {
TRANSFER_SRC = 1,
TRANSFER_DST = 2,
UNIFORM = 16,
STORAGE = 32,
INDEX = 64,
VERTEX = 128
};
friend inline Usage operator|(Usage a, Usage b) {
return static_cast<Usage>(static_cast<int>(a) | static_cast<int>(b));
}
struct requirement {
requirement(size_t size, Usage usage, const data_view view = data_view()):
size(size), usage(usage), view(view) { }
size_t size;
Usage usage;
data_view view;
};
};
/// Writable raw data buffer
class WritableBuffer: public Buffer {
public:
static _FORCE_INLINE_ std::unique_ptr<WritableBuffer> Create(size_t size, Usage usage = Usage::TRANSFER_SRC, const data_view write = data_view()) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(size, usage, write);
}
virtual void write(const data_view, size_t offset = 0) = 0;
virtual void read(data_ref, size_t offset = 0) = 0;
protected:
static std::unique_ptr<WritableBuffer> (*createFunc)(size_t, Usage, const data_view);
};
/// Fast raw data buffer
class FastBuffer: public Buffer {
public:
// NOTE: implementation may implicitly add Usage::TRANSFER_DST
static _FORCE_INLINE_ std::unique_ptr<FastBuffer> Create(size_t size, Usage usage, const data_view write = data_view()) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(size, usage, write);
}
protected:
static std::unique_ptr<FastBuffer> (*createFunc)(size_t, Usage, const data_view);
};
/// Grouped vertex and u16 index buffers
/// NOTE: vertex structure is undefined
class ShortIndexedVertexBuffer: public Buffer {
public:
static _FORCE_INLINE_ std::unique_ptr<ShortIndexedVertexBuffer> Create(const data_view vertices, const data_view indices) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(vertices, indices);
}
protected:
static std::unique_ptr<ShortIndexedVertexBuffer> (*createFunc)(const data_view, const data_view);
};
}
\ No newline at end of file
#include "texture.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <cmath>
#include <iostream>
#include <array>
#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
VkResult render::vk::dds::readDDS(const std::string& imagepath, std::vector<unsigned char>& data, header_info& info) {
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 VK_ERROR_INVALID_DEVICE_ADDRESS_EXT;
}
/* verify the type of file */
char filecode[4];
fread(filecode, 1, 4, fp);
if (strncmp(filecode, "DDS ", 4) != 0) {
fclose(fp);
return VK_ERROR_INVALID_DEVICE_ADDRESS_EXT;
}
/* get the surface desc */
fread(&header, 124, 1, fp);