diff options
author | Juan Linietsky <reduzio@gmail.com> | 2017-05-25 13:53:59 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2017-05-25 14:00:43 -0300 |
commit | bf6380ee7077b00ad425b242e483482e98854983 (patch) | |
tree | 96096045c762423fd41cd8d36cf16929c4b1167d /drivers | |
parent | 7592c2380d7072c0614f4a7dd6ae8c64e8956f15 (diff) |
Removed skybox support, added panorama support. Skybox support may come back eventually, but hope not.
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gles3/rasterizer_scene_gles3.cpp | 58 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_scene_gles3.h | 26 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_storage_gles3.cpp | 56 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_storage_gles3.h | 12 | ||||
-rw-r--r-- | drivers/gles3/shaders/copy.glsl | 43 | ||||
-rw-r--r-- | drivers/gles3/shaders/cubemap_filter.glsl | 35 |
6 files changed, 148 insertions, 82 deletions
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 79c6f7f01f..5214de6d06 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -798,20 +798,20 @@ void RasterizerSceneGLES3::environment_set_background(RID p_env, VS::Environment env->bg_mode = p_bg; } -void RasterizerSceneGLES3::environment_set_skybox(RID p_env, RID p_skybox) { +void RasterizerSceneGLES3::environment_set_sky(RID p_env, RID p_sky) { Environment *env = environment_owner.getornull(p_env); ERR_FAIL_COND(!env); - env->skybox = p_skybox; + env->sky = p_sky; } -void RasterizerSceneGLES3::environment_set_skybox_scale(RID p_env, float p_scale) { +void RasterizerSceneGLES3::environment_set_sky_scale(RID p_env, float p_scale) { Environment *env = environment_owner.getornull(p_env); ERR_FAIL_COND(!env); - env->skybox_scale = p_scale; + env->sky_scale = p_scale; } void RasterizerSceneGLES3::environment_set_bg_color(RID p_env, const Color &p_color) { @@ -836,14 +836,14 @@ void RasterizerSceneGLES3::environment_set_canvas_max_layer(RID p_env, int p_max env->canvas_max_layer = p_max_layer; } -void RasterizerSceneGLES3::environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy, float p_skybox_contribution) { +void RasterizerSceneGLES3::environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy, float p_sky_contribution) { Environment *env = environment_owner.getornull(p_env); ERR_FAIL_COND(!env); env->ambient_color = p_color; env->ambient_energy = p_energy; - env->ambient_skybox_contribution = p_skybox_contribution; + env->ambient_sky_contribution = p_sky_contribution; } void RasterizerSceneGLES3::environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) { @@ -2120,12 +2120,12 @@ void RasterizerSceneGLES3::_add_geometry(RasterizerStorageGLES3::Geometry *p_geo } } -void RasterizerSceneGLES3::_draw_skybox(RasterizerStorageGLES3::SkyBox *p_skybox, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_scale) { +void RasterizerSceneGLES3::_draw_sky(RasterizerStorageGLES3::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_scale) { - if (!p_skybox) + if (!p_sky) return; - RasterizerStorageGLES3::Texture *tex = storage->texture_owner.getornull(p_skybox->cubemap); + RasterizerStorageGLES3::Texture *tex = storage->texture_owner.getornull(p_sky->panorama); ERR_FAIL_COND(!tex); glActiveTexture(GL_TEXTURE0); @@ -2164,7 +2164,7 @@ void RasterizerSceneGLES3::_draw_skybox(RasterizerStorageGLES3::SkyBox *p_skybox }; - //skybox uv vectors + //sky uv vectors float vw, vh, zn; p_projection.get_viewport_size(vw, vh); zn = p_projection.get_z_near(); @@ -2181,13 +2181,13 @@ void RasterizerSceneGLES3::_draw_skybox(RasterizerStorageGLES3::SkyBox *p_skybox vertices[i * 2 + 1].z = -vertices[i * 2 + 1].z; } - glBindBuffer(GL_ARRAY_BUFFER, state.skybox_verts); + glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vector3) * 8, vertices); glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - glBindVertexArray(state.skybox_array); + glBindVertexArray(state.sky_array); - storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_CUBEMAP, true); + storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_PANORAMA, true); storage->shaders.copy.bind(); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -2195,7 +2195,7 @@ void RasterizerSceneGLES3::_draw_skybox(RasterizerStorageGLES3::SkyBox *p_skybox glBindVertexArray(0); glColorMask(1, 1, 1, 1); - storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_CUBEMAP, false); + storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_PANORAMA, false); } void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform) { @@ -2239,7 +2239,7 @@ void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatr state.ubo_data.bg_color[2] = bg_color.b; state.ubo_data.bg_color[3] = bg_color.a; - state.env_radiance_data.ambient_contribution = env->ambient_skybox_contribution; + state.env_radiance_data.ambient_contribution = env->ambient_sky_contribution; state.ubo_data.ambient_occlusion_affect_light = env->ssao_light_affect; } else { state.ubo_data.bg_energy = 1.0; @@ -2683,7 +2683,7 @@ void RasterizerSceneGLES3::_setup_reflections(RID *p_reflection_probe_cull_resul ambient_linear.r *= p_env->ambient_energy; ambient_linear.g *= p_env->ambient_energy; ambient_linear.b *= p_env->ambient_energy; - contrib = p_env->ambient_skybox_contribution; + contrib = p_env->ambient_sky_contribution; } reflection_ubo.ambient[0] = ambient_linear.r; @@ -3807,7 +3807,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const Color clear_color(0, 0, 0, 0); - RasterizerStorageGLES3::SkyBox *skybox = NULL; + RasterizerStorageGLES3::Sky *sky = NULL; GLuint env_radiance_tex = 0; if (!env || env->bg_mode == VS::ENV_BG_CLEAR_COLOR) { @@ -3822,12 +3822,12 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const clear_color = env->bg_color.to_linear(); storage->frame.clear_request = false; - } else if (env->bg_mode == VS::ENV_BG_SKYBOX) { + } else if (env->bg_mode == VS::ENV_BG_SKY) { - skybox = storage->skybox_owner.getornull(env->skybox); + sky = storage->sky_owner.getornull(env->sky); - if (skybox) { - env_radiance_tex = skybox->radiance; + if (sky) { + env_radiance_tex = sky->radiance; } storage->frame.clear_request = false; @@ -3878,14 +3878,14 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const glDrawBuffers(1, &gldb); } - if (env && env->bg_mode == VS::ENV_BG_SKYBOX) { + if (env && env->bg_mode == VS::ENV_BG_SKY) { /* if (use_mrt) { - glBindFramebuffer(GL_FRAMEBUFFER,storage->frame.current_rt->buffers.fbo); //switch to alpha fbo for skybox, only diffuse/ambient matters + glBindFramebuffer(GL_FRAMEBUFFER,storage->frame.current_rt->buffers.fbo); //switch to alpha fbo for sky, only diffuse/ambient matters */ - _draw_skybox(skybox, p_cam_projection, p_cam_transform, storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP], env->skybox_scale); + _draw_sky(sky, p_cam_projection, p_cam_transform, storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP], env->sky_scale); } //_render_list_forward(&alpha_render_list,camera_transform,camera_transform_inverse,camera_projection,false,fragment_lighting,true); @@ -4585,14 +4585,14 @@ void RasterizerSceneGLES3::initialize() { { //quad buffers - glGenBuffers(1, &state.skybox_verts); - glBindBuffer(GL_ARRAY_BUFFER, state.skybox_verts); + glGenBuffers(1, &state.sky_verts); + glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 8, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - glGenVertexArrays(1, &state.skybox_array); - glBindVertexArray(state.skybox_array); - glBindBuffer(GL_ARRAY_BUFFER, state.skybox_verts); + glGenVertexArrays(1, &state.sky_array); + glBindVertexArray(state.sky_array); + glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, 0); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_TEX_UV, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, ((uint8_t *)NULL) + sizeof(Vector3)); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 33698fc267..69a7e40604 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -141,8 +141,8 @@ public: GLuint brdf_texture; - GLuint skybox_verts; - GLuint skybox_array; + GLuint sky_verts; + GLuint sky_array; GLuint directional_ubo; @@ -329,16 +329,16 @@ public: VS::EnvironmentBG bg_mode; - RID skybox; - float skybox_scale; + RID sky; + float sky_scale; Color bg_color; float bg_energy; - float skybox_ambient; + float sky_ambient; Color ambient_color; float ambient_energy; - float ambient_skybox_contribution; + float ambient_sky_contribution; int canvas_max_layer; @@ -393,11 +393,11 @@ public: Environment() { bg_mode = VS::ENV_BG_CLEAR_COLOR; - skybox_scale = 1.0; + sky_scale = 1.0; bg_energy = 1.0; - skybox_ambient = 0; + sky_ambient = 0; ambient_energy = 1.0; - ambient_skybox_contribution = 0.0; + ambient_sky_contribution = 0.0; canvas_max_layer = 0; ssr_enabled = false; @@ -455,12 +455,12 @@ public: virtual RID environment_create(); virtual void environment_set_background(RID p_env, VS::EnvironmentBG p_bg); - virtual void environment_set_skybox(RID p_env, RID p_skybox); - virtual void environment_set_skybox_scale(RID p_env, float p_scale); + virtual void environment_set_sky(RID p_env, RID p_sky); + virtual void environment_set_sky_scale(RID p_env, float p_scale); virtual void environment_set_bg_color(RID p_env, const Color &p_color); virtual void environment_set_bg_energy(RID p_env, float p_energy); virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer); - virtual void environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy = 1.0, float p_skybox_contribution = 0.0); + virtual void environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy = 1.0, float p_sky_contribution = 0.0); virtual void environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_far_amount, VS::EnvironmentDOFBlurQuality p_quality); virtual void environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_far_amount, VS::EnvironmentDOFBlurQuality p_quality); @@ -700,7 +700,7 @@ public: _FORCE_INLINE_ void _add_geometry(RasterizerStorageGLES3::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES3::GeometryOwner *p_owner, int p_material, bool p_shadow); - void _draw_skybox(RasterizerStorageGLES3::SkyBox *p_skybox, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_scale); + void _draw_sky(RasterizerStorageGLES3::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_scale); void _setup_environment(Environment *env, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform); void _setup_directional_light(int p_index, const Transform &p_camera_inverse_transformm, bool p_use_shadows); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 8749021a12..08ff687510 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -1213,32 +1213,32 @@ RID RasterizerStorageGLES3::texture_create_radiance_cubemap(RID p_source, int p_ return texture_owner.make_rid(ctex); } -RID RasterizerStorageGLES3::skybox_create() { +RID RasterizerStorageGLES3::sky_create() { - SkyBox *skybox = memnew(SkyBox); - skybox->radiance = 0; - return skybox_owner.make_rid(skybox); + Sky *sky = memnew(Sky); + sky->radiance = 0; + return sky_owner.make_rid(sky); } -void RasterizerStorageGLES3::skybox_set_texture(RID p_skybox, RID p_cube_map, int p_radiance_size) { +void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size) { - SkyBox *skybox = skybox_owner.getornull(p_skybox); - ERR_FAIL_COND(!skybox); + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND(!sky); - if (skybox->cubemap.is_valid()) { - skybox->cubemap = RID(); - glDeleteTextures(1, &skybox->radiance); - skybox->radiance = 0; + if (sky->panorama.is_valid()) { + sky->panorama = RID(); + glDeleteTextures(1, &sky->radiance); + sky->radiance = 0; } - skybox->cubemap = p_cube_map; - if (!skybox->cubemap.is_valid()) + sky->panorama = p_panorama; + if (!sky->panorama.is_valid()) return; //cleared - Texture *texture = texture_owner.getornull(skybox->cubemap); - if (!texture || !(texture->flags & VS::TEXTURE_FLAG_CUBEMAP)) { - skybox->cubemap = RID(); - ERR_FAIL_COND(!texture || !(texture->flags & VS::TEXTURE_FLAG_CUBEMAP)); + Texture *texture = texture_owner.getornull(sky->panorama); + if (!texture) { + sky->panorama = RID(); + ERR_FAIL_COND(!texture); } glBindVertexArray(0); @@ -1263,8 +1263,8 @@ void RasterizerStorageGLES3::skybox_set_texture(RID p_skybox, RID p_cube_map, in } glActiveTexture(GL_TEXTURE1); - glGenTextures(1, &skybox->radiance); - glBindTexture(GL_TEXTURE_2D, skybox->radiance); + glGenTextures(1, &sky->radiance); + glBindTexture(GL_TEXTURE_2D, sky->radiance); GLuint tmp_fb; @@ -1304,11 +1304,12 @@ void RasterizerStorageGLES3::skybox_set_texture(RID p_skybox, RID p_cube_map, in size = p_radiance_size; shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, true); + shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_PANORAMA, true); shaders.cubemap_filter.bind(); while (mm_level) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, skybox->radiance, lod); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->radiance, lod); #ifdef DEBUG_ENABLED GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE); @@ -1331,6 +1332,7 @@ void RasterizerStorageGLES3::skybox_set_texture(RID p_skybox, RID p_cube_map, in mm_level--; } shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DUAL_PARABOLOID, false); + shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_PANORAMA, false); //restore ranges glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); @@ -6116,12 +6118,12 @@ bool RasterizerStorageGLES3::free(RID p_rid) { info.texture_mem -= texture->total_data_size; texture_owner.free(p_rid); memdelete(texture); - } else if (skybox_owner.owns(p_rid)) { - // delete the skybox - SkyBox *skybox = skybox_owner.get(p_rid); - skybox_set_texture(p_rid, RID(), 256); - skybox_owner.free(p_rid); - memdelete(skybox); + } else if (sky_owner.owns(p_rid)) { + // delete the sky + Sky *sky = sky_owner.get(p_rid); + sky_set_texture(p_rid, RID(), 256); + sky_owner.free(p_rid); + memdelete(sky); } else if (shader_owner.owns(p_rid)) { @@ -6478,7 +6480,7 @@ void RasterizerStorageGLES3::initialize() { glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } - //generic quadie for copying without touching skybox + //generic quadie for copying without touching sky { //transform feedback buffers diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 7cae58852d..26b7cea45e 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -308,19 +308,19 @@ public: virtual void texture_set_detect_3d_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); virtual void texture_set_detect_srgb_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); - /* SKYBOX API */ + /* SKY API */ - struct SkyBox : public RID_Data { + struct Sky : public RID_Data { - RID cubemap; + RID panorama; GLuint radiance; int radiance_size; }; - mutable RID_Owner<SkyBox> skybox_owner; + mutable RID_Owner<Sky> sky_owner; - virtual RID skybox_create(); - virtual void skybox_set_texture(RID p_skybox, RID p_cube_map, int p_radiance_size); + virtual RID sky_create(); + virtual void sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size); /* SHADER API */ diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index a87d62f2d7..f3c72a4e6f 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -2,14 +2,14 @@ layout(location=0) in highp vec4 vertex_attrib; -#ifdef USE_CUBEMAP +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) layout(location=4) in vec3 cube_in; #else layout(location=4) in vec2 uv_in; #endif layout(location=5) in vec2 uv2_in; -#ifdef USE_CUBEMAP +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) out vec3 cube_interp; #else out vec2 uv_interp; @@ -19,7 +19,7 @@ out vec2 uv2_interp; void main() { -#ifdef USE_CUBEMAP +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) cube_interp = cube_in; #else uv_interp = uv_in; @@ -30,15 +30,40 @@ void main() { [fragment] +#define M_PI 3.14159265359 -#ifdef USE_CUBEMAP + +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) in vec3 cube_interp; -uniform samplerCube source_cube; //texunit:0 #else in vec2 uv_interp; +#endif + +#ifdef USE_CUBEMAP +uniform samplerCube source_cube; //texunit:0 +#else uniform sampler2D source; //texunit:0 #endif +#ifdef USE_PANORAMA + +vec4 texturePanorama(vec3 normal,sampler2D pano ) { + + vec2 st = vec2( + atan(normal.x, normal.z), + acos(normal.y) + ); + + if(st.x < 0.0) + st.x += M_PI*2.0; + + st/=vec2(M_PI*2.0,M_PI); + + return textureLod(pano,st,0.0); + +} + +#endif float sRGB_gamma_correct(float c){ float a = 0.055; @@ -60,13 +85,19 @@ void main() { //vec4 color = color_interp; -#ifdef USE_CUBEMAP +#ifdef USE_PANORAMA + + vec4 color = texturePanorama( normalize(cube_interp), source ); + +#elif defined(USE_CUBEMAP) vec4 color = texture( source_cube, normalize(cube_interp) ); #else vec4 color = texture( source, uv_interp ); #endif + + #ifdef LINEAR_TO_SRGB //regular Linear -> SRGB conversion vec3 a = vec3(0.055); diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl index 768d20ad22..2aec6380f5 100644 --- a/drivers/gles3/shaders/cubemap_filter.glsl +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -19,8 +19,12 @@ void main() { precision highp float; precision highp int; - +#ifdef USE_PANORAMA +uniform sampler2D source_panorama; //texunit:0 +#else uniform samplerCube source_cube; //texunit:0 +#endif + uniform int face_id; uniform float roughness; in highp vec2 uv_interp; @@ -165,6 +169,26 @@ vec2 Hammersley(uint i, uint N) { uniform bool z_flip; +#ifdef USE_PANORAMA + +vec4 texturePanorama(vec3 normal,sampler2D pano ) { + + vec2 st = vec2( + atan(normal.x, normal.z), + acos(normal.y) + ); + + if(st.x < 0.0) + st.x += M_PI*2.0; + + st/=vec2(M_PI*2.0,M_PI); + + return textureLod(pano,st,0.0); + +} + +#endif + void main() { #ifdef USE_DUAL_PARABOLOID @@ -188,7 +212,12 @@ void main() { #ifdef USE_DIRECT_WRITE +#ifdef USE_PANORAMA + + frag_color=vec4(texturePanorama(N,source_panorama).rgb,1.0); +#else frag_color=vec4(texture(N,source_cube).rgb,1.0); +#endif #else @@ -204,7 +233,11 @@ void main() { float ndotl = clamp(dot(N, L),0.0,1.0); if (ndotl>0.0) { +#ifdef USE_PANORAMA + sum.rgb += texturePanorama(H,source_panorama).rgb *ndotl; +#else sum.rgb += textureLod(source_cube, H, 0.0).rgb *ndotl; +#endif sum.a += ndotl; } } |