Merge branch 'main' into update-particles
This commit is contained in:
@@ -18,18 +18,18 @@
|
||||
#include <glm/ext.hpp>
|
||||
|
||||
std::unique_ptr<ImageData> BlocksPreview::draw(
|
||||
const ContentGfxCache* cache,
|
||||
Shader* shader,
|
||||
Framebuffer* fbo,
|
||||
Batch3D* batch,
|
||||
const ContentGfxCache& cache,
|
||||
Shader& shader,
|
||||
const Framebuffer& fbo,
|
||||
Batch3D& batch,
|
||||
const Block& def,
|
||||
int size
|
||||
){
|
||||
Window::clear();
|
||||
blockid_t id = def.rt.id;
|
||||
const UVRegion texfaces[6]{cache->getRegion(id, 0), cache->getRegion(id, 1),
|
||||
cache->getRegion(id, 2), cache->getRegion(id, 3),
|
||||
cache->getRegion(id, 4), cache->getRegion(id, 5)};
|
||||
const UVRegion texfaces[6]{cache.getRegion(id, 0), cache.getRegion(id, 1),
|
||||
cache.getRegion(id, 2), cache.getRegion(id, 3),
|
||||
cache.getRegion(id, 4), cache.getRegion(id, 5)};
|
||||
|
||||
glm::vec3 offset(0.1f, 0.5f, 0.1f);
|
||||
switch (def.model) {
|
||||
@@ -37,10 +37,10 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
|
||||
// something went wrong...
|
||||
break;
|
||||
case BlockModel::block:
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
batch->blockCube(glm::vec3(size * 0.63f), texfaces,
|
||||
glm::vec4(1.0f), !def.rt.emissive);
|
||||
batch->flush();
|
||||
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
batch.blockCube(glm::vec3(size * 0.63f), texfaces,
|
||||
glm::vec4(1.0f), !def.rt.emissive);
|
||||
batch.flush();
|
||||
break;
|
||||
case BlockModel::aabb:
|
||||
{
|
||||
@@ -49,39 +49,39 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
|
||||
hitbox = glm::max(hitbox, box.size());
|
||||
}
|
||||
offset = glm::vec3(1, 1, 0.0f);
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
glm::vec3 scaledSize = glm::vec3(size * 0.63f);
|
||||
batch->cube(
|
||||
batch.cube(
|
||||
-hitbox * scaledSize * 0.5f * glm::vec3(1,1,-1),
|
||||
hitbox * scaledSize,
|
||||
texfaces, glm::vec4(1.0f),
|
||||
!def.rt.emissive
|
||||
);
|
||||
}
|
||||
batch->flush();
|
||||
batch.flush();
|
||||
break;
|
||||
case BlockModel::custom:{
|
||||
glm::vec3 pmul = glm::vec3(size * 0.63f);
|
||||
glm::vec3 hitbox = glm::vec3(1.0f);
|
||||
glm::vec3 poff = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
offset.y += (1.0f - hitbox).y * 0.5f;
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
const auto& model = cache->getModel(def.rt.id);
|
||||
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
const auto& model = cache.getModel(def.rt.id);
|
||||
|
||||
for (const auto& mesh : model.meshes) {
|
||||
for (const auto& vertex : mesh.vertices) {
|
||||
float d = glm::dot(glm::normalize(vertex.normal), glm::vec3(0.2, 0.8, 0.4));
|
||||
d = 0.8f + d * 0.2f;
|
||||
batch->vertex((vertex.coord - poff)*pmul, vertex.uv, glm::vec4(d, d, d, 1.0f));
|
||||
batch.vertex((vertex.coord - poff)*pmul, vertex.uv, glm::vec4(d, d, d, 1.0f));
|
||||
}
|
||||
batch->flush();
|
||||
batch.flush();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BlockModel::xsprite: {
|
||||
shader->uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
shader.uniformMatrix("u_apply", glm::translate(glm::mat4(1.0f), offset));
|
||||
glm::vec3 right = glm::normalize(glm::vec3(1.f, 0.f, -1.f));
|
||||
batch->sprite(
|
||||
batch.sprite(
|
||||
right*float(size)*0.43f+glm::vec3(0, size*0.4f, 0),
|
||||
glm::vec3(0.f, 1.f, 0.f),
|
||||
right,
|
||||
@@ -89,24 +89,23 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
|
||||
texfaces[0],
|
||||
glm::vec4(1.0f)
|
||||
);
|
||||
batch->flush();
|
||||
batch.flush();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fbo->getTexture()->readData();
|
||||
return fbo.getTexture()->readData();
|
||||
}
|
||||
|
||||
std::unique_ptr<Atlas> BlocksPreview::build(
|
||||
const ContentGfxCache* cache,
|
||||
Assets* assets,
|
||||
const Content* content
|
||||
const ContentGfxCache& cache,
|
||||
const Assets& assets,
|
||||
const ContentIndices& indices
|
||||
) {
|
||||
auto indices = content->getIndices();
|
||||
size_t count = indices->blocks.count();
|
||||
size_t count = indices.blocks.count();
|
||||
size_t iconSize = ITEM_ICON_SIZE;
|
||||
|
||||
auto shader = assets->get<Shader>("ui3d");
|
||||
auto atlas = assets->get<Atlas>("blocks");
|
||||
auto& shader = assets.require<Shader>("ui3d");
|
||||
const auto& atlas = assets.require<Atlas>("blocks");
|
||||
|
||||
Viewport viewport(iconSize, iconSize);
|
||||
DrawContext pctx(nullptr, viewport, nullptr);
|
||||
@@ -118,8 +117,8 @@ std::unique_ptr<Atlas> BlocksPreview::build(
|
||||
Batch3D batch(1024);
|
||||
batch.begin();
|
||||
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_projview",
|
||||
shader.use();
|
||||
shader.uniformMatrix("u_projview",
|
||||
glm::ortho(0.0f, float(iconSize), 0.0f, float(iconSize),
|
||||
-100.0f, 100.0f) *
|
||||
glm::lookAt(glm::vec3(0.57735f),
|
||||
@@ -132,9 +131,9 @@ std::unique_ptr<Atlas> BlocksPreview::build(
|
||||
|
||||
fbo.bind();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto& def = indices->blocks.require(i);
|
||||
atlas->getTexture()->bind();
|
||||
builder.add(def.name, draw(cache, shader, &fbo, &batch, def, iconSize));
|
||||
auto& def = indices.blocks.require(i);
|
||||
atlas.getTexture()->bind();
|
||||
builder.add(def.name, draw(cache, shader, fbo, batch, def, iconSize));
|
||||
}
|
||||
fbo.unbind();
|
||||
|
||||
|
||||
@@ -11,23 +11,23 @@ class Atlas;
|
||||
class Framebuffer;
|
||||
class Batch3D;
|
||||
class Block;
|
||||
class Content;
|
||||
class ContentIndices;
|
||||
class Shader;
|
||||
class ContentGfxCache;
|
||||
|
||||
class BlocksPreview {
|
||||
static std::unique_ptr<ImageData> draw(
|
||||
const ContentGfxCache* cache,
|
||||
Shader* shader,
|
||||
Framebuffer* framebuffer,
|
||||
Batch3D* batch,
|
||||
const ContentGfxCache& cache,
|
||||
Shader& shader,
|
||||
const Framebuffer& framebuffer,
|
||||
Batch3D& batch,
|
||||
const Block& block,
|
||||
int size
|
||||
);
|
||||
public:
|
||||
static std::unique_ptr<Atlas> build(
|
||||
const ContentGfxCache* cache,
|
||||
Assets* assets,
|
||||
const Content* content
|
||||
const ContentGfxCache& cache,
|
||||
const Assets& assets,
|
||||
const ContentIndices& indices
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,23 +5,22 @@
|
||||
#include "maths/UVRegion.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "voxels/ChunksStorage.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "lighting/Lightmap.hpp"
|
||||
#include "frontend/ContentGfxCache.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const uint BlocksRenderer::VERTEX_SIZE = 6;
|
||||
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
||||
|
||||
BlocksRenderer::BlocksRenderer(
|
||||
size_t capacity,
|
||||
const Content* content,
|
||||
const ContentGfxCache* cache,
|
||||
const EngineSettings* settings
|
||||
const Content& content,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
) : content(content),
|
||||
vertexBuffer(std::make_unique<float[]>(capacity * VERTEX_SIZE)),
|
||||
vertexBuffer(std::make_unique<float[]>(capacity * CHUNK_VERTEX_SIZE)),
|
||||
indexBuffer(std::make_unique<int[]>(capacity)),
|
||||
vertexOffset(0),
|
||||
indexOffset(0),
|
||||
@@ -34,7 +33,7 @@ BlocksRenderer::BlocksRenderer(
|
||||
CHUNK_W + voxelBufferPadding*2,
|
||||
CHUNK_H,
|
||||
CHUNK_D + voxelBufferPadding*2);
|
||||
blockDefsCache = content->getIndices()->blocks.getDefs();
|
||||
blockDefsCache = content.getIndices()->blocks.getDefs();
|
||||
}
|
||||
|
||||
BlocksRenderer::~BlocksRenderer() {
|
||||
@@ -85,7 +84,7 @@ void BlocksRenderer::face(
|
||||
const glm::vec4(&lights)[4],
|
||||
const glm::vec4& tint
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
@@ -125,7 +124,7 @@ void BlocksRenderer::faceAO(
|
||||
const UVRegion& region,
|
||||
bool lights
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +162,7 @@ void BlocksRenderer::face(
|
||||
glm::vec4 tint,
|
||||
bool lights
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
@@ -286,29 +285,35 @@ void BlocksRenderer::blockCustomModel(
|
||||
Z = orient.axisZ;
|
||||
}
|
||||
|
||||
const auto& model = cache->getModel(block->rt.id);
|
||||
const auto& model = cache.getModel(block->rt.id);
|
||||
for (const auto& mesh : model.meshes) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * mesh.vertices.size() > capacity) {
|
||||
if (vertexOffset + CHUNK_VERTEX_SIZE * mesh.vertices.size() > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
for (const auto& vertex : mesh.vertices) {
|
||||
auto n =
|
||||
vertex.normal.x * X + vertex.normal.y * Y + vertex.normal.z * Z;
|
||||
float d = glm::dot(glm::normalize(n), SUN_VECTOR);
|
||||
d = 0.8f + d * 0.2f;
|
||||
const auto& vcoord = vertex.coord - 0.5f;
|
||||
vertexAO(
|
||||
coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z,
|
||||
vertex.uv.x,
|
||||
vertex.uv.y,
|
||||
glm::vec4(1, 1, 1, 1),
|
||||
glm::vec3(1, 0, 0),
|
||||
glm::vec3(0, 1, 0),
|
||||
n
|
||||
);
|
||||
indexBuffer[indexSize++] = indexOffset++;
|
||||
for (int triangle = 0; triangle < mesh.vertices.size() / 3; triangle++) {
|
||||
auto r = mesh.vertices[triangle * 3 + (triangle % 2) * 2].coord -
|
||||
mesh.vertices[triangle * 3 + 1].coord;
|
||||
r = glm::normalize(r);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const auto& vertex = mesh.vertices[triangle * 3 + i];
|
||||
auto n = vertex.normal.x * X + vertex.normal.y * Y +
|
||||
vertex.normal.z * Z;
|
||||
float d = glm::dot(n, SUN_VECTOR);
|
||||
d = 0.8f + d * 0.2f;
|
||||
const auto& vcoord = vertex.coord - 0.5f;
|
||||
vertexAO(
|
||||
coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z,
|
||||
vertex.uv.x,
|
||||
vertex.uv.y,
|
||||
glm::vec4(d, d, d, d),
|
||||
glm::cross(r, n),
|
||||
r,
|
||||
n
|
||||
);
|
||||
indexBuffer[indexSize++] = indexOffset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -427,11 +432,16 @@ glm::vec4 BlocksRenderer::pickSoftLight(
|
||||
right, up);
|
||||
}
|
||||
|
||||
void BlocksRenderer::render(const voxel* voxels) {
|
||||
int begin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
||||
int end = chunk->top * (CHUNK_W * CHUNK_D);
|
||||
for (const auto drawGroup : *content->drawGroups) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
void BlocksRenderer::render(
|
||||
const voxel* voxels, int beginEnds[256][2]
|
||||
) {
|
||||
for (const auto drawGroup : *content.drawGroups) {
|
||||
int begin = beginEnds[drawGroup][0];
|
||||
if (begin == 0) {
|
||||
continue;
|
||||
}
|
||||
int end = beginEnds[drawGroup][1];
|
||||
for (int i = begin-1; i <= end; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
blockstate state = vox.state;
|
||||
@@ -439,13 +449,13 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
||||
continue;
|
||||
}
|
||||
if (def.translucent) {
|
||||
continue;
|
||||
}
|
||||
const UVRegion texfaces[6] {
|
||||
cache->getRegion(id, 0),
|
||||
cache->getRegion(id, 1),
|
||||
cache->getRegion(id, 2),
|
||||
cache->getRegion(id, 3),
|
||||
cache->getRegion(id, 4),
|
||||
cache->getRegion(id, 5)
|
||||
cache.getRegion(id, 0), cache.getRegion(id, 1),
|
||||
cache.getRegion(id, 2), cache.getRegion(id, 3),
|
||||
cache.getRegion(id, 4), cache.getRegion(id, 5)
|
||||
};
|
||||
int x = i % CHUNK_W;
|
||||
int y = i / (CHUNK_D * CHUNK_W);
|
||||
@@ -480,43 +490,185 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
}
|
||||
}
|
||||
|
||||
void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
SortingMeshData BlocksRenderer::renderTranslucent(
|
||||
const voxel* voxels, int beginEnds[256][2]
|
||||
) {
|
||||
SortingMeshData sortingMesh {{}};
|
||||
|
||||
AABB aabb {};
|
||||
bool aabbInit = false;
|
||||
size_t totalSize = 0;
|
||||
for (const auto drawGroup : *content.drawGroups) {
|
||||
int begin = beginEnds[drawGroup][0];
|
||||
if (begin == 0) {
|
||||
continue;
|
||||
}
|
||||
int end = beginEnds[drawGroup][1];
|
||||
for (int i = begin-1; i <= end; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
blockstate state = vox.state;
|
||||
const auto& def = *blockDefsCache[id];
|
||||
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
||||
continue;
|
||||
}
|
||||
if (!def.translucent) {
|
||||
continue;
|
||||
}
|
||||
const UVRegion texfaces[6] {
|
||||
cache.getRegion(id, 0), cache.getRegion(id, 1),
|
||||
cache.getRegion(id, 2), cache.getRegion(id, 3),
|
||||
cache.getRegion(id, 4), cache.getRegion(id, 5)
|
||||
};
|
||||
int x = i % CHUNK_W;
|
||||
int y = i / (CHUNK_D * CHUNK_W);
|
||||
int z = (i / CHUNK_D) % CHUNK_W;
|
||||
switch (def.model) {
|
||||
case BlockModel::block:
|
||||
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
||||
def.ambientOcclusion);
|
||||
break;
|
||||
case BlockModel::xsprite: {
|
||||
blockXSprite(x, y, z, glm::vec3(1.0f),
|
||||
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||
break;
|
||||
}
|
||||
case BlockModel::aabb: {
|
||||
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
|
||||
!def.shadeless, def.ambientOcclusion);
|
||||
break;
|
||||
}
|
||||
case BlockModel::custom: {
|
||||
blockCustomModel({x, y, z}, &def, vox.state.rotation,
|
||||
!def.shadeless, def.ambientOcclusion);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (vertexOffset == 0) {
|
||||
continue;
|
||||
}
|
||||
SortingMeshEntry entry {
|
||||
glm::vec3(
|
||||
x + chunk->x * CHUNK_W + 0.5f,
|
||||
y + 0.5f,
|
||||
z + chunk->z * CHUNK_D + 0.5f
|
||||
),
|
||||
util::Buffer<float>(indexSize * CHUNK_VERTEX_SIZE)};
|
||||
|
||||
totalSize += entry.vertexData.size();
|
||||
|
||||
for (int j = 0; j < indexSize; j++) {
|
||||
std::memcpy(
|
||||
entry.vertexData.data() + j * CHUNK_VERTEX_SIZE,
|
||||
vertexBuffer.get() + indexBuffer[j] * CHUNK_VERTEX_SIZE,
|
||||
sizeof(float) * CHUNK_VERTEX_SIZE
|
||||
);
|
||||
float& vx = entry.vertexData[j * CHUNK_VERTEX_SIZE + 0];
|
||||
float& vy = entry.vertexData[j * CHUNK_VERTEX_SIZE + 1];
|
||||
float& vz = entry.vertexData[j * CHUNK_VERTEX_SIZE + 2];
|
||||
|
||||
if (!aabbInit) {
|
||||
aabbInit = true;
|
||||
aabb.a = aabb.b = {vx, vy, vz};
|
||||
} else {
|
||||
aabb.addPoint(glm::vec3(vx, vy, vz));
|
||||
}
|
||||
vx += chunk->x * CHUNK_W + 0.5f;
|
||||
vy += 0.5f;
|
||||
vz += chunk->z * CHUNK_D + 0.5f;
|
||||
}
|
||||
sortingMesh.entries.push_back(std::move(entry));
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// additional powerful optimization
|
||||
auto size = aabb.size();
|
||||
if ((size.y < 0.01f || size.x < 0.01f || size.z < 0.01f) &&
|
||||
sortingMesh.entries.size() > 1) {
|
||||
SortingMeshEntry newEntry {
|
||||
sortingMesh.entries[0].position,
|
||||
util::Buffer<float>(totalSize)
|
||||
};
|
||||
size_t offset = 0;
|
||||
for (const auto& entry : sortingMesh.entries) {
|
||||
std::memcpy(
|
||||
newEntry.vertexData.data() + offset,
|
||||
entry.vertexData.data(), entry.vertexData.size() * sizeof(float)
|
||||
);
|
||||
offset += entry.vertexData.size();
|
||||
}
|
||||
return SortingMeshData {{std::move(newEntry)}};
|
||||
}
|
||||
return sortingMesh;
|
||||
}
|
||||
|
||||
void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
|
||||
this->chunk = chunk;
|
||||
voxelsBuffer->setPosition(
|
||||
chunk->x * CHUNK_W - voxelBufferPadding, 0,
|
||||
chunk->z * CHUNK_D - voxelBufferPadding);
|
||||
chunks->getVoxels(voxelsBuffer.get(), settings->graphics.backlight.get());
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get());
|
||||
|
||||
if (voxelsBuffer->pickBlockId(
|
||||
chunk->x * CHUNK_W, 0, chunk->z * CHUNK_D
|
||||
) == BLOCK_VOID) {
|
||||
cancelled = true;
|
||||
return;
|
||||
}
|
||||
cancelled = false;
|
||||
const voxel* voxels = chunk->voxels;
|
||||
render(voxels);
|
||||
|
||||
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
||||
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
|
||||
|
||||
int beginEnds[256][2] {};
|
||||
for (int i = totalBegin; i < totalEnd; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
const auto& def = *blockDefsCache[id];
|
||||
|
||||
if (beginEnds[def.drawGroup][0] == 0) {
|
||||
beginEnds[def.drawGroup][0] = i+1;
|
||||
}
|
||||
beginEnds[def.drawGroup][1] = i;
|
||||
}
|
||||
cancelled = false;
|
||||
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
|
||||
sortingMesh = std::move(renderTranslucent(voxels, beginEnds));
|
||||
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
|
||||
render(voxels, beginEnds);
|
||||
}
|
||||
|
||||
MeshData BlocksRenderer::createMesh() {
|
||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||
return MeshData(
|
||||
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
|
||||
util::Buffer<int>(indexBuffer.get(), indexSize),
|
||||
util::Buffer<vattr>({{3}, {2}, {1}, {0}})
|
||||
);
|
||||
ChunkMeshData BlocksRenderer::createMesh() {
|
||||
return ChunkMeshData {
|
||||
MeshData(
|
||||
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
|
||||
util::Buffer<int>(indexBuffer.get(), indexSize),
|
||||
util::Buffer<VertexAttribute>(
|
||||
CHUNK_VATTRS, sizeof(CHUNK_VATTRS) / sizeof(VertexAttribute)
|
||||
)
|
||||
),
|
||||
std::move(sortingMesh)};
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
ChunkMesh BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) {
|
||||
build(chunk, chunks);
|
||||
|
||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
|
||||
return std::make_shared<Mesh>(
|
||||
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, attrs
|
||||
);
|
||||
size_t vcount = vertexOffset / CHUNK_VERTEX_SIZE;
|
||||
return ChunkMesh{std::make_unique<Mesh>(
|
||||
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, CHUNK_VATTRS
|
||||
), std::move(sortingMesh)};
|
||||
}
|
||||
|
||||
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "voxels/VoxelsVolume.hpp"
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "maths/util.hpp"
|
||||
#include "commons.hpp"
|
||||
|
||||
class Content;
|
||||
class Mesh;
|
||||
@@ -19,15 +20,14 @@ class Block;
|
||||
class Chunk;
|
||||
class Chunks;
|
||||
class VoxelsVolume;
|
||||
class ChunksStorage;
|
||||
class Chunks;
|
||||
class ContentGfxCache;
|
||||
struct EngineSettings;
|
||||
struct UVRegion;
|
||||
|
||||
class BlocksRenderer {
|
||||
static const glm::vec3 SUN_VECTOR;
|
||||
static const uint VERTEX_SIZE;
|
||||
const Content* const content;
|
||||
const Content& content;
|
||||
std::unique_ptr<float[]> vertexBuffer;
|
||||
std::unique_ptr<int[]> indexBuffer;
|
||||
size_t vertexOffset;
|
||||
@@ -40,11 +40,13 @@ class BlocksRenderer {
|
||||
std::unique_ptr<VoxelsVolume> voxelsBuffer;
|
||||
|
||||
const Block* const* blockDefsCache;
|
||||
const ContentGfxCache* const cache;
|
||||
const EngineSettings* settings;
|
||||
const ContentGfxCache& cache;
|
||||
const EngineSettings& settings;
|
||||
|
||||
util::PseudoRandom randomizer;
|
||||
|
||||
SortingMeshData sortingMesh;
|
||||
|
||||
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
|
||||
void index(int a, int b, int c, int d, int e, int f);
|
||||
|
||||
@@ -115,7 +117,6 @@ class BlocksRenderer {
|
||||
|
||||
bool isOpenForLight(int x, int y, int z) const;
|
||||
|
||||
|
||||
// Does block allow to see other blocks sides (is it transparent)
|
||||
inline bool isOpen(const glm::ivec3& pos, ubyte group) const {
|
||||
auto id = voxelsBuffer->pickBlockId(
|
||||
@@ -135,14 +136,21 @@ class BlocksRenderer {
|
||||
glm::vec4 pickLight(const glm::ivec3& coord) const;
|
||||
glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||
void render(const voxel* voxels);
|
||||
|
||||
void render(const voxel* voxels, int beginEnds[256][2]);
|
||||
SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]);
|
||||
public:
|
||||
BlocksRenderer(size_t capacity, const Content* content, const ContentGfxCache* cache, const EngineSettings* settings);
|
||||
BlocksRenderer(
|
||||
size_t capacity,
|
||||
const Content& content,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
virtual ~BlocksRenderer();
|
||||
|
||||
void build(const Chunk* chunk, const ChunksStorage* chunks);
|
||||
std::shared_ptr<Mesh> render(const Chunk* chunk, const ChunksStorage* chunks);
|
||||
MeshData createMesh();
|
||||
void build(const Chunk* chunk, const Chunks* chunks);
|
||||
ChunkMesh render(const Chunk* chunk, const Chunks* chunks);
|
||||
ChunkMeshData createMesh();
|
||||
VoxelsVolume* getVoxelsBuffer() const;
|
||||
|
||||
bool isCancelled() const {
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
#include "ChunksRenderer.hpp"
|
||||
#include "BlocksRenderer.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
#include "graphics/core/Mesh.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "maths/FrustumCulling.hpp"
|
||||
#include "util/listutil.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
|
||||
static debug::Logger logger("chunks-render");
|
||||
|
||||
size_t ChunksRenderer::visibleChunks = 0;
|
||||
|
||||
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
|
||||
Level* level;
|
||||
const Level& level;
|
||||
BlocksRenderer renderer;
|
||||
public:
|
||||
RendererWorker(
|
||||
Level* level,
|
||||
const ContentGfxCache* cache,
|
||||
const EngineSettings* settings
|
||||
const Level& level,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
) : level(level),
|
||||
renderer(settings->graphics.chunkMaxVertices.get(),
|
||||
level->content, cache, settings)
|
||||
renderer(settings.graphics.chunkMaxVertices.get(),
|
||||
*level.content, cache, settings)
|
||||
{}
|
||||
|
||||
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
|
||||
renderer.build(chunk.get(), level->chunksStorage.get());
|
||||
renderer.build(chunk.get(), level.chunks.get());
|
||||
if (renderer.isCancelled()) {
|
||||
return RendererResult {
|
||||
glm::ivec2(chunk->x, chunk->z), true, MeshData()};
|
||||
@@ -38,24 +44,33 @@ public:
|
||||
};
|
||||
|
||||
ChunksRenderer::ChunksRenderer(
|
||||
Level* level,
|
||||
const ContentGfxCache* cache,
|
||||
const EngineSettings* settings
|
||||
) : level(level),
|
||||
const Level* level,
|
||||
const Assets& assets,
|
||||
const Frustum& frustum,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
) : level(*level),
|
||||
assets(assets),
|
||||
frustum(frustum),
|
||||
settings(settings),
|
||||
threadPool(
|
||||
"chunks-render-pool",
|
||||
[=](){return std::make_shared<RendererWorker>(level, cache, settings);},
|
||||
[=](RendererResult& result){
|
||||
[&](){return std::make_shared<RendererWorker>(*level, cache, settings);},
|
||||
[&](RendererResult& result){
|
||||
if (!result.cancelled) {
|
||||
meshes[result.key] = std::make_shared<Mesh>(result.meshData);
|
||||
auto meshData = std::move(result.meshData);
|
||||
meshes[result.key] = ChunkMesh {
|
||||
std::make_unique<Mesh>(meshData.mesh),
|
||||
std::move(meshData.sortingMesh)
|
||||
};
|
||||
}
|
||||
inwork.erase(result.key);
|
||||
}, settings->graphics.chunkMaxRenderers.get())
|
||||
}, settings.graphics.chunkMaxRenderers.get())
|
||||
{
|
||||
threadPool.setStopOnFail(false);
|
||||
renderer = std::make_unique<BlocksRenderer>(
|
||||
settings->graphics.chunkMaxVertices.get(),
|
||||
level->content, cache, settings
|
||||
settings.graphics.chunkMaxVertices.get(),
|
||||
*level->content, cache, settings
|
||||
);
|
||||
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
|
||||
}
|
||||
@@ -63,12 +78,16 @@ ChunksRenderer::ChunksRenderer(
|
||||
ChunksRenderer::~ChunksRenderer() {
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::render(const std::shared_ptr<Chunk>& chunk, bool important) {
|
||||
const Mesh* ChunksRenderer::render(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
) {
|
||||
chunk->flags.modified = false;
|
||||
if (important) {
|
||||
auto mesh = renderer->render(chunk.get(), level->chunksStorage.get());
|
||||
meshes[glm::ivec2(chunk->x, chunk->z)] = mesh;
|
||||
return mesh;
|
||||
auto mesh = renderer->render(chunk.get(), level.chunks.get());
|
||||
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
||||
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
||||
};
|
||||
return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get();
|
||||
}
|
||||
glm::ivec2 key(chunk->x, chunk->z);
|
||||
if (inwork.find(key) != inwork.end()) {
|
||||
@@ -92,7 +111,9 @@ void ChunksRenderer::clear() {
|
||||
threadPool.clearQueue();
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>& chunk, bool important) {
|
||||
const Mesh* ChunksRenderer::getOrRender(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
) {
|
||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found == meshes.end()) {
|
||||
return render(chunk, important);
|
||||
@@ -100,17 +121,180 @@ std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>&
|
||||
if (chunk->flags.modified) {
|
||||
render(chunk, important);
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> ChunksRenderer::get(Chunk* chunk) {
|
||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found != meshes.end()) {
|
||||
return found->second;
|
||||
}
|
||||
return nullptr;
|
||||
return found->second.mesh.get();
|
||||
}
|
||||
|
||||
void ChunksRenderer::update() {
|
||||
threadPool.update();
|
||||
}
|
||||
|
||||
const Mesh* ChunksRenderer::retrieveChunk(
|
||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||
) {
|
||||
auto chunk = level.chunks->getChunks()[index];
|
||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||
return nullptr;
|
||||
}
|
||||
float distance = glm::distance(
|
||||
camera.position,
|
||||
glm::vec3(
|
||||
(chunk->x + 0.5f) * CHUNK_W,
|
||||
camera.position.y,
|
||||
(chunk->z + 0.5f) * CHUNK_D
|
||||
)
|
||||
);
|
||||
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
||||
if (mesh == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (culling) {
|
||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||
glm::vec3 max(
|
||||
chunk->x * CHUNK_W + CHUNK_W,
|
||||
chunk->top,
|
||||
chunk->z * CHUNK_D + CHUNK_D
|
||||
);
|
||||
|
||||
if (!frustum.isBoxVisible(min, max)) return nullptr;
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void ChunksRenderer::drawChunks(
|
||||
const Camera& camera, Shader& shader
|
||||
) {
|
||||
const auto& chunks = *level.chunks;
|
||||
const auto& atlas = assets.require<Atlas>("blocks");
|
||||
|
||||
atlas.getTexture()->bind();
|
||||
update();
|
||||
|
||||
// [warning] this whole method is not thread-safe for chunks
|
||||
|
||||
int chunksWidth = chunks.getWidth();
|
||||
int chunksOffsetX = chunks.getOffsetX();
|
||||
int chunksOffsetY = chunks.getOffsetY();
|
||||
|
||||
if (indices.size() != chunks.getVolume()) {
|
||||
indices.clear();
|
||||
for (int i = 0; i < chunks.getVolume(); i++) {
|
||||
indices.push_back(ChunksSortEntry {i, 0});
|
||||
}
|
||||
}
|
||||
float px = camera.position.x / static_cast<float>(CHUNK_W) - 0.5f;
|
||||
float pz = camera.position.z / static_cast<float>(CHUNK_D) - 0.5f;
|
||||
for (auto& index : indices) {
|
||||
float x = index.index % chunksWidth + chunksOffsetX - px;
|
||||
float z = index.index / chunksWidth + chunksOffsetY - pz;
|
||||
index.d = (x * x + z * z) * 1024;
|
||||
}
|
||||
util::insertion_sort(indices.begin(), indices.end());
|
||||
|
||||
bool culling = settings.graphics.frustumCulling.get();
|
||||
|
||||
visibleChunks = 0;
|
||||
shader.uniform1i("u_alphaClip", true);
|
||||
|
||||
// TODO: minimize draw calls number
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
auto chunk = chunks.getChunks()[indices[i].index];
|
||||
auto mesh = retrieveChunk(indices[i].index, camera, shader, culling);
|
||||
|
||||
if (mesh) {
|
||||
glm::vec3 coord(
|
||||
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
|
||||
);
|
||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||
shader.uniformMatrix("u_model", model);
|
||||
mesh->draw();
|
||||
visibleChunks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void write_sorting_mesh_entries(
|
||||
float* buffer, const std::vector<SortingMeshEntry>& chunkEntries
|
||||
) {
|
||||
for (const auto& entry : chunkEntries) {
|
||||
const auto& vertexData = entry.vertexData;
|
||||
std::memcpy(
|
||||
buffer,
|
||||
vertexData.data(),
|
||||
vertexData.size() * sizeof(float)
|
||||
);
|
||||
buffer += vertexData.size();
|
||||
}
|
||||
}
|
||||
|
||||
void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
||||
const int sortInterval = TRANSLUCENT_BLOCKS_SORT_INTERVAL;
|
||||
static int frameid = 0;
|
||||
frameid++;
|
||||
|
||||
bool culling = settings.graphics.frustumCulling.get();
|
||||
const auto& chunks = level.chunks->getChunks();
|
||||
const auto& cameraPos = camera.position;
|
||||
const auto& atlas = assets.require<Atlas>("blocks");
|
||||
|
||||
atlas.getTexture()->bind();
|
||||
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||
shader.uniform1i("u_alphaClip", false);
|
||||
|
||||
for (const auto& index : indices) {
|
||||
const auto& chunk = chunks[index.index];
|
||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||
continue;
|
||||
}
|
||||
const auto& found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found == meshes.end() || found->second.sortingMeshData.entries.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||
glm::vec3 max(
|
||||
chunk->x * CHUNK_W + CHUNK_W,
|
||||
chunk->top,
|
||||
chunk->z * CHUNK_D + CHUNK_D
|
||||
);
|
||||
|
||||
if (!frustum.isBoxVisible(min, max)) continue;
|
||||
|
||||
auto& chunkEntries = found->second.sortingMeshData.entries;
|
||||
|
||||
if (chunkEntries.size() == 1) {
|
||||
auto& entry = chunkEntries.at(0);
|
||||
if (found->second.sortedMesh == nullptr) {
|
||||
found->second.sortedMesh = std::make_unique<Mesh>(
|
||||
entry.vertexData.data(),
|
||||
entry.vertexData.size() / CHUNK_VERTEX_SIZE,
|
||||
CHUNK_VATTRS
|
||||
);
|
||||
}
|
||||
found->second.sortedMesh->draw();
|
||||
continue;
|
||||
}
|
||||
for (auto& entry : chunkEntries) {
|
||||
entry.distance = static_cast<long long>(
|
||||
glm::distance2(entry.position, cameraPos)
|
||||
);
|
||||
}
|
||||
if (found->second.sortedMesh == nullptr ||
|
||||
(frameid + chunk->x) % sortInterval == 0) {
|
||||
std::sort(chunkEntries.begin(), chunkEntries.end());
|
||||
size_t size = 0;
|
||||
for (const auto& entry : chunkEntries) {
|
||||
size += entry.vertexData.size();
|
||||
}
|
||||
|
||||
static util::Buffer<float> buffer;
|
||||
if (buffer.size() < size) {
|
||||
buffer = util::Buffer<float>(size);
|
||||
}
|
||||
write_sorting_mesh_entries(buffer.data(), chunkEntries);
|
||||
found->second.sortedMesh = std::make_unique<Mesh>(
|
||||
buffer.data(), size / CHUNK_VERTEX_SIZE, CHUNK_VATTRS
|
||||
);
|
||||
}
|
||||
found->second.sortedMesh->draw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,47 +4,80 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/hash.hpp>
|
||||
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/ChunksStorage.hpp"
|
||||
#include "util/ThreadPool.hpp"
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "commons.hpp"
|
||||
|
||||
class Mesh;
|
||||
class Chunk;
|
||||
class Level;
|
||||
class Camera;
|
||||
class Shader;
|
||||
class Assets;
|
||||
class Frustum;
|
||||
class BlocksRenderer;
|
||||
class ContentGfxCache;
|
||||
struct EngineSettings;
|
||||
|
||||
struct ChunksSortEntry {
|
||||
int index;
|
||||
int d;
|
||||
|
||||
inline bool operator<(const ChunksSortEntry& o) const noexcept {
|
||||
return d > o.d;
|
||||
}
|
||||
};
|
||||
|
||||
struct RendererResult {
|
||||
glm::ivec2 key;
|
||||
bool cancelled;
|
||||
MeshData meshData;
|
||||
ChunkMeshData meshData;
|
||||
};
|
||||
|
||||
class ChunksRenderer {
|
||||
Level* level;
|
||||
std::unique_ptr<BlocksRenderer> renderer;
|
||||
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
|
||||
std::unordered_map<glm::ivec2, bool> inwork;
|
||||
const Level& level;
|
||||
const Assets& assets;
|
||||
const Frustum& frustum;
|
||||
const EngineSettings& settings;
|
||||
|
||||
std::unique_ptr<BlocksRenderer> renderer;
|
||||
std::unordered_map<glm::ivec2, ChunkMesh> meshes;
|
||||
std::unordered_map<glm::ivec2, bool> inwork;
|
||||
std::vector<ChunksSortEntry> indices;
|
||||
util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
|
||||
const Mesh* retrieveChunk(
|
||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||
);
|
||||
public:
|
||||
ChunksRenderer(
|
||||
Level* level,
|
||||
const ContentGfxCache* cache,
|
||||
const EngineSettings* settings
|
||||
const Level* level,
|
||||
const Assets& assets,
|
||||
const Frustum& frustum,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
virtual ~ChunksRenderer();
|
||||
|
||||
std::shared_ptr<Mesh> render(const std::shared_ptr<Chunk>& chunk, bool important);
|
||||
const Mesh* render(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
);
|
||||
void unload(const Chunk* chunk);
|
||||
void clear();
|
||||
|
||||
std::shared_ptr<Mesh> getOrRender(const std::shared_ptr<Chunk>& chunk, bool important);
|
||||
std::shared_ptr<Mesh> get(Chunk* chunk);
|
||||
const Mesh* getOrRender(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
);
|
||||
void drawChunks(const Camera& camera, Shader& shader);
|
||||
|
||||
void drawSortedMeshes(const Camera& camera, Shader& shader);
|
||||
|
||||
void update();
|
||||
|
||||
static size_t visibleChunks;
|
||||
};
|
||||
|
||||
@@ -64,6 +64,7 @@ void Emitter::update(
|
||||
return;
|
||||
}
|
||||
}
|
||||
timer += delta;
|
||||
const float maxDistance = preset.maxDistance;
|
||||
if (glm::distance2(position, cameraPosition) > maxDistance * maxDistance) {
|
||||
if (count > 0) {
|
||||
@@ -77,7 +78,6 @@ void Emitter::update(
|
||||
}
|
||||
return;
|
||||
}
|
||||
timer += delta;
|
||||
while (count && timer > spawnInterval) {
|
||||
// spawn particle
|
||||
Particle particle = prototype;
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "GuidesRenderer.hpp"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/LineBatch.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "maths/voxmaths.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "constants.hpp"
|
||||
|
||||
void GuidesRenderer::drawBorders(
|
||||
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
|
||||
) {
|
||||
int ww = ex - sx;
|
||||
int dd = ez - sz;
|
||||
/*corner*/ {
|
||||
batch.line(sx, sy, sz, sx, ey, sz, 0.8f, 0, 0.8f, 1);
|
||||
batch.line(sx, sy, ez, sx, ey, ez, 0.8f, 0, 0.8f, 1);
|
||||
batch.line(ex, sy, sz, ex, ey, sz, 0.8f, 0, 0.8f, 1);
|
||||
batch.line(ex, sy, ez, ex, ey, ez, 0.8f, 0, 0.8f, 1);
|
||||
}
|
||||
for (int i = 2; i < ww; i += 2) {
|
||||
batch.line(sx + i, sy, sz, sx + i, ey, sz, 0, 0, 0.8f, 1);
|
||||
batch.line(sx + i, sy, ez, sx + i, ey, ez, 0, 0, 0.8f, 1);
|
||||
}
|
||||
for (int i = 2; i < dd; i += 2) {
|
||||
batch.line(sx, sy, sz + i, sx, ey, sz + i, 0.8f, 0, 0, 1);
|
||||
batch.line(ex, sy, sz + i, ex, ey, sz + i, 0.8f, 0, 0, 1);
|
||||
}
|
||||
for (int i = sy; i < ey; i += 2) {
|
||||
batch.line(sx, i, sz, sx, i, ez, 0, 0.8f, 0, 1);
|
||||
batch.line(sx, i, ez, ex, i, ez, 0, 0.8f, 0, 1);
|
||||
batch.line(ex, i, ez, ex, i, sz, 0, 0.8f, 0, 1);
|
||||
batch.line(ex, i, sz, sx, i, sz, 0, 0.8f, 0, 1);
|
||||
}
|
||||
batch.flush();
|
||||
}
|
||||
|
||||
void GuidesRenderer::drawCoordSystem(
|
||||
LineBatch& batch, const DrawContext& pctx, float length
|
||||
) {
|
||||
auto ctx = pctx.sub();
|
||||
ctx.setDepthTest(false);
|
||||
batch.lineWidth(4.0f);
|
||||
batch.line(0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
|
||||
batch.line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 1.f);
|
||||
batch.line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 1.f);
|
||||
batch.flush();
|
||||
|
||||
ctx.setDepthTest(true);
|
||||
batch.lineWidth(2.0f);
|
||||
batch.line(0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f);
|
||||
batch.line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 1.f);
|
||||
batch.line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
void GuidesRenderer::renderDebugLines(
|
||||
const DrawContext& pctx,
|
||||
const Camera& camera,
|
||||
LineBatch& batch,
|
||||
Shader& linesShader,
|
||||
bool showChunkBorders
|
||||
) {
|
||||
DrawContext ctx = pctx.sub(&batch);
|
||||
const auto& viewport = ctx.getViewport();
|
||||
uint displayWidth = viewport.getWidth();
|
||||
uint displayHeight = viewport.getHeight();
|
||||
|
||||
ctx.setDepthTest(true);
|
||||
|
||||
linesShader.use();
|
||||
|
||||
if (showChunkBorders) {
|
||||
linesShader.uniformMatrix("u_projview", camera.getProjView());
|
||||
glm::vec3 coord = camera.position;
|
||||
if (coord.x < 0) coord.x--;
|
||||
if (coord.z < 0) coord.z--;
|
||||
int cx = floordiv(static_cast<int>(coord.x), CHUNK_W);
|
||||
int cz = floordiv(static_cast<int>(coord.z), CHUNK_D);
|
||||
|
||||
drawBorders(
|
||||
batch,
|
||||
cx * CHUNK_W,
|
||||
0,
|
||||
cz * CHUNK_D,
|
||||
(cx + 1) * CHUNK_W,
|
||||
CHUNK_H,
|
||||
(cz + 1) * CHUNK_D
|
||||
);
|
||||
}
|
||||
|
||||
float length = 40.f;
|
||||
glm::vec3 tsl(displayWidth / 2, displayHeight / 2, 0.f);
|
||||
glm::mat4 model(glm::translate(glm::mat4(1.f), tsl));
|
||||
linesShader.uniformMatrix(
|
||||
"u_projview",
|
||||
glm::ortho(
|
||||
0.f,
|
||||
static_cast<float>(displayWidth),
|
||||
0.f,
|
||||
static_cast<float>(displayHeight),
|
||||
-length,
|
||||
length
|
||||
) * model *
|
||||
glm::inverse(camera.rotation)
|
||||
);
|
||||
drawCoordSystem(batch, ctx, length);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
class LineBatch;
|
||||
class DrawContext;
|
||||
class Camera;
|
||||
class Shader;
|
||||
|
||||
class GuidesRenderer {
|
||||
public:
|
||||
void drawBorders(
|
||||
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
|
||||
);
|
||||
void drawCoordSystem(
|
||||
LineBatch& batch, const DrawContext& pctx, float length
|
||||
);
|
||||
|
||||
/// @brief Render all debug lines (chunks borders, coord system guides)
|
||||
/// @param context graphics context
|
||||
/// @param camera active camera
|
||||
/// @param linesShader shader used
|
||||
void renderDebugLines(
|
||||
const DrawContext& context,
|
||||
const Camera& camera,
|
||||
LineBatch& batch,
|
||||
Shader& linesShader,
|
||||
bool showChunkBorders
|
||||
);
|
||||
};
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
|
||||
static const vattr attrs[] = {
|
||||
static const VertexAttribute attrs[] = {
|
||||
{3}, {2}, {3}, {1}, {0}
|
||||
};
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ static glm::mat4 extract_rotation(glm::mat4 matrix) {
|
||||
|
||||
ModelBatch::ModelBatch(
|
||||
size_t capacity,
|
||||
Assets* assets,
|
||||
Chunks* chunks,
|
||||
const EngineSettings* settings
|
||||
const Assets& assets,
|
||||
const Chunks& chunks,
|
||||
const EngineSettings& settings
|
||||
)
|
||||
: batch(std::make_unique<MainBatch>(capacity)),
|
||||
assets(assets),
|
||||
@@ -73,7 +73,7 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix,
|
||||
if (mesh.lighting) {
|
||||
glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
gpos += lightsOffset;
|
||||
lights = MainBatch::sampleLight(gpos, *chunks, backlight);
|
||||
lights = MainBatch::sampleLight(gpos, chunks, backlight);
|
||||
}
|
||||
for (size_t i = 0; i < vcount / 3; i++) {
|
||||
batch->prepare(3);
|
||||
@@ -107,7 +107,7 @@ void ModelBatch::render() {
|
||||
return a.mesh->texture < b.mesh->texture;
|
||||
}
|
||||
);
|
||||
bool backlight = settings->graphics.backlight.get();
|
||||
bool backlight = settings.graphics.backlight.get();
|
||||
for (auto& entry : entries) {
|
||||
draw(
|
||||
*entry.mesh,
|
||||
@@ -136,6 +136,6 @@ void ModelBatch::setTexture(const std::string& name,
|
||||
return setTexture(found->second, varTextures);
|
||||
}
|
||||
}
|
||||
auto region = util::get_texture_region(*assets, name, "blocks:notfound");
|
||||
auto region = util::get_texture_region(assets, name, "blocks:notfound");
|
||||
batch->setTexture(region.texture, region.region);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace model {
|
||||
using texture_names_map = std::unordered_map<std::string, std::string>;
|
||||
|
||||
class ModelBatch {
|
||||
Assets* assets;
|
||||
Chunks* chunks;
|
||||
const Assets& assets;
|
||||
const Chunks& chunks;
|
||||
|
||||
const EngineSettings* settings;
|
||||
const EngineSettings& settings;
|
||||
glm::vec3 lightsOffset {};
|
||||
|
||||
static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f};
|
||||
@@ -39,6 +39,7 @@ class ModelBatch {
|
||||
glm::vec3 tint,
|
||||
const texture_names_map* varTextures,
|
||||
bool backlight);
|
||||
|
||||
void setTexture(const std::string& name,
|
||||
const texture_names_map* varTextures);
|
||||
|
||||
@@ -53,9 +54,9 @@ class ModelBatch {
|
||||
public:
|
||||
ModelBatch(
|
||||
size_t capacity,
|
||||
Assets* assets,
|
||||
Chunks* chunks,
|
||||
const EngineSettings* settings
|
||||
const Assets& assets,
|
||||
const Chunks& chunks,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
~ModelBatch();
|
||||
|
||||
|
||||
@@ -60,43 +60,42 @@ model::Model ModelsGenerator::fromCustom(
|
||||
auto& mesh = model.addMesh("blocks:" + modelTextures[i * 6]);
|
||||
mesh.lighting = lighting;
|
||||
const UVRegion boxtexfaces[6] = {
|
||||
get_region_for(modelTextures[i * 6], assets),
|
||||
get_region_for(modelTextures[i * 6 + 1], assets),
|
||||
get_region_for(modelTextures[i * 6 + 2], assets),
|
||||
get_region_for(modelTextures[i * 6 + 3], assets),
|
||||
get_region_for(modelTextures[i * 6 + 5], assets),
|
||||
get_region_for(modelTextures[i * 6 + 4], assets),
|
||||
get_region_for(modelTextures[i * 6 + 5], assets)
|
||||
get_region_for(modelTextures[i * 6 + 3], assets),
|
||||
get_region_for(modelTextures[i * 6 + 2], assets),
|
||||
get_region_for(modelTextures[i * 6 + 1], assets),
|
||||
get_region_for(modelTextures[i * 6 + 0], assets)
|
||||
};
|
||||
mesh.addBox(
|
||||
modelBoxes[i].center(), modelBoxes[i].size() * 0.5f, boxtexfaces
|
||||
);
|
||||
}
|
||||
glm::vec3 poff = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
glm::vec3 norm {0, 1, 0};
|
||||
for (size_t i = 0; i < points.size() / 4; i++) {
|
||||
auto texture = "blocks:" + modelTextures[modelBoxes.size() * 6 + i];
|
||||
auto texture = modelTextures[modelBoxes.size() * 6 + i];
|
||||
|
||||
auto& mesh = model.addMesh(texture);
|
||||
mesh.lighting = lighting;
|
||||
|
||||
auto reg = get_region_for(texture, assets);
|
||||
mesh.vertices.push_back(
|
||||
{points[i * 4 + 0] - poff, glm::vec2(reg.u1, reg.v1), norm}
|
||||
{points[i * 4 + 0], glm::vec2(reg.u1, reg.v1), norm}
|
||||
);
|
||||
mesh.vertices.push_back(
|
||||
{points[i * 4 + 1] - poff, glm::vec2(reg.u2, reg.v1), norm}
|
||||
{points[i * 4 + 1], glm::vec2(reg.u2, reg.v1), norm}
|
||||
);
|
||||
mesh.vertices.push_back(
|
||||
{points[i * 4 + 2] - poff, glm::vec2(reg.u2, reg.v2), norm}
|
||||
{points[i * 4 + 2], glm::vec2(reg.u2, reg.v2), norm}
|
||||
);
|
||||
mesh.vertices.push_back(
|
||||
{points[i * 4 + 3] - poff, glm::vec2(reg.u1, reg.v1), norm}
|
||||
{points[i * 4 + 0], glm::vec2(reg.u1, reg.v1), norm}
|
||||
);
|
||||
mesh.vertices.push_back(
|
||||
{points[i * 4 + 4] - poff, glm::vec2(reg.u2, reg.v2), norm}
|
||||
{points[i * 4 + 2], glm::vec2(reg.u2, reg.v2), norm}
|
||||
);
|
||||
mesh.vertices.push_back(
|
||||
{points[i * 4 + 0] - poff, glm::vec2(reg.u1, reg.v2), norm}
|
||||
{points[i * 4 + 3], glm::vec2(reg.u1, reg.v2), norm}
|
||||
);
|
||||
}
|
||||
return model;
|
||||
|
||||
@@ -18,7 +18,7 @@ size_t ParticlesRenderer::aliveEmitters = 0;
|
||||
ParticlesRenderer::ParticlesRenderer(
|
||||
const Assets& assets, const Level& level, const GraphicsSettings* settings
|
||||
)
|
||||
: batch(std::make_unique<MainBatch>(1024)),
|
||||
: batch(std::make_unique<MainBatch>(4096)),
|
||||
level(level),
|
||||
assets(assets),
|
||||
settings(settings) {}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <iostream>
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.141592
|
||||
@@ -23,7 +24,7 @@
|
||||
const int STARS_COUNT = 3000;
|
||||
const int STARS_SEED = 632;
|
||||
|
||||
Skybox::Skybox(uint size, Shader* shader)
|
||||
Skybox::Skybox(uint size, Shader& shader)
|
||||
: size(size),
|
||||
shader(shader),
|
||||
batch3d(std::make_unique<Batch3D>(4096))
|
||||
@@ -38,19 +39,19 @@ Skybox::Skybox(uint size, Shader* shader)
|
||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||
};
|
||||
vattr attrs[] {{2}, {0}};
|
||||
VertexAttribute attrs[] {{2}, {0}};
|
||||
mesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||
|
||||
sprites.push_back(skysprite {
|
||||
"misc/moon",
|
||||
M_PI*0.5f,
|
||||
glm::pi<float>()*0.5f,
|
||||
4.0f,
|
||||
false
|
||||
});
|
||||
|
||||
sprites.push_back(skysprite {
|
||||
"misc/sun",
|
||||
M_PI*1.5f,
|
||||
glm::pi<float>()*1.5f,
|
||||
4.0f,
|
||||
true
|
||||
});
|
||||
@@ -115,13 +116,13 @@ void Skybox::draw(
|
||||
p_shader->uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
batch3d->begin();
|
||||
|
||||
float angle = daytime * float(M_PI) * 2.0f;
|
||||
float angle = daytime * glm::pi<float>() * 2.0f;
|
||||
float opacity = glm::pow(1.0f-fog, 7.0f);
|
||||
|
||||
for (auto& sprite : sprites) {
|
||||
batch3d->texture(assets.get<Texture>(sprite.texture));
|
||||
|
||||
float sangle = daytime * float(M_PI)*2.0 + sprite.phase;
|
||||
float sangle = daytime * glm::pi<float>()*2.0 + sprite.phase;
|
||||
float distance = sprite.distance;
|
||||
|
||||
glm::vec3 pos(-std::cos(sangle)*distance, std::sin(sangle)*distance, 0);
|
||||
@@ -152,15 +153,15 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality)
|
||||
ready = true;
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
cubemap->bind();
|
||||
shader->use();
|
||||
t *= M_PI*2.0f;
|
||||
shader.use();
|
||||
t *= glm::pi<float>()*2.0f;
|
||||
|
||||
lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f));
|
||||
shader->uniform1i("u_quality", quality);
|
||||
shader->uniform1f("u_mie", mie);
|
||||
shader->uniform1f("u_fog", mie - 1.0f);
|
||||
shader->uniform3f("u_lightDir", lightDir);
|
||||
shader->uniform1f("u_dayTime", dayTime);
|
||||
shader.uniform1i("u_quality", quality);
|
||||
shader.uniform1f("u_mie", mie);
|
||||
shader.uniform1f("u_fog", mie - 1.0f);
|
||||
shader.uniform3f("u_lightDir", lightDir);
|
||||
shader.uniform1f("u_dayTime", dayTime);
|
||||
|
||||
if (glm::abs(mie-prevMie) + glm::abs(t-prevT) >= 0.01) {
|
||||
for (uint face = 0; face < 6; face++) {
|
||||
@@ -206,10 +207,16 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) {
|
||||
{0.0f, 0.0f, -1.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
};
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0);
|
||||
shader->uniform3f("u_xaxis", xaxs[face]);
|
||||
shader->uniform3f("u_yaxis", yaxs[face]);
|
||||
shader->uniform3f("u_zaxis", zaxs[face]);
|
||||
glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
|
||||
cubemap->getId(),
|
||||
0
|
||||
);
|
||||
shader.uniform3f("u_xaxis", xaxs[face]);
|
||||
shader.uniform3f("u_yaxis", yaxs[face]);
|
||||
shader.uniform3f("u_zaxis", zaxs[face]);
|
||||
mesh->draw();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ struct skysprite {
|
||||
class Skybox {
|
||||
std::unique_ptr<Framebuffer> fbo;
|
||||
uint size;
|
||||
Shader* shader;
|
||||
Shader& shader;
|
||||
bool ready = false;
|
||||
FastRandom random;
|
||||
glm::vec3 lightDir;
|
||||
@@ -46,7 +46,7 @@ class Skybox {
|
||||
);
|
||||
void refreshFace(uint face, Cubemap* cubemap);
|
||||
public:
|
||||
Skybox(uint size, Shader* shader);
|
||||
Skybox(uint size, Shader& shader);
|
||||
~Skybox();
|
||||
|
||||
void draw(
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "TextNote.hpp"
|
||||
|
||||
TextNote::TextNote(std::wstring text, NotePreset preset, glm::vec3 position)
|
||||
: text(std::move(text)),
|
||||
preset(std::move(preset)),
|
||||
position(std::move(position)) {
|
||||
}
|
||||
|
||||
void TextNote::setText(std::wstring_view text) {
|
||||
this->text = text;
|
||||
}
|
||||
|
||||
const std::wstring& TextNote::getText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
const NotePreset& TextNote::getPreset() const {
|
||||
return preset;
|
||||
}
|
||||
|
||||
void TextNote::updatePreset(const dv::value& data) {
|
||||
preset.deserialize(data);
|
||||
}
|
||||
|
||||
void TextNote::setPosition(const glm::vec3& position) {
|
||||
this->position = position;
|
||||
}
|
||||
|
||||
const glm::vec3& TextNote::getPosition() const {
|
||||
return position;
|
||||
}
|
||||
|
||||
const glm::vec3& TextNote::getAxisX() const {
|
||||
return xAxis;
|
||||
}
|
||||
|
||||
const glm::vec3& TextNote::getAxisY() const {
|
||||
return yAxis;
|
||||
}
|
||||
|
||||
void TextNote::setAxisX(const glm::vec3& vec) {
|
||||
xAxis = vec;
|
||||
}
|
||||
|
||||
void TextNote::setAxisY(const glm::vec3& vec) {
|
||||
yAxis = vec;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "presets/NotePreset.hpp"
|
||||
|
||||
/// @brief 3D text instance
|
||||
class TextNote {
|
||||
std::wstring text;
|
||||
NotePreset preset;
|
||||
glm::vec3 position;
|
||||
|
||||
glm::vec3 xAxis {1, 0, 0};
|
||||
glm::vec3 yAxis {0, 1, 0};
|
||||
public:
|
||||
TextNote(std::wstring text, NotePreset preset, glm::vec3 position);
|
||||
|
||||
void setText(std::wstring_view text);
|
||||
|
||||
const std::wstring& getText() const;
|
||||
|
||||
const NotePreset& getPreset() const;
|
||||
|
||||
void updatePreset(const dv::value& data);
|
||||
|
||||
void setPosition(const glm::vec3& position);
|
||||
const glm::vec3& getPosition() const;
|
||||
|
||||
const glm::vec3& getAxisX() const;
|
||||
const glm::vec3& getAxisY() const;
|
||||
|
||||
void setAxisX(const glm::vec3& vec);
|
||||
void setAxisY(const glm::vec3& vec);
|
||||
};
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "TextsRenderer.hpp"
|
||||
|
||||
#include "TextNote.hpp"
|
||||
#include "maths/util.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "window/Window.hpp"
|
||||
#include "maths/FrustumCulling.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "graphics/core/Batch3D.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "presets/NotePreset.hpp"
|
||||
|
||||
TextsRenderer::TextsRenderer(
|
||||
Batch3D& batch, const Assets& assets, const Frustum& frustum
|
||||
)
|
||||
: batch(batch), assets(assets), frustum(frustum) {
|
||||
}
|
||||
|
||||
void TextsRenderer::renderNote(
|
||||
const TextNote& note,
|
||||
const DrawContext& context,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
bool hudVisible,
|
||||
bool frontLayer,
|
||||
bool projected
|
||||
) {
|
||||
const auto& text = note.getText();
|
||||
const auto& preset = note.getPreset();
|
||||
auto pos = note.getPosition();
|
||||
|
||||
if (util::distance2(pos, camera.position) >
|
||||
util::sqr(preset.renderDistance / camera.zoom)) {
|
||||
return;
|
||||
}
|
||||
// Projected notes are displayed on the front layer only
|
||||
if ((preset.displayMode == NoteDisplayMode::PROJECTED) != projected) {
|
||||
return;
|
||||
}
|
||||
float opacity = 1.0f;
|
||||
if (frontLayer && preset.displayMode != NoteDisplayMode::PROJECTED) {
|
||||
if (preset.xrayOpacity <= 0.0001f) {
|
||||
return;
|
||||
}
|
||||
opacity = preset.xrayOpacity;
|
||||
}
|
||||
const auto& font = assets.require<Font>("normal");
|
||||
|
||||
glm::vec3 xvec = note.getAxisX();
|
||||
glm::vec3 yvec = note.getAxisY();
|
||||
|
||||
int width = font.calcWidth(text, text.length());
|
||||
if (preset.displayMode == NoteDisplayMode::Y_FREE_BILLBOARD ||
|
||||
preset.displayMode == NoteDisplayMode::XY_FREE_BILLBOARD) {
|
||||
xvec = camera.position - pos;
|
||||
xvec.y = 0;
|
||||
std::swap(xvec.x, xvec.z);
|
||||
xvec.z *= -1;
|
||||
xvec = glm::normalize(xvec);
|
||||
if (preset.displayMode == NoteDisplayMode::XY_FREE_BILLBOARD) {
|
||||
yvec = camera.up;
|
||||
}
|
||||
}
|
||||
if (preset.displayMode != NoteDisplayMode::PROJECTED) {
|
||||
if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f),
|
||||
pos + xvec * (width * 0.5f))) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
float scale = 1.0f;
|
||||
if (glm::abs(preset.perspective) > 0.0001f) {
|
||||
float scale2 = scale /
|
||||
(glm::distance(camera.position, pos) *
|
||||
util::sqr(camera.zoom) *
|
||||
glm::sqrt(glm::tan(camera.getFov() * 0.5f)));
|
||||
scale = scale2 * preset.perspective +
|
||||
scale * (1.0f - preset.perspective);
|
||||
}
|
||||
auto projpos = camera.getProjView() * glm::vec4(pos, 1.0f);
|
||||
pos = projpos;
|
||||
if (pos.z < 0.0f) {
|
||||
return;
|
||||
}
|
||||
pos /= pos.z;
|
||||
pos.z = 0;
|
||||
xvec = {2.0f/Window::width*scale, 0, 0};
|
||||
yvec = {0, 2.0f/Window::height*scale, 0};
|
||||
}
|
||||
auto color = preset.color;
|
||||
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
|
||||
font.draw(
|
||||
batch,
|
||||
text,
|
||||
pos - xvec * (width * 0.5f) * preset.scale,
|
||||
xvec * preset.scale,
|
||||
yvec * preset.scale
|
||||
);
|
||||
}
|
||||
|
||||
void TextsRenderer::render(
|
||||
const DrawContext& context,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
bool hudVisible,
|
||||
bool frontLayer
|
||||
) {
|
||||
auto& shader = assets.require<Shader>("ui3d");
|
||||
|
||||
shader.use();
|
||||
shader.uniformMatrix("u_projview", camera.getProjView());
|
||||
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
batch.begin();
|
||||
for (const auto& [_, note] : notes) {
|
||||
renderNote(*note, context, camera, settings, hudVisible, frontLayer, false);
|
||||
}
|
||||
batch.flush();
|
||||
if (frontLayer) {
|
||||
shader.uniformMatrix(
|
||||
"u_projview",
|
||||
glm::mat4(1.0f)
|
||||
);
|
||||
for (const auto& [_, note] : notes) {
|
||||
renderNote(*note, context, camera, settings, hudVisible, true, true);
|
||||
}
|
||||
batch.flush();
|
||||
}
|
||||
}
|
||||
|
||||
u64id_t TextsRenderer::add(std::unique_ptr<TextNote> note) {
|
||||
u64id_t uid = nextNote++;
|
||||
notes[uid] = std::move(note);
|
||||
return uid;
|
||||
}
|
||||
|
||||
TextNote* TextsRenderer::get(u64id_t id) const {
|
||||
const auto& found = notes.find(id);
|
||||
if (found == notes.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
void TextsRenderer::remove(u64id_t id) {
|
||||
notes.erase(id);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
#include "typedefs.hpp"
|
||||
|
||||
class DrawContext;
|
||||
class Camera;
|
||||
class Assets;
|
||||
class Batch3D;
|
||||
class Frustum;
|
||||
class TextNote;
|
||||
struct EngineSettings;
|
||||
|
||||
class TextsRenderer {
|
||||
Batch3D& batch;
|
||||
const Assets& assets;
|
||||
const Frustum& frustum;
|
||||
|
||||
std::unordered_map<u64id_t, std::unique_ptr<TextNote>> notes;
|
||||
u64id_t nextNote = 1;
|
||||
|
||||
void renderNote(
|
||||
const TextNote& note,
|
||||
const DrawContext& context,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
bool hudVisible,
|
||||
bool frontLayer,
|
||||
bool projected
|
||||
);
|
||||
public:
|
||||
TextsRenderer(Batch3D& batch, const Assets& assets, const Frustum& frustum);
|
||||
|
||||
void render(
|
||||
const DrawContext& context,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
bool hudVisible,
|
||||
bool frontLayer
|
||||
);
|
||||
|
||||
u64id_t add(std::unique_ptr<TextNote> note);
|
||||
|
||||
TextNote* get(u64id_t id) const;
|
||||
|
||||
void remove(u64id_t id);
|
||||
};
|
||||
@@ -40,160 +40,94 @@
|
||||
#include "graphics/core/PostProcessing.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "ParticlesRenderer.hpp"
|
||||
#include "TextsRenderer.hpp"
|
||||
#include "ChunksRenderer.hpp"
|
||||
#include "GuidesRenderer.hpp"
|
||||
#include "ModelBatch.hpp"
|
||||
#include "Skybox.hpp"
|
||||
#include "Emitter.hpp"
|
||||
#include "TextNote.hpp"
|
||||
|
||||
inline constexpr size_t BATCH3D_CAPACITY = 4096;
|
||||
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
|
||||
|
||||
bool WorldRenderer::showChunkBorders = false;
|
||||
bool WorldRenderer::showEntitiesDebug = false;
|
||||
|
||||
WorldRenderer::WorldRenderer(
|
||||
Engine* engine, LevelFrontend* frontend, Player* player
|
||||
Engine* engine, LevelFrontend& frontend, Player* player
|
||||
)
|
||||
: engine(engine),
|
||||
level(frontend->getLevel()),
|
||||
level(frontend.getLevel()),
|
||||
player(player),
|
||||
assets(*engine->getAssets()),
|
||||
frustumCulling(std::make_unique<Frustum>()),
|
||||
lineBatch(std::make_unique<LineBatch>()),
|
||||
batch3d(std::make_unique<Batch3D>(BATCH3D_CAPACITY)),
|
||||
modelBatch(std::make_unique<ModelBatch>(
|
||||
20'000,
|
||||
engine->getAssets(),
|
||||
level->chunks.get(),
|
||||
&engine->getSettings()
|
||||
MODEL_BATCH_CAPACITY, assets, *level.chunks, engine->getSettings()
|
||||
)),
|
||||
particles(std::make_unique<ParticlesRenderer>(
|
||||
*engine->getAssets(),
|
||||
*frontend->getLevel(),
|
||||
&engine->getSettings().graphics
|
||||
assets, level, &engine->getSettings().graphics
|
||||
)),
|
||||
texts(std::make_unique<TextsRenderer>(*batch3d, assets, *frustumCulling)),
|
||||
guides(std::make_unique<GuidesRenderer>()),
|
||||
chunks(std::make_unique<ChunksRenderer>(
|
||||
&level,
|
||||
assets,
|
||||
*frustumCulling,
|
||||
frontend.getContentGfxCache(),
|
||||
engine->getSettings()
|
||||
)) {
|
||||
renderer = std::make_unique<ChunksRenderer>(
|
||||
level, frontend->getContentGfxCache(), &engine->getSettings()
|
||||
);
|
||||
batch3d = std::make_unique<Batch3D>(4096);
|
||||
|
||||
auto& settings = engine->getSettings();
|
||||
level->events->listen(
|
||||
level.events->listen(
|
||||
EVT_CHUNK_HIDDEN,
|
||||
[this](lvl_event_type, Chunk* chunk) { renderer->unload(chunk); }
|
||||
[this](lvl_event_type, Chunk* chunk) { chunks->unload(chunk); }
|
||||
);
|
||||
auto assets = engine->getAssets();
|
||||
skybox = std::make_unique<Skybox>(
|
||||
settings.graphics.skyboxResolution.get(),
|
||||
assets->get<Shader>("skybox_gen")
|
||||
assets->require<Shader>("skybox_gen")
|
||||
);
|
||||
}
|
||||
|
||||
WorldRenderer::~WorldRenderer() = default;
|
||||
|
||||
bool WorldRenderer::drawChunk(
|
||||
size_t index, const Camera& camera, Shader* shader, bool culling
|
||||
) {
|
||||
auto chunk = level->chunks->getChunks()[index];
|
||||
if (!chunk->flags.lighted) {
|
||||
return false;
|
||||
}
|
||||
float distance = glm::distance(
|
||||
camera.position,
|
||||
glm::vec3(
|
||||
(chunk->x + 0.5f) * CHUNK_W,
|
||||
camera.position.y,
|
||||
(chunk->z + 0.5f) * CHUNK_D
|
||||
)
|
||||
);
|
||||
auto mesh = renderer->getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
||||
if (mesh == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (culling) {
|
||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||
glm::vec3 max(
|
||||
chunk->x * CHUNK_W + CHUNK_W,
|
||||
chunk->top,
|
||||
chunk->z * CHUNK_D + CHUNK_D
|
||||
);
|
||||
|
||||
if (!frustumCulling->isBoxVisible(min, max)) return false;
|
||||
}
|
||||
glm::vec3 coord(chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f);
|
||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||
shader->uniformMatrix("u_model", model);
|
||||
mesh->draw();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldRenderer::drawChunks(
|
||||
Chunks* chunks, const Camera& camera, Shader* shader
|
||||
) {
|
||||
auto assets = engine->getAssets();
|
||||
auto atlas = assets->get<Atlas>("blocks");
|
||||
|
||||
atlas->getTexture()->bind();
|
||||
renderer->update();
|
||||
|
||||
// [warning] this whole method is not thread-safe for chunks
|
||||
|
||||
std::vector<size_t> indices;
|
||||
for (size_t i = 0; i < chunks->getVolume(); i++) {
|
||||
if (chunks->getChunks()[i] == nullptr) continue;
|
||||
indices.emplace_back(i);
|
||||
}
|
||||
float px = camera.position.x / static_cast<float>(CHUNK_W) - 0.5f;
|
||||
float pz = camera.position.z / static_cast<float>(CHUNK_D) - 0.5f;
|
||||
std::sort(indices.begin(), indices.end(), [chunks, px, pz](auto i, auto j) {
|
||||
const auto& chunksBuffer = chunks->getChunks();
|
||||
const auto a = chunksBuffer[i].get();
|
||||
const auto b = chunksBuffer[j].get();
|
||||
auto adx = (a->x - px);
|
||||
auto adz = (a->z - pz);
|
||||
auto bdx = (b->x - px);
|
||||
auto bdz = (b->z - pz);
|
||||
return (adx * adx + adz * adz > bdx * bdx + bdz * bdz);
|
||||
});
|
||||
bool culling = engine->getSettings().graphics.frustumCulling.get();
|
||||
if (culling) {
|
||||
frustumCulling->update(camera.getProjView());
|
||||
}
|
||||
chunks->visible = 0;
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
chunks->visible += drawChunk(indices[i], camera, shader, culling);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::setupWorldShader(
|
||||
Shader* shader,
|
||||
Shader& shader,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
float fogFactor
|
||||
) {
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_model", glm::mat4(1.0f));
|
||||
shader->uniformMatrix("u_proj", camera.getProjection());
|
||||
shader->uniformMatrix("u_view", camera.getView());
|
||||
shader->uniform1f("u_timer", timer);
|
||||
shader->uniform1f("u_gamma", settings.graphics.gamma.get());
|
||||
shader->uniform1f("u_fogFactor", fogFactor);
|
||||
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
||||
shader->uniform1f("u_dayTime", level->getWorld()->getInfo().daytime);
|
||||
shader->uniform2f("u_lightDir", skybox->getLightDir());
|
||||
shader->uniform3f("u_cameraPos", camera.position);
|
||||
shader->uniform1i("u_cubemap", 1);
|
||||
shader.use();
|
||||
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||
shader.uniformMatrix("u_proj", camera.getProjection());
|
||||
shader.uniformMatrix("u_view", camera.getView());
|
||||
shader.uniform1f("u_timer", timer);
|
||||
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
|
||||
shader.uniform1f("u_fogFactor", fogFactor);
|
||||
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
||||
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
|
||||
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
||||
shader.uniform3f("u_cameraPos", camera.position);
|
||||
shader.uniform1i("u_cubemap", 1);
|
||||
|
||||
auto indices = level->content->getIndices();
|
||||
auto indices = level.content->getIndices();
|
||||
// Light emission when an emissive item is chosen
|
||||
{
|
||||
auto inventory = player->getInventory();
|
||||
ItemStack& stack = inventory->getSlot(player->getChosenSlot());
|
||||
auto& item = indices->items.require(stack.getItemId());
|
||||
float multiplier = 0.5f;
|
||||
shader->uniform3f(
|
||||
shader.uniform3f(
|
||||
"u_torchlightColor",
|
||||
item.emission[0] / 15.0f * multiplier,
|
||||
item.emission[1] / 15.0f * multiplier,
|
||||
item.emission[2] / 15.0f * multiplier
|
||||
);
|
||||
shader->uniform1f("u_torchlightDistance", 6.0f);
|
||||
shader.uniform1f("u_torchlightDistance", 6.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,32 +136,45 @@ void WorldRenderer::renderLevel(
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
float delta,
|
||||
bool pause
|
||||
bool pause,
|
||||
bool hudVisible
|
||||
) {
|
||||
auto assets = engine->getAssets();
|
||||
texts->render(ctx, camera, settings, hudVisible, false);
|
||||
|
||||
bool culling = engine->getSettings().graphics.frustumCulling.get();
|
||||
float fogFactor =
|
||||
15.0f / static_cast<float>(settings.chunks.loadDistance.get() - 2);
|
||||
|
||||
auto entityShader = assets->get<Shader>("entity");
|
||||
auto& entityShader = assets.require<Shader>("entity");
|
||||
setupWorldShader(entityShader, camera, settings, fogFactor);
|
||||
skybox->bind();
|
||||
|
||||
level->entities->render(
|
||||
if (culling) {
|
||||
frustumCulling->update(camera.getProjView());
|
||||
}
|
||||
|
||||
level.entities->render(
|
||||
assets,
|
||||
*modelBatch,
|
||||
culling ? frustumCulling.get() : nullptr,
|
||||
delta,
|
||||
pause
|
||||
);
|
||||
particles->render(camera, delta * !pause);
|
||||
modelBatch->render();
|
||||
particles->render(camera, delta * !pause);
|
||||
|
||||
auto& shader = assets.require<Shader>("main");
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
|
||||
auto shader = assets->get<Shader>("main");
|
||||
setupWorldShader(shader, camera, settings, fogFactor);
|
||||
|
||||
drawChunks(level->chunks.get(), camera, shader);
|
||||
chunks->drawChunks(camera, shader);
|
||||
|
||||
if (hudVisible) {
|
||||
renderLines(camera, linesShader, ctx);
|
||||
}
|
||||
shader.use();
|
||||
chunks->drawSortedMeshes(camera, shader);
|
||||
|
||||
if (!pause) {
|
||||
scripting::on_frontend_render();
|
||||
@@ -238,7 +185,7 @@ void WorldRenderer::renderLevel(
|
||||
|
||||
void WorldRenderer::renderBlockSelection() {
|
||||
const auto& selection = player->selection;
|
||||
auto indices = level->content->getIndices();
|
||||
auto indices = level.content->getIndices();
|
||||
blockid_t id = selection.vox.id;
|
||||
auto& block = indices->blocks.require(id);
|
||||
const glm::ivec3 pos = player->selection.position;
|
||||
@@ -254,7 +201,7 @@ void WorldRenderer::renderBlockSelection() {
|
||||
const glm::vec3 center = glm::vec3(pos) + hitbox.center();
|
||||
const glm::vec3 size = hitbox.size();
|
||||
lineBatch->box(
|
||||
center, size + glm::vec3(0.02), glm::vec4(0.f, 0.f, 0.f, 0.5f)
|
||||
center, size + glm::vec3(0.01), glm::vec4(0.f, 0.f, 0.f, 0.5f)
|
||||
);
|
||||
if (player->debug) {
|
||||
lineBatch->line(
|
||||
@@ -266,87 +213,27 @@ void WorldRenderer::renderBlockSelection() {
|
||||
}
|
||||
|
||||
void WorldRenderer::renderLines(
|
||||
const Camera& camera, Shader* linesShader, const DrawContext& pctx
|
||||
const Camera& camera, Shader& linesShader, const DrawContext& pctx
|
||||
) {
|
||||
linesShader->use();
|
||||
linesShader->uniformMatrix("u_projview", camera.getProjView());
|
||||
linesShader.use();
|
||||
linesShader.uniformMatrix("u_projview", camera.getProjView());
|
||||
if (player->selection.vox.id != BLOCK_VOID) {
|
||||
renderBlockSelection();
|
||||
}
|
||||
if (player->debug && showEntitiesDebug) {
|
||||
auto ctx = pctx.sub(lineBatch.get());
|
||||
bool culling = engine->getSettings().graphics.frustumCulling.get();
|
||||
level->entities->renderDebug(
|
||||
level.entities->renderDebug(
|
||||
*lineBatch, culling ? frustumCulling.get() : nullptr, ctx
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::renderDebugLines(
|
||||
const DrawContext& pctx, const Camera& camera, Shader* linesShader
|
||||
) {
|
||||
DrawContext ctx = pctx.sub(lineBatch.get());
|
||||
const auto& viewport = ctx.getViewport();
|
||||
uint displayWidth = viewport.getWidth();
|
||||
uint displayHeight = viewport.getHeight();
|
||||
|
||||
ctx.setDepthTest(true);
|
||||
|
||||
linesShader->use();
|
||||
|
||||
if (showChunkBorders) {
|
||||
linesShader->uniformMatrix("u_projview", camera.getProjView());
|
||||
glm::vec3 coord = player->fpCamera->position;
|
||||
if (coord.x < 0) coord.x--;
|
||||
if (coord.z < 0) coord.z--;
|
||||
int cx = floordiv(static_cast<int>(coord.x), CHUNK_W);
|
||||
int cz = floordiv(static_cast<int>(coord.z), CHUNK_D);
|
||||
|
||||
drawBorders(
|
||||
cx * CHUNK_W,
|
||||
0,
|
||||
cz * CHUNK_D,
|
||||
(cx + 1) * CHUNK_W,
|
||||
CHUNK_H,
|
||||
(cz + 1) * CHUNK_D
|
||||
);
|
||||
}
|
||||
|
||||
float length = 40.f;
|
||||
glm::vec3 tsl(displayWidth / 2, displayHeight / 2, 0.f);
|
||||
glm::mat4 model(glm::translate(glm::mat4(1.f), tsl));
|
||||
linesShader->uniformMatrix(
|
||||
"u_projview",
|
||||
glm::ortho(
|
||||
0.f,
|
||||
static_cast<float>(displayWidth),
|
||||
0.f,
|
||||
static_cast<float>(displayHeight),
|
||||
-length,
|
||||
length
|
||||
) * model *
|
||||
glm::inverse(camera.rotation)
|
||||
);
|
||||
|
||||
ctx.setDepthTest(false);
|
||||
lineBatch->lineWidth(4.0f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->flush();
|
||||
|
||||
ctx.setDepthTest(true);
|
||||
lineBatch->lineWidth(2.0f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 0.f, 1.f);
|
||||
lineBatch->line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
void WorldRenderer::renderHands(
|
||||
const Camera& camera, const Assets& assets, float delta
|
||||
const Camera& camera, float delta
|
||||
) {
|
||||
auto entityShader = assets.get<Shader>("entity");
|
||||
auto indices = level->content->getIndices();
|
||||
auto& entityShader = assets.require<Shader>("entity");
|
||||
auto indices = level.content->getIndices();
|
||||
|
||||
// get current chosen item
|
||||
const auto& inventory = player->getInventory();
|
||||
@@ -414,7 +301,7 @@ void WorldRenderer::draw(
|
||||
PostProcessing* postProcessing
|
||||
) {
|
||||
timer += delta * !pause;
|
||||
auto world = level->getWorld();
|
||||
auto world = level.getWorld();
|
||||
const Viewport& vp = pctx.getViewport();
|
||||
camera.aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
|
||||
|
||||
@@ -424,10 +311,9 @@ void WorldRenderer::draw(
|
||||
skybox->refresh(pctx, worldInfo.daytime, 1.0f + worldInfo.fog * 2.0f, 4);
|
||||
|
||||
const auto& assets = *engine->getAssets();
|
||||
auto linesShader = assets.get<Shader>("lines");
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
|
||||
// World render scope with diegetic HUD included
|
||||
{
|
||||
/* World render scope with diegetic HUD included */ {
|
||||
DrawContext wctx = pctx.sub();
|
||||
postProcessing->use(wctx);
|
||||
|
||||
@@ -435,25 +321,29 @@ void WorldRenderer::draw(
|
||||
|
||||
// Drawing background sky plane
|
||||
skybox->draw(pctx, camera, assets, worldInfo.daytime, worldInfo.fog);
|
||||
|
||||
// Actually world render with depth buffer on
|
||||
{
|
||||
|
||||
/* Actually world render with depth buffer on */ {
|
||||
DrawContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(true);
|
||||
ctx.setCullFace(true);
|
||||
renderLevel(ctx, camera, settings, delta, pause);
|
||||
renderLevel(ctx, camera, settings, delta, pause, hudVisible);
|
||||
// Debug lines
|
||||
if (hudVisible) {
|
||||
renderLines(camera, linesShader, ctx);
|
||||
if (player->debug) {
|
||||
guides->renderDebugLines(
|
||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
||||
);
|
||||
}
|
||||
if (player->currentCamera == player->fpCamera) {
|
||||
renderHands(camera, assets, delta * !pause);
|
||||
renderHands(camera, delta * !pause);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hudVisible && player->debug) {
|
||||
renderDebugLines(wctx, camera, linesShader);
|
||||
{
|
||||
DrawContext ctx = wctx.sub();
|
||||
texts->render(ctx, camera, settings, hudVisible, true);
|
||||
}
|
||||
renderBlockOverlay(wctx, assets);
|
||||
renderBlockOverlay(wctx);
|
||||
}
|
||||
|
||||
// Rendering fullscreen quad with
|
||||
@@ -464,14 +354,14 @@ void WorldRenderer::draw(
|
||||
postProcessing->render(pctx, screenShader);
|
||||
}
|
||||
|
||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& assets) {
|
||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||
int x = std::floor(player->currentCamera->position.x);
|
||||
int y = std::floor(player->currentCamera->position.y);
|
||||
int z = std::floor(player->currentCamera->position.z);
|
||||
auto block = level->chunks->get(x, y, z);
|
||||
auto block = level.chunks->get(x, y, z);
|
||||
if (block && block->id) {
|
||||
const auto& def =
|
||||
level->content->getIndices()->blocks.require(block->id);
|
||||
level.content->getIndices()->blocks.require(block->id);
|
||||
if (def.overlayTexture.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -487,7 +377,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& as
|
||||
batch3d->begin();
|
||||
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
|
||||
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
auto light = level->chunks->getLight(x, y, z);
|
||||
auto light = level.chunks->getLight(x, y, z);
|
||||
float s = Lightmap::extract(light, 3) / 15.0f;
|
||||
glm::vec4 tint(
|
||||
glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s),
|
||||
@@ -509,34 +399,6 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& as
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::drawBorders(
|
||||
int sx, int sy, int sz, int ex, int ey, int ez
|
||||
) {
|
||||
int ww = ex - sx;
|
||||
int dd = ez - sz;
|
||||
/*corner*/ {
|
||||
lineBatch->line(sx, sy, sz, sx, ey, sz, 0.8f, 0, 0.8f, 1);
|
||||
lineBatch->line(sx, sy, ez, sx, ey, ez, 0.8f, 0, 0.8f, 1);
|
||||
lineBatch->line(ex, sy, sz, ex, ey, sz, 0.8f, 0, 0.8f, 1);
|
||||
lineBatch->line(ex, sy, ez, ex, ey, ez, 0.8f, 0, 0.8f, 1);
|
||||
}
|
||||
for (int i = 2; i < ww; i += 2) {
|
||||
lineBatch->line(sx + i, sy, sz, sx + i, ey, sz, 0, 0, 0.8f, 1);
|
||||
lineBatch->line(sx + i, sy, ez, sx + i, ey, ez, 0, 0, 0.8f, 1);
|
||||
}
|
||||
for (int i = 2; i < dd; i += 2) {
|
||||
lineBatch->line(sx, sy, sz + i, sx, ey, sz + i, 0.8f, 0, 0, 1);
|
||||
lineBatch->line(ex, sy, sz + i, ex, ey, sz + i, 0.8f, 0, 0, 1);
|
||||
}
|
||||
for (int i = sy; i < ey; i += 2) {
|
||||
lineBatch->line(sx, i, sz, sx, i, ez, 0, 0.8f, 0, 1);
|
||||
lineBatch->line(sx, i, ez, ex, i, ez, 0, 0.8f, 0, 1);
|
||||
lineBatch->line(ex, i, ez, ex, i, sz, 0, 0.8f, 0, 1);
|
||||
lineBatch->line(ex, i, sz, sx, i, sz, 0, 0.8f, 0, 1);
|
||||
}
|
||||
lineBatch->flush();
|
||||
}
|
||||
|
||||
void WorldRenderer::clear() {
|
||||
renderer->clear();
|
||||
chunks->clear();
|
||||
}
|
||||
|
||||
@@ -17,76 +17,62 @@ class Batch3D;
|
||||
class LineBatch;
|
||||
class ChunksRenderer;
|
||||
class ParticlesRenderer;
|
||||
class GuidesRenderer;
|
||||
class TextsRenderer;
|
||||
class Shader;
|
||||
class Frustum;
|
||||
class Engine;
|
||||
class Chunks;
|
||||
class LevelFrontend;
|
||||
class Skybox;
|
||||
class PostProcessing;
|
||||
class DrawContext;
|
||||
class ModelBatch;
|
||||
class Assets;
|
||||
class Emitter;
|
||||
struct EngineSettings;
|
||||
|
||||
namespace model {
|
||||
struct Model;
|
||||
}
|
||||
|
||||
class WorldRenderer {
|
||||
Engine* engine;
|
||||
Level* level;
|
||||
const Level& level;
|
||||
Player* player;
|
||||
const Assets& assets;
|
||||
std::unique_ptr<Frustum> frustumCulling;
|
||||
std::unique_ptr<LineBatch> lineBatch;
|
||||
std::unique_ptr<ChunksRenderer> renderer;
|
||||
std::unique_ptr<Skybox> skybox;
|
||||
std::unique_ptr<Batch3D> batch3d;
|
||||
std::unique_ptr<ChunksRenderer> chunks;
|
||||
std::unique_ptr<GuidesRenderer> guides;
|
||||
std::unique_ptr<Skybox> skybox;
|
||||
std::unique_ptr<ModelBatch> modelBatch;
|
||||
|
||||
float timer = 0.0f;
|
||||
|
||||
bool drawChunk(size_t index, const Camera& camera, Shader* shader, bool culling);
|
||||
void drawChunks(Chunks* chunks, const Camera& camera, Shader* shader);
|
||||
|
||||
/// @brief Render block selection lines
|
||||
void renderBlockSelection();
|
||||
|
||||
void renderHands(const Camera& camera, const Assets& assets, float delta);
|
||||
void renderHands(const Camera& camera, float delta);
|
||||
|
||||
/// @brief Render lines (selection and debug)
|
||||
/// @param camera active camera
|
||||
/// @param linesShader shader used
|
||||
void renderLines(
|
||||
const Camera& camera, Shader* linesShader, const DrawContext& pctx
|
||||
const Camera& camera, Shader& linesShader, const DrawContext& pctx
|
||||
);
|
||||
|
||||
/// @brief Render all debug lines (chunks borders, coord system guides)
|
||||
/// @param context graphics context
|
||||
/// @param camera active camera
|
||||
/// @param linesShader shader used
|
||||
void renderDebugLines(
|
||||
const DrawContext& context,
|
||||
const Camera& camera,
|
||||
Shader* linesShader
|
||||
);
|
||||
|
||||
void renderBlockOverlay(const DrawContext& context, const Assets& assets);
|
||||
void renderBlockOverlay(const DrawContext& context);
|
||||
|
||||
void setupWorldShader(
|
||||
Shader* shader,
|
||||
Shader& shader,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
float fogFactor
|
||||
);
|
||||
public:
|
||||
std::unique_ptr<TextsRenderer> texts;
|
||||
std::unique_ptr<ParticlesRenderer> particles;
|
||||
|
||||
static bool showChunkBorders;
|
||||
static bool showEntitiesDebug;
|
||||
|
||||
WorldRenderer(Engine* engine, LevelFrontend* frontend, Player* player);
|
||||
WorldRenderer(Engine* engine, LevelFrontend& frontend, Player* player);
|
||||
~WorldRenderer();
|
||||
|
||||
void draw(
|
||||
@@ -97,7 +83,6 @@ public:
|
||||
float delta,
|
||||
PostProcessing* postProcessing
|
||||
);
|
||||
void drawBorders(int sx, int sy, int sz, int ex, int ey, int ez);
|
||||
|
||||
/// @brief Render level without diegetic interface
|
||||
/// @param context graphics context
|
||||
@@ -108,7 +93,8 @@ public:
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
float delta,
|
||||
bool pause
|
||||
bool pause,
|
||||
bool hudVisible
|
||||
);
|
||||
|
||||
void clear();
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
/// @brief Chunk mesh vertex attributes
|
||||
inline const VertexAttribute CHUNK_VATTRS[]{ {3}, {2}, {1}, {0} };
|
||||
/// @brief Chunk mesh vertex size divided by sizeof(float)
|
||||
inline constexpr int CHUNK_VERTEX_SIZE = 6;
|
||||
|
||||
class Mesh;
|
||||
|
||||
struct SortingMeshEntry {
|
||||
glm::vec3 position;
|
||||
util::Buffer<float> vertexData;
|
||||
long long distance;
|
||||
|
||||
inline bool operator<(const SortingMeshEntry& o) const noexcept {
|
||||
return distance > o.distance;
|
||||
}
|
||||
};
|
||||
|
||||
struct SortingMeshData {
|
||||
std::vector<SortingMeshEntry> entries;
|
||||
};
|
||||
|
||||
struct ChunkMeshData {
|
||||
MeshData mesh;
|
||||
SortingMeshData sortingMesh;
|
||||
};
|
||||
|
||||
struct ChunkMesh {
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
SortingMeshData sortingMeshData;
|
||||
std::unique_ptr<Mesh> sortedMesh = nullptr;
|
||||
};
|
||||
Reference in New Issue
Block a user