portal.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /**************************************************************************/
  2. /* portal.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "portal.h"
  31. #include "core/engine.h"
  32. #include "mesh_instance.h"
  33. #include "room.h"
  34. #include "room_group.h"
  35. #include "room_manager.h"
  36. #include "scene/main/viewport.h"
  37. #include "servers/visual_server.h"
  38. bool Portal::_portal_plane_convention = false;
  39. bool Portal::_settings_gizmo_show_margins = true;
  40. Portal::Portal() {
  41. clear();
  42. _settings_active = true;
  43. _settings_two_way = true;
  44. _settings_include_in_bound = false;
  45. _internal = false;
  46. _linkedroom_ID[0] = -1;
  47. _linkedroom_ID[1] = -1;
  48. _pts_world.clear();
  49. _pts_local.clear();
  50. _pts_local_raw.resize(0);
  51. _pt_center_world = Vector3();
  52. _plane = Plane();
  53. _margin = 1.0;
  54. _use_default_margin = true;
  55. // the visual server portal lifetime is linked to the lifetime of this object
  56. _portal_rid = RID_PRIME(VisualServer::get_singleton()->portal_create());
  57. #ifdef TOOLS_ENABLED
  58. _room_manager_godot_ID = 0;
  59. #endif
  60. // portals are defined COUNTER clockwise,
  61. // because they point OUTWARD from the room in the direction
  62. // of the normal
  63. PoolVector<Vector2> points;
  64. points.resize(4);
  65. points.set(0, Vector2(1, -1));
  66. points.set(1, Vector2(1, 1));
  67. points.set(2, Vector2(-1, 1));
  68. points.set(3, Vector2(-1, -1));
  69. set_points(points); // default shape
  70. }
  71. Portal::~Portal() {
  72. if (_portal_rid != RID()) {
  73. VisualServer::get_singleton()->free(_portal_rid);
  74. }
  75. }
  76. String Portal::get_configuration_warning() const {
  77. String warning = Spatial::get_configuration_warning();
  78. auto lambda = [](const Node *p_node) {
  79. return static_cast<bool>((Object::cast_to<RoomManager>(p_node) || Object::cast_to<Room>(p_node) || Object::cast_to<RoomGroup>(p_node)));
  80. };
  81. if (Room::detect_nodes_using_lambda(this, lambda)) {
  82. if (Room::detect_nodes_of_type<RoomManager>(this)) {
  83. if (!warning.empty()) {
  84. warning += "\n\n";
  85. }
  86. warning += TTR("The RoomManager should not be a child or grandchild of a Portal.");
  87. }
  88. if (Room::detect_nodes_of_type<Room>(this)) {
  89. if (!warning.empty()) {
  90. warning += "\n\n";
  91. }
  92. warning += TTR("A Room should not be a child or grandchild of a Portal.");
  93. }
  94. if (Room::detect_nodes_of_type<RoomGroup>(this)) {
  95. if (!warning.empty()) {
  96. warning += "\n\n";
  97. }
  98. warning += TTR("A RoomGroup should not be a child or grandchild of a Portal.");
  99. }
  100. }
  101. return warning;
  102. }
  103. void Portal::set_point(int p_idx, const Vector2 &p_point) {
  104. ERR_FAIL_INDEX_MSG(p_idx, _pts_local_raw.size(), "Index out of bounds. Call set_points() to set the size of the array.");
  105. _pts_local_raw.set(p_idx, p_point);
  106. _sanitize_points();
  107. update_gizmo();
  108. }
  109. void Portal::set_points(const PoolVector<Vector2> &p_points) {
  110. _pts_local_raw = p_points;
  111. _sanitize_points();
  112. if (is_inside_tree()) {
  113. portal_update();
  114. update_gizmo();
  115. }
  116. }
  117. PoolVector<Vector2> Portal::get_points() const {
  118. return _pts_local_raw;
  119. }
  120. // extra editor links to the room manager to allow unloading
  121. // on change, or re-converting
  122. void Portal::_changed() {
  123. #ifdef TOOLS_ENABLED
  124. RoomManager *rm = RoomManager::active_room_manager;
  125. if (!rm) {
  126. return;
  127. }
  128. rm->_rooms_changed("changed Portal " + get_name());
  129. #endif
  130. }
  131. void Portal::clear() {
  132. _internal = false;
  133. _linkedroom_ID[0] = -1;
  134. _linkedroom_ID[1] = -1;
  135. _importing_portal = false;
  136. }
  137. void Portal::_notification(int p_what) {
  138. switch (p_what) {
  139. case NOTIFICATION_ENTER_WORLD: {
  140. ERR_FAIL_COND(get_world().is_null());
  141. // Defer full creation of the visual server portal to when the editor portal is in the scene tree.
  142. VisualServer::get_singleton()->portal_set_scenario(_portal_rid, get_world()->get_scenario());
  143. // Update any components in visual server that require the scenario to be set.
  144. VisualServer::get_singleton()->portal_set_active(_portal_rid, _settings_active);
  145. // We can't calculate world points until we have entered the tree.
  146. portal_update();
  147. update_gizmo();
  148. } break;
  149. case NOTIFICATION_EXIT_WORLD: {
  150. // partially destroy the visual server portal when the editor portal exits the scene tree
  151. VisualServer::get_singleton()->portal_set_scenario(_portal_rid, RID());
  152. } break;
  153. case NOTIFICATION_TRANSFORM_CHANGED: {
  154. // keep the world points and the visual server up to date
  155. portal_update();
  156. // In theory we shouldn't need to update the gizmo when the transform
  157. // changes .. HOWEVER, the portal margin is displayed in world space units,
  158. // back transformed to model space.
  159. // If the Z scale is changed by the user, the portal margin length can become incorrect
  160. // and needs 'resyncing' to the global scale of the portal node.
  161. // We really only need to do this when Z scale is changed, but it is easier codewise
  162. // to always change it, unless we have evidence this is a performance problem.
  163. update_gizmo();
  164. } break;
  165. }
  166. }
  167. void Portal::set_portal_active(bool p_active) {
  168. _settings_active = p_active;
  169. // This can be called prior to entering the tree when loading packed scene,
  170. // where the scenario has not yet been set (and thus the visual server portal
  171. // is not yet fully created).
  172. // We therefore defer setting this until entering the tree.
  173. if (is_inside_tree()) {
  174. VisualServer::get_singleton()->portal_set_active(_portal_rid, p_active);
  175. }
  176. }
  177. bool Portal::get_portal_active() const {
  178. return _settings_active;
  179. }
  180. void Portal::set_use_default_margin(bool p_use) {
  181. _use_default_margin = p_use;
  182. update_gizmo();
  183. }
  184. bool Portal::get_use_default_margin() const {
  185. return _use_default_margin;
  186. }
  187. void Portal::set_portal_margin(real_t p_margin) {
  188. _margin = p_margin;
  189. if (!_use_default_margin) {
  190. // give visual feedback in the editor for the portal margin zone
  191. update_gizmo();
  192. }
  193. }
  194. real_t Portal::get_portal_margin() const {
  195. return _margin;
  196. }
  197. void Portal::resolve_links(const LocalVector<Room *, int32_t> &p_rooms, const RID &p_from_room_rid) {
  198. Room *linkedroom = nullptr;
  199. if (has_node(_settings_path_linkedroom)) {
  200. linkedroom = Object::cast_to<Room>(get_node(_settings_path_linkedroom));
  201. // only allow linking to rooms that are part of the roomlist
  202. // (already recognised).
  203. // If we don't check this, it will start trying to link to Room nodes that are invalid,
  204. // and crash.
  205. if (linkedroom && (p_rooms.find(linkedroom) == -1)) {
  206. // invalid room
  207. WARN_PRINT("Portal attempting to link to Room outside the roomlist : " + linkedroom->get_name());
  208. linkedroom = nullptr;
  209. }
  210. // this should not happen, but just in case
  211. if (linkedroom && (linkedroom->_room_ID >= p_rooms.size())) {
  212. WARN_PRINT("Portal attempting to link to invalid Room : " + linkedroom->get_name());
  213. linkedroom = nullptr;
  214. }
  215. }
  216. if (linkedroom) {
  217. _linkedroom_ID[1] = linkedroom->_room_ID;
  218. // send to visual server
  219. VisualServer::get_singleton()->portal_link(_portal_rid, p_from_room_rid, linkedroom->_room_rid, _settings_two_way);
  220. } else {
  221. _linkedroom_ID[1] = -1;
  222. }
  223. }
  224. void Portal::set_linked_room_internal(const NodePath &link_path) {
  225. _settings_path_linkedroom = link_path;
  226. }
  227. bool Portal::try_set_unique_name(const String &p_name) {
  228. SceneTree *scene_tree = get_tree();
  229. if (!scene_tree) {
  230. // should not happen in the editor
  231. return false;
  232. }
  233. Viewport *root = scene_tree->get_root();
  234. if (!root) {
  235. return false;
  236. }
  237. Node *found = root->find_node(p_name, true, false);
  238. // if the name does not already exist in the scene tree, we can use it
  239. if (!found) {
  240. set_name(p_name);
  241. return true;
  242. }
  243. // we are trying to set the same name this node already has...
  244. if (found == this) {
  245. // noop
  246. return true;
  247. }
  248. return false;
  249. }
  250. void Portal::set_linked_room(const NodePath &link_path) {
  251. _settings_path_linkedroom = link_path;
  252. // see if the link looks legit
  253. Room *linkedroom = nullptr;
  254. if (has_node(link_path)) {
  255. linkedroom = Object::cast_to<Room>(get_node(link_path));
  256. if (linkedroom) {
  257. if (linkedroom != get_parent()) {
  258. // was ok
  259. } else {
  260. WARN_PRINT("Linked room cannot be the parent room of a portal.");
  261. }
  262. } else {
  263. WARN_PRINT("Linked room path is not a room.");
  264. }
  265. }
  266. _changed();
  267. }
  268. NodePath Portal::get_linked_room() const {
  269. return _settings_path_linkedroom;
  270. }
  271. void Portal::flip() {
  272. // flip portal
  273. Transform tr = get_transform();
  274. Basis flip_basis = Basis(Vector3(0, Math_PI, 0));
  275. tr.basis *= flip_basis;
  276. set_transform(tr);
  277. _pts_local.clear();
  278. _pts_world.clear();
  279. // flip the raw verts
  280. Vector<Vector2> raw;
  281. raw.resize(_pts_local_raw.size());
  282. for (int n = 0; n < _pts_local_raw.size(); n++) {
  283. const Vector2 &pt = _pts_local_raw[n];
  284. raw.set(n, Vector2(-pt.x, pt.y));
  285. }
  286. // standardize raw verts winding
  287. Geometry::sort_polygon_winding(raw, false);
  288. for (int n = 0; n < raw.size(); n++) {
  289. _pts_local_raw.set(n, raw[n]);
  290. }
  291. _sanitize_points();
  292. portal_update();
  293. update_gizmo();
  294. }
  295. bool Portal::create_from_mesh_instance(const MeshInstance *p_mi) {
  296. ERR_FAIL_COND_V(!p_mi, false);
  297. _pts_local.clear();
  298. _pts_world.clear();
  299. Ref<Mesh> rmesh = p_mi->get_mesh();
  300. ERR_FAIL_COND_V(!rmesh.is_valid(), false);
  301. if (rmesh->get_surface_count() == 0) {
  302. WARN_PRINT(vformat("Portal '%s' has no surfaces, ignoring", get_name()));
  303. return false;
  304. }
  305. Array arrays = rmesh->surface_get_arrays(0);
  306. PoolVector<Vector3> vertices = arrays[VS::ARRAY_VERTEX];
  307. PoolVector<int> indices = arrays[VS::ARRAY_INDEX];
  308. // get the model space verts and find center
  309. int num_source_points = vertices.size();
  310. ERR_FAIL_COND_V(num_source_points < 3, false);
  311. const Transform &tr_source = p_mi->get_global_transform();
  312. Vector<Vector3> pts_world;
  313. for (int n = 0; n < num_source_points; n++) {
  314. Vector3 pt = tr_source.xform(vertices[n]);
  315. // test for duplicates.
  316. // Some geometry may contain duplicate verts in portals
  317. // which will muck up the winding etc...
  318. bool duplicate = false;
  319. for (int m = 0; m < pts_world.size(); m++) {
  320. Vector3 diff = pt - pts_world[m];
  321. // hopefully this epsilon will do in nearly all cases
  322. if (diff.length() < 0.001) {
  323. duplicate = true;
  324. break;
  325. }
  326. }
  327. if (!duplicate) {
  328. pts_world.push_back(pt);
  329. }
  330. }
  331. ERR_FAIL_COND_V(pts_world.size() < 3, false);
  332. // create the normal from 3 vertices .. either indexed, or use the first 3
  333. Vector3 three_pts[3];
  334. if (indices.size() >= 3) {
  335. for (int n = 0; n < 3; n++) {
  336. ERR_FAIL_COND_V(indices[n] >= num_source_points, false);
  337. three_pts[n] = tr_source.xform(vertices[indices[n]]);
  338. }
  339. } else {
  340. for (int n = 0; n < 3; n++) {
  341. three_pts[n] = pts_world[n];
  342. }
  343. }
  344. Vector3 normal = Plane(three_pts[0], three_pts[1], three_pts[2]).normal;
  345. if (_portal_plane_convention) {
  346. normal = -normal;
  347. }
  348. // get the verts sorted with winding, assume that the triangle initial winding
  349. // tells us the normal and hence which way the world space portal should be facing
  350. _sort_verts_clockwise(normal, pts_world);
  351. // back calculate the plane from *all* the portal points, this will give us a nice average plane
  352. // (in case of wonky portals where artwork isn't bang on)
  353. _plane = _plane_from_points_newell(pts_world);
  354. // change the portal transform to match our plane and the center of the portal
  355. Transform tr_global;
  356. // prevent warnings when poly normal matches the up vector
  357. Vector3 up(0, 1, 0);
  358. if (Math::abs(_plane.normal.dot(up)) > 0.9) {
  359. up = Vector3(1, 0, 0);
  360. }
  361. tr_global.set_look_at(Vector3(0, 0, 0), _plane.normal, up);
  362. tr_global.origin = _pt_center_world;
  363. // We can't directly set this global transform on the portal, because the parent node may already
  364. // have a transform applied, so we need to account for this and give a corrected local transform
  365. // for the portal, such that the end result global transform will be correct.
  366. // find the difference between this new global transform and the transform of the parent
  367. // then use this for the new local transform of the portal
  368. Spatial *parent = Object::cast_to<Spatial>(get_parent());
  369. ERR_FAIL_COND_V(!parent, false);
  370. Transform tr_inverse_parent = parent->get_global_transform().affine_inverse();
  371. Transform new_local_transform = tr_inverse_parent * tr_global;
  372. set_transform(new_local_transform);
  373. // now back calculate the local space coords of the portal from the world space coords.
  374. // The local space will be used in future for editing and as a 'master' store of the verts.
  375. _pts_local_raw.resize(pts_world.size());
  376. // back transform from global space to local space
  377. Transform tr = tr_global.affine_inverse();
  378. for (int n = 0; n < pts_world.size(); n++) {
  379. // pt3 is now in local space
  380. Vector3 pt3 = tr.xform(pts_world[n]);
  381. // only the x and y required
  382. _pts_local_raw.set(n, Vector2(pt3.x, pt3.y));
  383. // The z coordinate should be approx zero
  384. // DEV_ASSERT(Math::abs(pt3.z) < 0.1);
  385. }
  386. _sanitize_points();
  387. portal_update();
  388. return true;
  389. }
  390. void Portal::_update_aabb() {
  391. _aabb_local = AABB();
  392. if (_pts_local.size()) {
  393. Vector3 begin = _vec2to3(_pts_local[0]);
  394. Vector3 end = begin;
  395. for (int n = 1; n < _pts_local.size(); n++) {
  396. Vector3 pt = _vec2to3(_pts_local[n]);
  397. if (pt.x < begin.x) {
  398. begin.x = pt.x;
  399. }
  400. if (pt.y < begin.y) {
  401. begin.y = pt.y;
  402. }
  403. if (pt.z < begin.z) {
  404. begin.z = pt.z;
  405. }
  406. if (pt.x > end.x) {
  407. end.x = pt.x;
  408. }
  409. if (pt.y > end.y) {
  410. end.y = pt.y;
  411. }
  412. if (pt.z > end.z) {
  413. end.z = pt.z;
  414. }
  415. }
  416. _aabb_local.position = begin;
  417. _aabb_local.size = end - begin;
  418. }
  419. }
  420. void Portal::portal_update() {
  421. // first calculate the plane from the transform
  422. // (portals are standardized outward from source room once sanitized,
  423. // irrespective of the user portal plane convention)
  424. const Transform &tr = get_global_transform();
  425. _plane = Plane(0.0, 0.0, -1.0, 0.0);
  426. _plane = tr.xform(_plane);
  427. // after becoming a portal, the centre world IS the transform origin
  428. _pt_center_world = tr.origin;
  429. // recalculates world points from the local space
  430. int num_points = _pts_local.size();
  431. if (_pts_world.size() != num_points) {
  432. _pts_world.resize(num_points);
  433. }
  434. for (int n = 0; n < num_points; n++) {
  435. _pts_world.set(n, tr.xform(_vec2to3(_pts_local[n])));
  436. }
  437. // no need to check winding order, the points are pre-sanitized only when they change
  438. // extension margin to prevent objects too easily sprawling
  439. real_t margin = get_active_portal_margin();
  440. VisualServer::get_singleton()->portal_set_geometry(_portal_rid, _pts_world, margin);
  441. }
  442. real_t Portal::get_active_portal_margin() const {
  443. if (_use_default_margin) {
  444. return RoomManager::_get_default_portal_margin();
  445. }
  446. return _margin;
  447. }
  448. void Portal::_sanitize_points() {
  449. // remove duplicates? NYI maybe not necessary
  450. Vector<Vector2> raw;
  451. raw.resize(_pts_local_raw.size());
  452. for (int n = 0; n < _pts_local_raw.size(); n++) {
  453. raw.set(n, _pts_local_raw[n]);
  454. }
  455. // this function may get rid of some concave points due to user editing ..
  456. // may not be necessary, no idea how fast it is
  457. _pts_local = Geometry::convex_hull_2d(raw);
  458. // some peculiarity of convex_hull_2d function, it duplicates the last point for some reason
  459. if (_pts_local.size() > 1) {
  460. _pts_local.resize(_pts_local.size() - 1);
  461. }
  462. // sort winding, the system expects counter clockwise polys
  463. Geometry::sort_polygon_winding(_pts_local, false);
  464. // a bit of a bodge, but a small epsilon pulling in the portal edges towards the center
  465. // can hide walls in the opposite room that abutt the portal (due to floating point error)
  466. // find 2d center
  467. Vector2 center;
  468. for (int n = 0; n < _pts_local.size(); n++) {
  469. center += _pts_local[n];
  470. }
  471. center /= _pts_local.size();
  472. const real_t pull_in = 0.0001;
  473. for (int n = 0; n < _pts_local.size(); n++) {
  474. Vector2 offset = _pts_local[n] - center;
  475. real_t l = offset.length();
  476. // don't apply the pull in for tiny holes
  477. if (l > (pull_in * 2.0)) {
  478. real_t fract = (l - pull_in) / l;
  479. offset *= fract;
  480. _pts_local.set(n, center + offset);
  481. }
  482. }
  483. _update_aabb();
  484. }
  485. void Portal::_sort_verts_clockwise(const Vector3 &p_portal_normal, Vector<Vector3> &r_verts) {
  486. // cannot sort less than 3 verts
  487. if (r_verts.size() < 3) {
  488. return;
  489. }
  490. // find centroid
  491. int num_points = r_verts.size();
  492. _pt_center_world = Vector3(0, 0, 0);
  493. for (int n = 0; n < num_points; n++) {
  494. _pt_center_world += r_verts[n];
  495. }
  496. _pt_center_world /= num_points;
  497. /////////////////////////////////////////
  498. // now algorithm
  499. for (int n = 0; n < num_points - 2; n++) {
  500. Vector3 a = r_verts[n] - _pt_center_world;
  501. a.normalize();
  502. Plane p = Plane(r_verts[n], _pt_center_world, _pt_center_world + p_portal_normal);
  503. double smallest_angle = -1;
  504. int smallest = -1;
  505. for (int m = n + 1; m < num_points; m++) {
  506. if (p.distance_to(r_verts[m]) > 0.0) {
  507. Vector3 b = r_verts[m] - _pt_center_world;
  508. b.normalize();
  509. double angle = a.dot(b);
  510. if (angle > smallest_angle) {
  511. smallest_angle = angle;
  512. smallest = m;
  513. }
  514. } // which side
  515. } // for m
  516. // swap smallest and n+1 vert
  517. if (smallest != -1) {
  518. Vector3 temp = r_verts[smallest];
  519. r_verts.set(smallest, r_verts[n + 1]);
  520. r_verts.set(n + 1, temp);
  521. }
  522. } // for n
  523. // the vertices are now sorted, but may be in the opposite order to that wanted.
  524. // we detect this by calculating the normal of the poly, then flipping the order if the normal is pointing
  525. // the wrong way.
  526. Plane plane = Plane(r_verts[0], r_verts[1], r_verts[2]);
  527. if (p_portal_normal.dot(plane.normal) < 0.0) {
  528. // reverse winding order of verts
  529. r_verts.invert();
  530. }
  531. }
  532. Plane Portal::_plane_from_points_newell(const Vector<Vector3> &p_pts) {
  533. int num_points = p_pts.size();
  534. if (num_points < 3) {
  535. return Plane();
  536. }
  537. Vector3 normal;
  538. Vector3 center;
  539. for (int i = 0; i < num_points; i++) {
  540. int j = (i + 1) % num_points;
  541. const Vector3 &pi = p_pts[i];
  542. const Vector3 &pj = p_pts[j];
  543. center += pi;
  544. normal.x += (((pi.z) + (pj.z)) * ((pj.y) - (pi.y)));
  545. normal.y += (((pi.x) + (pj.x)) * ((pj.z) - (pi.z)));
  546. normal.z += (((pi.y) + (pj.y)) * ((pj.x) - (pi.x)));
  547. }
  548. normal.normalize();
  549. center /= num_points;
  550. _pt_center_world = center;
  551. // point and normal
  552. return Plane(center, normal);
  553. }
  554. void Portal::_bind_methods() {
  555. ClassDB::bind_method(D_METHOD("set_portal_active", "p_active"), &Portal::set_portal_active);
  556. ClassDB::bind_method(D_METHOD("get_portal_active"), &Portal::get_portal_active);
  557. ClassDB::bind_method(D_METHOD("set_two_way", "p_two_way"), &Portal::set_two_way);
  558. ClassDB::bind_method(D_METHOD("is_two_way"), &Portal::is_two_way);
  559. ClassDB::bind_method(D_METHOD("set_use_default_margin", "p_use"), &Portal::set_use_default_margin);
  560. ClassDB::bind_method(D_METHOD("get_use_default_margin"), &Portal::get_use_default_margin);
  561. ClassDB::bind_method(D_METHOD("set_portal_margin", "p_margin"), &Portal::set_portal_margin);
  562. ClassDB::bind_method(D_METHOD("get_portal_margin"), &Portal::get_portal_margin);
  563. ClassDB::bind_method(D_METHOD("set_linked_room", "p_room"), &Portal::set_linked_room);
  564. ClassDB::bind_method(D_METHOD("get_linked_room"), &Portal::get_linked_room);
  565. ClassDB::bind_method(D_METHOD("set_points", "points"), &Portal::set_points);
  566. ClassDB::bind_method(D_METHOD("get_points"), &Portal::get_points);
  567. ClassDB::bind_method(D_METHOD("set_point", "index", "position"), &Portal::set_point);
  568. ClassDB::bind_method(D_METHOD("set_include_in_bound", "p_enabled"), &Portal::set_include_in_bound);
  569. ClassDB::bind_method(D_METHOD("get_include_in_bound"), &Portal::get_include_in_bound);
  570. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "portal_active"), "set_portal_active", "get_portal_active");
  571. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "two_way"), "set_two_way", "is_two_way");
  572. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_in_bound"), "set_include_in_bound", "get_include_in_bound");
  573. ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "linked_room", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Room"), "set_linked_room", "get_linked_room");
  574. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_default_margin"), "set_use_default_margin", "get_use_default_margin");
  575. ADD_PROPERTY(PropertyInfo(Variant::REAL, "portal_margin", PROPERTY_HINT_RANGE, "0.0,10.0,0.01"), "set_portal_margin", "get_portal_margin");
  576. ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
  577. }