From 604fb2b738c1f369c608b7f3b778034bf3dc2725 Mon Sep 17 00:00:00 2001 From: x2048 Date: Fri, 20 May 2022 22:33:52 +0200 Subject: Fix lighting of the wield mesh (#12341) * Assign node light to player before final color blend. Fixes day/night lightbank ratio for wield meshes * Update wield mesh light when changing mesh --- src/client/camera.cpp | 10 +++++++--- src/client/camera.h | 3 +++ src/client/clientenvironment.cpp | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src/client') diff --git a/src/client/camera.cpp b/src/client/camera.cpp index d1f19adb3..7cc9cb6e8 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -47,7 +47,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *rendering_engine): m_draw_control(draw_control), - m_client(client) + m_client(client), + m_player_light_color(0xFFFFFFFF) { auto smgr = rendering_engine->get_scene_manager(); // note: making the camera node a child of the player node @@ -153,8 +154,10 @@ void Camera::step(f32 dtime) bool was_under_zero = m_wield_change_timer < 0; m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125); - if (m_wield_change_timer >= 0 && was_under_zero) + if (m_wield_change_timer >= 0 && was_under_zero) { m_wieldnode->setItem(m_wield_item_next, m_client); + m_wieldnode->setNodeLightColor(m_player_light_color); + } if (m_view_bobbing_state != 0) { @@ -555,7 +558,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio) m_wieldnode->setPosition(wield_position); m_wieldnode->setRotation(wield_rotation); - m_wieldnode->setNodeLightColor(player->light_color); + m_player_light_color = player->light_color; + m_wieldnode->setNodeLightColor(m_player_light_color); // Set render distance updateViewingRange(); diff --git a/src/client/camera.h b/src/client/camera.h index 403d6024c..cbf248d97 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -264,4 +264,7 @@ private: std::list m_nametags; bool m_show_nametag_backgrounds; + + // Last known light color of the player + video::SColor m_player_light_color; }; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 448af36c6..183a95007 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -305,6 +305,7 @@ void ClientEnvironment::step(float dtime) node_at_lplayer = m_map->getNode(p); u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); + lplayer->light_color = encode_light(light, 0); // this transfers light.alpha final_color_blend(&lplayer->light_color, light, day_night_ratio); } -- cgit v1.2.3 From a4ef62f5b215fe0f23e3e50672f1538854db4ed9 Mon Sep 17 00:00:00 2001 From: x2048 Date: Fri, 20 May 2022 22:35:03 +0200 Subject: Fix lighting of upright_sprite entities (#12336) Use MeshNode materials to set the light since ReadOnlyMaterials is now false --- src/client/content_cao.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/client') diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index d89bb53b3..9c3e5aa05 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -905,12 +905,8 @@ void GenericCAO::setNodeLight(const video::SColor &light_color) if (m_prop.visual == "upright_sprite") { if (!m_meshnode) return; - - scene::IMesh *mesh = m_meshnode->getMesh(); - for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); - buf->getMaterial().EmissiveColor = light_color; - } + for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) + m_meshnode->getMaterial(i).EmissiveColor = light_color; } else { scene::ISceneNode *node = getSceneNode(); if (!node) -- cgit v1.2.3 From dc45b85a543b4c8ad72f69a554ecfe7f0a60c533 Mon Sep 17 00:00:00 2001 From: x2048 Date: Sat, 21 May 2022 16:49:30 +0200 Subject: Improve shadow filters (#12195) * Rewrite shadow filtering for the new distortion * Calculate penumbra radius using a single sample * Avoid peter-panning effect due to filtering of short shadows * Add adaptive filter quality for soft shadows * Avoid sharp shadows on surfaces without normals (e.g. plants) * Increase default and maximum soft shadow radius * Make line numbers in shader errors match the code --- builtin/settingtypes.txt | 4 +- client/shaders/nodes_shader/opengl_fragment.glsl | 170 ++++++++------------- client/shaders/nodes_shader/opengl_vertex.glsl | 2 + client/shaders/object_shader/opengl_fragment.glsl | 174 ++++++++-------------- client/shaders/object_shader/opengl_vertex.glsl | 2 + src/client/clientmap.cpp | 2 + src/client/shader.cpp | 2 + src/client/shadows/dynamicshadowsrender.cpp | 1 + src/defaultsettings.cpp | 2 +- 9 files changed, 135 insertions(+), 224 deletions(-) (limited to 'src/client') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ff69d9741..3f6f6c9fc 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -631,8 +631,8 @@ shadow_update_frames (Map shadows update frames) int 8 1 16 # Set the soft shadow radius size. # Lower values mean sharper shadows, bigger values mean softer shadows. -# Minimum value: 1.0; maximum value: 10.0 -shadow_soft_radius (Soft shadow radius) float 1.0 1.0 10.0 +# Minimum value: 1.0; maximum value: 15.0 +shadow_soft_radius (Soft shadow radius) float 5.0 1.0 15.0 # Set the tilt of Sun/Moon orbit in degrees. # Value of 0 means no tilt / vertical orbit. diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 8110f6fd3..c4b947e72 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -25,6 +25,7 @@ uniform float animationTimer; varying float cosLight; varying float f_normal_length; varying vec3 shadow_position; + varying float perspective_factor; #endif @@ -116,23 +117,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance #if SHADOW_FILTER == 2 - #define PCFBOUND 3.5 - #define PCFSAMPLES 64.0 + #define PCFBOUND 2.0 // 5x5 + #define PCFSAMPLES 25 #elif SHADOW_FILTER == 1 - #define PCFBOUND 1.5 - #if defined(POISSON_FILTER) - #define PCFSAMPLES 32.0 - #else - #define PCFSAMPLES 16.0 - #endif + #define PCFBOUND 1.0 // 3x3 + #define PCFSAMPLES 9 #else #define PCFBOUND 0.0 - #if defined(POISSON_FILTER) - #define PCFSAMPLES 4.0 - #else - #define PCFSAMPLES 1.0 - #endif + #define PCFSAMPLES 1 #endif + #ifdef COLORED_SHADOWS float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { @@ -149,59 +143,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis } #endif -float getBaseLength(vec2 smTexCoord) -{ - float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy); // length in texture coords - return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0); // return to undistorted coords -} - -float getDeltaPerspectiveFactor(float l) -{ - return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1); // original distortion factor, divided by 10 -} +#define BASEFILTERRADIUS 1.0 -float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier) +float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; - // Return fast if sharp shadows are requested - if (PCFBOUND == 0.0) + if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0) return 0.0; - if (SOFTSHADOWRADIUS <= 1.0) { - perspectiveFactor = getDeltaPerspectiveFactor(baseLength); - return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS); - } - vec2 clampedpos; - float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5); float y, x; - float depth = 0.0; - float pointDepth; - float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier; - - float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND); - int n = 0; - - for (y = -bound; y <= bound; y += 1.0) - for (x = -bound; x <= bound; x += 1.0) { - clampedpos = vec2(x,y); - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius); - clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy; - - pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance); - if (pointDepth > -0.01) { - depth += pointDepth; - n += 1; - } - } - - depth = depth / n; - depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001; - - perspectiveFactor = getDeltaPerspectiveFactor(baseLength); - return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius); + float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance); + // A factor from 0 to 1 to reduce blurring of short shadows + float sharpness_factor = 1.0; + // conversion factor from shadow depth to blur radius + float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0; + if (depth > 0.0 && f_normal_length > 0.0) + // 5 is empirical factor that controls how fast shadow loses sharpness + sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0); + depth = 0.0; + + float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor + * f_textureresolution / 2.0 / f_shadowfar; + float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node + + return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS); } #ifdef POISSON_FILTER @@ -276,26 +242,23 @@ const vec2[64] poissonDisk = vec2[64]( vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - vec4 visibility = vec4(0.0); - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; + vec2 clampedpos; + vec4 visibility = vec4(0.0); + float scale_factor = radius / f_textureresolution; - float texture_size = 1.0 / (f_textureresolution * 0.5); - int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); + int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows + samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples))); int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); int end_offset = int(samples) + init_offset; for (int x = init_offset; x < end_offset; x++) { - clampedpos = poissonDisk[x]; - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy; + clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy; visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); } @@ -306,26 +269,23 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - float visibility = 0.0; - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; + vec2 clampedpos; + float visibility = 0.0; + float scale_factor = radius / f_textureresolution; - float texture_size = 1.0 / (f_textureresolution * 0.5); - int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); + int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows + samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples))); int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); int end_offset = int(samples) + init_offset; for (int x = init_offset; x < end_offset; x++) { - clampedpos = poissonDisk[x]; - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy; + clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy; visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); } @@ -341,65 +301,57 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - vec4 visibility = vec4(0.0); - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0); + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; - - float texture_size = 1.0 / (f_textureresolution * 0.5); - float y, x; - float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); - int n = 0; + vec2 clampedpos; + vec4 visibility = vec4(0.0); + float x, y; + float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows + bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound); + float scale_factor = radius / bound / f_textureresolution; + float n = 0.0; // basic PCF filter for (y = -bound; y <= bound; y += 1.0) for (x = -bound; x <= bound; x += 1.0) { - clampedpos = vec2(x,y); // screen offset - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted + clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy; visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); - n += 1; + n += 1.0; } - return visibility / n; + return visibility / max(n, 1.0); } #else float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - float visibility = 0.0; - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0); + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; - - float texture_size = 1.0 / (f_textureresolution * 0.5); - float y, x; - float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); - int n = 0; + vec2 clampedpos; + float visibility = 0.0; + float x, y; + float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows + bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound); + float scale_factor = radius / bound / f_textureresolution; + float n = 0.0; // basic PCF filter for (y = -bound; y <= bound; y += 1.0) for (x = -bound; x <= bound; x += 1.0) { - clampedpos = vec2(x,y); // screen offset - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted + clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy; visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); - n += 1; + n += 1.0; } - return visibility / n; + return visibility / max(n, 1.0); } #endif diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 3ea0faa36..d1fba2830 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -39,6 +39,7 @@ centroid varying vec2 varTexCoord; varying float adj_shadow_strength; varying float f_normal_length; varying vec3 shadow_position; + varying float perspective_factor; #endif @@ -253,6 +254,7 @@ void main(void) shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz; shadow_position.z -= z_bias; + perspective_factor = pFactor; if (f_timeofday < 0.2) { adj_shadow_strength = f_shadow_strength * 0.5 * diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 7baf5826f..1fefc764b 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -1,6 +1,5 @@ uniform sampler2D baseTexture; -uniform vec4 emissiveColor; uniform vec3 dayLight; uniform vec4 skyBgColor; uniform float fogDistance; @@ -26,6 +25,7 @@ uniform float animationTimer; varying float cosLight; varying float f_normal_length; varying vec3 shadow_position; + varying float perspective_factor; #endif @@ -119,23 +119,16 @@ float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance #if SHADOW_FILTER == 2 - #define PCFBOUND 3.5 - #define PCFSAMPLES 64.0 + #define PCFBOUND 2.0 // 5x5 + #define PCFSAMPLES 25 #elif SHADOW_FILTER == 1 - #define PCFBOUND 1.5 - #if defined(POISSON_FILTER) - #define PCFSAMPLES 32.0 - #else - #define PCFSAMPLES 16.0 - #endif + #define PCFBOUND 1.0 // 3x3 + #define PCFSAMPLES 9 #else #define PCFBOUND 0.0 - #if defined(POISSON_FILTER) - #define PCFSAMPLES 4.0 - #else - #define PCFSAMPLES 1.0 - #endif + #define PCFSAMPLES 1 #endif + #ifdef COLORED_SHADOWS float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { @@ -152,59 +145,31 @@ float getHardShadowDepth(sampler2D shadowsampler, vec2 smTexCoord, float realDis } #endif -float getBaseLength(vec2 smTexCoord) -{ - float l = length(2.0 * smTexCoord.xy - 1.0 - CameraPos.xy); // length in texture coords - return xyPerspectiveBias1 / (1.0 / l - xyPerspectiveBias0); // return to undistorted coords -} - -float getDeltaPerspectiveFactor(float l) -{ - return 0.04 * pow(512.0 / f_textureresolution, 0.4) / (xyPerspectiveBias0 * l + xyPerspectiveBias1); // original distortion factor, divided by 10 -} +#define BASEFILTERRADIUS 1.0 -float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance, float multiplier) +float getPenumbraRadius(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; - // Return fast if sharp shadows are requested - if (PCFBOUND == 0.0) + if (PCFBOUND == 0.0 || SOFTSHADOWRADIUS <= 0.0) return 0.0; - if (SOFTSHADOWRADIUS <= 1.0) { - perspectiveFactor = getDeltaPerspectiveFactor(baseLength); - return max(2 * length(smTexCoord.xy) * 2048 / f_textureresolution / pow(perspectiveFactor, 3), SOFTSHADOWRADIUS); - } - vec2 clampedpos; - float texture_size = 1.0 / (2048 /*f_textureresolution*/ * 0.5); float y, x; - float depth = 0.0; - float pointDepth; - float maxRadius = SOFTSHADOWRADIUS * 5.0 * multiplier; - - float bound = clamp(PCFBOUND * (1 - baseLength), 0.0, PCFBOUND); - int n = 0; - - for (y = -bound; y <= bound; y += 1.0) - for (x = -bound; x <= bound; x += 1.0) { - clampedpos = vec2(x,y); - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * maxRadius); - clampedpos = clampedpos * texture_size * perspectiveFactor * maxRadius * perspectiveFactor + smTexCoord.xy; - - pointDepth = getHardShadowDepth(shadowsampler, clampedpos.xy, realDistance); - if (pointDepth > -0.01) { - depth += pointDepth; - n += 1; - } - } - - depth = depth / n; - depth = pow(clamp(depth, 0.0, 1000.0), 1.6) / 0.001; - - perspectiveFactor = getDeltaPerspectiveFactor(baseLength); - return max(length(smTexCoord.xy) * 2 * 2048 / f_textureresolution / pow(perspectiveFactor, 3), depth * maxRadius); + float depth = getHardShadowDepth(shadowsampler, smTexCoord.xy, realDistance); + // A factor from 0 to 1 to reduce blurring of short shadows + float sharpness_factor = 1.0; + // conversion factor from shadow depth to blur radius + float depth_to_blur = f_shadowfar / SOFTSHADOWRADIUS / xyPerspectiveBias0; + if (depth > 0.0 && f_normal_length > 0.0) + // 5 is empirical factor that controls how fast shadow loses sharpness + sharpness_factor = clamp(5 * depth * depth_to_blur, 0.0, 1.0); + depth = 0.0; + + float world_to_texture = xyPerspectiveBias1 / perspective_factor / perspective_factor + * f_textureresolution / 2.0 / f_shadowfar; + float world_radius = 0.2; // shadow blur radius in world float coordinates, e.g. 0.2 = 0.02 of one node + + return max(BASEFILTERRADIUS * f_textureresolution / 4096.0, sharpness_factor * world_radius * world_to_texture * SOFTSHADOWRADIUS); } #ifdef POISSON_FILTER @@ -279,26 +244,23 @@ const vec2[64] poissonDisk = vec2[64]( vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - vec4 visibility = vec4(0.0); - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; + vec2 clampedpos; + vec4 visibility = vec4(0.0); + float scale_factor = radius / f_textureresolution; - float texture_size = 1.0 / (f_textureresolution * 0.5); - int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); + int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows + samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples))); int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); int end_offset = int(samples) + init_offset; for (int x = init_offset; x < end_offset; x++) { - clampedpos = poissonDisk[x]; - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy; + clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy; visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); } @@ -309,26 +271,23 @@ vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - float visibility = 0.0; - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.5); // scale to align with PCF + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; + vec2 clampedpos; + float visibility = 0.0; + float scale_factor = radius / f_textureresolution; - float texture_size = 1.0 / (f_textureresolution * 0.5); - int samples = int(clamp(PCFSAMPLES * (1 - baseLength) * (1 - baseLength), PCFSAMPLES / 4, PCFSAMPLES)); + int samples = (1 + 1 * int(SOFTSHADOWRADIUS > 1.0)) * PCFSAMPLES; // scale max samples for the soft shadows + samples = int(clamp(pow(4.0 * radius + 1.0, 2.0), 1.0, float(samples))); int init_offset = int(floor(mod(((smTexCoord.x * 34.0) + 1.0) * smTexCoord.y, 64.0-samples))); int end_offset = int(samples) + init_offset; for (int x = init_offset; x < end_offset; x++) { - clampedpos = poissonDisk[x]; - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor + smTexCoord.xy; + clampedpos = poissonDisk[x] * scale_factor + smTexCoord.xy; visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); } @@ -344,65 +303,57 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) vec4 getShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - vec4 visibility = vec4(0.0); - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0); + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadowColor(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; - - float texture_size = 1.0 / (f_textureresolution * 0.5); - float y, x; - float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); - int n = 0; + vec2 clampedpos; + vec4 visibility = vec4(0.0); + float x, y; + float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows + bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound); + float scale_factor = radius / bound / f_textureresolution; + float n = 0.0; // basic PCF filter for (y = -bound; y <= bound; y += 1.0) for (x = -bound; x <= bound; x += 1.0) { - clampedpos = vec2(x,y); // screen offset - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted + clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy; visibility += getHardShadowColor(shadowsampler, clampedpos.xy, realDistance); - n += 1; + n += 1.0; } - return visibility / n; + return visibility / max(n, 1.0); } #else float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { - vec2 clampedpos; - float visibility = 0.0; - float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance, 1.0); + float radius = getPenumbraRadius(shadowsampler, smTexCoord, realDistance); if (radius < 0.1) { // we are in the middle of even brightness, no need for filtering return getHardShadow(shadowsampler, smTexCoord.xy, realDistance); } - float baseLength = getBaseLength(smTexCoord); - float perspectiveFactor; - - float texture_size = 1.0 / (f_textureresolution * 0.5); - float y, x; - float bound = clamp(PCFBOUND * (1 - baseLength), PCFBOUND / 2, PCFBOUND); - int n = 0; + vec2 clampedpos; + float visibility = 0.0; + float x, y; + float bound = (1 + 0.5 * int(SOFTSHADOWRADIUS > 1.0)) * PCFBOUND; // scale max bound for soft shadows + bound = clamp(0.5 * (4.0 * radius - 1.0), 0.5, bound); + float scale_factor = radius / bound / f_textureresolution; + float n = 0.0; // basic PCF filter for (y = -bound; y <= bound; y += 1.0) for (x = -bound; x <= bound; x += 1.0) { - clampedpos = vec2(x,y); // screen offset - perspectiveFactor = getDeltaPerspectiveFactor(baseLength + length(clampedpos) * texture_size * radius / bound); - clampedpos = clampedpos * texture_size * perspectiveFactor * radius * perspectiveFactor / bound + smTexCoord.xy; // both dx,dy and radius are adjusted + clampedpos = vec2(x,y) * scale_factor + smTexCoord.xy; visibility += getHardShadow(shadowsampler, clampedpos.xy, realDistance); - n += 1; + n += 1.0; } - return visibility / n; + return visibility / max(n, 1.0); } #endif @@ -489,7 +440,6 @@ void main(void) shadow_color = visibility.gba; #else if (cosLight > 0.0 || f_normal_length < 1e-3) - if (cosLight > 0.0) shadow_int = getShadow(ShadowMapSampler, posLightSpace.xy, posLightSpace.z); else shadow_int = 1.0; @@ -540,6 +490,6 @@ void main(void) - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); col = mix(skyBgColor, col, clarity); col = vec4(col.rgb, base.a); - + gl_FragColor = col; } diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index 6dc25f854..dc9c70cdf 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -30,6 +30,7 @@ centroid varying vec2 varTexCoord; varying float adj_shadow_strength; varying float f_normal_length; varying vec3 shadow_position; + varying float perspective_factor; #endif varying vec3 eyeVec; @@ -162,6 +163,7 @@ void main(void) shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (inVertexPosition + vec4(normalOffsetScale * nNormal, 0.0))).xyz; shadow_position.z -= z_bias; + perspective_factor = pFactor; if (f_timeofday < 0.2) { adj_shadow_strength = f_shadow_strength * 0.5 * diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 10967c0cb..46cb115aa 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -489,6 +489,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Do not enable filter on shadow texture to avoid visual artifacts // with colored shadows. // Filtering is done in shader code anyway + layer.BilinearFilter = false; + layer.AnisotropicFilter = false; layer.TrilinearFilter = false; } driver->setMaterial(material); diff --git a/src/client/shader.cpp b/src/client/shader.cpp index bbb872761..009a4b3d7 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -771,6 +771,8 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n"; } + shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics + std::string common_header = shaders_header.str(); std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl"); diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index 07dc6daf2..c13cfe252 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -670,6 +670,7 @@ std::string ShadowRenderer::readShaderFile(const std::string &path) std::string prefix; if (m_shadow_map_colored) prefix.append("#define COLORED_SHADOWS 1\n"); + prefix.append("#line 0\n"); std::string content; fs::ReadFile(path, content); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 11d52efd3..0087f8d48 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -274,7 +274,7 @@ void set_default_settings() settings->setDefault("shadow_filters", "1"); settings->setDefault("shadow_poisson_filter", "true"); settings->setDefault("shadow_update_frames", "8"); - settings->setDefault("shadow_soft_radius", "1.0"); + settings->setDefault("shadow_soft_radius", "5.0"); settings->setDefault("shadow_sky_body_orbit_tilt", "0.0"); // Input -- cgit v1.2.3 From 2f32044273d107e82fb1c35d4a0f616fa480cdf0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 6 May 2022 20:13:45 +0200 Subject: Don't ignore server disconnects in client code If the server stops talking to us without saying bye we should actually end the in-game session with an error message. --- src/client/client.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/client') diff --git a/src/client/client.cpp b/src/client/client.cpp index cb556c1ce..8ab96b7d1 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -786,16 +786,18 @@ void Client::peerAdded(con::Peer *peer) infostream << "Client::peerAdded(): peer->id=" << peer->id << std::endl; } + void Client::deletingPeer(con::Peer *peer, bool timeout) { infostream << "Client::deletingPeer(): " "Server Peer is getting deleted " << "(timeout=" << timeout << ")" << std::endl; - if (timeout) { - m_access_denied = true; + m_access_denied = true; + if (timeout) m_access_denied_reason = gettext("Connection timed out."); - } + else + m_access_denied_reason = gettext("Connection aborted (protocol error?)."); } /* -- cgit v1.2.3 From 2742fef458c3626476193c9e2d1b9231e042e420 Mon Sep 17 00:00:00 2001 From: paradust7 <102263465+paradust7@users.noreply.github.com> Date: Sat, 21 May 2022 15:11:49 -0700 Subject: Fixes needed to use irrArray backed by std::vector (#12263) --- src/client/clientmap.cpp | 39 ++++++++++++++++++------------------- src/client/clientmap.h | 3 +++ src/client/mapblock_mesh.cpp | 21 ++++++++++---------- src/client/mapblock_mesh.h | 17 +++++++++++++--- src/irrlicht_changes/CGUITTFont.cpp | 14 +++++-------- src/irrlicht_changes/CGUITTFont.h | 38 +++++++++++++++++++++++------------- 6 files changed, 77 insertions(+), 55 deletions(-) (limited to 'src/client') diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 46cb115aa..98e3f40d3 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -449,15 +449,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) drawcall_count += draw_order.size(); for (auto &descriptor : draw_order) { - scene::IMeshBuffer *buf; - - if (descriptor.m_use_partial_buffer) { - descriptor.m_partial_buffer->beforeDraw(); - buf = descriptor.m_partial_buffer->getBuffer(); - } - else { - buf = descriptor.m_buffer; - } + scene::IMeshBuffer *buf = descriptor.getBuffer(); // Check and abort if the machine is swapping a lot if (draw.getTimerTime() > 2000) { @@ -501,7 +493,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) m.setTranslation(block_wpos - offset); driver->setTransform(video::ETS_WORLD, m); - driver->drawMeshBuffer(buf); + descriptor.draw(driver); vertex_count += buf->getIndexCount(); } @@ -812,15 +804,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, drawcall_count += draw_order.size(); for (auto &descriptor : draw_order) { - scene::IMeshBuffer *buf; - - if (descriptor.m_use_partial_buffer) { - descriptor.m_partial_buffer->beforeDraw(); - buf = descriptor.m_partial_buffer->getBuffer(); - } - else { - buf = descriptor.m_buffer; - } + scene::IMeshBuffer *buf = descriptor.getBuffer(); // Check and abort if the machine is swapping a lot if (draw.getTimerTime() > 1000) { @@ -845,7 +829,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, m.setTranslation(block_wpos - offset); driver->setTransform(video::ETS_WORLD, m); - driver->drawMeshBuffer(buf); + descriptor.draw(driver); vertex_count += buf->getIndexCount(); } @@ -966,3 +950,18 @@ void ClientMap::updateTransparentMeshBuffers() m_needs_update_transparent_meshes = false; } +scene::IMeshBuffer* ClientMap::DrawDescriptor::getBuffer() +{ + return m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer; +} + +void ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver) +{ + if (m_use_partial_buffer) { + m_partial_buffer->beforeDraw(); + driver->drawMeshBuffer(m_partial_buffer->getBuffer()); + m_partial_buffer->afterDraw(); + } else { + driver->drawMeshBuffer(m_buffer); + } +} diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 6d57f1911..823870c68 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -174,6 +174,9 @@ private: DrawDescriptor(v3s16 pos, const PartialMeshBuffer *buffer) : m_pos(pos), m_partial_buffer(buffer), m_reuse_material(false), m_use_partial_buffer(true) {} + + scene::IMeshBuffer* getBuffer(); + void draw(video::IVideoDriver* driver); }; Client *m_client; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 868573bf0..965dd5e29 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -1162,15 +1162,16 @@ void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector &output void PartialMeshBuffer::beforeDraw() const { // Patch the indexes in the mesh buffer before draw - - m_buffer->Indices.clear(); - if (!m_vertex_indexes.empty()) { - for (auto index : m_vertex_indexes) - m_buffer->Indices.push_back(index); - } + m_buffer->Indices = std::move(m_vertex_indexes); m_buffer->setDirty(scene::EBT_INDEX); } +void PartialMeshBuffer::afterDraw() const +{ + // Take the data back + m_vertex_indexes = std::move(m_buffer->Indices.steal()); +} + /* MapBlockMesh */ @@ -1514,7 +1515,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) const auto &t = m_transparent_triangles[i]; if (current_buffer != t.buffer) { if (current_buffer) { - m_transparent_buffers.emplace_back(current_buffer, current_strain); + m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); current_strain.clear(); } current_buffer = t.buffer; @@ -1525,7 +1526,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) } if (!current_strain.empty()) - m_transparent_buffers.emplace_back(current_buffer, current_strain); + m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); } void MapBlockMesh::consolidateTransparentBuffers() @@ -1539,7 +1540,7 @@ void MapBlockMesh::consolidateTransparentBuffers() for (const auto &t : m_transparent_triangles) { if (current_buffer != t.buffer) { if (current_buffer != nullptr) { - this->m_transparent_buffers.emplace_back(current_buffer, current_strain); + this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); current_strain.clear(); } current_buffer = t.buffer; @@ -1550,7 +1551,7 @@ void MapBlockMesh::consolidateTransparentBuffers() } if (!current_strain.empty()) { - this->m_transparent_buffers.emplace_back(current_buffer, current_strain); + this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); } } diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 72d128038..169b3a8c1 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -140,20 +140,31 @@ private: s32 root = -1; // index of the root node }; +/* + * PartialMeshBuffer + * + * Attach alternate `Indices` to an existing mesh buffer, to make it possible to use different + * indices with the same vertex buffer. + * + * Irrlicht does not currently support this: `CMeshBuffer` ties together a single vertex buffer + * and a single index buffer. There's no way to share these between mesh buffers. + * + */ class PartialMeshBuffer { public: - PartialMeshBuffer(scene::SMeshBuffer *buffer, const std::vector &vertex_indexes) : - m_buffer(buffer), m_vertex_indexes(vertex_indexes) + PartialMeshBuffer(scene::SMeshBuffer *buffer, std::vector &&vertex_indexes) : + m_buffer(buffer), m_vertex_indexes(std::move(vertex_indexes)) {} scene::IMeshBuffer *getBuffer() const { return m_buffer; } const std::vector &getVertexIndexes() const { return m_vertex_indexes; } void beforeDraw() const; + void afterDraw() const; private: scene::SMeshBuffer *m_buffer; - std::vector m_vertex_indexes; + mutable std::vector m_vertex_indexes; }; /* diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index fe86adae6..0f2572ee0 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -292,9 +292,6 @@ shadow_offset(0), shadow_alpha(0), fallback(0) Driver->grab(); setInvisibleCharacters(L" "); - - // Glyphs aren't reference counted, so don't try to delete them when we free the array. - Glyphs.set_free_when_destroyed(false); } bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency) @@ -411,8 +408,7 @@ CGUITTFont::~CGUITTFont() { // Delete the glyphs and glyph pages. reset_images(); - CGUITTAssistDelete::Delete(Glyphs); - //Glyphs.clear(); + Glyphs.clear(); // We aren't using this face anymore. auto n = c_faces.find(filename); @@ -675,6 +671,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect& positio update_glyph_pages(); auto it = Render_Map.begin(); auto ie = Render_Map.end(); + core::array tmp_positions; + core::array tmp_source_rects; while (it != ie) { CGUITTGlyphPage* page = it->second; @@ -696,10 +694,8 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect& positio do ++i; while (i < page->render_positions.size() && page->render_colors[i] == colprev); - core::array tmp_positions; - core::array tmp_source_rects; - tmp_positions.set_pointer(&page->render_positions[ibegin], i - ibegin, false, false); // no copy - tmp_source_rects.set_pointer(&page->render_source_rects[ibegin], i - ibegin, false, false); + tmp_positions.set_data(&page->render_positions[ibegin], i - ibegin); + tmp_source_rects.set_data(&page->render_source_rects[ibegin], i - ibegin); --i; if (!use_transparency) diff --git a/src/irrlicht_changes/CGUITTFont.h b/src/irrlicht_changes/CGUITTFont.h index 9457e5b18..210222ed4 100644 --- a/src/irrlicht_changes/CGUITTFont.h +++ b/src/irrlicht_changes/CGUITTFont.h @@ -37,6 +37,7 @@ #include #include #include "util/enriched_string.h" +#include "util/basic_macros.h" #include FT_FREETYPE_H namespace irr @@ -46,23 +47,34 @@ namespace gui struct SGUITTFace; class CGUITTFont; - //! Class to assist in deleting glyphs. - class CGUITTAssistDelete - { - public: - template - static void Delete(core::array& a) - { - TAlloc allocator; - allocator.deallocate(a.pointer()); - } - }; - //! Structure representing a single TrueType glyph. struct SGUITTGlyph { //! Constructor. - SGUITTGlyph() : isLoaded(false), glyph_page(0), surface(0), parent(0) {} + SGUITTGlyph() : + isLoaded(false), + glyph_page(0), + source_rect(), + offset(), + advance(), + surface(0), + parent(0) + {} + + DISABLE_CLASS_COPY(SGUITTGlyph); + + //! This class would be trivially copyable except for the reference count on `surface`. + SGUITTGlyph(SGUITTGlyph &&other) : + isLoaded(other.isLoaded), + glyph_page(other.glyph_page), + source_rect(other.source_rect), + offset(other.offset), + advance(other.advance), + surface(other.surface), + parent(other.parent) + { + other.surface = 0; + } //! Destructor. ~SGUITTGlyph() { unload(); } -- cgit v1.2.3