فهرست منبع

-Added .hdr format support
-Added default environment editor setting
-Added environment created by default in new projects
-Removed default light and ambient from spatial editor, to make the editor more PBR compliant

Juan Linietsky 8 سال پیش
والد
کامیت
bb20f230ad

+ 34 - 0
core/color.h

@@ -83,6 +83,40 @@ struct Color {
 		return res;
 	}
 
+	_FORCE_INLINE_ uint32_t to_rgbe9995() const {
+
+		const float pow2to9 = 512.0f;
+		const float B = 15.0f;
+		//const float Emax = 31.0f;
+		const float N = 9.0f;
+
+		float sharedexp = 65408.000f; //(( pow2to9  - 1.0f)/ pow2to9)*powf( 2.0f, 31.0f - 15.0f);
+
+		float cRed = MAX(0.0f, MIN(sharedexp, r));
+		float cGreen = MAX(0.0f, MIN(sharedexp, g));
+		float cBlue = MAX(0.0f, MIN(sharedexp, b));
+
+		float cMax = MAX(cRed, MAX(cGreen, cBlue));
+
+		// expp = MAX(-B - 1, log2(maxc)) + 1 + B
+
+		float expp = MAX(-B - 1.0f, floor(Math::log(cMax) / Math_LN2)) + 1.0f + B;
+
+		float sMax = (float)floor((cMax / Math::pow(2.0f, expp - B - N)) + 0.5f);
+
+		float exps = expp + 1.0f;
+
+		if (0.0 <= sMax && sMax < pow2to9) {
+			exps = expp;
+		}
+
+		float sRed = Math::floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
+		float sGreen = Math::floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
+		float sBlue = Math::floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
+
+		return (uint32_t(Math::fast_ftoi(sRed)) & 0x1FF) | ((uint32_t(Math::fast_ftoi(sGreen)) & 0x1FF) << 9) | ((uint32_t(Math::fast_ftoi(sBlue)) & 0x1FF) << 18) | ((uint32_t(Math::fast_ftoi(exps)) & 0x1F) << 27);
+	}
+
 	_FORCE_INLINE_ Color blend(const Color &p_over) const {
 
 		Color res;

+ 1 - 29
core/image.cpp

@@ -1981,35 +1981,7 @@ void Image::put_pixel(int p_x, int p_y, const Color &p_color) {
 		} break;
 		case FORMAT_RGBE9995: {
 
-			const float pow2to9 = 512.0f;
-			const float B = 7.0f;
-			//const float Emax = 31.0f;
-			const float N = 9.0f;
-
-			float sharedexp = 65408.000f; //(( pow2to9  - 1.0f)/ pow2to9)*powf( 2.0f, 31.0f - 15.0f);
-
-			float cRed = MAX(0.0f, MIN(sharedexp, p_color.r));
-			float cGreen = MAX(0.0f, MIN(sharedexp, p_color.g));
-			float cBlue = MAX(0.0f, MIN(sharedexp, p_color.b));
-
-			float cMax = MAX(cRed, MAX(cGreen, cBlue));
-
-			// expp = MAX(-B - 1, log2(maxc)) + 1 + B
-			float expp = MAX(-B - 1.0f, floor(Math::log(cMax) / Math::log(2.0))) + 1.0f + B;
-
-			float sMax = (float)floor((cMax / Math::pow(2.0f, expp - B - N)) + 0.5f);
-
-			float exps = expp + 1.0f;
-
-			if (0.0 <= sMax && sMax < pow2to9) {
-				exps = expp;
-			}
-
-			float sRed = (cRed / pow(2.0f, exps - B - N)) + 0.5f;
-			float sGreen = (cGreen / pow(2.0f, exps - B - N)) + 0.5f;
-			float sBlue = (cBlue / pow(2.0f, exps - B - N)) + 0.5f;
-
-			((uint32_t *)ptr)[ofs] = ((uint32_t)(sRed)&0x1FF) | (((uint32_t)(sGreen)&0x1FF) << 9) | (((uint32_t)(sBlue)&0x1FF) << 18) | (((uint32_t)(exps)&0x1F) << 27);
+			((uint32_t *)ptr)[ofs] = p_color.to_rgbe9995();
 
 		} break;
 		default: {

+ 21 - 0
core/os/file_access.cpp

@@ -252,6 +252,27 @@ double FileAccess::get_double() const {
 	return m.d;
 };
 
+String FileAccess::get_token() const {
+
+	CharString token;
+
+	CharType c = get_8();
+
+	while (!eof_reached()) {
+
+		if (c <= ' ') {
+			if (!token.empty())
+				break;
+		} else {
+			token.push_back(c);
+		}
+		c = get_8();
+	}
+
+	token.push_back(0);
+	return String::utf8(token.get_data());
+}
+
 String FileAccess::get_line() const {
 
 	CharString line;

+ 1 - 0
core/os/file_access.h

@@ -106,6 +106,7 @@ public:
 
 	virtual int get_buffer(uint8_t *p_dst, int p_length) const; ///< get an array of bytes
 	virtual String get_line() const;
+	virtual String get_token() const;
 	virtual Vector<String> get_csv_line(String delim = ",") const;
 
 	/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)

+ 146 - 13
core/variant_parser.cpp

@@ -694,6 +694,106 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
 				return ERR_PARSE_ERROR;
 			}
 
+			return OK;
+		} else if (id == "Object") {
+
+			get_token(p_stream, token, line, r_err_str);
+			if (token.type != TK_PARENTHESIS_OPEN) {
+				r_err_str = "Expected '('";
+				return ERR_PARSE_ERROR;
+			}
+
+			get_token(p_stream, token, line, r_err_str);
+
+			if (token.type != TK_IDENTIFIER) {
+				r_err_str = "Expected identifier with type of object";
+				return ERR_PARSE_ERROR;
+			}
+
+			String type = token.value;
+
+			Object *obj = ClassDB::instance(type);
+
+			if (!obj) {
+				r_err_str = "Can't instance Object() of type: " + type;
+				return ERR_PARSE_ERROR;
+			}
+
+			get_token(p_stream, token, line, r_err_str);
+			if (token.type != TK_COMMA) {
+				r_err_str = "Expected ',' after object type";
+				return ERR_PARSE_ERROR;
+			}
+
+			bool at_key = true;
+			String key;
+			Token token;
+			bool need_comma = false;
+
+			while (true) {
+
+				if (p_stream->is_eof()) {
+					r_err_str = "Unexpected End of File while parsing Object()";
+					return ERR_FILE_CORRUPT;
+				}
+
+				if (at_key) {
+
+					Error err = get_token(p_stream, token, line, r_err_str);
+					if (err != OK)
+						return err;
+
+					if (token.type == TK_PARENTHESIS_CLOSE) {
+
+						return OK;
+					}
+
+					if (need_comma) {
+
+						if (token.type != TK_COMMA) {
+
+							r_err_str = "Expected '}' or ','";
+							return ERR_PARSE_ERROR;
+						} else {
+							need_comma = false;
+							continue;
+						}
+					}
+
+					get_token(p_stream, token, line, r_err_str);
+					if (token.type != TK_STRING) {
+						r_err_str = "Expected property name as string";
+						return ERR_PARSE_ERROR;
+					}
+
+					key = token.value;
+
+					err = get_token(p_stream, token, line, r_err_str);
+
+					if (err != OK)
+						return err;
+					if (token.type != TK_COLON) {
+
+						r_err_str = "Expected ':'";
+						return ERR_PARSE_ERROR;
+					}
+					at_key = false;
+				} else {
+
+					Error err = get_token(p_stream, token, line, r_err_str);
+					if (err != OK)
+						return err;
+
+					Variant v;
+					err = parse_value(token, v, p_stream, line, r_err_str, p_res_parser);
+					if (err)
+						return err;
+					obj->set(key, v);
+					need_comma = true;
+					at_key = true;
+				}
+			}
+
 			return OK;
 
 		} else if (id == "Resource" || id == "SubResource" || id == "ExtResource") {
@@ -1611,30 +1711,63 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
 
 		case Variant::OBJECT: {
 
-			RES res = p_variant;
-			if (res.is_null()) {
+			Object *obj = p_variant;
+
+			if (!obj) {
 				p_store_string_func(p_store_string_ud, "null");
 				break; // don't save it
 			}
 
-			String res_text;
+			RES res = p_variant;
+			if (res.is_valid()) {
+				//is resource
+				String res_text;
 
-			if (p_encode_res_func) {
+				//try external function
+				if (p_encode_res_func) {
 
-				res_text = p_encode_res_func(p_encode_res_ud, res);
-			}
+					res_text = p_encode_res_func(p_encode_res_ud, res);
+				}
+
+				//try path because it's a file
+				if (res_text == String() && res->get_path().is_resource_file()) {
 
-			if (res_text == String() && res->get_path().is_resource_file()) {
+					//external resource
+					String path = res->get_path();
+					res_text = "Resource( \"" + path + "\")";
+				}
 
-				//external resource
-				String path = res->get_path();
-				res_text = "Resource( \"" + path + "\")";
+				//could come up with some sort of text
+				if (res_text != String()) {
+					p_store_string_func(p_store_string_ud, res_text);
+					break;
+				}
 			}
 
-			if (res_text == String())
-				res_text = "null";
+			//store as generic object
+
+			p_store_string_func(p_store_string_ud, "Object(" + obj->get_class() + ",");
+
+			List<PropertyInfo> props;
+			obj->get_property_list(&props);
+			bool first = true;
+			for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+				if (E->get().usage & PROPERTY_USAGE_STORAGE || E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE) {
+					//must be serialized
+
+					if (first) {
+						first = false;
+					} else {
+						p_store_string_func(p_store_string_ud, ",");
+					}
+
+					p_store_string_func(p_store_string_ud, "\"" + E->get().name + "\":");
+					write(obj->get(E->get().name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud);
+				}
+			}
 
-			p_store_string_func(p_store_string_ud, res_text);
+			p_store_string_func(p_store_string_ud, ")\n");
 
 		} break;
 

+ 28 - 4
editor/editor_node.cpp

@@ -889,6 +889,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
 	}
 
 	editor_data.apply_changes_in_editors();
+	_save_default_environment();
 
 	_set_scene_metadata(p_file, idx);
 
@@ -950,7 +951,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
 
 		_dialog_display_file_error(p_file, err);
 	}
-};
+}
 
 void EditorNode::_import_action(const String &p_action) {
 #if 0
@@ -1113,6 +1114,7 @@ void EditorNode::_dialog_action(String p_file) {
 			if (file->get_mode() == EditorFileDialog::MODE_SAVE_FILE) {
 
 				//_save_scene(p_file);
+				_save_default_environment();
 				_save_scene_with_preview(p_file);
 			}
 
@@ -1122,6 +1124,7 @@ void EditorNode::_dialog_action(String p_file) {
 			if (file->get_mode() == EditorFileDialog::MODE_SAVE_FILE) {
 
 				//_save_scene(p_file);
+				_save_default_environment();
 				_save_scene_with_preview(p_file);
 				_call_build();
 				_run(true);
@@ -1375,6 +1378,17 @@ void EditorNode::_property_editor_back() {
 		_edit_current();
 }
 
+void EditorNode::_save_default_environment() {
+
+	Ref<Environment> fallback = get_scene_root()->get_world()->get_fallback_environment();
+
+	if (fallback.is_valid() && fallback->get_path().is_resource_file()) {
+		Map<RES, bool> processed;
+		_find_and_save_edited_subresources(fallback.ptr(), processed, 0);
+		save_resource_in_path(fallback, fallback->get_path());
+	}
+}
+
 void EditorNode::_imported(Node *p_node) {
 
 	/*
@@ -1453,11 +1467,16 @@ void EditorNode::_edit_current() {
 
 		Node *current_node = current_obj->cast_to<Node>();
 		ERR_FAIL_COND(!current_node);
-		ERR_FAIL_COND(!current_node->is_inside_tree());
+		//		ERR_FAIL_COND(!current_node->is_inside_tree());
 
 		property_editor->edit(current_node);
-		node_dock->set_node(current_node);
-		scene_tree_dock->set_selected(current_node);
+		if (current_node->is_inside_tree()) {
+			node_dock->set_node(current_node);
+			scene_tree_dock->set_selected(current_node);
+		} else {
+			node_dock->set_node(NULL);
+			scene_tree_dock->set_selected(NULL);
+		}
 		object_menu->get_popup()->clear();
 
 		//top_pallete->set_current_tab(0);
@@ -1951,6 +1970,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 					}
 				} // else: ignore new scenes
 			}
+
+			_save_default_environment();
 		} break;
 		case FILE_SAVE_BEFORE_RUN: {
 			if (!p_confirmed) {
@@ -2384,6 +2405,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 
 		} break;
 		case RUN_PLAY_SCENE: {
+
+			_save_default_environment();
 			_menu_option_confirm(RUN_STOP, true);
 			_call_build();
 			_run(true);
@@ -5237,6 +5260,7 @@ EditorNode::EditorNode() {
 	p->add_separator();
 	p->add_item(TTR("Project Settings"), RUN_SETTINGS);
 	p->add_separator();
+
 #ifdef OSX_ENABLED
 	p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q);
 #else

+ 1 - 0
editor/editor_node.h

@@ -593,6 +593,7 @@ private:
 
 	static int plugin_init_callback_count;
 	static EditorPluginInitializeCallback plugin_init_callbacks[MAX_INIT_CALLBACKS];
+	void _save_default_environment();
 
 	void _call_build();
 	static int build_callback_count;

+ 2 - 2
editor/plugins/color_ramp_editor_plugin.cpp

@@ -46,10 +46,10 @@ ColorRampEditorPlugin::ColorRampEditorPlugin(EditorNode *p_node) {
 
 void ColorRampEditorPlugin::edit(Object *p_object) {
 
-	ColorRamp *color_ramp = p_object->cast_to<ColorRamp>();
+	Gradient *color_ramp = p_object->cast_to<Gradient>();
 	if (!color_ramp)
 		return;
-	color_ramp_ref = Ref<ColorRamp>(color_ramp);
+	color_ramp_ref = Ref<Gradient>(color_ramp);
 	ramp_editor->set_points(color_ramp_ref->get_points());
 }
 

+ 1 - 1
editor/plugins/color_ramp_editor_plugin.h

@@ -39,7 +39,7 @@ class ColorRampEditorPlugin : public EditorPlugin {
 	GDCLASS(ColorRampEditorPlugin, EditorPlugin);
 
 	bool _2d;
-	Ref<ColorRamp> color_ramp_ref;
+	Ref<Gradient> color_ramp_ref;
 	ColorRampEdit *ramp_editor;
 	EditorNode *editor;
 

+ 0 - 151
editor/plugins/spatial_editor_plugin.cpp

@@ -2493,17 +2493,11 @@ Dictionary SpatialEditor::get_state() const {
 
 	d["viewports"] = vpdata;
 
-	d["default_light"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT));
-	d["ambient_light_color"] = settings_ambient_color->get_pick_color();
-
-	d["default_srgb"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_DEFAULT_SRGB));
 	d["show_grid"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID));
 	d["show_origin"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN));
 	d["fov"] = get_fov();
 	d["znear"] = get_znear();
 	d["zfar"] = get_zfar();
-	d["deflight_rot_x"] = settings_default_light_rot_x;
-	d["deflight_rot_y"] = settings_default_light_rot_y;
 
 	return d;
 }
@@ -2565,26 +2559,6 @@ void SpatialEditor::set_state(const Dictionary &p_state) {
 	if (d.has("fov"))
 		settings_fov->set_value(float(d["fov"]));
 
-	if (d.has("default_light")) {
-		bool use = d["default_light"];
-
-		bool existing = light_instance.is_valid();
-		if (use != existing) {
-			if (existing) {
-				VisualServer::get_singleton()->free(light_instance);
-				light_instance = RID();
-			} else {
-				light_instance = VisualServer::get_singleton()->instance_create2(light, get_tree()->get_root()->get_world()->get_scenario());
-				VisualServer::get_singleton()->instance_set_transform(light_instance, light_transform);
-			}
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT), light_instance.is_valid());
-		}
-	}
-	if (d.has("ambient_light_color")) {
-		settings_ambient_color->set_pick_color(d["ambient_light_color"]);
-		//viewport_environment->fx_set_param(Environment::FX_PARAM_AMBIENT_LIGHT_COLOR,d["ambient_light_color"]);
-	}
-
 	if (d.has("default_srgb")) {
 		bool use = d["default_srgb"];
 
@@ -2607,13 +2581,6 @@ void SpatialEditor::set_state(const Dictionary &p_state) {
 			VisualServer::get_singleton()->instance_set_visible(origin_instance, use);
 		}
 	}
-
-	if (d.has("deflight_rot_x"))
-		settings_default_light_rot_x = d["deflight_rot_x"];
-	if (d.has("deflight_rot_y"))
-		settings_default_light_rot_y = d["deflight_rot_y"];
-
-	_update_default_light_angle();
 }
 
 void SpatialEditor::edit(Spatial *p_spatial) {
@@ -2749,38 +2716,6 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
 
 			xform_dialog->popup_centered(Size2(200, 200));
 
-		} break;
-		case MENU_VIEW_USE_DEFAULT_LIGHT: {
-
-			bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option));
-
-			if (is_checked) {
-				VisualServer::get_singleton()->free(light_instance);
-				light_instance = RID();
-			} else {
-				light_instance = VisualServer::get_singleton()->instance_create2(light, get_tree()->get_root()->get_world()->get_scenario());
-				VisualServer::get_singleton()->instance_set_transform(light_instance, light_transform);
-
-				_update_default_light_angle();
-			}
-
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), light_instance.is_valid());
-
-		} break;
-		case MENU_VIEW_USE_DEFAULT_SRGB: {
-
-			bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option));
-
-			if (is_checked) {
-				//viewport_environment->set_enable_fx(Environment::FX_SRGB,false);
-			} else {
-				//viewport_environment->set_enable_fx(Environment::FX_SRGB,true);
-			}
-
-			is_checked = !is_checked;
-
-			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), is_checked);
-
 		} break;
 		case MENU_VIEW_USE_1_VIEWPORT: {
 
@@ -2993,14 +2928,6 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
 
 void SpatialEditor::_init_indicators() {
 
-	//make sure that the camera indicator is not selectable
-	light = VisualServer::get_singleton()->light_create(VisualServer::LIGHT_DIRECTIONAL);
-	//VisualServer::get_singleton()->light_set_shadow( light, true );
-	light_instance = VisualServer::get_singleton()->instance_create2(light, get_tree()->get_root()->get_world()->get_scenario());
-
-	light_transform.rotate(Vector3(1, 0, 0), -Math_PI / 5.0);
-	VisualServer::get_singleton()->instance_set_transform(light_instance, light_transform);
-
 	//RID mat = VisualServer::get_singleton()->fixed_material_create();
 	///VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true);
 	//VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true);
@@ -3258,8 +3185,6 @@ void SpatialEditor::_finish_indicators() {
 		VisualServer::get_singleton()->free(grid_instance[i]);
 		VisualServer::get_singleton()->free(grid[i]);
 	}
-	VisualServer::get_singleton()->free(light_instance);
-	VisualServer::get_singleton()->free(light);
 	//VisualServer::get_singleton()->free(poly);
 	//VisualServer::get_singleton()->free(indicators_instance);
 	//VisualServer::get_singleton()->free(indicators);
@@ -3351,14 +3276,12 @@ void SpatialEditor::_notification(int p_what) {
 		_menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
 
 		get_tree()->connect("node_removed", this, "_node_removed");
-		VS::get_singleton()->scenario_set_fallback_environment(get_viewport()->find_world()->get_scenario(), viewport_environment->get_rid());
 	}
 
 	if (p_what == NOTIFICATION_ENTER_TREE) {
 
 		gizmos = memnew(SpatialEditorGizmos);
 		_init_indicators();
-		_update_default_light_angle();
 	}
 
 	if (p_what == NOTIFICATION_EXIT_TREE) {
@@ -3481,8 +3404,6 @@ void SpatialEditor::_bind_methods() {
 	ClassDB::bind_method("_xform_dialog_action", &SpatialEditor::_xform_dialog_action);
 	ClassDB::bind_method("_get_editor_data", &SpatialEditor::_get_editor_data);
 	ClassDB::bind_method("_request_gizmo", &SpatialEditor::_request_gizmo);
-	ClassDB::bind_method("_default_light_angle_input", &SpatialEditor::_default_light_angle_input);
-	ClassDB::bind_method("_update_ambient_light_color", &SpatialEditor::_update_ambient_light_color);
 	ClassDB::bind_method("_toggle_maximize_view", &SpatialEditor::_toggle_maximize_view);
 
 	ADD_SIGNAL(MethodInfo("transform_key_request"));
@@ -3517,43 +3438,6 @@ void SpatialEditor::clear() {
 	}
 
 	view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), true);
-
-	settings_default_light_rot_x = Math_PI * 0.3;
-	settings_default_light_rot_y = Math_PI * 0.2;
-
-	//viewport_environment->fx_set_param(Environment::FX_PARAM_AMBIENT_LIGHT_COLOR,Color(0.15,0.15,0.15));
-	settings_ambient_color->set_pick_color(Color(0.15, 0.15, 0.15));
-	if (!light_instance.is_valid())
-		_menu_item_pressed(MENU_VIEW_USE_DEFAULT_LIGHT);
-
-	_update_default_light_angle();
-}
-
-void SpatialEditor::_update_ambient_light_color(const Color &p_color) {
-
-	//viewport_environment->fx_set_param(Environment::FX_PARAM_AMBIENT_LIGHT_COLOR,settings_ambient_color->get_color());
-}
-
-void SpatialEditor::_update_default_light_angle() {
-
-	Transform t;
-	t.basis.rotate(Vector3(1, 0, 0), -settings_default_light_rot_x);
-	t.basis.rotate(Vector3(0, 1, 0), -settings_default_light_rot_y);
-	settings_dlight->set_transform(t);
-	if (light_instance.is_valid()) {
-		VS::get_singleton()->instance_set_transform(light_instance, t);
-	}
-}
-
-void SpatialEditor::_default_light_angle_input(const Ref<InputEvent> &p_event) {
-
-	Ref<InputEventMouseMotion> mm = p_event;
-	if (mm.is_valid() && mm->get_button_mask() & (0x1 | 0x2 | 0x4)) {
-
-		settings_default_light_rot_y = Math::fposmod(settings_default_light_rot_y - mm->get_relative().x * 0.01, Math_PI * 2.0);
-		settings_default_light_rot_x = Math::fposmod(settings_default_light_rot_x - mm->get_relative().y * 0.01, Math_PI * 2.0);
-		_update_default_light_angle();
-	}
 }
 
 SpatialEditor::SpatialEditor(EditorNode *p_editor) {
@@ -3674,10 +3558,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 
 	p = view_menu->get_popup();
 
-	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/use_default_light", TTR("Use Default Light")), MENU_VIEW_USE_DEFAULT_LIGHT);
-	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/use_default_srgb", TTR("Use Default sRGB")), MENU_VIEW_USE_DEFAULT_SRGB);
-	p->add_separator();
-
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
@@ -3696,7 +3576,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 	p->add_separator();
 	p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings")), MENU_VIEW_CAMERA_SETTINGS);
 
-	p->set_item_checked(p->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT), true);
 	p->set_item_checked(p->get_item_index(MENU_VIEW_DISPLAY_NORMAL), true);
 	p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true);
 	p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true);
@@ -3755,36 +3634,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 	settings_dialog->add_child(settings_vbc);
 	//settings_dialog->set_child_rect(settings_vbc);
 
-	settings_light_base = memnew(ViewportContainer);
-	settings_light_base->set_custom_minimum_size(Size2(128, 128));
-	settings_light_base->connect("gui_input", this, "_default_light_angle_input");
-	settings_vbc->add_margin_child(TTR("Default Light Normal:"), settings_light_base);
-	settings_light_vp = memnew(Viewport);
-	settings_light_vp->set_disable_input(true);
-	settings_light_vp->set_use_own_world(true);
-	settings_light_base->add_child(settings_light_vp);
-
-	settings_dlight = memnew(DirectionalLight);
-	settings_light_vp->add_child(settings_dlight);
-	settings_sphere = memnew(ImmediateGeometry);
-	settings_sphere->begin(Mesh::PRIMITIVE_TRIANGLES, Ref<Texture>());
-	settings_sphere->set_color(Color(1, 1, 1));
-	settings_sphere->add_sphere(32, 16, 1);
-	settings_sphere->end();
-	settings_light_vp->add_child(settings_sphere);
-	settings_camera = memnew(Camera);
-	settings_light_vp->add_child(settings_camera);
-	settings_camera->set_translation(Vector3(0, 0, 2));
-	settings_camera->set_orthogonal(2.1, 0.1, 5);
-
-	settings_default_light_rot_x = Math_PI * 0.3;
-	settings_default_light_rot_y = Math_PI * 0.2;
-
-	settings_ambient_color = memnew(ColorPickerButton);
-	settings_vbc->add_margin_child(TTR("Ambient Light Color:"), settings_ambient_color);
-	settings_ambient_color->connect("color_changed", this, "_update_ambient_light_color");
-	settings_ambient_color->set_pick_color(Color(0.15, 0.15, 0.15));
-
 	settings_fov = memnew(SpinBox);
 	settings_fov->set_max(179);
 	settings_fov->set_min(1);

+ 0 - 20
editor/plugins/spatial_editor_plugin.h

@@ -321,10 +321,6 @@ private:
 
 	VisualServer::ScenarioDebugMode scenario_debug;
 
-	RID light;
-	RID light_instance;
-	Transform light_transform;
-
 	RID origin;
 	RID origin_instance;
 	RID grid[3];
@@ -383,8 +379,6 @@ private:
 		MENU_VIEW_USE_3_VIEWPORTS,
 		MENU_VIEW_USE_3_VIEWPORTS_ALT,
 		MENU_VIEW_USE_4_VIEWPORTS,
-		MENU_VIEW_USE_DEFAULT_LIGHT,
-		MENU_VIEW_USE_DEFAULT_SRGB,
 		MENU_VIEW_DISPLAY_NORMAL,
 		MENU_VIEW_DISPLAY_WIREFRAME,
 		MENU_VIEW_DISPLAY_OVERDRAW,
@@ -419,16 +413,6 @@ private:
 	SpinBox *settings_fov;
 	SpinBox *settings_znear;
 	SpinBox *settings_zfar;
-	DirectionalLight *settings_dlight;
-	ImmediateGeometry *settings_sphere;
-	Camera *settings_camera;
-	float settings_default_light_rot_x;
-	float settings_default_light_rot_y;
-
-	ViewportContainer *settings_light_base;
-	Viewport *settings_light_vp;
-	ColorPickerButton *settings_ambient_color;
-	Ref<Image> settings_light_dir_image;
 
 	void _xform_dialog_action();
 	void _menu_item_pressed(int p_option);
@@ -462,10 +446,6 @@ private:
 	SpatialEditorGizmos *gizmos;
 	SpatialEditor();
 
-	void _update_ambient_light_color(const Color &p_color);
-	void _update_default_light_angle();
-	void _default_light_angle_input(const Ref<InputEvent> &p_event);
-
 	bool is_any_freelook_active() const;
 
 protected:

+ 14 - 1
editor/project_manager.cpp

@@ -203,10 +203,23 @@ private:
 					f->store_line("\n");
 					f->store_line("name=\"" + project_name->get_text() + "\"");
 					f->store_line("icon=\"res://icon.png\"");
-
+					f->store_line("[rendering]");
+					f->store_line("viewport/default_environment=\"res://default_env.tres\"");
 					memdelete(f);
 
 					ResourceSaver::save(dir.plus_file("/icon.png"), get_icon("DefaultProjectIcon", "EditorIcons"));
+
+					f = FileAccess::open(dir.plus_file("/default_env.tres"), FileAccess::WRITE);
+					if (!f) {
+						error->set_text(TTR("Couldn't create project.godot in project path."));
+					} else {
+						f->store_line("[gd_resource type=\"Environment\" load_steps=2 format=2]");
+						f->store_line("[sub_resource type=\"ProceduralSky\" id=1]");
+						f->store_line("[resource]");
+						f->store_line("background_mode = 2");
+						f->store_line("background_sky = SubResource( 1 )");
+						memdelete(f);
+					}
 				}
 
 			} else if (mode == MODE_INSTALL) {

+ 53 - 0
main/tests/test_math.cpp

@@ -462,6 +462,59 @@ uint32_t ihash3(uint32_t a) {
 
 MainLoop *test() {
 
+	{
+		float r = 1;
+		float g = 0.5;
+		float b = 0.1;
+
+		const float pow2to9 = 512.0f;
+		const float B = 15.0f;
+		//const float Emax = 31.0f;
+		const float N = 9.0f;
+
+		float sharedexp = 65408.000f; //(( pow2to9  - 1.0f)/ pow2to9)*powf( 2.0f, 31.0f - 15.0f);
+
+		float cRed = MAX(0.0f, MIN(sharedexp, r));
+		float cGreen = MAX(0.0f, MIN(sharedexp, g));
+		float cBlue = MAX(0.0f, MIN(sharedexp, b));
+
+		float cMax = MAX(cRed, MAX(cGreen, cBlue));
+
+		// expp = MAX(-B - 1, log2(maxc)) + 1 + B
+
+		float expp = MAX(-B - 1.0f, floor(Math::log(cMax) / Math_LN2)) + 1.0f + B;
+
+		float sMax = (float)floor((cMax / Math::pow(2.0f, expp - B - N)) + 0.5f);
+
+		float exps = expp + 1.0f;
+
+		if (0.0 <= sMax && sMax < pow2to9) {
+			exps = expp;
+		}
+
+		float sRed = Math::floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
+		float sGreen = Math::floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
+		float sBlue = Math::floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
+
+		print_line("R: " + rtos(sRed) + " G: " + rtos(sGreen) + " B: " + rtos(sBlue) + " EXP: " + rtos(exps));
+
+		uint32_t rgbe = (Math::fast_ftoi(sRed) & 0x1FF) | ((Math::fast_ftoi(sGreen) & 0x1FF) << 9) | ((Math::fast_ftoi(sBlue) & 0x1FF) << 18) | ((Math::fast_ftoi(exps) & 0x1F) << 27);
+
+		float rb = rgbe & 0x1ff;
+		float gb = (rgbe >> 9) & 0x1ff;
+		float bb = (rgbe >> 18) & 0x1ff;
+		float eb = (rgbe >> 27);
+		float mb = Math::pow(2, eb - 15.0 - 9.0);
+		;
+		float rd = rb * mb;
+		float gd = gb * mb;
+		float bd = bb * mb;
+
+		print_line("RGBE: " + Color(rd, gd, bd));
+
+		return NULL;
+	}
+
 	print_line("Dvectors: " + itos(MemoryPool::allocs_used));
 	print_line("Mem used: " + itos(MemoryPool::total_memory));
 	print_line("MAx mem used: " + itos(MemoryPool::max_memory));

+ 9 - 0
modules/hdr/SCsub

@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_hdr = env_modules.Clone()
+
+# Godot's own source files
+env_hdr.add_source_files(env.modules_sources, "*.cpp")

+ 7 - 0
modules/hdr/config.py

@@ -0,0 +1,7 @@
+
+def can_build(platform):
+    return True
+
+
+def configure(env):
+    pass

+ 151 - 0
modules/hdr/image_loader_hdr.cpp

@@ -0,0 +1,151 @@
+/*************************************************************************/
+/*  image_loader_jpegd.cpp                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                    http://www.godotengine.org                         */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+#include "image_loader_hdr.h"
+
+#include "os/os.h"
+#include "print_string.h"
+
+#include "thirdparty/tinyexr/tinyexr.h"
+
+Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f) {
+
+	String header = f->get_token();
+
+	print_line("HEADER: " + header);
+	ERR_FAIL_COND_V(header != "#?RADIANCE", ERR_FILE_UNRECOGNIZED);
+
+	String format = f->get_token();
+	print_line("FORMAT: " + format);
+
+	ERR_FAIL_COND_V(format != "FORMAT=32-bit_rle_rgbe", ERR_FILE_UNRECOGNIZED);
+
+	String token = f->get_token();
+
+	ERR_FAIL_COND_V(token != "-Y", ERR_FILE_CORRUPT);
+
+	int height = f->get_token().to_int();
+
+	token = f->get_token();
+
+	ERR_FAIL_COND_V(token != "+X", ERR_FILE_CORRUPT);
+
+	int width = f->get_line().to_int();
+
+	print_line("HDR w: " + itos(width) + " h:" + itos(height));
+
+	PoolVector<uint8_t> imgdata;
+
+	imgdata.resize(height * width * sizeof(uint32_t));
+
+	{
+
+		PoolVector<uint8_t>::Write w = imgdata.write();
+
+		uint8_t *ptr = (uint8_t *)w.ptr();
+
+		if (width < 8 || width >= 32768) {
+			// Read flat data
+
+			f->get_buffer(ptr, width * height * 4);
+		} else {
+			// Read RLE-encoded data
+
+			for (int j = 0; j < height; ++j) {
+				int c1 = f->get_8();
+				int c2 = f->get_8();
+				int len = f->get_8();
+				if (c1 != 2 || c2 != 2 || (len & 0x80)) {
+					// not run-length encoded, so we have to actually use THIS data as a decoded
+					// pixel (note this can't be a valid pixel--one of RGB must be >= 128)
+
+					ptr[(j * width) * 4 + 0] = uint8_t(c1);
+					ptr[(j * width) * 4 + 1] = uint8_t(c2);
+					ptr[(j * width) * 4 + 2] = uint8_t(len);
+					ptr[(j * width) * 4 + 3] = f->get_8();
+
+					f->get_buffer(&ptr[(j * width + 1) * 4], (width - 1) * 4);
+					continue;
+				}
+				len <<= 8;
+				len |= f->get_8();
+
+				print_line("line: " + itos(len));
+				if (len != width) {
+					ERR_EXPLAIN("invalid decoded scanline length, corrupt HDR");
+					ERR_FAIL_V(ERR_FILE_CORRUPT);
+				}
+
+				for (int k = 0; k < 4; ++k) {
+					int i = 0;
+					while (i < width) {
+						int count = f->get_8();
+						if (count > 128) {
+							// Run
+							int value = f->get_8();
+							count -= 128;
+							for (int z = 0; z < count; ++z)
+								ptr[(j * width + i++) * 4 + k] = uint8_t(value);
+						} else {
+							// Dump
+							for (int z = 0; z < count; ++z)
+								ptr[(j * width + i++) * 4 + k] = f->get_8();
+						}
+					}
+				}
+			}
+		}
+
+		//convert
+		for (int i = 0; i < width * height; i++) {
+
+			float exp = pow(2, ptr[3] - 128);
+
+			Color c(
+					ptr[0] * exp / 255.0,
+					ptr[1] * exp / 255.0,
+					ptr[2] * exp / 255.0);
+
+			*(uint32_t *)ptr = c.to_rgbe9995();
+			ptr += 4;
+		}
+	}
+
+	p_image->create(width, height, false, Image::FORMAT_RGBE9995, imgdata);
+
+	return OK;
+}
+
+void ImageLoaderHDR::get_recognized_extensions(List<String> *p_extensions) const {
+
+	p_extensions->push_back("hdr");
+}
+
+ImageLoaderHDR::ImageLoaderHDR() {
+}

+ 46 - 0
modules/hdr/image_loader_hdr.h

@@ -0,0 +1,46 @@
+/*************************************************************************/
+/*  image_loader_jpegd.h                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                    http://www.godotengine.org                         */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+#ifndef IMAGE_LOADER_TINYEXR_H
+#define IMAGE_LOADER_TINYEXR_H
+
+#include "io/image_loader.h"
+
+/**
+	@author Juan Linietsky <[email protected]>
+*/
+class ImageLoaderHDR : public ImageFormatLoader {
+
+public:
+	virtual Error load_image(Ref<Image> p_image, FileAccess *f);
+	virtual void get_recognized_extensions(List<String> *p_extensions) const;
+	ImageLoaderHDR();
+};
+
+#endif

+ 45 - 0
modules/hdr/register_types.cpp

@@ -0,0 +1,45 @@
+/*************************************************************************/
+/*  register_types.cpp                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                    http://www.godotengine.org                         */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "image_loader_hdr.h"
+
+static ImageLoaderHDR *image_loader_hdr = NULL;
+
+void register_hdr_types() {
+
+	image_loader_hdr = memnew(ImageLoaderHDR);
+	ImageLoader::add_image_format_loader(image_loader_hdr);
+}
+
+void unregister_hdr_types() {
+
+	memdelete(image_loader_hdr);
+}

+ 31 - 0
modules/hdr/register_types.h

@@ -0,0 +1,31 @@
+/*************************************************************************/
+/*  register_types.h                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                    http://www.godotengine.org                         */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+void register_hdr_types();
+void unregister_hdr_types();

+ 2 - 2
scene/2d/line_2d.cpp

@@ -99,7 +99,7 @@ Color Line2D::get_default_color() const {
 	return _default_color;
 }
 
-void Line2D::set_gradient(const Ref<ColorRamp> &gradient) {
+void Line2D::set_gradient(const Ref<Gradient> &gradient) {
 
 	// Cleanup previous connection if any
 	if (_gradient.is_valid()) {
@@ -116,7 +116,7 @@ void Line2D::set_gradient(const Ref<ColorRamp> &gradient) {
 	update();
 }
 
-Ref<ColorRamp> Line2D::get_gradient() const {
+Ref<Gradient> Line2D::get_gradient() const {
 	return _gradient;
 }
 

+ 3 - 3
scene/2d/line_2d.h

@@ -57,8 +57,8 @@ public:
 	void set_default_color(Color color);
 	Color get_default_color() const;
 
-	void set_gradient(const Ref<ColorRamp> &gradient);
-	Ref<ColorRamp> get_gradient() const;
+	void set_gradient(const Ref<Gradient> &gradient);
+	Ref<Gradient> get_gradient() const;
 
 	void set_texture(const Ref<Texture> &texture);
 	Ref<Texture> get_texture() const;
@@ -97,7 +97,7 @@ private:
 	LineCapMode _end_cap_mode;
 	float _width;
 	Color _default_color;
-	Ref<ColorRamp> _gradient;
+	Ref<Gradient> _gradient;
 	Ref<Texture> _texture;
 	LineTextureMode _texture_mode;
 	float _sharp_limit;

+ 1 - 1
scene/2d/line_builder.h

@@ -62,7 +62,7 @@ public:
 	LineCapMode end_cap_mode;
 	float width;
 	Color default_color;
-	ColorRamp *gradient;
+	Gradient *gradient;
 	LineTextureMode texture_mode;
 	float sharp_limit;
 	int round_precision;

+ 3 - 3
scene/2d/particles_2d.cpp

@@ -774,12 +774,12 @@ Color Particles2D::get_color() const {
 	return default_color;
 }
 
-void Particles2D::set_color_ramp(const Ref<ColorRamp> &p_color_ramp) {
+void Particles2D::set_color_ramp(const Ref<Gradient> &p_color_ramp) {
 
 	color_ramp = p_color_ramp;
 }
 
-Ref<ColorRamp> Particles2D::get_color_ramp() const {
+Ref<Gradient> Particles2D::get_color_ramp() const {
 
 	return color_ramp;
 }
@@ -810,7 +810,7 @@ void Particles2D::set_color_phases(int p_phases) {
 	//Create color ramp if we have 2 or more phases.
 	//Otherwise first phase phase will be assigned to default color.
 	if (p_phases > 1 && color_ramp.is_null()) {
-		color_ramp = Ref<ColorRamp>(memnew(ColorRamp()));
+		color_ramp = Ref<Gradient>(memnew(Gradient()));
 	}
 	if (color_ramp.is_valid()) {
 		color_ramp->get_points().resize(p_phases);

+ 3 - 3
scene/2d/particles_2d.h

@@ -169,7 +169,7 @@ private:
 
 	//If no color ramp is set then default color is used. Created as simple alternative to color_ramp.
 	Color default_color;
-	Ref<ColorRamp> color_ramp;
+	Ref<Gradient> color_ramp;
 
 	void _process_particles(float p_delta);
 	friend class ParticleAttractor2D;
@@ -241,8 +241,8 @@ public:
 	void set_color(const Color &p_color);
 	Color get_color() const;
 
-	void set_color_ramp(const Ref<ColorRamp> &p_texture);
-	Ref<ColorRamp> get_color_ramp() const;
+	void set_color_ramp(const Ref<Gradient> &p_texture);
+	Ref<Gradient> get_color_ramp() const;
 
 	void set_emissor_offset(const Point2 &p_offset);
 	Point2 get_emissor_offset() const;

+ 9 - 9
scene/gui/color_ramp_edit.cpp

@@ -114,7 +114,7 @@ void ColorRampEdit::_gui_input(const Ref<InputEvent> &p_event) {
 
 		if (grabbed != -1) {
 			int total_w = get_size().width - get_size().height - 3;
-			ColorRamp::Point newPoint = points[grabbed];
+			Gradient::Point newPoint = points[grabbed];
 			newPoint.offset = CLAMP(x / float(total_w), 0, 1);
 
 			points.push_back(newPoint);
@@ -152,11 +152,11 @@ void ColorRampEdit::_gui_input(const Ref<InputEvent> &p_event) {
 		}
 
 		//insert
-		ColorRamp::Point newPoint;
+		Gradient::Point newPoint;
 		newPoint.offset = CLAMP(x / float(total_w), 0, 1);
 
-		ColorRamp::Point prev;
-		ColorRamp::Point next;
+		Gradient::Point prev;
+		Gradient::Point next;
 
 		int pos = -1;
 		for (int i = 0; i < points.size(); i++) {
@@ -293,7 +293,7 @@ void ColorRampEdit::_notification(int p_what) {
 		_draw_checker(0, 0, total_w, h);
 
 		//Draw color ramp
-		ColorRamp::Point prev;
+		Gradient::Point prev;
 		prev.offset = 0;
 		if (points.size() == 0)
 			prev.color = Color(0, 0, 0); //Draw black rectangle if we have no points
@@ -302,7 +302,7 @@ void ColorRampEdit::_notification(int p_what) {
 
 		for (int i = -1; i < points.size(); i++) {
 
-			ColorRamp::Point next;
+			Gradient::Point next;
 			//If there is no next point
 			if (i + 1 == points.size()) {
 				if (points.size() == 0)
@@ -410,7 +410,7 @@ void ColorRampEdit::set_ramp(const Vector<float> &p_offsets, const Vector<Color>
 	ERR_FAIL_COND(p_offsets.size() != p_colors.size());
 	points.clear();
 	for (int i = 0; i < p_offsets.size(); i++) {
-		ColorRamp::Point p;
+		Gradient::Point p;
 		p.offset = p_offsets[i];
 		p.color = p_colors[i];
 		points.push_back(p);
@@ -434,14 +434,14 @@ Vector<Color> ColorRampEdit::get_colors() const {
 	return ret;
 }
 
-void ColorRampEdit::set_points(Vector<ColorRamp::Point> &p_points) {
+void ColorRampEdit::set_points(Vector<Gradient::Point> &p_points) {
 	if (points.size() != p_points.size())
 		grabbed = -1;
 	points.clear();
 	points = p_points;
 }
 
-Vector<ColorRamp::Point> &ColorRampEdit::get_points() {
+Vector<Gradient::Point> &ColorRampEdit::get_points() {
 	return points;
 }
 

+ 3 - 3
scene/gui/color_ramp_edit.h

@@ -48,7 +48,7 @@ class ColorRampEdit : public Control {
 
 	bool grabbing;
 	int grabbed;
-	Vector<ColorRamp::Point> points;
+	Vector<Gradient::Point> points;
 
 	void _draw_checker(int x, int y, int w, int h);
 	void _color_changed(const Color &p_color);
@@ -64,8 +64,8 @@ public:
 	void set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors);
 	Vector<float> get_offsets() const;
 	Vector<Color> get_colors() const;
-	void set_points(Vector<ColorRamp::Point> &p_points);
-	Vector<ColorRamp::Point> &get_points();
+	void set_points(Vector<Gradient::Point> &p_points);
+	Vector<Gradient::Point> &get_points();
 	virtual Size2 get_minimum_size() const;
 
 	ColorRampEdit();

+ 59 - 1
scene/main/scene_main_loop.cpp

@@ -37,6 +37,7 @@
 #include "print_string.h"
 #include <stdio.h>
 //#include "servers/spatial_sound_2d_server.h"
+
 #include "io/marshalls.h"
 #include "io/resource_loader.h"
 #include "scene/resources/material.h"
@@ -609,6 +610,30 @@ bool SceneTree::idle(float p_time) {
 
 	_call_idle_callbacks();
 
+#ifdef TOOLS_ENABLED
+
+	if (is_editor_hint()) {
+		//simple hack to reload fallback environment if it changed from editor
+		String env_path = GlobalConfig::get_singleton()->get("rendering/viewport/default_environment");
+		env_path = env_path.strip_edges(); //user may have added a space or two
+		String cpath;
+		Ref<Environment> fallback = get_root()->get_world()->get_fallback_environment();
+		if (fallback.is_valid()) {
+			cpath = fallback->get_path();
+		}
+		if (cpath != env_path) {
+
+			if (env_path != String()) {
+				fallback = ResourceLoader::load(env_path);
+			} else {
+				fallback.unref();
+			}
+			get_root()->get_world()->set_fallback_environment(fallback);
+		}
+	}
+
+#endif
+
 	return _quit;
 }
 
@@ -2301,7 +2326,9 @@ SceneTree::SceneTree() {
 
 	root = memnew(Viewport);
 	root->set_name("root");
-	root->set_world(Ref<World>(memnew(World)));
+	if (!root->get_world().is_valid())
+		root->set_world(Ref<World>(memnew(World)));
+
 	//root->set_world_2d( Ref<World2D>( memnew( World2D )));
 	root->set_as_audio_listener(true);
 	root->set_as_audio_listener_2d(true);
@@ -2317,6 +2344,37 @@ SceneTree::SceneTree() {
 
 	VS::get_singleton()->scenario_set_reflection_atlas_size(root->get_world()->get_scenario(), ref_atlas_size, ref_atlas_subdiv);
 
+	{ //load default fallback environment
+		//get possible extensions
+		List<String> exts;
+		ResourceLoader::get_recognized_extensions_for_type("Environment", &exts);
+		String ext_hint;
+		for (List<String>::Element *E = exts.front(); E; E = E->next()) {
+			if (ext_hint != String())
+				ext_hint += ",";
+			ext_hint += "*." + E->get();
+		}
+		//get path
+		String env_path = GLOBAL_DEF("rendering/viewport/default_environment", "");
+		//setup property
+		GlobalConfig::get_singleton()->set_custom_property_info("rendering/viewport/default_environment", PropertyInfo(Variant::STRING, "rendering/viewport/default_environment", PROPERTY_HINT_FILE, ext_hint));
+		env_path = env_path.strip_edges();
+		if (env_path != String()) {
+			Ref<Environment> env = ResourceLoader::load(env_path);
+			if (env.is_valid()) {
+				root->get_world()->set_fallback_environment(env);
+			} else {
+				if (is_editor_hint()) {
+					//file was erased, clear the field.
+					GlobalConfig::get_singleton()->set("rendering/viewport/default_environment", "");
+				} else {
+					//file was erased, notify user.
+					ERR_PRINTS(RTR("Default Environment as specified in Project Setings (Rendering -> Viewport -> Default Environment) could not be loaded."));
+				}
+			}
+		}
+	}
+
 	stretch_mode = STRETCH_MODE_DISABLED;
 	stretch_aspect = STRETCH_ASPECT_IGNORE;
 

+ 2 - 1
scene/register_scene_types.cpp

@@ -563,6 +563,7 @@ void register_scene_types() {
 	ClassDB::register_virtual_class<Texture>();
 	ClassDB::register_virtual_class<Sky>();
 	ClassDB::register_class<PanoramaSky>();
+	ClassDB::register_class<ProceduralSky>();
 	ClassDB::register_class<StreamTexture>();
 	ClassDB::register_class<ImageTexture>();
 	ClassDB::register_class<AtlasTexture>();
@@ -584,7 +585,7 @@ void register_scene_types() {
 
 	ClassDB::register_class<PolygonPathFinder>();
 	ClassDB::register_class<BitMap>();
-	ClassDB::register_class<ColorRamp>();
+	ClassDB::register_class<Gradient>();
 
 	OS::get_singleton()->yield(); //may take time to init
 

+ 28 - 28
scene/resources/color_ramp.cpp

@@ -36,7 +36,7 @@
 #define COLOR_RAMP_SET_OFFSETS "set_offsets"
 #define COLOR_RAMP_SET_COLORS "set_colors"
 
-ColorRamp::ColorRamp() {
+Gradient::Gradient() {
 	//Set initial color ramp transition from black to white
 	points.resize(2);
 	points[0].color = Color(0, 0, 0, 1);
@@ -46,35 +46,35 @@ ColorRamp::ColorRamp() {
 	is_sorted = true;
 }
 
-ColorRamp::~ColorRamp() {
+Gradient::~Gradient() {
 }
 
-void ColorRamp::_bind_methods() {
+void Gradient::_bind_methods() {
 
-	ClassDB::bind_method(D_METHOD("add_point", "offset", "color"), &ColorRamp::add_point);
-	ClassDB::bind_method(D_METHOD("remove_point", "offset", "color"), &ColorRamp::remove_point);
+	ClassDB::bind_method(D_METHOD("add_point", "offset", "color"), &Gradient::add_point);
+	ClassDB::bind_method(D_METHOD("remove_point", "offset", "color"), &Gradient::remove_point);
 
-	ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &ColorRamp::set_offset);
-	ClassDB::bind_method(D_METHOD("get_offset", "point"), &ColorRamp::get_offset);
+	ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &Gradient::set_offset);
+	ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_offset);
 
-	ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &ColorRamp::set_color);
-	ClassDB::bind_method(D_METHOD("get_color", "point"), &ColorRamp::get_color);
+	ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &Gradient::set_color);
+	ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color);
 
-	ClassDB::bind_method(D_METHOD("interpolate", "offset"), &ColorRamp::get_color_at_offset);
+	ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Gradient::get_color_at_offset);
 
-	ClassDB::bind_method(D_METHOD("get_point_count"), &ColorRamp::get_points_count);
+	ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count);
 
-	ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_OFFSETS, "offsets"), &ColorRamp::set_offsets);
-	ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_OFFSETS), &ColorRamp::get_offsets);
+	ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_OFFSETS, "offsets"), &Gradient::set_offsets);
+	ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_OFFSETS), &Gradient::get_offsets);
 
-	ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_COLORS, "colors"), &ColorRamp::set_colors);
-	ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &ColorRamp::get_colors);
+	ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_COLORS, "colors"), &Gradient::set_colors);
+	ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &Gradient::get_colors);
 
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS);
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS);
 }
 
-Vector<float> ColorRamp::get_offsets() const {
+Vector<float> Gradient::get_offsets() const {
 	Vector<float> offsets;
 	offsets.resize(points.size());
 	for (int i = 0; i < points.size(); i++) {
@@ -83,7 +83,7 @@ Vector<float> ColorRamp::get_offsets() const {
 	return offsets;
 }
 
-Vector<Color> ColorRamp::get_colors() const {
+Vector<Color> Gradient::get_colors() const {
 	Vector<Color> colors;
 	colors.resize(points.size());
 	for (int i = 0; i < points.size(); i++) {
@@ -92,7 +92,7 @@ Vector<Color> ColorRamp::get_colors() const {
 	return colors;
 }
 
-void ColorRamp::set_offsets(const Vector<float> &p_offsets) {
+void Gradient::set_offsets(const Vector<float> &p_offsets) {
 	points.resize(p_offsets.size());
 	for (int i = 0; i < points.size(); i++) {
 		points[i].offset = p_offsets[i];
@@ -101,7 +101,7 @@ void ColorRamp::set_offsets(const Vector<float> &p_offsets) {
 	emit_signal(CoreStringNames::get_singleton()->changed);
 }
 
-void ColorRamp::set_colors(const Vector<Color> &p_colors) {
+void Gradient::set_colors(const Vector<Color> &p_colors) {
 	if (points.size() < p_colors.size())
 		is_sorted = false;
 	points.resize(p_colors.size());
@@ -111,11 +111,11 @@ void ColorRamp::set_colors(const Vector<Color> &p_colors) {
 	emit_signal(CoreStringNames::get_singleton()->changed);
 }
 
-Vector<ColorRamp::Point> &ColorRamp::get_points() {
+Vector<Gradient::Point> &Gradient::get_points() {
 	return points;
 }
 
-void ColorRamp::add_point(float p_offset, const Color &p_color) {
+void Gradient::add_point(float p_offset, const Color &p_color) {
 
 	Point p;
 	p.offset = p_offset;
@@ -126,7 +126,7 @@ void ColorRamp::add_point(float p_offset, const Color &p_color) {
 	emit_signal(CoreStringNames::get_singleton()->changed);
 }
 
-void ColorRamp::remove_point(int p_index) {
+void Gradient::remove_point(int p_index) {
 
 	ERR_FAIL_INDEX(p_index, points.size());
 	ERR_FAIL_COND(points.size() <= 2);
@@ -134,13 +134,13 @@ void ColorRamp::remove_point(int p_index) {
 	emit_signal(CoreStringNames::get_singleton()->changed);
 }
 
-void ColorRamp::set_points(Vector<ColorRamp::Point> &p_points) {
+void Gradient::set_points(Vector<Gradient::Point> &p_points) {
 	points = p_points;
 	is_sorted = false;
 	emit_signal(CoreStringNames::get_singleton()->changed);
 }
 
-void ColorRamp::set_offset(int pos, const float offset) {
+void Gradient::set_offset(int pos, const float offset) {
 	if (points.size() <= pos)
 		points.resize(pos + 1);
 	points[pos].offset = offset;
@@ -148,13 +148,13 @@ void ColorRamp::set_offset(int pos, const float offset) {
 	emit_signal(CoreStringNames::get_singleton()->changed);
 }
 
-float ColorRamp::get_offset(int pos) const {
+float Gradient::get_offset(int pos) const {
 	if (points.size() > pos)
 		return points[pos].offset;
 	return 0; //TODO: Maybe throw some error instead?
 }
 
-void ColorRamp::set_color(int pos, const Color &color) {
+void Gradient::set_color(int pos, const Color &color) {
 	if (points.size() <= pos) {
 		points.resize(pos + 1);
 		is_sorted = false;
@@ -163,12 +163,12 @@ void ColorRamp::set_color(int pos, const Color &color) {
 	emit_signal(CoreStringNames::get_singleton()->changed);
 }
 
-Color ColorRamp::get_color(int pos) const {
+Color Gradient::get_color(int pos) const {
 	if (points.size() > pos)
 		return points[pos].color;
 	return Color(0, 0, 0, 1); //TODO: Maybe throw some error instead?
 }
 
-int ColorRamp::get_points_count() const {
+int Gradient::get_points_count() const {
 	return points.size();
 }

+ 5 - 5
scene/resources/color_ramp.h

@@ -32,9 +32,9 @@
 
 #include "resource.h"
 
-class ColorRamp : public Resource {
-	GDCLASS(ColorRamp, Resource);
-	OBJ_SAVE_TYPE(ColorRamp);
+class Gradient : public Resource {
+	GDCLASS(Gradient, Resource);
+	OBJ_SAVE_TYPE(Gradient);
 
 public:
 	struct Point {
@@ -54,8 +54,8 @@ protected:
 	static void _bind_methods();
 
 public:
-	ColorRamp();
-	virtual ~ColorRamp();
+	Gradient();
+	virtual ~Gradient();
 
 	void add_point(float p_offset, const Color &p_color);
 	void remove_point(int p_index);

+ 391 - 0
scene/resources/sky_box.cpp

@@ -112,3 +112,394 @@ PanoramaSky::~PanoramaSky() {
 
 	VS::get_singleton()->free(sky);
 }
+//////////////////////////////////
+
+void ProceduralSky::_radiance_changed() {
+
+	if (update_queued)
+		return; //do nothing yet
+
+	static const int size[RADIANCE_SIZE_MAX] = {
+		256, 512, 1024, 2048
+	};
+	VS::get_singleton()->sky_set_texture(sky, texture, size[get_radiance_size()]);
+}
+
+void ProceduralSky::_update_sky() {
+
+	update_queued = false;
+
+	PoolVector<uint8_t> imgdata;
+
+	static const int size[TEXTURE_SIZE_MAX] = {
+		1024, 2048, 4096
+	};
+
+	int w = size[texture_size];
+	int h = w / 2;
+
+	imgdata.resize(w * h * 4); //RGBE
+
+	{
+		PoolVector<uint8_t>::Write dataw = imgdata.write();
+
+		uint32_t *ptr = (uint32_t *)dataw.ptr();
+
+		Color sky_top_linear = sky_top_color.to_linear();
+		Color sky_horizon_linear = sky_horizon_color.to_linear();
+
+		Color ground_bottom_linear = ground_bottom_color.to_linear();
+		Color ground_horizon_linear = ground_horizon_color.to_linear();
+
+		//Color sun_linear = sun_color.to_linear();
+
+		Vector3 sun(0, 0, -1);
+
+		sun = Basis(Vector3(1, 0, 0), Math::deg2rad(sun_latitude)).xform(sun);
+		sun = Basis(Vector3(0, 1, 0), Math::deg2rad(sun_longitude)).xform(sun);
+
+		sun.normalize();
+
+		for (int i = 0; i < w; i++) {
+
+			float u = float(i) / (w - 1);
+			float phi = u * 2.0 * Math_PI;
+
+			for (int j = 0; j < h; j++) {
+
+				float v = float(j) / (h - 1);
+				float theta = v * Math_PI;
+
+				Vector3 normal(
+						Math::sin(phi) * Math::sin(theta) * -1.0,
+						Math::cos(theta),
+						Math::cos(phi) * Math::sin(theta) * -1.0);
+
+				normal.normalize();
+
+				float v_angle = Math::acos(normal.y);
+
+				Color color;
+
+				if (normal.y < 0) {
+					//ground
+
+					float c = (v_angle - (Math_PI * 0.5)) / (Math_PI * 0.5);
+					color = ground_horizon_linear.linear_interpolate(ground_bottom_linear, Math::ease(c, ground_curve));
+				} else {
+					float c = v_angle / (Math_PI * 0.5);
+					color = sky_horizon_linear.linear_interpolate(sky_top_linear, Math::ease(1.0 - c, sky_curve));
+
+					float sun_angle = Math::rad2deg(Math::acos(sun.dot(normal)));
+
+					if (sun_angle < sun_angle_min) {
+						color = color.blend(sun_color);
+					} else if (sun_angle < sun_angle_max) {
+
+						float c2 = (sun_angle - sun_angle_min) / (sun_angle_max - sun_angle_min);
+						c2 = Math::ease(c2, sun_curve);
+
+						color = color.blend(sun_color).linear_interpolate(color, c2);
+					}
+				}
+
+				ptr[j * w + i] = color.to_rgbe9995();
+			}
+		}
+	}
+
+	Ref<Image> image;
+	image.instance();
+	image->create(w, h, false, Image::FORMAT_RGBE9995, imgdata);
+
+	VS::get_singleton()->texture_allocate(texture, w, h, Image::FORMAT_RGBE9995, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT);
+	VS::get_singleton()->texture_set_data(texture, image);
+	_radiance_changed();
+}
+
+void ProceduralSky::set_sky_top_color(const Color &p_sky_top) {
+
+	sky_top_color = p_sky_top;
+	_queue_update();
+}
+
+Color ProceduralSky::get_sky_top_color() const {
+
+	return sky_top_color;
+}
+
+void ProceduralSky::set_sky_horizon_color(const Color &p_sky_horizon) {
+
+	sky_horizon_color = p_sky_horizon;
+	_queue_update();
+}
+Color ProceduralSky::get_sky_horizon_color() const {
+
+	return sky_horizon_color;
+}
+
+void ProceduralSky::set_sky_curve(float p_curve) {
+
+	sky_curve = p_curve;
+	_queue_update();
+}
+float ProceduralSky::get_sky_curve() const {
+
+	return sky_curve;
+}
+
+void ProceduralSky::set_sky_energy(float p_energy) {
+
+	sky_energy = p_energy;
+	_queue_update();
+}
+float ProceduralSky::get_sky_energy() const {
+
+	return sky_energy;
+}
+
+void ProceduralSky::set_ground_bottom_color(const Color &p_ground_bottom) {
+
+	ground_bottom_color = p_ground_bottom;
+	_queue_update();
+}
+Color ProceduralSky::get_ground_bottom_color() const {
+
+	return ground_bottom_color;
+}
+
+void ProceduralSky::set_ground_horizon_color(const Color &p_ground_horizon) {
+
+	ground_horizon_color = p_ground_horizon;
+	_queue_update();
+}
+Color ProceduralSky::get_ground_horizon_color() const {
+
+	return ground_horizon_color;
+}
+
+void ProceduralSky::set_ground_curve(float p_curve) {
+
+	ground_curve = p_curve;
+	_queue_update();
+}
+float ProceduralSky::get_ground_curve() const {
+
+	return ground_curve;
+}
+
+void ProceduralSky::set_ground_energy(float p_energy) {
+
+	ground_energy = p_energy;
+	_queue_update();
+}
+float ProceduralSky::get_ground_energy() const {
+
+	return ground_energy;
+}
+
+void ProceduralSky::set_sun_color(const Color &p_sun) {
+
+	sun_color = p_sun;
+	_queue_update();
+}
+Color ProceduralSky::get_sun_color() const {
+
+	return sun_color;
+}
+
+void ProceduralSky::set_sun_latitude(float p_angle) {
+
+	sun_latitude = p_angle;
+	_queue_update();
+}
+float ProceduralSky::get_sun_latitude() const {
+
+	return sun_latitude;
+}
+
+void ProceduralSky::set_sun_longitude(float p_angle) {
+
+	sun_longitude = p_angle;
+	_queue_update();
+}
+float ProceduralSky::get_sun_longitude() const {
+
+	return sun_longitude;
+}
+
+void ProceduralSky::set_sun_angle_min(float p_angle) {
+
+	sun_angle_min = p_angle;
+	_queue_update();
+}
+float ProceduralSky::get_sun_angle_min() const {
+
+	return sun_angle_min;
+}
+
+void ProceduralSky::set_sun_angle_max(float p_angle) {
+
+	sun_angle_max = p_angle;
+	_queue_update();
+}
+float ProceduralSky::get_sun_angle_max() const {
+
+	return sun_angle_max;
+}
+
+void ProceduralSky::set_sun_curve(float p_curve) {
+
+	sun_curve = p_curve;
+	_queue_update();
+}
+float ProceduralSky::get_sun_curve() const {
+
+	return sun_curve;
+}
+
+void ProceduralSky::set_sun_energy(float p_energy) {
+
+	sun_energy = p_energy;
+	_queue_update();
+}
+float ProceduralSky::get_sun_energy() const {
+
+	return sun_energy;
+}
+
+void ProceduralSky::set_texture_size(TextureSize p_size) {
+	ERR_FAIL_INDEX(p_size, TEXTURE_SIZE_MAX);
+
+	texture_size = p_size;
+	_queue_update();
+}
+ProceduralSky::TextureSize ProceduralSky::get_texture_size() const {
+	return texture_size;
+}
+
+RID ProceduralSky::get_rid() const {
+	return sky;
+}
+
+void ProceduralSky::_queue_update() {
+
+	if (update_queued)
+		return;
+
+	update_queued = true;
+	call_deferred("_update_sky");
+}
+
+void ProceduralSky::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("_update_sky"), &ProceduralSky::_update_sky);
+
+	ClassDB::bind_method(D_METHOD("set_sky_top_color", "color"), &ProceduralSky::set_sky_top_color);
+	ClassDB::bind_method(D_METHOD("get_sky_top_color"), &ProceduralSky::get_sky_top_color);
+
+	ClassDB::bind_method(D_METHOD("set_sky_horizon_color", "color"), &ProceduralSky::set_sky_horizon_color);
+	ClassDB::bind_method(D_METHOD("get_sky_horizon_color"), &ProceduralSky::get_sky_horizon_color);
+
+	ClassDB::bind_method(D_METHOD("set_sky_curve", "curve"), &ProceduralSky::set_sky_curve);
+	ClassDB::bind_method(D_METHOD("get_sky_curve"), &ProceduralSky::get_sky_curve);
+
+	ClassDB::bind_method(D_METHOD("set_sky_energy", "energy"), &ProceduralSky::set_sky_energy);
+	ClassDB::bind_method(D_METHOD("get_sky_energy"), &ProceduralSky::get_sky_energy);
+
+	ClassDB::bind_method(D_METHOD("set_ground_bottom_color", "color"), &ProceduralSky::set_ground_bottom_color);
+	ClassDB::bind_method(D_METHOD("get_ground_bottom_color"), &ProceduralSky::get_ground_bottom_color);
+
+	ClassDB::bind_method(D_METHOD("set_ground_horizon_color", "color"), &ProceduralSky::set_ground_horizon_color);
+	ClassDB::bind_method(D_METHOD("get_ground_horizon_color"), &ProceduralSky::get_ground_horizon_color);
+
+	ClassDB::bind_method(D_METHOD("set_ground_curve", "curve"), &ProceduralSky::set_ground_curve);
+	ClassDB::bind_method(D_METHOD("get_ground_curve"), &ProceduralSky::get_ground_curve);
+
+	ClassDB::bind_method(D_METHOD("set_ground_energy", "energy"), &ProceduralSky::set_ground_energy);
+	ClassDB::bind_method(D_METHOD("get_ground_energy"), &ProceduralSky::get_ground_energy);
+
+	ClassDB::bind_method(D_METHOD("set_sun_color", "color"), &ProceduralSky::set_sun_color);
+	ClassDB::bind_method(D_METHOD("get_sun_color"), &ProceduralSky::get_sun_color);
+
+	ClassDB::bind_method(D_METHOD("set_sun_latitude", "degrees"), &ProceduralSky::set_sun_latitude);
+	ClassDB::bind_method(D_METHOD("get_sun_latitude"), &ProceduralSky::get_sun_latitude);
+
+	ClassDB::bind_method(D_METHOD("set_sun_longitude", "degrees"), &ProceduralSky::set_sun_longitude);
+	ClassDB::bind_method(D_METHOD("get_sun_longitude"), &ProceduralSky::get_sun_longitude);
+
+	ClassDB::bind_method(D_METHOD("set_sun_angle_min", "degrees"), &ProceduralSky::set_sun_angle_min);
+	ClassDB::bind_method(D_METHOD("get_sun_angle_min"), &ProceduralSky::get_sun_angle_min);
+
+	ClassDB::bind_method(D_METHOD("set_sun_angle_max", "degrees"), &ProceduralSky::set_sun_angle_max);
+	ClassDB::bind_method(D_METHOD("get_sun_angle_max"), &ProceduralSky::get_sun_angle_max);
+
+	ClassDB::bind_method(D_METHOD("set_sun_curve", "curve"), &ProceduralSky::set_sun_curve);
+	ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSky::get_sun_curve);
+
+	ClassDB::bind_method(D_METHOD("set_sun_energy", "energy"), &ProceduralSky::set_sun_energy);
+	ClassDB::bind_method(D_METHOD("get_sun_energy"), &ProceduralSky::get_sun_energy);
+
+	ClassDB::bind_method(D_METHOD("set_texture_size", "size"), &ProceduralSky::set_texture_size);
+	ClassDB::bind_method(D_METHOD("get_texture_size"), &ProceduralSky::get_texture_size);
+
+	ADD_GROUP("Sky", "sky_");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color"), "set_sky_top_color", "get_sky_top_color");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color"), "set_sky_horizon_color", "get_sky_horizon_color");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy");
+
+	ADD_GROUP("Ground", "ground_");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color"), "set_ground_bottom_color", "get_ground_bottom_color");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color"), "set_ground_horizon_color", "get_ground_horizon_color");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy");
+
+	ADD_GROUP("Sun", "sun_");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sun_color"), "set_sun_color", "get_sun_color");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sun_latitude", PROPERTY_HINT_RANGE, "-180,180,0.01"), "set_sun_latitude", "get_sun_latitude");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sun_longitude", PROPERTY_HINT_RANGE, "-180,180,0.01"), "set_sun_longitude", "get_sun_longitude");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sun_angle_min", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_angle_min", "get_sun_angle_min");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_angle_max", "get_sun_angle_max");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sun_curve", PROPERTY_HINT_EXP_EASING), "set_sun_curve", "get_sun_curve");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "sun_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sun_energy", "get_sun_energy");
+
+	ADD_GROUP("Texture", "texture_");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_size", PROPERTY_HINT_ENUM, "1024,2048,4096"), "set_texture_size", "get_texture_size");
+}
+
+ProceduralSky::ProceduralSky() {
+
+	sky = VS::get_singleton()->sky_create();
+	texture = VS::get_singleton()->texture_create();
+
+	update_queued = false;
+
+	sky_top_color = Color::hex(0x4d67e8ff);
+	sky_horizon_color = Color::hex(0x8ed2e8ff);
+	sky_curve = 0.25;
+	sky_energy = 1;
+
+	ground_bottom_color = Color::hex(0x322719ff);
+	ground_horizon_color = Color::hex(0x543610ff);
+	ground_curve = 0.25;
+	ground_energy = 1;
+
+	sun_color = Color(1, 1, 1);
+	sun_latitude = 35;
+	sun_longitude = 0;
+	sun_angle_min = 1;
+	sun_angle_max = 100;
+	sun_curve = 0.05;
+	sun_energy = 16;
+
+	texture_size = TEXTURE_SIZE_1024;
+
+	_queue_update();
+}
+
+ProceduralSky::~ProceduralSky() {
+
+	VS::get_singleton()->free(sky);
+	VS::get_singleton()->free(texture);
+}

+ 100 - 12
scene/resources/sky_box.h

@@ -62,17 +62,6 @@ VARIANT_ENUM_CAST(Sky::RadianceSize)
 class PanoramaSky : public Sky {
 	GDCLASS(PanoramaSky, Sky);
 
-public:
-	enum ImagePath {
-		IMAGE_PATH_NEGATIVE_X,
-		IMAGE_PATH_POSITIVE_X,
-		IMAGE_PATH_NEGATIVE_Y,
-		IMAGE_PATH_POSITIVE_Y,
-		IMAGE_PATH_NEGATIVE_Z,
-		IMAGE_PATH_POSITIVE_Z,
-		IMAGE_PATH_MAX
-	};
-
 private:
 	RID sky;
 	Ref<Texture> panorama;
@@ -91,6 +80,105 @@ public:
 	~PanoramaSky();
 };
 
-VARIANT_ENUM_CAST(PanoramaSky::ImagePath)
+class ProceduralSky : public Sky {
+	GDCLASS(ProceduralSky, Sky);
+
+public:
+	enum TextureSize {
+		TEXTURE_SIZE_1024,
+		TEXTURE_SIZE_2048,
+		TEXTURE_SIZE_4096,
+		TEXTURE_SIZE_MAX
+	};
+
+private:
+	Color sky_top_color;
+	Color sky_horizon_color;
+	float sky_curve;
+	float sky_energy;
+
+	Color ground_bottom_color;
+	Color ground_horizon_color;
+	float ground_curve;
+	float ground_energy;
+
+	Color sun_color;
+	float sun_latitude;
+	float sun_longitude;
+	float sun_angle_min;
+	float sun_angle_max;
+	float sun_curve;
+	float sun_energy;
+
+	TextureSize texture_size;
+
+	RID sky;
+	RID texture;
+
+	bool update_queued;
+
+protected:
+	static void _bind_methods();
+	virtual void _radiance_changed();
+
+	void _update_sky();
+	void _queue_update();
+
+public:
+	void set_sky_top_color(const Color &p_sky_top);
+	Color get_sky_top_color() const;
+
+	void set_sky_horizon_color(const Color &p_sky_horizon);
+	Color get_sky_horizon_color() const;
+
+	void set_sky_curve(float p_curve);
+	float get_sky_curve() const;
+
+	void set_sky_energy(float p_energy);
+	float get_sky_energy() const;
+
+	void set_ground_bottom_color(const Color &p_ground_bottom);
+	Color get_ground_bottom_color() const;
+
+	void set_ground_horizon_color(const Color &p_ground_horizon);
+	Color get_ground_horizon_color() const;
+
+	void set_ground_curve(float p_curve);
+	float get_ground_curve() const;
+
+	void set_ground_energy(float p_energy);
+	float get_ground_energy() const;
+
+	void set_sun_color(const Color &p_sun);
+	Color get_sun_color() const;
+
+	void set_sun_latitude(float p_angle);
+	float get_sun_latitude() const;
+
+	void set_sun_longitude(float p_angle);
+	float get_sun_longitude() const;
+
+	void set_sun_angle_min(float p_angle);
+	float get_sun_angle_min() const;
+
+	void set_sun_angle_max(float p_angle);
+	float get_sun_angle_max() const;
+
+	void set_sun_curve(float p_curve);
+	float get_sun_curve() const;
+
+	void set_sun_energy(float p_energy);
+	float get_sun_energy() const;
+
+	void set_texture_size(TextureSize p_size);
+	TextureSize get_texture_size() const;
+
+	virtual RID get_rid() const;
+
+	ProceduralSky();
+	~ProceduralSky();
+};
+
+VARIANT_ENUM_CAST(ProceduralSky::TextureSize)
 
 #endif // Sky_H

+ 17 - 0
scene/resources/world.cpp

@@ -280,6 +280,20 @@ Ref<Environment> World::get_environment() const {
 	return environment;
 }
 
+void World::set_fallback_environment(const Ref<Environment> &p_environment) {
+
+	fallback_environment = p_environment;
+	if (fallback_environment.is_valid())
+		VS::get_singleton()->scenario_set_fallback_environment(scenario, p_environment->get_rid());
+	else
+		VS::get_singleton()->scenario_set_fallback_environment(scenario, RID());
+}
+
+Ref<Environment> World::get_fallback_environment() const {
+
+	return fallback_environment;
+}
+
 PhysicsDirectSpaceState *World::get_direct_space_state() {
 
 	return PhysicsServer::get_singleton()->space_get_direct_state(space);
@@ -291,8 +305,11 @@ void World::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_scenario"), &World::get_scenario);
 	ClassDB::bind_method(D_METHOD("set_environment", "env:Environment"), &World::set_environment);
 	ClassDB::bind_method(D_METHOD("get_environment:Environment"), &World::get_environment);
+	ClassDB::bind_method(D_METHOD("set_fallback_environment", "env:Environment"), &World::set_fallback_environment);
+	ClassDB::bind_method(D_METHOD("get_fallback_environment:Environment"), &World::get_fallback_environment);
 	ClassDB::bind_method(D_METHOD("get_direct_space_state:PhysicsDirectSpaceState"), &World::get_direct_space_state);
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment");
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment");
 }
 
 World::World() {

+ 5 - 0
scene/resources/world.h

@@ -48,6 +48,7 @@ private:
 	RID scenario;
 	SpatialIndexer *indexer;
 	Ref<Environment> environment;
+	Ref<Environment> fallback_environment;
 
 protected:
 	static void _bind_methods();
@@ -68,9 +69,13 @@ protected:
 public:
 	RID get_space() const;
 	RID get_scenario() const;
+
 	void set_environment(const Ref<Environment> &p_environment);
 	Ref<Environment> get_environment() const;
 
+	void set_fallback_environment(const Ref<Environment> &p_environment);
+	Ref<Environment> get_fallback_environment() const;
+
 	PhysicsDirectSpaceState *get_direct_space_state();
 
 	World();