From c5dcbeb160b37d8ffd70061d76eb980756fe36b4 Mon Sep 17 00:00:00 2001 From: Rémi Verschelde Date: Tue, 12 Feb 2019 17:18:13 +0100 Subject: Scene: Ensure classes match their header filename Also drop some unused files. Renamed: - `scene/2d/navigation2d.h` -> `navigation_2d.h` - `scene/2d/screen_button.h` -> `touch_screen_button.h` - `scene/3d/scenario_fx.h` -> `world_environment.h` - `scene/audio/audio_player.h` -> `audio_stream_player.h` - `scene/resources/bit_mask.h` -> `bit_map.h` - `scene/resources/color_ramp.h` -> `gradient.h` - `scene/resources/shape_line_2d.h` -> `line_shape_2d.h` - `scene/resources/scene_format_text.h` -> `resource_format_text.h` - `scene/resources/sky_box.h` -> `sky.h` Dropped: - `scene/resources/bounds.h` --- editor/editor_export.cpp | 2 +- editor/import/resource_importer_bitmask.cpp | 2 +- editor/import/resource_importer_scene.cpp | 2 +- editor/plugins/audio_stream_editor_plugin.h | 2 +- editor/plugins/canvas_item_editor_plugin.cpp | 2 +- .../plugins/collision_shape_2d_editor_plugin.cpp | 2 +- editor/plugins/editor_preview_plugins.cpp | 2 +- modules/gdnative/nativescript/nativescript.cpp | 2 +- scene/2d/collision_shape_2d.cpp | 2 +- scene/2d/line_builder.h | 2 +- scene/2d/navigation2d.cpp | 739 -------- scene/2d/navigation2d.h | 174 -- scene/2d/navigation_2d.cpp | 739 ++++++++ scene/2d/navigation_2d.h | 174 ++ scene/2d/navigation_polygon.cpp | 2 +- scene/2d/screen_button.cpp | 423 ----- scene/2d/screen_button.h | 115 -- scene/2d/tile_map.h | 2 +- scene/2d/touch_screen_button.cpp | 424 +++++ scene/2d/touch_screen_button.h | 115 ++ scene/3d/SCsub | 2 +- scene/3d/reflection_probe.h | 2 +- scene/3d/scenario_fx.cpp | 112 -- scene/3d/scenario_fx.h | 59 - scene/3d/world_environment.cpp | 112 ++ scene/3d/world_environment.h | 59 + scene/audio/audio_player.cpp | 395 ----- scene/audio/audio_player.h | 113 -- scene/audio/audio_stream_player.cpp | 395 +++++ scene/audio/audio_stream_player.h | 114 ++ scene/gui/gradient_edit.h | 2 +- scene/gui/link_button.h | 2 +- scene/gui/texture_button.h | 2 +- scene/main/scene_tree.h | 1 - scene/main/viewport.cpp | 2 +- scene/register_scene_types.cpp | 18 +- scene/resources/bit_map.cpp | 625 +++++++ scene/resources/bit_map.h | 75 + scene/resources/bit_mask.cpp | 625 ------- scene/resources/bit_mask.h | 75 - scene/resources/bounds.cpp | 52 - scene/resources/bounds.h | 52 - scene/resources/color_ramp.cpp | 175 -- scene/resources/color_ramp.h | 129 -- scene/resources/environment.h | 2 +- scene/resources/gradient.cpp | 176 ++ scene/resources/gradient.h | 129 ++ scene/resources/line_shape_2d.cpp | 121 ++ scene/resources/line_shape_2d.h | 62 + scene/resources/resource_format_text.cpp | 1756 ++++++++++++++++++++ scene/resources/resource_format_text.h | 183 ++ scene/resources/scene_format_text.cpp | 1755 ------------------- scene/resources/scene_format_text.h | 183 -- scene/resources/shape_line_2d.cpp | 120 -- scene/resources/shape_line_2d.h | 62 - scene/resources/sky.cpp | 577 +++++++ scene/resources/sky.h | 200 +++ scene/resources/sky_box.cpp | 576 ------- scene/resources/sky_box.h | 199 --- scene/resources/texture.cpp | 2 +- scene/resources/texture.h | 2 +- 61 files changed, 6066 insertions(+), 6164 deletions(-) delete mode 100644 scene/2d/navigation2d.cpp delete mode 100644 scene/2d/navigation2d.h create mode 100644 scene/2d/navigation_2d.cpp create mode 100644 scene/2d/navigation_2d.h delete mode 100644 scene/2d/screen_button.cpp delete mode 100644 scene/2d/screen_button.h create mode 100644 scene/2d/touch_screen_button.cpp create mode 100644 scene/2d/touch_screen_button.h delete mode 100644 scene/3d/scenario_fx.cpp delete mode 100644 scene/3d/scenario_fx.h create mode 100644 scene/3d/world_environment.cpp create mode 100644 scene/3d/world_environment.h delete mode 100644 scene/audio/audio_player.cpp delete mode 100644 scene/audio/audio_player.h create mode 100644 scene/audio/audio_stream_player.cpp create mode 100644 scene/audio/audio_stream_player.h create mode 100644 scene/resources/bit_map.cpp create mode 100644 scene/resources/bit_map.h delete mode 100644 scene/resources/bit_mask.cpp delete mode 100644 scene/resources/bit_mask.h delete mode 100644 scene/resources/bounds.cpp delete mode 100644 scene/resources/bounds.h delete mode 100644 scene/resources/color_ramp.cpp delete mode 100644 scene/resources/color_ramp.h create mode 100644 scene/resources/gradient.cpp create mode 100644 scene/resources/gradient.h create mode 100644 scene/resources/line_shape_2d.cpp create mode 100644 scene/resources/line_shape_2d.h create mode 100644 scene/resources/resource_format_text.cpp create mode 100644 scene/resources/resource_format_text.h delete mode 100644 scene/resources/scene_format_text.cpp delete mode 100644 scene/resources/scene_format_text.h delete mode 100644 scene/resources/shape_line_2d.cpp delete mode 100644 scene/resources/shape_line_2d.h create mode 100644 scene/resources/sky.cpp create mode 100644 scene/resources/sky.h delete mode 100644 scene/resources/sky_box.cpp delete mode 100644 scene/resources/sky_box.h diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 7c99318dca..4e8b2c82f1 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -42,7 +42,7 @@ #include "editor/plugins/script_editor_plugin.h" #include "editor_node.h" #include "editor_settings.h" -#include "scene/resources/scene_format_text.h" +#include "scene/resources/resource_format_text.h" #include "thirdparty/misc/md5.h" static int _get_pad(int p_alignment, int p_n) { diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp index 431396d584..b568cbda9b 100644 --- a/editor/import/resource_importer_bitmask.cpp +++ b/editor/import/resource_importer_bitmask.cpp @@ -34,7 +34,7 @@ #include "core/io/image_loader.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" #include "scene/resources/texture.h" String ResourceImporterBitMap::get_importer_name() const { diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index f230fa1b8b..2616ba6e87 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -46,7 +46,7 @@ #include "scene/resources/box_shape.h" #include "scene/resources/plane_shape.h" #include "scene/resources/ray_shape.h" -#include "scene/resources/scene_format_text.h" +#include "scene/resources/resource_format_text.h" #include "scene/resources/sphere_shape.h" uint32_t EditorSceneImporter::get_import_flags() const { diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h index e60bf6a38f..12e4faef94 100644 --- a/editor/plugins/audio_stream_editor_plugin.h +++ b/editor/plugins/audio_stream_editor_plugin.h @@ -33,7 +33,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" -#include "scene/audio/audio_player.h" +#include "scene/audio/audio_stream_player.h" #include "scene/gui/color_rect.h" #include "scene/resources/texture.h" diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 98d639a2d3..873bdd9e7f 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -42,9 +42,9 @@ #include "scene/2d/light_2d.h" #include "scene/2d/particles_2d.h" #include "scene/2d/polygon_2d.h" -#include "scene/2d/screen_button.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite.h" +#include "scene/2d/touch_screen_button.h" #include "scene/gui/grid_container.h" #include "scene/gui/nine_patch_rect.h" #include "scene/main/canvas_layer.h" diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index fc572f54e1..10023d88bf 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -35,9 +35,9 @@ #include "scene/resources/circle_shape_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/line_shape_2d.h" #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/segment_shape_2d.h" -#include "scene/resources/shape_line_2d.h" Variant CollisionShape2DEditor::get_handle_value(int idx) const { diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 0c0cc9d635..071a0287e6 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -36,7 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 0370060937..9fd0a2e8ec 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -39,7 +39,7 @@ #include "core/project_settings.h" #include "scene/main/scene_tree.h" -#include "scene/resources/scene_format_text.h" +#include "scene/resources/resource_format_text.h" #include diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index c756f49bbd..5440a1d8c3 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -36,9 +36,9 @@ #include "scene/resources/circle_shape_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/line_shape_2d.h" #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/segment_shape_2d.h" -#include "scene/resources/shape_line_2d.h" void CollisionShape2D::_shape_changed() { diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h index 2ca28d09c4..b961385e33 100644 --- a/scene/2d/line_builder.h +++ b/scene/2d/line_builder.h @@ -34,7 +34,7 @@ #include "core/color.h" #include "core/math/vector2.h" #include "line_2d.h" -#include "scene/resources/color_ramp.h" +#include "scene/resources/gradient.h" class LineBuilder { public: diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp deleted file mode 100644 index 1c0e924433..0000000000 --- a/scene/2d/navigation2d.cpp +++ /dev/null @@ -1,739 +0,0 @@ -/*************************************************************************/ -/* navigation2d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "navigation2d.h" - -#define USE_ENTRY_POINT - -void Navigation2D::_navpoly_link(int p_id) { - - ERR_FAIL_COND(!navpoly_map.has(p_id)); - NavMesh &nm = navpoly_map[p_id]; - ERR_FAIL_COND(nm.linked); - - PoolVector vertices = nm.navpoly->get_vertices(); - int len = vertices.size(); - if (len == 0) - return; - - PoolVector::Read r = vertices.read(); - - for (int i = 0; i < nm.navpoly->get_polygon_count(); i++) { - - //build - - List::Element *P = nm.polygons.push_back(Polygon()); - Polygon &p = P->get(); - p.owner = &nm; - - Vector poly = nm.navpoly->get_polygon(i); - int plen = poly.size(); - const int *indices = poly.ptr(); - bool valid = true; - p.edges.resize(plen); - - Vector2 center; - float sum = 0; - - for (int j = 0; j < plen; j++) { - - int idx = indices[j]; - if (idx < 0 || idx >= len) { - valid = false; - break; - } - - Polygon::Edge e; - Vector2 ep = nm.xform.xform(r[idx]); - center += ep; - e.point = _get_point(ep); - p.edges.write[j] = e; - - int idxn = indices[(j + 1) % plen]; - if (idxn < 0 || idxn >= len) { - valid = false; - break; - } - - Vector2 epn = nm.xform.xform(r[idxn]); - - sum += (epn.x - ep.x) * (epn.y + ep.y); - } - - p.clockwise = sum > 0; - - if (!valid) { - nm.polygons.pop_back(); - ERR_CONTINUE(!valid); - continue; - } - - p.center = center / plen; - - //connect - - for (int j = 0; j < plen; j++) { - - int next = (j + 1) % plen; - EdgeKey ek(p.edges[j].point, p.edges[next].point); - - Map::Element *C = connections.find(ek); - if (!C) { - - Connection c; - c.A = &p; - c.A_edge = j; - c.B = NULL; - c.B_edge = -1; - connections[ek] = c; - } else { - - if (C->get().B != NULL) { - ConnectionPending pending; - pending.polygon = &p; - pending.edge = j; - p.edges.write[j].P = C->get().pending.push_back(pending); - continue; - } - - C->get().B = &p; - C->get().B_edge = j; - C->get().A->edges.write[C->get().A_edge].C = &p; - C->get().A->edges.write[C->get().A_edge].C_edge = j; - p.edges.write[j].C = C->get().A; - p.edges.write[j].C_edge = C->get().A_edge; - //connection successful. - } - } - } - - nm.linked = true; -} - -void Navigation2D::_navpoly_unlink(int p_id) { - - ERR_FAIL_COND(!navpoly_map.has(p_id)); - NavMesh &nm = navpoly_map[p_id]; - ERR_FAIL_COND(!nm.linked); - - for (List::Element *E = nm.polygons.front(); E; E = E->next()) { - - Polygon &p = E->get(); - - int ec = p.edges.size(); - Polygon::Edge *edges = p.edges.ptrw(); - - for (int i = 0; i < ec; i++) { - int next = (i + 1) % ec; - - EdgeKey ek(edges[i].point, edges[next].point); - Map::Element *C = connections.find(ek); - ERR_CONTINUE(!C); - - if (edges[i].P) { - C->get().pending.erase(edges[i].P); - edges[i].P = NULL; - - } else if (C->get().B) { - //disconnect - - C->get().B->edges.write[C->get().B_edge].C = NULL; - C->get().B->edges.write[C->get().B_edge].C_edge = -1; - C->get().A->edges.write[C->get().A_edge].C = NULL; - C->get().A->edges.write[C->get().A_edge].C_edge = -1; - - if (C->get().A == &E->get()) { - - C->get().A = C->get().B; - C->get().A_edge = C->get().B_edge; - } - C->get().B = NULL; - C->get().B_edge = -1; - - if (C->get().pending.size()) { - //reconnect if something is pending - ConnectionPending cp = C->get().pending.front()->get(); - C->get().pending.pop_front(); - - C->get().B = cp.polygon; - C->get().B_edge = cp.edge; - C->get().A->edges.write[C->get().A_edge].C = cp.polygon; - C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge; - cp.polygon->edges.write[cp.edge].C = C->get().A; - cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge; - cp.polygon->edges.write[cp.edge].P = NULL; - } - - } else { - connections.erase(C); - //erase - } - } - } - - nm.polygons.clear(); - - nm.linked = false; -} - -int Navigation2D::navpoly_add(const Ref &p_mesh, const Transform2D &p_xform, Object *p_owner) { - - int id = last_id++; - NavMesh nm; - nm.linked = false; - nm.navpoly = p_mesh; - nm.xform = p_xform; - nm.owner = p_owner; - navpoly_map[id] = nm; - - _navpoly_link(id); - - return id; -} - -void Navigation2D::navpoly_set_transform(int p_id, const Transform2D &p_xform) { - - ERR_FAIL_COND(!navpoly_map.has(p_id)); - NavMesh &nm = navpoly_map[p_id]; - if (nm.xform == p_xform) - return; //bleh - _navpoly_unlink(p_id); - nm.xform = p_xform; - _navpoly_link(p_id); -} -void Navigation2D::navpoly_remove(int p_id) { - - ERR_FAIL_COND(!navpoly_map.has(p_id)); - _navpoly_unlink(p_id); - navpoly_map.erase(p_id); -} - -Vector Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) { - - Polygon *begin_poly = NULL; - Polygon *end_poly = NULL; - Vector2 begin_point; - Vector2 end_point; - float begin_d = 1e20; - float end_d = 1e20; - - //look for point inside triangle - - for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { - - if (!E->get().linked) - continue; - for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { - - Polygon &p = F->get(); - if (begin_d || end_d) { - for (int i = 2; i < p.edges.size(); i++) { - - if (begin_d > 0) { - - if (Geometry::is_point_in_triangle(p_start, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { - - begin_poly = &p; - begin_point = p_start; - begin_d = 0; - if (end_d == 0) - break; - } - } - - if (end_d > 0) { - - if (Geometry::is_point_in_triangle(p_end, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { - - end_poly = &p; - end_point = p_end; - end_d = 0; - if (begin_d == 0) - break; - } - } - } - } - - p.prev_edge = -1; - } - } - - //start or end not inside triangle.. look for closest segment :| - if (begin_d || end_d) { - for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { - - if (!E->get().linked) - continue; - for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { - - Polygon &p = F->get(); - int es = p.edges.size(); - for (int i = 0; i < es; i++) { - - Vector2 edge[2] = { - _get_vertex(p.edges[i].point), - _get_vertex(p.edges[(i + 1) % es].point) - }; - - if (begin_d > 0) { - Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_start, edge); - float d = spoint.distance_to(p_start); - if (d < begin_d) { - begin_poly = &p; - begin_point = spoint; - begin_d = d; - } - } - - if (end_d > 0) { - Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_end, edge); - float d = spoint.distance_to(p_end); - if (d < end_d) { - end_poly = &p; - end_point = spoint; - end_d = d; - } - } - } - } - } - } - - if (!begin_poly || !end_poly) { - - return Vector(); //no path - } - - if (begin_poly == end_poly) { - - Vector path; - path.resize(2); - path.write[0] = begin_point; - path.write[1] = end_point; - return path; - } - - bool found_route = false; - - List open_list; - - begin_poly->entry = p_start; - - for (int i = 0; i < begin_poly->edges.size(); i++) { - - if (begin_poly->edges[i].C) { - - begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge; -#ifdef USE_ENTRY_POINT - Vector2 edge[2] = { - _get_vertex(begin_poly->edges[i].point), - _get_vertex(begin_poly->edges[(i + 1) % begin_poly->edges.size()].point) - }; - - Vector2 entry = Geometry::get_closest_point_to_segment_2d(begin_poly->entry, edge); - begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry); - begin_poly->edges[i].C->entry = entry; -#else - begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center); -#endif - open_list.push_back(begin_poly->edges[i].C); - - if (begin_poly->edges[i].C == end_poly) { - found_route = true; - } - } - } - - while (!found_route) { - - if (open_list.size() == 0) { - break; - } - //check open list - - List::Element *least_cost_poly = NULL; - float least_cost = 1e30; - - //this could be faster (cache previous results) - for (List::Element *E = open_list.front(); E; E = E->next()) { - - Polygon *p = E->get(); - - float cost = p->distance; - -#ifdef USE_ENTRY_POINT - int es = p->edges.size(); - - float shortest_distance = 1e30; - - for (int i = 0; i < es; i++) { - Polygon::Edge &e = p->edges.write[i]; - - if (!e.C) - continue; - - Vector2 edge[2] = { - _get_vertex(p->edges[i].point), - _get_vertex(p->edges[(i + 1) % es].point) - }; - - Vector2 edge_point = Geometry::get_closest_point_to_segment_2d(p->entry, edge); - float dist = p->entry.distance_to(edge_point); - if (dist < shortest_distance) - shortest_distance = dist; - } - - cost += shortest_distance; -#else - cost += p->center.distance_to(end_point); -#endif - if (cost < least_cost) { - least_cost_poly = E; - least_cost = cost; - } - } - - Polygon *p = least_cost_poly->get(); - //open the neighbours for search - int es = p->edges.size(); - - for (int i = 0; i < es; i++) { - - Polygon::Edge &e = p->edges.write[i]; - - if (!e.C) - continue; - -#ifdef USE_ENTRY_POINT - Vector2 edge[2] = { - _get_vertex(p->edges[i].point), - _get_vertex(p->edges[(i + 1) % es].point) - }; - - Vector2 edge_entry = Geometry::get_closest_point_to_segment_2d(p->entry, edge); - float distance = p->entry.distance_to(edge_entry) + p->distance; - -#else - - float distance = p->center.distance_to(e.C->center) + p->distance; - -#endif - - if (e.C->prev_edge != -1) { - //oh this was visited already, can we win the cost? - - if (e.C->distance > distance) { - - e.C->prev_edge = e.C_edge; - e.C->distance = distance; -#ifdef USE_ENTRY_POINT - e.C->entry = edge_entry; -#endif - } - } else { - //add to open neighbours - - e.C->prev_edge = e.C_edge; - e.C->distance = distance; -#ifdef USE_ENTRY_POINT - e.C->entry = edge_entry; -#endif - - open_list.push_back(e.C); - - if (e.C == end_poly) { - //oh my reached end! stop algorithm - found_route = true; - break; - } - } - } - - if (found_route) - break; - - open_list.erase(least_cost_poly); - } - - if (found_route) { - - Vector path; - - if (p_optimize) { - //string pulling - - Vector2 apex_point = end_point; - Vector2 portal_left = apex_point; - Vector2 portal_right = apex_point; - Polygon *left_poly = end_poly; - Polygon *right_poly = end_poly; - Polygon *p = end_poly; - - while (p) { - - Vector2 left; - Vector2 right; - -//#define CLOCK_TANGENT(m_a,m_b,m_c) ( ((m_a)-(m_c)).cross((m_a)-(m_b)) ) -#define CLOCK_TANGENT(m_a, m_b, m_c) ((((m_a).x - (m_c).x) * ((m_b).y - (m_c).y) - ((m_b).x - (m_c).x) * ((m_a).y - (m_c).y))) - - if (p == begin_poly) { - left = begin_point; - right = begin_point; - } else { - int prev = p->prev_edge; - int prev_n = (p->prev_edge + 1) % p->edges.size(); - left = _get_vertex(p->edges[prev].point); - right = _get_vertex(p->edges[prev_n].point); - - if (p->clockwise) { - SWAP(left, right); - } - /*if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5) < 0){ - SWAP(left,right); - }*/ - } - - bool skip = false; - - /* - print_line("-----\nAPEX: "+(apex_point-end_point)); - print_line("LEFT:"); - print_line("\tPortal: "+(portal_left-end_point)); - print_line("\tPoint: "+(left-end_point)); - print_line("\tLeft Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_left,left))); - print_line("\tLeft Distance: "+rtos(portal_left.distance_squared_to(apex_point))); - print_line("\tLeft Test: "+rtos(CLOCK_TANGENT(apex_point,left,portal_right))); - print_line("RIGHT:"); - print_line("\tPortal: "+(portal_right-end_point)); - print_line("\tPoint: "+(right-end_point)); - print_line("\tRight Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_right,right))); - print_line("\tRight Distance: "+rtos(portal_right.distance_squared_to(apex_point))); - print_line("\tRight Test: "+rtos(CLOCK_TANGENT(apex_point,right,portal_left))); - */ - - if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) { - //process - if (portal_left.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, left, portal_right) > 0) { - left_poly = p; - portal_left = left; - } else { - - apex_point = portal_right; - p = right_poly; - left_poly = p; - portal_left = apex_point; - portal_right = apex_point; - if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON) - path.push_back(apex_point); - skip = true; - } - } - - if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) { - //process - if (portal_right.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, right, portal_left) < 0) { - right_poly = p; - portal_right = right; - } else { - - apex_point = portal_left; - p = left_poly; - right_poly = p; - portal_right = apex_point; - portal_left = apex_point; - if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON) - path.push_back(apex_point); - } - } - - if (p != begin_poly) - p = p->edges[p->prev_edge].C; - else - p = NULL; - } - - } else { - //midpoints - Polygon *p = end_poly; - - while (true) { - int prev = p->prev_edge; - int prev_n = (p->prev_edge + 1) % p->edges.size(); - Vector2 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5; - path.push_back(point); - p = p->edges[prev].C; - if (p == begin_poly) - break; - } - } - - if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) { - path.push_back(begin_point); // Add the begin point - } else { - path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point - } - - path.invert(); - - if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) { - path.push_back(end_point); // Add the end point - } else { - path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point - } - - return path; - } - - return Vector(); -} - -Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) { - - Vector2 closest_point = Vector2(); - float closest_point_d = 1e20; - - for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { - - if (!E->get().linked) - continue; - for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { - - Polygon &p = F->get(); - for (int i = 2; i < p.edges.size(); i++) { - - if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { - - return p_point; //inside triangle, nothing else to discuss - } - } - } - } - - for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { - - if (!E->get().linked) - continue; - for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { - - Polygon &p = F->get(); - int es = p.edges.size(); - for (int i = 0; i < es; i++) { - - Vector2 edge[2] = { - _get_vertex(p.edges[i].point), - _get_vertex(p.edges[(i + 1) % es].point) - }; - - Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge); - float d = spoint.distance_squared_to(p_point); - if (d < closest_point_d) { - - closest_point = spoint; - closest_point_d = d; - } - } - } - } - - return closest_point; -} - -Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) { - - Object *owner = NULL; - Vector2 closest_point = Vector2(); - float closest_point_d = 1e20; - - for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { - - if (!E->get().linked) - continue; - for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { - - Polygon &p = F->get(); - for (int i = 2; i < p.edges.size(); i++) { - - if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { - - return E->get().owner; - } - } - } - } - - for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { - - if (!E->get().linked) - continue; - for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { - - Polygon &p = F->get(); - int es = p.edges.size(); - for (int i = 0; i < es; i++) { - - Vector2 edge[2] = { - _get_vertex(p.edges[i].point), - _get_vertex(p.edges[(i + 1) % es].point) - }; - - Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge); - float d = spoint.distance_squared_to(p_point); - if (d < closest_point_d) { - - closest_point = spoint; - closest_point_d = d; - owner = E->get().owner; - } - } - } - } - - return owner; -} - -void Navigation2D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("navpoly_add", "mesh", "xform", "owner"), &Navigation2D::navpoly_add, DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("navpoly_set_transform", "id", "xform"), &Navigation2D::navpoly_set_transform); - ClassDB::bind_method(D_METHOD("navpoly_remove", "id"), &Navigation2D::navpoly_remove); - - ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation2D::get_simple_path, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation2D::get_closest_point); - ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation2D::get_closest_point_owner); -} - -Navigation2D::Navigation2D() { - - ERR_FAIL_COND(sizeof(Point) != 8); - cell_size = 1; // one pixel - last_id = 1; -} diff --git a/scene/2d/navigation2d.h b/scene/2d/navigation2d.h deleted file mode 100644 index fc1762221c..0000000000 --- a/scene/2d/navigation2d.h +++ /dev/null @@ -1,174 +0,0 @@ -/*************************************************************************/ -/* navigation2d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 NAVIGATION_2D_H -#define NAVIGATION_2D_H - -#include "scene/2d/navigation_polygon.h" -#include "scene/2d/node_2d.h" - -class Navigation2D : public Node2D { - - GDCLASS(Navigation2D, Node2D); - - union Point { - - struct { - int64_t x : 32; - int64_t y : 32; - }; - - uint64_t key; - bool operator<(const Point &p_key) const { return key < p_key.key; } - }; - - struct EdgeKey { - - Point a; - Point b; - - bool operator<(const EdgeKey &p_key) const { - return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key); - }; - - EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) : - a(p_a), - b(p_b) { - if (a.key > b.key) { - SWAP(a, b); - } - } - }; - - struct NavMesh; - struct Polygon; - - struct ConnectionPending { - - Polygon *polygon; - int edge; - }; - - struct Polygon { - - struct Edge { - Point point; - Polygon *C; //connection - int C_edge; - List::Element *P; - Edge() { - C = NULL; - C_edge = -1; - P = NULL; - } - }; - - Vector edges; - - Vector2 center; - Vector2 entry; - - float distance; - int prev_edge; - - bool clockwise; - - NavMesh *owner; - }; - - struct Connection { - - Polygon *A; - int A_edge; - Polygon *B; - int B_edge; - - List pending; - - Connection() { - A = NULL; - B = NULL; - A_edge = -1; - B_edge = -1; - } - }; - - Map connections; - - struct NavMesh { - - Object *owner; - Transform2D xform; - bool linked; - Ref navpoly; - List polygons; - }; - - _FORCE_INLINE_ Point _get_point(const Vector2 &p_pos) const { - - int x = int(Math::floor(p_pos.x / cell_size)); - int y = int(Math::floor(p_pos.y / cell_size)); - - Point p; - p.key = 0; - p.x = x; - p.y = y; - return p; - } - - _FORCE_INLINE_ Vector2 _get_vertex(const Point &p_point) const { - - return Vector2(p_point.x, p_point.y) * cell_size; - } - - void _navpoly_link(int p_id); - void _navpoly_unlink(int p_id); - - float cell_size; - Map navpoly_map; - int last_id; - -protected: - static void _bind_methods(); - -public: - //API should be as dynamic as possible - int navpoly_add(const Ref &p_mesh, const Transform2D &p_xform, Object *p_owner = NULL); - void navpoly_set_transform(int p_id, const Transform2D &p_xform); - void navpoly_remove(int p_id); - - Vector get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize = true); - Vector2 get_closest_point(const Vector2 &p_point); - Object *get_closest_point_owner(const Vector2 &p_point); - - Navigation2D(); -}; - -#endif // Navigation2D2D_H diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp new file mode 100644 index 0000000000..57e0a5b118 --- /dev/null +++ b/scene/2d/navigation_2d.cpp @@ -0,0 +1,739 @@ +/*************************************************************************/ +/* navigation_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "navigation_2d.h" + +#define USE_ENTRY_POINT + +void Navigation2D::_navpoly_link(int p_id) { + + ERR_FAIL_COND(!navpoly_map.has(p_id)); + NavMesh &nm = navpoly_map[p_id]; + ERR_FAIL_COND(nm.linked); + + PoolVector vertices = nm.navpoly->get_vertices(); + int len = vertices.size(); + if (len == 0) + return; + + PoolVector::Read r = vertices.read(); + + for (int i = 0; i < nm.navpoly->get_polygon_count(); i++) { + + //build + + List::Element *P = nm.polygons.push_back(Polygon()); + Polygon &p = P->get(); + p.owner = &nm; + + Vector poly = nm.navpoly->get_polygon(i); + int plen = poly.size(); + const int *indices = poly.ptr(); + bool valid = true; + p.edges.resize(plen); + + Vector2 center; + float sum = 0; + + for (int j = 0; j < plen; j++) { + + int idx = indices[j]; + if (idx < 0 || idx >= len) { + valid = false; + break; + } + + Polygon::Edge e; + Vector2 ep = nm.xform.xform(r[idx]); + center += ep; + e.point = _get_point(ep); + p.edges.write[j] = e; + + int idxn = indices[(j + 1) % plen]; + if (idxn < 0 || idxn >= len) { + valid = false; + break; + } + + Vector2 epn = nm.xform.xform(r[idxn]); + + sum += (epn.x - ep.x) * (epn.y + ep.y); + } + + p.clockwise = sum > 0; + + if (!valid) { + nm.polygons.pop_back(); + ERR_CONTINUE(!valid); + continue; + } + + p.center = center / plen; + + //connect + + for (int j = 0; j < plen; j++) { + + int next = (j + 1) % plen; + EdgeKey ek(p.edges[j].point, p.edges[next].point); + + Map::Element *C = connections.find(ek); + if (!C) { + + Connection c; + c.A = &p; + c.A_edge = j; + c.B = NULL; + c.B_edge = -1; + connections[ek] = c; + } else { + + if (C->get().B != NULL) { + ConnectionPending pending; + pending.polygon = &p; + pending.edge = j; + p.edges.write[j].P = C->get().pending.push_back(pending); + continue; + } + + C->get().B = &p; + C->get().B_edge = j; + C->get().A->edges.write[C->get().A_edge].C = &p; + C->get().A->edges.write[C->get().A_edge].C_edge = j; + p.edges.write[j].C = C->get().A; + p.edges.write[j].C_edge = C->get().A_edge; + //connection successful. + } + } + } + + nm.linked = true; +} + +void Navigation2D::_navpoly_unlink(int p_id) { + + ERR_FAIL_COND(!navpoly_map.has(p_id)); + NavMesh &nm = navpoly_map[p_id]; + ERR_FAIL_COND(!nm.linked); + + for (List::Element *E = nm.polygons.front(); E; E = E->next()) { + + Polygon &p = E->get(); + + int ec = p.edges.size(); + Polygon::Edge *edges = p.edges.ptrw(); + + for (int i = 0; i < ec; i++) { + int next = (i + 1) % ec; + + EdgeKey ek(edges[i].point, edges[next].point); + Map::Element *C = connections.find(ek); + ERR_CONTINUE(!C); + + if (edges[i].P) { + C->get().pending.erase(edges[i].P); + edges[i].P = NULL; + + } else if (C->get().B) { + //disconnect + + C->get().B->edges.write[C->get().B_edge].C = NULL; + C->get().B->edges.write[C->get().B_edge].C_edge = -1; + C->get().A->edges.write[C->get().A_edge].C = NULL; + C->get().A->edges.write[C->get().A_edge].C_edge = -1; + + if (C->get().A == &E->get()) { + + C->get().A = C->get().B; + C->get().A_edge = C->get().B_edge; + } + C->get().B = NULL; + C->get().B_edge = -1; + + if (C->get().pending.size()) { + //reconnect if something is pending + ConnectionPending cp = C->get().pending.front()->get(); + C->get().pending.pop_front(); + + C->get().B = cp.polygon; + C->get().B_edge = cp.edge; + C->get().A->edges.write[C->get().A_edge].C = cp.polygon; + C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge; + cp.polygon->edges.write[cp.edge].C = C->get().A; + cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge; + cp.polygon->edges.write[cp.edge].P = NULL; + } + + } else { + connections.erase(C); + //erase + } + } + } + + nm.polygons.clear(); + + nm.linked = false; +} + +int Navigation2D::navpoly_add(const Ref &p_mesh, const Transform2D &p_xform, Object *p_owner) { + + int id = last_id++; + NavMesh nm; + nm.linked = false; + nm.navpoly = p_mesh; + nm.xform = p_xform; + nm.owner = p_owner; + navpoly_map[id] = nm; + + _navpoly_link(id); + + return id; +} + +void Navigation2D::navpoly_set_transform(int p_id, const Transform2D &p_xform) { + + ERR_FAIL_COND(!navpoly_map.has(p_id)); + NavMesh &nm = navpoly_map[p_id]; + if (nm.xform == p_xform) + return; //bleh + _navpoly_unlink(p_id); + nm.xform = p_xform; + _navpoly_link(p_id); +} +void Navigation2D::navpoly_remove(int p_id) { + + ERR_FAIL_COND(!navpoly_map.has(p_id)); + _navpoly_unlink(p_id); + navpoly_map.erase(p_id); +} + +Vector Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) { + + Polygon *begin_poly = NULL; + Polygon *end_poly = NULL; + Vector2 begin_point; + Vector2 end_point; + float begin_d = 1e20; + float end_d = 1e20; + + //look for point inside triangle + + for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { + + if (!E->get().linked) + continue; + for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { + + Polygon &p = F->get(); + if (begin_d || end_d) { + for (int i = 2; i < p.edges.size(); i++) { + + if (begin_d > 0) { + + if (Geometry::is_point_in_triangle(p_start, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { + + begin_poly = &p; + begin_point = p_start; + begin_d = 0; + if (end_d == 0) + break; + } + } + + if (end_d > 0) { + + if (Geometry::is_point_in_triangle(p_end, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { + + end_poly = &p; + end_point = p_end; + end_d = 0; + if (begin_d == 0) + break; + } + } + } + } + + p.prev_edge = -1; + } + } + + //start or end not inside triangle.. look for closest segment :| + if (begin_d || end_d) { + for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { + + if (!E->get().linked) + continue; + for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { + + Polygon &p = F->get(); + int es = p.edges.size(); + for (int i = 0; i < es; i++) { + + Vector2 edge[2] = { + _get_vertex(p.edges[i].point), + _get_vertex(p.edges[(i + 1) % es].point) + }; + + if (begin_d > 0) { + Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_start, edge); + float d = spoint.distance_to(p_start); + if (d < begin_d) { + begin_poly = &p; + begin_point = spoint; + begin_d = d; + } + } + + if (end_d > 0) { + Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_end, edge); + float d = spoint.distance_to(p_end); + if (d < end_d) { + end_poly = &p; + end_point = spoint; + end_d = d; + } + } + } + } + } + } + + if (!begin_poly || !end_poly) { + + return Vector(); //no path + } + + if (begin_poly == end_poly) { + + Vector path; + path.resize(2); + path.write[0] = begin_point; + path.write[1] = end_point; + return path; + } + + bool found_route = false; + + List open_list; + + begin_poly->entry = p_start; + + for (int i = 0; i < begin_poly->edges.size(); i++) { + + if (begin_poly->edges[i].C) { + + begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge; +#ifdef USE_ENTRY_POINT + Vector2 edge[2] = { + _get_vertex(begin_poly->edges[i].point), + _get_vertex(begin_poly->edges[(i + 1) % begin_poly->edges.size()].point) + }; + + Vector2 entry = Geometry::get_closest_point_to_segment_2d(begin_poly->entry, edge); + begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry); + begin_poly->edges[i].C->entry = entry; +#else + begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center); +#endif + open_list.push_back(begin_poly->edges[i].C); + + if (begin_poly->edges[i].C == end_poly) { + found_route = true; + } + } + } + + while (!found_route) { + + if (open_list.size() == 0) { + break; + } + //check open list + + List::Element *least_cost_poly = NULL; + float least_cost = 1e30; + + //this could be faster (cache previous results) + for (List::Element *E = open_list.front(); E; E = E->next()) { + + Polygon *p = E->get(); + + float cost = p->distance; + +#ifdef USE_ENTRY_POINT + int es = p->edges.size(); + + float shortest_distance = 1e30; + + for (int i = 0; i < es; i++) { + Polygon::Edge &e = p->edges.write[i]; + + if (!e.C) + continue; + + Vector2 edge[2] = { + _get_vertex(p->edges[i].point), + _get_vertex(p->edges[(i + 1) % es].point) + }; + + Vector2 edge_point = Geometry::get_closest_point_to_segment_2d(p->entry, edge); + float dist = p->entry.distance_to(edge_point); + if (dist < shortest_distance) + shortest_distance = dist; + } + + cost += shortest_distance; +#else + cost += p->center.distance_to(end_point); +#endif + if (cost < least_cost) { + least_cost_poly = E; + least_cost = cost; + } + } + + Polygon *p = least_cost_poly->get(); + //open the neighbours for search + int es = p->edges.size(); + + for (int i = 0; i < es; i++) { + + Polygon::Edge &e = p->edges.write[i]; + + if (!e.C) + continue; + +#ifdef USE_ENTRY_POINT + Vector2 edge[2] = { + _get_vertex(p->edges[i].point), + _get_vertex(p->edges[(i + 1) % es].point) + }; + + Vector2 edge_entry = Geometry::get_closest_point_to_segment_2d(p->entry, edge); + float distance = p->entry.distance_to(edge_entry) + p->distance; + +#else + + float distance = p->center.distance_to(e.C->center) + p->distance; + +#endif + + if (e.C->prev_edge != -1) { + //oh this was visited already, can we win the cost? + + if (e.C->distance > distance) { + + e.C->prev_edge = e.C_edge; + e.C->distance = distance; +#ifdef USE_ENTRY_POINT + e.C->entry = edge_entry; +#endif + } + } else { + //add to open neighbours + + e.C->prev_edge = e.C_edge; + e.C->distance = distance; +#ifdef USE_ENTRY_POINT + e.C->entry = edge_entry; +#endif + + open_list.push_back(e.C); + + if (e.C == end_poly) { + //oh my reached end! stop algorithm + found_route = true; + break; + } + } + } + + if (found_route) + break; + + open_list.erase(least_cost_poly); + } + + if (found_route) { + + Vector path; + + if (p_optimize) { + //string pulling + + Vector2 apex_point = end_point; + Vector2 portal_left = apex_point; + Vector2 portal_right = apex_point; + Polygon *left_poly = end_poly; + Polygon *right_poly = end_poly; + Polygon *p = end_poly; + + while (p) { + + Vector2 left; + Vector2 right; + +//#define CLOCK_TANGENT(m_a,m_b,m_c) ( ((m_a)-(m_c)).cross((m_a)-(m_b)) ) +#define CLOCK_TANGENT(m_a, m_b, m_c) ((((m_a).x - (m_c).x) * ((m_b).y - (m_c).y) - ((m_b).x - (m_c).x) * ((m_a).y - (m_c).y))) + + if (p == begin_poly) { + left = begin_point; + right = begin_point; + } else { + int prev = p->prev_edge; + int prev_n = (p->prev_edge + 1) % p->edges.size(); + left = _get_vertex(p->edges[prev].point); + right = _get_vertex(p->edges[prev_n].point); + + if (p->clockwise) { + SWAP(left, right); + } + /*if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5) < 0){ + SWAP(left,right); + }*/ + } + + bool skip = false; + + /* + print_line("-----\nAPEX: "+(apex_point-end_point)); + print_line("LEFT:"); + print_line("\tPortal: "+(portal_left-end_point)); + print_line("\tPoint: "+(left-end_point)); + print_line("\tLeft Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_left,left))); + print_line("\tLeft Distance: "+rtos(portal_left.distance_squared_to(apex_point))); + print_line("\tLeft Test: "+rtos(CLOCK_TANGENT(apex_point,left,portal_right))); + print_line("RIGHT:"); + print_line("\tPortal: "+(portal_right-end_point)); + print_line("\tPoint: "+(right-end_point)); + print_line("\tRight Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_right,right))); + print_line("\tRight Distance: "+rtos(portal_right.distance_squared_to(apex_point))); + print_line("\tRight Test: "+rtos(CLOCK_TANGENT(apex_point,right,portal_left))); + */ + + if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) { + //process + if (portal_left.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, left, portal_right) > 0) { + left_poly = p; + portal_left = left; + } else { + + apex_point = portal_right; + p = right_poly; + left_poly = p; + portal_left = apex_point; + portal_right = apex_point; + if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON) + path.push_back(apex_point); + skip = true; + } + } + + if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) { + //process + if (portal_right.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, right, portal_left) < 0) { + right_poly = p; + portal_right = right; + } else { + + apex_point = portal_left; + p = left_poly; + right_poly = p; + portal_right = apex_point; + portal_left = apex_point; + if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON) + path.push_back(apex_point); + } + } + + if (p != begin_poly) + p = p->edges[p->prev_edge].C; + else + p = NULL; + } + + } else { + //midpoints + Polygon *p = end_poly; + + while (true) { + int prev = p->prev_edge; + int prev_n = (p->prev_edge + 1) % p->edges.size(); + Vector2 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5; + path.push_back(point); + p = p->edges[prev].C; + if (p == begin_poly) + break; + } + } + + if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) { + path.push_back(begin_point); // Add the begin point + } else { + path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point + } + + path.invert(); + + if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) { + path.push_back(end_point); // Add the end point + } else { + path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point + } + + return path; + } + + return Vector(); +} + +Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) { + + Vector2 closest_point = Vector2(); + float closest_point_d = 1e20; + + for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { + + if (!E->get().linked) + continue; + for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { + + Polygon &p = F->get(); + for (int i = 2; i < p.edges.size(); i++) { + + if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { + + return p_point; //inside triangle, nothing else to discuss + } + } + } + } + + for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { + + if (!E->get().linked) + continue; + for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { + + Polygon &p = F->get(); + int es = p.edges.size(); + for (int i = 0; i < es; i++) { + + Vector2 edge[2] = { + _get_vertex(p.edges[i].point), + _get_vertex(p.edges[(i + 1) % es].point) + }; + + Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge); + float d = spoint.distance_squared_to(p_point); + if (d < closest_point_d) { + + closest_point = spoint; + closest_point_d = d; + } + } + } + } + + return closest_point; +} + +Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) { + + Object *owner = NULL; + Vector2 closest_point = Vector2(); + float closest_point_d = 1e20; + + for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { + + if (!E->get().linked) + continue; + for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { + + Polygon &p = F->get(); + for (int i = 2; i < p.edges.size(); i++) { + + if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) { + + return E->get().owner; + } + } + } + } + + for (Map::Element *E = navpoly_map.front(); E; E = E->next()) { + + if (!E->get().linked) + continue; + for (List::Element *F = E->get().polygons.front(); F; F = F->next()) { + + Polygon &p = F->get(); + int es = p.edges.size(); + for (int i = 0; i < es; i++) { + + Vector2 edge[2] = { + _get_vertex(p.edges[i].point), + _get_vertex(p.edges[(i + 1) % es].point) + }; + + Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge); + float d = spoint.distance_squared_to(p_point); + if (d < closest_point_d) { + + closest_point = spoint; + closest_point_d = d; + owner = E->get().owner; + } + } + } + } + + return owner; +} + +void Navigation2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("navpoly_add", "mesh", "xform", "owner"), &Navigation2D::navpoly_add, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("navpoly_set_transform", "id", "xform"), &Navigation2D::navpoly_set_transform); + ClassDB::bind_method(D_METHOD("navpoly_remove", "id"), &Navigation2D::navpoly_remove); + + ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation2D::get_simple_path, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation2D::get_closest_point); + ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation2D::get_closest_point_owner); +} + +Navigation2D::Navigation2D() { + + ERR_FAIL_COND(sizeof(Point) != 8); + cell_size = 1; // one pixel + last_id = 1; +} diff --git a/scene/2d/navigation_2d.h b/scene/2d/navigation_2d.h new file mode 100644 index 0000000000..b4d659ff5c --- /dev/null +++ b/scene/2d/navigation_2d.h @@ -0,0 +1,174 @@ +/*************************************************************************/ +/* navigation_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 NAVIGATION_2D_H +#define NAVIGATION_2D_H + +#include "scene/2d/navigation_polygon.h" +#include "scene/2d/node_2d.h" + +class Navigation2D : public Node2D { + + GDCLASS(Navigation2D, Node2D); + + union Point { + + struct { + int64_t x : 32; + int64_t y : 32; + }; + + uint64_t key; + bool operator<(const Point &p_key) const { return key < p_key.key; } + }; + + struct EdgeKey { + + Point a; + Point b; + + bool operator<(const EdgeKey &p_key) const { + return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key); + }; + + EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) : + a(p_a), + b(p_b) { + if (a.key > b.key) { + SWAP(a, b); + } + } + }; + + struct NavMesh; + struct Polygon; + + struct ConnectionPending { + + Polygon *polygon; + int edge; + }; + + struct Polygon { + + struct Edge { + Point point; + Polygon *C; //connection + int C_edge; + List::Element *P; + Edge() { + C = NULL; + C_edge = -1; + P = NULL; + } + }; + + Vector edges; + + Vector2 center; + Vector2 entry; + + float distance; + int prev_edge; + + bool clockwise; + + NavMesh *owner; + }; + + struct Connection { + + Polygon *A; + int A_edge; + Polygon *B; + int B_edge; + + List pending; + + Connection() { + A = NULL; + B = NULL; + A_edge = -1; + B_edge = -1; + } + }; + + Map connections; + + struct NavMesh { + + Object *owner; + Transform2D xform; + bool linked; + Ref navpoly; + List polygons; + }; + + _FORCE_INLINE_ Point _get_point(const Vector2 &p_pos) const { + + int x = int(Math::floor(p_pos.x / cell_size)); + int y = int(Math::floor(p_pos.y / cell_size)); + + Point p; + p.key = 0; + p.x = x; + p.y = y; + return p; + } + + _FORCE_INLINE_ Vector2 _get_vertex(const Point &p_point) const { + + return Vector2(p_point.x, p_point.y) * cell_size; + } + + void _navpoly_link(int p_id); + void _navpoly_unlink(int p_id); + + float cell_size; + Map navpoly_map; + int last_id; + +protected: + static void _bind_methods(); + +public: + //API should be as dynamic as possible + int navpoly_add(const Ref &p_mesh, const Transform2D &p_xform, Object *p_owner = NULL); + void navpoly_set_transform(int p_id, const Transform2D &p_xform); + void navpoly_remove(int p_id); + + Vector get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize = true); + Vector2 get_closest_point(const Vector2 &p_point); + Object *get_closest_point_owner(const Vector2 &p_point); + + Navigation2D(); +}; + +#endif // NAVIGATION_2D_H diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index 50618c6baa..0f6af358bd 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -32,7 +32,7 @@ #include "core/core_string_names.h" #include "core/engine.h" -#include "navigation2d.h" +#include "navigation_2d.h" #include "thirdparty/misc/triangulator.h" diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp deleted file mode 100644 index fb1558a404..0000000000 --- a/scene/2d/screen_button.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/*************************************************************************/ -/* screen_button.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "screen_button.h" -#include "core/input_map.h" -#include "core/os/input.h" -#include "core/os/os.h" - -void TouchScreenButton::set_texture(const Ref &p_texture) { - - texture = p_texture; - update(); -} - -Ref TouchScreenButton::get_texture() const { - - return texture; -} - -void TouchScreenButton::set_texture_pressed(const Ref &p_texture_pressed) { - - texture_pressed = p_texture_pressed; - update(); -} - -Ref TouchScreenButton::get_texture_pressed() const { - - return texture_pressed; -} - -void TouchScreenButton::set_bitmask(const Ref &p_bitmask) { - - bitmask = p_bitmask; -} - -Ref TouchScreenButton::get_bitmask() const { - - return bitmask; -} - -void TouchScreenButton::set_shape(const Ref &p_shape) { - - if (shape.is_valid()) - shape->disconnect("changed", this, "update"); - - shape = p_shape; - - if (shape.is_valid()) - shape->connect("changed", this, "update"); - - update(); -} - -Ref TouchScreenButton::get_shape() const { - - return shape; -} - -void TouchScreenButton::set_shape_centered(bool p_shape_centered) { - - shape_centered = p_shape_centered; - update(); -} - -bool TouchScreenButton::is_shape_visible() const { - - return shape_visible; -} - -void TouchScreenButton::set_shape_visible(bool p_shape_visible) { - - shape_visible = p_shape_visible; - update(); -} - -bool TouchScreenButton::is_shape_centered() const { - - return shape_centered; -} - -void TouchScreenButton::_notification(int p_what) { - - switch (p_what) { - - case NOTIFICATION_DRAW: { - - if (!is_inside_tree()) - return; - if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) - return; - - if (finger_pressed != -1) { - - if (texture_pressed.is_valid()) - draw_texture(texture_pressed, Point2()); - else if (texture.is_valid()) - draw_texture(texture, Point2()); - - } else { - if (texture.is_valid()) - draw_texture(texture, Point2()); - } - - if (!shape_visible) - return; - if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) - return; - if (shape.is_valid()) { - Color draw_col = get_tree()->get_debug_collisions_color(); - Vector2 pos = shape_centered ? _edit_get_rect().size * 0.5f : Vector2(); - draw_set_transform_matrix(get_canvas_transform().translated(pos)); - shape->draw(get_canvas_item(), draw_col); - } - - } break; - case NOTIFICATION_ENTER_TREE: { - - if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) - return; - update(); - - if (!Engine::get_singleton()->is_editor_hint()) - set_process_input(is_visible_in_tree()); - - } break; - case NOTIFICATION_EXIT_TREE: { - if (is_pressed()) - _release(true); - } break; - case NOTIFICATION_VISIBILITY_CHANGED: { - if (Engine::get_singleton()->is_editor_hint()) - break; - if (is_visible_in_tree()) { - set_process_input(true); - } else { - set_process_input(false); - if (is_pressed()) - _release(); - } - } break; - case NOTIFICATION_PAUSED: { - if (is_pressed()) - _release(); - } break; - } -} - -bool TouchScreenButton::is_pressed() const { - - return finger_pressed != -1; -} - -void TouchScreenButton::set_action(const String &p_action) { - - action = p_action; -} - -String TouchScreenButton::get_action() const { - - return action; -} - -void TouchScreenButton::_input(const Ref &p_event) { - - if (!get_tree()) - return; - - if (p_event->get_device() != 0) - return; - - ERR_FAIL_COND(!is_visible_in_tree()); - - const InputEventScreenTouch *st = Object::cast_to(*p_event); - - if (passby_press) { - - const InputEventScreenDrag *sd = Object::cast_to(*p_event); - - if (st && !st->is_pressed() && finger_pressed == st->get_index()) { - - _release(); - } - - if ((st && st->is_pressed()) || sd) { - - int index = st ? st->get_index() : sd->get_index(); - Point2 coord = st ? st->get_position() : sd->get_position(); - - if (finger_pressed == -1 || index == finger_pressed) { - - if (_is_point_inside(coord)) { - if (finger_pressed == -1) { - _press(index); - } - } else { - if (finger_pressed != -1) { - _release(); - } - } - } - } - - } else { - - if (st) { - - if (st->is_pressed()) { - - const bool can_press = finger_pressed == -1; - if (!can_press) - return; //already fingering - - if (_is_point_inside(st->get_position())) { - _press(st->get_index()); - } - } else { - if (st->get_index() == finger_pressed) { - _release(); - } - } - } - } -} - -bool TouchScreenButton::_is_point_inside(const Point2 &p_point) { - - Point2 coord = (get_global_transform_with_canvas()).affine_inverse().xform(p_point); - Rect2 item_rect = _edit_get_rect(); - - bool touched = false; - bool check_rect = true; - - if (shape.is_valid()) { - - check_rect = false; - Transform2D xform = shape_centered ? Transform2D().translated(item_rect.size * 0.5f) : Transform2D(); - touched = shape->collide(xform, unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5))); - } - - if (bitmask.is_valid()) { - - check_rect = false; - if (!touched && Rect2(Point2(), bitmask->get_size()).has_point(coord)) { - - if (bitmask->get_bit(coord)) - touched = true; - } - } - - if (!touched && check_rect) { - if (texture.is_valid()) - touched = item_rect.has_point(coord); - } - - return touched; -} - -void TouchScreenButton::_press(int p_finger_pressed) { - - finger_pressed = p_finger_pressed; - - if (action != StringName()) { - - Input::get_singleton()->action_press(action); - Ref iea; - iea.instance(); - iea->set_action(action); - iea->set_pressed(true); - get_tree()->input_event(iea); - } - - emit_signal("pressed"); - update(); -} - -void TouchScreenButton::_release(bool p_exiting_tree) { - - finger_pressed = -1; - - if (action != StringName()) { - - Input::get_singleton()->action_release(action); - if (!p_exiting_tree) { - - Ref iea; - iea.instance(); - iea->set_action(action); - iea->set_pressed(false); - get_tree()->input_event(iea); - } - } - - if (!p_exiting_tree) { - emit_signal("released"); - update(); - } -} - -Rect2 TouchScreenButton::_edit_get_rect() const { - if (texture.is_null()) - return CanvasItem::_edit_get_rect(); - - return Rect2(Size2(), texture->get_size()); -} - -bool TouchScreenButton::_edit_use_rect() const { - return !texture.is_null(); -} - -Rect2 TouchScreenButton::get_anchorable_rect() const { - if (texture.is_null()) - return CanvasItem::get_anchorable_rect(); - - return Rect2(Size2(), texture->get_size()); -} - -void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) { - visibility = p_mode; - update(); -} - -TouchScreenButton::VisibilityMode TouchScreenButton::get_visibility_mode() const { - - return visibility; -} - -void TouchScreenButton::set_passby_press(bool p_enable) { - - passby_press = p_enable; -} - -bool TouchScreenButton::is_passby_press_enabled() const { - - return passby_press; -} - -void TouchScreenButton::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TouchScreenButton::set_texture); - ClassDB::bind_method(D_METHOD("get_texture"), &TouchScreenButton::get_texture); - - ClassDB::bind_method(D_METHOD("set_texture_pressed", "texture_pressed"), &TouchScreenButton::set_texture_pressed); - ClassDB::bind_method(D_METHOD("get_texture_pressed"), &TouchScreenButton::get_texture_pressed); - - ClassDB::bind_method(D_METHOD("set_bitmask", "bitmask"), &TouchScreenButton::set_bitmask); - ClassDB::bind_method(D_METHOD("get_bitmask"), &TouchScreenButton::get_bitmask); - - ClassDB::bind_method(D_METHOD("set_shape", "shape"), &TouchScreenButton::set_shape); - ClassDB::bind_method(D_METHOD("get_shape"), &TouchScreenButton::get_shape); - - ClassDB::bind_method(D_METHOD("set_shape_centered", "bool"), &TouchScreenButton::set_shape_centered); - ClassDB::bind_method(D_METHOD("is_shape_centered"), &TouchScreenButton::is_shape_centered); - - ClassDB::bind_method(D_METHOD("set_shape_visible", "bool"), &TouchScreenButton::set_shape_visible); - ClassDB::bind_method(D_METHOD("is_shape_visible"), &TouchScreenButton::is_shape_visible); - - ClassDB::bind_method(D_METHOD("set_action", "action"), &TouchScreenButton::set_action); - ClassDB::bind_method(D_METHOD("get_action"), &TouchScreenButton::get_action); - - ClassDB::bind_method(D_METHOD("set_visibility_mode", "mode"), &TouchScreenButton::set_visibility_mode); - ClassDB::bind_method(D_METHOD("get_visibility_mode"), &TouchScreenButton::get_visibility_mode); - - ClassDB::bind_method(D_METHOD("set_passby_press", "enabled"), &TouchScreenButton::set_passby_press); - ClassDB::bind_method(D_METHOD("is_passby_press_enabled"), &TouchScreenButton::is_passby_press_enabled); - - ClassDB::bind_method(D_METHOD("is_pressed"), &TouchScreenButton::is_pressed); - - ClassDB::bind_method(D_METHOD("_input"), &TouchScreenButton::_input); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture_pressed", "get_texture_pressed"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "bitmask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_bitmask", "get_bitmask"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_centered"), "set_shape_centered", "is_shape_centered"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_visible"), "set_shape_visible", "is_shape_visible"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "passby_press"), "set_passby_press", "is_passby_press_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), "set_action", "get_action"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_mode", PROPERTY_HINT_ENUM, "Always,TouchScreen Only"), "set_visibility_mode", "get_visibility_mode"); - - ADD_SIGNAL(MethodInfo("pressed")); - ADD_SIGNAL(MethodInfo("released")); - - BIND_ENUM_CONSTANT(VISIBILITY_ALWAYS); - BIND_ENUM_CONSTANT(VISIBILITY_TOUCHSCREEN_ONLY); -} - -TouchScreenButton::TouchScreenButton() { - - finger_pressed = -1; - passby_press = false; - visibility = VISIBILITY_ALWAYS; - shape_centered = true; - shape_visible = true; - unit_rect = Ref(memnew(RectangleShape2D)); - unit_rect->set_extents(Vector2(0.5, 0.5)); -} diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h deleted file mode 100644 index fd944ead64..0000000000 --- a/scene/2d/screen_button.h +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************/ -/* screen_button.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 SCREEN_BUTTON_H -#define SCREEN_BUTTON_H - -#include "scene/2d/node_2d.h" -#include "scene/resources/bit_mask.h" -#include "scene/resources/rectangle_shape_2d.h" -#include "scene/resources/texture.h" - -class TouchScreenButton : public Node2D { - - GDCLASS(TouchScreenButton, Node2D); - -public: - enum VisibilityMode { - VISIBILITY_ALWAYS, - VISIBILITY_TOUCHSCREEN_ONLY - }; - -private: - Ref texture; - Ref texture_pressed; - Ref bitmask; - Ref shape; - bool shape_centered; - bool shape_visible; - - Ref unit_rect; - - StringName action; - bool passby_press; - int finger_pressed; - - VisibilityMode visibility; - - void _input(const Ref &p_event); - - bool _is_point_inside(const Point2 &p_point); - - void _press(int p_finger_pressed); - void _release(bool p_exiting_tree = false); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_texture(const Ref &p_texture); - Ref get_texture() const; - - void set_texture_pressed(const Ref &p_texture_pressed); - Ref get_texture_pressed() const; - - void set_bitmask(const Ref &p_bitmask); - Ref get_bitmask() const; - - void set_shape(const Ref &p_shape); - Ref get_shape() const; - - void set_shape_centered(bool p_shape_centered); - bool is_shape_centered() const; - - void set_shape_visible(bool p_shape_visible); - bool is_shape_visible() const; - - void set_action(const String &p_action); - String get_action() const; - - void set_passby_press(bool p_enable); - bool is_passby_press_enabled() const; - - void set_visibility_mode(VisibilityMode p_mode); - VisibilityMode get_visibility_mode() const; - - bool is_pressed() const; - - virtual Rect2 _edit_get_rect() const; - virtual bool _edit_use_rect() const; - virtual Rect2 get_anchorable_rect() const; - - TouchScreenButton(); -}; - -VARIANT_ENUM_CAST(TouchScreenButton::VisibilityMode); - -#endif // SCREEN_BUTTON_H diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index a44098fd77..e450e1e256 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -33,7 +33,7 @@ #include "core/self_list.h" #include "core/vset.h" -#include "scene/2d/navigation2d.h" +#include "scene/2d/navigation_2d.h" #include "scene/2d/node_2d.h" #include "scene/resources/tile_set.h" diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp new file mode 100644 index 0000000000..9a1a759e72 --- /dev/null +++ b/scene/2d/touch_screen_button.cpp @@ -0,0 +1,424 @@ +/*************************************************************************/ +/* touch_screen_button.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "touch_screen_button.h" + +#include "core/input_map.h" +#include "core/os/input.h" +#include "core/os/os.h" + +void TouchScreenButton::set_texture(const Ref &p_texture) { + + texture = p_texture; + update(); +} + +Ref TouchScreenButton::get_texture() const { + + return texture; +} + +void TouchScreenButton::set_texture_pressed(const Ref &p_texture_pressed) { + + texture_pressed = p_texture_pressed; + update(); +} + +Ref TouchScreenButton::get_texture_pressed() const { + + return texture_pressed; +} + +void TouchScreenButton::set_bitmask(const Ref &p_bitmask) { + + bitmask = p_bitmask; +} + +Ref TouchScreenButton::get_bitmask() const { + + return bitmask; +} + +void TouchScreenButton::set_shape(const Ref &p_shape) { + + if (shape.is_valid()) + shape->disconnect("changed", this, "update"); + + shape = p_shape; + + if (shape.is_valid()) + shape->connect("changed", this, "update"); + + update(); +} + +Ref TouchScreenButton::get_shape() const { + + return shape; +} + +void TouchScreenButton::set_shape_centered(bool p_shape_centered) { + + shape_centered = p_shape_centered; + update(); +} + +bool TouchScreenButton::is_shape_visible() const { + + return shape_visible; +} + +void TouchScreenButton::set_shape_visible(bool p_shape_visible) { + + shape_visible = p_shape_visible; + update(); +} + +bool TouchScreenButton::is_shape_centered() const { + + return shape_centered; +} + +void TouchScreenButton::_notification(int p_what) { + + switch (p_what) { + + case NOTIFICATION_DRAW: { + + if (!is_inside_tree()) + return; + if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) + return; + + if (finger_pressed != -1) { + + if (texture_pressed.is_valid()) + draw_texture(texture_pressed, Point2()); + else if (texture.is_valid()) + draw_texture(texture, Point2()); + + } else { + if (texture.is_valid()) + draw_texture(texture, Point2()); + } + + if (!shape_visible) + return; + if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) + return; + if (shape.is_valid()) { + Color draw_col = get_tree()->get_debug_collisions_color(); + Vector2 pos = shape_centered ? _edit_get_rect().size * 0.5f : Vector2(); + draw_set_transform_matrix(get_canvas_transform().translated(pos)); + shape->draw(get_canvas_item(), draw_col); + } + + } break; + case NOTIFICATION_ENTER_TREE: { + + if (!Engine::get_singleton()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) + return; + update(); + + if (!Engine::get_singleton()->is_editor_hint()) + set_process_input(is_visible_in_tree()); + + } break; + case NOTIFICATION_EXIT_TREE: { + if (is_pressed()) + _release(true); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (Engine::get_singleton()->is_editor_hint()) + break; + if (is_visible_in_tree()) { + set_process_input(true); + } else { + set_process_input(false); + if (is_pressed()) + _release(); + } + } break; + case NOTIFICATION_PAUSED: { + if (is_pressed()) + _release(); + } break; + } +} + +bool TouchScreenButton::is_pressed() const { + + return finger_pressed != -1; +} + +void TouchScreenButton::set_action(const String &p_action) { + + action = p_action; +} + +String TouchScreenButton::get_action() const { + + return action; +} + +void TouchScreenButton::_input(const Ref &p_event) { + + if (!get_tree()) + return; + + if (p_event->get_device() != 0) + return; + + ERR_FAIL_COND(!is_visible_in_tree()); + + const InputEventScreenTouch *st = Object::cast_to(*p_event); + + if (passby_press) { + + const InputEventScreenDrag *sd = Object::cast_to(*p_event); + + if (st && !st->is_pressed() && finger_pressed == st->get_index()) { + + _release(); + } + + if ((st && st->is_pressed()) || sd) { + + int index = st ? st->get_index() : sd->get_index(); + Point2 coord = st ? st->get_position() : sd->get_position(); + + if (finger_pressed == -1 || index == finger_pressed) { + + if (_is_point_inside(coord)) { + if (finger_pressed == -1) { + _press(index); + } + } else { + if (finger_pressed != -1) { + _release(); + } + } + } + } + + } else { + + if (st) { + + if (st->is_pressed()) { + + const bool can_press = finger_pressed == -1; + if (!can_press) + return; //already fingering + + if (_is_point_inside(st->get_position())) { + _press(st->get_index()); + } + } else { + if (st->get_index() == finger_pressed) { + _release(); + } + } + } + } +} + +bool TouchScreenButton::_is_point_inside(const Point2 &p_point) { + + Point2 coord = (get_global_transform_with_canvas()).affine_inverse().xform(p_point); + Rect2 item_rect = _edit_get_rect(); + + bool touched = false; + bool check_rect = true; + + if (shape.is_valid()) { + + check_rect = false; + Transform2D xform = shape_centered ? Transform2D().translated(item_rect.size * 0.5f) : Transform2D(); + touched = shape->collide(xform, unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5))); + } + + if (bitmask.is_valid()) { + + check_rect = false; + if (!touched && Rect2(Point2(), bitmask->get_size()).has_point(coord)) { + + if (bitmask->get_bit(coord)) + touched = true; + } + } + + if (!touched && check_rect) { + if (texture.is_valid()) + touched = item_rect.has_point(coord); + } + + return touched; +} + +void TouchScreenButton::_press(int p_finger_pressed) { + + finger_pressed = p_finger_pressed; + + if (action != StringName()) { + + Input::get_singleton()->action_press(action); + Ref iea; + iea.instance(); + iea->set_action(action); + iea->set_pressed(true); + get_tree()->input_event(iea); + } + + emit_signal("pressed"); + update(); +} + +void TouchScreenButton::_release(bool p_exiting_tree) { + + finger_pressed = -1; + + if (action != StringName()) { + + Input::get_singleton()->action_release(action); + if (!p_exiting_tree) { + + Ref iea; + iea.instance(); + iea->set_action(action); + iea->set_pressed(false); + get_tree()->input_event(iea); + } + } + + if (!p_exiting_tree) { + emit_signal("released"); + update(); + } +} + +Rect2 TouchScreenButton::_edit_get_rect() const { + if (texture.is_null()) + return CanvasItem::_edit_get_rect(); + + return Rect2(Size2(), texture->get_size()); +} + +bool TouchScreenButton::_edit_use_rect() const { + return !texture.is_null(); +} + +Rect2 TouchScreenButton::get_anchorable_rect() const { + if (texture.is_null()) + return CanvasItem::get_anchorable_rect(); + + return Rect2(Size2(), texture->get_size()); +} + +void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) { + visibility = p_mode; + update(); +} + +TouchScreenButton::VisibilityMode TouchScreenButton::get_visibility_mode() const { + + return visibility; +} + +void TouchScreenButton::set_passby_press(bool p_enable) { + + passby_press = p_enable; +} + +bool TouchScreenButton::is_passby_press_enabled() const { + + return passby_press; +} + +void TouchScreenButton::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TouchScreenButton::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &TouchScreenButton::get_texture); + + ClassDB::bind_method(D_METHOD("set_texture_pressed", "texture_pressed"), &TouchScreenButton::set_texture_pressed); + ClassDB::bind_method(D_METHOD("get_texture_pressed"), &TouchScreenButton::get_texture_pressed); + + ClassDB::bind_method(D_METHOD("set_bitmask", "bitmask"), &TouchScreenButton::set_bitmask); + ClassDB::bind_method(D_METHOD("get_bitmask"), &TouchScreenButton::get_bitmask); + + ClassDB::bind_method(D_METHOD("set_shape", "shape"), &TouchScreenButton::set_shape); + ClassDB::bind_method(D_METHOD("get_shape"), &TouchScreenButton::get_shape); + + ClassDB::bind_method(D_METHOD("set_shape_centered", "bool"), &TouchScreenButton::set_shape_centered); + ClassDB::bind_method(D_METHOD("is_shape_centered"), &TouchScreenButton::is_shape_centered); + + ClassDB::bind_method(D_METHOD("set_shape_visible", "bool"), &TouchScreenButton::set_shape_visible); + ClassDB::bind_method(D_METHOD("is_shape_visible"), &TouchScreenButton::is_shape_visible); + + ClassDB::bind_method(D_METHOD("set_action", "action"), &TouchScreenButton::set_action); + ClassDB::bind_method(D_METHOD("get_action"), &TouchScreenButton::get_action); + + ClassDB::bind_method(D_METHOD("set_visibility_mode", "mode"), &TouchScreenButton::set_visibility_mode); + ClassDB::bind_method(D_METHOD("get_visibility_mode"), &TouchScreenButton::get_visibility_mode); + + ClassDB::bind_method(D_METHOD("set_passby_press", "enabled"), &TouchScreenButton::set_passby_press); + ClassDB::bind_method(D_METHOD("is_passby_press_enabled"), &TouchScreenButton::is_passby_press_enabled); + + ClassDB::bind_method(D_METHOD("is_pressed"), &TouchScreenButton::is_pressed); + + ClassDB::bind_method(D_METHOD("_input"), &TouchScreenButton::_input); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture_pressed", "get_texture_pressed"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "bitmask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_bitmask", "get_bitmask"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_centered"), "set_shape_centered", "is_shape_centered"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_visible"), "set_shape_visible", "is_shape_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "passby_press"), "set_passby_press", "is_passby_press_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), "set_action", "get_action"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_mode", PROPERTY_HINT_ENUM, "Always,TouchScreen Only"), "set_visibility_mode", "get_visibility_mode"); + + ADD_SIGNAL(MethodInfo("pressed")); + ADD_SIGNAL(MethodInfo("released")); + + BIND_ENUM_CONSTANT(VISIBILITY_ALWAYS); + BIND_ENUM_CONSTANT(VISIBILITY_TOUCHSCREEN_ONLY); +} + +TouchScreenButton::TouchScreenButton() { + + finger_pressed = -1; + passby_press = false; + visibility = VISIBILITY_ALWAYS; + shape_centered = true; + shape_visible = true; + unit_rect = Ref(memnew(RectangleShape2D)); + unit_rect->set_extents(Vector2(0.5, 0.5)); +} diff --git a/scene/2d/touch_screen_button.h b/scene/2d/touch_screen_button.h new file mode 100644 index 0000000000..df54e5340b --- /dev/null +++ b/scene/2d/touch_screen_button.h @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* touch_screen_button.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 TOUCH_SCREEN_BUTTON_H +#define TOUCH_SCREEN_BUTTON_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/bit_map.h" +#include "scene/resources/rectangle_shape_2d.h" +#include "scene/resources/texture.h" + +class TouchScreenButton : public Node2D { + + GDCLASS(TouchScreenButton, Node2D); + +public: + enum VisibilityMode { + VISIBILITY_ALWAYS, + VISIBILITY_TOUCHSCREEN_ONLY + }; + +private: + Ref texture; + Ref texture_pressed; + Ref bitmask; + Ref shape; + bool shape_centered; + bool shape_visible; + + Ref unit_rect; + + StringName action; + bool passby_press; + int finger_pressed; + + VisibilityMode visibility; + + void _input(const Ref &p_event); + + bool _is_point_inside(const Point2 &p_point); + + void _press(int p_finger_pressed); + void _release(bool p_exiting_tree = false); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_texture(const Ref &p_texture); + Ref get_texture() const; + + void set_texture_pressed(const Ref &p_texture_pressed); + Ref get_texture_pressed() const; + + void set_bitmask(const Ref &p_bitmask); + Ref get_bitmask() const; + + void set_shape(const Ref &p_shape); + Ref get_shape() const; + + void set_shape_centered(bool p_shape_centered); + bool is_shape_centered() const; + + void set_shape_visible(bool p_shape_visible); + bool is_shape_visible() const; + + void set_action(const String &p_action); + String get_action() const; + + void set_passby_press(bool p_enable); + bool is_passby_press_enabled() const; + + void set_visibility_mode(VisibilityMode p_mode); + VisibilityMode get_visibility_mode() const; + + bool is_pressed() const; + + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; + virtual Rect2 get_anchorable_rect() const; + + TouchScreenButton(); +}; + +VARIANT_ENUM_CAST(TouchScreenButton::VisibilityMode); + +#endif // TOUCH_SCREEN_BUTTON_H diff --git a/scene/3d/SCsub b/scene/3d/SCsub index 35cc7479d8..200cf4316f 100644 --- a/scene/3d/SCsub +++ b/scene/3d/SCsub @@ -7,6 +7,6 @@ if env['disable_3d']: env.scene_sources.append("3d/skeleton.cpp") env.scene_sources.append("3d/particles.cpp") env.scene_sources.append("3d/visual_instance.cpp") - env.scene_sources.append("3d/scenario_fx.cpp") + env.scene_sources.append("3d/world_environment.cpp") else: env.add_source_files(env.scene_sources, "*.cpp") diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 2921a3a881..48d65b79f7 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -32,7 +32,7 @@ #define REFLECTIONPROBE_H #include "scene/3d/visual_instance.h" -#include "scene/resources/sky_box.h" +#include "scene/resources/sky.h" #include "scene/resources/texture.h" #include "servers/visual_server.h" diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp deleted file mode 100644 index ad9b61e1ff..0000000000 --- a/scene/3d/scenario_fx.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************/ -/* scenario_fx.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "scenario_fx.h" -#include "scene/main/viewport.h" - -void WorldEnvironment::_notification(int p_what) { - - if (p_what == Spatial::NOTIFICATION_ENTER_WORLD || p_what == Spatial::NOTIFICATION_ENTER_TREE) { - - if (environment.is_valid()) { - if (get_viewport()->find_world()->get_environment().is_valid()) { - WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); - } - get_viewport()->find_world()->set_environment(environment); - add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); - } - - } else if (p_what == Spatial::NOTIFICATION_EXIT_WORLD || p_what == Spatial::NOTIFICATION_EXIT_TREE) { - - if (environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) { - get_viewport()->find_world()->set_environment(Ref()); - remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); - } - } -} - -void WorldEnvironment::set_environment(const Ref &p_environment) { - - if (is_inside_tree() && environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) { - get_viewport()->find_world()->set_environment(Ref()); - remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); - //clean up - } - - environment = p_environment; - if (is_inside_tree() && environment.is_valid()) { - if (get_viewport()->find_world()->get_environment().is_valid()) { - WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); - } - get_viewport()->find_world()->set_environment(environment); - add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); - } - - update_configuration_warning(); -} - -Ref WorldEnvironment::get_environment() const { - - return environment; -} - -String WorldEnvironment::get_configuration_warning() const { - - if (!environment.is_valid()) { - return TTR("WorldEnvironment needs an Environment resource."); - } - - if (/*!is_visible_in_tree() ||*/ !is_inside_tree()) - return String(); - - List nodes; - get_tree()->get_nodes_in_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()), &nodes); - - if (nodes.size() > 1) { - return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."); - } - - // Commenting this warning for now, I think it makes no sense. If anyone can figure out what its supposed to do, feedback welcome. Else it should be deprecated. - //if (environment.is_valid() && get_viewport() && !get_viewport()->get_camera() && environment->get_background() != Environment::BG_CANVAS) { - // return TTR("This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set this environment's Background Mode to Canvas (for 2D scenes)."); - //} - - return String(); -} - -void WorldEnvironment::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_environment", "env"), &WorldEnvironment::set_environment); - ClassDB::bind_method(D_METHOD("get_environment"), &WorldEnvironment::get_environment); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); -} - -WorldEnvironment::WorldEnvironment() { -} diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h deleted file mode 100644 index 6317dae75d..0000000000 --- a/scene/3d/scenario_fx.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************/ -/* scenario_fx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 SCENARIO_FX_H -#define SCENARIO_FX_H - -#include "scene/3d/spatial.h" - -/** - @author Juan Linietsky -*/ - -class WorldEnvironment : public Node { - - GDCLASS(WorldEnvironment, Node); - - Ref environment; - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_environment(const Ref &p_environment); - Ref get_environment() const; - - String get_configuration_warning() const; - - WorldEnvironment(); -}; - -#endif diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp new file mode 100644 index 0000000000..3cd43cbf5b --- /dev/null +++ b/scene/3d/world_environment.cpp @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* world_environment.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "world_environment.h" +#include "scene/main/viewport.h" + +void WorldEnvironment::_notification(int p_what) { + + if (p_what == Spatial::NOTIFICATION_ENTER_WORLD || p_what == Spatial::NOTIFICATION_ENTER_TREE) { + + if (environment.is_valid()) { + if (get_viewport()->find_world()->get_environment().is_valid()) { + WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); + } + get_viewport()->find_world()->set_environment(environment); + add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); + } + + } else if (p_what == Spatial::NOTIFICATION_EXIT_WORLD || p_what == Spatial::NOTIFICATION_EXIT_TREE) { + + if (environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) { + get_viewport()->find_world()->set_environment(Ref()); + remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); + } + } +} + +void WorldEnvironment::set_environment(const Ref &p_environment) { + + if (is_inside_tree() && environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) { + get_viewport()->find_world()->set_environment(Ref()); + remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); + //clean up + } + + environment = p_environment; + if (is_inside_tree() && environment.is_valid()) { + if (get_viewport()->find_world()->get_environment().is_valid()) { + WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); + } + get_viewport()->find_world()->set_environment(environment); + add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id())); + } + + update_configuration_warning(); +} + +Ref WorldEnvironment::get_environment() const { + + return environment; +} + +String WorldEnvironment::get_configuration_warning() const { + + if (!environment.is_valid()) { + return TTR("WorldEnvironment needs an Environment resource."); + } + + if (/*!is_visible_in_tree() ||*/ !is_inside_tree()) + return String(); + + List nodes; + get_tree()->get_nodes_in_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()), &nodes); + + if (nodes.size() > 1) { + return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."); + } + + // Commenting this warning for now, I think it makes no sense. If anyone can figure out what its supposed to do, feedback welcome. Else it should be deprecated. + //if (environment.is_valid() && get_viewport() && !get_viewport()->get_camera() && environment->get_background() != Environment::BG_CANVAS) { + // return TTR("This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set this environment's Background Mode to Canvas (for 2D scenes)."); + //} + + return String(); +} + +void WorldEnvironment::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_environment", "env"), &WorldEnvironment::set_environment); + ClassDB::bind_method(D_METHOD("get_environment"), &WorldEnvironment::get_environment); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); +} + +WorldEnvironment::WorldEnvironment() { +} diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h new file mode 100644 index 0000000000..bf36a0a532 --- /dev/null +++ b/scene/3d/world_environment.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* world_environment.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 SCENARIO_FX_H +#define SCENARIO_FX_H + +#include "scene/3d/spatial.h" + +/** + @author Juan Linietsky +*/ + +class WorldEnvironment : public Node { + + GDCLASS(WorldEnvironment, Node); + + Ref environment; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_environment(const Ref &p_environment); + Ref get_environment() const; + + String get_configuration_warning() const; + + WorldEnvironment(); +}; + +#endif diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp deleted file mode 100644 index 4eae3b04e7..0000000000 --- a/scene/audio/audio_player.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/*************************************************************************/ -/* audio_player.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "audio_player.h" - -#include "core/engine.h" - -void AudioStreamPlayer::_mix_internal(bool p_fadeout) { - - int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); - - //get data - AudioFrame *buffer = mix_buffer.ptrw(); - int buffer_size = mix_buffer.size(); - - if (p_fadeout) { - // Short fadeout ramp - buffer_size = MIN(buffer_size, 128); - } - - stream_playback->mix(buffer, pitch_scale, buffer_size); - - //multiply volume interpolating to avoid clicks if this changes - float target_volume = p_fadeout ? -80.0 : volume_db; - float vol = Math::db2linear(mix_volume_db); - float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size); - - for (int i = 0; i < buffer_size; i++) { - buffer[i] *= vol; - vol += vol_inc; - } - - //set volume for next mix - mix_volume_db = target_volume; - - AudioFrame *targets[4] = { NULL, NULL, NULL, NULL }; - - if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) { - targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0); - } else { - switch (mix_target) { - case MIX_TARGET_STEREO: { - targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0); - } break; - case MIX_TARGET_SURROUND: { - for (int i = 0; i < AudioServer::get_singleton()->get_channel_count(); i++) { - targets[i] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, i); - } - } break; - case MIX_TARGET_CENTER: { - targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1); - } break; - } - } - - for (int c = 0; c < 4; c++) { - if (!targets[c]) - break; - for (int i = 0; i < buffer_size; i++) { - targets[c][i] += buffer[i]; - } - } -} - -void AudioStreamPlayer::_mix_audio() { - - if (!stream_playback.is_valid() || !active) - return; - - if (stream_paused) { - if (stream_paused_fade) { - _mix_internal(true); - stream_paused_fade = false; - } - return; - } - - if (setseek >= 0.0) { - if (stream_playback->is_playing()) { - - //fade out to avoid pops - _mix_internal(true); - } - stream_playback->start(setseek); - setseek = -1.0; //reset seek - mix_volume_db = volume_db; //reset ramp - } - - _mix_internal(false); -} - -void AudioStreamPlayer::_notification(int p_what) { - - if (p_what == NOTIFICATION_ENTER_TREE) { - - AudioServer::get_singleton()->add_callback(_mix_audios, this); - if (autoplay && !Engine::get_singleton()->is_editor_hint()) { - play(); - } - } - - if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - - if (!active || (setseek < 0 && !stream_playback->is_playing())) { - active = false; - set_process_internal(false); - emit_signal("finished"); - } - } - - if (p_what == NOTIFICATION_EXIT_TREE) { - - AudioServer::get_singleton()->remove_callback(_mix_audios, this); - } - - if (p_what == NOTIFICATION_PAUSED) { - if (!can_process()) { - // Node can't process so we start fading out to silence - set_stream_paused(true); - } - } - - if (p_what == NOTIFICATION_UNPAUSED) { - set_stream_paused(false); - } -} - -void AudioStreamPlayer::set_stream(Ref p_stream) { - - AudioServer::get_singleton()->lock(); - - mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); - - if (stream_playback.is_valid()) { - stream_playback.unref(); - stream.unref(); - active = false; - setseek = -1; - } - - if (p_stream.is_valid()) { - stream = p_stream; - stream_playback = p_stream->instance_playback(); - } - - AudioServer::get_singleton()->unlock(); - - if (p_stream.is_valid() && stream_playback.is_null()) { - stream.unref(); - } -} - -Ref AudioStreamPlayer::get_stream() const { - - return stream; -} - -void AudioStreamPlayer::set_volume_db(float p_volume) { - - volume_db = p_volume; -} -float AudioStreamPlayer::get_volume_db() const { - - return volume_db; -} - -void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) { - ERR_FAIL_COND(p_pitch_scale <= 0.0); - pitch_scale = p_pitch_scale; -} -float AudioStreamPlayer::get_pitch_scale() const { - return pitch_scale; -} - -void AudioStreamPlayer::play(float p_from_pos) { - - if (stream_playback.is_valid()) { - //mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks - setseek = p_from_pos; - active = true; - set_process_internal(true); - } -} - -void AudioStreamPlayer::seek(float p_seconds) { - - if (stream_playback.is_valid()) { - setseek = p_seconds; - } -} - -void AudioStreamPlayer::stop() { - - if (stream_playback.is_valid()) { - stream_playback->stop(); - active = false; - set_process_internal(false); - } -} - -bool AudioStreamPlayer::is_playing() const { - - if (stream_playback.is_valid()) { - return active; //&& stream_playback->is_playing(); - } - - return false; -} - -float AudioStreamPlayer::get_playback_position() { - - if (stream_playback.is_valid()) { - return stream_playback->get_playback_position(); - } - - return 0; -} - -void AudioStreamPlayer::set_bus(const StringName &p_bus) { - - //if audio is active, must lock this - AudioServer::get_singleton()->lock(); - bus = p_bus; - AudioServer::get_singleton()->unlock(); -} -StringName AudioStreamPlayer::get_bus() const { - - for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { - if (AudioServer::get_singleton()->get_bus_name(i) == bus) { - return bus; - } - } - return "Master"; -} - -void AudioStreamPlayer::set_autoplay(bool p_enable) { - - autoplay = p_enable; -} -bool AudioStreamPlayer::is_autoplay_enabled() { - - return autoplay; -} - -void AudioStreamPlayer::set_mix_target(MixTarget p_target) { - - mix_target = p_target; -} - -AudioStreamPlayer::MixTarget AudioStreamPlayer::get_mix_target() const { - - return mix_target; -} - -void AudioStreamPlayer::_set_playing(bool p_enable) { - - if (p_enable) - play(); - else - stop(); -} -bool AudioStreamPlayer::_is_active() const { - - return active; -} - -void AudioStreamPlayer::set_stream_paused(bool p_pause) { - - if (p_pause != stream_paused) { - stream_paused = p_pause; - stream_paused_fade = p_pause ? true : false; - } -} - -bool AudioStreamPlayer::get_stream_paused() const { - - return stream_paused; -} - -void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { - - if (property.name == "bus") { - - String options; - for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { - if (i > 0) - options += ","; - String name = AudioServer::get_singleton()->get_bus_name(i); - options += name; - } - - property.hint_string = options; - } -} - -void AudioStreamPlayer::_bus_layout_changed() { - - _change_notify(); -} - -void AudioStreamPlayer::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer::set_stream); - ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer::get_stream); - - ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer::set_volume_db); - ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer::get_volume_db); - - ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer::set_pitch_scale); - ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer::get_pitch_scale); - - ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer::play, DEFVAL(0.0)); - ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer::seek); - ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer::stop); - - ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayer::is_playing); - ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayer::get_playback_position); - - ClassDB::bind_method(D_METHOD("set_bus", "bus"), &AudioStreamPlayer::set_bus); - ClassDB::bind_method(D_METHOD("get_bus"), &AudioStreamPlayer::get_bus); - - ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer::set_autoplay); - ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer::is_autoplay_enabled); - - ClassDB::bind_method(D_METHOD("set_mix_target", "mix_target"), &AudioStreamPlayer::set_mix_target); - ClassDB::bind_method(D_METHOD("get_mix_target"), &AudioStreamPlayer::get_mix_target); - - ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer::_set_playing); - ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer::_is_active); - - ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer::_bus_layout_changed); - - ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused); - ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); - - ADD_SIGNAL(MethodInfo("finished")); - - BIND_ENUM_CONSTANT(MIX_TARGET_STEREO); - BIND_ENUM_CONSTANT(MIX_TARGET_SURROUND); - BIND_ENUM_CONSTANT(MIX_TARGET_CENTER); -} - -AudioStreamPlayer::AudioStreamPlayer() { - - mix_volume_db = 0; - pitch_scale = 1.0; - volume_db = 0; - autoplay = false; - setseek = -1; - active = false; - stream_paused = false; - stream_paused_fade = false; - mix_target = MIX_TARGET_STEREO; - - AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); -} - -AudioStreamPlayer::~AudioStreamPlayer() { -} diff --git a/scene/audio/audio_player.h b/scene/audio/audio_player.h deleted file mode 100644 index 2e9526c335..0000000000 --- a/scene/audio/audio_player.h +++ /dev/null @@ -1,113 +0,0 @@ -/*************************************************************************/ -/* audio_player.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 AUDIOPLAYER_H -#define AUDIOPLAYER_H - -#include "scene/main/node.h" -#include "servers/audio/audio_stream.h" - -class AudioStreamPlayer : public Node { - - GDCLASS(AudioStreamPlayer, Node) - -public: - enum MixTarget { - MIX_TARGET_STEREO, - MIX_TARGET_SURROUND, - MIX_TARGET_CENTER - }; - -private: - Ref stream_playback; - Ref stream; - Vector mix_buffer; - - volatile float setseek; - volatile bool active; - - float mix_volume_db; - float pitch_scale; - float volume_db; - bool autoplay; - bool stream_paused; - bool stream_paused_fade; - StringName bus; - - MixTarget mix_target; - - void _mix_internal(bool p_fadeout); - void _mix_audio(); - static void _mix_audios(void *self) { reinterpret_cast(self)->_mix_audio(); } - - void _set_playing(bool p_enable); - bool _is_active() const; - - void _bus_layout_changed(); - -protected: - void _validate_property(PropertyInfo &property) const; - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_stream(Ref p_stream); - Ref get_stream() const; - - void set_volume_db(float p_volume); - float get_volume_db() const; - - void set_pitch_scale(float p_pitch_scale); - float get_pitch_scale() const; - - void play(float p_from_pos = 0.0); - void seek(float p_seconds); - void stop(); - bool is_playing() const; - float get_playback_position(); - - void set_bus(const StringName &p_bus); - StringName get_bus() const; - - void set_autoplay(bool p_enable); - bool is_autoplay_enabled(); - - void set_mix_target(MixTarget p_target); - MixTarget get_mix_target() const; - - void set_stream_paused(bool p_pause); - bool get_stream_paused() const; - - AudioStreamPlayer(); - ~AudioStreamPlayer(); -}; - -VARIANT_ENUM_CAST(AudioStreamPlayer::MixTarget) -#endif // AUDIOPLAYER_H diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp new file mode 100644 index 0000000000..e6864e2117 --- /dev/null +++ b/scene/audio/audio_stream_player.cpp @@ -0,0 +1,395 @@ +/*************************************************************************/ +/* audio_stream_player.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "audio_stream_player.h" + +#include "core/engine.h" + +void AudioStreamPlayer::_mix_internal(bool p_fadeout) { + + int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); + + //get data + AudioFrame *buffer = mix_buffer.ptrw(); + int buffer_size = mix_buffer.size(); + + if (p_fadeout) { + // Short fadeout ramp + buffer_size = MIN(buffer_size, 128); + } + + stream_playback->mix(buffer, pitch_scale, buffer_size); + + //multiply volume interpolating to avoid clicks if this changes + float target_volume = p_fadeout ? -80.0 : volume_db; + float vol = Math::db2linear(mix_volume_db); + float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size); + + for (int i = 0; i < buffer_size; i++) { + buffer[i] *= vol; + vol += vol_inc; + } + + //set volume for next mix + mix_volume_db = target_volume; + + AudioFrame *targets[4] = { NULL, NULL, NULL, NULL }; + + if (AudioServer::get_singleton()->get_speaker_mode() == AudioServer::SPEAKER_MODE_STEREO) { + targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0); + } else { + switch (mix_target) { + case MIX_TARGET_STEREO: { + targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0); + } break; + case MIX_TARGET_SURROUND: { + for (int i = 0; i < AudioServer::get_singleton()->get_channel_count(); i++) { + targets[i] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, i); + } + } break; + case MIX_TARGET_CENTER: { + targets[0] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1); + } break; + } + } + + for (int c = 0; c < 4; c++) { + if (!targets[c]) + break; + for (int i = 0; i < buffer_size; i++) { + targets[c][i] += buffer[i]; + } + } +} + +void AudioStreamPlayer::_mix_audio() { + + if (!stream_playback.is_valid() || !active) + return; + + if (stream_paused) { + if (stream_paused_fade) { + _mix_internal(true); + stream_paused_fade = false; + } + return; + } + + if (setseek >= 0.0) { + if (stream_playback->is_playing()) { + + //fade out to avoid pops + _mix_internal(true); + } + stream_playback->start(setseek); + setseek = -1.0; //reset seek + mix_volume_db = volume_db; //reset ramp + } + + _mix_internal(false); +} + +void AudioStreamPlayer::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + AudioServer::get_singleton()->add_callback(_mix_audios, this); + if (autoplay && !Engine::get_singleton()->is_editor_hint()) { + play(); + } + } + + if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + + if (!active || (setseek < 0 && !stream_playback->is_playing())) { + active = false; + set_process_internal(false); + emit_signal("finished"); + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + + AudioServer::get_singleton()->remove_callback(_mix_audios, this); + } + + if (p_what == NOTIFICATION_PAUSED) { + if (!can_process()) { + // Node can't process so we start fading out to silence + set_stream_paused(true); + } + } + + if (p_what == NOTIFICATION_UNPAUSED) { + set_stream_paused(false); + } +} + +void AudioStreamPlayer::set_stream(Ref p_stream) { + + AudioServer::get_singleton()->lock(); + + mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); + + if (stream_playback.is_valid()) { + stream_playback.unref(); + stream.unref(); + active = false; + setseek = -1; + } + + if (p_stream.is_valid()) { + stream = p_stream; + stream_playback = p_stream->instance_playback(); + } + + AudioServer::get_singleton()->unlock(); + + if (p_stream.is_valid() && stream_playback.is_null()) { + stream.unref(); + } +} + +Ref AudioStreamPlayer::get_stream() const { + + return stream; +} + +void AudioStreamPlayer::set_volume_db(float p_volume) { + + volume_db = p_volume; +} +float AudioStreamPlayer::get_volume_db() const { + + return volume_db; +} + +void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) { + ERR_FAIL_COND(p_pitch_scale <= 0.0); + pitch_scale = p_pitch_scale; +} +float AudioStreamPlayer::get_pitch_scale() const { + return pitch_scale; +} + +void AudioStreamPlayer::play(float p_from_pos) { + + if (stream_playback.is_valid()) { + //mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks + setseek = p_from_pos; + active = true; + set_process_internal(true); + } +} + +void AudioStreamPlayer::seek(float p_seconds) { + + if (stream_playback.is_valid()) { + setseek = p_seconds; + } +} + +void AudioStreamPlayer::stop() { + + if (stream_playback.is_valid()) { + stream_playback->stop(); + active = false; + set_process_internal(false); + } +} + +bool AudioStreamPlayer::is_playing() const { + + if (stream_playback.is_valid()) { + return active; //&& stream_playback->is_playing(); + } + + return false; +} + +float AudioStreamPlayer::get_playback_position() { + + if (stream_playback.is_valid()) { + return stream_playback->get_playback_position(); + } + + return 0; +} + +void AudioStreamPlayer::set_bus(const StringName &p_bus) { + + //if audio is active, must lock this + AudioServer::get_singleton()->lock(); + bus = p_bus; + AudioServer::get_singleton()->unlock(); +} +StringName AudioStreamPlayer::get_bus() const { + + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (AudioServer::get_singleton()->get_bus_name(i) == bus) { + return bus; + } + } + return "Master"; +} + +void AudioStreamPlayer::set_autoplay(bool p_enable) { + + autoplay = p_enable; +} +bool AudioStreamPlayer::is_autoplay_enabled() { + + return autoplay; +} + +void AudioStreamPlayer::set_mix_target(MixTarget p_target) { + + mix_target = p_target; +} + +AudioStreamPlayer::MixTarget AudioStreamPlayer::get_mix_target() const { + + return mix_target; +} + +void AudioStreamPlayer::_set_playing(bool p_enable) { + + if (p_enable) + play(); + else + stop(); +} +bool AudioStreamPlayer::_is_active() const { + + return active; +} + +void AudioStreamPlayer::set_stream_paused(bool p_pause) { + + if (p_pause != stream_paused) { + stream_paused = p_pause; + stream_paused_fade = p_pause ? true : false; + } +} + +bool AudioStreamPlayer::get_stream_paused() const { + + return stream_paused; +} + +void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { + + if (property.name == "bus") { + + String options; + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (i > 0) + options += ","; + String name = AudioServer::get_singleton()->get_bus_name(i); + options += name; + } + + property.hint_string = options; + } +} + +void AudioStreamPlayer::_bus_layout_changed() { + + _change_notify(); +} + +void AudioStreamPlayer::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer::set_stream); + ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer::get_stream); + + ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer::set_volume_db); + ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer::get_volume_db); + + ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer::set_pitch_scale); + ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer::get_pitch_scale); + + ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer::play, DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer::seek); + ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer::stop); + + ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayer::is_playing); + ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayer::get_playback_position); + + ClassDB::bind_method(D_METHOD("set_bus", "bus"), &AudioStreamPlayer::set_bus); + ClassDB::bind_method(D_METHOD("get_bus"), &AudioStreamPlayer::get_bus); + + ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer::set_autoplay); + ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer::is_autoplay_enabled); + + ClassDB::bind_method(D_METHOD("set_mix_target", "mix_target"), &AudioStreamPlayer::set_mix_target); + ClassDB::bind_method(D_METHOD("get_mix_target"), &AudioStreamPlayer::get_mix_target); + + ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer::_set_playing); + ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer::_is_active); + + ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer::_bus_layout_changed); + + ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused); + ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); + + ADD_SIGNAL(MethodInfo("finished")); + + BIND_ENUM_CONSTANT(MIX_TARGET_STEREO); + BIND_ENUM_CONSTANT(MIX_TARGET_SURROUND); + BIND_ENUM_CONSTANT(MIX_TARGET_CENTER); +} + +AudioStreamPlayer::AudioStreamPlayer() { + + mix_volume_db = 0; + pitch_scale = 1.0; + volume_db = 0; + autoplay = false; + setseek = -1; + active = false; + stream_paused = false; + stream_paused_fade = false; + mix_target = MIX_TARGET_STEREO; + + AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); +} + +AudioStreamPlayer::~AudioStreamPlayer() { +} diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h new file mode 100644 index 0000000000..0f7713bf33 --- /dev/null +++ b/scene/audio/audio_stream_player.h @@ -0,0 +1,114 @@ +/*************************************************************************/ +/* audio_stream_player.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 AUDIO_STREAM_PLAYER_H +#define AUDIO_STREAM_PLAYER_H + +#include "scene/main/node.h" +#include "servers/audio/audio_stream.h" + +class AudioStreamPlayer : public Node { + + GDCLASS(AudioStreamPlayer, Node) + +public: + enum MixTarget { + MIX_TARGET_STEREO, + MIX_TARGET_SURROUND, + MIX_TARGET_CENTER + }; + +private: + Ref stream_playback; + Ref stream; + Vector mix_buffer; + + volatile float setseek; + volatile bool active; + + float mix_volume_db; + float pitch_scale; + float volume_db; + bool autoplay; + bool stream_paused; + bool stream_paused_fade; + StringName bus; + + MixTarget mix_target; + + void _mix_internal(bool p_fadeout); + void _mix_audio(); + static void _mix_audios(void *self) { reinterpret_cast(self)->_mix_audio(); } + + void _set_playing(bool p_enable); + bool _is_active() const; + + void _bus_layout_changed(); + +protected: + void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_stream(Ref p_stream); + Ref get_stream() const; + + void set_volume_db(float p_volume); + float get_volume_db() const; + + void set_pitch_scale(float p_pitch_scale); + float get_pitch_scale() const; + + void play(float p_from_pos = 0.0); + void seek(float p_seconds); + void stop(); + bool is_playing() const; + float get_playback_position(); + + void set_bus(const StringName &p_bus); + StringName get_bus() const; + + void set_autoplay(bool p_enable); + bool is_autoplay_enabled(); + + void set_mix_target(MixTarget p_target); + MixTarget get_mix_target() const; + + void set_stream_paused(bool p_pause); + bool get_stream_paused() const; + + AudioStreamPlayer(); + ~AudioStreamPlayer(); +}; + +VARIANT_ENUM_CAST(AudioStreamPlayer::MixTarget) + +#endif // AUDIO_STREAM_PLAYER_H diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h index fd340b3f6c..662278a17b 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -33,8 +33,8 @@ #include "scene/gui/color_picker.h" #include "scene/gui/popup.h" -#include "scene/resources/color_ramp.h" #include "scene/resources/default_theme/theme_data.h" +#include "scene/resources/gradient.h" #define POINT_WIDTH (8 * EDSCALE) diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index ffe248ac5e..17c4bca67b 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -32,7 +32,7 @@ #define LINKBUTTON_H #include "scene/gui/base_button.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" class LinkButton : public BaseButton { diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h index 4dc0de5358..d9224de686 100644 --- a/scene/gui/texture_button.h +++ b/scene/gui/texture_button.h @@ -32,7 +32,7 @@ #define TEXTURE_BUTTON_H #include "scene/gui/base_button.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" class TextureButton : public BaseButton { GDCLASS(TextureButton, BaseButton); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 3a1ff5cb06..e15a64604d 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -43,7 +43,6 @@ @author Juan Linietsky */ -class SceneTree; class PackedScene; class Node; class Viewport; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 61d6fc7401..c66814d115 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -37,8 +37,8 @@ #include "scene/3d/camera.h" #include "scene/3d/collision_object.h" #include "scene/3d/listener.h" -#include "scene/3d/scenario_fx.h" #include "scene/3d/spatial.h" +#include "scene/3d/world_environment.h" #include "scene/gui/control.h" #include "scene/gui/label.h" #include "scene/gui/menu_button.h" diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 078d880d0e..49c9c4c23c 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -48,7 +48,7 @@ #include "scene/2d/light_occluder_2d.h" #include "scene/2d/line_2d.h" #include "scene/2d/mesh_instance_2d.h" -#include "scene/2d/navigation2d.h" +#include "scene/2d/navigation_2d.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" #include "scene/2d/particles_2d.h" @@ -58,10 +58,10 @@ #include "scene/2d/position_2d.h" #include "scene/2d/ray_cast_2d.h" #include "scene/2d/remote_transform_2d.h" -#include "scene/2d/screen_button.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite.h" #include "scene/2d/tile_map.h" +#include "scene/2d/touch_screen_button.h" #include "scene/2d/visibility_notifier_2d.h" #include "scene/2d/y_sort.h" #include "scene/animation/animation_blend_space_1d.h" @@ -73,7 +73,7 @@ #include "scene/animation/animation_tree_player.h" #include "scene/animation/root_motion_view.h" #include "scene/animation/tween.h" -#include "scene/audio/audio_player.h" +#include "scene/audio/audio_stream_player.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/center_container.h" @@ -125,12 +125,11 @@ #include "scene/main/timer.h" #include "scene/main/viewport.h" #include "scene/resources/audio_stream_sample.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" #include "scene/resources/box_shape.h" #include "scene/resources/capsule_shape.h" #include "scene/resources/capsule_shape_2d.h" #include "scene/resources/circle_shape_2d.h" -#include "scene/resources/color_ramp.h" #include "scene/resources/concave_polygon_shape.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape.h" @@ -139,6 +138,8 @@ #include "scene/resources/default_theme/default_theme.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/dynamic_font_stb.h" +#include "scene/resources/gradient.h" +#include "scene/resources/line_shape_2d.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" #include "scene/resources/mesh_data_tool.h" @@ -151,10 +152,9 @@ #include "scene/resources/primitive_meshes.h" #include "scene/resources/ray_shape.h" #include "scene/resources/rectangle_shape_2d.h" -#include "scene/resources/scene_format_text.h" +#include "scene/resources/resource_format_text.h" #include "scene/resources/segment_shape_2d.h" -#include "scene/resources/shape_line_2d.h" -#include "scene/resources/sky_box.h" +#include "scene/resources/sky.h" #include "scene/resources/sphere_shape.h" #include "scene/resources/surface_tool.h" #include "scene/resources/text_file.h" @@ -167,8 +167,8 @@ #include "scene/resources/world_2d.h" #include "scene/scene_string_names.h" -#include "scene/3d/scenario_fx.h" #include "scene/3d/spatial.h" +#include "scene/3d/world_environment.h" #ifndef _3D_DISABLED #include "scene/3d/area.h" diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp new file mode 100644 index 0000000000..8263096c8f --- /dev/null +++ b/scene/resources/bit_map.cpp @@ -0,0 +1,625 @@ +/*************************************************************************/ +/* bit_map.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "bit_map.h" + +#include "core/io/image_loader.h" + +void BitMap::create(const Size2 &p_size) { + + ERR_FAIL_COND(p_size.width < 1); + ERR_FAIL_COND(p_size.height < 1); + + width = p_size.width; + height = p_size.height; + bitmask.resize(((width * height) / 8) + 1); + zeromem(bitmask.ptrw(), bitmask.size()); +} + +void BitMap::create_from_image_alpha(const Ref &p_image, float p_threshold) { + + ERR_FAIL_COND(p_image.is_null() || p_image->empty()); + Ref img = p_image->duplicate(); + img->convert(Image::FORMAT_LA8); + ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); + + create(Size2(img->get_width(), img->get_height())); + + PoolVector::Read r = img->get_data().read(); + uint8_t *w = bitmask.ptrw(); + + for (int i = 0; i < width * height; i++) { + + int bbyte = i / 8; + int bbit = i % 8; + if (r[i * 2 + 1] / 255.0 > p_threshold) { + w[bbyte] |= (1 << bbit); + } + } +} + +void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) { + + Rect2i current = Rect2i(0, 0, width, height).clip(p_rect); + uint8_t *data = bitmask.ptrw(); + + for (int i = current.position.x; i < current.position.x + current.size.x; i++) { + + for (int j = current.position.y; j < current.position.y + current.size.y; j++) { + + int ofs = width * j + i; + int bbyte = ofs / 8; + int bbit = ofs % 8; + + uint8_t b = data[bbyte]; + + if (p_value) + b |= (1 << bbit); + else + b &= ~(1 << bbit); + + data[bbyte] = b; + } + } +} + +int BitMap::get_true_bit_count() const { + + int ds = bitmask.size(); + const uint8_t *d = bitmask.ptr(); + int c = 0; + + //fast, almot branchless version + + for (int i = 0; i < ds; i++) { + + c += (d[i] & (1 << 7)) >> 7; + c += (d[i] & (1 << 6)) >> 6; + c += (d[i] & (1 << 5)) >> 5; + c += (d[i] & (1 << 4)) >> 4; + c += (d[i] & (1 << 3)) >> 3; + c += (d[i] & (1 << 2)) >> 2; + c += d[i] & 1; + } + + return c; +} + +void BitMap::set_bit(const Point2 &p_pos, bool p_value) { + + int x = p_pos.x; + int y = p_pos.y; + + ERR_FAIL_INDEX(x, width); + ERR_FAIL_INDEX(y, height); + + int ofs = width * y + x; + int bbyte = ofs / 8; + int bbit = ofs % 8; + + uint8_t b = bitmask[bbyte]; + + if (p_value) + b |= (1 << bbit); + else + b &= ~(1 << bbit); + + bitmask.write[bbyte] = b; +} + +bool BitMap::get_bit(const Point2 &p_pos) const { + + int x = Math::fast_ftoi(p_pos.x); + int y = Math::fast_ftoi(p_pos.y); + ERR_FAIL_INDEX_V(x, width, false); + ERR_FAIL_INDEX_V(y, height, false); + + int ofs = width * y + x; + int bbyte = ofs / 8; + int bbit = ofs % 8; + + return (bitmask[bbyte] & (1 << bbit)) != 0; +} + +Size2 BitMap::get_size() const { + + return Size2(width, height); +} + +void BitMap::_set_data(const Dictionary &p_d) { + + ERR_FAIL_COND(!p_d.has("size")); + ERR_FAIL_COND(!p_d.has("data")); + + create(p_d["size"]); + bitmask = p_d["data"]; +} + +Dictionary BitMap::_get_data() const { + + Dictionary d; + d["size"] = get_size(); + d["data"] = bitmask; + return d; +} + +Vector BitMap::_march_square(const Rect2i &rect, const Point2i &start) const { + + int stepx = 0; + int stepy = 0; + int prevx = 0; + int prevy = 0; + int startx = start.x; + int starty = start.y; + int curx = startx; + int cury = starty; + unsigned int count = 0; + Set case9s; + Set case6s; + Vector _points; + do { + int sv = 0; + { //square value + + /* + checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent + +---+---+ + | 1 | 2 | + +---+---+ + | 4 | 8 | <- current pixel (curx,cury) + +---+---+ + */ + //NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel + Rect2i fixed_rect = Rect2i(rect.position, rect.size - Size2i(2, 2)); + Point2i tl = Point2i(curx - 1, cury - 1); + sv += (fixed_rect.has_point(tl) && get_bit(tl)) ? 1 : 0; + Point2i tr = Point2i(curx, cury - 1); + sv += (fixed_rect.has_point(tr) && get_bit(tr)) ? 2 : 0; + Point2i bl = Point2i(curx - 1, cury); + sv += (fixed_rect.has_point(bl) && get_bit(bl)) ? 4 : 0; + Point2i br = Point2i(curx, cury); + sv += (fixed_rect.has_point(br) && get_bit(br)) ? 8 : 0; + ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector()); + } + + switch (sv) { + + case 1: + case 5: + case 13: + /* going UP with these cases: + 1 5 13 + +---+---+ +---+---+ +---+---+ + | 1 | | | 1 | | | 1 | | + +---+---+ +---+---+ +---+---+ + | | | | 4 | | | 4 | 8 | + +---+---+ +---+---+ +---+---+ + */ + stepx = 0; + stepy = -1; + break; + + case 8: + case 10: + case 11: + /* going DOWN with these cases: + 8 10 11 + +---+---+ +---+---+ +---+---+ + | | | | | 2 | | 1 | 2 | + +---+---+ +---+---+ +---+---+ + | | 8 | | | 8 | | | 8 | + +---+---+ +---+---+ +---+---+ + */ + stepx = 0; + stepy = 1; + break; + + case 4: + case 12: + case 14: + /* going LEFT with these cases: + 4 12 14 + +---+---+ +---+---+ +---+---+ + | | | | | | | | 2 | + +---+---+ +---+---+ +---+---+ + | 4 | | | 4 | 8 | | 4 | 8 | + +---+---+ +---+---+ +---+---+ + */ + stepx = -1; + stepy = 0; + break; + + case 2: + case 3: + case 7: + /* going RIGHT with these cases: + 2 3 7 + +---+---+ +---+---+ +---+---+ + | | 2 | | 1 | 2 | | 1 | 2 | + +---+---+ +---+---+ +---+---+ + | | | | | | | 4 | | + +---+---+ +---+---+ +---+---+ + */ + stepx = 1; + stepy = 0; + break; + case 9: + /* + +---+---+ + | 1 | | + +---+---+ + | | 8 | + +---+---+ + this should normally go UP, but if we already been here, we go down + */ + if (case9s.has(Point2i(curx, cury))) { + //found, so we go down, and delete from case9s; + stepx = 0; + stepy = 1; + case9s.erase(Point2i(curx, cury)); + } else { + //not found, we go up, and add to case9s; + stepx = 0; + stepy = -1; + case9s.insert(Point2i(curx, cury)); + } + break; + case 6: + /* + 6 + +---+---+ + | | 2 | + +---+---+ + | 4 | | + +---+---+ + this normally go RIGHT, but if its coming from UP, it should go LEFT + */ + if (case6s.has(Point2i(curx, cury))) { + //found, so we go down, and delete from case6s; + stepx = -1; + stepy = 0; + case6s.erase(Point2i(curx, cury)); + } else { + //not found, we go up, and add to case6s; + stepx = 1; + stepy = 0; + case6s.insert(Point2i(curx, cury)); + } + break; + default: + ERR_PRINT("this shouldn't happen."); + } + //little optimization + // if previous direction is same as current direction, + // then we should modify the last vec to current + curx += stepx; + cury += stepy; + if (stepx == prevx && stepy == prevy) { + _points.write[_points.size() - 1].x = (float)(curx - rect.position.x); + _points.write[_points.size() - 1].y = (float)(cury + rect.position.y); + } else { + _points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y))); + } + + count++; + prevx = stepx; + prevy = stepy; + + ERR_FAIL_COND_V(count > width * height, _points); + } while (curx != startx || cury != starty); + return _points; +} + +static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) { + float res; + float slope; + float intercept; + + if (start.x == end.x) { + res = Math::absf(i.x - end.x); + } else if (start.y == end.y) { + res = Math::absf(i.y - end.y); + } else { + slope = (end.y - start.y) / (end.x - start.x); + intercept = start.y - (slope * start.x); + res = Math::absf(slope * i.x - i.y + intercept) / Math::sqrt(Math::pow(slope, 2.0f) + 1.0); + } + return res; +} + +static Vector rdp(const Vector &v, float optimization) { + if (v.size() < 3) + return v; + + int index = -1; + float dist = 0; + //not looping first and last point + for (size_t i = 1, size = v.size(); i < size - 1; ++i) { + float cdist = perpendicular_distance(v[i], v[0], v[v.size() - 1]); + if (cdist > dist) { + dist = cdist; + index = static_cast(i); + } + } + if (dist > optimization) { + + Vector left, right; + left.resize(index); + for (int i = 0; i < index; i++) { + left.write[i] = v[i]; + } + right.resize(v.size() - index); + for (int i = 0; i < right.size(); i++) { + right.write[i] = v[index + i]; + } + Vector r1 = rdp(left, optimization); + Vector r2 = rdp(right, optimization); + + int middle = r1.size(); + r1.resize(r1.size() + r2.size()); + for (int i = 0; i < r2.size(); i++) { + r1.write[middle + i] = r2[i]; + } + return r1; + } else { + Vector ret; + ret.push_back(v[0]); + ret.push_back(v[v.size() - 1]); + return ret; + } +} + +static Vector reduce(const Vector &points, const Rect2i &rect, float epsilon) { + int size = points.size(); + // if there are less than 3 points, then we have nothing + ERR_FAIL_COND_V(size < 3, Vector()); + // if there are less than 9 points (but more than 3), then we don't need to reduce it + if (size < 9) { + return points; + } + + float maxEp = MIN(rect.size.width, rect.size.height); + float ep = CLAMP(epsilon, 0.0, maxEp / 2); + Vector result = rdp(points, ep); + + Vector2 last = result[result.size() - 1]; + + if (last.y > result[0].y && last.distance_to(result[0]) < ep * 0.5f) { + result.write[0].y = last.y; + result.resize(result.size() - 1); + } + return result; +} + +struct FillBitsStackEntry { + Point2i pos; + int i; + int j; +}; + +static void fill_bits(const BitMap *p_src, Ref &p_map, const Point2i &p_pos, const Rect2i &rect) { + + // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps + PoolVector stack; + // Tracking size since we won't be shrinking the stack vector + int stack_size = 0; + + Point2i pos = p_pos; + int next_i = 0; + int next_j = 0; + + bool reenter = true; + bool popped = false; + do { + if (reenter) { + next_i = pos.x - 1; + next_j = pos.y - 1; + reenter = false; + } + + for (int i = next_i; i <= pos.x + 1; i++) { + for (int j = next_j; j <= pos.y + 1; j++) { + if (popped) { + // The next loop over j must start normally + next_j = pos.y; + popped = false; + // Skip because an iteration was already executed with current counter values + continue; + } + + if (i < rect.position.x || i >= rect.position.x + rect.size.x) + continue; + if (j < rect.position.y || j >= rect.position.y + rect.size.y) + continue; + + if (p_map->get_bit(Vector2(i, j))) + continue; + + else if (p_src->get_bit(Vector2(i, j))) { + p_map->set_bit(Vector2(i, j), true); + + FillBitsStackEntry se = { pos, i, j }; + stack.resize(MAX(stack_size + 1, stack.size())); + stack.set(stack_size, se); + stack_size++; + + pos = Point2i(i, j); + reenter = true; + break; + } + } + if (reenter) { + break; + } + } + if (!reenter) { + if (stack_size) { + FillBitsStackEntry se = stack.get(stack_size - 1); + stack_size--; + pos = se.pos; + next_i = se.i; + next_j = se.j; + popped = true; + } + } + } while (reenter || popped); + + print_verbose("BitMap: Max stack size: " + itos(stack.size())); +} + +Vector > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { + + Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); + print_verbose("BitMap: Rect: " + r); + + Point2i from; + Ref fill; + fill.instance(); + fill->create(get_size()); + + Vector > polygons; + for (int i = r.position.y; i < r.position.y + r.size.height; i++) { + for (int j = r.position.x; j < r.position.x + r.size.width; j++) { + if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { + + Vector polygon = _march_square(r, Point2i(j, i)); + print_verbose("BitMap: Pre reduce: " + itos(polygon.size())); + polygon = reduce(polygon, r, p_epsilon); + print_verbose("BitMap: Post reduce: " + itos(polygon.size())); + polygons.push_back(polygon); + fill_bits(this, fill, Point2i(j, i), r); + } + } + } + + return polygons; +} + +void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { + + Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); + + Ref copy; + copy.instance(); + copy->create(get_size()); + copy->bitmask = bitmask; + + for (int i = r.position.y; i < r.position.y + r.size.height; i++) { + for (int j = r.position.x; j < r.position.x + r.size.width; j++) { + if (copy->get_bit(Point2(j, i))) + continue; + + bool found = false; + + for (int y = i - p_pixels; y <= i + p_pixels; y++) { + for (int x = j - p_pixels; x <= j + p_pixels; x++) { + + if (x < p_rect.position.x || x >= p_rect.position.x + p_rect.size.x) + continue; + if (y < p_rect.position.y || y >= p_rect.position.y + p_rect.size.y) + continue; + + float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON; + if (d > p_pixels) + continue; + + if (copy->get_bit(Point2(x, y))) { + found = true; + break; + } + } + if (found) + break; + } + + if (found) { + set_bit(Point2(j, i), true); + } + } + } +} + +Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const { + + Vector > result = clip_opaque_to_polygons(p_rect, p_epsilon); + + // Convert result to bindable types + + Array result_array; + result_array.resize(result.size()); + for (int i = 0; i < result.size(); i++) { + + const Vector &polygon = result[i]; + + PoolVector2Array polygon_array; + polygon_array.resize(polygon.size()); + + { + PoolVector2Array::Write w = polygon_array.write(); + for (int j = 0; j < polygon.size(); j++) { + w[j] = polygon[j]; + } + } + + result_array[i] = polygon_array; + } + + return result_array; +} + +void BitMap::_bind_methods() { + + ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); + ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image", "threshold"), &BitMap::create_from_image_alpha, DEFVAL(0.1)); + + ClassDB::bind_method(D_METHOD("set_bit", "position", "bit"), &BitMap::set_bit); + ClassDB::bind_method(D_METHOD("get_bit", "position"), &BitMap::get_bit); + + ClassDB::bind_method(D_METHOD("set_bit_rect", "rect", "bit"), &BitMap::set_bit_rect); + ClassDB::bind_method(D_METHOD("get_true_bit_count"), &BitMap::get_true_bit_count); + + ClassDB::bind_method(D_METHOD("get_size"), &BitMap::get_size); + + ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data); + + ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask); + ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0)); + + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); +} + +BitMap::BitMap() { + + width = 0; + height = 0; +} + +////////////////////////////////////// diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h new file mode 100644 index 0000000000..b3c86afd38 --- /dev/null +++ b/scene/resources/bit_map.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* bit_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 BIT_MAP_H +#define BIT_MAP_H + +#include "core/image.h" +#include "core/io/resource_loader.h" +#include "core/resource.h" + +class BitMap : public Resource { + + GDCLASS(BitMap, Resource); + OBJ_SAVE_TYPE(BitMap); + + Vector bitmask; + int width; + int height; + + Vector _march_square(const Rect2i &rect, const Point2i &start) const; + + Array _opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const; + +protected: + void _set_data(const Dictionary &p_d); + Dictionary _get_data() const; + + static void _bind_methods(); + +public: + void create(const Size2 &p_size); + void create_from_image_alpha(const Ref &p_image, float p_threshold = 0.1); + + void set_bit(const Point2 &p_pos, bool p_value); + bool get_bit(const Point2 &p_pos) const; + void set_bit_rect(const Rect2 &p_rect, bool p_value); + int get_true_bit_count() const; + + Size2 get_size() const; + + void grow_mask(int p_pixels, const Rect2 &p_rect); + + Vector > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; + + BitMap(); +}; + +#endif // BIT_MAP_H diff --git a/scene/resources/bit_mask.cpp b/scene/resources/bit_mask.cpp deleted file mode 100644 index 0e2152f244..0000000000 --- a/scene/resources/bit_mask.cpp +++ /dev/null @@ -1,625 +0,0 @@ -/*************************************************************************/ -/* bit_mask.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "bit_mask.h" - -#include "core/io/image_loader.h" - -void BitMap::create(const Size2 &p_size) { - - ERR_FAIL_COND(p_size.width < 1); - ERR_FAIL_COND(p_size.height < 1); - - width = p_size.width; - height = p_size.height; - bitmask.resize(((width * height) / 8) + 1); - zeromem(bitmask.ptrw(), bitmask.size()); -} - -void BitMap::create_from_image_alpha(const Ref &p_image, float p_threshold) { - - ERR_FAIL_COND(p_image.is_null() || p_image->empty()); - Ref img = p_image->duplicate(); - img->convert(Image::FORMAT_LA8); - ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); - - create(Size2(img->get_width(), img->get_height())); - - PoolVector::Read r = img->get_data().read(); - uint8_t *w = bitmask.ptrw(); - - for (int i = 0; i < width * height; i++) { - - int bbyte = i / 8; - int bbit = i % 8; - if (r[i * 2 + 1] / 255.0 > p_threshold) { - w[bbyte] |= (1 << bbit); - } - } -} - -void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) { - - Rect2i current = Rect2i(0, 0, width, height).clip(p_rect); - uint8_t *data = bitmask.ptrw(); - - for (int i = current.position.x; i < current.position.x + current.size.x; i++) { - - for (int j = current.position.y; j < current.position.y + current.size.y; j++) { - - int ofs = width * j + i; - int bbyte = ofs / 8; - int bbit = ofs % 8; - - uint8_t b = data[bbyte]; - - if (p_value) - b |= (1 << bbit); - else - b &= ~(1 << bbit); - - data[bbyte] = b; - } - } -} - -int BitMap::get_true_bit_count() const { - - int ds = bitmask.size(); - const uint8_t *d = bitmask.ptr(); - int c = 0; - - //fast, almot branchless version - - for (int i = 0; i < ds; i++) { - - c += (d[i] & (1 << 7)) >> 7; - c += (d[i] & (1 << 6)) >> 6; - c += (d[i] & (1 << 5)) >> 5; - c += (d[i] & (1 << 4)) >> 4; - c += (d[i] & (1 << 3)) >> 3; - c += (d[i] & (1 << 2)) >> 2; - c += d[i] & 1; - } - - return c; -} - -void BitMap::set_bit(const Point2 &p_pos, bool p_value) { - - int x = p_pos.x; - int y = p_pos.y; - - ERR_FAIL_INDEX(x, width); - ERR_FAIL_INDEX(y, height); - - int ofs = width * y + x; - int bbyte = ofs / 8; - int bbit = ofs % 8; - - uint8_t b = bitmask[bbyte]; - - if (p_value) - b |= (1 << bbit); - else - b &= ~(1 << bbit); - - bitmask.write[bbyte] = b; -} - -bool BitMap::get_bit(const Point2 &p_pos) const { - - int x = Math::fast_ftoi(p_pos.x); - int y = Math::fast_ftoi(p_pos.y); - ERR_FAIL_INDEX_V(x, width, false); - ERR_FAIL_INDEX_V(y, height, false); - - int ofs = width * y + x; - int bbyte = ofs / 8; - int bbit = ofs % 8; - - return (bitmask[bbyte] & (1 << bbit)) != 0; -} - -Size2 BitMap::get_size() const { - - return Size2(width, height); -} - -void BitMap::_set_data(const Dictionary &p_d) { - - ERR_FAIL_COND(!p_d.has("size")); - ERR_FAIL_COND(!p_d.has("data")); - - create(p_d["size"]); - bitmask = p_d["data"]; -} - -Dictionary BitMap::_get_data() const { - - Dictionary d; - d["size"] = get_size(); - d["data"] = bitmask; - return d; -} - -Vector BitMap::_march_square(const Rect2i &rect, const Point2i &start) const { - - int stepx = 0; - int stepy = 0; - int prevx = 0; - int prevy = 0; - int startx = start.x; - int starty = start.y; - int curx = startx; - int cury = starty; - unsigned int count = 0; - Set case9s; - Set case6s; - Vector _points; - do { - int sv = 0; - { //square value - - /* - checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent - +---+---+ - | 1 | 2 | - +---+---+ - | 4 | 8 | <- current pixel (curx,cury) - +---+---+ - */ - //NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel - Rect2i fixed_rect = Rect2i(rect.position, rect.size - Size2i(2, 2)); - Point2i tl = Point2i(curx - 1, cury - 1); - sv += (fixed_rect.has_point(tl) && get_bit(tl)) ? 1 : 0; - Point2i tr = Point2i(curx, cury - 1); - sv += (fixed_rect.has_point(tr) && get_bit(tr)) ? 2 : 0; - Point2i bl = Point2i(curx - 1, cury); - sv += (fixed_rect.has_point(bl) && get_bit(bl)) ? 4 : 0; - Point2i br = Point2i(curx, cury); - sv += (fixed_rect.has_point(br) && get_bit(br)) ? 8 : 0; - ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector()); - } - - switch (sv) { - - case 1: - case 5: - case 13: - /* going UP with these cases: - 1 5 13 - +---+---+ +---+---+ +---+---+ - | 1 | | | 1 | | | 1 | | - +---+---+ +---+---+ +---+---+ - | | | | 4 | | | 4 | 8 | - +---+---+ +---+---+ +---+---+ - */ - stepx = 0; - stepy = -1; - break; - - case 8: - case 10: - case 11: - /* going DOWN with these cases: - 8 10 11 - +---+---+ +---+---+ +---+---+ - | | | | | 2 | | 1 | 2 | - +---+---+ +---+---+ +---+---+ - | | 8 | | | 8 | | | 8 | - +---+---+ +---+---+ +---+---+ - */ - stepx = 0; - stepy = 1; - break; - - case 4: - case 12: - case 14: - /* going LEFT with these cases: - 4 12 14 - +---+---+ +---+---+ +---+---+ - | | | | | | | | 2 | - +---+---+ +---+---+ +---+---+ - | 4 | | | 4 | 8 | | 4 | 8 | - +---+---+ +---+---+ +---+---+ - */ - stepx = -1; - stepy = 0; - break; - - case 2: - case 3: - case 7: - /* going RIGHT with these cases: - 2 3 7 - +---+---+ +---+---+ +---+---+ - | | 2 | | 1 | 2 | | 1 | 2 | - +---+---+ +---+---+ +---+---+ - | | | | | | | 4 | | - +---+---+ +---+---+ +---+---+ - */ - stepx = 1; - stepy = 0; - break; - case 9: - /* - +---+---+ - | 1 | | - +---+---+ - | | 8 | - +---+---+ - this should normally go UP, but if we already been here, we go down - */ - if (case9s.has(Point2i(curx, cury))) { - //found, so we go down, and delete from case9s; - stepx = 0; - stepy = 1; - case9s.erase(Point2i(curx, cury)); - } else { - //not found, we go up, and add to case9s; - stepx = 0; - stepy = -1; - case9s.insert(Point2i(curx, cury)); - } - break; - case 6: - /* - 6 - +---+---+ - | | 2 | - +---+---+ - | 4 | | - +---+---+ - this normally go RIGHT, but if its coming from UP, it should go LEFT - */ - if (case6s.has(Point2i(curx, cury))) { - //found, so we go down, and delete from case6s; - stepx = -1; - stepy = 0; - case6s.erase(Point2i(curx, cury)); - } else { - //not found, we go up, and add to case6s; - stepx = 1; - stepy = 0; - case6s.insert(Point2i(curx, cury)); - } - break; - default: - ERR_PRINT("this shouldn't happen."); - } - //little optimization - // if previous direction is same as current direction, - // then we should modify the last vec to current - curx += stepx; - cury += stepy; - if (stepx == prevx && stepy == prevy) { - _points.write[_points.size() - 1].x = (float)(curx - rect.position.x); - _points.write[_points.size() - 1].y = (float)(cury + rect.position.y); - } else { - _points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y))); - } - - count++; - prevx = stepx; - prevy = stepy; - - ERR_FAIL_COND_V(count > width * height, _points); - } while (curx != startx || cury != starty); - return _points; -} - -static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) { - float res; - float slope; - float intercept; - - if (start.x == end.x) { - res = Math::absf(i.x - end.x); - } else if (start.y == end.y) { - res = Math::absf(i.y - end.y); - } else { - slope = (end.y - start.y) / (end.x - start.x); - intercept = start.y - (slope * start.x); - res = Math::absf(slope * i.x - i.y + intercept) / Math::sqrt(Math::pow(slope, 2.0f) + 1.0); - } - return res; -} - -static Vector rdp(const Vector &v, float optimization) { - if (v.size() < 3) - return v; - - int index = -1; - float dist = 0; - //not looping first and last point - for (size_t i = 1, size = v.size(); i < size - 1; ++i) { - float cdist = perpendicular_distance(v[i], v[0], v[v.size() - 1]); - if (cdist > dist) { - dist = cdist; - index = static_cast(i); - } - } - if (dist > optimization) { - - Vector left, right; - left.resize(index); - for (int i = 0; i < index; i++) { - left.write[i] = v[i]; - } - right.resize(v.size() - index); - for (int i = 0; i < right.size(); i++) { - right.write[i] = v[index + i]; - } - Vector r1 = rdp(left, optimization); - Vector r2 = rdp(right, optimization); - - int middle = r1.size(); - r1.resize(r1.size() + r2.size()); - for (int i = 0; i < r2.size(); i++) { - r1.write[middle + i] = r2[i]; - } - return r1; - } else { - Vector ret; - ret.push_back(v[0]); - ret.push_back(v[v.size() - 1]); - return ret; - } -} - -static Vector reduce(const Vector &points, const Rect2i &rect, float epsilon) { - int size = points.size(); - // if there are less than 3 points, then we have nothing - ERR_FAIL_COND_V(size < 3, Vector()); - // if there are less than 9 points (but more than 3), then we don't need to reduce it - if (size < 9) { - return points; - } - - float maxEp = MIN(rect.size.width, rect.size.height); - float ep = CLAMP(epsilon, 0.0, maxEp / 2); - Vector result = rdp(points, ep); - - Vector2 last = result[result.size() - 1]; - - if (last.y > result[0].y && last.distance_to(result[0]) < ep * 0.5f) { - result.write[0].y = last.y; - result.resize(result.size() - 1); - } - return result; -} - -struct FillBitsStackEntry { - Point2i pos; - int i; - int j; -}; - -static void fill_bits(const BitMap *p_src, Ref &p_map, const Point2i &p_pos, const Rect2i &rect) { - - // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps - PoolVector stack; - // Tracking size since we won't be shrinking the stack vector - int stack_size = 0; - - Point2i pos = p_pos; - int next_i = 0; - int next_j = 0; - - bool reenter = true; - bool popped = false; - do { - if (reenter) { - next_i = pos.x - 1; - next_j = pos.y - 1; - reenter = false; - } - - for (int i = next_i; i <= pos.x + 1; i++) { - for (int j = next_j; j <= pos.y + 1; j++) { - if (popped) { - // The next loop over j must start normally - next_j = pos.y; - popped = false; - // Skip because an iteration was already executed with current counter values - continue; - } - - if (i < rect.position.x || i >= rect.position.x + rect.size.x) - continue; - if (j < rect.position.y || j >= rect.position.y + rect.size.y) - continue; - - if (p_map->get_bit(Vector2(i, j))) - continue; - - else if (p_src->get_bit(Vector2(i, j))) { - p_map->set_bit(Vector2(i, j), true); - - FillBitsStackEntry se = { pos, i, j }; - stack.resize(MAX(stack_size + 1, stack.size())); - stack.set(stack_size, se); - stack_size++; - - pos = Point2i(i, j); - reenter = true; - break; - } - } - if (reenter) { - break; - } - } - if (!reenter) { - if (stack_size) { - FillBitsStackEntry se = stack.get(stack_size - 1); - stack_size--; - pos = se.pos; - next_i = se.i; - next_j = se.j; - popped = true; - } - } - } while (reenter || popped); - - print_verbose("BitMap: Max stack size: " + itos(stack.size())); -} - -Vector > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { - - Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); - print_verbose("BitMap: Rect: " + r); - - Point2i from; - Ref fill; - fill.instance(); - fill->create(get_size()); - - Vector > polygons; - for (int i = r.position.y; i < r.position.y + r.size.height; i++) { - for (int j = r.position.x; j < r.position.x + r.size.width; j++) { - if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { - - Vector polygon = _march_square(r, Point2i(j, i)); - print_verbose("BitMap: Pre reduce: " + itos(polygon.size())); - polygon = reduce(polygon, r, p_epsilon); - print_verbose("BitMap: Post reduce: " + itos(polygon.size())); - polygons.push_back(polygon); - fill_bits(this, fill, Point2i(j, i), r); - } - } - } - - return polygons; -} - -void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { - - Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); - - Ref copy; - copy.instance(); - copy->create(get_size()); - copy->bitmask = bitmask; - - for (int i = r.position.y; i < r.position.y + r.size.height; i++) { - for (int j = r.position.x; j < r.position.x + r.size.width; j++) { - if (copy->get_bit(Point2(j, i))) - continue; - - bool found = false; - - for (int y = i - p_pixels; y <= i + p_pixels; y++) { - for (int x = j - p_pixels; x <= j + p_pixels; x++) { - - if (x < p_rect.position.x || x >= p_rect.position.x + p_rect.size.x) - continue; - if (y < p_rect.position.y || y >= p_rect.position.y + p_rect.size.y) - continue; - - float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON; - if (d > p_pixels) - continue; - - if (copy->get_bit(Point2(x, y))) { - found = true; - break; - } - } - if (found) - break; - } - - if (found) { - set_bit(Point2(j, i), true); - } - } - } -} - -Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const { - - Vector > result = clip_opaque_to_polygons(p_rect, p_epsilon); - - // Convert result to bindable types - - Array result_array; - result_array.resize(result.size()); - for (int i = 0; i < result.size(); i++) { - - const Vector &polygon = result[i]; - - PoolVector2Array polygon_array; - polygon_array.resize(polygon.size()); - - { - PoolVector2Array::Write w = polygon_array.write(); - for (int j = 0; j < polygon.size(); j++) { - w[j] = polygon[j]; - } - } - - result_array[i] = polygon_array; - } - - return result_array; -} - -void BitMap::_bind_methods() { - - ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); - ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image", "threshold"), &BitMap::create_from_image_alpha, DEFVAL(0.1)); - - ClassDB::bind_method(D_METHOD("set_bit", "position", "bit"), &BitMap::set_bit); - ClassDB::bind_method(D_METHOD("get_bit", "position"), &BitMap::get_bit); - - ClassDB::bind_method(D_METHOD("set_bit_rect", "rect", "bit"), &BitMap::set_bit_rect); - ClassDB::bind_method(D_METHOD("get_true_bit_count"), &BitMap::get_true_bit_count); - - ClassDB::bind_method(D_METHOD("get_size"), &BitMap::get_size); - - ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data); - ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data); - - ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask); - ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0)); - - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); -} - -BitMap::BitMap() { - - width = 0; - height = 0; -} - -////////////////////////////////////// diff --git a/scene/resources/bit_mask.h b/scene/resources/bit_mask.h deleted file mode 100644 index 4575064260..0000000000 --- a/scene/resources/bit_mask.h +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************/ -/* bit_mask.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 BIT_MASK_H -#define BIT_MASK_H - -#include "core/image.h" -#include "core/io/resource_loader.h" -#include "core/resource.h" - -class BitMap : public Resource { - - GDCLASS(BitMap, Resource); - OBJ_SAVE_TYPE(BitMap); - - Vector bitmask; - int width; - int height; - - Vector _march_square(const Rect2i &rect, const Point2i &start) const; - - Array _opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const; - -protected: - void _set_data(const Dictionary &p_d); - Dictionary _get_data() const; - - static void _bind_methods(); - -public: - void create(const Size2 &p_size); - void create_from_image_alpha(const Ref &p_image, float p_threshold = 0.1); - - void set_bit(const Point2 &p_pos, bool p_value); - bool get_bit(const Point2 &p_pos) const; - void set_bit_rect(const Rect2 &p_rect, bool p_value); - int get_true_bit_count() const; - - Size2 get_size() const; - - void grow_mask(int p_pixels, const Rect2 &p_rect); - - Vector > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; - - BitMap(); -}; - -#endif // BIT_MASK_H diff --git a/scene/resources/bounds.cpp b/scene/resources/bounds.cpp deleted file mode 100644 index e6fa5b818d..0000000000 --- a/scene/resources/bounds.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************/ -/* bounds.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "bounds.h" - -void Bounds::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_bsp_tree", "bsp_tree"), &Bounds::set_bsp_tree); - ClassDB::bind_method(D_METHOD("get_bsp_tree"), &Bounds::get_bsp_tree); - - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bsp_tree"), "set_bsp_tree", "get_bsp_tree"); -} - -void Bounds::set_bsp_tree(const BSP_Tree &p_bsp_tree) { - - bsp_tree = p_bsp_tree; -} - -BSP_Tree Bounds::get_bsp_tree() const { - - return bsp_tree; -} - -Bounds::Bounds() { -} diff --git a/scene/resources/bounds.h b/scene/resources/bounds.h deleted file mode 100644 index 9a1801f23d..0000000000 --- a/scene/resources/bounds.h +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************/ -/* bounds.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 BOUNDS_H -#define BOUNDS_H - -#include "core/math/bsp_tree.h" -#include "core/resource.h" - -class Bounds : public Resource { - - GDCLASS(Bounds, Resource); - BSP_Tree bsp_tree; - -protected: - static void _bind_methods(); - -public: - void set_bsp_tree(const BSP_Tree &p_bsp_tree); - BSP_Tree get_bsp_tree() const; - - Bounds(); -}; - -#endif // BOUNDS_H diff --git a/scene/resources/color_ramp.cpp b/scene/resources/color_ramp.cpp deleted file mode 100644 index 845a1474a4..0000000000 --- a/scene/resources/color_ramp.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/*************************************************************************/ -/* color_ramp.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "color_ramp.h" -#include "core/core_string_names.h" - -//setter and getter names for property serialization -#define COLOR_RAMP_GET_OFFSETS "get_offsets" -#define COLOR_RAMP_GET_COLORS "get_colors" -#define COLOR_RAMP_SET_OFFSETS "set_offsets" -#define COLOR_RAMP_SET_COLORS "set_colors" - -Gradient::Gradient() { - //Set initial color ramp transition from black to white - points.resize(2); - points.write[0].color = Color(0, 0, 0, 1); - points.write[0].offset = 0; - points.write[1].color = Color(1, 1, 1, 1); - points.write[1].offset = 1; - is_sorted = true; -} - -Gradient::~Gradient() { -} - -void Gradient::_bind_methods() { - - ClassDB::bind_method(D_METHOD("add_point", "offset", "color"), &Gradient::add_point); - ClassDB::bind_method(D_METHOD("remove_point", "offset"), &Gradient::remove_point); - - 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"), &Gradient::set_color); - ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color); - - ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Gradient::get_color_at_offset); - - ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count); - - 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"), &Gradient::set_colors); - ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &Gradient::get_colors); - - ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS); - ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS); -} - -Vector Gradient::get_offsets() const { - Vector offsets; - offsets.resize(points.size()); - for (int i = 0; i < points.size(); i++) { - offsets.write[i] = points[i].offset; - } - return offsets; -} - -Vector Gradient::get_colors() const { - Vector colors; - colors.resize(points.size()); - for (int i = 0; i < points.size(); i++) { - colors.write[i] = points[i].color; - } - return colors; -} - -void Gradient::set_offsets(const Vector &p_offsets) { - points.resize(p_offsets.size()); - for (int i = 0; i < points.size(); i++) { - points.write[i].offset = p_offsets[i]; - } - is_sorted = false; - emit_signal(CoreStringNames::get_singleton()->changed); -} - -void Gradient::set_colors(const Vector &p_colors) { - if (points.size() < p_colors.size()) - is_sorted = false; - points.resize(p_colors.size()); - for (int i = 0; i < points.size(); i++) { - points.write[i].color = p_colors[i]; - } - emit_signal(CoreStringNames::get_singleton()->changed); -} - -Vector &Gradient::get_points() { - return points; -} - -void Gradient::add_point(float p_offset, const Color &p_color) { - - Point p; - p.offset = p_offset; - p.color = p_color; - is_sorted = false; - points.push_back(p); - - emit_signal(CoreStringNames::get_singleton()->changed); -} - -void Gradient::remove_point(int p_index) { - - ERR_FAIL_INDEX(p_index, points.size()); - ERR_FAIL_COND(points.size() <= 2); - points.remove(p_index); - emit_signal(CoreStringNames::get_singleton()->changed); -} - -void Gradient::set_points(Vector &p_points) { - points = p_points; - is_sorted = false; - emit_signal(CoreStringNames::get_singleton()->changed); -} - -void Gradient::set_offset(int pos, const float offset) { - if (points.size() <= pos) - points.resize(pos + 1); - points.write[pos].offset = offset; - is_sorted = false; - emit_signal(CoreStringNames::get_singleton()->changed); -} - -float Gradient::get_offset(int pos) const { - if (points.size() && points.size() > pos) - return points[pos].offset; - return 0; //TODO: Maybe throw some error instead? -} - -void Gradient::set_color(int pos, const Color &color) { - if (points.size() <= pos) { - points.resize(pos + 1); - is_sorted = false; - } - points.write[pos].color = color; - emit_signal(CoreStringNames::get_singleton()->changed); -} - -Color Gradient::get_color(int pos) const { - if (points.size() && points.size() > pos) - return points[pos].color; - return Color(0, 0, 0, 1); //TODO: Maybe throw some error instead? -} - -int Gradient::get_points_count() const { - return points.size(); -} diff --git a/scene/resources/color_ramp.h b/scene/resources/color_ramp.h deleted file mode 100644 index 7a96bb429b..0000000000 --- a/scene/resources/color_ramp.h +++ /dev/null @@ -1,129 +0,0 @@ -/*************************************************************************/ -/* color_ramp.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 SCENE_RESOURCES_COLOR_RAMP_H_ -#define SCENE_RESOURCES_COLOR_RAMP_H_ - -#include "core/resource.h" - -class Gradient : public Resource { - GDCLASS(Gradient, Resource); - OBJ_SAVE_TYPE(Gradient); - -public: - struct Point { - - float offset; - Color color; - bool operator<(const Point &p_ponit) const { - return offset < p_ponit.offset; - } - }; - -private: - Vector points; - bool is_sorted; - -protected: - static void _bind_methods(); - -public: - Gradient(); - virtual ~Gradient(); - - void add_point(float p_offset, const Color &p_color); - void remove_point(int p_index); - - void set_points(Vector &p_points); - Vector &get_points(); - - void set_offset(int pos, const float offset); - float get_offset(int pos) const; - - void set_color(int pos, const Color &color); - Color get_color(int pos) const; - - void set_offsets(const Vector &p_offsets); - Vector get_offsets() const; - - void set_colors(const Vector &p_colors); - Vector get_colors() const; - - _FORCE_INLINE_ Color get_color_at_offset(float p_offset) { - - if (points.empty()) - return Color(0, 0, 0, 1); - - if (!is_sorted) { - points.sort(); - is_sorted = true; - } - - //binary search - int low = 0; - int high = points.size() - 1; - int middle = 0; - -#if DEBUG_ENABLED - if (low > high) - ERR_PRINT("low > high, this may be a bug"); -#endif - - while (low <= high) { - middle = (low + high) / 2; - const Point &point = points[middle]; - if (point.offset > p_offset) { - high = middle - 1; //search low end of array - } else if (point.offset < p_offset) { - low = middle + 1; //search high end of array - } else { - return point.color; - } - } - - //return interpolated value - if (points[middle].offset > p_offset) { - middle--; - } - int first = middle; - int second = middle + 1; - if (second >= points.size()) - return points[points.size() - 1].color; - if (first < 0) - return points[0].color; - const Point &pointFirst = points[first]; - const Point &pointSecond = points[second]; - return pointFirst.color.linear_interpolate(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset)); - } - - int get_points_count() const; -}; - -#endif /* SCENE_RESOURCES_COLOR_RAMP_H_ */ diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 666112b473..a54f13a88f 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -32,7 +32,7 @@ #define ENVIRONMENT_H #include "core/resource.h" -#include "scene/resources/sky_box.h" +#include "scene/resources/sky.h" #include "scene/resources/texture.h" #include "servers/visual_server.h" diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp new file mode 100644 index 0000000000..99ce8ef821 --- /dev/null +++ b/scene/resources/gradient.cpp @@ -0,0 +1,176 @@ +/*************************************************************************/ +/* gradient.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "gradient.h" + +#include "core/core_string_names.h" + +//setter and getter names for property serialization +#define COLOR_RAMP_GET_OFFSETS "get_offsets" +#define COLOR_RAMP_GET_COLORS "get_colors" +#define COLOR_RAMP_SET_OFFSETS "set_offsets" +#define COLOR_RAMP_SET_COLORS "set_colors" + +Gradient::Gradient() { + //Set initial color ramp transition from black to white + points.resize(2); + points.write[0].color = Color(0, 0, 0, 1); + points.write[0].offset = 0; + points.write[1].color = Color(1, 1, 1, 1); + points.write[1].offset = 1; + is_sorted = true; +} + +Gradient::~Gradient() { +} + +void Gradient::_bind_methods() { + + ClassDB::bind_method(D_METHOD("add_point", "offset", "color"), &Gradient::add_point); + ClassDB::bind_method(D_METHOD("remove_point", "offset"), &Gradient::remove_point); + + 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"), &Gradient::set_color); + ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color); + + ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Gradient::get_color_at_offset); + + ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count); + + 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"), &Gradient::set_colors); + ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &Gradient::get_colors); + + ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS); + ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS); +} + +Vector Gradient::get_offsets() const { + Vector offsets; + offsets.resize(points.size()); + for (int i = 0; i < points.size(); i++) { + offsets.write[i] = points[i].offset; + } + return offsets; +} + +Vector Gradient::get_colors() const { + Vector colors; + colors.resize(points.size()); + for (int i = 0; i < points.size(); i++) { + colors.write[i] = points[i].color; + } + return colors; +} + +void Gradient::set_offsets(const Vector &p_offsets) { + points.resize(p_offsets.size()); + for (int i = 0; i < points.size(); i++) { + points.write[i].offset = p_offsets[i]; + } + is_sorted = false; + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void Gradient::set_colors(const Vector &p_colors) { + if (points.size() < p_colors.size()) + is_sorted = false; + points.resize(p_colors.size()); + for (int i = 0; i < points.size(); i++) { + points.write[i].color = p_colors[i]; + } + emit_signal(CoreStringNames::get_singleton()->changed); +} + +Vector &Gradient::get_points() { + return points; +} + +void Gradient::add_point(float p_offset, const Color &p_color) { + + Point p; + p.offset = p_offset; + p.color = p_color; + is_sorted = false; + points.push_back(p); + + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void Gradient::remove_point(int p_index) { + + ERR_FAIL_INDEX(p_index, points.size()); + ERR_FAIL_COND(points.size() <= 2); + points.remove(p_index); + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void Gradient::set_points(Vector &p_points) { + points = p_points; + is_sorted = false; + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void Gradient::set_offset(int pos, const float offset) { + if (points.size() <= pos) + points.resize(pos + 1); + points.write[pos].offset = offset; + is_sorted = false; + emit_signal(CoreStringNames::get_singleton()->changed); +} + +float Gradient::get_offset(int pos) const { + if (points.size() && points.size() > pos) + return points[pos].offset; + return 0; //TODO: Maybe throw some error instead? +} + +void Gradient::set_color(int pos, const Color &color) { + if (points.size() <= pos) { + points.resize(pos + 1); + is_sorted = false; + } + points.write[pos].color = color; + emit_signal(CoreStringNames::get_singleton()->changed); +} + +Color Gradient::get_color(int pos) const { + if (points.size() && points.size() > pos) + return points[pos].color; + return Color(0, 0, 0, 1); //TODO: Maybe throw some error instead? +} + +int Gradient::get_points_count() const { + return points.size(); +} diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h new file mode 100644 index 0000000000..a51a0ca0d0 --- /dev/null +++ b/scene/resources/gradient.h @@ -0,0 +1,129 @@ +/*************************************************************************/ +/* gradient.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 GRADIENT_H +#define GRADIENT_H + +#include "core/resource.h" + +class Gradient : public Resource { + GDCLASS(Gradient, Resource); + OBJ_SAVE_TYPE(Gradient); + +public: + struct Point { + + float offset; + Color color; + bool operator<(const Point &p_ponit) const { + return offset < p_ponit.offset; + } + }; + +private: + Vector points; + bool is_sorted; + +protected: + static void _bind_methods(); + +public: + Gradient(); + virtual ~Gradient(); + + void add_point(float p_offset, const Color &p_color); + void remove_point(int p_index); + + void set_points(Vector &p_points); + Vector &get_points(); + + void set_offset(int pos, const float offset); + float get_offset(int pos) const; + + void set_color(int pos, const Color &color); + Color get_color(int pos) const; + + void set_offsets(const Vector &p_offsets); + Vector get_offsets() const; + + void set_colors(const Vector &p_colors); + Vector get_colors() const; + + _FORCE_INLINE_ Color get_color_at_offset(float p_offset) { + + if (points.empty()) + return Color(0, 0, 0, 1); + + if (!is_sorted) { + points.sort(); + is_sorted = true; + } + + //binary search + int low = 0; + int high = points.size() - 1; + int middle = 0; + +#if DEBUG_ENABLED + if (low > high) + ERR_PRINT("low > high, this may be a bug"); +#endif + + while (low <= high) { + middle = (low + high) / 2; + const Point &point = points[middle]; + if (point.offset > p_offset) { + high = middle - 1; //search low end of array + } else if (point.offset < p_offset) { + low = middle + 1; //search high end of array + } else { + return point.color; + } + } + + //return interpolated value + if (points[middle].offset > p_offset) { + middle--; + } + int first = middle; + int second = middle + 1; + if (second >= points.size()) + return points[points.size() - 1].color; + if (first < 0) + return points[0].color; + const Point &pointFirst = points[first]; + const Point &pointSecond = points[second]; + return pointFirst.color.linear_interpolate(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset)); + } + + int get_points_count() const; +}; + +#endif // GRADIENT_H diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/line_shape_2d.cpp new file mode 100644 index 0000000000..f5d5fb561a --- /dev/null +++ b/scene/resources/line_shape_2d.cpp @@ -0,0 +1,121 @@ +/*************************************************************************/ +/* line_shape_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "line_shape_2d.h" + +#include "servers/physics_2d_server.h" +#include "servers/visual_server.h" + +bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + Vector2 point = get_d() * get_normal(); + Vector2 l[2][2] = { { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }, { point, point + get_normal() * 30 } }; + + for (int i = 0; i < 2; i++) { + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l[i]); + if (p_point.distance_to(closest) < p_tolerance) + return true; + } + + return false; +} + +void LineShape2D::_update_shape() { + + Array arr; + arr.push_back(normal); + arr.push_back(d); + Physics2DServer::get_singleton()->shape_set_data(get_rid(), arr); + emit_changed(); +} + +void LineShape2D::set_normal(const Vector2 &p_normal) { + + normal = p_normal; + _update_shape(); +} + +void LineShape2D::set_d(real_t p_d) { + + d = p_d; + _update_shape(); +} + +Vector2 LineShape2D::get_normal() const { + + return normal; +} +real_t LineShape2D::get_d() const { + + return d; +} + +void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) { + + Vector2 point = get_d() * get_normal(); + + Vector2 l1[2] = { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }; + VS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, 3); + Vector2 l2[2] = { point, point + get_normal() * 30 }; + VS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3); +} +Rect2 LineShape2D::get_rect() const { + + Vector2 point = get_d() * get_normal(); + + Vector2 l1[2] = { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }; + Vector2 l2[2] = { point, point + get_normal() * 30 }; + Rect2 rect; + rect.position = l1[0]; + rect.expand_to(l1[1]); + rect.expand_to(l2[0]); + rect.expand_to(l2[1]); + return rect; +} + +void LineShape2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_normal", "normal"), &LineShape2D::set_normal); + ClassDB::bind_method(D_METHOD("get_normal"), &LineShape2D::get_normal); + + ClassDB::bind_method(D_METHOD("set_d", "d"), &LineShape2D::set_d); + ClassDB::bind_method(D_METHOD("get_d"), &LineShape2D::get_d); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "d"), "set_d", "get_d"); +} + +LineShape2D::LineShape2D() : + Shape2D(Physics2DServer::get_singleton()->line_shape_create()) { + + normal = Vector2(0, -1); + d = 0; + _update_shape(); +} diff --git a/scene/resources/line_shape_2d.h b/scene/resources/line_shape_2d.h new file mode 100644 index 0000000000..f684862025 --- /dev/null +++ b/scene/resources/line_shape_2d.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* line_shape_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 LINE_SHAPE_2D_H +#define LINE_SHAPE_2D_H + +#include "scene/resources/shape_2d.h" + +class LineShape2D : public Shape2D { + GDCLASS(LineShape2D, Shape2D); + + Vector2 normal; + real_t d; + + void _update_shape(); + +protected: + static void _bind_methods(); + +public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + + void set_normal(const Vector2 &p_normal); + void set_d(real_t p_d); + + Vector2 get_normal() const; + real_t get_d() const; + + virtual void draw(const RID &p_to_rid, const Color &p_color); + virtual Rect2 get_rect() const; + + LineShape2D(); +}; + +#endif // LINE_SHAPE_2D_H diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp new file mode 100644 index 0000000000..e838d4a685 --- /dev/null +++ b/scene/resources/resource_format_text.cpp @@ -0,0 +1,1756 @@ +/*************************************************************************/ +/* resource_format_text.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "resource_format_text.h" + +#include "core/io/resource_format_binary.h" +#include "core/os/dir_access.h" +#include "core/project_settings.h" +#include "core/version.h" + +//version 2: changed names for basis, aabb, poolvectors, etc. +#define FORMAT_VERSION 2 + +#include "core/os/dir_access.h" +#include "core/version.h" + +#define _printerr() ERR_PRINT(String(res_path + ":" + itos(lines) + " - Parse Error: " + error_text).utf8().get_data()); + +/// + +void ResourceInteractiveLoaderText::set_local_path(const String &p_local_path) { + + res_path = p_local_path; +} + +Ref ResourceInteractiveLoaderText::get_resource() { + + return resource; +} + +Error ResourceInteractiveLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { + + VariantParser::Token token; + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_NUMBER) { + r_err_str = "Expected number (sub-resource index)"; + return ERR_PARSE_ERROR; + } + + int index = token.value; + + if (!p_data->resource_map.has(index)) { + Ref dr; + dr.instance(); + dr->set_subindex(index); + p_data->resource_map[index] = dr; + p_data->resource_set.insert(dr); + } + + r_res = p_data->resource_map[index]; + + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { + r_err_str = "Expected ')'"; + return ERR_PARSE_ERROR; + } + + return OK; +} + +Error ResourceInteractiveLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { + + VariantParser::Token token; + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_NUMBER) { + r_err_str = "Expected number (sub-resource index)"; + return ERR_PARSE_ERROR; + } + + int id = token.value; + + ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR); + + r_res = p_data->rev_external_resources[id]; + + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { + r_err_str = "Expected ')'"; + return ERR_PARSE_ERROR; + } + + return OK; +} + +Error ResourceInteractiveLoaderText::_parse_sub_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { + + VariantParser::Token token; + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_NUMBER) { + r_err_str = "Expected number (sub-resource index)"; + return ERR_PARSE_ERROR; + } + + int index = token.value; + + String path = local_path + "::" + itos(index); + + if (!ignore_resource_parsing) { + + if (!ResourceCache::has(path)) { + r_err_str = "Can't load cached sub-resource: " + path; + return ERR_PARSE_ERROR; + } + + r_res = RES(ResourceCache::get(path)); + } else { + r_res = RES(); + } + + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { + r_err_str = "Expected ')'"; + return ERR_PARSE_ERROR; + } + + return OK; +} + +Error ResourceInteractiveLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { + + VariantParser::Token token; + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_NUMBER) { + r_err_str = "Expected number (sub-resource index)"; + return ERR_PARSE_ERROR; + } + + int id = token.value; + + if (!ignore_resource_parsing) { + + if (!ext_resources.has(id)) { + r_err_str = "Can't load cached ext-resource #" + itos(id); + return ERR_PARSE_ERROR; + } + + String path = ext_resources[id].path; + String type = ext_resources[id].type; + + if (path.find("://") == -1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); + } + + r_res = ResourceLoader::load(path, type); + + if (r_res.is_null()) { + WARN_PRINT(String("Couldn't load external resource: " + path).utf8().get_data()); + } + } else { + r_res = RES(); + } + + VariantParser::get_token(p_stream, token, line, r_err_str); + if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { + r_err_str = "Expected ')'"; + return ERR_PARSE_ERROR; + } + + return OK; +} + +Ref ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) { + Ref packed_scene; + packed_scene.instance(); + + while (true) { + + if (next_tag.name == "node") { + + int parent = -1; + int owner = -1; + int type = -1; + int name = -1; + int instance = -1; + int index = -1; + //int base_scene=-1; + + if (next_tag.fields.has("name")) { + name = packed_scene->get_state()->add_name(next_tag.fields["name"]); + } + + if (next_tag.fields.has("parent")) { + NodePath np = next_tag.fields["parent"]; + np.prepend_period(); //compatible to how it manages paths internally + parent = packed_scene->get_state()->add_node_path(np); + } + + if (next_tag.fields.has("type")) { + type = packed_scene->get_state()->add_name(next_tag.fields["type"]); + } else { + type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced + } + + if (next_tag.fields.has("instance")) { + + instance = packed_scene->get_state()->add_value(next_tag.fields["instance"]); + + if (packed_scene->get_state()->get_node_count() == 0 && parent == -1) { + packed_scene->get_state()->set_base_scene(instance); + instance = -1; + } + } + + if (next_tag.fields.has("instance_placeholder")) { + + String path = next_tag.fields["instance_placeholder"]; + + int path_v = packed_scene->get_state()->add_value(path); + + if (packed_scene->get_state()->get_node_count() == 0) { + error = ERR_FILE_CORRUPT; + error_text = "Instance Placeholder can't be used for inheritance."; + _printerr(); + return Ref(); + } + + instance = path_v | SceneState::FLAG_INSTANCE_IS_PLACEHOLDER; + } + + if (next_tag.fields.has("owner")) { + owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]); + } else { + if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1)) + owner = 0; //if no owner, owner is root + } + + if (next_tag.fields.has("index")) { + index = next_tag.fields["index"]; + } + + int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance, index); + + if (next_tag.fields.has("groups")) { + + Array groups = next_tag.fields["groups"]; + for (int i = 0; i < groups.size(); i++) { + packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i])); + } + } + + while (true) { + + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &parser); + + if (error) { + if (error != ERR_FILE_EOF) { + _printerr(); + return Ref(); + } else { + return packed_scene; + } + } + + if (assign != String()) { + int nameidx = packed_scene->get_state()->add_name(assign); + int valueidx = packed_scene->get_state()->add_value(value); + packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx); + //it's assignment + } else if (next_tag.name != String()) { + break; + } + } + } else if (next_tag.name == "connection") { + + if (!next_tag.fields.has("from")) { + error = ERR_FILE_CORRUPT; + error_text = "missing 'from' field from connection tag"; + return Ref(); + } + + if (!next_tag.fields.has("to")) { + error = ERR_FILE_CORRUPT; + error_text = "missing 'to' field from connection tag"; + return Ref(); + } + + if (!next_tag.fields.has("signal")) { + error = ERR_FILE_CORRUPT; + error_text = "missing 'signal' field from connection tag"; + return Ref(); + } + + if (!next_tag.fields.has("method")) { + error = ERR_FILE_CORRUPT; + error_text = "missing 'method' field from connection tag"; + return Ref(); + } + + NodePath from = next_tag.fields["from"]; + NodePath to = next_tag.fields["to"]; + StringName method = next_tag.fields["method"]; + StringName signal = next_tag.fields["signal"]; + int flags = CONNECT_PERSIST; + Array binds; + + if (next_tag.fields.has("flags")) { + flags = next_tag.fields["flags"]; + } + + if (next_tag.fields.has("binds")) { + binds = next_tag.fields["binds"]; + } + + Vector bind_ints; + for (int i = 0; i < binds.size(); i++) { + bind_ints.push_back(packed_scene->get_state()->add_value(binds[i])); + } + + packed_scene->get_state()->add_connection( + packed_scene->get_state()->add_node_path(from.simplified()), + packed_scene->get_state()->add_node_path(to.simplified()), + packed_scene->get_state()->add_name(signal), + packed_scene->get_state()->add_name(method), + flags, + bind_ints); + + error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser); + + if (error) { + if (error != ERR_FILE_EOF) { + _printerr(); + return Ref(); + } else { + return packed_scene; + } + } + } else if (next_tag.name == "editable") { + + if (!next_tag.fields.has("path")) { + error = ERR_FILE_CORRUPT; + error_text = "missing 'path' field from connection tag"; + _printerr(); + return Ref(); + } + + NodePath path = next_tag.fields["path"]; + + packed_scene->get_state()->add_editable_instance(path.simplified()); + + error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser); + + if (error) { + if (error != ERR_FILE_EOF) { + _printerr(); + return Ref(); + } else { + return packed_scene; + } + } + } else { + + error = ERR_FILE_CORRUPT; + _printerr(); + return Ref(); + } + } + + return packed_scene; +} + +Error ResourceInteractiveLoaderText::poll() { + + if (error != OK) + return error; + + if (next_tag.name == "ext_resource") { + + if (!next_tag.fields.has("path")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'path' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("id")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'id' in external resource tag"; + _printerr(); + return error; + } + + String path = next_tag.fields["path"]; + String type = next_tag.fields["type"]; + int index = next_tag.fields["id"]; + + if (path.find("://") == -1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); + } + + if (remaps.has(path)) { + path = remaps[path]; + } + + RES res = ResourceLoader::load(path, type); + + if (res.is_null()) { + + if (ResourceLoader::get_abort_on_missing_resources()) { + error = ERR_FILE_CORRUPT; + error_text = "[ext_resource] referenced nonexistent resource at: " + path; + _printerr(); + return error; + } else { + ResourceLoader::notify_dependency_error(local_path, path, type); + } + } else { + + resource_cache.push_back(res); + } + + ExtResource er; + er.path = path; + er.type = type; + ext_resources[index] = er; + + error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); + + if (error) { + _printerr(); + } + + resource_current++; + return error; + + } else if (next_tag.name == "sub_resource") { + + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("id")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'index' in external resource tag"; + _printerr(); + return error; + } + + String type = next_tag.fields["type"]; + int id = next_tag.fields["id"]; + + String path = local_path + "::" + itos(id); + + //bool exists=ResourceCache::has(path); + + Ref res; + + if (!ResourceCache::has(path)) { //only if it doesn't exist + + Object *obj = ClassDB::instance(type); + if (!obj) { + + error_text += "Can't create sub resource of type: " + type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + Resource *r = Object::cast_to(obj); + if (!r) { + + error_text += "Can't create sub resource of type, because not a resource: " + type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + res = Ref(r); + resource_cache.push_back(res); + res->set_path(path); + } + + resource_current++; + + while (true) { + + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); + + if (error) { + _printerr(); + return error; + } + + if (assign != String()) { + if (res.is_valid()) { + res->set(assign, value); + } + //it's assignment + } else if (next_tag.name != String()) { + + error = OK; + break; + } else { + error = ERR_FILE_CORRUPT; + error_text = "Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; + } + } + + return OK; + + } else if (next_tag.name == "resource") { + + if (is_scene) { + + error_text += "found the 'resource' tag on a scene file!"; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + Object *obj = ClassDB::instance(res_type); + if (!obj) { + + error_text += "Can't create sub resource of type: " + res_type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + Resource *r = Object::cast_to(obj); + if (!r) { + + error_text += "Can't create sub resource of type, because not a resource: " + res_type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + resource = Ref(r); + + resource_current++; + + while (true) { + + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); + + if (error) { + if (error != ERR_FILE_EOF) { + _printerr(); + } else { + if (!ResourceCache::has(res_path)) { + resource->set_path(res_path); + } + resource->set_as_translation_remapped(translation_remapped); + } + return error; + } + + if (assign != String()) { + resource->set(assign, value); + //it's assignment + } else if (next_tag.name != String()) { + + error = ERR_FILE_CORRUPT; + error_text = "Extra tag found when parsing main resource file"; + _printerr(); + return error; + } else { + error = ERR_FILE_EOF; + return error; + } + } + + return OK; + + } else if (next_tag.name == "node") { + + if (!is_scene) { + + error_text += "found the 'node' tag on a resource file!"; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + Ref packed_scene = _parse_node_tag(rp); + + if (!packed_scene.is_valid()) + return error; + + error = ERR_FILE_EOF; + //get it here + resource = packed_scene; + if (!ResourceCache::has(res_path)) { + packed_scene->set_path(res_path); + } + + return error; + + } else { + error_text += "Unknown tag in file: " + next_tag.name; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + return OK; +} + +int ResourceInteractiveLoaderText::get_stage() const { + + return resource_current; +} +int ResourceInteractiveLoaderText::get_stage_count() const { + + return resources_total; //+ext_resources; +} + +void ResourceInteractiveLoaderText::set_translation_remapped(bool p_remapped) { + + translation_remapped = p_remapped; +} + +ResourceInteractiveLoaderText::ResourceInteractiveLoaderText() { + translation_remapped = false; +} + +ResourceInteractiveLoaderText::~ResourceInteractiveLoaderText() { + + memdelete(f); +} + +void ResourceInteractiveLoaderText::get_dependencies(FileAccess *p_f, List *p_dependencies, bool p_add_types) { + + open(p_f); + ignore_resource_parsing = true; + ERR_FAIL_COND(error != OK); + + while (next_tag.name == "ext_resource") { + + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return; + } + + if (!next_tag.fields.has("id")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'index' in external resource tag"; + _printerr(); + return; + } + + String path = next_tag.fields["path"]; + String type = next_tag.fields["type"]; + + if (path.find("://") == -1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); + } + + if (p_add_types) { + path += "::" + type; + } + + p_dependencies->push_back(path); + + Error err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); + + if (err) { + print_line(error_text + " - " + itos(lines)); + error_text = "Unexpected end of file"; + _printerr(); + error = ERR_FILE_CORRUPT; + } + } +} + +Error ResourceInteractiveLoaderText::rename_dependencies(FileAccess *p_f, const String &p_path, const Map &p_map) { + + open(p_f, true); + ERR_FAIL_COND_V(error != OK, error); + ignore_resource_parsing = true; + //FileAccess + + FileAccess *fw = NULL; + + String base_path = local_path.get_base_dir(); + + uint64_t tag_end = f->get_position(); + + while (true) { + + Error err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); + + if (err != OK) { + if (fw) { + memdelete(fw); + } + error = ERR_FILE_CORRUPT; + ERR_FAIL_V(error); + } + + if (next_tag.name != "ext_resource") { + + //nothing was done + if (!fw) + return OK; + + break; + + } else { + + if (!fw) { + + fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); + if (is_scene) { + fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); + } else { + fw->store_line("[gd_resource type=\"" + res_type + "\" load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); + } + } + + if (!next_tag.fields.has("path") || !next_tag.fields.has("id") || !next_tag.fields.has("type")) { + memdelete(fw); + error = ERR_FILE_CORRUPT; + ERR_FAIL_V(error); + } + + String path = next_tag.fields["path"]; + int index = next_tag.fields["id"]; + String type = next_tag.fields["type"]; + + bool relative = false; + if (!path.begins_with("res://")) { + path = base_path.plus_file(path).simplify_path(); + relative = true; + } + + if (p_map.has(path)) { + String np = p_map[path]; + path = np; + } + + if (relative) { + //restore relative + path = base_path.path_to_file(path); + } + + fw->store_line("[ext_resource path=\"" + path + "\" type=\"" + type + "\" id=" + itos(index) + "]"); + + tag_end = f->get_position(); + } + } + + f->seek(tag_end); + + uint8_t c = f->get_8(); + while (!f->eof_reached()) { + fw->store_8(c); + c = f->get_8(); + } + f->close(); + + bool all_ok = fw->get_error() == OK; + + memdelete(fw); + + if (!all_ok) { + return ERR_CANT_CREATE; + } + + DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(p_path); + da->rename(p_path + ".depren", p_path); + memdelete(da); + + return OK; +} + +void ResourceInteractiveLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) { + + error = OK; + + lines = 1; + f = p_f; + + stream.f = f; + is_scene = false; + ignore_resource_parsing = false; + resource_current = 0; + + VariantParser::Tag tag; + Error err = VariantParser::parse_tag(&stream, lines, error_text, tag); + + if (err) { + + error = err; + _printerr(); + return; + } + + if (tag.fields.has("format")) { + int fmt = tag.fields["format"]; + if (fmt > FORMAT_VERSION) { + error_text = "Saved with newer format version"; + _printerr(); + error = ERR_PARSE_ERROR; + return; + } + } + + if (tag.name == "gd_scene") { + is_scene = true; + + } else if (tag.name == "gd_resource") { + if (!tag.fields.has("type")) { + error_text = "Missing 'type' field in 'gd_resource' tag"; + _printerr(); + error = ERR_PARSE_ERROR; + return; + } + + res_type = tag.fields["type"]; + + } else { + error_text = "Unrecognized file type: " + tag.name; + _printerr(); + error = ERR_PARSE_ERROR; + return; + } + + if (tag.fields.has("load_steps")) { + resources_total = tag.fields["load_steps"]; + } else { + resources_total = 0; + } + + if (!p_skip_first_tag) { + + err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); + + if (err) { + error_text = "Unexpected end of file"; + _printerr(); + error = ERR_FILE_CORRUPT; + } + } + + rp.ext_func = _parse_ext_resources; + rp.sub_func = _parse_sub_resources; + rp.func = NULL; + rp.userdata = this; +} + +static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false) { + + CharString utf8 = p_string.utf8(); + if (p_bit_on_len) { + f->store_32((utf8.length() + 1) | 0x80000000); + } else { + f->store_32(utf8.length() + 1); + } + f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); +} + +Error ResourceInteractiveLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) { + + if (error) + return error; + + FileAccessRef wf = FileAccess::open(p_path, FileAccess::WRITE); + if (!wf) { + return ERR_CANT_OPEN; + } + + //save header compressed + static const uint8_t header[4] = { 'R', 'S', 'R', 'C' }; + wf->store_buffer(header, 4); + + wf->store_32(0); //endianness, little endian + wf->store_32(0); //64 bits file, false for now + wf->store_32(VERSION_MAJOR); + wf->store_32(VERSION_MINOR); + static const int save_format_version = 3; //use format version 3 for saving + wf->store_32(save_format_version); + + bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type); + wf->store_64(0); //offset to import metadata, this is no longer used + for (int i = 0; i < 14; i++) + wf->store_32(0); // reserved + + wf->store_32(0); //string table size, will not be in use + size_t ext_res_count_pos = wf->get_position(); + + wf->store_32(0); //zero ext resources, still parsing them + + //go with external resources + + DummyReadData dummy_read; + VariantParser::ResourceParser rp; + rp.ext_func = _parse_ext_resource_dummys; + rp.sub_func = _parse_sub_resource_dummys; + rp.userdata = &dummy_read; + + while (next_tag.name == "ext_resource") { + + if (!next_tag.fields.has("path")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'path' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("id")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'id' in external resource tag"; + _printerr(); + return error; + } + + String path = next_tag.fields["path"]; + String type = next_tag.fields["type"]; + int index = next_tag.fields["id"]; + + bs_save_unicode_string(wf.f, type); + bs_save_unicode_string(wf.f, path); + + int lindex = dummy_read.external_resources.size(); + Ref dr; + dr.instance(); + dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external + dummy_read.external_resources[dr] = lindex; + dummy_read.rev_external_resources[index] = dr; + + error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); + + if (error) { + _printerr(); + return error; + } + } + + // save external resource table + wf->seek(ext_res_count_pos); + wf->store_32(dummy_read.external_resources.size()); + wf->seek_end(); + + //now, save resources to a separate file, for now + + size_t sub_res_count_pos = wf->get_position(); + wf->store_32(0); //zero sub resources, still parsing them + + String temp_file = p_path + ".temp"; + FileAccessRef wf2 = FileAccess::open(temp_file, FileAccess::WRITE); + if (!wf2) { + return ERR_CANT_OPEN; + } + + Vector local_offsets; + Vector local_pointers_pos; + + while (next_tag.name == "sub_resource" || next_tag.name == "resource") { + + String type; + int id = -1; + bool main_res; + + if (next_tag.name == "sub_resource") { + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("id")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'index' in external resource tag"; + _printerr(); + return error; + } + + type = next_tag.fields["type"]; + id = next_tag.fields["id"]; + main_res = false; + } else { + type = res_type; + id = 0; //used for last anyway + main_res = true; + } + + local_offsets.push_back(wf2->get_position()); + + bs_save_unicode_string(wf, "local://" + itos(id)); + local_pointers_pos.push_back(wf->get_position()); + wf->store_64(0); //temp local offset + + bs_save_unicode_string(wf2, type); + size_t propcount_ofs = wf2->get_position(); + wf2->store_32(0); + + int prop_count = 0; + + while (true) { + + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); + + if (error) { + if (main_res && error == ERR_FILE_EOF) { + next_tag.name = ""; //exit + break; + } + + _printerr(); + return error; + } + + if (assign != String()) { + + Map empty_string_map; //unused + bs_save_unicode_string(wf2, assign, true); + ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map); + prop_count++; + + } else if (next_tag.name != String()) { + + error = OK; + break; + } else { + error = ERR_FILE_CORRUPT; + error_text = "Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; + } + } + + wf2->seek(propcount_ofs); + wf2->store_32(prop_count); + wf2->seek_end(); + } + + if (next_tag.name == "node") { + //this is a node, must save one more! + + if (!is_scene) { + + error_text += "found the 'node' tag on a resource file!"; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } + + Ref packed_scene = _parse_node_tag(rp); + + if (!packed_scene.is_valid()) + return error; + + error = OK; + //get it here + List props; + packed_scene->get_property_list(&props); + + bs_save_unicode_string(wf, "local://0"); + local_pointers_pos.push_back(wf->get_position()); + wf->store_64(0); //temp local offset + + local_offsets.push_back(wf2->get_position()); + bs_save_unicode_string(wf2, "PackedScene"); + size_t propcount_ofs = wf2->get_position(); + wf2->store_32(0); + + int prop_count = 0; + + for (List::Element *E = props.front(); E; E = E->next()) { + + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + continue; + + String name = E->get().name; + Variant value = packed_scene->get(name); + + Map empty_string_map; //unused + bs_save_unicode_string(wf2, name, true); + ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map); + prop_count++; + } + + wf2->seek(propcount_ofs); + wf2->store_32(prop_count); + wf2->seek_end(); + } + + wf2->close(); + + size_t offset_from = wf->get_position(); + wf->seek(sub_res_count_pos); //plus one because the saved one + wf->store_32(local_offsets.size()); + + for (int i = 0; i < local_offsets.size(); i++) { + wf->seek(local_pointers_pos[i]); + wf->store_64(local_offsets[i] + offset_from); + } + + wf->seek_end(); + + Vector data = FileAccess::get_file_as_array(temp_file); + wf->store_buffer(data.ptr(), data.size()); + { + DirAccessRef dar = DirAccess::open(temp_file.get_base_dir()); + dar->remove(temp_file); + } + + wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end + + wf->close(); + + return OK; +} + +String ResourceInteractiveLoaderText::recognize(FileAccess *p_f) { + + error = OK; + + lines = 1; + f = p_f; + + stream.f = f; + + ignore_resource_parsing = true; + + VariantParser::Tag tag; + Error err = VariantParser::parse_tag(&stream, lines, error_text, tag); + + if (err) { + _printerr(); + return ""; + } + + if (tag.fields.has("format")) { + int fmt = tag.fields["format"]; + if (fmt > FORMAT_VERSION) { + error_text = "Saved with newer format version"; + _printerr(); + return ""; + } + } + + if (tag.name == "gd_scene") + return "PackedScene"; + + if (tag.name != "gd_resource") + return ""; + + if (!tag.fields.has("type")) { + error_text = "Missing 'type' field in 'gd_resource' tag"; + _printerr(); + return ""; + } + + return tag.fields["type"]; +} + +///////////////////// + +Ref ResourceFormatLoaderText::load_interactive(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) + *r_error = ERR_CANT_OPEN; + + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + + if (err != OK) { + + ERR_FAIL_COND_V(err != OK, Ref()); + } + + Ref ria = memnew(ResourceInteractiveLoaderText); + String path = p_original_path != "" ? p_original_path : p_path; + ria->local_path = ProjectSettings::get_singleton()->localize_path(path); + ria->res_path = ria->local_path; + //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); + ria->open(f); + + return ria; +} + +void ResourceFormatLoaderText::get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const { + + if (p_type == "") { + get_recognized_extensions(p_extensions); + return; + } + + if (p_type == "PackedScene") + p_extensions->push_back("tscn"); + else + p_extensions->push_back("tres"); +} + +void ResourceFormatLoaderText::get_recognized_extensions(List *p_extensions) const { + + p_extensions->push_back("tscn"); + p_extensions->push_back("tres"); +} + +bool ResourceFormatLoaderText::handles_type(const String &p_type) const { + + return true; +} +String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { + + String ext = p_path.get_extension().to_lower(); + if (ext == "tscn") + return "PackedScene"; + else if (ext != "tres") + return String(); + + //for anyhting else must test.. + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + + return ""; //could not rwead + } + + Ref ria = memnew(ResourceInteractiveLoaderText); + ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); + ria->res_path = ria->local_path; + //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); + String r = ria->recognize(f); + return r; +} + +void ResourceFormatLoaderText::get_dependencies(const String &p_path, List *p_dependencies, bool p_add_types) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + + ERR_FAIL(); + } + + Ref ria = memnew(ResourceInteractiveLoaderText); + ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); + ria->res_path = ria->local_path; + //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); + ria->get_dependencies(f, p_dependencies, p_add_types); +} + +Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const Map &p_map) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + + ERR_FAIL_V(ERR_CANT_OPEN); + } + + Ref ria = memnew(ResourceInteractiveLoaderText); + ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); + ria->res_path = ria->local_path; + //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); + return ria->rename_dependencies(f, p_path, p_map); +} + +ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = NULL; + +Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) { + + Error err; + FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err); + + if (err != OK) { + + ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN); + } + + Ref ria = memnew(ResourceInteractiveLoaderText); + String path = p_src_path; + ria->local_path = ProjectSettings::get_singleton()->localize_path(path); + ria->res_path = ria->local_path; + //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); + ria->open(f); + return ria->save_as_binary(f, p_dst_path); +} + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +String ResourceFormatSaverTextInstance::_write_resources(void *ud, const RES &p_resource) { + + ResourceFormatSaverTextInstance *rsi = (ResourceFormatSaverTextInstance *)ud; + return rsi->_write_resource(p_resource); +} + +String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { + + if (external_resources.has(res)) { + + return "ExtResource( " + itos(external_resources[res] + 1) + " )"; + } else { + + if (internal_resources.has(res)) { + return "SubResource( " + itos(internal_resources[res]) + " )"; + } else if (res->get_path().length() && res->get_path().find("::") == -1) { + + //external resource + String path = relative_paths ? local_path.path_to_file(res->get_path()) : res->get_path(); + return "Resource( \"" + path + "\" )"; + } else { + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL_V("null"); + //internal resource + } + } + + return "null"; +} + +void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, bool p_main) { + + switch (p_variant.get_type()) { + case Variant::OBJECT: { + + RES res = p_variant.operator RefPtr(); + + if (res.is_null() || external_resources.has(res)) + return; + + if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { + int index = external_resources.size(); + external_resources[res] = index; + return; + } + + if (resource_set.has(res)) + return; + + List property_list; + + res->get_property_list(&property_list); + property_list.sort(); + + List::Element *I = property_list.front(); + + while (I) { + + PropertyInfo pi = I->get(); + + if (pi.usage & PROPERTY_USAGE_STORAGE) { + + Variant v = res->get(I->get().name); + _find_resources(v); + } + + I = I->next(); + } + + resource_set.insert(res); //saved after, so the children it needs are available when loaded + saved_resources.push_back(res); + + } break; + case Variant::ARRAY: { + + Array varray = p_variant; + int len = varray.size(); + for (int i = 0; i < len; i++) { + + Variant v = varray.get(i); + _find_resources(v); + } + + } break; + case Variant::DICTIONARY: { + + Dictionary d = p_variant; + List keys; + d.get_key_list(&keys); + for (List::Element *E = keys.front(); E; E = E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } +} + +static String _valprop(const String &p_name) { + + // Escape and quote strings with extended ASCII or further Unicode characters + // as well as '"', '=' or ' ' (32) + const CharType *cstr = p_name.c_str(); + for (int i = 0; cstr[i]; i++) { + if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] < 33 || cstr[i] > 126) { + return "\"" + p_name.c_escape_multiline() + "\""; + } + } + // Keep as is + return p_name; +} + +Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + + if (p_path.ends_with(".tscn")) { + packed_scene = p_resource; + } + + Error err; + f = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(err, ERR_CANT_OPEN); + FileAccessRef _fref(f); + + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + + relative_paths = p_flags & ResourceSaver::FLAG_RELATIVE_PATHS; + skip_editor = p_flags & ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources = p_flags & ResourceSaver::FLAG_BUNDLE_RESOURCES; + takeover_paths = p_flags & ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS; + if (!p_path.begins_with("res://")) { + takeover_paths = false; + } + + // save resources + _find_resources(p_resource, true); + + if (packed_scene.is_valid()) { + //add instances to external resources if saving a packed scene + for (int i = 0; i < packed_scene->get_state()->get_node_count(); i++) { + if (packed_scene->get_state()->is_node_instance_placeholder(i)) + continue; + + Ref instance = packed_scene->get_state()->get_node_instance(i); + if (instance.is_valid() && !external_resources.has(instance)) { + int index = external_resources.size(); + external_resources[instance] = index; + } + } + } + + ERR_FAIL_COND_V(err != OK, err); + + { + String title = packed_scene.is_valid() ? "[gd_scene " : "[gd_resource "; + if (packed_scene.is_null()) + title += "type=\"" + p_resource->get_class() + "\" "; + int load_steps = saved_resources.size() + external_resources.size(); + /* + if (packed_scene.is_valid()) { + load_steps+=packed_scene->get_node_count(); + } + //no, better to not use load steps from nodes, no point to that + */ + + if (load_steps > 1) { + title += "load_steps=" + itos(load_steps) + " "; + } + title += "format=" + itos(FORMAT_VERSION) + ""; + + f->store_string(title); + f->store_line("]\n"); //one empty line + } + + Vector sorted_er; + sorted_er.resize(external_resources.size()); + + for (Map::Element *E = external_resources.front(); E; E = E->next()) { + + sorted_er.write[E->get()] = E->key(); + } + + for (int i = 0; i < sorted_er.size(); i++) { + String p = sorted_er[i]->get_path(); + + f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i]->get_save_class() + "\" id=" + itos(i + 1) + "]\n"); //bundled + } + + if (external_resources.size()) + f->store_line(String()); //separate + + Set used_indices; + + for (List::Element *E = saved_resources.front(); E; E = E->next()) { + + RES res = E->get(); + if (E->next() && (res->get_path() == "" || res->get_path().find("::") != -1)) { + + if (res->get_subindex() != 0) { + if (used_indices.has(res->get_subindex())) { + res->set_subindex(0); //repeated + } else { + used_indices.insert(res->get_subindex()); + } + } + } + } + + for (List::Element *E = saved_resources.front(); E; E = E->next()) { + + RES res = E->get(); + ERR_CONTINUE(!resource_set.has(res)); + bool main = (E->next() == NULL); + + if (main && packed_scene.is_valid()) + break; //save as a scene + + if (main) { + f->store_line("[resource]"); + } else { + String line = "[sub_resource "; + if (res->get_subindex() == 0) { + int new_subindex = 1; + if (used_indices.size()) { + new_subindex = used_indices.back()->get() + 1; + } + + res->set_subindex(new_subindex); + used_indices.insert(new_subindex); + } + + int idx = res->get_subindex(); + line += "type=\"" + res->get_class() + "\" id=" + itos(idx); + f->store_line(line + "]"); + if (takeover_paths) { + res->set_path(p_path + "::" + itos(idx), true); + } + + internal_resources[res] = idx; +#ifdef TOOLS_ENABLED + res->set_edited(false); +#endif + } + + List property_list; + res->get_property_list(&property_list); + //property_list.sort(); + for (List::Element *PE = property_list.front(); PE; PE = PE->next()) { + + if (skip_editor && PE->get().name.begins_with("__editor")) + continue; + + if (PE->get().usage & PROPERTY_USAGE_STORAGE) { + + String name = PE->get().name; + Variant value = res->get(name); + Variant default_value = ClassDB::class_get_default_property_value(res->get_class(), name); + + if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { + continue; + } + + if (PE->get().type == Variant::OBJECT && value.is_zero() && !(PE->get().usage & PROPERTY_USAGE_STORE_IF_NULL)) + continue; + + String vars; + VariantWriter::write_to_string(value, vars, _write_resources, this); + f->store_string(_valprop(name) + " = " + vars + "\n"); + } + } + + f->store_string("\n"); + } + + if (packed_scene.is_valid()) { + //if this is a scene, save nodes and connections! + Ref state = packed_scene->get_state(); + for (int i = 0; i < state->get_node_count(); i++) { + + StringName type = state->get_node_type(i); + StringName name = state->get_node_name(i); + int index = state->get_node_index(i); + NodePath path = state->get_node_path(i, true); + NodePath owner = state->get_node_owner_path(i); + Ref instance = state->get_node_instance(i); + String instance_placeholder = state->get_node_instance_placeholder(i); + Vector groups = state->get_node_groups(i); + + String header = "[node"; + header += " name=\"" + String(name) + "\""; + if (type != StringName()) { + header += " type=\"" + String(type) + "\""; + } + if (path != NodePath()) { + header += " parent=\"" + String(path.simplified()) + "\""; + } + if (owner != NodePath() && owner != NodePath(".")) { + header += " owner=\"" + String(owner.simplified()) + "\""; + } + if (index >= 0) { + header += " index=\"" + itos(index) + "\""; + } + + if (groups.size()) { + String sgroups = " groups=[\n"; + for (int j = 0; j < groups.size(); j++) { + sgroups += "\"" + String(groups[j]).c_escape() + "\",\n"; + } + sgroups += "]"; + header += sgroups; + } + + f->store_string(header); + + if (instance_placeholder != String()) { + + String vars; + f->store_string(" instance_placeholder="); + VariantWriter::write_to_string(instance_placeholder, vars, _write_resources, this); + f->store_string(vars); + } + + if (instance.is_valid()) { + + String vars; + f->store_string(" instance="); + VariantWriter::write_to_string(instance, vars, _write_resources, this); + f->store_string(vars); + } + + f->store_line("]"); + + for (int j = 0; j < state->get_node_property_count(i); j++) { + + String vars; + VariantWriter::write_to_string(state->get_node_property_value(i, j), vars, _write_resources, this); + + f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n"); + } + + f->store_line(String()); + } + + for (int i = 0; i < state->get_connection_count(); i++) { + + String connstr = "[connection"; + connstr += " signal=\"" + String(state->get_connection_signal(i)) + "\""; + connstr += " from=\"" + String(state->get_connection_source(i).simplified()) + "\""; + connstr += " to=\"" + String(state->get_connection_target(i).simplified()) + "\""; + connstr += " method=\"" + String(state->get_connection_method(i)) + "\""; + int flags = state->get_connection_flags(i); + if (flags != Object::CONNECT_PERSIST) { + connstr += " flags=" + itos(flags); + } + + Array binds = state->get_connection_binds(i); + f->store_string(connstr); + if (binds.size()) { + String vars; + VariantWriter::write_to_string(binds, vars, _write_resources, this); + f->store_string(" binds= " + vars); + } + + f->store_line("]"); + } + + Vector editable_instances = state->get_editable_instances(); + for (int i = 0; i < editable_instances.size(); i++) { + f->store_line("\n[editable path=\"" + editable_instances[i].operator String() + "\"]"); + } + } + + if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { + f->close(); + return ERR_CANT_CREATE; + } + + f->close(); + //memdelete(f); + + return OK; +} + +Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + + if (p_path.ends_with(".sct") && p_resource->get_class() != "PackedScene") { + return ERR_FILE_UNRECOGNIZED; + } + + ResourceFormatSaverTextInstance saver; + return saver.save(p_path, p_resource, p_flags); +} + +bool ResourceFormatSaverText::recognize(const RES &p_resource) const { + + return true; // all recognized! +} +void ResourceFormatSaverText::get_recognized_extensions(const RES &p_resource, List *p_extensions) const { + + if (p_resource->get_class() == "PackedScene") + p_extensions->push_back("tscn"); //text scene + else + p_extensions->push_back("tres"); //text resource +} + +ResourceFormatSaverText *ResourceFormatSaverText::singleton = NULL; +ResourceFormatSaverText::ResourceFormatSaverText() { + singleton = this; +} diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h new file mode 100644 index 0000000000..526f7760d2 --- /dev/null +++ b/scene/resources/resource_format_text.h @@ -0,0 +1,183 @@ +/*************************************************************************/ +/* resource_format_text.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 RESOURCE_FORMAT_TEXT_H +#define RESOURCE_FORMAT_TEXT_H + +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "core/variant_parser.h" +#include "scene/resources/packed_scene.h" + +class ResourceInteractiveLoaderText : public ResourceInteractiveLoader { + + bool translation_remapped; + String local_path; + String res_path; + String error_text; + + FileAccess *f; + + VariantParser::StreamFile stream; + + struct ExtResource { + String path; + String type; + }; + + bool is_scene; + String res_type; + + bool ignore_resource_parsing; + + //Map remaps; + + Map ext_resources; + + int resources_total; + int resource_current; + String resource_type; + + VariantParser::Tag next_tag; + + mutable int lines; + + Map remaps; + //void _printerr(); + + static Error _parse_sub_resources(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return reinterpret_cast(p_self)->_parse_sub_resource(p_stream, r_res, line, r_err_str); } + static Error _parse_ext_resources(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return reinterpret_cast(p_self)->_parse_ext_resource(p_stream, r_res, line, r_err_str); } + + Error _parse_sub_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); + Error _parse_ext_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); + + // for converter + class DummyResource : public Resource { + public: + }; + + struct DummyReadData { + + Map external_resources; + Map rev_external_resources; + Set resource_set; + Map resource_map; + }; + + static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } + static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } + + static Error _parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); + static Error _parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); + + VariantParser::ResourceParser rp; + + friend class ResourceFormatLoaderText; + + List resource_cache; + Error error; + + RES resource; + + Ref _parse_node_tag(VariantParser::ResourceParser &parser); + +public: + virtual void set_local_path(const String &p_local_path); + virtual Ref get_resource(); + virtual Error poll(); + virtual int get_stage() const; + virtual int get_stage_count() const; + virtual void set_translation_remapped(bool p_remapped); + + void open(FileAccess *p_f, bool p_skip_first_tag = false); + String recognize(FileAccess *p_f); + void get_dependencies(FileAccess *p_f, List *p_dependencies, bool p_add_types); + Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map &p_map); + + Error save_as_binary(FileAccess *p_f, const String &p_path); + ResourceInteractiveLoaderText(); + ~ResourceInteractiveLoaderText(); +}; + +class ResourceFormatLoaderText : public ResourceFormatLoader { + GDCLASS(ResourceFormatLoaderText, ResourceFormatLoader) +public: + static ResourceFormatLoaderText *singleton; + virtual Ref load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const; + virtual void get_recognized_extensions(List *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String &p_path, List *p_dependencies, bool p_add_types = false); + virtual Error rename_dependencies(const String &p_path, const Map &p_map); + + static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path); + + ResourceFormatLoaderText() { singleton = this; } +}; + +class ResourceFormatSaverTextInstance { + + String local_path; + + Ref packed_scene; + + bool takeover_paths; + bool relative_paths; + bool bundle_resources; + bool skip_editor; + FileAccess *f; + Set resource_set; + List saved_resources; + Map external_resources; + Map internal_resources; + + void _find_resources(const Variant &p_variant, bool p_main = false); + + static String _write_resources(void *ud, const RES &p_resource); + String _write_resource(const RES &res); + +public: + Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); +}; + +class ResourceFormatSaverText : public ResourceFormatSaver { + GDCLASS(ResourceFormatSaverText, ResourceFormatSaver) +public: + static ResourceFormatSaverText *singleton; + virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + virtual bool recognize(const RES &p_resource) const; + virtual void get_recognized_extensions(const RES &p_resource, List *p_extensions) const; + + ResourceFormatSaverText(); +}; + +#endif // RESOURCE_FORMAT_TEXT_H diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp deleted file mode 100644 index d00a7c2918..0000000000 --- a/scene/resources/scene_format_text.cpp +++ /dev/null @@ -1,1755 +0,0 @@ -/*************************************************************************/ -/* scene_format_text.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "scene_format_text.h" -#include "core/io/resource_format_binary.h" -#include "core/os/dir_access.h" -#include "core/project_settings.h" -#include "core/version.h" - -//version 2: changed names for basis, aabb, poolvectors, etc. -#define FORMAT_VERSION 2 - -#include "core/os/dir_access.h" -#include "core/version.h" - -#define _printerr() ERR_PRINT(String(res_path + ":" + itos(lines) + " - Parse Error: " + error_text).utf8().get_data()); - -/// - -void ResourceInteractiveLoaderText::set_local_path(const String &p_local_path) { - - res_path = p_local_path; -} - -Ref ResourceInteractiveLoaderText::get_resource() { - - return resource; -} - -Error ResourceInteractiveLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { - - VariantParser::Token token; - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_NUMBER) { - r_err_str = "Expected number (sub-resource index)"; - return ERR_PARSE_ERROR; - } - - int index = token.value; - - if (!p_data->resource_map.has(index)) { - Ref dr; - dr.instance(); - dr->set_subindex(index); - p_data->resource_map[index] = dr; - p_data->resource_set.insert(dr); - } - - r_res = p_data->resource_map[index]; - - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - return OK; -} - -Error ResourceInteractiveLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { - - VariantParser::Token token; - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_NUMBER) { - r_err_str = "Expected number (sub-resource index)"; - return ERR_PARSE_ERROR; - } - - int id = token.value; - - ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR); - - r_res = p_data->rev_external_resources[id]; - - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - return OK; -} - -Error ResourceInteractiveLoaderText::_parse_sub_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { - - VariantParser::Token token; - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_NUMBER) { - r_err_str = "Expected number (sub-resource index)"; - return ERR_PARSE_ERROR; - } - - int index = token.value; - - String path = local_path + "::" + itos(index); - - if (!ignore_resource_parsing) { - - if (!ResourceCache::has(path)) { - r_err_str = "Can't load cached sub-resource: " + path; - return ERR_PARSE_ERROR; - } - - r_res = RES(ResourceCache::get(path)); - } else { - r_res = RES(); - } - - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - return OK; -} - -Error ResourceInteractiveLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { - - VariantParser::Token token; - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_NUMBER) { - r_err_str = "Expected number (sub-resource index)"; - return ERR_PARSE_ERROR; - } - - int id = token.value; - - if (!ignore_resource_parsing) { - - if (!ext_resources.has(id)) { - r_err_str = "Can't load cached ext-resource #" + itos(id); - return ERR_PARSE_ERROR; - } - - String path = ext_resources[id].path; - String type = ext_resources[id].type; - - if (path.find("://") == -1 && path.is_rel_path()) { - // path is relative to file being loaded, so convert to a resource path - path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); - } - - r_res = ResourceLoader::load(path, type); - - if (r_res.is_null()) { - WARN_PRINT(String("Couldn't load external resource: " + path).utf8().get_data()); - } - } else { - r_res = RES(); - } - - VariantParser::get_token(p_stream, token, line, r_err_str); - if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { - r_err_str = "Expected ')'"; - return ERR_PARSE_ERROR; - } - - return OK; -} - -Ref ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) { - Ref packed_scene; - packed_scene.instance(); - - while (true) { - - if (next_tag.name == "node") { - - int parent = -1; - int owner = -1; - int type = -1; - int name = -1; - int instance = -1; - int index = -1; - //int base_scene=-1; - - if (next_tag.fields.has("name")) { - name = packed_scene->get_state()->add_name(next_tag.fields["name"]); - } - - if (next_tag.fields.has("parent")) { - NodePath np = next_tag.fields["parent"]; - np.prepend_period(); //compatible to how it manages paths internally - parent = packed_scene->get_state()->add_node_path(np); - } - - if (next_tag.fields.has("type")) { - type = packed_scene->get_state()->add_name(next_tag.fields["type"]); - } else { - type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced - } - - if (next_tag.fields.has("instance")) { - - instance = packed_scene->get_state()->add_value(next_tag.fields["instance"]); - - if (packed_scene->get_state()->get_node_count() == 0 && parent == -1) { - packed_scene->get_state()->set_base_scene(instance); - instance = -1; - } - } - - if (next_tag.fields.has("instance_placeholder")) { - - String path = next_tag.fields["instance_placeholder"]; - - int path_v = packed_scene->get_state()->add_value(path); - - if (packed_scene->get_state()->get_node_count() == 0) { - error = ERR_FILE_CORRUPT; - error_text = "Instance Placeholder can't be used for inheritance."; - _printerr(); - return Ref(); - } - - instance = path_v | SceneState::FLAG_INSTANCE_IS_PLACEHOLDER; - } - - if (next_tag.fields.has("owner")) { - owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]); - } else { - if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1)) - owner = 0; //if no owner, owner is root - } - - if (next_tag.fields.has("index")) { - index = next_tag.fields["index"]; - } - - int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance, index); - - if (next_tag.fields.has("groups")) { - - Array groups = next_tag.fields["groups"]; - for (int i = 0; i < groups.size(); i++) { - packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i])); - } - } - - while (true) { - - String assign; - Variant value; - - error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &parser); - - if (error) { - if (error != ERR_FILE_EOF) { - _printerr(); - return Ref(); - } else { - return packed_scene; - } - } - - if (assign != String()) { - int nameidx = packed_scene->get_state()->add_name(assign); - int valueidx = packed_scene->get_state()->add_value(value); - packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx); - //it's assignment - } else if (next_tag.name != String()) { - break; - } - } - } else if (next_tag.name == "connection") { - - if (!next_tag.fields.has("from")) { - error = ERR_FILE_CORRUPT; - error_text = "missing 'from' field from connection tag"; - return Ref(); - } - - if (!next_tag.fields.has("to")) { - error = ERR_FILE_CORRUPT; - error_text = "missing 'to' field from connection tag"; - return Ref(); - } - - if (!next_tag.fields.has("signal")) { - error = ERR_FILE_CORRUPT; - error_text = "missing 'signal' field from connection tag"; - return Ref(); - } - - if (!next_tag.fields.has("method")) { - error = ERR_FILE_CORRUPT; - error_text = "missing 'method' field from connection tag"; - return Ref(); - } - - NodePath from = next_tag.fields["from"]; - NodePath to = next_tag.fields["to"]; - StringName method = next_tag.fields["method"]; - StringName signal = next_tag.fields["signal"]; - int flags = CONNECT_PERSIST; - Array binds; - - if (next_tag.fields.has("flags")) { - flags = next_tag.fields["flags"]; - } - - if (next_tag.fields.has("binds")) { - binds = next_tag.fields["binds"]; - } - - Vector bind_ints; - for (int i = 0; i < binds.size(); i++) { - bind_ints.push_back(packed_scene->get_state()->add_value(binds[i])); - } - - packed_scene->get_state()->add_connection( - packed_scene->get_state()->add_node_path(from.simplified()), - packed_scene->get_state()->add_node_path(to.simplified()), - packed_scene->get_state()->add_name(signal), - packed_scene->get_state()->add_name(method), - flags, - bind_ints); - - error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser); - - if (error) { - if (error != ERR_FILE_EOF) { - _printerr(); - return Ref(); - } else { - return packed_scene; - } - } - } else if (next_tag.name == "editable") { - - if (!next_tag.fields.has("path")) { - error = ERR_FILE_CORRUPT; - error_text = "missing 'path' field from connection tag"; - _printerr(); - return Ref(); - } - - NodePath path = next_tag.fields["path"]; - - packed_scene->get_state()->add_editable_instance(path.simplified()); - - error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser); - - if (error) { - if (error != ERR_FILE_EOF) { - _printerr(); - return Ref(); - } else { - return packed_scene; - } - } - } else { - - error = ERR_FILE_CORRUPT; - _printerr(); - return Ref(); - } - } - - return packed_scene; -} - -Error ResourceInteractiveLoaderText::poll() { - - if (error != OK) - return error; - - if (next_tag.name == "ext_resource") { - - if (!next_tag.fields.has("path")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'path' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'id' in external resource tag"; - _printerr(); - return error; - } - - String path = next_tag.fields["path"]; - String type = next_tag.fields["type"]; - int index = next_tag.fields["id"]; - - if (path.find("://") == -1 && path.is_rel_path()) { - // path is relative to file being loaded, so convert to a resource path - path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); - } - - if (remaps.has(path)) { - path = remaps[path]; - } - - RES res = ResourceLoader::load(path, type); - - if (res.is_null()) { - - if (ResourceLoader::get_abort_on_missing_resources()) { - error = ERR_FILE_CORRUPT; - error_text = "[ext_resource] referenced nonexistent resource at: " + path; - _printerr(); - return error; - } else { - ResourceLoader::notify_dependency_error(local_path, path, type); - } - } else { - - resource_cache.push_back(res); - } - - ExtResource er; - er.path = path; - er.type = type; - ext_resources[index] = er; - - error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); - - if (error) { - _printerr(); - } - - resource_current++; - return error; - - } else if (next_tag.name == "sub_resource") { - - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'index' in external resource tag"; - _printerr(); - return error; - } - - String type = next_tag.fields["type"]; - int id = next_tag.fields["id"]; - - String path = local_path + "::" + itos(id); - - //bool exists=ResourceCache::has(path); - - Ref res; - - if (!ResourceCache::has(path)) { //only if it doesn't exist - - Object *obj = ClassDB::instance(type); - if (!obj) { - - error_text += "Can't create sub resource of type: " + type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - Resource *r = Object::cast_to(obj); - if (!r) { - - error_text += "Can't create sub resource of type, because not a resource: " + type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - res = Ref(r); - resource_cache.push_back(res); - res->set_path(path); - } - - resource_current++; - - while (true) { - - String assign; - Variant value; - - error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); - - if (error) { - _printerr(); - return error; - } - - if (assign != String()) { - if (res.is_valid()) { - res->set(assign, value); - } - //it's assignment - } else if (next_tag.name != String()) { - - error = OK; - break; - } else { - error = ERR_FILE_CORRUPT; - error_text = "Premature end of file while parsing [sub_resource]"; - _printerr(); - return error; - } - } - - return OK; - - } else if (next_tag.name == "resource") { - - if (is_scene) { - - error_text += "found the 'resource' tag on a scene file!"; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - Object *obj = ClassDB::instance(res_type); - if (!obj) { - - error_text += "Can't create sub resource of type: " + res_type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - Resource *r = Object::cast_to(obj); - if (!r) { - - error_text += "Can't create sub resource of type, because not a resource: " + res_type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - resource = Ref(r); - - resource_current++; - - while (true) { - - String assign; - Variant value; - - error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); - - if (error) { - if (error != ERR_FILE_EOF) { - _printerr(); - } else { - if (!ResourceCache::has(res_path)) { - resource->set_path(res_path); - } - resource->set_as_translation_remapped(translation_remapped); - } - return error; - } - - if (assign != String()) { - resource->set(assign, value); - //it's assignment - } else if (next_tag.name != String()) { - - error = ERR_FILE_CORRUPT; - error_text = "Extra tag found when parsing main resource file"; - _printerr(); - return error; - } else { - error = ERR_FILE_EOF; - return error; - } - } - - return OK; - - } else if (next_tag.name == "node") { - - if (!is_scene) { - - error_text += "found the 'node' tag on a resource file!"; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - Ref packed_scene = _parse_node_tag(rp); - - if (!packed_scene.is_valid()) - return error; - - error = ERR_FILE_EOF; - //get it here - resource = packed_scene; - if (!ResourceCache::has(res_path)) { - packed_scene->set_path(res_path); - } - - return error; - - } else { - error_text += "Unknown tag in file: " + next_tag.name; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - return OK; -} - -int ResourceInteractiveLoaderText::get_stage() const { - - return resource_current; -} -int ResourceInteractiveLoaderText::get_stage_count() const { - - return resources_total; //+ext_resources; -} - -void ResourceInteractiveLoaderText::set_translation_remapped(bool p_remapped) { - - translation_remapped = p_remapped; -} - -ResourceInteractiveLoaderText::ResourceInteractiveLoaderText() { - translation_remapped = false; -} - -ResourceInteractiveLoaderText::~ResourceInteractiveLoaderText() { - - memdelete(f); -} - -void ResourceInteractiveLoaderText::get_dependencies(FileAccess *p_f, List *p_dependencies, bool p_add_types) { - - open(p_f); - ignore_resource_parsing = true; - ERR_FAIL_COND(error != OK); - - while (next_tag.name == "ext_resource") { - - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return; - } - - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'index' in external resource tag"; - _printerr(); - return; - } - - String path = next_tag.fields["path"]; - String type = next_tag.fields["type"]; - - if (path.find("://") == -1 && path.is_rel_path()) { - // path is relative to file being loaded, so convert to a resource path - path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); - } - - if (p_add_types) { - path += "::" + type; - } - - p_dependencies->push_back(path); - - Error err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); - - if (err) { - print_line(error_text + " - " + itos(lines)); - error_text = "Unexpected end of file"; - _printerr(); - error = ERR_FILE_CORRUPT; - } - } -} - -Error ResourceInteractiveLoaderText::rename_dependencies(FileAccess *p_f, const String &p_path, const Map &p_map) { - - open(p_f, true); - ERR_FAIL_COND_V(error != OK, error); - ignore_resource_parsing = true; - //FileAccess - - FileAccess *fw = NULL; - - String base_path = local_path.get_base_dir(); - - uint64_t tag_end = f->get_position(); - - while (true) { - - Error err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); - - if (err != OK) { - if (fw) { - memdelete(fw); - } - error = ERR_FILE_CORRUPT; - ERR_FAIL_V(error); - } - - if (next_tag.name != "ext_resource") { - - //nothing was done - if (!fw) - return OK; - - break; - - } else { - - if (!fw) { - - fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); - if (is_scene) { - fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); - } else { - fw->store_line("[gd_resource type=\"" + res_type + "\" load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); - } - } - - if (!next_tag.fields.has("path") || !next_tag.fields.has("id") || !next_tag.fields.has("type")) { - memdelete(fw); - error = ERR_FILE_CORRUPT; - ERR_FAIL_V(error); - } - - String path = next_tag.fields["path"]; - int index = next_tag.fields["id"]; - String type = next_tag.fields["type"]; - - bool relative = false; - if (!path.begins_with("res://")) { - path = base_path.plus_file(path).simplify_path(); - relative = true; - } - - if (p_map.has(path)) { - String np = p_map[path]; - path = np; - } - - if (relative) { - //restore relative - path = base_path.path_to_file(path); - } - - fw->store_line("[ext_resource path=\"" + path + "\" type=\"" + type + "\" id=" + itos(index) + "]"); - - tag_end = f->get_position(); - } - } - - f->seek(tag_end); - - uint8_t c = f->get_8(); - while (!f->eof_reached()) { - fw->store_8(c); - c = f->get_8(); - } - f->close(); - - bool all_ok = fw->get_error() == OK; - - memdelete(fw); - - if (!all_ok) { - return ERR_CANT_CREATE; - } - - DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - da->remove(p_path); - da->rename(p_path + ".depren", p_path); - memdelete(da); - - return OK; -} - -void ResourceInteractiveLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) { - - error = OK; - - lines = 1; - f = p_f; - - stream.f = f; - is_scene = false; - ignore_resource_parsing = false; - resource_current = 0; - - VariantParser::Tag tag; - Error err = VariantParser::parse_tag(&stream, lines, error_text, tag); - - if (err) { - - error = err; - _printerr(); - return; - } - - if (tag.fields.has("format")) { - int fmt = tag.fields["format"]; - if (fmt > FORMAT_VERSION) { - error_text = "Saved with newer format version"; - _printerr(); - error = ERR_PARSE_ERROR; - return; - } - } - - if (tag.name == "gd_scene") { - is_scene = true; - - } else if (tag.name == "gd_resource") { - if (!tag.fields.has("type")) { - error_text = "Missing 'type' field in 'gd_resource' tag"; - _printerr(); - error = ERR_PARSE_ERROR; - return; - } - - res_type = tag.fields["type"]; - - } else { - error_text = "Unrecognized file type: " + tag.name; - _printerr(); - error = ERR_PARSE_ERROR; - return; - } - - if (tag.fields.has("load_steps")) { - resources_total = tag.fields["load_steps"]; - } else { - resources_total = 0; - } - - if (!p_skip_first_tag) { - - err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); - - if (err) { - error_text = "Unexpected end of file"; - _printerr(); - error = ERR_FILE_CORRUPT; - } - } - - rp.ext_func = _parse_ext_resources; - rp.sub_func = _parse_sub_resources; - rp.func = NULL; - rp.userdata = this; -} - -static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false) { - - CharString utf8 = p_string.utf8(); - if (p_bit_on_len) { - f->store_32((utf8.length() + 1) | 0x80000000); - } else { - f->store_32(utf8.length() + 1); - } - f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); -} - -Error ResourceInteractiveLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) { - - if (error) - return error; - - FileAccessRef wf = FileAccess::open(p_path, FileAccess::WRITE); - if (!wf) { - return ERR_CANT_OPEN; - } - - //save header compressed - static const uint8_t header[4] = { 'R', 'S', 'R', 'C' }; - wf->store_buffer(header, 4); - - wf->store_32(0); //endianness, little endian - wf->store_32(0); //64 bits file, false for now - wf->store_32(VERSION_MAJOR); - wf->store_32(VERSION_MINOR); - static const int save_format_version = 3; //use format version 3 for saving - wf->store_32(save_format_version); - - bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type); - wf->store_64(0); //offset to import metadata, this is no longer used - for (int i = 0; i < 14; i++) - wf->store_32(0); // reserved - - wf->store_32(0); //string table size, will not be in use - size_t ext_res_count_pos = wf->get_position(); - - wf->store_32(0); //zero ext resources, still parsing them - - //go with external resources - - DummyReadData dummy_read; - VariantParser::ResourceParser rp; - rp.ext_func = _parse_ext_resource_dummys; - rp.sub_func = _parse_sub_resource_dummys; - rp.userdata = &dummy_read; - - while (next_tag.name == "ext_resource") { - - if (!next_tag.fields.has("path")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'path' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'id' in external resource tag"; - _printerr(); - return error; - } - - String path = next_tag.fields["path"]; - String type = next_tag.fields["type"]; - int index = next_tag.fields["id"]; - - bs_save_unicode_string(wf.f, type); - bs_save_unicode_string(wf.f, path); - - int lindex = dummy_read.external_resources.size(); - Ref dr; - dr.instance(); - dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external - dummy_read.external_resources[dr] = lindex; - dummy_read.rev_external_resources[index] = dr; - - error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); - - if (error) { - _printerr(); - return error; - } - } - - // save external resource table - wf->seek(ext_res_count_pos); - wf->store_32(dummy_read.external_resources.size()); - wf->seek_end(); - - //now, save resources to a separate file, for now - - size_t sub_res_count_pos = wf->get_position(); - wf->store_32(0); //zero sub resources, still parsing them - - String temp_file = p_path + ".temp"; - FileAccessRef wf2 = FileAccess::open(temp_file, FileAccess::WRITE); - if (!wf2) { - return ERR_CANT_OPEN; - } - - Vector local_offsets; - Vector local_pointers_pos; - - while (next_tag.name == "sub_resource" || next_tag.name == "resource") { - - String type; - int id = -1; - bool main_res; - - if (next_tag.name == "sub_resource") { - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return error; - } - - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'index' in external resource tag"; - _printerr(); - return error; - } - - type = next_tag.fields["type"]; - id = next_tag.fields["id"]; - main_res = false; - } else { - type = res_type; - id = 0; //used for last anyway - main_res = true; - } - - local_offsets.push_back(wf2->get_position()); - - bs_save_unicode_string(wf, "local://" + itos(id)); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset - - bs_save_unicode_string(wf2, type); - size_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); - - int prop_count = 0; - - while (true) { - - String assign; - Variant value; - - error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); - - if (error) { - if (main_res && error == ERR_FILE_EOF) { - next_tag.name = ""; //exit - break; - } - - _printerr(); - return error; - } - - if (assign != String()) { - - Map empty_string_map; //unused - bs_save_unicode_string(wf2, assign, true); - ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map); - prop_count++; - - } else if (next_tag.name != String()) { - - error = OK; - break; - } else { - error = ERR_FILE_CORRUPT; - error_text = "Premature end of file while parsing [sub_resource]"; - _printerr(); - return error; - } - } - - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); - } - - if (next_tag.name == "node") { - //this is a node, must save one more! - - if (!is_scene) { - - error_text += "found the 'node' tag on a resource file!"; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } - - Ref packed_scene = _parse_node_tag(rp); - - if (!packed_scene.is_valid()) - return error; - - error = OK; - //get it here - List props; - packed_scene->get_property_list(&props); - - bs_save_unicode_string(wf, "local://0"); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset - - local_offsets.push_back(wf2->get_position()); - bs_save_unicode_string(wf2, "PackedScene"); - size_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); - - int prop_count = 0; - - for (List::Element *E = props.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) - continue; - - String name = E->get().name; - Variant value = packed_scene->get(name); - - Map empty_string_map; //unused - bs_save_unicode_string(wf2, name, true); - ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map); - prop_count++; - } - - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); - } - - wf2->close(); - - size_t offset_from = wf->get_position(); - wf->seek(sub_res_count_pos); //plus one because the saved one - wf->store_32(local_offsets.size()); - - for (int i = 0; i < local_offsets.size(); i++) { - wf->seek(local_pointers_pos[i]); - wf->store_64(local_offsets[i] + offset_from); - } - - wf->seek_end(); - - Vector data = FileAccess::get_file_as_array(temp_file); - wf->store_buffer(data.ptr(), data.size()); - { - DirAccessRef dar = DirAccess::open(temp_file.get_base_dir()); - dar->remove(temp_file); - } - - wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end - - wf->close(); - - return OK; -} - -String ResourceInteractiveLoaderText::recognize(FileAccess *p_f) { - - error = OK; - - lines = 1; - f = p_f; - - stream.f = f; - - ignore_resource_parsing = true; - - VariantParser::Tag tag; - Error err = VariantParser::parse_tag(&stream, lines, error_text, tag); - - if (err) { - _printerr(); - return ""; - } - - if (tag.fields.has("format")) { - int fmt = tag.fields["format"]; - if (fmt > FORMAT_VERSION) { - error_text = "Saved with newer format version"; - _printerr(); - return ""; - } - } - - if (tag.name == "gd_scene") - return "PackedScene"; - - if (tag.name != "gd_resource") - return ""; - - if (!tag.fields.has("type")) { - error_text = "Missing 'type' field in 'gd_resource' tag"; - _printerr(); - return ""; - } - - return tag.fields["type"]; -} - -///////////////////// - -Ref ResourceFormatLoaderText::load_interactive(const String &p_path, const String &p_original_path, Error *r_error) { - - if (r_error) - *r_error = ERR_CANT_OPEN; - - Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - - if (err != OK) { - - ERR_FAIL_COND_V(err != OK, Ref()); - } - - Ref ria = memnew(ResourceInteractiveLoaderText); - String path = p_original_path != "" ? p_original_path : p_path; - ria->local_path = ProjectSettings::get_singleton()->localize_path(path); - ria->res_path = ria->local_path; - //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); - ria->open(f); - - return ria; -} - -void ResourceFormatLoaderText::get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const { - - if (p_type == "") { - get_recognized_extensions(p_extensions); - return; - } - - if (p_type == "PackedScene") - p_extensions->push_back("tscn"); - else - p_extensions->push_back("tres"); -} - -void ResourceFormatLoaderText::get_recognized_extensions(List *p_extensions) const { - - p_extensions->push_back("tscn"); - p_extensions->push_back("tres"); -} - -bool ResourceFormatLoaderText::handles_type(const String &p_type) const { - - return true; -} -String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { - - String ext = p_path.get_extension().to_lower(); - if (ext == "tscn") - return "PackedScene"; - else if (ext != "tres") - return String(); - - //for anyhting else must test.. - - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { - - return ""; //could not rwead - } - - Ref ria = memnew(ResourceInteractiveLoaderText); - ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); - ria->res_path = ria->local_path; - //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); - String r = ria->recognize(f); - return r; -} - -void ResourceFormatLoaderText::get_dependencies(const String &p_path, List *p_dependencies, bool p_add_types) { - - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { - - ERR_FAIL(); - } - - Ref ria = memnew(ResourceInteractiveLoaderText); - ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); - ria->res_path = ria->local_path; - //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); - ria->get_dependencies(f, p_dependencies, p_add_types); -} - -Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const Map &p_map) { - - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { - - ERR_FAIL_V(ERR_CANT_OPEN); - } - - Ref ria = memnew(ResourceInteractiveLoaderText); - ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); - ria->res_path = ria->local_path; - //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); - return ria->rename_dependencies(f, p_path, p_map); -} - -ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = NULL; - -Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) { - - Error err; - FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err); - - if (err != OK) { - - ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN); - } - - Ref ria = memnew(ResourceInteractiveLoaderText); - String path = p_src_path; - ria->local_path = ProjectSettings::get_singleton()->localize_path(path); - ria->res_path = ria->local_path; - //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) ); - ria->open(f); - return ria->save_as_binary(f, p_dst_path); -} - -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ - -String ResourceFormatSaverTextInstance::_write_resources(void *ud, const RES &p_resource) { - - ResourceFormatSaverTextInstance *rsi = (ResourceFormatSaverTextInstance *)ud; - return rsi->_write_resource(p_resource); -} - -String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { - - if (external_resources.has(res)) { - - return "ExtResource( " + itos(external_resources[res] + 1) + " )"; - } else { - - if (internal_resources.has(res)) { - return "SubResource( " + itos(internal_resources[res]) + " )"; - } else if (res->get_path().length() && res->get_path().find("::") == -1) { - - //external resource - String path = relative_paths ? local_path.path_to_file(res->get_path()) : res->get_path(); - return "Resource( \"" + path + "\" )"; - } else { - ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); - ERR_FAIL_V("null"); - //internal resource - } - } - - return "null"; -} - -void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, bool p_main) { - - switch (p_variant.get_type()) { - case Variant::OBJECT: { - - RES res = p_variant.operator RefPtr(); - - if (res.is_null() || external_resources.has(res)) - return; - - if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { - int index = external_resources.size(); - external_resources[res] = index; - return; - } - - if (resource_set.has(res)) - return; - - List property_list; - - res->get_property_list(&property_list); - property_list.sort(); - - List::Element *I = property_list.front(); - - while (I) { - - PropertyInfo pi = I->get(); - - if (pi.usage & PROPERTY_USAGE_STORAGE) { - - Variant v = res->get(I->get().name); - _find_resources(v); - } - - I = I->next(); - } - - resource_set.insert(res); //saved after, so the children it needs are available when loaded - saved_resources.push_back(res); - - } break; - case Variant::ARRAY: { - - Array varray = p_variant; - int len = varray.size(); - for (int i = 0; i < len; i++) { - - Variant v = varray.get(i); - _find_resources(v); - } - - } break; - case Variant::DICTIONARY: { - - Dictionary d = p_variant; - List keys; - d.get_key_list(&keys); - for (List::Element *E = keys.front(); E; E = E->next()) { - - Variant v = d[E->get()]; - _find_resources(v); - } - } break; - default: {} - } -} - -static String _valprop(const String &p_name) { - - // Escape and quote strings with extended ASCII or further Unicode characters - // as well as '"', '=' or ' ' (32) - const CharType *cstr = p_name.c_str(); - for (int i = 0; cstr[i]; i++) { - if (cstr[i] == '=' || cstr[i] == '"' || cstr[i] < 33 || cstr[i] > 126) { - return "\"" + p_name.c_escape_multiline() + "\""; - } - } - // Keep as is - return p_name; -} - -Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - - if (p_path.ends_with(".tscn")) { - packed_scene = p_resource; - } - - Error err; - f = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V(err, ERR_CANT_OPEN); - FileAccessRef _fref(f); - - local_path = ProjectSettings::get_singleton()->localize_path(p_path); - - relative_paths = p_flags & ResourceSaver::FLAG_RELATIVE_PATHS; - skip_editor = p_flags & ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; - bundle_resources = p_flags & ResourceSaver::FLAG_BUNDLE_RESOURCES; - takeover_paths = p_flags & ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS; - if (!p_path.begins_with("res://")) { - takeover_paths = false; - } - - // save resources - _find_resources(p_resource, true); - - if (packed_scene.is_valid()) { - //add instances to external resources if saving a packed scene - for (int i = 0; i < packed_scene->get_state()->get_node_count(); i++) { - if (packed_scene->get_state()->is_node_instance_placeholder(i)) - continue; - - Ref instance = packed_scene->get_state()->get_node_instance(i); - if (instance.is_valid() && !external_resources.has(instance)) { - int index = external_resources.size(); - external_resources[instance] = index; - } - } - } - - ERR_FAIL_COND_V(err != OK, err); - - { - String title = packed_scene.is_valid() ? "[gd_scene " : "[gd_resource "; - if (packed_scene.is_null()) - title += "type=\"" + p_resource->get_class() + "\" "; - int load_steps = saved_resources.size() + external_resources.size(); - /* - if (packed_scene.is_valid()) { - load_steps+=packed_scene->get_node_count(); - } - //no, better to not use load steps from nodes, no point to that - */ - - if (load_steps > 1) { - title += "load_steps=" + itos(load_steps) + " "; - } - title += "format=" + itos(FORMAT_VERSION) + ""; - - f->store_string(title); - f->store_line("]\n"); //one empty line - } - - Vector sorted_er; - sorted_er.resize(external_resources.size()); - - for (Map::Element *E = external_resources.front(); E; E = E->next()) { - - sorted_er.write[E->get()] = E->key(); - } - - for (int i = 0; i < sorted_er.size(); i++) { - String p = sorted_er[i]->get_path(); - - f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i]->get_save_class() + "\" id=" + itos(i + 1) + "]\n"); //bundled - } - - if (external_resources.size()) - f->store_line(String()); //separate - - Set used_indices; - - for (List::Element *E = saved_resources.front(); E; E = E->next()) { - - RES res = E->get(); - if (E->next() && (res->get_path() == "" || res->get_path().find("::") != -1)) { - - if (res->get_subindex() != 0) { - if (used_indices.has(res->get_subindex())) { - res->set_subindex(0); //repeated - } else { - used_indices.insert(res->get_subindex()); - } - } - } - } - - for (List::Element *E = saved_resources.front(); E; E = E->next()) { - - RES res = E->get(); - ERR_CONTINUE(!resource_set.has(res)); - bool main = (E->next() == NULL); - - if (main && packed_scene.is_valid()) - break; //save as a scene - - if (main) { - f->store_line("[resource]"); - } else { - String line = "[sub_resource "; - if (res->get_subindex() == 0) { - int new_subindex = 1; - if (used_indices.size()) { - new_subindex = used_indices.back()->get() + 1; - } - - res->set_subindex(new_subindex); - used_indices.insert(new_subindex); - } - - int idx = res->get_subindex(); - line += "type=\"" + res->get_class() + "\" id=" + itos(idx); - f->store_line(line + "]"); - if (takeover_paths) { - res->set_path(p_path + "::" + itos(idx), true); - } - - internal_resources[res] = idx; -#ifdef TOOLS_ENABLED - res->set_edited(false); -#endif - } - - List property_list; - res->get_property_list(&property_list); - //property_list.sort(); - for (List::Element *PE = property_list.front(); PE; PE = PE->next()) { - - if (skip_editor && PE->get().name.begins_with("__editor")) - continue; - - if (PE->get().usage & PROPERTY_USAGE_STORAGE) { - - String name = PE->get().name; - Variant value = res->get(name); - Variant default_value = ClassDB::class_get_default_property_value(res->get_class(), name); - - if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { - continue; - } - - if (PE->get().type == Variant::OBJECT && value.is_zero() && !(PE->get().usage & PROPERTY_USAGE_STORE_IF_NULL)) - continue; - - String vars; - VariantWriter::write_to_string(value, vars, _write_resources, this); - f->store_string(_valprop(name) + " = " + vars + "\n"); - } - } - - f->store_string("\n"); - } - - if (packed_scene.is_valid()) { - //if this is a scene, save nodes and connections! - Ref state = packed_scene->get_state(); - for (int i = 0; i < state->get_node_count(); i++) { - - StringName type = state->get_node_type(i); - StringName name = state->get_node_name(i); - int index = state->get_node_index(i); - NodePath path = state->get_node_path(i, true); - NodePath owner = state->get_node_owner_path(i); - Ref instance = state->get_node_instance(i); - String instance_placeholder = state->get_node_instance_placeholder(i); - Vector groups = state->get_node_groups(i); - - String header = "[node"; - header += " name=\"" + String(name) + "\""; - if (type != StringName()) { - header += " type=\"" + String(type) + "\""; - } - if (path != NodePath()) { - header += " parent=\"" + String(path.simplified()) + "\""; - } - if (owner != NodePath() && owner != NodePath(".")) { - header += " owner=\"" + String(owner.simplified()) + "\""; - } - if (index >= 0) { - header += " index=\"" + itos(index) + "\""; - } - - if (groups.size()) { - String sgroups = " groups=[\n"; - for (int j = 0; j < groups.size(); j++) { - sgroups += "\"" + String(groups[j]).c_escape() + "\",\n"; - } - sgroups += "]"; - header += sgroups; - } - - f->store_string(header); - - if (instance_placeholder != String()) { - - String vars; - f->store_string(" instance_placeholder="); - VariantWriter::write_to_string(instance_placeholder, vars, _write_resources, this); - f->store_string(vars); - } - - if (instance.is_valid()) { - - String vars; - f->store_string(" instance="); - VariantWriter::write_to_string(instance, vars, _write_resources, this); - f->store_string(vars); - } - - f->store_line("]"); - - for (int j = 0; j < state->get_node_property_count(i); j++) { - - String vars; - VariantWriter::write_to_string(state->get_node_property_value(i, j), vars, _write_resources, this); - - f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n"); - } - - f->store_line(String()); - } - - for (int i = 0; i < state->get_connection_count(); i++) { - - String connstr = "[connection"; - connstr += " signal=\"" + String(state->get_connection_signal(i)) + "\""; - connstr += " from=\"" + String(state->get_connection_source(i).simplified()) + "\""; - connstr += " to=\"" + String(state->get_connection_target(i).simplified()) + "\""; - connstr += " method=\"" + String(state->get_connection_method(i)) + "\""; - int flags = state->get_connection_flags(i); - if (flags != Object::CONNECT_PERSIST) { - connstr += " flags=" + itos(flags); - } - - Array binds = state->get_connection_binds(i); - f->store_string(connstr); - if (binds.size()) { - String vars; - VariantWriter::write_to_string(binds, vars, _write_resources, this); - f->store_string(" binds= " + vars); - } - - f->store_line("]"); - } - - Vector editable_instances = state->get_editable_instances(); - for (int i = 0; i < editable_instances.size(); i++) { - f->store_line("\n[editable path=\"" + editable_instances[i].operator String() + "\"]"); - } - } - - if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { - f->close(); - return ERR_CANT_CREATE; - } - - f->close(); - //memdelete(f); - - return OK; -} - -Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - - if (p_path.ends_with(".sct") && p_resource->get_class() != "PackedScene") { - return ERR_FILE_UNRECOGNIZED; - } - - ResourceFormatSaverTextInstance saver; - return saver.save(p_path, p_resource, p_flags); -} - -bool ResourceFormatSaverText::recognize(const RES &p_resource) const { - - return true; // all recognized! -} -void ResourceFormatSaverText::get_recognized_extensions(const RES &p_resource, List *p_extensions) const { - - if (p_resource->get_class() == "PackedScene") - p_extensions->push_back("tscn"); //text scene - else - p_extensions->push_back("tres"); //text resource -} - -ResourceFormatSaverText *ResourceFormatSaverText::singleton = NULL; -ResourceFormatSaverText::ResourceFormatSaverText() { - singleton = this; -} diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h deleted file mode 100644 index 8fedbd0dd6..0000000000 --- a/scene/resources/scene_format_text.h +++ /dev/null @@ -1,183 +0,0 @@ -/*************************************************************************/ -/* scene_format_text.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 SCENE_FORMAT_TEXT_H -#define SCENE_FORMAT_TEXT_H - -#include "core/io/resource_loader.h" -#include "core/io/resource_saver.h" -#include "core/os/file_access.h" -#include "core/variant_parser.h" -#include "scene/resources/packed_scene.h" - -class ResourceInteractiveLoaderText : public ResourceInteractiveLoader { - - bool translation_remapped; - String local_path; - String res_path; - String error_text; - - FileAccess *f; - - VariantParser::StreamFile stream; - - struct ExtResource { - String path; - String type; - }; - - bool is_scene; - String res_type; - - bool ignore_resource_parsing; - - //Map remaps; - - Map ext_resources; - - int resources_total; - int resource_current; - String resource_type; - - VariantParser::Tag next_tag; - - mutable int lines; - - Map remaps; - //void _printerr(); - - static Error _parse_sub_resources(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return reinterpret_cast(p_self)->_parse_sub_resource(p_stream, r_res, line, r_err_str); } - static Error _parse_ext_resources(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return reinterpret_cast(p_self)->_parse_ext_resource(p_stream, r_res, line, r_err_str); } - - Error _parse_sub_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); - Error _parse_ext_resource(VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); - - // for converter - class DummyResource : public Resource { - public: - }; - - struct DummyReadData { - - Map external_resources; - Map rev_external_resources; - Set resource_set; - Map resource_map; - }; - - static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } - static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } - - static Error _parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); - static Error _parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref &r_res, int &line, String &r_err_str); - - VariantParser::ResourceParser rp; - - friend class ResourceFormatLoaderText; - - List resource_cache; - Error error; - - RES resource; - - Ref _parse_node_tag(VariantParser::ResourceParser &parser); - -public: - virtual void set_local_path(const String &p_local_path); - virtual Ref get_resource(); - virtual Error poll(); - virtual int get_stage() const; - virtual int get_stage_count() const; - virtual void set_translation_remapped(bool p_remapped); - - void open(FileAccess *p_f, bool p_skip_first_tag = false); - String recognize(FileAccess *p_f); - void get_dependencies(FileAccess *p_f, List *p_dependencies, bool p_add_types); - Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map &p_map); - - Error save_as_binary(FileAccess *p_f, const String &p_path); - ResourceInteractiveLoaderText(); - ~ResourceInteractiveLoaderText(); -}; - -class ResourceFormatLoaderText : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderText, ResourceFormatLoader) -public: - static ResourceFormatLoaderText *singleton; - virtual Ref load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); - virtual void get_recognized_extensions_for_type(const String &p_type, List *p_extensions) const; - virtual void get_recognized_extensions(List *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; - virtual void get_dependencies(const String &p_path, List *p_dependencies, bool p_add_types = false); - virtual Error rename_dependencies(const String &p_path, const Map &p_map); - - static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path); - - ResourceFormatLoaderText() { singleton = this; } -}; - -class ResourceFormatSaverTextInstance { - - String local_path; - - Ref packed_scene; - - bool takeover_paths; - bool relative_paths; - bool bundle_resources; - bool skip_editor; - FileAccess *f; - Set resource_set; - List saved_resources; - Map external_resources; - Map internal_resources; - - void _find_resources(const Variant &p_variant, bool p_main = false); - - static String _write_resources(void *ud, const RES &p_resource); - String _write_resource(const RES &res); - -public: - Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); -}; - -class ResourceFormatSaverText : public ResourceFormatSaver { - GDCLASS(ResourceFormatSaverText, ResourceFormatSaver) -public: - static ResourceFormatSaverText *singleton; - virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - virtual bool recognize(const RES &p_resource) const; - virtual void get_recognized_extensions(const RES &p_resource, List *p_extensions) const; - - ResourceFormatSaverText(); -}; - -#endif // SCENE_FORMAT_TEXT_H diff --git a/scene/resources/shape_line_2d.cpp b/scene/resources/shape_line_2d.cpp deleted file mode 100644 index 4ca535658f..0000000000 --- a/scene/resources/shape_line_2d.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/*************************************************************************/ -/* shape_line_2d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "shape_line_2d.h" -#include "servers/physics_2d_server.h" -#include "servers/visual_server.h" - -bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - - Vector2 point = get_d() * get_normal(); - Vector2 l[2][2] = { { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }, { point, point + get_normal() * 30 } }; - - for (int i = 0; i < 2; i++) { - Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l[i]); - if (p_point.distance_to(closest) < p_tolerance) - return true; - } - - return false; -} - -void LineShape2D::_update_shape() { - - Array arr; - arr.push_back(normal); - arr.push_back(d); - Physics2DServer::get_singleton()->shape_set_data(get_rid(), arr); - emit_changed(); -} - -void LineShape2D::set_normal(const Vector2 &p_normal) { - - normal = p_normal; - _update_shape(); -} - -void LineShape2D::set_d(real_t p_d) { - - d = p_d; - _update_shape(); -} - -Vector2 LineShape2D::get_normal() const { - - return normal; -} -real_t LineShape2D::get_d() const { - - return d; -} - -void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) { - - Vector2 point = get_d() * get_normal(); - - Vector2 l1[2] = { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }; - VS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, 3); - Vector2 l2[2] = { point, point + get_normal() * 30 }; - VS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3); -} -Rect2 LineShape2D::get_rect() const { - - Vector2 point = get_d() * get_normal(); - - Vector2 l1[2] = { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }; - Vector2 l2[2] = { point, point + get_normal() * 30 }; - Rect2 rect; - rect.position = l1[0]; - rect.expand_to(l1[1]); - rect.expand_to(l2[0]); - rect.expand_to(l2[1]); - return rect; -} - -void LineShape2D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_normal", "normal"), &LineShape2D::set_normal); - ClassDB::bind_method(D_METHOD("get_normal"), &LineShape2D::get_normal); - - ClassDB::bind_method(D_METHOD("set_d", "d"), &LineShape2D::set_d); - ClassDB::bind_method(D_METHOD("get_d"), &LineShape2D::get_d); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "d"), "set_d", "get_d"); -} - -LineShape2D::LineShape2D() : - Shape2D(Physics2DServer::get_singleton()->line_shape_create()) { - - normal = Vector2(0, -1); - d = 0; - _update_shape(); -} diff --git a/scene/resources/shape_line_2d.h b/scene/resources/shape_line_2d.h deleted file mode 100644 index dd3dbe3a43..0000000000 --- a/scene/resources/shape_line_2d.h +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************/ -/* shape_line_2d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 SHAPE_LINE_2D_H -#define SHAPE_LINE_2D_H - -#include "scene/resources/shape_2d.h" - -class LineShape2D : public Shape2D { - GDCLASS(LineShape2D, Shape2D); - - Vector2 normal; - real_t d; - - void _update_shape(); - -protected: - static void _bind_methods(); - -public: - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; - - void set_normal(const Vector2 &p_normal); - void set_d(real_t p_d); - - Vector2 get_normal() const; - real_t get_d() const; - - virtual void draw(const RID &p_to_rid, const Color &p_color); - virtual Rect2 get_rect() const; - - LineShape2D(); -}; - -#endif // SHAPE_LINE_2D_H diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp new file mode 100644 index 0000000000..a473fe8b7f --- /dev/null +++ b/scene/resources/sky.cpp @@ -0,0 +1,577 @@ +/*************************************************************************/ +/* sky.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "sky.h" + +#include "core/io/image_loader.h" + +void Sky::set_radiance_size(RadianceSize p_size) { + ERR_FAIL_INDEX(p_size, RADIANCE_SIZE_MAX); + + radiance_size = p_size; + _radiance_changed(); +} + +Sky::RadianceSize Sky::get_radiance_size() const { + + return radiance_size; +} + +void Sky::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_radiance_size", "size"), &Sky::set_radiance_size); + ClassDB::bind_method(D_METHOD("get_radiance_size"), &Sky::get_radiance_size); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "radiance_size", PROPERTY_HINT_ENUM, "32,64,128,256,512,1024,2048"), "set_radiance_size", "get_radiance_size"); + + BIND_ENUM_CONSTANT(RADIANCE_SIZE_32); + BIND_ENUM_CONSTANT(RADIANCE_SIZE_64); + BIND_ENUM_CONSTANT(RADIANCE_SIZE_128); + BIND_ENUM_CONSTANT(RADIANCE_SIZE_256); + BIND_ENUM_CONSTANT(RADIANCE_SIZE_512); + BIND_ENUM_CONSTANT(RADIANCE_SIZE_1024); + BIND_ENUM_CONSTANT(RADIANCE_SIZE_2048); + BIND_ENUM_CONSTANT(RADIANCE_SIZE_MAX); +} + +Sky::Sky() { + radiance_size = RADIANCE_SIZE_512; +} + +///////////////////////////////////////// + +void PanoramaSky::_radiance_changed() { + + if (panorama.is_valid()) { + static const int size[RADIANCE_SIZE_MAX] = { + 32, 64, 128, 256, 512, 1024, 2048 + }; + VS::get_singleton()->sky_set_texture(sky, panorama->get_rid(), size[get_radiance_size()]); + } +} + +void PanoramaSky::set_panorama(const Ref &p_panorama) { + + panorama = p_panorama; + + if (panorama.is_valid()) { + + _radiance_changed(); + + } else { + VS::get_singleton()->sky_set_texture(sky, RID(), 0); + } +} + +Ref PanoramaSky::get_panorama() const { + + return panorama; +} + +RID PanoramaSky::get_rid() const { + + return sky; +} + +void PanoramaSky::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_panorama", "texture"), &PanoramaSky::set_panorama); + ClassDB::bind_method(D_METHOD("get_panorama"), &PanoramaSky::get_panorama); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "panorama", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_panorama", "get_panorama"); +} + +PanoramaSky::PanoramaSky() { + + sky = VS::get_singleton()->sky_create(); +} + +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] = { + 32, 64, 128, 256, 512, 1024, 2048 + }; + VS::get_singleton()->sky_set_texture(sky, texture, size[get_radiance_size()]); +} + +Ref ProceduralSky::_generate_sky() { + + update_queued = false; + + PoolVector imgdata; + + static const int size[TEXTURE_SIZE_MAX] = { + 256, 512, 1024, 2048, 4096 + }; + + int w = size[texture_size]; + int h = w / 2; + + imgdata.resize(w * h * 4); //RGBE + + { + PoolVector::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(CLAMP(normal.y, -1.0, 1.0)); + + 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)); + color.r *= ground_energy; + color.g *= ground_energy; + color.b *= ground_energy; + } 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)); + color.r *= sky_energy; + color.g *= sky_energy; + color.b *= sky_energy; + + float sun_angle = Math::rad2deg(Math::acos(CLAMP(sun.dot(normal), -1.0, 1.0))); + + 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.instance(); + image->create(w, h, false, Image::FORMAT_RGBE9995, imgdata); + + return image; +} + +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::_update_sky() { + + bool use_thread = true; + if (first_time) { + use_thread = false; + first_time = false; + } +#ifdef NO_THREADS + use_thread = false; +#endif + if (use_thread) { + + if (!sky_thread) { + sky_thread = Thread::create(_thread_function, this); + regen_queued = false; + } else { + regen_queued = true; + } + + } else { + Ref image = _generate_sky(); + VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); + VS::get_singleton()->texture_set_data(texture, image); + _radiance_changed(); + } +} + +void ProceduralSky::_queue_update() { + + if (update_queued) + return; + + update_queued = true; + call_deferred("_update_sky"); +} + +void ProceduralSky::_thread_done(const Ref &p_image) { + + VS::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); + VS::get_singleton()->texture_set_data(texture, p_image); + _radiance_changed(); + Thread::wait_to_finish(sky_thread); + memdelete(sky_thread); + sky_thread = NULL; + if (regen_queued) { + sky_thread = Thread::create(_thread_function, this); + regen_queued = false; + } +} + +void ProceduralSky::_thread_function(void *p_ud) { + + ProceduralSky *psky = (ProceduralSky *)p_ud; + psky->call_deferred("_thread_done", psky->_generate_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); + + ClassDB::bind_method(D_METHOD("_thread_done", "image"), &ProceduralSky::_thread_done); + + 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, "256,512,1024,2048,4096"), "set_texture_size", "get_texture_size"); + + BIND_ENUM_CONSTANT(TEXTURE_SIZE_256); + BIND_ENUM_CONSTANT(TEXTURE_SIZE_512); + BIND_ENUM_CONSTANT(TEXTURE_SIZE_1024); + BIND_ENUM_CONSTANT(TEXTURE_SIZE_2048); + BIND_ENUM_CONSTANT(TEXTURE_SIZE_4096); + BIND_ENUM_CONSTANT(TEXTURE_SIZE_MAX); +} + +ProceduralSky::ProceduralSky() { + + sky = VS::get_singleton()->sky_create(); + texture = VS::get_singleton()->texture_create(); + + update_queued = false; + sky_top_color = Color::hex(0xa5d6f1ff); + sky_horizon_color = Color::hex(0xd6eafaff); + sky_curve = 0.09; + sky_energy = 1; + + ground_bottom_color = Color::hex(0x282f36ff); + ground_horizon_color = Color::hex(0x6c655fff); + ground_curve = 0.02; + 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; + sky_thread = NULL; + regen_queued = false; + first_time = true; + + _queue_update(); +} + +ProceduralSky::~ProceduralSky() { + + if (sky_thread) { + Thread::wait_to_finish(sky_thread); + memdelete(sky_thread); + sky_thread = NULL; + } + VS::get_singleton()->free(sky); + VS::get_singleton()->free(texture); +} diff --git a/scene/resources/sky.h b/scene/resources/sky.h new file mode 100644 index 0000000000..3b410396e0 --- /dev/null +++ b/scene/resources/sky.h @@ -0,0 +1,200 @@ +/*************************************************************************/ +/* sky.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 SKY_H +#define SKY_H + +#include "core/os/thread.h" +#include "scene/resources/texture.h" + +class Sky : public Resource { + GDCLASS(Sky, Resource); + +public: + enum RadianceSize { + RADIANCE_SIZE_32, + RADIANCE_SIZE_64, + RADIANCE_SIZE_128, + RADIANCE_SIZE_256, + RADIANCE_SIZE_512, + RADIANCE_SIZE_1024, + RADIANCE_SIZE_2048, + RADIANCE_SIZE_MAX + }; + +private: + RadianceSize radiance_size; + +protected: + static void _bind_methods(); + virtual void _radiance_changed() = 0; + +public: + void set_radiance_size(RadianceSize p_size); + RadianceSize get_radiance_size() const; + Sky(); +}; + +VARIANT_ENUM_CAST(Sky::RadianceSize) + +class PanoramaSky : public Sky { + GDCLASS(PanoramaSky, Sky); + +private: + RID sky; + Ref panorama; + +protected: + static void _bind_methods(); + virtual void _radiance_changed(); + +public: + void set_panorama(const Ref &p_panorama); + Ref get_panorama() const; + + virtual RID get_rid() const; + + PanoramaSky(); + ~PanoramaSky(); +}; + +class ProceduralSky : public Sky { + GDCLASS(ProceduralSky, Sky); + +public: + enum TextureSize { + TEXTURE_SIZE_256, + TEXTURE_SIZE_512, + TEXTURE_SIZE_1024, + TEXTURE_SIZE_2048, + TEXTURE_SIZE_4096, + TEXTURE_SIZE_MAX + }; + +private: + Thread *sky_thread; + 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; + bool regen_queued; + + bool first_time; + + void _thread_done(const Ref &p_image); + static void _thread_function(void *p_ud); + +protected: + static void _bind_methods(); + virtual void _radiance_changed(); + + Ref _generate_sky(); + 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 diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp deleted file mode 100644 index d9da85310e..0000000000 --- a/scene/resources/sky_box.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/*************************************************************************/ -/* sky_box.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "sky_box.h" -#include "core/io/image_loader.h" - -void Sky::set_radiance_size(RadianceSize p_size) { - ERR_FAIL_INDEX(p_size, RADIANCE_SIZE_MAX); - - radiance_size = p_size; - _radiance_changed(); -} - -Sky::RadianceSize Sky::get_radiance_size() const { - - return radiance_size; -} - -void Sky::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_radiance_size", "size"), &Sky::set_radiance_size); - ClassDB::bind_method(D_METHOD("get_radiance_size"), &Sky::get_radiance_size); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "radiance_size", PROPERTY_HINT_ENUM, "32,64,128,256,512,1024,2048"), "set_radiance_size", "get_radiance_size"); - - BIND_ENUM_CONSTANT(RADIANCE_SIZE_32); - BIND_ENUM_CONSTANT(RADIANCE_SIZE_64); - BIND_ENUM_CONSTANT(RADIANCE_SIZE_128); - BIND_ENUM_CONSTANT(RADIANCE_SIZE_256); - BIND_ENUM_CONSTANT(RADIANCE_SIZE_512); - BIND_ENUM_CONSTANT(RADIANCE_SIZE_1024); - BIND_ENUM_CONSTANT(RADIANCE_SIZE_2048); - BIND_ENUM_CONSTANT(RADIANCE_SIZE_MAX); -} - -Sky::Sky() { - radiance_size = RADIANCE_SIZE_512; -} - -///////////////////////////////////////// - -void PanoramaSky::_radiance_changed() { - - if (panorama.is_valid()) { - static const int size[RADIANCE_SIZE_MAX] = { - 32, 64, 128, 256, 512, 1024, 2048 - }; - VS::get_singleton()->sky_set_texture(sky, panorama->get_rid(), size[get_radiance_size()]); - } -} - -void PanoramaSky::set_panorama(const Ref &p_panorama) { - - panorama = p_panorama; - - if (panorama.is_valid()) { - - _radiance_changed(); - - } else { - VS::get_singleton()->sky_set_texture(sky, RID(), 0); - } -} - -Ref PanoramaSky::get_panorama() const { - - return panorama; -} - -RID PanoramaSky::get_rid() const { - - return sky; -} - -void PanoramaSky::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_panorama", "texture"), &PanoramaSky::set_panorama); - ClassDB::bind_method(D_METHOD("get_panorama"), &PanoramaSky::get_panorama); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "panorama", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_panorama", "get_panorama"); -} - -PanoramaSky::PanoramaSky() { - - sky = VS::get_singleton()->sky_create(); -} - -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] = { - 32, 64, 128, 256, 512, 1024, 2048 - }; - VS::get_singleton()->sky_set_texture(sky, texture, size[get_radiance_size()]); -} - -Ref ProceduralSky::_generate_sky() { - - update_queued = false; - - PoolVector imgdata; - - static const int size[TEXTURE_SIZE_MAX] = { - 256, 512, 1024, 2048, 4096 - }; - - int w = size[texture_size]; - int h = w / 2; - - imgdata.resize(w * h * 4); //RGBE - - { - PoolVector::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(CLAMP(normal.y, -1.0, 1.0)); - - 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)); - color.r *= ground_energy; - color.g *= ground_energy; - color.b *= ground_energy; - } 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)); - color.r *= sky_energy; - color.g *= sky_energy; - color.b *= sky_energy; - - float sun_angle = Math::rad2deg(Math::acos(CLAMP(sun.dot(normal), -1.0, 1.0))); - - 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.instance(); - image->create(w, h, false, Image::FORMAT_RGBE9995, imgdata); - - return image; -} - -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::_update_sky() { - - bool use_thread = true; - if (first_time) { - use_thread = false; - first_time = false; - } -#ifdef NO_THREADS - use_thread = false; -#endif - if (use_thread) { - - if (!sky_thread) { - sky_thread = Thread::create(_thread_function, this); - regen_queued = false; - } else { - regen_queued = true; - } - - } else { - Ref image = _generate_sky(); - VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); - VS::get_singleton()->texture_set_data(texture, image); - _radiance_changed(); - } -} - -void ProceduralSky::_queue_update() { - - if (update_queued) - return; - - update_queued = true; - call_deferred("_update_sky"); -} - -void ProceduralSky::_thread_done(const Ref &p_image) { - - VS::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); - VS::get_singleton()->texture_set_data(texture, p_image); - _radiance_changed(); - Thread::wait_to_finish(sky_thread); - memdelete(sky_thread); - sky_thread = NULL; - if (regen_queued) { - sky_thread = Thread::create(_thread_function, this); - regen_queued = false; - } -} - -void ProceduralSky::_thread_function(void *p_ud) { - - ProceduralSky *psky = (ProceduralSky *)p_ud; - psky->call_deferred("_thread_done", psky->_generate_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); - - ClassDB::bind_method(D_METHOD("_thread_done", "image"), &ProceduralSky::_thread_done); - - 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, "256,512,1024,2048,4096"), "set_texture_size", "get_texture_size"); - - BIND_ENUM_CONSTANT(TEXTURE_SIZE_256); - BIND_ENUM_CONSTANT(TEXTURE_SIZE_512); - BIND_ENUM_CONSTANT(TEXTURE_SIZE_1024); - BIND_ENUM_CONSTANT(TEXTURE_SIZE_2048); - BIND_ENUM_CONSTANT(TEXTURE_SIZE_4096); - BIND_ENUM_CONSTANT(TEXTURE_SIZE_MAX); -} - -ProceduralSky::ProceduralSky() { - - sky = VS::get_singleton()->sky_create(); - texture = VS::get_singleton()->texture_create(); - - update_queued = false; - sky_top_color = Color::hex(0xa5d6f1ff); - sky_horizon_color = Color::hex(0xd6eafaff); - sky_curve = 0.09; - sky_energy = 1; - - ground_bottom_color = Color::hex(0x282f36ff); - ground_horizon_color = Color::hex(0x6c655fff); - ground_curve = 0.02; - 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; - sky_thread = NULL; - regen_queued = false; - first_time = true; - - _queue_update(); -} - -ProceduralSky::~ProceduralSky() { - - if (sky_thread) { - Thread::wait_to_finish(sky_thread); - memdelete(sky_thread); - sky_thread = NULL; - } - VS::get_singleton()->free(sky); - VS::get_singleton()->free(texture); -} diff --git a/scene/resources/sky_box.h b/scene/resources/sky_box.h deleted file mode 100644 index af10803b36..0000000000 --- a/scene/resources/sky_box.h +++ /dev/null @@ -1,199 +0,0 @@ -/*************************************************************************/ -/* sky_box.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 SKY_BOX_H -#define SKY_BOX_H - -#include "core/os/thread.h" -#include "scene/resources/texture.h" -class Sky : public Resource { - GDCLASS(Sky, Resource); - -public: - enum RadianceSize { - RADIANCE_SIZE_32, - RADIANCE_SIZE_64, - RADIANCE_SIZE_128, - RADIANCE_SIZE_256, - RADIANCE_SIZE_512, - RADIANCE_SIZE_1024, - RADIANCE_SIZE_2048, - RADIANCE_SIZE_MAX - }; - -private: - RadianceSize radiance_size; - -protected: - static void _bind_methods(); - virtual void _radiance_changed() = 0; - -public: - void set_radiance_size(RadianceSize p_size); - RadianceSize get_radiance_size() const; - Sky(); -}; - -VARIANT_ENUM_CAST(Sky::RadianceSize) - -class PanoramaSky : public Sky { - GDCLASS(PanoramaSky, Sky); - -private: - RID sky; - Ref panorama; - -protected: - static void _bind_methods(); - virtual void _radiance_changed(); - -public: - void set_panorama(const Ref &p_panorama); - Ref get_panorama() const; - - virtual RID get_rid() const; - - PanoramaSky(); - ~PanoramaSky(); -}; - -class ProceduralSky : public Sky { - GDCLASS(ProceduralSky, Sky); - -public: - enum TextureSize { - TEXTURE_SIZE_256, - TEXTURE_SIZE_512, - TEXTURE_SIZE_1024, - TEXTURE_SIZE_2048, - TEXTURE_SIZE_4096, - TEXTURE_SIZE_MAX - }; - -private: - Thread *sky_thread; - 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; - bool regen_queued; - - bool first_time; - - void _thread_done(const Ref &p_image); - static void _thread_function(void *p_ud); - -protected: - static void _bind_methods(); - virtual void _radiance_changed(); - - Ref _generate_sky(); - 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_BOX_H diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index ff5900cd3e..b60ec9a80d 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -34,7 +34,7 @@ #include "core/io/image_loader.h" #include "core/method_bind_ext.gen.inc" #include "core/os/os.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" Size2 Texture::get_size() const { diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 4b5b504510..bfea0f9300 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -37,8 +37,8 @@ #include "core/os/rw_lock.h" #include "core/os/thread_safe.h" #include "core/resource.h" -#include "scene/resources/color_ramp.h" #include "scene/resources/curve.h" +#include "scene/resources/gradient.h" #include "servers/visual_server.h" /** -- cgit v1.2.3