Pārlūkot izejas kodu

-Lightmap and lightmap capture support for GLES2
-Added hint to not show some properties when running on low end gfx

Juan Linietsky 7 gadi atpakaļ
vecāks
revīzija
c83742ba86

+ 1 - 0
core/object.h

@@ -116,6 +116,7 @@ enum PropertyUsageFlags {
 	PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19,
 	PROPERTY_USAGE_INTERNAL = 1 << 20,
 	PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated
+	PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
 
 	PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
 	PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,

+ 2 - 0
drivers/dummy/rasterizer_dummy.h

@@ -803,6 +803,8 @@ public:
 		_create_func = _create_current;
 	}
 
+	virtual bool is_low_end() const { return true; }
+
 	RasterizerDummy() {}
 	~RasterizerDummy() {}
 };

+ 2 - 0
drivers/gles2/rasterizer_gles2.h

@@ -66,6 +66,8 @@ public:
 	static void make_current();
 	static void register_config();
 
+	virtual bool is_low_end() const { return true; }
+
 	RasterizerGLES2();
 	~RasterizerGLES2();
 };

+ 88 - 2
drivers/gles2/rasterizer_scene_gles2.cpp

@@ -2028,6 +2028,10 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 		glDisable(GL_BLEND);
 	}
 
+	RasterizerStorageGLES2::Texture *prev_lightmap = NULL;
+	float lightmap_energy = 1.0;
+	bool prev_use_lightmap_capture = false;
+
 	for (int i = 0; i < p_element_count; i++) {
 		RenderList::Element *e = p_elements[i];
 
@@ -2039,6 +2043,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 		LightInstance *light = NULL;
 		ReflectionProbeInstance *refprobe_1 = NULL;
 		ReflectionProbeInstance *refprobe_2 = NULL;
+		RasterizerStorageGLES2::Texture *lightmap = NULL;
+		bool use_lightmap_capture = false;
 
 		if (!p_shadow) {
 
@@ -2147,15 +2153,44 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 				state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, refprobe_1 != NULL);
 				state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, refprobe_2 != NULL);
 				if (refprobe_1 != NULL && refprobe_1 != prev_refprobe_1) {
-					glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
+					glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5);
 					glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_1->cubemap);
 				}
 				if (refprobe_2 != NULL && refprobe_2 != prev_refprobe_2) {
-					glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5);
+					glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6);
 					glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_2->cubemap);
 				}
 				rebind = true;
 			}
+
+			use_lightmap_capture = !unshaded && !accum_pass && !e->instance->lightmap_capture_data.empty();
+
+			if (use_lightmap_capture != prev_use_lightmap_capture) {
+
+				state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, use_lightmap_capture);
+				rebind = true;
+			}
+
+			if (!unshaded && !accum_pass && e->instance->lightmap.is_valid()) {
+
+				lightmap = storage->texture_owner.getornull(e->instance->lightmap);
+				lightmap_energy = 1.0;
+				if (lightmap) {
+					RasterizerStorageGLES2::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base);
+					if (capture) {
+						lightmap_energy = capture->energy;
+					}
+				}
+			}
+
+			if (lightmap != prev_lightmap) {
+				state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, lightmap != NULL);
+				if (lightmap != NULL) {
+					glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
+					glBindTexture(GL_TEXTURE_2D, lightmap->tex_id);
+				}
+				rebind = true;
+			}
 		}
 
 		bool instancing = e->instancing;
@@ -2224,6 +2259,10 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 				if (refprobe_1 || refprobe_2) {
 					_setup_refprobes(refprobe_1, refprobe_2, p_view_transform, p_env);
 				}
+
+				if (lightmap) {
+					state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_ENERGY, lightmap_energy);
+				}
 			}
 
 			state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, view_transform_inverse);
@@ -2239,6 +2278,11 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 
 		state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
 
+		if (use_lightmap_capture) { //this is per instance, must be set always if present
+			glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr());
+			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_CAPTURE_SKY, false);
+		}
+
 		_render_geometry(e);
 
 		prev_geometry = e->geometry;
@@ -2248,6 +2292,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 		prev_light = light;
 		prev_refprobe_1 = refprobe_1;
 		prev_refprobe_2 = refprobe_2;
+		prev_lightmap = lightmap;
+		prev_use_lightmap_capture = use_lightmap_capture;
 	}
 
 	_setup_light_type(NULL, NULL); //clear light stuff
@@ -2260,6 +2306,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 	state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, false);
 	state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, false);
 	state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, false);
+	state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, false);
+	state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, false);
 }
 
 void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy) {
@@ -2818,6 +2866,44 @@ void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) {
 }
 
 bool RasterizerSceneGLES2::free(RID p_rid) {
+
+	if (light_instance_owner.owns(p_rid)) {
+
+		LightInstance *light_instance = light_instance_owner.getptr(p_rid);
+
+		//remove from shadow atlases..
+		for (Set<RID>::Element *E = light_instance->shadow_atlases.front(); E; E = E->next()) {
+			ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(E->get());
+			ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_rid));
+			uint32_t key = shadow_atlas->shadow_owners[p_rid];
+			uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3;
+			uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK;
+
+			shadow_atlas->quadrants[q].shadows.write[s].owner = RID();
+			shadow_atlas->shadow_owners.erase(p_rid);
+		}
+
+		light_instance_owner.free(p_rid);
+		memdelete(light_instance);
+
+	} else if (shadow_atlas_owner.owns(p_rid)) {
+
+		ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(p_rid);
+		shadow_atlas_set_size(p_rid, 0);
+		shadow_atlas_owner.free(p_rid);
+		memdelete(shadow_atlas);
+	} else if (reflection_probe_instance_owner.owns(p_rid)) {
+
+		ReflectionProbeInstance *reflection_instance = reflection_probe_instance_owner.get(p_rid);
+
+		reflection_probe_release_atlas_index(p_rid);
+		reflection_probe_instance_owner.free(p_rid);
+		memdelete(reflection_instance);
+
+	} else {
+		return false;
+	}
+
 	return true;
 }
 

+ 79 - 31
drivers/gles2/rasterizer_storage_gles2.cpp

@@ -496,22 +496,6 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p
 		glTexParameterf(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 	}
 
-//set swizle for older format compatibility
-#ifdef GLES_OVER_GL
-	switch (texture->format) {
-
-		case Image::FORMAT_L8: {
-
-		} break;
-		case Image::FORMAT_LA8: {
-
-		} break;
-		default: {
-
-		} break;
-	}
-#endif
-
 	int mipmaps = ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && img->has_mipmaps()) ? img->get_mipmap_count() + 1 : 1;
 
 	int w = img->get_width();
@@ -591,7 +575,7 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer)
 
 	PoolVector<uint8_t> data;
 
-	int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0);
+	int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1);
 
 	data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
 	PoolVector<uint8_t>::Write wb = data.write();
@@ -3595,46 +3579,100 @@ void RasterizerStorageGLES2::gi_probe_dynamic_data_update(RID p_gi_probe_data, i
 ///////
 
 RID RasterizerStorageGLES2::lightmap_capture_create() {
-	return RID();
+
+	LightmapCapture *capture = memnew(LightmapCapture);
+	return lightmap_capture_data_owner.make_rid(capture);
 }
 
 void RasterizerStorageGLES2::lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) {
-}
 
-AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const {
-	return AABB();
+	LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND(!capture);
+	capture->bounds = p_bounds;
+	capture->instance_change_notify();
 }
+AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const {
 
-void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) {
+	const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND_V(!capture, AABB());
+	return capture->bounds;
 }
+void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) {
+
+	LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND(!capture);
+
+	ERR_FAIL_COND(p_octree.size() == 0 || (p_octree.size() % sizeof(LightmapCaptureOctree)) != 0);
 
+	capture->octree.resize(p_octree.size() / sizeof(LightmapCaptureOctree));
+	if (p_octree.size()) {
+		PoolVector<LightmapCaptureOctree>::Write w = capture->octree.write();
+		PoolVector<uint8_t>::Read r = p_octree.read();
+		copymem(w.ptr(), r.ptr(), p_octree.size());
+	}
+	capture->instance_change_notify();
+}
 PoolVector<uint8_t> RasterizerStorageGLES2::lightmap_capture_get_octree(RID p_capture) const {
-	return PoolVector<uint8_t>();
+
+	const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND_V(!capture, PoolVector<uint8_t>());
+
+	if (capture->octree.size() == 0)
+		return PoolVector<uint8_t>();
+
+	PoolVector<uint8_t> ret;
+	ret.resize(capture->octree.size() * sizeof(LightmapCaptureOctree));
+	{
+		PoolVector<LightmapCaptureOctree>::Read r = capture->octree.read();
+		PoolVector<uint8_t>::Write w = ret.write();
+		copymem(w.ptr(), r.ptr(), ret.size());
+	}
+
+	return ret;
 }
 
 void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) {
+	LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND(!capture);
+	capture->cell_xform = p_xform;
 }
 
 Transform RasterizerStorageGLES2::lightmap_capture_get_octree_cell_transform(RID p_capture) const {
-	return Transform();
+	const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND_V(!capture, Transform());
+	return capture->cell_xform;
 }
 
 void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) {
+	LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND(!capture);
+	capture->cell_subdiv = p_subdiv;
 }
 
 int RasterizerStorageGLES2::lightmap_capture_get_octree_cell_subdiv(RID p_capture) const {
-	return 0;
+	const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND_V(!capture, 0);
+	return capture->cell_subdiv;
 }
 
 void RasterizerStorageGLES2::lightmap_capture_set_energy(RID p_capture, float p_energy) {
+
+	LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND(!capture);
+	capture->energy = p_energy;
 }
 
 float RasterizerStorageGLES2::lightmap_capture_get_energy(RID p_capture) const {
-	return 0.0;
+
+	const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND_V(!capture, 0);
+	return capture->energy;
 }
 
 const PoolVector<RasterizerStorage::LightmapCaptureOctree> *RasterizerStorageGLES2::lightmap_capture_get_octree_ptr(RID p_capture) const {
-	return NULL;
+	const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
+	ERR_FAIL_COND_V(!capture, NULL);
+	return &capture->octree;
 }
 
 ///////
@@ -3773,10 +3811,10 @@ void RasterizerStorageGLES2::instance_add_dependency(RID p_base, RasterizerScene
 			inst = gi_probe_owner.getornull(p_base);
 			ERR_FAIL_COND(!inst);
 		} break;*/
-		/*case VS::INSTANCE_LIGHTMAP_CAPTURE: {
+		case VS::INSTANCE_LIGHTMAP_CAPTURE: {
 			inst = lightmap_capture_data_owner.getornull(p_base);
 			ERR_FAIL_COND(!inst);
-		} break;*/
+		} break;
 		default: {
 			if (!inst) {
 				ERR_FAIL();
@@ -3819,11 +3857,11 @@ void RasterizerStorageGLES2::instance_remove_dependency(RID p_base, RasterizerSc
 		/*case VS::INSTANCE_GI_PROBE: {
 			inst = gi_probe_owner.getornull(p_base);
 			ERR_FAIL_COND(!inst);
-		} break;
+		} break; */
 		case VS::INSTANCE_LIGHTMAP_CAPTURE: {
 			inst = lightmap_capture_data_owner.getornull(p_base);
 			ERR_FAIL_COND(!inst);
-		} break;*/
+		} break;
 		default: {
 
 			if (!inst) {
@@ -4094,6 +4132,8 @@ VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const {
 		return VS::INSTANCE_IMMEDIATE;
 	} else if (reflection_probe_owner.owns(p_rid)) {
 		return VS::INSTANCE_REFLECTION_PROBE;
+	} else if (lightmap_capture_data_owner.owns(p_rid)) {
+		return VS::INSTANCE_LIGHTMAP_CAPTURE;
 	} else {
 		return VS::INSTANCE_NONE;
 	}
@@ -4281,7 +4321,15 @@ bool RasterizerStorageGLES2::free(RID p_rid) {
 		memdelete(reflection_probe);
 
 		return true;
+	} else if (lightmap_capture_data_owner.owns(p_rid)) {
 
+		// delete the texture
+		LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid);
+		lightmap_capture->instance_remove_deps();
+
+		lightmap_capture_data_owner.free(p_rid);
+		memdelete(lightmap_capture);
+		return true;
 	} else {
 		return false;
 	}

+ 15 - 0
drivers/gles2/rasterizer_storage_gles2.h

@@ -1046,6 +1046,21 @@ public:
 
 	/* LIGHTMAP */
 
+	struct LightmapCapture : public Instanciable {
+
+		PoolVector<LightmapCaptureOctree> octree;
+		AABB bounds;
+		Transform cell_xform;
+		int cell_subdiv;
+		float energy;
+		LightmapCapture() {
+			energy = 1.0;
+			cell_subdiv = 1;
+		}
+	};
+
+	mutable RID_Owner<LightmapCapture> lightmap_capture_data_owner;
+
 	virtual RID lightmap_capture_create();
 	virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds);
 	virtual AABB lightmap_capture_get_bounds(RID p_capture) const;

+ 69 - 19
drivers/gles2/shaders/scene.glsl

@@ -27,15 +27,15 @@ attribute vec3 normal_attrib; // attrib:1
 attribute vec4 tangent_attrib; // attrib:2
 #endif
 
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
 attribute vec4 color_attrib; // attrib:3
 #endif
 
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
 attribute vec2 uv_attrib; // attrib:4
 #endif
 
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
 attribute vec2 uv2_attrib; // attrib:5
 #endif
 
@@ -102,15 +102,15 @@ varying vec3 tangent_interp;
 varying vec3 binormal_interp;
 #endif
 
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
 varying vec4 color_interp;
 #endif
 
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
 varying vec2 uv_interp;
 #endif
 
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
 varying vec2 uv2_interp;
 #endif
 
@@ -317,18 +317,18 @@ void main() {
 	vec3 binormal = normalize(cross(normal, tangent) * binormalf);
 #endif
 
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
 	color_interp = color_attrib;
 #ifdef USE_INSTANCING
 	color_interp *= instance_color;
 #endif
 #endif
 
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
 	uv_interp = uv_attrib;
 #endif
 
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
 	uv2_interp = uv2_attrib;
 #endif
 
@@ -521,7 +521,7 @@ VERTEX_SHADER_CODE
 
 #if defined(LIGHT_USE_PSSM4)
 	shadow_coord3 = light_shadow_matrix3 * vi4;
-	shadow_coord3 = light_shadow_matrix3 * vi4;
+	shadow_coord4 = light_shadow_matrix4 * vi4;
 
 #endif
 
@@ -612,7 +612,7 @@ uniform mat4 world_transform;
 
 uniform highp float time;
 
-#ifdef SCREEN_UV_USED
+#if defined(SCREEN_UV_USED)
 uniform vec2 screen_pixel_size;
 #endif
 
@@ -643,7 +643,7 @@ uniform mat4 refprobe1_local_matrix;
 
 uniform bool refprobe1_exterior;
 
-uniform highp samplerCube reflection_probe1; //texunit:-4
+uniform highp samplerCube reflection_probe1; //texunit:-5
 
 uniform float refprobe1_intensity;
 uniform vec4 refprobe1_ambient;
@@ -670,7 +670,7 @@ uniform mat4 refprobe2_local_matrix;
 
 uniform bool refprobe2_exterior;
 
-uniform highp samplerCube reflection_probe2; //texunit:-5
+uniform highp samplerCube reflection_probe2; //texunit:-6
 
 uniform float refprobe2_intensity;
 uniform vec4 refprobe2_ambient;
@@ -773,7 +773,16 @@ void reflection_process(samplerCube reflection_map,
 
 #endif //use refprobe 1 or 2
 
+#ifdef USE_LIGHTMAP
+uniform mediump sampler2D lightmap; //texunit:-4
+uniform mediump float lightmap_energy;
+#endif
+
+#ifdef USE_LIGHTMAP_CAPTURE
+uniform mediump vec4[12] lightmap_captures;
+uniform bool lightmap_capture_sky;
 
+#endif
 
 #ifdef USE_RADIANCE_MAP
 
@@ -866,15 +875,15 @@ varying vec3 tangent_interp;
 varying vec3 binormal_interp;
 #endif
 
-#ifdef ENABLE_COLOR_INTERP
+#if defined(ENABLE_COLOR_INTERP)
 varying vec4 color_interp;
 #endif
 
-#ifdef ENABLE_UV_INTERP
+#if defined(ENABLE_UV_INTERP)
 varying vec2 uv_interp;
 #endif
 
-#ifdef ENABLE_UV2_INTERP
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
 varying vec2 uv2_interp;
 #endif
 
@@ -1285,11 +1294,11 @@ void main() {
 #endif
 	float normaldepth = 1.0;
 
-#ifdef ALPHA_SCISSOR_USED
+#if defined(ALPHA_SCISSOR_USED)
 	float alpha_scissor = 0.5;
 #endif
 
-#ifdef SCREEN_UV_USED
+#if defined(SCREEN_UV_USED)
 	vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
 #endif
 
@@ -1319,7 +1328,7 @@ FRAGMENT_SHADER_CODE
 
 	vec3 eye_position = -normalize(vertex_interp);
 
-#ifdef ALPHA_SCISSOR_USED
+#if defined(ALPHA_SCISSOR_USED)
 	if (alpha < alpha_scissor) {
 		discard;
 	}
@@ -1404,6 +1413,47 @@ FRAGMENT_SHADER_CODE
 
 #endif //use reflection probe 1
 
+#ifdef USE_LIGHTMAP
+	//ambient light will come entirely from lightmap is lightmap is used
+	ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy;
+#endif
+
+#ifdef USE_LIGHTMAP_CAPTURE
+	{
+		vec3 cone_dirs[12] = vec3[](
+				vec3(0, 0, 1),
+				vec3(0.866025, 0, 0.5),
+				vec3(0.267617, 0.823639, 0.5),
+				vec3(-0.700629, 0.509037, 0.5),
+				vec3(-0.700629, -0.509037, 0.5),
+				vec3(0.267617, -0.823639, 0.5),
+				vec3(0, 0, -1),
+				vec3(0.866025, 0, -0.5),
+				vec3(0.267617, 0.823639, -0.5),
+				vec3(-0.700629, 0.509037, -0.5),
+				vec3(-0.700629, -0.509037, -0.5),
+				vec3(0.267617, -0.823639, -0.5));
+
+		vec3 local_normal = normalize(camera_matrix * vec4(normal, 0.0)).xyz;
+		vec4 captured = vec4(0.0);
+		float sum = 0.0;
+		for (int i = 0; i < 12; i++) {
+			float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect
+			captured += lightmap_captures[i] * amount;
+			sum += amount;
+		}
+
+		captured /= sum;
+
+		if (lightmap_capture_sky) {
+			ambient_light = mix(ambient_light, captured.rgb, captured.a);
+		} else {
+			ambient_light = captured.rgb;
+		}
+	}
+#endif
+
+
 #endif //BASE PASS
 
 //

+ 2 - 0
drivers/gles3/rasterizer_gles3.h

@@ -66,6 +66,8 @@ public:
 	static void make_current();
 	static void register_config();
 
+	virtual bool is_low_end() const { return false; }
+
 	RasterizerGLES3();
 	~RasterizerGLES3();
 };

+ 3 - 3
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -1029,7 +1029,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer)
 
 	PoolVector<uint8_t> data;
 
-	int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0);
+	int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1);
 
 	data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
 	PoolVector<uint8_t>::Write wb = data.write();
@@ -7364,7 +7364,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
 		GIProbeData *gi_probe_data = gi_probe_data_owner.get(p_rid);
 
 		glDeleteTextures(1, &gi_probe_data->tex_id);
-		gi_probe_owner.free(p_rid);
+		gi_probe_data_owner.free(p_rid);
 		memdelete(gi_probe_data);
 	} else if (lightmap_capture_data_owner.owns(p_rid)) {
 
@@ -7372,7 +7372,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
 		LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid);
 		lightmap_capture->instance_remove_deps();
 
-		gi_probe_owner.free(p_rid);
+		lightmap_capture_data_owner.free(p_rid);
 		memdelete(lightmap_capture);
 
 	} else if (canvas_occluder_owner.owns(p_rid)) {

+ 3 - 0
editor/editor_inspector.cpp

@@ -1449,6 +1449,9 @@ void EditorInspector::update_tree() {
 		} else if (!(p.usage & PROPERTY_USAGE_EDITOR))
 			continue;
 
+		if (p.usage & PROPERTY_USAGE_HIGH_END_GFX && VS::get_singleton()->is_low_end())
+			continue; //do not show this property in low end gfx
+
 		if (p.name == "script" && (hide_script || bool(object->call("_hide_script_from_inspector")))) {
 			continue;
 		}

+ 1 - 4
gles_builders.py

@@ -68,12 +68,9 @@ def include_file_in_legacygl_header(filename, header_data, depth):
 
             line = fs.readline()
 
-        if line.find("#ifdef ") != -1 or line.find("#elif defined(") != -1:
+        if line.find("#ifdef ") != -1:
             if line.find("#ifdef ") != -1:
                 ifdefline = line.replace("#ifdef ", "").strip()
-            else:
-                ifdefline = line.replace("#elif defined(", "").strip()
-                ifdefline = ifdefline.replace(")", "").strip()
 
             if line.find("_EN_") != -1:
                 enumbase = ifdefline[:ifdefline.find("_EN_")]

+ 12 - 0
scene/resources/material.cpp

@@ -1369,6 +1369,12 @@ void SpatialMaterial::_validate_feature(const String &text, Feature feature, Pro
 	}
 }
 
+void SpatialMaterial::_validate_high_end(const String &text, PropertyInfo &property) const {
+	if (property.name.begins_with(text)) {
+		property.usage |= PROPERTY_USAGE_HIGH_END_GFX;
+	}
+}
+
 void SpatialMaterial::_validate_property(PropertyInfo &property) const {
 	_validate_feature("normal", FEATURE_NORMAL_MAPPING, property);
 	_validate_feature("emission", FEATURE_EMISSION, property);
@@ -1382,6 +1388,12 @@ void SpatialMaterial::_validate_property(PropertyInfo &property) const {
 	_validate_feature("refraction", FEATURE_REFRACTION, property);
 	_validate_feature("detail", FEATURE_DETAIL, property);
 
+	_validate_high_end("refraction", property);
+	_validate_high_end("subsurf_scatter", property);
+	_validate_high_end("anisotropy", property);
+	_validate_high_end("clearcoat", property);
+	_validate_high_end("depth", property);
+
 	if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) {
 		property.usage = 0;
 	}

+ 2 - 0
scene/resources/material.h

@@ -442,6 +442,8 @@ private:
 
 	static Ref<SpatialMaterial> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff
 
+	void _validate_high_end(const String &text, PropertyInfo &property) const;
+
 protected:
 	static void _bind_methods();
 	void _validate_property(PropertyInfo &property) const;

+ 3 - 0
scene/resources/texture.cpp

@@ -207,6 +207,9 @@ void ImageTexture::set_flags(uint32_t p_flags) {
 
 	flags=p_flags|cube;	*/
 	flags = p_flags;
+	if (w == 0 || h == 0) {
+		return; //uninitialized, do not set to texture
+	}
 	VisualServer::get_singleton()->texture_set_flags(texture, p_flags);
 }
 

+ 2 - 0
servers/visual/rasterizer.h

@@ -1107,6 +1107,8 @@ public:
 	virtual void end_frame(bool p_swap_buffers) = 0;
 	virtual void finalize() = 0;
 
+	virtual bool is_low_end() const = 0;
+
 	virtual ~Rasterizer() {}
 };
 

+ 3 - 0
servers/visual/visual_server_raster.cpp

@@ -189,6 +189,9 @@ void VisualServerRaster::call_set_use_vsync(bool p_enable) {
 	OS::get_singleton()->_set_use_vsync(p_enable);
 }
 
+bool VisualServerRaster::is_low_end() const {
+	return VSG::rasterizer->is_low_end();
+}
 VisualServerRaster::VisualServerRaster() {
 
 	VSG::canvas = memnew(VisualServerCanvas);

+ 2 - 0
servers/visual/visual_server_raster.h

@@ -690,6 +690,8 @@ public:
 
 	virtual void call_set_use_vsync(bool p_enable);
 
+	virtual bool is_low_end() const;
+
 	VisualServerRaster();
 	~VisualServerRaster();
 

+ 4 - 0
servers/visual/visual_server_wrap_mt.h

@@ -609,6 +609,10 @@ public:
 
 	static void set_use_vsync_callback(bool p_enable);
 
+	virtual bool is_low_end() const {
+		return visual_server->is_low_end();
+	}
+
 	VisualServerWrapMT(VisualServer *p_contained, bool p_create_thread);
 	~VisualServerWrapMT();
 

+ 2 - 0
servers/visual_server.h

@@ -1037,6 +1037,8 @@ public:
 
 	virtual void call_set_use_vsync(bool p_enable) = 0;
 
+	virtual bool is_low_end() const = 0;
+
 	VisualServer();
 	virtual ~VisualServer();
 };