Clip posteffect color to respective nodes
This commit is contained in:
parent
f2b1cc3e61
commit
e3ff58a1ab
@ -169,6 +169,7 @@ void main(void)
|
||||
// Generate waves with Perlin-type noise.
|
||||
// The constants are calibrated such that they roughly
|
||||
// correspond to the old sine waves.
|
||||
// Note: The same thing is implemented in clientmap.cpp. Keep them consistent!
|
||||
vec3 wavePos = (mWorld * pos).xyz + cameraOffset;
|
||||
// The waves are slightly compressed along the z-axis to get
|
||||
// wave-fronts along the x-axis.
|
||||
|
@ -5,6 +5,9 @@ uniform lowp vec4 fogColor;
|
||||
uniform float fogDistance;
|
||||
uniform float fogShadingParameter;
|
||||
|
||||
// Only used for the wieldhand. 0 otherwise.
|
||||
uniform vec4 wield_posteffect_color;
|
||||
|
||||
// The cameraOffset is the current center of the visible world.
|
||||
uniform highp vec3 cameraOffset;
|
||||
uniform float animationTimer;
|
||||
@ -458,5 +461,12 @@ void main(void)
|
||||
col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity);
|
||||
col = vec4(col.rgb, base.a);
|
||||
|
||||
// Apply posteffect color for the wieldhand, by blending it above this
|
||||
// fragment.
|
||||
// The alpha channel is not blended. The posteffect geometry behind the
|
||||
// wieldhand already makes the image less transparent.
|
||||
// wield_posteffect_color.rgb is premultiplied.
|
||||
col.rgb = col.rgb * (1.0 - wield_posteffect_color.a) + wield_posteffect_color.rgb;
|
||||
|
||||
gl_FragData[0] = col;
|
||||
}
|
||||
|
@ -600,7 +600,8 @@ void Camera::wield(const ItemStack &item)
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::drawWieldedTool(irr::core::matrix4* translation)
|
||||
void Camera::drawWieldedTool(irr::core::matrix4* translation,
|
||||
const video::SColorf &post_color)
|
||||
{
|
||||
// Clear Z buffer so that the wielded tool stays in front of world geometry
|
||||
m_wieldmgr->getVideoDriver()->clearBuffers(video::ECBF_DEPTH);
|
||||
@ -624,7 +625,9 @@ void Camera::drawWieldedTool(irr::core::matrix4* translation)
|
||||
cam->updateAbsolutePosition();
|
||||
cam->setTarget(focusPoint);
|
||||
}
|
||||
m_client->setWieldPostEffectColor(post_color);
|
||||
m_wieldmgr->drawAll();
|
||||
m_client->setWieldPostEffectColor(video::SColorf(0.0f,0.0f,0.0f,0.0f));
|
||||
}
|
||||
|
||||
void Camera::drawNametags()
|
||||
|
@ -160,7 +160,8 @@ public:
|
||||
// Draw the wielded tool.
|
||||
// This has to happen *after* the main scene is drawn.
|
||||
// Warning: This clears the Z buffer.
|
||||
void drawWieldedTool(irr::core::matrix4* translation=NULL);
|
||||
void drawWieldedTool(irr::core::matrix4* translation = nullptr,
|
||||
const video::SColorf &post_color = video::SColorf(0.0f,0.0f,0.0f,0.0f));
|
||||
|
||||
// Toggle the current camera mode
|
||||
void toggleCameraMode() {
|
||||
|
@ -430,6 +430,16 @@ public:
|
||||
return m_mesh_grid;
|
||||
}
|
||||
|
||||
video::SColorf getWieldPostEffectColor() const
|
||||
{
|
||||
return m_wield_posteffect_color;
|
||||
}
|
||||
|
||||
void setWieldPostEffectColor(video::SColorf color)
|
||||
{
|
||||
m_wield_posteffect_color = color;
|
||||
}
|
||||
|
||||
bool inhibit_inventory_revert = false;
|
||||
|
||||
private:
|
||||
@ -592,4 +602,6 @@ private:
|
||||
|
||||
// The number of blocks the client will combine for mesh generation.
|
||||
MeshGrid m_mesh_grid;
|
||||
|
||||
video::SColorf m_wield_posteffect_color{0};
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "clientmap.h"
|
||||
#include "client.h"
|
||||
#include "client/mesh.h"
|
||||
#include "content_mapblock.h"
|
||||
#include "mapblock_mesh.h"
|
||||
#include <IMaterialRenderer.h>
|
||||
#include <IVideoDriver.h>
|
||||
@ -16,9 +17,9 @@
|
||||
#include "settings.h"
|
||||
#include "camera.h" // CameraModes
|
||||
#include "util/basic_macros.h"
|
||||
#include "util/convex_polygon.h"
|
||||
#include "util/tracy_wrapper.h"
|
||||
#include "client/renderingengine.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace {
|
||||
@ -1206,39 +1207,312 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ClientMap::renderPostFx(CameraMode cam_mode)
|
||||
std::vector<std::pair<ConvexPolygon, video::SColor>>
|
||||
ClientMap::getPostFxPolygons()
|
||||
{
|
||||
// Sadly ISceneManager has no "post effects" render pass, in that case we
|
||||
// could just register for that and handle it in renderMap().
|
||||
using mat4 = core::matrix4;
|
||||
using v4f = std::array<f32, 4>;
|
||||
|
||||
MapNode n = getNode(floatToInt(m_camera_position, BS));
|
||||
auto vector_cross_4d = [](const v4f &v1, const v4f &v2, const v4f &v3) -> v4f
|
||||
{
|
||||
using varr3 = std::array<f32, 3>;
|
||||
using mat33 = std::array<varr3, 3>;
|
||||
|
||||
const ContentFeatures& features = m_nodedef->get(n);
|
||||
video::SColor post_color = features.post_effect_color;
|
||||
|
||||
if (features.post_effect_color_shaded) {
|
||||
auto apply_light = [] (u32 color, u32 light) {
|
||||
return core::clamp(core::round32(color * light / 255.0f), 0, 255);
|
||||
auto det_3d = [](const mat33 &m) {
|
||||
return m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
|
||||
- m[0][1] * (m[1][0] * m[2][2] - m[2][0] * m[1][2])
|
||||
+ m[0][2] * (m[1][0] * m[2][1] - m[2][0] * m[1][1]);
|
||||
};
|
||||
post_color.setRed(apply_light(post_color.getRed(), m_camera_light_color.getRed()));
|
||||
post_color.setGreen(apply_light(post_color.getGreen(), m_camera_light_color.getGreen()));
|
||||
post_color.setBlue(apply_light(post_color.getBlue(), m_camera_light_color.getBlue()));
|
||||
|
||||
auto index33 = [&](int i1, int i2, int i3) -> mat33 {
|
||||
return {
|
||||
varr3{v1[i1], v1[i2], v1[i3]},
|
||||
varr3{v2[i1], v2[i2], v2[i3]},
|
||||
varr3{v3[i1], v3[i2], v3[i3]}
|
||||
};
|
||||
};
|
||||
|
||||
return v4f{
|
||||
+det_3d(index33( 1, 2, 3)),
|
||||
-det_3d(index33(0, 2, 3)),
|
||||
+det_3d(index33(0, 1, 3)),
|
||||
-det_3d(index33(0, 1, 2 )),
|
||||
};
|
||||
};
|
||||
|
||||
auto mat4_mult_v4f = [](const mat4 &m, const v4f &v) {
|
||||
v4f ret;
|
||||
m.transformVec4(ret.data(), v.data());
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto get_post_color = [&](const ContentFeatures &features) {
|
||||
video::SColor post_color = features.post_effect_color;
|
||||
|
||||
if (features.post_effect_color_shaded) {
|
||||
auto apply_light = [](u32 color, u32 light) {
|
||||
return core::clamp(core::round32(color * light / 255.0f), 0, 255);
|
||||
};
|
||||
post_color.setRed(apply_light(post_color.getRed(), m_camera_light_color.getRed()));
|
||||
post_color.setGreen(apply_light(post_color.getGreen(), m_camera_light_color.getGreen()));
|
||||
post_color.setBlue(apply_light(post_color.getBlue(), m_camera_light_color.getBlue()));
|
||||
}
|
||||
|
||||
// Solid nodes are full of darkness.
|
||||
if (features.solidness == 2 && !m_control.allow_noclip)
|
||||
post_color = video::SColor(255, 0, 0, 0);
|
||||
|
||||
return post_color;
|
||||
};
|
||||
|
||||
const std::optional<LiquidWaveParams> liquid_wave_params =
|
||||
g_settings->getBool("enable_waving_water") ?
|
||||
LiquidWaveParams{
|
||||
g_settings->getFloat("water_wave_height"),
|
||||
g_settings->getFloat("water_wave_length"),
|
||||
g_settings->getFloat("water_wave_speed"),
|
||||
(float)(m_client->getEnv().getFrameTime() % 1000000) / 100000.f,
|
||||
}
|
||||
: std::optional<LiquidWaveParams>(std::nullopt);
|
||||
|
||||
const bool smooth_lighting = g_settings->getBool("smooth_lighting");
|
||||
|
||||
// transposed inversed view-(quasi)projection matrix
|
||||
// This matrix moves the near plane from camera-offset-local world-space to
|
||||
// clip-space.
|
||||
// (It's the transposed inversed because we want to transform planes.)
|
||||
// (Only the near plane is transformed correctly (we don't care about the
|
||||
// rest of the space), therefore "(quasi)projection".)
|
||||
// (We also don't care for positive scalar multiples, so the adjugate would
|
||||
// work too.)
|
||||
const mat4 vp_mat_ti = [&]() {
|
||||
auto camera_node = m_client->getCamera()->getCameraNode();
|
||||
|
||||
const mat4 view_mat = camera_node->getViewMatrix();
|
||||
|
||||
// The projection matrix moves the near-left-up corner of the frustum
|
||||
// to (-1,1,-1), and near-right-down to (1,-1,-1). But we need (0,0,0)
|
||||
// and (1,1,0). coord_transf moves the near plane accordingly.
|
||||
// (We only care about the near plane, so this scale + translate is ok.)
|
||||
const mat4 coord_transf = mat4(
|
||||
0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, -0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, 1.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
).getTransposed();
|
||||
const mat4 proj_mat = coord_transf * camera_node->getProjectionMatrix();
|
||||
|
||||
return mat4(proj_mat * view_mat, mat4::EM4CONST_INVERSE_TRANSPOSED);
|
||||
}();
|
||||
|
||||
std::vector<std::pair<ConvexPolygon, video::SColor>> polygons;
|
||||
|
||||
// Go through all 8 nodes that the near plane possibly instersects with
|
||||
|
||||
// the one in the (-1,-1,-1) corner
|
||||
const v3s16 minus_corner(
|
||||
std::floor(m_camera_position.X/BS),
|
||||
std::floor(m_camera_position.Y/BS),
|
||||
std::floor(m_camera_position.Z/BS)
|
||||
);
|
||||
|
||||
for (const v3s16 &corner_dir : {
|
||||
v3s16(0, 0, 0),
|
||||
v3s16(1, 0, 0),
|
||||
v3s16(0, 1, 0),
|
||||
v3s16(1, 1, 0),
|
||||
v3s16(0, 0, 1),
|
||||
v3s16(1, 0, 1),
|
||||
v3s16(0, 1, 1),
|
||||
v3s16(1, 1, 1),
|
||||
}) {
|
||||
const v3s16 npos = minus_corner + corner_dir;
|
||||
|
||||
const MapNode node = getNode(npos);
|
||||
|
||||
const ContentFeatures &features = m_nodedef->get(node);
|
||||
const video::SColor post_color = get_post_color(features);
|
||||
|
||||
if (post_color.getAlpha() == 0)
|
||||
continue;
|
||||
|
||||
const ConvexPolygon screen_polygon{{
|
||||
{0.0f, 0.0f}, // upper-left
|
||||
{1.0f, 0.0f}, // upper-right
|
||||
{1.0f, 1.0f}, // lower-right
|
||||
{0.0f, 1.0f}, // lower-left
|
||||
}};
|
||||
|
||||
// the (inverse and inverse transposed) world matrix for the node
|
||||
// Transforms planes from node-local space to camera-offset-local world
|
||||
// BS space.
|
||||
mat4 world_mat_i = mat4::EM4CONST_IDENTITY;
|
||||
world_mat_i.setTranslation(intToFloat(-npos + m_camera_offset, 1.0f));
|
||||
world_mat_i.setScale(1.0f/BS);
|
||||
const mat4 world_mat_ti(world_mat_i, mat4::EM4CONST_TRANSPOSED);
|
||||
|
||||
const mat4 wvp_mat_ti = vp_mat_ti * world_mat_ti;
|
||||
|
||||
auto clip_polygon_by_objsp_plane = [&](ConvexPolygon &polyg,
|
||||
const v4f &clip_plane) -> bool {
|
||||
v4f clip_plane_clipspace = mat4_mult_v4f(wvp_mat_ti, clip_plane);
|
||||
|
||||
// The clip plane (and all points p in the clip plane) are at z=0.
|
||||
// So the c component in the following inequation drops away:
|
||||
// a*p.X + b*p.Y + c*p.Z + d >= 0
|
||||
v3f clip_line(clip_plane_clipspace[0], clip_plane_clipspace[1], clip_plane_clipspace[3]);
|
||||
|
||||
polyg = polyg.clip(clip_line);
|
||||
return polyg.vertices.size() >= 3;
|
||||
};
|
||||
|
||||
auto clip_polygon_by_objsp_planes = [&](ConvexPolygon &polyg,
|
||||
const std::vector<v4f> &clip_planes) -> bool {
|
||||
for (const v4f &clip_plane : clip_planes)
|
||||
if (!clip_polygon_by_objsp_plane(polyg, clip_plane))
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
if (features.drawtype == NDT_NORMAL) {
|
||||
// draw full cube
|
||||
ConvexPolygon polygon = screen_polygon;
|
||||
if (!clip_polygon_by_objsp_planes(polygon, {
|
||||
{ 2.0f, 0.0f, 0.0f, 1.0f},
|
||||
{-2.0f, 0.0f, 0.0f, 1.0f},
|
||||
{0.0f, 2.0f, 0.0f, 1.0f},
|
||||
{0.0f, -2.0f, 0.0f, 1.0f},
|
||||
{0.0f, 0.0f, 2.0f, 1.0f},
|
||||
{0.0f, 0.0f, -2.0f, 1.0f},
|
||||
}))
|
||||
continue;
|
||||
polygons.emplace_back(std::move(polygon), post_color);
|
||||
|
||||
} else if (features.drawtype == NDT_LIQUID
|
||||
|| features.drawtype == NDT_FLOWINGLIQUID) {
|
||||
ConvexPolygon polygon = screen_polygon;
|
||||
// clip by all but top
|
||||
if (!clip_polygon_by_objsp_planes(polygon, {
|
||||
{ 2.0f, 0.0f, 0.0f, 1.0f},
|
||||
{-2.0f, 0.0f, 0.0f, 1.0f},
|
||||
{0.0f, 2.0f, 0.0f, 1.0f},
|
||||
{0.0f, 0.0f, 2.0f, 1.0f},
|
||||
{0.0f, 0.0f, -2.0f, 1.0f},
|
||||
}))
|
||||
continue;
|
||||
|
||||
// The top side of the water has two triangles, separated by the
|
||||
// quad diagonal, so we need two convex polygons for the two convex
|
||||
// polytopes that have these triangles as top
|
||||
|
||||
// if there is no top face, the full cube is filled with liquid
|
||||
auto top_corners_heights = getLiquidTopFaceHeights(npos, liquid_wave_params)
|
||||
.value_or(std::array{0.5f, 0.5f, 0.5f, 0.5f});
|
||||
|
||||
std::array<v4f, 4> vertices{
|
||||
v4f{-0.5f, top_corners_heights[0], -0.5f, 1.0f},
|
||||
v4f{ 0.5f, top_corners_heights[1], -0.5f, 1.0f},
|
||||
v4f{-0.5f, top_corners_heights[2], 0.5f, 1.0f},
|
||||
v4f{ 0.5f, top_corners_heights[3], 0.5f, 1.0f},
|
||||
};
|
||||
|
||||
// For smooth lit solid nodes, the quad diagonal depends on lighting
|
||||
QuadDiagonal quad_diag = QuadDiagonal::Diag02;
|
||||
if (features.drawtype == NDT_LIQUID && smooth_lighting) {
|
||||
// same as light_dirs[light_indices[0][0..4]] in content_mapblock.cpp
|
||||
constexpr v3s16 corners[4] = {
|
||||
v3s16(-1, 1, 1),
|
||||
v3s16( 1, 1, 1),
|
||||
v3s16( 1, 1, -1),
|
||||
v3s16(-1, 1, -1),
|
||||
};
|
||||
LightPair lights[4];
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
lights[k] = LightPair(getSmoothLightSolid(npos, v3s16(0,1,0),
|
||||
corners[k], m_nodedef,
|
||||
[this](v3s16 p) { return getNode(p); }
|
||||
));
|
||||
}
|
||||
quad_diag = LightPair::quadDiagonalForFace(lights);
|
||||
}
|
||||
v4f triangle_split_plane;
|
||||
v4f triangle_split_plane_neg;
|
||||
std::array<int, 6> indices;
|
||||
if (quad_diag == QuadDiagonal::Diag02) {
|
||||
triangle_split_plane = {-1.0f, 0.0f, -1.0f, 0.0f};
|
||||
triangle_split_plane_neg = { 1.0f, 0.0f, 1.0f, 0.0f};
|
||||
indices = {0, 1, 2, 1, 3, 2};
|
||||
} else {
|
||||
triangle_split_plane = { 1.0f, 0.0f, -1.0f, 0.0f};
|
||||
triangle_split_plane_neg = {-1.0f, 0.0f, 1.0f, 0.0f};
|
||||
indices = {0, 1, 3, 0, 3, 2};
|
||||
}
|
||||
|
||||
ConvexPolygon polygon1 = polygon;
|
||||
ConvexPolygon &polygon2 = polygon;
|
||||
|
||||
if (clip_polygon_by_objsp_plane(polygon1, triangle_split_plane)
|
||||
&& clip_polygon_by_objsp_plane(polygon1,
|
||||
vector_cross_4d(vertices[indices[0]], vertices[indices[1]],
|
||||
vertices[indices[2]]))) {
|
||||
polygons.emplace_back(std::move(polygon1), post_color);
|
||||
}
|
||||
|
||||
if (clip_polygon_by_objsp_plane(polygon2, triangle_split_plane_neg)
|
||||
&& clip_polygon_by_objsp_plane(polygon2,
|
||||
vector_cross_4d(vertices[indices[3]], vertices[indices[4]],
|
||||
vertices[indices[5]]))) {
|
||||
polygons.emplace_back(std::move(polygon2), post_color);
|
||||
}
|
||||
|
||||
} else {
|
||||
// for other drawtypes: draw color fullscreen if camera in node
|
||||
if (floatToInt(m_camera_position, BS) != npos)
|
||||
continue;
|
||||
polygons.emplace_back(screen_polygon, post_color);
|
||||
}
|
||||
}
|
||||
|
||||
// If the camera is in a solid node, make everything black.
|
||||
// (first person mode only)
|
||||
if (features.solidness == 2 && cam_mode == CAMERA_MODE_FIRST &&
|
||||
!m_control.allow_noclip) {
|
||||
post_color = video::SColor(255, 0, 0, 0);
|
||||
return polygons;
|
||||
}
|
||||
|
||||
video::SColorf ClientMap::renderPostFx()
|
||||
{
|
||||
auto polygons = getPostFxPolygons();
|
||||
|
||||
video::IVideoDriver *driver = SceneManager->getVideoDriver();
|
||||
|
||||
for (const auto &[polygon, post_color] : polygons) {
|
||||
polygon.draw(driver, post_color);
|
||||
}
|
||||
|
||||
if (post_color.getAlpha() != 0) {
|
||||
// Draw a full-screen rectangle
|
||||
video::IVideoDriver* driver = SceneManager->getVideoDriver();
|
||||
v2u32 ss = driver->getScreenSize();
|
||||
core::rect<s32> rect(0,0, ss.X, ss.Y);
|
||||
driver->draw2DRectangle(post_color, rect);
|
||||
// Color for wield item
|
||||
// We take the weighted (by area and alpha) sum of all polygons.
|
||||
// The result is alpha-premultiplied.
|
||||
video::SColorf color_sum(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
for (const auto &[polygon, post_color] : polygons) {
|
||||
video::SColorf color = post_color;
|
||||
|
||||
// (screen size and aspect ratio don't matter for relative screen area)
|
||||
f32 area = polygon.area();
|
||||
color.a *= area;
|
||||
|
||||
color_sum.r += color.a * color.r;
|
||||
color_sum.g += color.a * color.g;
|
||||
color_sum.b += color.a * color.b;
|
||||
color_sum.a = color_sum.a + color.a;
|
||||
}
|
||||
|
||||
if (color_sum.a > 1.0f) {
|
||||
f32 reci_a = 1.0f / color_sum.a;
|
||||
color_sum.a *= reci_a;
|
||||
color_sum.r *= reci_a;
|
||||
color_sum.g *= reci_a;
|
||||
color_sum.b *= reci_a;
|
||||
}
|
||||
|
||||
return color_sum;
|
||||
}
|
||||
|
||||
void ClientMap::PrintInfo(std::ostream &out)
|
||||
@ -1485,6 +1759,124 @@ void ClientMap::updateTransparentMeshBuffers()
|
||||
m_needs_update_transparent_meshes = false;
|
||||
}
|
||||
|
||||
std::optional<std::array<f32, 4>>
|
||||
ClientMap::getLiquidTopFaceHeights(v3s16 pos,
|
||||
const std::optional<LiquidWaveParams> &wave_params)
|
||||
{
|
||||
std::optional<std::array<f32, 4>> heights_opt = std::nullopt;
|
||||
|
||||
const content_t node_c = getNode(pos).getContent();
|
||||
const ContentFeatures &node_f = m_nodedef->get(node_c);
|
||||
const content_t node_above_c = getNode(pos + v3s16(0, 1, 0)).getContent();
|
||||
|
||||
if (node_f.drawtype == NDT_LIQUID) {
|
||||
if (MapblockMeshGenerator::isSolidFaceDrawn(node_c, node_f, node_above_c,
|
||||
m_nodedef))
|
||||
heights_opt = std::array<f32, 4>{0.5f, 0.5f, 0.5f, 0.5f};
|
||||
|
||||
} else if (node_f.drawtype == NDT_FLOWINGLIQUID) {
|
||||
using LiquidNeighborData = MapblockMeshGenerator::LiquidData::NeighborData;
|
||||
|
||||
content_t c_flowing = node_f.liquid_alternative_flowing_id;
|
||||
content_t c_source = node_f.liquid_alternative_source_id;
|
||||
u8 liquid_range = node_f.liquid_range;
|
||||
auto get_node_rel = [&](v3s16 relpos) -> MapNode {
|
||||
return getNode(pos + relpos);
|
||||
};
|
||||
|
||||
std::array<std::array<LiquidNeighborData, 3>, 3> neighbors =
|
||||
MapblockMeshGenerator::getLiquidNeighborsData(c_source, c_flowing,
|
||||
liquid_range, get_node_rel);
|
||||
|
||||
if (!neighbors[1][1].top_is_same_liquid) {
|
||||
std::array<std::array<f32, 2>, 2> corners =
|
||||
MapblockMeshGenerator::calculateLiquidCornerLevels(c_source,
|
||||
c_flowing, neighbors);
|
||||
|
||||
heights_opt = {
|
||||
corners[0][0], corners[0][1],
|
||||
corners[1][0], corners[1][1]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!heights_opt || !wave_params || node_f.waving != 3)
|
||||
return heights_opt;
|
||||
|
||||
// do the wave
|
||||
// Note: The same thing is implemented in the nodes vertex shader. Keep
|
||||
// them consistent!
|
||||
auto wave_func = [&wave_params](v3f wavePos) {
|
||||
using v4f = std::array<f32, 4>;
|
||||
|
||||
// Why does C++ still not have a normal mod function?!
|
||||
auto mod = [](f32 a, f32 b) {
|
||||
return a - b * std::floor(a/b);
|
||||
};
|
||||
|
||||
//
|
||||
// Simple, fast noise function.
|
||||
// See: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83
|
||||
//
|
||||
auto perm = [&mod](v4f v) -> v4f {
|
||||
auto f = [&](f32 x) { return mod(((x * 34.0f) + 1.0f) * x, 289.0f); };
|
||||
return v4f{f(v[0]), f(v[1]), f(v[2]), f(v[3])};
|
||||
};
|
||||
|
||||
auto snoise = [&perm, &mod](v3f p) -> f32 {
|
||||
auto v4add = [](v4f v1, v4f v2) {
|
||||
return v4f{v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2], v1[3] + v2[3]};
|
||||
};
|
||||
auto v4adds = [](v4f v, f32 s) {
|
||||
return v4f{v[0] + s, v[1] + s, v[2] + s, v[3] + s};
|
||||
};
|
||||
auto v4scale = [](v4f v, f32 s) {
|
||||
return v4f{v[0] * s, v[1] * s, v[2] * s, v[3] * s};
|
||||
};
|
||||
auto v3schur = [](v3f v1, v3f v2) {
|
||||
return v3f{v1.X * v2.X, v1.Y * v2.Y, v1.Z * v2.Z};
|
||||
};
|
||||
|
||||
v3f a = v3f{std::floor(p.X), std::floor(p.Y), std::floor(p.Z)};
|
||||
v3f d = p - a;
|
||||
d = v3schur(v3schur(d, d), v3f(3.0f) - 2.0f * d);
|
||||
|
||||
v4f b = v4add(v4f{a.X, a.X, a.Y, a.Y}, v4f{0.0f, 1.0f, 0.0f, 1.0f});
|
||||
v4f k1 = perm(v4f{b[0], b[1], b[0], b[1]});
|
||||
v4f k2 = perm(v4add(v4f{k1[0], k1[1], k1[0], k1[1]}, v4f{b[2], b[2], b[3], b[3]}));
|
||||
|
||||
v4f c = v4adds(k2, a.Z);
|
||||
v4f k3 = perm(c);
|
||||
v4f k4 = perm(v4adds(c, 1.0f));
|
||||
|
||||
auto f1 = [&](f32 x) { return mod(x * (1.0f / 41.0f), 1.0f); };
|
||||
v4f o1 = {f1(k3[0]), f1(k3[1]), f1(k3[2]), f1(k3[3])};
|
||||
v4f o2 = {f1(k4[0]), f1(k4[1]), f1(k4[2]), f1(k4[3])};
|
||||
|
||||
v4f o3 = v4add(v4scale(o2, d.Z), v4scale(o1, 1.0f - d.Z));
|
||||
v2f o4 = v2f{o3[1], o3[3]} * d.X + v2f{o3[0], o3[2]} * (1.0f - d.X);
|
||||
|
||||
return o4.Y * d.Y + o4.X * (1.0f - d.Y);
|
||||
};
|
||||
|
||||
// The waves are slightly compressed along the z-axis to get
|
||||
// wave-fronts along the x-axis.
|
||||
wavePos.X /= wave_params->length * 3.0f;
|
||||
wavePos.Z /= wave_params->length * 2.0f;
|
||||
wavePos.Z += wave_params->animation_timer * wave_params->speed * 10.0f;
|
||||
return (snoise(wavePos) - 1.0f) * wave_params->height * 5.0f;
|
||||
};
|
||||
|
||||
const v3f pos_bs = intToFloat(pos, BS);
|
||||
auto &heights = *heights_opt;
|
||||
heights[0] += wave_func(pos_bs + BS * v3f(-0.5f, heights[0], -0.5f)) * (1.0f/BS);
|
||||
heights[1] += wave_func(pos_bs + BS * v3f( 0.5f, heights[1], -0.5f)) * (1.0f/BS);
|
||||
heights[2] += wave_func(pos_bs + BS * v3f(-0.5f, heights[2], 0.5f)) * (1.0f/BS);
|
||||
heights[3] += wave_func(pos_bs + BS * v3f( 0.5f, heights[3], 0.5f)) * (1.0f/BS);
|
||||
|
||||
return heights_opt;
|
||||
}
|
||||
|
||||
video::SMaterial &DrawDescriptor::getMaterial()
|
||||
{
|
||||
return (m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer)->getMaterial();
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
struct ConvexPolygon;
|
||||
|
||||
struct MapDrawControl
|
||||
{
|
||||
// Wanted drawing range
|
||||
@ -22,6 +24,13 @@ struct MapDrawControl
|
||||
bool show_wireframe = false;
|
||||
};
|
||||
|
||||
struct LiquidWaveParams {
|
||||
f32 height;
|
||||
f32 length;
|
||||
f32 speed;
|
||||
f32 animation_timer;
|
||||
};
|
||||
|
||||
class Client;
|
||||
class ITextureSource;
|
||||
class PartialMeshBuffer;
|
||||
@ -93,7 +102,8 @@ public:
|
||||
int getBackgroundBrightness(float max_d, u32 daylight_factor,
|
||||
int oldvalue, bool *sunlight_seen_result);
|
||||
|
||||
void renderPostFx(CameraMode cam_mode);
|
||||
// Returns the post effect color for the wield hand
|
||||
video::SColorf renderPostFx();
|
||||
|
||||
// For debug printing
|
||||
void PrintInfo(std::ostream &out) override;
|
||||
@ -102,6 +112,24 @@ public:
|
||||
f32 getWantedRange() const { return m_control.wanted_range; }
|
||||
f32 getCameraFov() const { return m_camera_fov; }
|
||||
|
||||
/**
|
||||
* Finds the heights of the corners of the top face of a liquid node at the
|
||||
* given pos.
|
||||
*
|
||||
* Note: Liquid top faces are not quads, but two triangles, split by a
|
||||
* diagonal.
|
||||
*
|
||||
* @param pos The position of the node.
|
||||
* @param wave_params If given, the corner heights are waved like in the node
|
||||
* shader.
|
||||
* @return The heights of the corners in local node space coordinates, in the
|
||||
* following order: {-x-z, +x-z, -x+z, +x+z}
|
||||
* Or nullopt if there is no lquid top face.
|
||||
*/
|
||||
std::optional<std::array<f32, 4>>
|
||||
getLiquidTopFaceHeights(v3s16 pos,
|
||||
const std::optional<LiquidWaveParams> &wave_params);
|
||||
|
||||
void onSettingChanged(std::string_view name, bool all);
|
||||
|
||||
protected:
|
||||
@ -109,12 +137,16 @@ protected:
|
||||
virtual ~ClientMap();
|
||||
|
||||
void reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks) override;
|
||||
|
||||
private:
|
||||
bool isMeshOccluded(MapBlock *mesh_block, u16 mesh_size, v3s16 cam_pos_nodes);
|
||||
|
||||
// update the vertex order in transparent mesh buffers
|
||||
void updateTransparentMeshBuffers();
|
||||
|
||||
// helper for renderPostFx
|
||||
std::vector<std::pair<ConvexPolygon, video::SColor>> getPostFxPolygons();
|
||||
|
||||
// Orders blocks by distance to the camera
|
||||
class MapBlockComparer
|
||||
{
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "client.h"
|
||||
#include "noise.h"
|
||||
|
||||
using LiquidData = MapblockMeshGenerator::LiquidData;
|
||||
|
||||
// Distance of light extrapolation (for oversized nodes)
|
||||
// After this distance, it gives up and considers light level constant
|
||||
#define SMOOTH_LIGHTING_OVERSIZE 1.0
|
||||
@ -57,6 +59,20 @@ static constexpr u16 quad_indices_02[] = {0, 1, 2, 2, 3, 0};
|
||||
static constexpr u16 quad_indices_13[] = {0, 1, 3, 3, 1, 2};
|
||||
static const auto &quad_indices = quad_indices_02;
|
||||
|
||||
int LightPair::lightDiff(LightPair a, LightPair b)
|
||||
{
|
||||
return std::abs(a.lightDay - b.lightDay) + std::abs(a.lightNight - b.lightNight);
|
||||
}
|
||||
|
||||
QuadDiagonal LightPair::quadDiagonalForFace(LightPair final_lights[4])
|
||||
{
|
||||
if (lightDiff(final_lights[1], final_lights[3])
|
||||
< lightDiff(final_lights[0], final_lights[2]))
|
||||
return QuadDiagonal::Diag13;
|
||||
else
|
||||
return QuadDiagonal::Diag02;
|
||||
}
|
||||
|
||||
const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
|
||||
|
||||
MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output):
|
||||
@ -198,11 +214,6 @@ static std::array<video::S3DVertex, 24> setupCuboidVertices(const aabb3f &box, c
|
||||
return vertices;
|
||||
}
|
||||
|
||||
enum class QuadDiagonal {
|
||||
Diag02,
|
||||
Diag13,
|
||||
};
|
||||
|
||||
// Create a cuboid with custom lighting.
|
||||
// tiles - the tiles (materials) to use (for all 6 faces)
|
||||
// tilecount - number of entries in tiles, 1<=tilecount<=6
|
||||
@ -318,11 +329,6 @@ void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *
|
||||
coords[i] = txc[i];
|
||||
}
|
||||
|
||||
static inline int lightDiff(LightPair a, LightPair b)
|
||||
{
|
||||
return abs(a.lightDay - b.lightDay) + abs(a.lightNight - b.lightNight);
|
||||
}
|
||||
|
||||
void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
|
||||
TileSpec *tiles, int tile_count, u8 mask)
|
||||
{
|
||||
@ -371,9 +377,7 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
|
||||
if (!cur_node.f->light_source)
|
||||
applyFacesShading(vertex.Color, vertex.Normal);
|
||||
}
|
||||
if (lightDiff(final_lights[1], final_lights[3]) < lightDiff(final_lights[0], final_lights[2]))
|
||||
return QuadDiagonal::Diag13;
|
||||
return QuadDiagonal::Diag02;
|
||||
return LightPair::quadDiagonalForFace(final_lights);
|
||||
});
|
||||
} else {
|
||||
drawCuboid(box, tiles, tile_count, txc, mask, [&] (int face, video::S3DVertex vertices[4]) {
|
||||
@ -389,6 +393,30 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
|
||||
}
|
||||
}
|
||||
|
||||
bool MapblockMeshGenerator::isSolidFaceDrawn(content_t n1, const ContentFeatures &f1,
|
||||
content_t n2, const NodeDefManager *nodedef, bool *backface_culling_out)
|
||||
{
|
||||
bool backface_culling = f1.drawtype == NDT_NORMAL;
|
||||
if (n2 == n1)
|
||||
return false;
|
||||
if (n2 == CONTENT_IGNORE)
|
||||
return false;
|
||||
if (n2 != CONTENT_AIR) {
|
||||
const ContentFeatures &f2 = nodedef->get(n2);
|
||||
if (f2.solidness == 2)
|
||||
return false;
|
||||
if (f1.drawtype == NDT_LIQUID) {
|
||||
if (f1.sameLiquidRender(f2))
|
||||
return false;
|
||||
backface_culling = f2.solidness || f2.visual_solidness;
|
||||
}
|
||||
}
|
||||
|
||||
if (backface_culling_out)
|
||||
*backface_culling_out = backface_culling;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapblockMeshGenerator::drawSolidNode()
|
||||
{
|
||||
u8 faces = 0; // k-th bit will be set if k-th face is to be drawn.
|
||||
@ -407,21 +435,9 @@ void MapblockMeshGenerator::drawSolidNode()
|
||||
v3s16 p2 = blockpos_nodes + cur_node.p + tile_dirs[face];
|
||||
MapNode neighbor = data->m_vmanip.getNodeNoEx(p2);
|
||||
content_t n2 = neighbor.getContent();
|
||||
bool backface_culling = cur_node.f->drawtype == NDT_NORMAL;
|
||||
if (n2 == n1)
|
||||
bool backface_culling;
|
||||
if (!isSolidFaceDrawn(n1, *cur_node.f, n2, nodedef, &backface_culling))
|
||||
continue;
|
||||
if (n2 == CONTENT_IGNORE)
|
||||
continue;
|
||||
if (n2 != CONTENT_AIR) {
|
||||
const ContentFeatures &f2 = nodedef->get(n2);
|
||||
if (f2.solidness == 2)
|
||||
continue;
|
||||
if (cur_node.f->drawtype == NDT_LIQUID) {
|
||||
if (cur_node.f->sameLiquidRender(f2))
|
||||
continue;
|
||||
backface_culling = f2.solidness || f2.visual_solidness;
|
||||
}
|
||||
}
|
||||
faces |= 1 << face;
|
||||
getTile(tile_dirs[face], &tiles[face]);
|
||||
for (auto &layer : tiles[face].layers) {
|
||||
@ -463,9 +479,7 @@ void MapblockMeshGenerator::drawSolidNode()
|
||||
if (!cur_node.f->light_source)
|
||||
applyFacesShading(vertex.Color, vertex.Normal);
|
||||
}
|
||||
if (lightDiff(final_lights[1], final_lights[3]) < lightDiff(final_lights[0], final_lights[2]))
|
||||
return QuadDiagonal::Diag13;
|
||||
return QuadDiagonal::Diag02;
|
||||
return LightPair::quadDiagonalForFace(final_lights);
|
||||
});
|
||||
} else {
|
||||
drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) {
|
||||
@ -555,15 +569,19 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing()
|
||||
cur_node.color = encode_light(cur_node.light, cur_node.f->light_source);
|
||||
}
|
||||
|
||||
void MapblockMeshGenerator::getLiquidNeighborhood()
|
||||
template <typename F>
|
||||
std::array<std::array<LiquidData::NeighborData, 3>, 3>
|
||||
MapblockMeshGenerator::getLiquidNeighborsDataRaw(
|
||||
content_t c_source, content_t c_flowing, u8 liquid_range, F &&get_node_rel)
|
||||
{
|
||||
u8 range = rangelim(nodedef->get(cur_liquid.c_flowing).liquid_range, 1, 8);
|
||||
std::array<std::array<LiquidData::NeighborData, 3>, 3> ret;
|
||||
|
||||
u8 range = rangelim(liquid_range, 1, 8);
|
||||
|
||||
for (int w = -1; w <= 1; w++)
|
||||
for (int u = -1; u <= 1; u++) {
|
||||
LiquidData::NeighborData &neighbor = cur_liquid.neighbors[w + 1][u + 1];
|
||||
v3s16 p2 = cur_node.p + v3s16(u, 0, w);
|
||||
MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
|
||||
LiquidData::NeighborData &neighbor = ret[w + 1][u + 1];
|
||||
const MapNode n2 = get_node_rel(v3s16(u, 0, w));
|
||||
neighbor.content = n2.getContent();
|
||||
neighbor.level = -0.5f;
|
||||
neighbor.is_same_liquid = false;
|
||||
@ -572,10 +590,10 @@ void MapblockMeshGenerator::getLiquidNeighborhood()
|
||||
if (neighbor.content == CONTENT_IGNORE)
|
||||
continue;
|
||||
|
||||
if (neighbor.content == cur_liquid.c_source) {
|
||||
if (neighbor.content == c_source) {
|
||||
neighbor.is_same_liquid = true;
|
||||
neighbor.level = 0.5f;
|
||||
} else if (neighbor.content == cur_liquid.c_flowing) {
|
||||
} else if (neighbor.content == c_flowing) {
|
||||
neighbor.is_same_liquid = true;
|
||||
u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
|
||||
if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
|
||||
@ -588,28 +606,41 @@ void MapblockMeshGenerator::getLiquidNeighborhood()
|
||||
// Check node above neighbor.
|
||||
// NOTE: This doesn't get executed if neighbor
|
||||
// doesn't exist
|
||||
p2.Y++;
|
||||
n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
|
||||
if (n2.getContent() == cur_liquid.c_source || n2.getContent() == cur_liquid.c_flowing)
|
||||
const MapNode n3 = get_node_rel(v3s16(u, 1, w));
|
||||
if (n3.getContent() == c_source || n3.getContent() == c_flowing)
|
||||
neighbor.top_is_same_liquid = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MapblockMeshGenerator::calculateCornerLevels()
|
||||
std::array<std::array<LiquidData::NeighborData, 3>, 3>
|
||||
MapblockMeshGenerator::getLiquidNeighborsData(
|
||||
content_t c_source, content_t c_flowing, u8 liquid_range,
|
||||
const std::function<MapNode(v3s16)> &get_node_rel)
|
||||
{
|
||||
for (int k = 0; k < 2; k++)
|
||||
for (int i = 0; i < 2; i++)
|
||||
cur_liquid.corner_levels[k][i] = getCornerLevel(i, k);
|
||||
return getLiquidNeighborsDataRaw(c_source, c_flowing, liquid_range, get_node_rel);
|
||||
}
|
||||
|
||||
f32 MapblockMeshGenerator::getCornerLevel(int i, int k) const
|
||||
void MapblockMeshGenerator::getLiquidNeighborhood()
|
||||
{
|
||||
u8 liquid_range = nodedef->get(cur_liquid.c_flowing).liquid_range;
|
||||
auto get_node_rel = [&](v3s16 relpos) {
|
||||
return data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p + relpos);
|
||||
};
|
||||
|
||||
cur_liquid.neighbors = getLiquidNeighborsDataRaw(cur_liquid.c_source,
|
||||
cur_liquid.c_flowing, liquid_range, get_node_rel);
|
||||
}
|
||||
|
||||
f32 MapblockMeshGenerator::calculateLiquidCornerLevel(
|
||||
content_t c_source, content_t c_flowing,
|
||||
const std::array<std::reference_wrapper<const LiquidData::NeighborData>, 4> &neighbors)
|
||||
{
|
||||
float sum = 0;
|
||||
int count = 0;
|
||||
int air_count = 0;
|
||||
for (int dk = 0; dk < 2; dk++)
|
||||
for (int di = 0; di < 2; di++) {
|
||||
const LiquidData::NeighborData &neighbor_data = cur_liquid.neighbors[k + dk][i + di];
|
||||
for (const LiquidData::NeighborData &neighbor_data : neighbors) {
|
||||
content_t content = neighbor_data.content;
|
||||
|
||||
// If top is liquid, draw starting from top of node
|
||||
@ -617,11 +648,11 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k) const
|
||||
return 0.5f;
|
||||
|
||||
// Source always has the full height
|
||||
if (content == cur_liquid.c_source)
|
||||
if (content == c_source)
|
||||
return 0.5f;
|
||||
|
||||
// Flowing liquid has level information
|
||||
if (content == cur_liquid.c_flowing) {
|
||||
if (content == c_flowing) {
|
||||
sum += neighbor_data.level;
|
||||
count++;
|
||||
} else if (content == CONTENT_AIR) {
|
||||
@ -635,6 +666,30 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::array<std::array<f32, 2>, 2>
|
||||
MapblockMeshGenerator::calculateLiquidCornerLevels(
|
||||
content_t c_source, content_t c_flowing,
|
||||
const std::array<std::array<LiquidData::NeighborData, 3>, 3> &neighbors)
|
||||
{
|
||||
std::array<std::array<f32, 2>, 2> corners;
|
||||
|
||||
for (int k = 0; k < 2; k++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
corners[k][i] = calculateLiquidCornerLevel(c_source, c_flowing, {
|
||||
std::cref(neighbors[k][i]), std::cref(neighbors[k][i+1]),
|
||||
std::cref(neighbors[k+1][i]), std::cref(neighbors[k+1][i+1]),
|
||||
});
|
||||
}}
|
||||
|
||||
return corners;
|
||||
}
|
||||
|
||||
void MapblockMeshGenerator::calculateCornerLevels()
|
||||
{
|
||||
cur_liquid.corner_levels = calculateLiquidCornerLevels(cur_liquid.c_source,
|
||||
cur_liquid.c_flowing, cur_liquid.neighbors);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct LiquidFaceDesc {
|
||||
v3s16 dir; // XZ
|
||||
|
@ -5,10 +5,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodedef.h"
|
||||
#include <array>
|
||||
|
||||
struct MeshMakeData;
|
||||
struct MeshCollector;
|
||||
|
||||
enum class QuadDiagonal {
|
||||
Diag02,
|
||||
Diag13,
|
||||
};
|
||||
|
||||
struct LightPair {
|
||||
u8 lightDay;
|
||||
u8 lightNight;
|
||||
@ -20,6 +26,9 @@ struct LightPair {
|
||||
lightDay(core::clamp(core::round32(valueA), 0, 255)),
|
||||
lightNight(core::clamp(core::round32(valueB), 0, 255)) {}
|
||||
operator u16() const { return lightDay | lightNight << 8; }
|
||||
|
||||
static int lightDiff(LightPair a, LightPair b);
|
||||
static QuadDiagonal quadDiagonalForFace(LightPair final_lights[4]);
|
||||
};
|
||||
|
||||
struct LightInfo {
|
||||
@ -93,10 +102,16 @@ private:
|
||||
void drawAutoLightedCuboid(aabb3f box, f32 const *txc = nullptr, TileSpec *tiles = nullptr, int tile_count = 0, u8 mask = 0);
|
||||
u8 getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const;
|
||||
|
||||
// solid-specific
|
||||
public:
|
||||
static bool isSolidFaceDrawn(content_t n1, const ContentFeatures &f1,
|
||||
content_t n2, const NodeDefManager *nodedef,
|
||||
bool *backface_culling_out = nullptr);
|
||||
|
||||
// liquid-specific
|
||||
struct LiquidData {
|
||||
struct NeighborData {
|
||||
f32 level;
|
||||
f32 level; // in range [-0.5, 0.5]
|
||||
content_t content;
|
||||
bool is_same_liquid;
|
||||
bool top_is_same_liquid;
|
||||
@ -109,16 +124,33 @@ private:
|
||||
content_t c_flowing;
|
||||
content_t c_source;
|
||||
video::SColor color_top;
|
||||
NeighborData neighbors[3][3];
|
||||
f32 corner_levels[2][2];
|
||||
std::array<std::array<NeighborData, 3>, 3> neighbors;
|
||||
std::array<std::array<f32, 2>, 2> corner_levels;
|
||||
};
|
||||
LiquidData cur_liquid;
|
||||
bool smooth_liquids = false;
|
||||
|
||||
static std::array<std::array<LiquidData::NeighborData, 3>, 3>
|
||||
getLiquidNeighborsData(content_t c_source, content_t c_flowing, u8 liquid_range,
|
||||
const std::function<MapNode(v3s16)> &get_node_rel);
|
||||
|
||||
// the arrays are indexed Z-first, i.e. neighbors[z][x], ret[z][x]
|
||||
static std::array<std::array<f32, 2>, 2>
|
||||
calculateLiquidCornerLevels(content_t c_source, content_t c_flowing,
|
||||
const std::array<std::array<LiquidData::NeighborData, 3>, 3> &neighbors);
|
||||
|
||||
private:
|
||||
template <typename F>
|
||||
static std::array<std::array<LiquidData::NeighborData, 3>, 3>
|
||||
getLiquidNeighborsDataRaw(content_t c_source, content_t c_flowing,
|
||||
u8 liquid_range, F &&get_node_rel);
|
||||
|
||||
static f32 calculateLiquidCornerLevel(content_t c_source, content_t c_flowing,
|
||||
const std::array<std::reference_wrapper<const LiquidData::NeighborData>, 4> &neighbors);
|
||||
|
||||
void prepareLiquidNodeDrawing();
|
||||
void getLiquidNeighborhood();
|
||||
void calculateCornerLevels();
|
||||
f32 getCornerLevel(int i, int k) const;
|
||||
void drawLiquidSides();
|
||||
void drawLiquidTop();
|
||||
void drawLiquidBottom();
|
||||
|
@ -223,6 +223,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
||||
CachedPixelShaderSetting<float> m_bloom_strength_pixel{"bloomStrength"};
|
||||
CachedPixelShaderSetting<float> m_bloom_radius_pixel{"bloomRadius"};
|
||||
CachedPixelShaderSetting<float> m_saturation_pixel{"saturation"};
|
||||
CachedPixelShaderSetting<float, 4>
|
||||
m_wield_posteffect_color{"wield_posteffect_color"};
|
||||
bool m_volumetric_light_enabled;
|
||||
CachedPixelShaderSetting<float, 3>
|
||||
m_sun_position_pixel{"sunPositionScreen"};
|
||||
@ -336,6 +338,8 @@ public:
|
||||
float saturation = lighting.saturation;
|
||||
m_saturation_pixel.set(&saturation, services);
|
||||
|
||||
m_wield_posteffect_color.set(m_client->getWieldPostEffectColor(), services);
|
||||
|
||||
if (m_volumetric_light_enabled) {
|
||||
// Map directional light to screen space
|
||||
auto camera_node = m_client->getCamera()->getCameraNode();
|
||||
|
@ -130,11 +130,10 @@ u16 getFaceLight(MapNode n, MapNode n2, const NodeDefManager *ndef)
|
||||
Calculate smooth lighting at the XYZ- corner of p.
|
||||
Both light banks
|
||||
*/
|
||||
template <typename F>
|
||||
static u16 getSmoothLightCombined(const v3s16 &p,
|
||||
const std::array<v3s16,8> &dirs, MeshMakeData *data)
|
||||
const std::array<v3s16,8> &dirs, const NodeDefManager *ndef, F &&get_node)
|
||||
{
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
u16 ambient_occlusion = 0;
|
||||
u16 light_count = 0;
|
||||
u8 light_source_max = 0;
|
||||
@ -147,7 +146,7 @@ static u16 getSmoothLightCombined(const v3s16 &p,
|
||||
ambient_occlusion++;
|
||||
return false;
|
||||
}
|
||||
MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
|
||||
MapNode n = get_node(p + dirs[i]);
|
||||
if (n.getContent() == CONTENT_IGNORE)
|
||||
return true;
|
||||
const ContentFeatures &f = ndef->get(n);
|
||||
@ -234,22 +233,14 @@ static u16 getSmoothLightCombined(const v3s16 &p,
|
||||
return light_day | (light_night << 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate smooth lighting at the given corner of p.
|
||||
Both light banks.
|
||||
Node at p is solid, and thus the lighting is face-dependent.
|
||||
*/
|
||||
u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
|
||||
{
|
||||
return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate smooth lighting at the given corner of p.
|
||||
Both light banks.
|
||||
Node at p is not solid, and the lighting is not face-dependent.
|
||||
*/
|
||||
u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
|
||||
template <typename F>
|
||||
static u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner,
|
||||
const NodeDefManager *ndef, F &&get_node)
|
||||
{
|
||||
const std::array<v3s16,8> dirs = {{
|
||||
// Always shine light
|
||||
@ -264,7 +255,35 @@ u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData
|
||||
v3s16(0,corner.Y,corner.Z),
|
||||
v3s16(corner.X,corner.Y,corner.Z)
|
||||
}};
|
||||
return getSmoothLightCombined(p, dirs, data);
|
||||
|
||||
return getSmoothLightCombined(p, dirs, ndef, std::forward<F>(get_node));
|
||||
}
|
||||
|
||||
u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
|
||||
{
|
||||
return getSmoothLightTransparent(p, corner, data->nodedef,
|
||||
[data](v3s16 p) -> MapNode {
|
||||
return data->m_vmanip.getNodeNoExNoEmerge(p);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate smooth lighting at the given corner of p.
|
||||
Both light banks.
|
||||
Node at p is solid, and thus the lighting is face-dependent.
|
||||
*/
|
||||
u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner,
|
||||
MeshMakeData *data)
|
||||
{
|
||||
return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
|
||||
}
|
||||
|
||||
u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner,
|
||||
const NodeDefManager *ndef, const std::function<MapNode(v3s16)> &get_node)
|
||||
{
|
||||
return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir,
|
||||
ndef, get_node);
|
||||
}
|
||||
|
||||
void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio)
|
||||
|
@ -283,6 +283,8 @@ video::SColor encode_light(u16 light, u8 emissive_light);
|
||||
u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef);
|
||||
u16 getFaceLight(MapNode n, MapNode n2, const NodeDefManager *ndef);
|
||||
u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data);
|
||||
u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner,
|
||||
const NodeDefManager *ndef, const std::function<MapNode(v3s16)> &get_node);
|
||||
u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data);
|
||||
|
||||
/*!
|
||||
|
@ -71,7 +71,7 @@ void populateAnaglyphPipeline(RenderPipeline *pipeline, Client *client)
|
||||
pipeline->addStep<OffsetCameraStep>(0.0f);
|
||||
pipeline->addStep<SetColorMaskStep>(video::ECP_ALL);
|
||||
|
||||
pipeline->addStep<DrawWield>();
|
||||
pipeline->addStep<MapPostFxStep>();
|
||||
pipeline->addStep<DrawWield>();
|
||||
pipeline->addStep<DrawHUD>();
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ void populateInterlacedPipeline(RenderPipeline *pipeline, Client *client)
|
||||
auto output = pipeline->createOwned<TextureBufferOutput>(buffer, right ? TEXTURE_RIGHT : TEXTURE_LEFT);
|
||||
pipeline->addStep<SetRenderTargetStep>(step3D, output);
|
||||
pipeline->addStep(step3D);
|
||||
pipeline->addStep<DrawWield>();
|
||||
pipeline->addStep<MapPostFxStep>();
|
||||
pipeline->addStep<DrawWield>();
|
||||
}
|
||||
|
||||
pipeline->addStep<OffsetCameraStep>(0.0f);
|
||||
|
@ -36,6 +36,7 @@ struct PipelineContext
|
||||
ShadowRenderer *shadow_renderer;
|
||||
video::SColor clear_color;
|
||||
v2u32 target_size;
|
||||
video::SColorf wield_post_color{0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
bool show_hud {true};
|
||||
bool draw_wield_tool {true};
|
||||
|
@ -32,7 +32,7 @@ void DrawWield::run(PipelineContext &context)
|
||||
m_target->activate(context);
|
||||
|
||||
if (context.draw_wield_tool)
|
||||
context.client->getCamera()->drawWieldedTool();
|
||||
context.client->getCamera()->drawWieldedTool(nullptr, context.wield_post_color);
|
||||
}
|
||||
|
||||
void DrawHUD::run(PipelineContext &context)
|
||||
@ -63,7 +63,8 @@ void MapPostFxStep::run(PipelineContext &context)
|
||||
if (target)
|
||||
target->activate(context);
|
||||
|
||||
context.client->getEnv().getClientMap().renderPostFx(context.client->getCamera()->getCameraMode());
|
||||
context.wield_post_color = context.client->getEnv().getClientMap()
|
||||
.renderPostFx();
|
||||
}
|
||||
|
||||
void RenderShadowMapStep::run(PipelineContext &context)
|
||||
@ -143,8 +144,8 @@ void populatePlainPipeline(RenderPipeline *pipeline, Client *client)
|
||||
auto downscale_factor = getDownscaleFactor();
|
||||
auto step3D = pipeline->own(create3DStage(client, downscale_factor));
|
||||
pipeline->addStep(step3D);
|
||||
pipeline->addStep<DrawWield>();
|
||||
pipeline->addStep<MapPostFxStep>();
|
||||
pipeline->addStep<DrawWield>();
|
||||
|
||||
step3D = addUpscaling(pipeline, step3D, downscale_factor, client);
|
||||
|
||||
|
@ -23,6 +23,9 @@ private:
|
||||
RenderTarget *m_target {nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a pipeline step that renders the wieldhand
|
||||
*/
|
||||
class DrawWield : public RenderStep
|
||||
{
|
||||
public:
|
||||
|
@ -66,8 +66,8 @@ void populateSideBySidePipeline(RenderPipeline *pipeline, Client *client, bool h
|
||||
buffer, std::vector<u8> {right ? TEXTURE_RIGHT : TEXTURE_LEFT}, TEXTURE_DEPTH);
|
||||
pipeline->addStep<SetRenderTargetStep>(step3D, output);
|
||||
pipeline->addStep(step3D);
|
||||
pipeline->addStep<DrawWield>();
|
||||
pipeline->addStep<MapPostFxStep>();
|
||||
pipeline->addStep<DrawWield>();
|
||||
pipeline->addStep<DrawHUD>();
|
||||
}
|
||||
|
||||
@ -82,4 +82,4 @@ void populateSideBySidePipeline(RenderPipeline *pipeline, Client *client, bool h
|
||||
step->setRenderSource(buffer);
|
||||
step->setRenderTarget(screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ set(util_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/colorize.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convex_polygon.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hashing.cpp
|
||||
|
159
src/util/convex_polygon.cpp
Normal file
159
src/util/convex_polygon.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
// SPDX-FileCopyrightText: 2024 Minetest
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "convex_polygon.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef SERVER
|
||||
|
||||
#include <IVideoDriver.h>
|
||||
|
||||
void ConvexPolygon::draw(video::IVideoDriver *driver, const video::SColor &color) const
|
||||
{
|
||||
if (vertices.size() < 3)
|
||||
return;
|
||||
|
||||
auto new_2d_vertex = [&color](const v2f &pos) -> video::S3DVertex {
|
||||
return video::S3DVertex(
|
||||
v3f(pos.X * 2.0f - 1.0f, -(pos.Y * 2.0f - 1.0f), 0.0f),
|
||||
v3f(), color, v2f());
|
||||
};
|
||||
|
||||
std::vector<video::S3DVertex> s3d_vertices;
|
||||
s3d_vertices.reserve(vertices.size());
|
||||
for (const v2f &v : vertices)
|
||||
s3d_vertices.push_back(new_2d_vertex(v));
|
||||
|
||||
std::vector<u16> index_list;
|
||||
index_list.reserve(vertices.size());
|
||||
for (size_t i = 0; i < vertices.size(); ++i)
|
||||
index_list.push_back(i);
|
||||
|
||||
video::SMaterial material;
|
||||
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
|
||||
material.ZWriteEnable = video::EZW_OFF;
|
||||
driver->setMaterial(material);
|
||||
|
||||
driver->setTransform(video::ETS_PROJECTION, core::matrix4::EM4CONST_IDENTITY);
|
||||
driver->setTransform(video::ETS_VIEW, core::matrix4::EM4CONST_IDENTITY);
|
||||
driver->setTransform(video::ETS_WORLD, core::matrix4::EM4CONST_IDENTITY);
|
||||
|
||||
driver->drawVertexPrimitiveList((void *)&s3d_vertices[0], (u32)s3d_vertices.size(),
|
||||
(void *)&index_list[0], (u32)index_list.size() - 2,
|
||||
video::EVT_STANDARD, // S3DVertex vertices
|
||||
scene::EPT_TRIANGLE_FAN,
|
||||
video::EIT_16BIT); // 16 bit indices
|
||||
}
|
||||
|
||||
#endif // !def(SERVER)
|
||||
|
||||
f32 ConvexPolygon::area() const
|
||||
{
|
||||
if (vertices.size() < 3)
|
||||
return 0.0f;
|
||||
// sum up the areas of all triangles
|
||||
f32 area = 0.0f;
|
||||
const v2f &v1 = vertices[0];
|
||||
for (size_t i = 2; i < vertices.size(); ++i) {
|
||||
const v2f &v2 = vertices[i-1];
|
||||
const v2f &v3 = vertices[i];
|
||||
// area of the triangle v1 v2 v3: 0.5 * det(d1 d2)
|
||||
// (winding order matters, for sign)
|
||||
v2f d1 = v2 - v1;
|
||||
v2f d2 = v3 - v1;
|
||||
area += 0.5f * (d1.X * d2.Y - d1.Y * d2.X);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
ConvexPolygon ConvexPolygon::clip(v3f clip_line) const
|
||||
{
|
||||
using polygon_iterator = std::vector<v2f>::const_iterator;
|
||||
|
||||
// the return value
|
||||
ConvexPolygon clipped;
|
||||
auto &vertices_out = clipped.vertices;
|
||||
|
||||
if (vertices.empty())
|
||||
return clipped; // emtpty
|
||||
|
||||
// returns whether pos is in the not clipped half-space
|
||||
auto is_in = [&](const v2f &pos) {
|
||||
return pos.X * clip_line.X + pos.Y * clip_line.Y + clip_line.Z >= 0.0f;
|
||||
};
|
||||
|
||||
auto is_out = [&](const v2f &pos) {
|
||||
return !is_in(pos);
|
||||
};
|
||||
|
||||
// There are 3 cases:
|
||||
// * all vertices are clipped away
|
||||
// * no vertices are clipped away
|
||||
// * clip_line intersects the polygon at two points.
|
||||
// There is hence one (possibly over the list end) contiguous sub-list of
|
||||
// vertices that are all in (not clipped)
|
||||
//
|
||||
// is_in applied on the polygon vertices looks like this:
|
||||
// {in, in, in, out, out, in, in}
|
||||
// last_in ^ ^ first_in
|
||||
// first_out ^ ^ last_out
|
||||
//
|
||||
// We need to cut the polygon between the out-in and in-out vertex pairs.
|
||||
|
||||
// find the first vertex that is in/out of the clip_line, and also the vertex
|
||||
// before
|
||||
polygon_iterator first_in, first_out;
|
||||
polygon_iterator last_out, last_in;
|
||||
if (is_in(vertices[0])) {
|
||||
first_out = std::find_if(vertices.begin() + 1, vertices.end(), is_out);
|
||||
if (first_out == vertices.end()) {
|
||||
// all are in
|
||||
clipped = {vertices};
|
||||
return clipped;
|
||||
}
|
||||
last_in = first_out - 1;
|
||||
first_in = std::find_if(first_out + 1, vertices.end(), is_in);
|
||||
last_out = first_in - 1;
|
||||
if (first_in == vertices.end())
|
||||
first_in = vertices.begin(); // we already checked that the 0th is in
|
||||
} else {
|
||||
first_in = std::find_if(vertices.begin() + 1, vertices.end(), is_in);
|
||||
if (first_in == vertices.end()) {
|
||||
// all are out
|
||||
return clipped; // empty
|
||||
}
|
||||
last_out = first_in - 1;
|
||||
first_out = std::find_if(first_in + 1, vertices.end(), is_out);
|
||||
last_in = first_out - 1;
|
||||
if (first_out == vertices.end())
|
||||
first_out = vertices.begin(); // we already checked that the 0th is out
|
||||
}
|
||||
|
||||
// copy all vertices that are in
|
||||
if (first_in <= last_in) {
|
||||
vertices_out.reserve((last_in - first_in) + 1 + 2);
|
||||
vertices_out.insert(vertices_out.end(), first_in, last_in + 1);
|
||||
} else {
|
||||
vertices_out.reserve((vertices.end() - first_in) + (last_in - vertices.begin()) + 1 + 2);
|
||||
vertices_out.insert(vertices_out.end(), first_in, vertices.end());
|
||||
vertices_out.insert(vertices_out.end(), vertices.begin(), last_in + 1);
|
||||
}
|
||||
|
||||
auto split_edge = [&](const v2f &p1, const v2f &p2) {
|
||||
auto homogenize = [](const v2f &p) { return v3f(p.X, p.Y, 1.0f); };
|
||||
// find a pos that is on clip_line and between p1 and p2
|
||||
// compute: meet(clip_line, join(p1, p2))
|
||||
v3f pos_homo = clip_line.crossProduct(homogenize(p1).crossProduct(homogenize(p2)));
|
||||
// dehomogenize
|
||||
return v2f(pos_homo.X / pos_homo.Z, pos_homo.Y / pos_homo.Z);
|
||||
};
|
||||
|
||||
// split in-out pair
|
||||
vertices_out.push_back(split_edge(*last_in, *first_out));
|
||||
|
||||
// split out-in pair
|
||||
vertices_out.push_back(split_edge(*last_out, *first_in));
|
||||
|
||||
return clipped;
|
||||
}
|
50
src/util/convex_polygon.h
Normal file
50
src/util/convex_polygon.h
Normal file
@ -0,0 +1,50 @@
|
||||
// SPDX-FileCopyrightText: 2024 Minetest
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irr_v2d.h"
|
||||
#include "irr_v3d.h"
|
||||
#include <SColor.h>
|
||||
#include <vector>
|
||||
|
||||
#ifndef SERVER
|
||||
|
||||
namespace irr::video { class IVideoDriver; }
|
||||
#endif // !def(SERVER)
|
||||
|
||||
/**
|
||||
* A polygon, stored as closed polyonal chain (ordered vertex list).
|
||||
* The functions assume it's convex.
|
||||
*/
|
||||
struct ConvexPolygon
|
||||
{
|
||||
std::vector<v2f> vertices;
|
||||
|
||||
#ifndef SERVER
|
||||
|
||||
/** Draws the polygon over the whole screen.
|
||||
*
|
||||
* X coordinates go from 0 (left) to 1 (right), Y from 0 (up) to 1 (down).
|
||||
*/
|
||||
void draw(video::IVideoDriver *driver, const video::SColor &color) const;
|
||||
|
||||
#endif // !def(SERVER)
|
||||
|
||||
/** Calculates the area.
|
||||
*
|
||||
* @return The area.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
f32 area() const;
|
||||
|
||||
/** Clips away the parts of the polygon that are on the wrong side of the
|
||||
* given clip line.
|
||||
* @param clip_line The homogeneous coordinates of an oriented 2D line. Clips
|
||||
* away all points p for which dot(clip_line, p) < 0 holds.
|
||||
* @return The clipped polygon.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
ConvexPolygon clip(v3f clip_line) const;
|
||||
};
|
Loading…
Reference in New Issue
Block a user