diff options
Diffstat (limited to 'modules')
202 files changed, 8060 insertions, 1617 deletions
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index 648919e612..bfb452d109 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -68,7 +68,9 @@ AreaBullet::AreaBullet() : } AreaBullet::~AreaBullet() { - remove_all_overlapping_instantly(); + // signal are handled by godot, so just clear without notify + for (int i = overlappingObjects.size() - 1; 0 <= i; --i) + overlappingObjects[i].object->on_exit_area(this); } void AreaBullet::dispatch_callbacks() { @@ -121,23 +123,21 @@ void AreaBullet::scratch() { isScratched = true; } -void AreaBullet::remove_all_overlapping_instantly() { - CollisionObjectBullet *supportObject; +void AreaBullet::clear_overlaps(bool p_notify) { for (int i = overlappingObjects.size() - 1; 0 <= i; --i) { - supportObject = overlappingObjects[i].object; - call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED); - supportObject->on_exit_area(this); + if (p_notify) + call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED); + overlappingObjects[i].object->on_exit_area(this); } overlappingObjects.clear(); } -void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object) { - CollisionObjectBullet *supportObject; +void AreaBullet::remove_overlap(CollisionObjectBullet *p_object, bool p_notify) { for (int i = overlappingObjects.size() - 1; 0 <= i; --i) { - supportObject = overlappingObjects[i].object; - if (supportObject == p_object) { - call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED); - supportObject->on_exit_area(this); + if (overlappingObjects[i].object == p_object) { + if (p_notify) + call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED); + overlappingObjects[i].object->on_exit_area(this); overlappingObjects.remove(i); break; } diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index 78136d574b..b2046c684e 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -150,9 +150,9 @@ public: void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant()); void scratch(); - void remove_all_overlapping_instantly(); + void clear_overlaps(bool p_notify); // Dispatch the callbacks and removes from overlapping list - void remove_overlapping_instantly(CollisionObjectBullet *p_object); + void remove_overlap(CollisionObjectBullet *p_object, bool p_notify); virtual void on_collision_filters_change(); virtual void on_collision_checker_start() {} diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 595e4951a2..6246a295ec 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -89,7 +89,9 @@ BulletPhysicsServer::BulletPhysicsServer() : active(true), active_spaces_count(0) {} -BulletPhysicsServer::~BulletPhysicsServer() {} +BulletPhysicsServer::~BulletPhysicsServer() { + bulletdelete(emptyShape); +} RID BulletPhysicsServer::shape_create(ShapeType p_shape) { ShapeBullet *shape = NULL; @@ -619,11 +621,11 @@ uint32_t BulletPhysicsServer::body_get_collision_mask(RID p_body) const { } void BulletPhysicsServer::body_set_user_flags(RID p_body, uint32_t p_flags) { - WARN_PRINT("This function si not currently supported by bullet and Godot"); + // This function si not currently supported } uint32_t BulletPhysicsServer::body_get_user_flags(RID p_body) const { - WARN_PRINT("This function si not currently supported by bullet and Godot"); + // This function si not currently supported return 0; } @@ -783,22 +785,27 @@ int BulletPhysicsServer::body_get_max_contacts_reported(RID p_body) const { return body->get_max_collisions_detection(); } -void BulletPhysicsServer::body_set_contacts_reported_depth_threshold(RID p_body, float p_treshold) { - WARN_PRINT("Not supported by bullet and even Godot"); +void BulletPhysicsServer::body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold) { + // Not supported by bullet and even Godot } float BulletPhysicsServer::body_get_contacts_reported_depth_threshold(RID p_body) const { - WARN_PRINT("Not supported by bullet and even Godot"); + // Not supported by bullet and even Godot return 0.; } void BulletPhysicsServer::body_set_omit_force_integration(RID p_body, bool p_omit) { - WARN_PRINT("Not supported by bullet"); + RigidBodyBullet *body = rigid_body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->set_omit_forces_integration(p_omit); } bool BulletPhysicsServer::body_is_omitting_force_integration(RID p_body) const { - WARN_PRINT("Not supported by bullet"); - return false; + RigidBodyBullet *body = rigid_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, false); + + return body->get_omit_forces_integration(); } void BulletPhysicsServer::body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata) { @@ -979,11 +986,11 @@ PhysicsServer::JointType BulletPhysicsServer::joint_get_type(RID p_joint) const } void BulletPhysicsServer::joint_set_solver_priority(RID p_joint, int p_priority) { - //WARN_PRINTS("Joint priority not supported by bullet"); + // Joint priority not supported by bullet } int BulletPhysicsServer::joint_get_solver_priority(RID p_joint) const { - //WARN_PRINTS("Joint priority not supported by bullet"); + // Joint priority not supported by bullet return 0; } diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 885d58d98d..e931915bba 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -239,7 +239,7 @@ public: virtual void body_set_max_contacts_reported(RID p_body, int p_contacts); virtual int body_get_max_contacts_reported(RID p_body) const; - virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_treshold); + virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold); virtual float body_get_contacts_reported_depth_threshold(RID p_body) const; virtual void body_set_omit_force_integration(RID p_body, bool p_omit); diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index 34aff68a4a..57e4db708e 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -49,7 +49,7 @@ CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {} void CollisionObjectBullet::ShapeWrapper::set_transform(const Transform &p_transform) { - G_TO_B(p_transform.get_basis().get_scale(), scale); + G_TO_B(p_transform.get_basis().get_scale_abs(), scale); G_TO_B(p_transform, transform); UNSCALE_BT_BASIS(transform); } @@ -68,12 +68,10 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) : force_shape_reset(false) {} CollisionObjectBullet::~CollisionObjectBullet() { - // Remove all overlapping + // Remove all overlapping, notify is not required since godot take care of it for (int i = areasOverlapped.size() - 1; 0 <= i; --i) { - areasOverlapped[i]->remove_overlapping_instantly(this); + areasOverlapped[i]->remove_overlap(this, /*Notify*/ false); } - // not required - // areasOverlapped.clear(); destroyBulletCollisionObject(); } @@ -160,7 +158,7 @@ int CollisionObjectBullet::get_godot_object_flags() const { void CollisionObjectBullet::set_transform(const Transform &p_global_transform) { - set_body_scale(p_global_transform.basis.get_scale()); + set_body_scale(p_global_transform.basis.get_scale_abs()); btTransform bt_transform; G_TO_B(p_global_transform, bt_transform); diff --git a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml index 8c8647e097..a4dc61d0bc 100644 --- a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml +++ b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.1-dev"> +<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/bullet/doc_classes/BulletPhysicsServer.xml b/modules/bullet/doc_classes/BulletPhysicsServer.xml index 8ed84e1dec..1486936cf4 100644 --- a/modules/bullet/doc_classes/BulletPhysicsServer.xml +++ b/modules/bullet/doc_classes/BulletPhysicsServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.1-dev"> +<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/bullet/godot_motion_state.h b/modules/bullet/godot_motion_state.h index fe5d8418b7..fa58e86589 100644 --- a/modules/bullet/godot_motion_state.h +++ b/modules/bullet/godot_motion_state.h @@ -87,7 +87,7 @@ public: public: /// Use this function to move kinematic body - /// -- or set initial transfom before body creation. + /// -- or set initial transform before body creation. void moveBody(const btTransform &newWorldTransform) { bodyKinematicWorldTransf = newWorldTransform; } diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 7c051f8f17..197550d686 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -63,6 +63,9 @@ bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) co } bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { + if (count >= m_resultMax) + return false; + const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask); if (needs) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); @@ -70,6 +73,7 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con if (m_exclude->has(gObj->get_self())) { return false; } + return true; } else { return false; @@ -87,7 +91,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id); ++count; - return count < m_resultMax; + return 1; // not used by bullet } bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { @@ -106,7 +110,7 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox if (gObj->getType() == CollisionObjectBullet::TYPE_AREA) return false; - if (m_self_object->has_collision_exception(gObj)) + if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object)) return false; } return true; @@ -168,10 +172,7 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con result.shape = cp.m_index0; } - if (colObj) - result.collider_id = colObj->get_instance_id(); - else - result.collider_id = 0; + result.collider_id = colObj->get_instance_id(); result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id); result.rid = colObj->get_self(); ++m_count; @@ -181,6 +182,9 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con } bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { + if (m_count >= m_resultMax) + return false; + const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask); if (needs) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); @@ -206,7 +210,7 @@ btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint ++m_count; - return m_count < m_resultMax; + return 1; // Not used by bullet } bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { @@ -243,32 +247,23 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp m_rest_info_collision_object = colObj0Wrap->getCollisionObject(); } - if (colObj) - m_result->collider_id = colObj->get_instance_id(); - else - m_result->collider_id = 0; + m_result->collider_id = colObj->get_instance_id(); m_result->rid = colObj->get_self(); m_collided = true; } - return cp.getDistance(); + return 1; // Not used by bullet } void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth) { - if (depth < 0) { - // Has penetration - if (m_most_penetrated_distance > depth) { + if (m_penetration_distance > depth) { // Has penetration? - bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject(); - - m_most_penetrated_distance = depth; - m_pointCollisionObject = (isSwapped ? m_body0Wrap : m_body1Wrap)->getCollisionObject(); - m_other_compound_shape_index = isSwapped ? m_index1 : m_index0; - m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld; - m_pointWorld = isSwapped ? (pointInWorldOnB + normalOnBInWorld * depth) : pointInWorldOnB; - m_penetration_distance = depth; - } + bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject(); + m_penetration_distance = depth; + m_other_compound_shape_index = isSwapped ? m_index0 : m_index1; + m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld; + m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB; } } diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index ed6d4b7d6d..363051f24c 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -185,24 +185,18 @@ struct GodotDeepPenetrationContactResultCallback : public btManifoldResult { btVector3 m_pointWorld; btScalar m_penetration_distance; int m_other_compound_shape_index; - const btCollisionObject *m_pointCollisionObject; - - btScalar m_most_penetrated_distance; GodotDeepPenetrationContactResultCallback(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap) : btManifoldResult(body0Wrap, body1Wrap), - m_pointCollisionObject(NULL), m_penetration_distance(0), - m_other_compound_shape_index(0), - m_most_penetrated_distance(1e20) {} + m_other_compound_shape_index(0) {} void reset() { - m_pointCollisionObject = NULL; - m_most_penetrated_distance = 1e20; + m_penetration_distance = 0; } bool hasHit() { - return m_pointCollisionObject; + return m_penetration_distance < 0; } virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth); diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 75b4cc054a..2494063c22 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -255,6 +255,7 @@ RigidBodyBullet::RigidBodyBullet() : linearDamp(0), angularDamp(0), can_sleep(true), + omit_forces_integration(false), force_integration_callback(NULL), isTransformChanged(false), previousActiveState(true), @@ -334,6 +335,9 @@ void RigidBodyBullet::dispatch_callbacks() { /// The check isTransformChanged is necessary in order to call integrated forces only when the first transform is sent if ((btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && isTransformChanged) { + if (omit_forces_integration) + btBody->clearForces(); + BulletPhysicsDirectBodyState *bodyDirect = BulletPhysicsDirectBodyState::get_singleton(this); Variant variantBodyDirect = bodyDirect; @@ -437,6 +441,10 @@ bool RigidBodyBullet::is_active() const { return btBody->isActive(); } +void RigidBodyBullet::set_omit_forces_integration(bool p_omit) { + omit_forces_integration = p_omit; +} + void RigidBodyBullet::set_param(PhysicsServer::BodyParameter p_param, real_t p_value) { switch (p_param) { case PhysicsServer::BODY_PARAM_BOUNCE: diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index 2d529f6dc7..b9511243c7 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -198,6 +198,7 @@ private: real_t linearDamp; real_t angularDamp; bool can_sleep; + bool omit_forces_integration; Vector<CollisionData> collisions; // these parameters are used to avoid vector resize @@ -254,6 +255,9 @@ public: void set_activation_state(bool p_active); bool is_active() const; + void set_omit_forces_integration(bool p_omit); + _FORCE_INLINE_ bool get_omit_forces_integration() const { return omit_forces_integration; } + void set_param(PhysicsServer::BodyParameter p_param, real_t); real_t get_param(PhysicsServer::BodyParameter p_param) const; diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 5d8d391bd9..76d9614465 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -125,14 +125,13 @@ btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMes } } -btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) { +btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) { const btScalar ignoredHeightScale(1); - const btScalar fieldHeight(500); // Meters const int YAxis = 1; // 0=X, 1=Y, 2=Z const bool flipQuadEdges = false; const void *heightsPtr = p_heights.read().ptr(); - return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges)); + return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges)); } btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) { @@ -337,10 +336,10 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) { int src_face_count = faces.size(); if (0 < src_face_count) { - btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh); - // It counts the faces and assert the array contains the correct number of vertices. ERR_FAIL_COND(src_face_count % 3); + + btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh); src_face_count /= 3; PoolVector<Vector3>::Read r = p_faces.read(); const Vector3 *facesr = r.ptr(); @@ -387,19 +386,44 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) { Dictionary d = p_data; ERR_FAIL_COND(!d.has("width")); ERR_FAIL_COND(!d.has("depth")); - ERR_FAIL_COND(!d.has("cell_size")); ERR_FAIL_COND(!d.has("heights")); + real_t l_min_height = 0.0; + real_t l_max_height = 0.0; + + // If specified, min and max height will be used as precomputed values + if (d.has("min_height")) + l_min_height = d["min_height"]; + if (d.has("max_height")) + l_max_height = d["max_height"]; + + ERR_FAIL_COND(l_min_height > l_max_height); + int l_width = d["width"]; int l_depth = d["depth"]; - real_t l_cell_size = d["cell_size"]; PoolVector<real_t> l_heights = d["heights"]; ERR_FAIL_COND(l_width <= 0); ERR_FAIL_COND(l_depth <= 0); - ERR_FAIL_COND(l_cell_size <= CMP_EPSILON); - ERR_FAIL_COND(l_heights.size() != (width * depth)); - setup(heights, width, depth, cell_size); + ERR_FAIL_COND(l_heights.size() != (l_width * l_depth)); + + // Compute min and max heights if not specified. + if (!d.has("min_height") && !d.has("max_height")) { + + PoolVector<real_t>::Read r = heights.read(); + int heights_size = heights.size(); + + for (int i = 0; i < heights_size; ++i) { + real_t h = r[i]; + + if (h < l_min_height) + l_min_height = h; + else if (h > l_max_height) + l_max_height = h; + } + } + + setup(l_heights, l_width, l_depth, l_min_height, l_max_height); } Variant HeightMapShapeBullet::get_data() const { @@ -410,8 +434,14 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const { return PhysicsServer::SHAPE_HEIGHTMAP; } -void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) { +void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) { + // TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes + { // Copy + + // TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields + // without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data). + const int heights_size = p_heights.size(); heights.resize(heights_size); PoolVector<real_t>::Read p_heights_r = p_heights.read(); @@ -420,14 +450,16 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int heights_w[i] = p_heights_r[i]; } } + width = p_width; depth = p_depth; - cell_size = p_cell_size; + min_height = p_min_height; + max_height = p_max_height; notifyShapeChanged(); } btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) { - btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, cell_size)); + btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height)); cs->setLocalScaling(p_implicit_scale); prepare(cs); cs->setMargin(p_margin); diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 2acba90e36..abeea0f9ce 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -85,7 +85,7 @@ public: /// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface(); static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1)); static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1)); - static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size); + static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height); static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope); }; @@ -199,7 +199,8 @@ public: PoolVector<real_t> heights; int width; int depth; - real_t cell_size; + real_t min_height; + real_t max_height; HeightMapShapeBullet(); @@ -209,7 +210,7 @@ public: virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: - void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size); + void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height); }; class RayShapeBullet : public ShapeBullet { diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 56441f7ef2..3a1f5d78dd 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -122,7 +122,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); - btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin); + btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin); if (!btShape->isConvex()) { bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); @@ -202,7 +202,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform & ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); - btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale(), p_margin); + btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin); if (!btShape->isConvex()) { bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); @@ -234,7 +234,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); - btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale(), p_margin); + btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin); if (!btShape->isConvex()) { bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); @@ -628,7 +628,7 @@ void SpaceBullet::destroy_world() { void SpaceBullet::check_ghost_overlaps() { - /// Algorith support variables + /// Algorithm support variables btConvexShape *other_body_shape; btConvexShape *area_shape; btGjkPairDetector::ClosestPointInput gjk_input; @@ -660,7 +660,10 @@ void SpaceBullet::check_ghost_overlaps() { // For each overlapping for (i = ghostOverlaps.size() - 1; 0 <= i; --i) { - if (!(ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_RIGID_BODY || ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA)) + if (ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA) { + if (!static_cast<AreaBullet *>(ghostOverlaps[i]->getUserPointer())->is_monitorable()) + continue; + } else if (ghostOverlaps[i]->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY) continue; otherObject = static_cast<RigidCollisionObjectBullet *>(ghostOverlaps[i]->getUserPointer()); @@ -790,7 +793,7 @@ void SpaceBullet::update_gravity() { /// I'm leaving this here just for future tests. /// Debug motion and normal vector drawing #define debug_test_motion 0 -#define PERFORM_INITIAL_UNSTACK 0 + #define RECOVERING_MOVEMENT_SCALE 0.4 #define RECOVERING_MOVEMENT_CYCLES 4 @@ -842,7 +845,6 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f G_TO_B(p_from, body_safe_position); UNSCALE_BT_BASIS(body_safe_position); -#if PERFORM_INITIAL_UNSTACK btVector3 recover_initial_position(0, 0, 0); { /// Phase one - multi shapes depenetration using margin for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { @@ -854,7 +856,6 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f // Add recover movement in order to make it safe body_safe_position.getOrigin() += recover_initial_position; } -#endif btVector3 motion; G_TO_B(p_motion, motion); @@ -893,7 +894,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f btResult.m_collisionFilterGroup = p_body->get_collision_layer(); btResult.m_collisionFilterMask = p_body->get_collision_mask(); - dynamicsWorld->convexSweepTest(convex_shape_test, shape_world_from, shape_world_to, btResult, 0.002); + dynamicsWorld->convexSweepTest(convex_shape_test, shape_world_from, shape_world_to, btResult, dynamicsWorld->getDispatchInfo().m_allowedCcdPenetration); if (btResult.hasHit()) { /// Since for each sweep test I fix the motion of new shapes in base the recover result, @@ -918,11 +919,8 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f l_has_penetration = recover_from_penetration(p_body, body_safe_position, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, delta_recover_movement, &r_recover_result); if (r_result) { -#if PERFORM_INITIAL_UNSTACK B_TO_G(motion + delta_recover_movement + recover_initial_position, r_result->motion); -#else - B_TO_G(motion + delta_recover_movement, r_result->motion); -#endif + if (l_has_penetration) { has_penetration = true; if (l_penetration_distance <= r_recover_result.penetration_distance) { @@ -998,7 +996,7 @@ public: } void reset() { - result_collision_objects.empty(); + result_collision_objects.clear(); } }; @@ -1033,7 +1031,10 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) { btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i]; - if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()) || (p_infinite_inertia && !otherObject->isStaticOrKinematicObject())) + if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) { + otherObject->activate(); // Force activation of hitten rigid, soft body + continue; + } else if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object())) continue; if (otherObject->getCollisionShape()->isCompound()) { @@ -1112,7 +1113,7 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC btCollisionObjectWrapper obA(NULL, p_shapeA, p_objectA, tA, -1, p_shapeId_A); btCollisionObjectWrapper obB(NULL, p_shapeB, p_objectB, p_transformB, -1, p_shapeId_B); - btCollisionAlgorithm *algorithm = dispatcher->findAlgorithm(&obA, &obB, NULL, BT_CLOSEST_POINT_ALGORITHMS); + btCollisionAlgorithm *algorithm = dispatcher->findAlgorithm(&obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS); if (algorithm) { GodotDeepPenetrationContactResultCallback contactPointResult(&obA, &obB); //discrete collision detection query diff --git a/modules/csg/SCsub b/modules/csg/SCsub new file mode 100644 index 0000000000..57c504efd8 --- /dev/null +++ b/modules/csg/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_csg = env_modules.Clone() + +# Godot's own source files +env_csg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/csg/config.py b/modules/csg/config.py new file mode 100644 index 0000000000..5f133eba90 --- /dev/null +++ b/modules/csg/config.py @@ -0,0 +1,5 @@ +def can_build(platform): + return True + +def configure(env): + pass diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp new file mode 100644 index 0000000000..c1fe11d6aa --- /dev/null +++ b/modules/csg/csg.cpp @@ -0,0 +1,1488 @@ +#include "csg.h" +#include "face3.h" +#include "geometry.h" +#include "os/os.h" +#include "sort.h" +#include "thirdparty/misc/triangulator.h" + +void CSGBrush::clear() { + faces.clear(); +} + +void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces) { + + clear(); + + int vc = p_vertices.size(); + + ERR_FAIL_COND((vc % 3) != 0) + + PoolVector<Vector3>::Read rv = p_vertices.read(); + int uvc = p_uvs.size(); + PoolVector<Vector2>::Read ruv = p_uvs.read(); + int sc = p_smooth.size(); + PoolVector<bool>::Read rs = p_smooth.read(); + int mc = p_materials.size(); + PoolVector<Ref<Material> >::Read rm = p_materials.read(); + int ic = p_invert_faces.size(); + PoolVector<bool>::Read ri = p_invert_faces.read(); + + Map<Ref<Material>, int> material_map; + + faces.resize(p_vertices.size() / 3); + + for (int i = 0; i < faces.size(); i++) { + Face &f = faces[i]; + f.vertices[0] = rv[i * 3 + 0]; + f.vertices[1] = rv[i * 3 + 1]; + f.vertices[2] = rv[i * 3 + 2]; + if (uvc == vc) { + f.uvs[0] = ruv[i * 3 + 0]; + f.uvs[1] = ruv[i * 3 + 1]; + f.uvs[2] = ruv[i * 3 + 2]; + } + if (sc == vc / 3) { + f.smooth = rs[i]; + } else { + f.smooth = false; + } + + if (ic == vc / 3) { + f.invert = ri[i]; + } else { + f.invert = false; + } + + if (mc == vc / 3) { + Ref<Material> mat = rm[i]; + if (mat.is_valid()) { + const Map<Ref<Material>, int>::Element *E = material_map.find(mat); + if (E) { + f.material = E->get(); + } else { + f.material = material_map.size(); + material_map[mat] = f.material; + } + } else { + f.material = -1; + } + } + } + + materials.resize(material_map.size()); + for (Map<Ref<Material>, int>::Element *E = material_map.front(); E; E = E->next()) { + materials[E->get()] = E->key(); + } + + _regen_face_aabbs(); +} + +void CSGBrush::_regen_face_aabbs() { + + for (int i = 0; i < faces.size(); i++) { + + faces[i].aabb.position = faces[i].vertices[0]; + faces[i].aabb.expand_to(faces[i].vertices[1]); + faces[i].aabb.expand_to(faces[i].vertices[2]); + faces[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros + } +} + +void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) { + + faces = p_brush.faces; + materials = p_brush.materials; + + for (int i = 0; i < faces.size(); i++) { + for (int j = 0; j < 3; j++) { + faces[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]); + } + } + + _regen_face_aabbs(); +} + +//////////////////////// + +void CSGBrushOperation::BuildPoly::create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) { + + //creates the initial face that will be used for clipping against the other faces + + Vector3 va[3] = { + p_brush->faces[p_face].vertices[0], + p_brush->faces[p_face].vertices[1], + p_brush->faces[p_face].vertices[2], + }; + + plane = Plane(va[0], va[1], va[2]); + + to_world.origin = va[0]; + + to_world.basis.set_axis(2, plane.normal); + to_world.basis.set_axis(0, (va[1] - va[2]).normalized()); + to_world.basis.set_axis(1, to_world.basis.get_axis(0).cross(to_world.basis.get_axis(2)).normalized()); + + to_poly = to_world.affine_inverse(); + + face_index = p_face; + + for (int i = 0; i < 3; i++) { + + Point p; + Vector3 localp = to_poly.xform(va[i]); + p.point.x = localp.x; + p.point.y = localp.y; + p.uv = p_brush->faces[p_face].uvs[i]; + + points.push_back(p); + + ///edge + + Edge e; + e.points[0] = i; + e.points[1] = (i + 1) % 3; + e.outer = true; + edges.push_back(e); + } + + smooth = p_brush->faces[p_face].smooth; + invert = p_brush->faces[p_face].invert; + + if (p_brush->faces[p_face].material != -1) { + material = p_brush->materials[p_brush->faces[p_face].material]; + } + + base_edges = 3; +} + +static Vector2 interpolate_uv(const Vector2 &p_vertex_a, const Vector2 &p_vertex_b, const Vector2 &p_vertex_c, const Vector2 &p_uv_a, const Vector2 &p_uv_c) { + + float len_a_c = (p_vertex_c - p_vertex_a).length(); + if (len_a_c < CMP_EPSILON) { + return p_uv_a; + } + + float len_a_b = (p_vertex_b - p_vertex_a).length(); + + float c = len_a_b / len_a_c; + + return p_uv_a.linear_interpolate(p_uv_c, c); +} + +static Vector2 interpolate_triangle_uv(const Vector2 &p_pos, const Vector2 *p_vtx, const Vector2 *p_uv) { + + if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) { + return p_uv[0]; + } + if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) { + return p_uv[1]; + } + if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) { + return p_uv[2]; + } + + Vector2 v0 = p_vtx[1] - p_vtx[0]; + Vector2 v1 = p_vtx[2] - p_vtx[0]; + Vector2 v2 = p_pos - p_vtx[0]; + + float d00 = v0.dot(v0); + float d01 = v0.dot(v1); + float d11 = v1.dot(v1); + float d20 = v2.dot(v0); + float d21 = v2.dot(v1); + float denom = (d00 * d11 - d01 * d01); + if (denom == 0) { + return p_uv[0]; + } + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + + return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w; +} + +void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B) { + + //keep track of what was inserted + Vector<int> inserted_points; + + //keep track of point indices for what was inserted, allowing reuse of points. + int segment_idx[2] = { -1, -1 }; + + //check if edge and poly share a vertex, of so, assign it to segment_idx + for (int i = 0; i < points.size(); i++) { + for (int j = 0; j < 2; j++) { + if (segment[j].distance_to(points[i].point) < CMP_EPSILON) { + segment_idx[j] = i; + inserted_points.push_back(i); + break; + } + } + } + + //check if both segment points are shared with other vertices + if (segment_idx[0] != -1 && segment_idx[1] != -1) { + + if (segment_idx[0] == segment_idx[1]) { + return; //segment was too tiny, both mapped to same point + } + + bool found = false; + + //check if the segment already exists + for (int i = 0; i < edges.size(); i++) { + + if ( + (edges[i].points[0] == segment_idx[0] && edges[i].points[1] == segment_idx[1]) || + (edges[i].points[0] == segment_idx[1] && edges[i].points[1] == segment_idx[0])) { + found = true; + break; + } + } + + if (found) { + //it does already exist, do nothing + return; + } + + //directly add the new segment + Edge new_edge; + new_edge.points[0] = segment_idx[0]; + new_edge.points[1] = segment_idx[1]; + edges.push_back(new_edge); + return; + } + + //check edge by edge against the segment points to see if intersects + + for (int i = 0; i < base_edges; i++) { + + //if a point is shared with one of the edge points, then this edge must not be tested, as it will result in a numerical precision error. + bool edge_valid = true; + for (int j = 0; j < 2; j++) { + + if (edges[i].points[0] == segment_idx[0] || edges[i].points[1] == segment_idx[1] || edges[i].points[0] == segment_idx[1] || edges[i].points[1] == segment_idx[0]) { + edge_valid = false; //segment has this point, cant check against this + break; + } + } + + if (!edge_valid) //already hit a point in this edge, so dont test it + continue; + + //see if either points are within the edge isntead of crossing it + Vector2 res; + bool found = false; + int assign_segment_id = -1; + + for (int j = 0; j < 2; j++) { + + Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point }; + Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg); + + if (closest.distance_to(segment[j]) < CMP_EPSILON) { + //point rest of this edge + res = closest; + found = true; + assign_segment_id = j; + } + } + + //test if the point crosses the edge + if (!found && Geometry::segment_intersects_segment_2d(segment[0], segment[1], points[edges[i].points[0]].point, points[edges[i].points[1]].point, &res)) { + //point does cross the edge + found = true; + } + + //check whether an intersection against the segment happened + if (found) { + + //It did! so first, must slice the segment + Point new_point; + new_point.point = res; + //make sure to interpolate UV too + new_point.uv = interpolate_uv(points[edges[i].points[0]].point, new_point.point, points[edges[i].points[1]].point, points[edges[i].points[0]].uv, points[edges[i].points[1]].uv); + + int point_idx = points.size(); + points.push_back(new_point); + + //split the edge in 2 + Edge new_edge; + new_edge.points[0] = edges[i].points[0]; + new_edge.points[1] = point_idx; + new_edge.outer = edges[i].outer; + edges[i].points[0] = point_idx; + edges.insert(i, new_edge); + i++; //skip newly inserted edge + base_edges++; //will need an extra one in the base triangle + if (assign_segment_id >= 0) { + //point did split a segment, so make sure to remember this + segment_idx[assign_segment_id] = point_idx; + } + inserted_points.push_back(point_idx); + } + } + + //final step: after cutting the original triangle, try to see if we can still insert + //this segment + + //if already inserted two points, just use them for a segment + + if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error + //two points were inserted, create the new edge + Edge new_edge; + new_edge.points[0] = inserted_points[0]; + new_edge.points[1] = inserted_points[1]; + edges.push_back(new_edge); + return; + } + + // One or no points were inserted (besides splitting), so try to see if extra points can be placed inside the triangle. + // This needs to be done here, after the previous tests were exhausted + for (int i = 0; i < 2; i++) { + + if (segment_idx[i] != -1) + continue; //already assigned to something, so skip + + //check whether one of the segment endpoints is inside the triangle. If it is, this points needs to be inserted + if (Geometry::is_point_in_triangle(segment[i], points[0].point, points[1].point, points[2].point)) { + + Point new_point; + new_point.point = segment[i]; + + Vector2 point3[3] = { points[0].point, points[1].point, points[2].point }; + Vector2 uv3[3] = { points[0].uv, points[1].uv, points[2].uv }; + + new_point.uv = interpolate_triangle_uv(new_point.point, point3, uv3); + + int point_idx = points.size(); + points.push_back(new_point); + inserted_points.push_back(point_idx); + } + } + + //check again whether two points were inserted, if so then create the new edge + if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error + Edge new_edge; + new_edge.points[0] = inserted_points[0]; + new_edge.points[1] = inserted_points[1]; + edges.push_back(new_edge); + } +} + +void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) { + + //Clip function.. find triangle points that will be mapped to the plane and form a segment + + Vector2 segment[3]; //2D + + int src_points = 0; + + for (int i = 0; i < 3; i++) { + Vector3 p = p_brush->faces[p_face].vertices[i]; + if (plane.has_point(p)) { + Vector3 pp = plane.project(p); + pp = to_poly.xform(pp); + segment[src_points++] = Vector2(pp.x, pp.y); + } else { + Vector3 q = p_brush->faces[p_face].vertices[(i + 1) % 3]; + if (plane.has_point(q)) + continue; //next point is in plane, will be added eventually + if (plane.is_point_over(p) == plane.is_point_over(q)) + continue; // both on same side of the plane, don't add + + Vector3 res; + if (plane.intersects_segment(p, q, &res)) { + res = to_poly.xform(res); + segment[src_points++] = Vector2(res.x, res.y); + } + } + } + + //all above or all below, nothing to do. Should not happen though since a precheck was done before. + if (src_points == 0) + return; + + //just one point in plane is not worth doing anything + if (src_points == 1) + return; + + //transform A points to 2D + + if (segment[0].distance_to(segment[1]) < CMP_EPSILON) + return; //too small + + _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B); +} + +void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge) { + + //construct a frame of reference for both transforms, in order to do intersection test + Vector3 va[3] = { + A->faces[p_face_a].vertices[0], + A->faces[p_face_a].vertices[1], + A->faces[p_face_a].vertices[2], + }; + Vector3 vb[3] = { + B->faces[p_face_b].vertices[0], + B->faces[p_face_b].vertices[1], + B->faces[p_face_b].vertices[2], + }; + + { + //check if either is a degenerate + if (va[0].distance_to(va[1]) < CMP_EPSILON || va[0].distance_to(va[2]) < CMP_EPSILON || va[1].distance_to(va[2]) < CMP_EPSILON) + return; + + if (vb[0].distance_to(vb[1]) < CMP_EPSILON || vb[0].distance_to(vb[2]) < CMP_EPSILON || vb[1].distance_to(vb[2]) < CMP_EPSILON) + return; + } + + { + //check if points are the same + int equal_count = 0; + + for (int i = 0; i < 3; i++) { + + for (int j = 0; j < 3; j++) { + if (va[i].distance_to(vb[j]) < mesh_merge.vertex_snap) { + equal_count++; + break; + } + } + } + + //if 2 or 3 points are the same, there is no point in doing anything. They can't + //be clipped either, so add both. + if (equal_count == 2 || equal_count == 3) { + return; + } + } + + // do a quick pre-check for no-intersection using the SAT theorem + + { + + //b under or over a plane + int over_count = 0, in_plane_count = 0, under_count = 0; + Plane plane_a(va[0], va[1], va[2]); + if (plane_a.normal == Vector3()) { + return; //degenerate + } + + for (int i = 0; i < 3; i++) { + if (plane_a.has_point(vb[i])) + in_plane_count++; + else if (plane_a.is_point_over(vb[i])) + over_count++; + else + under_count++; + } + + if (over_count == 0 || under_count == 0) + return; //no intersection, something needs to be under AND over + + //a under or over b plane + over_count = 0; + under_count = 0; + in_plane_count = 0; + + Plane plane_b(vb[0], vb[1], vb[2]); + if (plane_b.normal == Vector3()) + return; //degenerate + + for (int i = 0; i < 3; i++) { + if (plane_b.has_point(va[i])) + in_plane_count++; + else if (plane_b.is_point_over(va[i])) + over_count++; + else + under_count++; + } + + if (over_count == 0 || under_count == 0) + return; //no intersection, something needs to be under AND over + + //edge pairs (cross product combinations), see SAT theorem + + for (int i = 0; i < 3; i++) { + + Vector3 axis_a = (va[i] - va[(i + 1) % 3]).normalized(); + + for (int j = 0; j < 3; j++) { + + Vector3 axis_b = (vb[j] - vb[(j + 1) % 3]).normalized(); + + Vector3 sep_axis = axis_a.cross(axis_b); + if (sep_axis == Vector3()) + continue; //colineal + sep_axis.normalize(); + + real_t min_a = 1e20, max_a = -1e20; + real_t min_b = 1e20, max_b = -1e20; + + for (int k = 0; k < 3; k++) { + real_t d = sep_axis.dot(va[k]); + min_a = MIN(min_a, d); + max_a = MAX(max_a, d); + d = sep_axis.dot(vb[k]); + min_b = MIN(min_b, d); + max_b = MAX(max_b, d); + } + + min_b -= (max_a - min_a) * 0.5; + max_b += (max_a - min_a) * 0.5; + + real_t dmin = min_b - (min_a + max_a) * 0.5; + real_t dmax = max_b - (min_a + max_a) * 0.5; + + if (dmin > CMP_EPSILON || dmax < -CMP_EPSILON) { + return; //does not contain zero, so they don't overlap + } + } + } + } + + //if we are still here, it means they most likely intersect, so create BuildPolys if they dont existy + + BuildPoly *poly_a = NULL; + + if (!build_polys_a.has(p_face_a)) { + + BuildPoly bp; + bp.create(A, p_face_a, mesh_merge, false); + build_polys_a[p_face_a] = bp; + } + + poly_a = &build_polys_a[p_face_a]; + + BuildPoly *poly_b = NULL; + + if (!build_polys_b.has(p_face_b)) { + + BuildPoly bp; + bp.create(B, p_face_b, mesh_merge, true); + build_polys_b[p_face_b] = bp; + } + + poly_b = &build_polys_b[p_face_b]; + + //clip each other, this could be improved by using vertex unique IDs (more vertices may be shared instead of using snap) + poly_a->clip(B, p_face_b, mesh_merge, false); + poly_b->clip(A, p_face_a, mesh_merge, true); +} + +void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly) { + + //this function follows the polygon points counter clockwise and adds them. It creates lists of unique polygons + //every time an unused edge is found, it's pushed to a stack and continues from there. + + List<EdgeSort> edge_stack; + + { + EdgeSort es; + es.angle = 0; //wont be checked here + es.edge = p_edge; + es.prev_point = p_from_point; + es.edge_point = p_to_point; + + edge_stack.push_back(es); + } + + //attempt to empty the stack. + while (edge_stack.size()) { + + EdgeSort e = edge_stack.front()->get(); + edge_stack.pop_front(); + + if (edge_process[e.edge]) { + //nothing to do here + continue; + } + + Vector<int> points; + points.push_back(e.prev_point); + + int prev_point = e.prev_point; + int to_point = e.edge_point; + int current_edge = e.edge; + + edge_process[e.edge] = true; //mark as processed + + int limit = p_poly.points.size() * 4; //avoid infinite recursion + + while (to_point != e.prev_point && limit) { + + Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point }; + + //construct a basis transform from the segment, which will be used to check the angle + Transform2D t2d; + t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y + t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent + t2d[2] = segment[1]; //origin + + if (t2d.basis_determinant() == 0) + break; //abort poly + + t2d.affine_invert(); + + //push all edges found here, they will be sorted by minimum angle later. + Vector<EdgeSort> next_edges; + + for (int i = 0; i < vertex_process[to_point].size(); i++) { + + int edge = vertex_process[to_point][i]; + int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0]; + if (opposite_point == prev_point) + continue; //not going back + + EdgeSort e; + Vector2 local_vec = t2d.xform(p_poly.points[opposite_point].point); + e.angle = -local_vec.angle(); //negate so we can sort by minimum angle + e.edge = edge; + e.edge_point = opposite_point; + e.prev_point = to_point; + + next_edges.push_back(e); + } + + //finally, sort by minimum angle + next_edges.sort(); + + int next_point = -1; + int next_edge = -1; + + for (int i = 0; i < next_edges.size(); i++) { + + if (i == 0) { + //minimum angle found is the next point + next_point = next_edges[i].edge_point; + next_edge = next_edges[i].edge; + + } else { + //the rest are pushed to the stack IF they were not processed yet. + if (!edge_process[next_edges[i].edge]) { + edge_stack.push_back(next_edges[i]); + } + } + } + + if (next_edge == -1) { + //did not find anything, may be a dead-end edge (this should normally not happen) + //just flip the direction and go back + next_point = prev_point; + next_edge = current_edge; + } + + points.push_back(to_point); + + prev_point = to_point; + to_point = next_point; + edge_process[next_edge] = true; //mark this edge as processed + current_edge = next_edge; + + limit--; + } + + //if more than 2 points were added to the polygon, add it to the list of polygons. + if (points.size() > 2) { + PolyPoints pp; + pp.points = points; + r_poly.push_back(pp); + } + } +} + +void CSGBrushOperation::_add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline) { + + //this is the opposite of the function above. It adds polygon outlines instead. + //this is used for triangulating holes. + //no stack is used here because only the bigger outline is interesting. + + r_outline.push_back(p_from_point); + + int prev_point = p_from_point; + int to_point = p_to_point; + + int limit = p_poly.points.size() * 4; //avoid infinite recursion + + while (to_point != p_from_point && limit) { + + Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point }; + //again create a transform to compute the angle. + Transform2D t2d; + t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y + t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent + t2d[2] = segment[1]; //origin + + if (t2d.basis_determinant() == 0) + break; //abort poly + + t2d.affine_invert(); + + float max_angle; + int next_point_angle = -1; + + for (int i = 0; i < vertex_process[to_point].size(); i++) { + + int edge = vertex_process[to_point][i]; + int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0]; + if (opposite_point == prev_point) + continue; //not going back + + float angle = -t2d.xform(p_poly.points[opposite_point].point).angle(); + if (next_point_angle == -1 || angle > max_angle) { //same as before but use greater to check. + max_angle = angle; + next_point_angle = opposite_point; + } + } + + if (next_point_angle == -1) { + //go back because no route found + next_point_angle = prev_point; + } + + r_outline.push_back(to_point); + prev_point = to_point; + to_point = next_point_angle; + + limit--; + } +} + +void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b) { + + //finally, merge the 2D polygon back to 3D + + Vector<Vector<int> > vertex_process; + Vector<bool> edge_process; + + vertex_process.resize(p_poly.points.size()); + edge_process.resize(p_poly.edges.size()); + + //none processed by default + for (int i = 0; i < edge_process.size(); i++) { + edge_process[i] = false; + } + + //put edges in points, so points can go through them + for (int i = 0; i < p_poly.edges.size(); i++) { + vertex_process[p_poly.edges[i].points[0]].push_back(i); + vertex_process[p_poly.edges[i].points[1]].push_back(i); + } + + Vector<PolyPoints> polys; + + //process points that were not processed + for (int i = 0; i < edge_process.size(); i++) { + if (edge_process[i] == true) + continue; //already processed + + int intersect_poly = -1; + + if (i > 0) { + //this is disconnected, so it's clearly a hole. lets find where it belongs + Vector2 ref_point = p_poly.points[p_poly.edges[i].points[0]].point; + + for (int j = 0; j < polys.size(); j++) { + + //find a point outside poly + Vector2 out_point(-1e20, -1e20); + + const PolyPoints &pp = polys[j]; + + for (int k = 0; k < pp.points.size(); k++) { + Vector2 p = p_poly.points[pp.points[k]].point; + out_point.x = MAX(out_point.x, p.x); + out_point.y = MAX(out_point.y, p.y); + } + + out_point += Vector2(0.12341234, 0.4123412); // move to a random place to avoid direct edge-point chances + + int intersections = 0; + + for (int k = 0; k < pp.points.size(); k++) { + Vector2 p1 = p_poly.points[pp.points[k]].point; + Vector2 p2 = p_poly.points[pp.points[(k + 1) % pp.points.size()]].point; + + if (Geometry::segment_intersects_segment_2d(ref_point, out_point, p1, p2, NULL)) { + intersections++; + } + } + + if (intersections % 2 == 1) { + //hole is inside this poly + intersect_poly = j; + break; + } + } + } + + if (intersect_poly != -1) { + //must add this as a hole + Vector<int> outline; + _add_poly_outline(p_poly, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, outline); + + if (outline.size() > 1) { + polys[intersect_poly].holes.push_back(outline); + } + } + _add_poly_points(p_poly, i, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, edge_process, polys); + } + + //get rid of holes, not the most optiomal way, but also not a common case at all to be inoptimal + for (int i = 0; i < polys.size(); i++) { + + if (!polys[i].holes.size()) + continue; + + //repeat until no more holes are left to be merged + while (polys[i].holes.size()) { + + //try to merge a hole with the outline + bool added_hole = false; + + for (int j = 0; j < polys[i].holes.size(); j++) { + + //try hole vertices + int with_outline_vertex = -1; + int from_hole_vertex = -1; + + bool found = false; + + for (int k = 0; k < polys[i].holes[j].size(); k++) { + + int from_idx = polys[i].holes[j][k]; + Vector2 from = p_poly.points[from_idx].point; + + //try a segment from hole vertex to outline vertices + from_hole_vertex = k; + + bool valid = true; + + for (int l = 0; l < polys[i].points.size(); l++) { + + int to_idx = polys[i].points[l]; + Vector2 to = p_poly.points[to_idx].point; + with_outline_vertex = l; + + //try agaisnt outline (other points) first + + valid = true; + + for (int m = 0; m < polys[i].points.size(); m++) { + + int m_next = (m + 1) % polys[i].points.size(); + if (m == with_outline_vertex || m_next == with_outline_vertex) //do not test with edges that share this point + continue; + + if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].points[m]].point, p_poly.points[polys[i].points[m_next]].point, NULL)) { + valid = false; + break; + } + } + + if (!valid) + continue; + + //try agaisnt all holes including self + + for (int m = 0; m < polys[i].holes.size(); m++) { + + for (int n = 0; n < polys[i].holes[m].size(); n++) { + + int n_next = (n + 1) % polys[i].holes[m].size(); + if (m == j && (n == from_hole_vertex || n_next == from_hole_vertex)) //contains vertex being tested from current hole, skip + continue; + + if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].holes[m][n]].point, p_poly.points[polys[i].holes[m][n_next]].point, NULL)) { + valid = false; + break; + } + } + + if (!valid) + break; + } + + if (valid) //all passed! exit loop + break; + else + continue; //something went wrong, go on. + } + + if (valid) { + found = true; //if in the end this was valid, use it + break; + } + } + + if (found) { + + //hook this hole with outline, and remove from list of holes + + //duplicate point + int insert_at = with_outline_vertex; + polys[i].points.insert(insert_at, polys[i].points[insert_at]); + insert_at++; + //insert all others, outline should be backwards (must check) + int holesize = polys[i].holes[j].size(); + for (int k = 0; k <= holesize; k++) { + int idx = (from_hole_vertex + k) % holesize; + polys[i].points.insert(insert_at, polys[i].holes[j][idx]); + insert_at++; + } + + added_hole = true; + polys[i].holes.remove(j); + break; //got rid of hole, break and continue + } + } + + ERR_BREAK(!added_hole); + } + } + + //triangulate polygons + + for (int i = 0; i < polys.size(); i++) { + + Vector<Vector2> vertices; + vertices.resize(polys[i].points.size()); + for (int j = 0; j < vertices.size(); j++) { + vertices[j] = p_poly.points[polys[i].points[j]].point; + } + + Vector<int> indices = Geometry::triangulate_polygon(vertices); + + for (int j = 0; j < indices.size(); j += 3) { + + //obtain the vertex + + Vector3 face[3]; + Vector2 uv[3]; + float cp = Geometry::vec2_cross(p_poly.points[polys[i].points[indices[j + 0]]].point, p_poly.points[polys[i].points[indices[j + 1]]].point, p_poly.points[polys[i].points[indices[j + 2]]].point); + if (Math::abs(cp) < CMP_EPSILON) + continue; + + for (int k = 0; k < 3; k++) { + + Vector2 p = p_poly.points[polys[i].points[indices[j + k]]].point; + face[k] = p_poly.to_world.xform(Vector3(p.x, p.y, 0)); + uv[k] = p_poly.points[polys[i].points[indices[j + k]]].uv; + } + + mesh.add_face(face[0], face[1], face[2], uv[0], uv[1], uv[2], p_poly.smooth, p_poly.invert, p_poly.material, p_from_b); + } + } +} + +//use a limit to speed up bvh and limit the depth +#define BVH_LIMIT 8 + +int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) { + + if (p_depth > max_depth) { + max_depth = p_depth; + } + + if (p_size <= BVH_LIMIT) { + + for (int i = 0; i < p_size - 1; i++) { + p_bb[p_from + i]->next = p_bb[p_from + i + 1] - p_bvh; + } + return p_bb[p_from] - p_bvh; + } else if (p_size == 0) { + + return -1; + } + + AABB aabb; + aabb = p_bb[p_from]->aabb; + for (int i = 1; i < p_size; i++) { + + aabb.merge_with(p_bb[p_from + i]->aabb); + } + + int li = aabb.get_longest_axis_index(); + + switch (li) { + + case Vector3::AXIS_X: { + SortArray<BVH *, BVHCmpX> sort_x; + sort_x.nth_element(0, p_size, p_size / 2, &p_bb[p_from]); + //sort_x.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Y: { + SortArray<BVH *, BVHCmpY> sort_y; + sort_y.nth_element(0, p_size, p_size / 2, &p_bb[p_from]); + //sort_y.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Z: { + SortArray<BVH *, BVHCmpZ> sort_z; + sort_z.nth_element(0, p_size, p_size / 2, &p_bb[p_from]); + //sort_z.sort(&p_bb[p_from],p_size); + + } break; + } + + int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc); + int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc); + + int index = max_alloc++; + BVH *_new = &p_bvh[index]; + _new->aabb = aabb; + _new->center = aabb.position + aabb.size * 0.5; + _new->face = -1; + _new->left = left; + _new->right = right; + _new->next = -1; + + return index; +} + +int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const { + + uint32_t *stack = (uint32_t *)alloca(sizeof(int) * p_max_depth); + + enum { + TEST_AABB_BIT = 0, + VISIT_LEFT_BIT = 1, + VISIT_RIGHT_BIT = 2, + VISIT_DONE_BIT = 3, + VISITED_BIT_SHIFT = 29, + NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1, + VISITED_BIT_MASK = ~NODE_IDX_MASK, + + }; + + int intersections = 0; + + int level = 0; + + const Vector3 *vertexptr = points.ptr(); + const Face *facesptr = faces.ptr(); + AABB segment_aabb; + segment_aabb.position = p_begin; + segment_aabb.expand_to(p_end); + + int pos = p_bvh_first; + + stack[0] = pos; + while (true) { + + uint32_t node = stack[level] & NODE_IDX_MASK; + const BVH &b = bvhptr[node]; + bool done = false; + + switch (stack[level] >> VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + if (b.face >= 0) { + + const BVH *bp = &b; + + while (bp) { + + bool valid = segment_aabb.intersects(bp->aabb) && bp->aabb.intersects_segment(p_begin, p_end); + + if (valid && p_exclude != bp->face) { + const Face &s = facesptr[bp->face]; + Face3 f3(vertexptr[s.points[0]], vertexptr[s.points[1]], vertexptr[s.points[2]]); + + Vector3 res; + + if (f3.intersects_segment(p_begin, p_end, &res)) { + intersections++; + } + } + if (bp->next != -1) { + bp = &bvhptr[bp->next]; + } else { + bp = NULL; + } + } + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + bool valid = segment_aabb.intersects(b.aabb) && b.aabb.intersects_segment(p_begin, p_end); + + if (!valid) { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; + } + } + continue; + } + case VISIT_LEFT_BIT: { + + stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.left | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_RIGHT_BIT: { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.right | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_DONE_BIT: { + + if (level == 0) { + done = true; + break; + } else + level--; + continue; + } + } + + if (done) + break; + } + + return intersections; +} + +void CSGBrushOperation::MeshMerge::mark_inside_faces() { + + // mark faces that are inside. This helps later do the boolean ops when merging. + // this approach is very brute force (with a bunch of optimizatios, such as BVH and pre AABB intersection test) + + AABB aabb; + + for (int i = 0; i < points.size(); i++) { + if (i == 0) { + aabb.position = points[i]; + } else { + aabb.expand_to(points[i]); + } + } + + float max_distance = aabb.size.length() * 1.2; + + Vector<BVH> bvhvec; + bvhvec.resize(faces.size() * 3); //will never be larger than this (todo make better) + BVH *bvh = bvhvec.ptrw(); + + AABB faces_a; + AABB faces_b; + + bool first_a = true; + bool first_b = true; + + for (int i = 0; i < faces.size(); i++) { + bvh[i].left = -1; + bvh[i].right = -1; + bvh[i].face = i; + bvh[i].aabb.position = points[faces[i].points[0]]; + bvh[i].aabb.expand_to(points[faces[i].points[1]]); + bvh[i].aabb.expand_to(points[faces[i].points[2]]); + bvh[i].center = bvh[i].aabb.position + bvh[i].aabb.size * 0.5; + bvh[i].next = -1; + if (faces[i].from_b) { + if (first_b) { + faces_b = bvh[i].aabb; + first_b = false; + } else { + faces_b.merge_with(bvh[i].aabb); + } + } else { + if (first_a) { + faces_a = bvh[i].aabb; + first_a = false; + } else { + faces_a.merge_with(bvh[i].aabb); + } + } + } + + AABB intersection_aabb = faces_a.intersection(faces_b); + intersection_aabb.grow_by(intersection_aabb.get_longest_axis_size() * 0.01); //grow a little, avoid numerical error + + if (intersection_aabb.size == Vector3()) //AABB do not intersect, so neither do shapes. + return; + + Vector<BVH *> bvhtrvec; + bvhtrvec.resize(faces.size()); + BVH **bvhptr = bvhtrvec.ptrw(); + for (int i = 0; i < faces.size(); i++) { + + bvhptr[i] = &bvh[i]; + } + + int max_depth = 0; + int max_alloc = faces.size(); + _create_bvh(bvh, bvhptr, 0, faces.size(), 1, max_depth, max_alloc); + + for (int i = 0; i < faces.size(); i++) { + + if (!intersection_aabb.intersects(bvh[i].aabb)) + continue; //not in AABB intersection, so not in face intersection + Vector3 center = points[faces[i].points[0]]; + center += points[faces[i].points[1]]; + center += points[faces[i].points[2]]; + center /= 3.0; + + Plane plane(points[faces[i].points[0]], points[faces[i].points[1]], points[faces[i].points[2]]); + Vector3 target = center + plane.normal * max_distance + Vector3(0.0001234, 0.000512, 0.00013423); //reduce chance of edge hits by doing a small increment + + int intersections = _bvh_count_intersections(bvh, max_depth, max_alloc - 1, center, target, i); + + if (intersections & 1) { + faces[i].inside = true; + } + } +} + +void CSGBrushOperation::MeshMerge::add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) { + + Vector3 src_points[3] = { p_a, p_b, p_c }; + Vector2 src_uvs[3] = { p_uv_a, p_uv_b, p_uv_c }; + int indices[3]; + for (int i = 0; i < 3; i++) { + + VertexKey vk; + vk.x = int((double(src_points[i].x) + double(vertex_snap) * 0.31234) / double(vertex_snap)); + vk.y = int((double(src_points[i].y) + double(vertex_snap) * 0.31234) / double(vertex_snap)); + vk.z = int((double(src_points[i].z) + double(vertex_snap) * 0.31234) / double(vertex_snap)); + + int res; + if (snap_cache.lookup(vk, res)) { + indices[i] = res; + } else { + indices[i] = points.size(); + points.push_back(src_points[i]); + snap_cache.set(vk, indices[i]); + } + } + + if (indices[0] == indices[2] || indices[0] == indices[1] || indices[1] == indices[2]) + return; //not adding degenerate + + MeshMerge::Face face; + face.from_b = p_from_b; + face.inside = false; + face.smooth = p_smooth; + face.invert = p_invert; + if (p_material.is_valid()) { + if (!materials.has(p_material)) { + face.material_idx = materials.size(); + materials[p_material] = face.material_idx; + } else { + face.material_idx = materials[p_material]; + } + } else { + face.material_idx = -1; + } + + for (int k = 0; k < 3; k++) { + + face.points[k] = indices[k]; + face.uvs[k] = src_uvs[k]; + ; + } + + faces.push_back(face); +} + +void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap) { + + CallbackData cd; + cd.self = this; + cd.A = &p_A; + cd.B = &p_B; + + MeshMerge mesh_merge; + mesh_merge.vertex_snap = p_snap; + + //check intersections between faces. Use AABB to speed up precheck + //this generates list of buildpolys and clips them. + //this was originally BVH optimized, but its not really worth it. + for (int i = 0; i < p_A.faces.size(); i++) { + cd.face_a = i; + for (int j = 0; j < p_B.faces.size(); j++) { + if (p_A.faces[i].aabb.intersects(p_B.faces[j].aabb)) { + _collision_callback(&p_A, i, cd.build_polys_A, &p_B, j, cd.build_polys_B, mesh_merge); + } + } + } + + //merge the already cliped polys back to 3D + for (Map<int, BuildPoly>::Element *E = cd.build_polys_A.front(); E; E = E->next()) { + _merge_poly(mesh_merge, E->key(), E->get(), false); + } + + for (Map<int, BuildPoly>::Element *E = cd.build_polys_B.front(); E; E = E->next()) { + _merge_poly(mesh_merge, E->key(), E->get(), true); + } + + //merge the non clipped faces back + + for (int i = 0; i < p_A.faces.size(); i++) { + + if (cd.build_polys_A.has(i)) + continue; //made from buildpoly, skipping + + Vector3 points[3]; + Vector2 uvs[3]; + for (int j = 0; j < 3; j++) { + points[j] = p_A.faces[i].vertices[j]; + uvs[j] = p_A.faces[i].uvs[j]; + } + Ref<Material> material; + if (p_A.faces[i].material != -1) { + material = p_A.materials[p_A.faces[i].material]; + } + mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_A.faces[i].smooth, p_A.faces[i].invert, material, false); + } + + for (int i = 0; i < p_B.faces.size(); i++) { + + if (cd.build_polys_B.has(i)) + continue; //made from buildpoly, skipping + + Vector3 points[3]; + Vector2 uvs[3]; + for (int j = 0; j < 3; j++) { + points[j] = p_B.faces[i].vertices[j]; + uvs[j] = p_B.faces[i].uvs[j]; + } + Ref<Material> material; + if (p_B.faces[i].material != -1) { + material = p_B.materials[p_B.faces[i].material]; + } + mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_B.faces[i].smooth, p_B.faces[i].invert, material, true); + } + + //mark faces that ended up inside the intersection + mesh_merge.mark_inside_faces(); + + //regen new brush to start filling it again + result.clear(); + + switch (p_operation) { + + case OPERATION_UNION: { + + int outside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (mesh_merge.faces[i].inside) + continue; + + outside_count++; + } + + result.faces.resize(outside_count); + + outside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (mesh_merge.faces[i].inside) + continue; + for (int j = 0; j < 3; j++) { + result.faces[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + } + + result.faces[outside_count].smooth = mesh_merge.faces[i].smooth; + result.faces[outside_count].invert = mesh_merge.faces[i].invert; + result.faces[outside_count].material = mesh_merge.faces[i].material_idx; + outside_count++; + } + + result._regen_face_aabbs(); + + } break; + case OPERATION_INTERSECTION: { + + int inside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (!mesh_merge.faces[i].inside) + continue; + + inside_count++; + } + + result.faces.resize(inside_count); + + inside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (!mesh_merge.faces[i].inside) + continue; + for (int j = 0; j < 3; j++) { + result.faces[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + } + + result.faces[inside_count].smooth = mesh_merge.faces[i].smooth; + result.faces[inside_count].invert = mesh_merge.faces[i].invert; + result.faces[inside_count].material = mesh_merge.faces[i].material_idx; + inside_count++; + } + + result._regen_face_aabbs(); + + } break; + case OPERATION_SUBSTRACTION: { + + int face_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside) + continue; + if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside) + continue; + + face_count++; + } + + result.faces.resize(face_count); + + face_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + + if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside) + continue; + if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside) + continue; + + for (int j = 0; j < 3; j++) { + result.faces[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces[face_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + } + + if (mesh_merge.faces[i].from_b) { + //invert facing of insides of B + SWAP(result.faces[face_count].vertices[1], result.faces[face_count].vertices[2]); + SWAP(result.faces[face_count].uvs[1], result.faces[face_count].uvs[2]); + } + + result.faces[face_count].smooth = mesh_merge.faces[i].smooth; + result.faces[face_count].invert = mesh_merge.faces[i].invert; + result.faces[face_count].material = mesh_merge.faces[i].material_idx; + face_count++; + } + + result._regen_face_aabbs(); + + } break; + } + + //updatelist of materials + result.materials.resize(mesh_merge.materials.size()); + for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) { + result.materials[E->get()] = E->key(); + } +} diff --git a/modules/csg/csg.h b/modules/csg/csg.h new file mode 100644 index 0000000000..bb67e1fb36 --- /dev/null +++ b/modules/csg/csg.h @@ -0,0 +1,206 @@ +#ifndef CSG_H +#define CSG_H + +#include "aabb.h" +#include "dvector.h" +#include "map.h" +#include "math_2d.h" +#include "oa_hash_map.h" +#include "plane.h" +#include "scene/resources/material.h" +#include "transform.h" +#include "vector3.h" + +struct CSGBrush { + + struct Face { + + Vector3 vertices[3]; + Vector2 uvs[3]; + AABB aabb; + bool smooth; + bool invert; + int material; + }; + + Vector<Face> faces; + Vector<Ref<Material> > materials; + + void _regen_face_aabbs(); + //create a brush from faces + void build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces); + void copy_from(const CSGBrush &p_brush, const Transform &p_xform); + + void clear(); +}; + +struct CSGBrushOperation { + + enum Operation { + OPERATION_UNION, + OPERATION_INTERSECTION, + OPERATION_SUBSTRACTION, + + }; + + struct MeshMerge { + + struct BVH { + int face; + int left; + int right; + int next; + Vector3 center; + AABB aabb; + }; + + struct BVHCmpX { + + bool operator()(const BVH *p_left, const BVH *p_right) const { + + return p_left->center.x < p_right->center.x; + } + }; + + struct BVHCmpY { + + bool operator()(const BVH *p_left, const BVH *p_right) const { + + return p_left->center.y < p_right->center.y; + } + }; + struct BVHCmpZ { + + bool operator()(const BVH *p_left, const BVH *p_right) const { + + return p_left->center.z < p_right->center.z; + } + }; + + int _bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const; + int _create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc); + + struct VertexKey { + int32_t x, y, z; + _FORCE_INLINE_ bool operator<(const VertexKey &p_key) const { + if (x == p_key.x) { + if (y == p_key.y) { + return z < p_key.z; + } else { + return y < p_key.y; + } + } else { + return x < p_key.x; + } + } + + _FORCE_INLINE_ bool operator==(const VertexKey &p_key) const { + return (x == p_key.x && y == p_key.y && z == p_key.z); + } + }; + + struct VertexKeyHash { + static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) { + uint32_t h = hash_djb2_one_32(p_vk.x); + h = hash_djb2_one_32(p_vk.y, h); + h = hash_djb2_one_32(p_vk.z, h); + return h; + } + }; + + OAHashMap<VertexKey, int, VertexKeyHash> snap_cache; + + Vector<Vector3> points; + + struct Face { + bool from_b; + bool inside; + int points[3]; + Vector2 uvs[3]; + bool smooth; + bool invert; + int material_idx; + }; + + Vector<Face> faces; + + Map<Ref<Material>, int> materials; + + Map<Vector3, int> vertex_map; + void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b); + // void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, bool p_from_b); + + float vertex_snap; + void mark_inside_faces(); + }; + + struct BuildPoly { + + Plane plane; + Transform to_poly; + Transform to_world; + int face_index; + + struct Point { + Vector2 point; + Vector2 uv; + }; + + Vector<Point> points; + + struct Edge { + bool outer; + int points[2]; + Edge() { + outer = false; + } + }; + + Vector<Edge> edges; + Ref<Material> material; + bool smooth; + bool invert; + + int base_edges; //edges from original triangle, even if split + + void _clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B); + + void create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B); + void clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B); + }; + + struct PolyPoints { + + Vector<int> points; + + Vector<Vector<int> > holes; + }; + + struct EdgeSort { + int edge; + int prev_point; + int edge_point; + float angle; + bool operator<(const EdgeSort &p_edge) const { return angle < p_edge.angle; } + }; + + struct CallbackData { + const CSGBrush *A; + const CSGBrush *B; + int face_a; + CSGBrushOperation *self; + Map<int, BuildPoly> build_polys_A; + Map<int, BuildPoly> build_polys_B; + }; + + void _add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly); + void _add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline); + void _merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b); + + void _collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge); + + static void _collision_callbacks(void *ud, int p_face_b); + void merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap = 0.001); +}; + +#endif // CSG_H diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp new file mode 100644 index 0000000000..06cbaab3b0 --- /dev/null +++ b/modules/csg/csg_gizmos.cpp @@ -0,0 +1,315 @@ +#include "csg_gizmos.h" + +/////////// + +String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const { + + if (Object::cast_to<CSGSphere>(cs)) { + + return "Radius"; + } + + if (Object::cast_to<CSGBox>(cs)) { + + static const char *hname[3] = { "Width", "Height", "Depth" }; + return hname[p_idx]; + } + + if (Object::cast_to<CSGCylinder>(cs)) { + + return p_idx == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to<CSGTorus>(cs)) { + + return p_idx == 0 ? "InnerRadius" : "OuterRadius"; + } + + return ""; +} +Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const { + + if (Object::cast_to<CSGSphere>(cs)) { + + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + return s->get_radius(); + } + + if (Object::cast_to<CSGBox>(cs)) { + + CSGBox *s = Object::cast_to<CSGBox>(cs); + switch (p_idx) { + case 0: return s->get_width(); + case 1: return s->get_height(); + case 2: return s->get_depth(); + } + } + + if (Object::cast_to<CSGCylinder>(cs)) { + + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + return p_idx == 0 ? s->get_radius() : s->get_height(); + } + + if (Object::cast_to<CSGTorus>(cs)) { + + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius(); + } + + return Variant(); +} +void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { + + Transform gt = cs->get_global_transform(); + gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + if (Object::cast_to<CSGSphere>(cs)) { + + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + float d = ra.x; + if (d < 0.001) + d = 0.001; + + s->set_radius(d); + } + + if (Object::cast_to<CSGBox>(cs)) { + + CSGBox *s = Object::cast_to<CSGBox>(cs); + + Vector3 axis; + axis[p_idx] = 1.0; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = ra[p_idx]; + if (d < 0.001) + d = 0.001; + + switch (p_idx) { + case 0: s->set_width(d); break; + case 1: s->set_height(d); break; + case 2: s->set_depth(d); break; + } + } + + if (Object::cast_to<CSGCylinder>(cs)) { + + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + + Vector3 axis; + axis[p_idx == 0 ? 0 : 1] = 1.0; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + s->set_radius(d); + else if (p_idx == 1) + s->set_height(d * 2.0); + } + + if (Object::cast_to<CSGTorus>(cs)) { + + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + + Vector3 axis; + axis[0] = 1.0; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + s->set_inner_radius(d); + else if (p_idx == 1) + s->set_outer_radius(d); + } +} +void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { + + if (Object::cast_to<CSGSphere>(cs)) { + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + if (p_cancel) { + s->set_radius(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Sphere Shape Radius")); + ur->add_do_method(s, "set_radius", s->get_radius()); + ur->add_undo_method(s, "set_radius", p_restore); + ur->commit_action(); + } + + if (Object::cast_to<CSGBox>(cs)) { + CSGBox *s = Object::cast_to<CSGBox>(cs); + if (p_cancel) { + switch (p_idx) { + case 0: s->set_width(p_restore); break; + case 1: s->set_height(p_restore); break; + case 2: s->set_depth(p_restore); break; + } + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Box Shape Extents")); + static const char *method[3] = { "set_width", "set_height", "set_depth" }; + float current; + switch (p_idx) { + case 0: current = s->get_width(); break; + case 1: current = s->get_height(); break; + case 2: current = s->get_depth(); break; + } + + ur->add_do_method(s, method[p_idx], current); + ur->add_undo_method(s, method[p_idx], p_restore); + ur->commit_action(); + } + + if (Object::cast_to<CSGCylinder>(cs)) { + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + if (p_cancel) { + if (p_idx == 0) + s->set_radius(p_restore); + else + s->set_height(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Cylinder Radius")); + ur->add_do_method(s, "set_radius", s->get_radius()); + ur->add_undo_method(s, "set_radius", p_restore); + } else { + ur->create_action(TTR("Change Cylinder Height")); + ur->add_do_method(s, "set_height", s->get_height()); + ur->add_undo_method(s, "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to<CSGTorus>(cs)) { + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + if (p_cancel) { + if (p_idx == 0) + s->set_inner_radius(p_restore); + else + s->set_outer_radius(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Torus Inner Radius")); + ur->add_do_method(s, "set_inner_radius", s->get_inner_radius()); + ur->add_undo_method(s, "set_inner_radius", p_restore); + } else { + ur->create_action(TTR("Change Torus Outer Radius")); + ur->add_do_method(s, "set_outer_radius", s->get_outer_radius()); + ur->add_undo_method(s, "set_outer_radius", p_restore); + } + + ur->commit_action(); + } +} +void CSGShapeSpatialGizmo::redraw() { + + clear(); + + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg"); + Ref<Material> material = create_material("shape_material", gizmo_color); + + PoolVector<Vector3> faces = cs->get_brush_faces(); + + Vector<Vector3> lines; + lines.resize(faces.size() * 2); + { + PoolVector<Vector3>::Read r = faces.read(); + + for (int i = 0; i < lines.size(); i += 6) { + int f = i / 6; + for (int j = 0; j < 3; j++) { + int j_n = (j + 1) % 3; + lines[i + j * 2 + 0] = r[f * 3 + j]; + lines[i + j * 2 + 1] = r[f * 3 + j_n]; + } + } + } + + add_lines(lines, material); + add_collision_segments(lines); + + if (Object::cast_to<CSGSphere>(cs)) { + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + + float r = s->get_radius(); + Vector<Vector3> handles; + handles.push_back(Vector3(r, 0, 0)); + add_handles(handles); + } + + if (Object::cast_to<CSGBox>(cs)) { + CSGBox *s = Object::cast_to<CSGBox>(cs); + + Vector<Vector3> handles; + handles.push_back(Vector3(s->get_width(), 0, 0)); + handles.push_back(Vector3(0, s->get_height(), 0)); + handles.push_back(Vector3(0, 0, s->get_depth())); + add_handles(handles); + } + + if (Object::cast_to<CSGCylinder>(cs)) { + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + + Vector<Vector3> handles; + handles.push_back(Vector3(s->get_radius(), 0, 0)); + handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); + add_handles(handles); + } + + if (Object::cast_to<CSGTorus>(cs)) { + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + + Vector<Vector3> handles; + handles.push_back(Vector3(s->get_inner_radius(), 0, 0)); + handles.push_back(Vector3(s->get_outer_radius(), 0, 0)); + add_handles(handles); + } +} +CSGShapeSpatialGizmo::CSGShapeSpatialGizmo(CSGShape *p_cs) { + + cs = p_cs; + set_spatial_node(p_cs); +} + +Ref<SpatialEditorGizmo> EditorPluginCSG::create_spatial_gizmo(Spatial *p_spatial) { + if (Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial)) { + Ref<CSGShapeSpatialGizmo> csg = memnew(CSGShapeSpatialGizmo(Object::cast_to<CSGShape>(p_spatial))); + return csg; + } + + return Ref<SpatialEditorGizmo>(); +} + +EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) { + + EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1)); +} diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h new file mode 100644 index 0000000000..b5e394ecad --- /dev/null +++ b/modules/csg/csg_gizmos.h @@ -0,0 +1,30 @@ +#ifndef CSG_GIZMOS_H +#define CSG_GIZMOS_H + +#include "csg_shape.h" +#include "editor/editor_plugin.h" +#include "editor/spatial_editor_gizmos.h" + +class CSGShapeSpatialGizmo : public EditorSpatialGizmo { + + GDCLASS(CSGShapeSpatialGizmo, EditorSpatialGizmo); + + CSGShape *cs; + +public: + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); + virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(); + CSGShapeSpatialGizmo(CSGShape *p_cs = NULL); +}; + +class EditorPluginCSG : public EditorPlugin { + GDCLASS(EditorPluginCSG, EditorPlugin) +public: + virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial); + EditorPluginCSG(EditorNode *p_editor); +}; + +#endif // CSG_GIZMOS_H diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp new file mode 100644 index 0000000000..1f2f12f54d --- /dev/null +++ b/modules/csg/csg_shape.cpp @@ -0,0 +1,2125 @@ +#include "csg_shape.h" +#include "scene/3d/path.h" + +void CSGShape::set_use_collision(bool p_enable) { + + if (use_collision == p_enable) + return; + + use_collision = p_enable; + + if (!is_inside_tree() || !is_root_shape()) + return; + + if (use_collision) { + root_collision_shape.instance(); + root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC); + PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); + PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); + PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); + _make_dirty(); //force update + } else { + PhysicsServer::get_singleton()->free(root_collision_instance); + root_collision_instance = RID(); + root_collision_shape.unref(); + } +} + +bool CSGShape::is_using_collision() const { + return use_collision; +} + +bool CSGShape::is_root_shape() const { + + return !parent; +} + +void CSGShape::set_snap(float p_snap) { + snap = p_snap; +} + +float CSGShape::get_snap() const { + return snap; +} + +void CSGShape::_make_dirty() { + + if (!is_inside_tree()) + return; + + if (dirty) { + return; + } + + dirty = true; + + if (parent) { + parent->_make_dirty(); + } else { + //only parent will do + call_deferred("_update_shape"); + } +} + +CSGBrush *CSGShape::_get_brush() { + + if (dirty) { + if (brush) { + memdelete(brush); + } + brush = NULL; + + CSGBrush *n = _build_brush(); + + for (int i = 0; i < get_child_count(); i++) { + + CSGShape *child = Object::cast_to<CSGShape>(get_child(i)); + if (!child) + continue; + if (!child->is_visible_in_tree()) + continue; + + CSGBrush *n2 = child->_get_brush(); + if (!n2) + continue; + if (!n) { + n = memnew(CSGBrush); + + n->copy_from(*n2, child->get_transform()); + + } else { + + CSGBrush *nn = memnew(CSGBrush); + CSGBrush *nn2 = memnew(CSGBrush); + nn2->copy_from(*n2, child->get_transform()); + + CSGBrushOperation bop; + + switch (child->get_operation()) { + case CSGShape::OPERATION_UNION: bop.merge_brushes(CSGBrushOperation::OPERATION_UNION, *n, *nn2, *nn, snap); break; + case CSGShape::OPERATION_INTERSECTION: bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap); break; + case CSGShape::OPERATION_SUBTRACTION: bop.merge_brushes(CSGBrushOperation::OPERATION_SUBSTRACTION, *n, *nn2, *nn, snap); break; + } + memdelete(n); + memdelete(nn2); + n = nn; + } + } + + if (n) { + AABB aabb; + for (int i = 0; i < n->faces.size(); i++) { + for (int j = 0; j < 3; j++) { + if (i == 0 && j == 0) + aabb.position = n->faces[i].vertices[j]; + else + aabb.expand_to(n->faces[i].vertices[j]); + } + } + node_aabb = aabb; + } else { + node_aabb = AABB(); + } + + brush = n; + + dirty = false; + } + + return brush; +} + +void CSGShape::_update_shape() { + + //print_line("updating shape for " + String(get_path())); + set_base(RID()); + root_mesh.unref(); //byebye root mesh + + CSGBrush *n = _get_brush(); + ERR_FAIL_COND(!n); + + OAHashMap<Vector3, Vector3> vec_map; + + Vector<int> face_count; + face_count.resize(n->materials.size() + 1); + for (int i = 0; i < face_count.size(); i++) { + face_count[i] = 0; + } + + for (int i = 0; i < n->faces.size(); i++) { + int mat = n->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= face_count.size()); + int idx = mat == -1 ? face_count.size() - 1 : mat; + if (n->faces[i].smooth) { + + Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); + + for (int j = 0; j < 3; j++) { + Vector3 v = n->faces[i].vertices[j]; + Vector3 add; + if (vec_map.lookup(v, add)) { + add += p.normal; + } else { + add = p.normal; + } + vec_map.set(v, add); + } + } + + face_count[idx]++; + } + + Vector<ShapeUpdateSurface> surfaces; + + surfaces.resize(face_count.size()); + + //create arrays + for (int i = 0; i < surfaces.size(); i++) { + + surfaces[i].vertices.resize(face_count[i] * 3); + surfaces[i].normals.resize(face_count[i] * 3); + surfaces[i].uvs.resize(face_count[i] * 3); + surfaces[i].last_added = 0; + + if (i != surfaces.size() - 1) { + surfaces[i].material = n->materials[i]; + } + + surfaces[i].verticesw = surfaces[i].vertices.write(); + surfaces[i].normalsw = surfaces[i].normals.write(); + surfaces[i].uvsw = surfaces[i].uvs.write(); + } + + //fill arrays + PoolVector<Vector3> physics_faces; + bool fill_physics_faces = false; + if (root_collision_shape.is_valid()) { + physics_faces.resize(n->faces.size() * 3); + fill_physics_faces = true; + } + + { + PoolVector<Vector3>::Write physicsw; + + if (fill_physics_faces) { + physicsw = physics_faces.write(); + } + + for (int i = 0; i < n->faces.size(); i++) { + + int order[3] = { 0, 1, 2 }; + + if (n->faces[i].invert) { + SWAP(order[1], order[2]); + } + + if (fill_physics_faces) { + physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]]; + physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]]; + physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]]; + } + + int mat = n->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= face_count.size()); + int idx = mat == -1 ? face_count.size() - 1 : mat; + + int last = surfaces[idx].last_added; + + Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); + + for (int j = 0; j < 3; j++) { + + Vector3 v = n->faces[i].vertices[j]; + + Vector3 normal = p.normal; + + if (n->faces[i].smooth && vec_map.lookup(v, normal)) { + normal.normalize(); + } + + if (n->faces[i].invert) { + + normal = -normal; + } + + surfaces[idx].verticesw[last + order[j]] = v; + surfaces[idx].uvsw[last + order[j]] = n->faces[i].uvs[j]; + surfaces[idx].normalsw[last + order[j]] = normal; + } + + surfaces[idx].last_added += 3; + } + } + + root_mesh.instance(); + //create surfaces + + for (int i = 0; i < surfaces.size(); i++) { + + surfaces[i].verticesw = PoolVector<Vector3>::Write(); + surfaces[i].normalsw = PoolVector<Vector3>::Write(); + surfaces[i].uvsw = PoolVector<Vector2>::Write(); + + if (surfaces[i].last_added == 0) + continue; + + Array array; + array.resize(Mesh::ARRAY_MAX); + + array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices; + array[Mesh::ARRAY_NORMAL] = surfaces[i].normals; + array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs; + + int idx = root_mesh->get_surface_count(); + root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array); + root_mesh->surface_set_material(idx, surfaces[i].material); + } + + if (root_collision_shape.is_valid()) { + root_collision_shape->set_faces(physics_faces); + } + + set_base(root_mesh->get_rid()); +} +AABB CSGShape::get_aabb() const { + return node_aabb; +} + +PoolVector<Vector3> CSGShape::get_brush_faces() { + ERR_FAIL_COND_V(!is_inside_tree(), PoolVector<Vector3>()); + CSGBrush *b = _get_brush(); + if (!b) { + return PoolVector<Vector3>(); + } + + PoolVector<Vector3> faces; + int fc = b->faces.size(); + faces.resize(fc * 3); + { + PoolVector<Vector3>::Write w = faces.write(); + for (int i = 0; i < fc; i++) { + w[i * 3 + 0] = b->faces[i].vertices[0]; + w[i * 3 + 1] = b->faces[i].vertices[1]; + w[i * 3 + 2] = b->faces[i].vertices[2]; + } + } + + return faces; +} + +PoolVector<Face3> CSGShape::get_faces(uint32_t p_usage_flags) const { + + return PoolVector<Face3>(); +} + +void CSGShape::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + Node *parentn = get_parent(); + if (parentn) { + parent = Object::cast_to<CSGShape>(parentn); + } + + if (use_collision && is_root_shape()) { + root_collision_shape.instance(); + root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC); + PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); + PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); + PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); + } + + _make_dirty(); + } + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + + //print_line("local xform changed"); + if (parent) { + parent->_make_dirty(); + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + if (parent) + parent->_make_dirty(); + parent = NULL; + + if (use_collision && is_root_shape()) { + PhysicsServer::get_singleton()->free(root_collision_instance); + root_collision_instance = RID(); + root_collision_shape.unref(); + } + _make_dirty(); + } +} + +void CSGShape::set_operation(Operation p_operation) { + + operation = p_operation; + _make_dirty(); +} + +CSGShape::Operation CSGShape::get_operation() const { + return operation; +} + +void CSGShape::_validate_property(PropertyInfo &property) const { + if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) { + //hide collision if not root + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + +void CSGShape::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape::_update_shape); + ClassDB::bind_method(D_METHOD("is_root_shape"), &CSGShape::is_root_shape); + + ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation); + ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation); + + ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision); + ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision); + + ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap); + ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap"); + + BIND_CONSTANT(OPERATION_UNION); + BIND_CONSTANT(OPERATION_INTERSECTION); + BIND_CONSTANT(OPERATION_SUBTRACTION); +} + +CSGShape::CSGShape() { + brush = NULL; + set_notify_local_transform(true); + dirty = false; + parent = NULL; + use_collision = false; + operation = OPERATION_UNION; + snap = 0.001; +} + +CSGShape::~CSGShape() { + if (brush) { + memdelete(brush); + brush = NULL; + } +} +////////////////////////////////// + +CSGBrush *CSGCombiner::_build_brush() { + + return NULL; //does not build anything +} + +CSGCombiner::CSGCombiner() { +} + +///////////////////// + +CSGBrush *CSGPrimitive::_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials) { + + CSGBrush *brush = memnew(CSGBrush); + + PoolVector<bool> invert; + invert.resize(p_vertices.size() / 3); + { + int ic = invert.size(); + PoolVector<bool>::Write w = invert.write(); + for (int i = 0; i < ic; i++) { + w[i] = invert_faces; + } + } + brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert); + + return brush; +} + +void CSGPrimitive::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitive::set_invert_faces); + ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitive::is_inverting_faces); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces"); +} + +void CSGPrimitive::set_invert_faces(bool p_invert) { + if (invert_faces == p_invert) + return; + + invert_faces = p_invert; + + _make_dirty(); +} + +bool CSGPrimitive::is_inverting_faces() { + return invert_faces; +} + +CSGPrimitive::CSGPrimitive() { + invert_faces = false; +} + +///////////////////// + +CSGBrush *CSGMesh::_build_brush() { + + if (!mesh.is_valid()) + return NULL; + + PoolVector<Vector3> vertices; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<Vector2> uvs; + + for (int i = 0; i < mesh->get_surface_count(); i++) { + + if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + + Array arrays = mesh->surface_get_arrays(i); + + PoolVector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX]; + if (avertices.size() == 0) + continue; + + PoolVector<Vector3>::Read vr = avertices.read(); + + PoolVector<Vector3> anormals = arrays[Mesh::ARRAY_NORMAL]; + PoolVector<Vector3>::Read nr; + bool nr_used = false; + if (anormals.size()) { + nr = anormals.read(); + nr_used = true; + } + + PoolVector<Vector2> auvs = arrays[Mesh::ARRAY_TEX_UV]; + PoolVector<Vector2>::Read uvr; + bool uvr_used = false; + if (auvs.size()) { + uvr = auvs.read(); + uvr_used = true; + } + + Ref<Material> mat = mesh->surface_get_material(i); + + PoolVector<int> aindices = arrays[Mesh::ARRAY_INDEX]; + if (aindices.size()) { + int as = vertices.size(); + int is = aindices.size(); + + vertices.resize(as + is); + smooth.resize((as + is) / 3); + materials.resize((as + is) / 3); + uvs.resize(as + is); + + PoolVector<Vector3>::Write vw = vertices.write(); + PoolVector<bool>::Write sw = smooth.write(); + PoolVector<Vector2>::Write uvw = uvs.write(); + PoolVector<Ref<Material> >::Write mw = materials.write(); + + PoolVector<int>::Read ir = aindices.read(); + + for (int j = 0; j < is; j += 3) { + + Vector3 vertex[3]; + Vector3 normal[3]; + Vector2 uv[3]; + + for (int k = 0; k < 3; k++) { + int idx = ir[j + k]; + vertex[k] = vr[idx]; + if (nr_used) { + normal[k] = nr[idx]; + } + if (uvr_used) { + uv[k] = uvr[idx]; + } + } + + bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; + + vw[as + j + 0] = vertex[0]; + vw[as + j + 1] = vertex[1]; + vw[as + j + 2] = vertex[2]; + + uvw[as + j + 0] = uv[0]; + uvw[as + j + 1] = uv[1]; + uvw[as + j + 2] = uv[2]; + + sw[j / 3] = !flat; + mw[j / 3] = mat; + } + } else { + int is = vertices.size(); + int as = avertices.size(); + + vertices.resize(as + is); + smooth.resize((as + is) / 3); + uvs.resize(as + is); + materials.resize((as + is) / 3); + + PoolVector<Vector3>::Write vw = vertices.write(); + PoolVector<bool>::Write sw = smooth.write(); + PoolVector<Vector2>::Write uvw = uvs.write(); + PoolVector<Ref<Material> >::Write mw = materials.write(); + + for (int j = 0; j < is; j += 3) { + + Vector3 vertex[3]; + Vector3 normal[3]; + Vector2 uv[3]; + + for (int k = 0; k < 3; k++) { + vertex[k] = vr[j + k]; + if (nr_used) { + normal[k] = nr[j + k]; + } + if (uvr_used) { + uv[k] = uvr[j + k]; + } + } + + bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; + + vw[as + j + 0] = vertex[0]; + vw[as + j + 1] = vertex[1]; + vw[as + j + 2] = vertex[2]; + + uvw[as + j + 0] = uv[0]; + uvw[as + j + 1] = uv[1]; + uvw[as + j + 2] = uv[2]; + + sw[j / 3] = !flat; + mw[j / 3] = mat; + } + } + } + + //print_line("total vertices? " + itos(vertices.size())); + if (vertices.size() == 0) + return NULL; + + return _create_brush_from_arrays(vertices, uvs, smooth, materials); +} + +void CSGMesh::_mesh_changed() { + _make_dirty(); + update_gizmo(); +} + +void CSGMesh::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMesh::get_mesh); + + ClassDB::bind_method(D_METHOD("_mesh_changed"), &CSGMesh::_mesh_changed); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); +} + +void CSGMesh::set_mesh(const Ref<Mesh> &p_mesh) { + + if (mesh == p_mesh) + return; + if (mesh.is_valid()) { + mesh->disconnect("changed", this, "_mesh_changed"); + } + mesh = p_mesh; + + if (mesh.is_valid()) { + mesh->connect("changed", this, "_mesh_changed"); + } + + _make_dirty(); +} + +Ref<Mesh> CSGMesh::get_mesh() { + return mesh; +} + +//////////////////////////////// + +CSGBrush *CSGSphere::_build_brush() { + + // set our bounding box + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = rings * radial_segments * 2 - radial_segments * 2; + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + for (int i = 1; i <= rings; i++) { + double lat0 = Math_PI * (-0.5 + (double)(i - 1) / rings); + double z0 = Math::sin(lat0); + double zr0 = Math::cos(lat0); + double u0 = double(i - 1) / rings; + + double lat1 = Math_PI * (-0.5 + (double)i / rings); + double z1 = Math::sin(lat1); + double zr1 = Math::cos(lat1); + double u1 = double(i) / rings; + + for (int j = radial_segments; j >= 1; j--) { + + double lng0 = 2 * Math_PI * (double)(j - 1) / radial_segments; + double x0 = Math::cos(lng0); + double y0 = Math::sin(lng0); + double v0 = double(i - 1) / radial_segments; + + double lng1 = 2 * Math_PI * (double)(j) / radial_segments; + double x1 = Math::cos(lng1); + double y1 = Math::sin(lng1); + double v1 = double(i) / radial_segments; + + Vector3 v[4] = { + Vector3(x1 * zr0, z0, y1 * zr0) * radius, + Vector3(x1 * zr1, z1, y1 * zr1) * radius, + Vector3(x0 * zr1, z1, y0 * zr1) * radius, + Vector3(x0 * zr0, z0, y0 * zr0) * radius + }; + + Vector2 u[4] = { + Vector2(v1, u0), + Vector2(v1, u1), + Vector2(v0, u1), + Vector2(v0, u0), + + }; + + if (i < rings) { + + //face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + + if (i > 1) { + //face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGSphere::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphere::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphere::get_radius); + + ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGSphere::set_radial_segments); + ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGSphere::get_radial_segments); + ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphere::set_rings); + ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphere::get_rings); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGSphere::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGSphere::get_smooth_faces); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere::get_material); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGSphere::set_radius(const float p_radius) { + ERR_FAIL_COND(p_radius <= 0); + radius = p_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGSphere::get_radius() const { + return radius; +} + +void CSGSphere::set_radial_segments(const int p_radial_segments) { + radial_segments = p_radial_segments > 4 ? p_radial_segments : 4; + _make_dirty(); + update_gizmo(); +} + +int CSGSphere::get_radial_segments() const { + return radial_segments; +} + +void CSGSphere::set_rings(const int p_rings) { + rings = p_rings > 1 ? p_rings : 1; + _make_dirty(); + update_gizmo(); +} + +int CSGSphere::get_rings() const { + return rings; +} + +void CSGSphere::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGSphere::get_smooth_faces() const { + return smooth_faces; +} + +void CSGSphere::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGSphere::get_material() const { + + return material; +} + +CSGSphere::CSGSphere() { + // defaults + radius = 1.0; + radial_segments = 12; + rings = 6; + smooth_faces = true; +} + +/////////////// + +CSGBrush *CSGBox::_build_brush() { + + // set our bounding box + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = 12; //it's a cube.. + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + Vector3 vertex_mul(width, height, depth); + + { + + for (int i = 0; i < 6; i++) { + + Vector3 face_points[4]; + float uv_points[8] = { 0, 0, 0, 1, 1, 1, 1, 0 }; + + for (int j = 0; j < 4; j++) { + + float v[3]; + v[0] = 1.0; + v[1] = 1 - 2 * ((j >> 1) & 1); + v[2] = v[1] * (1 - 2 * (j & 1)); + + for (int k = 0; k < 3; k++) { + + if (i < 3) + face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + else + face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + } + } + + Vector2 u[4]; + for (int j = 0; j < 4; j++) { + u[j] = Vector2(uv_points[j * 2 + 0], uv_points[j * 2 + 1]); + } + + //face 1 + facesw[face * 3 + 0] = face_points[0] * vertex_mul; + facesw[face * 3 + 1] = face_points[1] * vertex_mul; + facesw[face * 3 + 2] = face_points[2] * vertex_mul; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + //face 1 + facesw[face * 3 + 0] = face_points[2] * vertex_mul; + facesw[face * 3 + 1] = face_points[3] * vertex_mul; + facesw[face * 3 + 2] = face_points[0] * vertex_mul; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGBox::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_width", "width"), &CSGBox::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &CSGBox::get_width); + + ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGBox::set_height); + ClassDB::bind_method(D_METHOD("get_height"), &CSGBox::get_height); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGBox::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &CSGBox::get_depth); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGBox::get_material); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "width", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGBox::set_width(const float p_width) { + width = p_width; + _make_dirty(); + update_gizmo(); +} + +float CSGBox::get_width() const { + return width; +} + +void CSGBox::set_height(const float p_height) { + height = p_height; + _make_dirty(); + update_gizmo(); +} + +float CSGBox::get_height() const { + return height; +} + +void CSGBox::set_depth(const float p_depth) { + depth = p_depth; + _make_dirty(); + update_gizmo(); +} + +float CSGBox::get_depth() const { + return depth; +} + +void CSGBox::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); + update_gizmo(); +} + +Ref<Material> CSGBox::get_material() const { + + return material; +} + +CSGBox::CSGBox() { + // defaults + width = 1.0; + height = 1.0; + depth = 1.0; +} + +/////////////// + +CSGBrush *CSGCylinder::_build_brush() { + + // set our bounding box + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides); + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + Vector3 vertex_mul(radius, height * 0.5, radius); + + { + + for (int i = 0; i < sides; i++) { + + float inc = float(i) / sides; + float inc_n = float((i + 1)) / sides; + + float ang = inc * Math_PI * 2.0; + float ang_n = inc_n * Math_PI * 2.0; + + Vector3 base(Math::cos(ang), 0, Math::sin(ang)); + Vector3 base_n(Math::cos(ang_n), 0, Math::sin(ang_n)); + + Vector3 face_points[4] = { + base + Vector3(0, -1, 0), + base_n + Vector3(0, -1, 0), + base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0), + base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0), + }; + + Vector2 u[4] = { + Vector2(inc, 0), + Vector2(inc_n, 0), + Vector2(inc_n, 1), + Vector2(inc, 1), + }; + + //side face 1 + facesw[face * 3 + 0] = face_points[0] * vertex_mul; + facesw[face * 3 + 1] = face_points[1] * vertex_mul; + facesw[face * 3 + 2] = face_points[2] * vertex_mul; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + if (!cone) { + //side face 2 + facesw[face * 3 + 0] = face_points[2] * vertex_mul; + facesw[face * 3 + 1] = face_points[3] * vertex_mul; + facesw[face * 3 + 2] = face_points[0] * vertex_mul; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + } + + //bottom face 1 + facesw[face * 3 + 0] = face_points[1] * vertex_mul; + facesw[face * 3 + 1] = face_points[0] * vertex_mul; + facesw[face * 3 + 2] = Vector3(0, -1, 0) * vertex_mul; + + uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 2] = Vector2(0.5, 0.5); + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + + if (!cone) { + //top face 1 + facesw[face * 3 + 0] = face_points[3] * vertex_mul; + facesw[face * 3 + 1] = face_points[2] * vertex_mul; + facesw[face * 3 + 2] = Vector3(0, 1, 0) * vertex_mul; + + uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 2] = Vector2(0.5, 0.5); + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + } + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGCylinder::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinder::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinder::get_radius); + + ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCylinder::set_height); + ClassDB::bind_method(D_METHOD("get_height"), &CSGCylinder::get_height); + + ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGCylinder::set_sides); + ClassDB::bind_method(D_METHOD("get_sides"), &CSGCylinder::get_sides); + + ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinder::set_cone); + ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinder::is_cone); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGCylinder::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGCylinder::get_material); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder::get_smooth_faces); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGCylinder::set_radius(const float p_radius) { + radius = p_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGCylinder::get_radius() const { + return radius; +} + +void CSGCylinder::set_height(const float p_height) { + height = p_height; + _make_dirty(); + update_gizmo(); +} + +float CSGCylinder::get_height() const { + return height; +} + +void CSGCylinder::set_sides(const int p_sides) { + ERR_FAIL_COND(p_sides < 3); + sides = p_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGCylinder::get_sides() const { + return sides; +} + +void CSGCylinder::set_cone(const bool p_cone) { + cone = p_cone; + _make_dirty(); + update_gizmo(); +} + +bool CSGCylinder::is_cone() const { + return cone; +} + +void CSGCylinder::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGCylinder::get_smooth_faces() const { + return smooth_faces; +} + +void CSGCylinder::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGCylinder::get_material() const { + + return material; +} + +CSGCylinder::CSGCylinder() { + // defaults + radius = 1.0; + height = 1.0; + sides = 8; + cone = false; + smooth_faces = true; +} + +/////////////// + +CSGBrush *CSGTorus::_build_brush() { + + // set our bounding box + + float min_radius = inner_radius; + float max_radius = outer_radius; + + if (min_radius == max_radius) + return NULL; //sorry, can't + + if (min_radius > max_radius) { + SWAP(min_radius, max_radius); + } + + float radius = (max_radius - min_radius) * 0.5; + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = ring_sides * sides * 2; + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + { + + for (int i = 0; i < sides; i++) { + + float inci = float(i) / sides; + float inci_n = float((i + 1)) / sides; + + float angi = inci * Math_PI * 2.0; + float angi_n = inci_n * Math_PI * 2.0; + + Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi)); + Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n)); + + for (int j = 0; j < ring_sides; j++) { + + float incj = float(j) / ring_sides; + float incj_n = float((j + 1)) / ring_sides; + + float angj = incj * Math_PI * 2.0; + float angj_n = incj_n * Math_PI * 2.0; + + Vector2 normalj = Vector2(Math::cos(angj), Math::sin(angj)) * radius + Vector2(min_radius + radius, 0); + Vector2 normalj_n = Vector2(Math::cos(angj_n), Math::sin(angj_n)) * radius + Vector2(min_radius + radius, 0); + + Vector3 face_points[4] = { + Vector3(normali.x * normalj.x, normalj.y, normali.z * normalj.x), + Vector3(normali.x * normalj_n.x, normalj_n.y, normali.z * normalj_n.x), + Vector3(normali_n.x * normalj_n.x, normalj_n.y, normali_n.z * normalj_n.x), + Vector3(normali_n.x * normalj.x, normalj.y, normali_n.z * normalj.x) + }; + + Vector2 u[4] = { + Vector2(inci, incj), + Vector2(inci, incj_n), + Vector2(inci_n, incj_n), + Vector2(inci_n, incj), + }; + + // face 1 + facesw[face * 3 + 0] = face_points[0]; + facesw[face * 3 + 1] = face_points[2]; + facesw[face * 3 + 2] = face_points[1]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[2]; + uvsw[face * 3 + 2] = u[1]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + //face 2 + facesw[face * 3 + 0] = face_points[3]; + facesw[face * 3 + 1] = face_points[2]; + facesw[face * 3 + 2] = face_points[0]; + + uvsw[face * 3 + 0] = u[3]; + uvsw[face * 3 + 1] = u[2]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + } + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGTorus::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorus::set_inner_radius); + ClassDB::bind_method(D_METHOD("get_inner_radius"), &CSGTorus::get_inner_radius); + + ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &CSGTorus::set_outer_radius); + ClassDB::bind_method(D_METHOD("get_outer_radius"), &CSGTorus::get_outer_radius); + + ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorus::set_sides); + ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorus::get_sides); + + ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorus::set_ring_sides); + ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorus::get_ring_sides); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGTorus::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGTorus::get_material); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus::get_smooth_faces); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_inner_radius", "get_inner_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_outer_radius", "get_outer_radius"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGTorus::set_inner_radius(const float p_inner_radius) { + inner_radius = p_inner_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGTorus::get_inner_radius() const { + return inner_radius; +} + +void CSGTorus::set_outer_radius(const float p_outer_radius) { + outer_radius = p_outer_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGTorus::get_outer_radius() const { + return outer_radius; +} + +void CSGTorus::set_sides(const int p_sides) { + ERR_FAIL_COND(p_sides < 3); + sides = p_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGTorus::get_sides() const { + return sides; +} + +void CSGTorus::set_ring_sides(const int p_ring_sides) { + ERR_FAIL_COND(p_ring_sides < 3); + ring_sides = p_ring_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGTorus::get_ring_sides() const { + return ring_sides; +} + +void CSGTorus::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGTorus::get_smooth_faces() const { + return smooth_faces; +} + +void CSGTorus::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGTorus::get_material() const { + + return material; +} + +CSGTorus::CSGTorus() { + // defaults + inner_radius = 2.0; + outer_radius = 3.0; + sides = 8; + ring_sides = 6; + smooth_faces = true; +} + +/////////////// + +CSGBrush *CSGPolygon::_build_brush() { + + // set our bounding box + + if (polygon.size() < 3) + return NULL; + + Vector<Point2> final_polygon = polygon; + + if (Triangulate::get_area(final_polygon) > 0) { + final_polygon.invert(); + } + + Vector<int> triangles = Geometry::triangulate_polygon(final_polygon); + + if (triangles.size() < 3) + return NULL; + + Path *path = NULL; + Ref<Curve3D> curve; + + if (mode == MODE_PATH) { + if (!has_node(path_node)) + return NULL; + Node *n = get_node(path_node); + if (!n) + return NULL; + path = Object::cast_to<Path>(n); + if (!path) + return NULL; + + if (path != path_cache) { + if (path_cache) { + path_cache->disconnect("tree_exited", this, "_path_exited"); + path_cache->disconnect("curve_changed", this, "_path_changed"); + path_cache = NULL; + } + + path_cache = path; + + if (path_cache) { + path_cache->connect("tree_exited", this, "_path_exited"); + path_cache->connect("curve_changed", this, "_path_changed"); + path_cache = NULL; + } + } + curve = path->get_curve(); + if (curve.is_null()) + return NULL; + if (curve->get_baked_length() <= 0) + return NULL; + } + CSGBrush *brush = memnew(CSGBrush); + + int face_count; + + switch (mode) { + case MODE_DEPTH: face_count = triangles.size() * 2 / 3 + (final_polygon.size()) * 2; break; + case MODE_SPIN: face_count = (spin_degrees < 360 ? triangles.size() * 2 / 3 : 0) + (final_polygon.size()) * 2 * spin_sides; break; + case MODE_PATH: { + float bl = curve->get_baked_length(); + int splits = MAX(2, Math::ceil(bl / path_interval)); + face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2; + } break; + } + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + AABB aabb; //must be computed + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + switch (mode) { + case MODE_DEPTH: { + + //add triangles, front and back + for (int i = 0; i < 2; i++) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, i == 0 ? 1 : 2, i == 0 ? 2 : 1 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + if (i == 0) { + v.z -= depth; + } + facesw[face * 3 + k] = v; + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + //add triangles for depth + for (int i = 0; i < final_polygon.size(); i++) { + + int i_n = (i + 1) % final_polygon.size(); + + Vector3 v[4] = { + Vector3(final_polygon[i].x, final_polygon[i].y, -depth), + Vector3(final_polygon[i_n].x, final_polygon[i_n].y, -depth), + Vector3(final_polygon[i_n].x, final_polygon[i_n].y, 0), + Vector3(final_polygon[i].x, final_polygon[i].y, 0), + }; + + Vector2 u[4] = { + Vector2(0, 0), + Vector2(0, 1), + Vector2(1, 1), + Vector2(1, 0) + }; + + // face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + // face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + + } break; + case MODE_SPIN: { + + for (int i = 0; i < spin_sides; i++) { + + float inci = float(i) / spin_sides; + float inci_n = float((i + 1)) / spin_sides; + + float angi = -(inci * spin_degrees / 360.0) * Math_PI * 2.0; + float angi_n = -(inci_n * spin_degrees / 360.0) * Math_PI * 2.0; + + Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi)); + Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n)); + + //add triangles for depth + for (int j = 0; j < final_polygon.size(); j++) { + + int j_n = (j + 1) % final_polygon.size(); + + Vector3 v[4] = { + Vector3(normali.x * final_polygon[j].x, final_polygon[j].y, normali.z * final_polygon[j].x), + Vector3(normali.x * final_polygon[j_n].x, final_polygon[j_n].y, normali.z * final_polygon[j_n].x), + Vector3(normali_n.x * final_polygon[j_n].x, final_polygon[j_n].y, normali_n.z * final_polygon[j_n].x), + Vector3(normali_n.x * final_polygon[j].x, final_polygon[j].y, normali_n.z * final_polygon[j].x), + }; + + Vector2 u[4] = { + Vector2(0, 0), + Vector2(0, 1), + Vector2(1, 1), + Vector2(1, 0) + }; + + // face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[2]; + facesw[face * 3 + 2] = v[1]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[2]; + uvsw[face * 3 + 2] = u[1]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + // face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[0]; + facesw[face * 3 + 2] = v[3]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[0]; + uvsw[face * 3 + 2] = u[3]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + + if (i == 0 && spin_degrees < 360) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 2, 1 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + facesw[face * 3 + k] = v; + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + if (i == spin_sides - 1 && spin_degrees < 360) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 1, 2 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(normali_n.x * p.x, p.y, normali_n.z * p.x); + facesw[face * 3 + k] = v; + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + } + } break; + case MODE_PATH: { + + float bl = curve->get_baked_length(); + int splits = MAX(2, Math::ceil(bl / path_interval)); + + Transform path_to_this = get_global_transform().affine_inverse() * path->get_global_transform(); + + Transform prev_xf; + + Vector3 lookat_dir; + + if (path_rotation == PATH_ROTATION_POLYGON) { + lookat_dir = (path->get_global_transform().affine_inverse() * get_global_transform()).xform(Vector3(0, 0, -1)); + } else { + Vector3 p1, p2; + p1 = curve->interpolate_baked(0); + p2 = curve->interpolate_baked(0.1); + lookat_dir = (p2 - p1).normalized(); + } + + for (int i = 0; i <= splits; i++) { + + float ofs = i * path_interval; + + Transform xf; + xf.origin = curve->interpolate_baked(ofs); + + Vector3 local_dir; + + if (path_rotation == PATH_ROTATION_PATH_FOLLOW && ofs > 0) { + //before end + Vector3 p1 = curve->interpolate_baked(ofs - 0.1); + Vector3 p2 = curve->interpolate_baked(ofs); + local_dir = (p2 - p1).normalized(); + + } else { + local_dir = lookat_dir; + } + + xf = xf.looking_at(xf.origin + local_dir, Vector3(0, 1, 0)); + Basis rot(Vector3(0, 0, 1), curve->interpolate_baked_tilt(ofs)); + + xf = xf * rot; //post mult + + xf = path_to_this * xf; + + if (i > 0) { + //put triangles where they belong + //add triangles for depth + for (int j = 0; j < final_polygon.size(); j++) { + + int j_n = (j + 1) % final_polygon.size(); + + Vector3 v[4] = { + prev_xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)), + prev_xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)), + xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)), + xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)), + }; + + Vector2 u[4] = { + Vector2(0, 0), + Vector2(0, 1), + Vector2(1, 1), + Vector2(1, 0) + }; + + // face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + // face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + } + + if (i == 0) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 1, 2 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + facesw[face * 3 + k] = xf.xform(v); + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + if (i == splits) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 2, 1 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + facesw[face * 3 + k] = xf.xform(v); + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + prev_xf = xf; + } + + } break; + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + for (int i = 0; i < face_count * 3; i++) { + if (i == 0) { + aabb.position = facesw[i]; + } else { + aabb.expand_to(facesw[i]); + } + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGPolygon::_notification(int p_what) { + if (p_what == NOTIFICATION_EXIT_TREE) { + if (path_cache) { + path_cache->disconnect("tree_exited", this, "_path_exited"); + path_cache->disconnect("curve_changed", this, "_path_changed"); + path_cache = NULL; + } + } +} + +void CSGPolygon::_validate_property(PropertyInfo &property) const { + if (property.name.begins_with("spin") && mode != MODE_SPIN) { + property.usage = 0; + } + if (property.name.begins_with("path") && mode != MODE_PATH) { + property.usage = 0; + } + if (property.name == "depth" && mode != MODE_DEPTH) { + property.usage = 0; + } + + CSGShape::_validate_property(property); +} + +void CSGPolygon::_path_changed() { + _make_dirty(); + update_gizmo(); +} + +void CSGPolygon::_path_exited() { + path_cache = NULL; +} + +void CSGPolygon::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CSGPolygon::set_polygon); + ClassDB::bind_method(D_METHOD("get_polygon"), &CSGPolygon::get_polygon); + + ClassDB::bind_method(D_METHOD("set_mode", "mode"), &CSGPolygon::set_mode); + ClassDB::bind_method(D_METHOD("get_mode"), &CSGPolygon::get_mode); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGPolygon::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &CSGPolygon::get_depth); + + ClassDB::bind_method(D_METHOD("set_spin_degrees", "degrees"), &CSGPolygon::set_spin_degrees); + ClassDB::bind_method(D_METHOD("get_spin_degrees"), &CSGPolygon::get_spin_degrees); + + ClassDB::bind_method(D_METHOD("set_spin_sides", "spin_sides"), &CSGPolygon::set_spin_sides); + ClassDB::bind_method(D_METHOD("get_spin_sides"), &CSGPolygon::get_spin_sides); + + ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon::set_path_node); + ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon::get_path_node); + + ClassDB::bind_method(D_METHOD("set_path_interval", "distance"), &CSGPolygon::set_path_interval); + ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon::get_path_interval); + + ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygon::set_path_rotation); + ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon::get_path_rotation); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon::get_material); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPolygon::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPolygon::get_smooth_faces); + + ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CSGPolygon::_is_editable_3d_polygon); + ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &CSGPolygon::_has_editable_3d_polygon_no_depth); + + ClassDB::bind_method(D_METHOD("_path_exited"), &CSGPolygon::_path_exited); + ClassDB::bind_method(D_METHOD("_path_changed"), &CSGPolygon::_path_changed); + + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node"), "set_path_node", "get_path_node"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_interval", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_path_interval", "get_path_interval"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); + + BIND_ENUM_CONSTANT(MODE_DEPTH); + BIND_ENUM_CONSTANT(MODE_SPIN); + BIND_ENUM_CONSTANT(MODE_PATH); + + BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON); + BIND_ENUM_CONSTANT(PATH_ROTATION_PATH); + BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW); +} + +void CSGPolygon::set_polygon(const Vector<Vector2> &p_polygon) { + polygon = p_polygon; + _make_dirty(); + update_gizmo(); +} + +Vector<Vector2> CSGPolygon::get_polygon() const { + return polygon; +} + +void CSGPolygon::set_mode(Mode p_mode) { + mode = p_mode; + _make_dirty(); + update_gizmo(); + _change_notify(); +} + +CSGPolygon::Mode CSGPolygon::get_mode() const { + return mode; +} + +void CSGPolygon::set_depth(const float p_depth) { + ERR_FAIL_COND(p_depth < 0.001); + depth = p_depth; + _make_dirty(); + update_gizmo(); +} + +float CSGPolygon::get_depth() const { + return depth; +} + +void CSGPolygon::set_spin_degrees(const float p_spin_degrees) { + ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360); + spin_degrees = p_spin_degrees; + _make_dirty(); + update_gizmo(); +} + +float CSGPolygon::get_spin_degrees() const { + return spin_degrees; +} + +void CSGPolygon::set_spin_sides(const int p_spin_sides) { + ERR_FAIL_COND(p_spin_sides < 3); + spin_sides = p_spin_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGPolygon::get_spin_sides() const { + return spin_sides; +} + +void CSGPolygon::set_path_node(const NodePath &p_path) { + path_node = p_path; + _make_dirty(); + update_gizmo(); +} + +NodePath CSGPolygon::get_path_node() const { + return path_node; +} + +void CSGPolygon::set_path_interval(float p_interval) { + ERR_FAIL_COND(p_interval < 0.001); + path_interval = p_interval; + _make_dirty(); + update_gizmo(); +} +float CSGPolygon::get_path_interval() const { + return path_interval; +} + +void CSGPolygon::set_path_rotation(PathRotation p_rotation) { + path_rotation = p_rotation; + _make_dirty(); + update_gizmo(); +} + +CSGPolygon::PathRotation CSGPolygon::get_path_rotation() const { + return path_rotation; +} + +void CSGPolygon::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGPolygon::get_smooth_faces() const { + return smooth_faces; +} + +void CSGPolygon::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGPolygon::get_material() const { + + return material; +} + +bool CSGPolygon::_is_editable_3d_polygon() const { + return true; +} + +bool CSGPolygon::_has_editable_3d_polygon_no_depth() const { + return true; +} + +CSGPolygon::CSGPolygon() { + // defaults + mode = MODE_DEPTH; + polygon.push_back(Vector2(0, 0)); + polygon.push_back(Vector2(0, 1)); + polygon.push_back(Vector2(1, 1)); + polygon.push_back(Vector2(1, 0)); + depth = 1.0; + spin_degrees = 360; + spin_sides = 8; + smooth_faces = false; + path_interval = 1; + path_rotation = PATH_ROTATION_PATH; + path_cache = NULL; +} diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h new file mode 100644 index 0000000000..c1d2cce606 --- /dev/null +++ b/modules/csg/csg_shape.h @@ -0,0 +1,360 @@ +#ifndef CSG_SHAPE_H +#define CSG_SHAPE_H + +#define CSGJS_HEADER_ONLY + +#include "csg.h" +#include "scene/3d/visual_instance.h" +#include "scene/resources/concave_polygon_shape.h" + +class CSGShape : public VisualInstance { + GDCLASS(CSGShape, VisualInstance); + +public: + enum Operation { + OPERATION_UNION, + OPERATION_INTERSECTION, + OPERATION_SUBTRACTION, + + }; + +private: + Operation operation; + CSGShape *parent; + + CSGBrush *brush; + + AABB node_aabb; + + bool dirty; + float snap; + + bool use_collision; + Ref<ConcavePolygonShape> root_collision_shape; + RID root_collision_instance; + + Ref<ArrayMesh> root_mesh; + + struct Vector3Hasher { + _ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const { + uint32_t h = hash_djb2_one_float(p_vec3.x); + h = hash_djb2_one_float(p_vec3.y, h); + h = hash_djb2_one_float(p_vec3.z, h); + return h; + } + }; + + struct ShapeUpdateSurface { + PoolVector<Vector3> vertices; + PoolVector<Vector3> normals; + PoolVector<Vector2> uvs; + Ref<Material> material; + int last_added; + + PoolVector<Vector3>::Write verticesw; + PoolVector<Vector3>::Write normalsw; + PoolVector<Vector2>::Write uvsw; + }; + + void _update_shape(); + +protected: + void _notification(int p_what); + virtual CSGBrush *_build_brush() = 0; + void _make_dirty(); + + static void _bind_methods(); + + friend class CSGCombiner; + CSGBrush *_get_brush(); + + virtual void _validate_property(PropertyInfo &property) const; + +public: + void set_operation(Operation p_operation); + Operation get_operation() const; + + virtual PoolVector<Vector3> get_brush_faces(); + + virtual AABB get_aabb() const; + virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const; + + void set_use_collision(bool p_enable); + bool is_using_collision() const; + + void set_snap(float p_snap); + float get_snap() const; + + bool is_root_shape() const; + CSGShape(); + ~CSGShape(); +}; + +VARIANT_ENUM_CAST(CSGShape::Operation) + +class CSGCombiner : public CSGShape { + GDCLASS(CSGCombiner, CSGShape) +private: + virtual CSGBrush *_build_brush(); + +public: + CSGCombiner(); +}; + +class CSGPrimitive : public CSGShape { + GDCLASS(CSGPrimitive, CSGShape) + +private: + bool invert_faces; + +protected: + CSGBrush *_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials); + static void _bind_methods(); + +public: + void set_invert_faces(bool p_invert); + bool is_inverting_faces(); + + CSGPrimitive(); +}; + +class CSGMesh : public CSGPrimitive { + GDCLASS(CSGMesh, CSGPrimitive) + + virtual CSGBrush *_build_brush(); + + Ref<Mesh> mesh; + + void _mesh_changed(); + +protected: + static void _bind_methods(); + +public: + void set_mesh(const Ref<Mesh> &p_mesh); + Ref<Mesh> get_mesh(); +}; + +class CSGSphere : public CSGPrimitive { + + GDCLASS(CSGSphere, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + bool smooth_faces; + float radius; + int radial_segments; + int rings; + +protected: + static void _bind_methods(); + +public: + void set_radius(const float p_radius); + float get_radius() const; + + void set_radial_segments(const int p_radial_segments); + int get_radial_segments() const; + + void set_rings(const int p_rings); + int get_rings() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + CSGSphere(); +}; + +class CSGBox : public CSGPrimitive { + + GDCLASS(CSGBox, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + float width; + float height; + float depth; + +protected: + static void _bind_methods(); + +public: + void set_width(const float p_width); + float get_width() const; + + void set_height(const float p_height); + float get_height() const; + + void set_depth(const float p_depth); + float get_depth() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGBox(); +}; + +class CSGCylinder : public CSGPrimitive { + + GDCLASS(CSGCylinder, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + float radius; + float height; + int sides; + bool cone; + bool smooth_faces; + +protected: + static void _bind_methods(); + +public: + void set_radius(const float p_radius); + float get_radius() const; + + void set_height(const float p_height); + float get_height() const; + + void set_sides(const int p_sides); + int get_sides() const; + + void set_cone(const bool p_cone); + bool is_cone() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGCylinder(); +}; + +class CSGTorus : public CSGPrimitive { + + GDCLASS(CSGTorus, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + float inner_radius; + float outer_radius; + int sides; + int ring_sides; + bool smooth_faces; + +protected: + static void _bind_methods(); + +public: + void set_inner_radius(const float p_inner_radius); + float get_inner_radius() const; + + void set_outer_radius(const float p_outer_radius); + float get_outer_radius() const; + + void set_sides(const int p_sides); + int get_sides() const; + + void set_ring_sides(const int p_ring_sides); + int get_ring_sides() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGTorus(); +}; + +class CSGPolygon : public CSGPrimitive { + + GDCLASS(CSGPolygon, CSGPrimitive) + +public: + enum Mode { + MODE_DEPTH, + MODE_SPIN, + MODE_PATH + }; + + enum PathRotation { + PATH_ROTATION_POLYGON, + PATH_ROTATION_PATH, + PATH_ROTATION_PATH_FOLLOW, + }; + +private: + virtual CSGBrush *_build_brush(); + + Vector<Vector2> polygon; + Ref<Material> material; + + Mode mode; + + float depth; + + float spin_degrees; + int spin_sides; + + NodePath path_node; + float path_interval; + PathRotation path_rotation; + + Node *path_cache; + + bool smooth_faces; + + bool _is_editable_3d_polygon() const; + bool _has_editable_3d_polygon_no_depth() const; + + void _path_changed(); + void _path_exited(); + +protected: + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); + +public: + void set_polygon(const Vector<Vector2> &p_polygon); + Vector<Vector2> get_polygon() const; + + void set_mode(Mode p_mode); + Mode get_mode() const; + + void set_depth(float p_depth); + float get_depth() const; + + void set_spin_degrees(float p_spin_degrees); + float get_spin_degrees() const; + + void set_spin_sides(int p_sides); + int get_spin_sides() const; + + void set_path_node(const NodePath &p_path); + NodePath get_path_node() const; + + void set_path_interval(float p_interval); + float get_path_interval() const; + + void set_path_rotation(PathRotation p_rotation); + PathRotation get_path_rotation() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGPolygon(); +}; + +VARIANT_ENUM_CAST(CSGPolygon::Mode) +VARIANT_ENUM_CAST(CSGPolygon::PathRotation) + +#endif // CSG_SHAPE_H diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp new file mode 100644 index 0000000000..020724ee59 --- /dev/null +++ b/modules/csg/register_types.cpp @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "csg_shape.h" +#include "csg_gizmos.h" + +void register_csg_types() { + +#ifndef _3D_DISABLED + + ClassDB::register_virtual_class<CSGShape>(); + ClassDB::register_virtual_class<CSGPrimitive>(); + ClassDB::register_class<CSGMesh>(); + ClassDB::register_class<CSGSphere>(); + ClassDB::register_class<CSGBox>(); + ClassDB::register_class<CSGCylinder>(); + ClassDB::register_class<CSGTorus>(); + ClassDB::register_class<CSGPolygon>(); + ClassDB::register_class<CSGCombiner>(); + +#ifdef TOOLS_ENABLED + EditorPlugins::add_by_type<EditorPluginCSG>(); +#endif +#endif + +} + +void unregister_csg_types() { + +} diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h new file mode 100644 index 0000000000..49490d31d3 --- /dev/null +++ b/modules/csg/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +void register_csg_types(); +void unregister_csg_types(); diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index 55a7a1ac77..7bee63019b 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -1,12 +1,14 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1-dev"> +<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1"> <brief_description> PacketPeer implementation using the ENet library. </brief_description> <description> - A connection (or a listening server) that should be passed to [method SceneTree.set_network_peer]. Socket events can be handled by connecting to [SceneTree] signals. + A PacketPeer implementation that should be passed to [method SceneTree.set_network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals. </description> <tutorials> + http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html + http://enet.bespin.org/usergroup0.html </tutorials> <demos> </demos> @@ -15,6 +17,7 @@ <return type="void"> </return> <description> + Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server. </description> </method> <method name="create_client"> @@ -29,7 +32,7 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> - Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code]. + Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code]. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code]. The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created. </description> </method> <method name="create_server"> @@ -44,7 +47,7 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> - Create server that listens to connections via [code]port[/code]. + Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]*[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [code]OK[/code] if a server was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the server could not be created. </description> </method> <method name="set_bind_ip"> @@ -53,23 +56,30 @@ <argument index="0" name="ip" type="String"> </argument> <description> + The IP used when creating a server. This is set to the wildcard [code]*[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code]. </description> </method> </methods> <members> <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode"> + The compression method used for network packets. Default is no compression. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. </member> </members> <constants> <constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> + No compression. </constant> <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode"> + ENet's buildin range encoding. </constant> <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode"> + FastLZ compression. </constant> <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode"> + zlib compression. </constant> <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode"> + ZStandard compression. </constant> </constants> </class> diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index f3f4acd768..bd76c766a0 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "networked_multiplayer_enet.h" +#include "io/ip.h" #include "io/marshalls.h" #include "os/os.h" @@ -57,6 +58,10 @@ int NetworkedMultiplayerENet::get_packet_peer() const { Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) { ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_max_clients < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER); ENetAddress address; @@ -79,8 +84,8 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int host = enet_host_create(&address /* the address to bind the server host to */, p_max_clients /* allow up to 32 clients and/or outgoing connections */, SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, - p_in_bandwidth /* assume any amount of incoming bandwidth */, - p_out_bandwidth /* assume any amount of outgoing bandwidth */); + p_in_bandwidth /* limit incoming bandwith if > 0 */, + p_out_bandwidth /* limit outgoing bandwith if > 0 */); ERR_FAIL_COND_V(!host, ERR_CANT_CREATE); @@ -92,35 +97,76 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int connection_status = CONNECTION_CONNECTED; return OK; } -Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth) { +Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth, int p_client_port) { ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_client_port < 0 || p_client_port > 65535, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER); - host = enet_host_create(NULL /* create a client host */, - 1 /* only allow 1 outgoing connection */, - SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, - p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */, - p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */); + if (p_client_port != 0) { + ENetAddress c_client; + +#ifdef GODOT_ENET + if (bind_ip.is_wildcard()) { + c_client.wildcard = 1; + } else { + enet_address_set_ip(&c_client, bind_ip.get_ipv6(), 16); + } +#else + if (bind_ip.is_wildcard()) { + c_client.host = 0; + } else { + ERR_FAIL_COND_V(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER); + c_client.host = *(uint32_t *)bind_ip.get_ipv4(); + } +#endif + + c_client.port = p_client_port; + + host = enet_host_create(&c_client /* create a client host */, + 1 /* only allow 1 outgoing connection */, + SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, + p_in_bandwidth /* limit incoming bandwith if > 0 */, + p_out_bandwidth /* limit outgoing bandwith if > 0 */); + } else { + host = enet_host_create(NULL /* create a client host */, + 1 /* only allow 1 outgoing connection */, + SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */, + p_in_bandwidth /* limit incoming bandwith if > 0 */, + p_out_bandwidth /* limit outgoing bandwith if > 0 */); + } ERR_FAIL_COND_V(!host, ERR_CANT_CREATE); _setup_compressor(); + IP_Address ip; + if (p_address.is_valid_ip_address()) { + ip = p_address; + } else { +#ifdef GODOT_ENET + ip = IP::get_singleton()->resolve_hostname(p_address); +#else + ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4); +#endif + + ERR_FAIL_COND_V(!ip.is_valid(), ERR_CANT_RESOLVE); + } + ENetAddress address; #ifdef GODOT_ENET - enet_address_set_ip(&address, p_ip.get_ipv6(), 16); + enet_address_set_ip(&address, ip.get_ipv6(), 16); #else - ERR_FAIL_COND_V(!p_ip.is_ipv4(), ERR_INVALID_PARAMETER); - address.host = *(uint32_t *)p_ip.get_ipv4(); + ERR_FAIL_COND_V(!ip.is_ipv4(), ERR_INVALID_PARAMETER); + address.host = *(uint32_t *)ip.get_ipv4(); #endif address.port = p_port; - //enet_address_set_host (& address, "localhost"); - //address.port = p_port; - unique_id = _gen_unique_id(); - /* Initiate the connection, allocating the enough channels */ + // Initiate connection, allocating enough channels ENetPeer *peer = enet_host_connect(host, &address, SYSCH_MAX, unique_id); if (peer == NULL) { @@ -128,7 +174,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port ERR_FAIL_COND_V(!peer, ERR_CANT_CREATE); } - //technically safe to ignore the peer or anything else. + // Technically safe to ignore the peer or anything else. connection_status = CONNECTION_CONNECTING; active = true; @@ -148,13 +194,13 @@ void NetworkedMultiplayerENet::poll() { /* Wait up to 1000 milliseconds for an event. */ while (true) { - if (!host || !active) //might have been disconnected while emitting a notification + if (!host || !active) // Might have been disconnected while emitting a notification return; int ret = enet_host_service(host, &event, 1); if (ret < 0) { - //error, do something? + // Error, do something? break; } else if (ret == 0) { break; @@ -162,7 +208,7 @@ void NetworkedMultiplayerENet::poll() { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: { - /* Store any relevant client information here. */ + // Store any relevant client information here. if (server && refuse_connections) { enet_peer_reset(event.peer); @@ -172,7 +218,7 @@ void NetworkedMultiplayerENet::poll() { int *new_id = memnew(int); *new_id = event.data; - if (*new_id == 0) { //data zero is sent by server (enet won't let you configure this). Server is always 1 + if (*new_id == 0) { // Data zero is sent by server (enet won't let you configure this). Server is always 1. *new_id = 1; } @@ -180,22 +226,22 @@ void NetworkedMultiplayerENet::poll() { peer_map[*new_id] = event.peer; - connection_status = CONNECTION_CONNECTED; //if connecting, this means it connected t something! + connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something! emit_signal("peer_connected", *new_id); if (server) { - //someone connected, let it know of all the peers available + // Someone connected, notify all the peers available for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { if (E->key() == *new_id) continue; - //send existing peers to new peer + // Send existing peers to new peer ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); encode_uint32(E->key(), &packet->data[4]); enet_peer_send(event.peer, SYSCH_CONFIG, packet); - //send the new peer to existing peers + // Send the new peer to existing peers packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); encode_uint32(*new_id, &packet->data[4]); @@ -209,7 +255,7 @@ void NetworkedMultiplayerENet::poll() { } break; case ENET_EVENT_TYPE_DISCONNECT: { - /* Reset the peer's client information. */ + // Reset the peer's client information. int *id = (int *)event.peer->data; @@ -220,12 +266,12 @@ void NetworkedMultiplayerENet::poll() { } else { if (server) { - //someone disconnected, let it know to everyone else + // Someone disconnected, notify everyone else for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { if (E->key() == *id) continue; - //send the new peer to existing peers + ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); encode_uint32(*id, &packet->data[4]); @@ -246,7 +292,7 @@ void NetworkedMultiplayerENet::poll() { case ENET_EVENT_TYPE_RECEIVE: { if (event.channelID == SYSCH_CONFIG) { - //some config message + // Some config message ERR_CONTINUE(event.packet->dataLength < 8); // Only server can send config messages @@ -292,13 +338,13 @@ void NetworkedMultiplayerENet::poll() { packet.from = *id; if (target == 0) { - //re-send the everyone but sender :| + // Re-send to everyone but sender :| incoming_packets.push_back(packet); - //and make copies for sending + // And make copies for sending for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (uint32_t(E->key()) == source) //do not resend to self + if (uint32_t(E->key()) == source) // Do not resend to self continue; ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); @@ -307,12 +353,12 @@ void NetworkedMultiplayerENet::poll() { } } else if (target < 0) { - //to all but one + // To all but one - //and make copies for sending + // And make copies for sending for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (uint32_t(E->key()) == source || E->key() == -target) //do not resend to self, also do not send to excluded + if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded continue; ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); @@ -321,18 +367,18 @@ void NetworkedMultiplayerENet::poll() { } if (-target != 1) { - //server is not excluded + // Server is not excluded incoming_packets.push_back(packet); } else { - //server is excluded, erase packet + // Server is excluded, erase packet enet_packet_destroy(packet.packet); } } else if (target == 1) { - //to myself and only myself + // To myself and only myself incoming_packets.push_back(packet); } else { - //to someone else, specifically + // To someone else, specifically ERR_CONTINUE(!peer_map.has(target)); enet_peer_send(peer_map[target], event.channelID, packet.packet); } @@ -341,14 +387,14 @@ void NetworkedMultiplayerENet::poll() { incoming_packets.push_back(packet); } - //destroy packet later.. + // Destroy packet later } else { ERR_CONTINUE(true); } } break; case ENET_EVENT_TYPE_NONE: { - //do nothing + // Do nothing } break; } } @@ -360,10 +406,10 @@ bool NetworkedMultiplayerENet::is_server() const { return server; } -void NetworkedMultiplayerENet::close_connection() { +void NetworkedMultiplayerENet::close_connection(uint32_t wait_usec) { - if (!active) - return; + ERR_FAIL_COND(!active); + ERR_FAIL_COND(wait_usec < 0); _pop_current_packet(); @@ -377,20 +423,54 @@ void NetworkedMultiplayerENet::close_connection() { if (peers_disconnected) { enet_host_flush(host); - OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send + + if (wait_usec > 0) { + OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send + } } enet_host_destroy(host); active = false; incoming_packets.clear(); - unique_id = 1; //server is 1 + unique_id = 1; // Server is 1 connection_status = CONNECTION_DISCONNECTED; } +void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) { + + ERR_FAIL_COND(!active); + ERR_FAIL_COND(!is_server()); + ERR_FAIL_COND(!peer_map.has(p_peer)) + + if (now) { + enet_peer_disconnect_now(peer_map[p_peer], 0); + + // enet_peer_disconnect_now doesn't generate ENET_EVENT_TYPE_DISCONNECT, + // notify everyone else, send disconnect signal & remove from peer_map like in poll() + + for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { + + if (E->key() == p_peer) + continue; + + ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); + encode_uint32(p_peer, &packet->data[4]); + enet_peer_send(E->get(), SYSCH_CONFIG, packet); + } + + emit_signal("peer_disconnected", p_peer); + peer_map.erase(p_peer); + } else { + enet_peer_disconnect_later(peer_map[p_peer], 0); + } +} + int NetworkedMultiplayerENet::get_available_packet_count() const { return incoming_packets.size(); } + Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { ERR_FAIL_COND_V(incoming_packets.size() == 0, ERR_UNAVAILABLE); @@ -405,6 +485,7 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buff return OK; } + Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!active, ERR_UNCONFIGURED); @@ -440,9 +521,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer } ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags); - encode_uint32(unique_id, &packet->data[0]); //source ID - encode_uint32(target_peer, &packet->data[4]); //dest ID - encode_uint32(packet_flags, &packet->data[8]); //dest ID + encode_uint32(unique_id, &packet->data[0]); // Source ID + encode_uint32(target_peer, &packet->data[4]); // Dest ID + encode_uint32(packet_flags, &packet->data[8]); // Dest ID copymem(&packet->data[12], p_buffer, p_buffer_size); if (server) { @@ -450,14 +531,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer if (target_peer == 0) { enet_host_broadcast(host, channel, packet); } else if (target_peer < 0) { - //send to all but one - //and make copies for sending + // Send to all but one + // and make copies for sending int exclude = -target_peer; for (Map<int, ENetPeer *>::Element *F = peer_map.front(); F; F = F->next()) { - if (F->key() == exclude) // exclude packet + if (F->key() == exclude) // Exclude packet continue; ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags); @@ -465,14 +546,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer enet_peer_send(F->get(), channel, packet2); } - enet_packet_destroy(packet); //original packet no longer needed + enet_packet_destroy(packet); // Original packet no longer needed } else { enet_peer_send(E->get(), channel, packet); } } else { ERR_FAIL_COND_V(!peer_map.has(1), ERR_BUG); - enet_peer_send(peer_map[1], channel, packet); //send to server for broadcast.. + enet_peer_send(peer_map[1], channel, packet); // Send to server for broadcast } enet_host_flush(host); @@ -482,7 +563,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer int NetworkedMultiplayerENet::get_max_packet_size() const { - return 1 << 24; //anything is good + return 1 << 24; // Anything is good } void NetworkedMultiplayerENet::_pop_current_packet() { @@ -511,16 +592,12 @@ uint32_t NetworkedMultiplayerENet::_gen_unique_id() const { (uint32_t)OS::get_singleton()->get_unix_time(), hash); hash = hash_djb2_one_32( (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash); - /* - hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_unique_id().hash64(), hash ); - */ hash = hash_djb2_one_32( - (uint32_t)((uint64_t)this), hash); //rely on aslr heap + (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap hash = hash_djb2_one_32( - (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack + (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack - hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion + hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion } return hash; @@ -596,7 +673,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer * return 0; if (ret > int(outLimit)) - return 0; //do not bother + return 0; // Do not bother copymem(outData, enet->dst_compressor_mem.ptr(), ret); @@ -651,17 +728,48 @@ void NetworkedMultiplayerENet::_setup_compressor() { void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) { - //do none + // Nothing to do +} + +IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const { + + ERR_FAIL_COND_V(!peer_map.has(p_peer_id), IP_Address()); + ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, IP_Address()); + ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, IP_Address()); + + IP_Address out; +#ifdef GODOT_ENET + out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host)); +#else + out.set_ipv4((uint8_t *)&(peer_map[p_peer_id]->address.host)); +#endif + + return out; +} + +int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const { + + ERR_FAIL_COND_V(!peer_map.has(p_peer_id), 0); + ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, 0); + ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, 0); +#ifdef GODOT_ENET + return peer_map[p_peer_id]->address.port; +#else + return peer_map[p_peer_id]->address.port; +#endif } void NetworkedMultiplayerENet::_bind_methods() { ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("create_client", "ip", "port", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("close_connection"), &NetworkedMultiplayerENet::close_connection); + ClassDB::bind_method(D_METHOD("create_client", "address", "port", "in_bandwidth", "out_bandwidth", "client_port"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &NetworkedMultiplayerENet::close_connection, DEFVAL(100)); + ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "now"), &NetworkedMultiplayerENet::disconnect_peer, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &NetworkedMultiplayerENet::set_compression_mode); ClassDB::bind_method(D_METHOD("get_compression_mode"), &NetworkedMultiplayerENet::get_compression_mode); ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &NetworkedMultiplayerENet::set_bind_ip); + ClassDB::bind_method(D_METHOD("get_peer_address"), &NetworkedMultiplayerENet::get_peer_address); + ClassDB::bind_method(D_METHOD("get_peer_port"), &NetworkedMultiplayerENet::get_peer_port); ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode"); @@ -696,7 +804,7 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet() { close_connection(); } -// sets IP for ENet to bind when using create_server +// Sets IP for ENet to bind when using create_server or create_client // if no IP is set, then ENet bind to ENET_HOST_ANY void NetworkedMultiplayerENet::set_bind_ip(const IP_Address &p_ip) { ERR_FAIL_COND(!p_ip.is_valid() && !p_ip.is_wildcard()); diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index 440e9b5400..d481f5d496 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -115,10 +115,15 @@ public: virtual int get_packet_peer() const; + virtual IP_Address get_peer_address(int p_peer_id) const; + virtual int get_peer_port(int p_peer_id) const; + Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0); - Error create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0); + Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_client_port = 0); + + void close_connection(uint32_t wait_usec = 100); - void close_connection(); + void disconnect_peer(int p_peer, bool now = false); virtual void poll(); diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index a34a650a4d..8a7c2a773a 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -49,7 +49,6 @@ if env['builtin_freetype']: "src/pshinter/pshinter.c", "src/psnames/psnames.c", "src/raster/raster.c", - "src/sfnt/sfnt.c", "src/smooth/smooth.c", "src/truetype/truetype.c", "src/type1/type1.c", @@ -58,9 +57,18 @@ if env['builtin_freetype']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - # Include header for UWP to fix build issues - if "platform" in env and env["platform"] == "uwp": - env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) + sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' + + if 'platform' in env: + if env['platform'] == 'uwp': + # Include header for UWP to fix build issues + env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) + elif env['platform'] == 'javascript': + # Forcibly undefine this macro so SIMD is not used in this file, + # since currently unsuported in WASM + sfnt = env.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__']) + + thirdparty_sources += [sfnt] env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"]) diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index acfb83bc10..8654ef3d82 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -275,8 +275,8 @@ if ARGUMENTS.get('gdnative_wrapper', False): if gd_wrapper_env['use_lto']: if not env.msvc: - gd_wrapper_env.Append(CCFLAGS=['--no-lto']) - gd_wrapper_env.Append(LINKFLAGS=['--no-lto']) + gd_wrapper_env.Append(CCFLAGS=['-fno-lto']) + gd_wrapper_env.Append(LINKFLAGS=['-fno-lto']) else: gd_wrapper_env.Append(CCFLAGS=['/GL-']) gd_wrapper_env.Append(LINKFLAGS=['/LTCG:OFF']) diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml index 998460eee1..be86ff0541 100644 --- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml +++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.1-dev"> +<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.1"> <brief_description> GDNative wrapper for an ARVR interface </brief_description> diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml index 4e87cbf450..ca0457623f 100644 --- a/modules/gdnative/doc_classes/GDNative.xml +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNative" inherits="Reference" category="Core" version="3.1-dev"> +<class name="GDNative" inherits="Reference" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index ca1bae0598..754a6d2514 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.1-dev"> +<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.1"> <brief_description> </brief_description> <description> @@ -9,12 +9,6 @@ <demos> </demos> <methods> - <method name="get_config_file"> - <return type="ConfigFile"> - </return> - <description> - </description> - </method> <method name="get_current_dependencies" qualifiers="const"> <return type="PoolStringArray"> </return> @@ -29,6 +23,8 @@ </method> </methods> <members> + <member name="config_file" type="ConfigFile" setter="set_config_file" getter="get_config_file"> + </member> <member name="load_once" type="bool" setter="set_load_once" getter="should_load_once"> </member> <member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable"> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index 6a71cd8d4d..1d3053244b 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NativeScript" inherits="Script" category="Core" version="3.1-dev"> +<class name="NativeScript" inherits="Script" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml index 3783d9d0a4..27c6adae3f 100644 --- a/modules/gdnative/doc_classes/PluginScript.xml +++ b/modules/gdnative/doc_classes/PluginScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PluginScript" inherits="Script" category="Core" version="3.1-dev"> +<class name="PluginScript" inherits="Script" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 42c3028f2c..897588385a 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -66,8 +66,169 @@ GDNativeLibrary::GDNativeLibrary() { GDNativeLibrary::~GDNativeLibrary() { } +bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_property) { + + String name = p_name; + + if (name.begins_with("entry/")) { + String key = name.substr(6, name.length() - 6); + + config_file->set_value("entry", key, p_property); + + set_config_file(config_file); + + return true; + } + + if (name.begins_with("dependency/")) { + String key = name.substr(11, name.length() - 11); + + config_file->set_value("dependencies", key, p_property); + + set_config_file(config_file); + + return true; + } + + return false; +} + +bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_property) const { + String name = p_name; + + if (name.begins_with("entry/")) { + String key = name.substr(6, name.length() - 6); + + r_property = config_file->get_value("entry", key); + + return true; + } + + if (name.begins_with("dependency/")) { + String key = name.substr(11, name.length() - 11); + + r_property = config_file->get_value("dependencies", key); + + return true; + } + + return false; +} + +void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const { + // set entries + List<String> entry_key_list; + + if (config_file->has_section("entry")) + config_file->get_section_keys("entry", &entry_key_list); + + for (List<String>::Element *E = entry_key_list.front(); E; E = E->next()) { + String key = E->get(); + + PropertyInfo prop; + + prop.type = Variant::STRING; + prop.name = "entry/" + key; + + p_list->push_back(prop); + } + + // set dependencies + List<String> dependency_key_list; + + if (config_file->has_section("dependencies")) + config_file->get_section_keys("dependencies", &dependency_key_list); + + for (List<String>::Element *E = dependency_key_list.front(); E; E = E->next()) { + String key = E->get(); + + PropertyInfo prop; + + prop.type = Variant::STRING; + prop.name = "dependency/" + key; + + p_list->push_back(prop); + } +} + +void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) { + + set_singleton(p_config_file->get_value("general", "singleton", default_singleton)); + set_load_once(p_config_file->get_value("general", "load_once", default_load_once)); + set_symbol_prefix(p_config_file->get_value("general", "symbol_prefix", default_symbol_prefix)); + set_reloadable(p_config_file->get_value("general", "reloadable", default_reloadable)); + + String entry_lib_path; + { + + List<String> entry_keys; + + if (p_config_file->has_section("entry")) + p_config_file->get_section_keys("entry", &entry_keys); + + for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) { + String key = E->get(); + + Vector<String> tags = key.split("."); + + bool skip = false; + for (int i = 0; i < tags.size(); i++) { + bool has_feature = OS::get_singleton()->has_feature(tags[i]); + + if (!has_feature) { + skip = true; + break; + } + } + + if (skip) { + continue; + } + + entry_lib_path = p_config_file->get_value("entry", key); + break; + } + } + + Vector<String> dependency_paths; + { + + List<String> dependency_keys; + + if (p_config_file->has_section("dependencies")) + p_config_file->get_section_keys("dependencies", &dependency_keys); + + for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) { + String key = E->get(); + + Vector<String> tags = key.split("."); + + bool skip = false; + for (int i = 0; i < tags.size(); i++) { + bool has_feature = OS::get_singleton()->has_feature(tags[i]); + + if (!has_feature) { + skip = true; + break; + } + } + + if (skip) { + continue; + } + + dependency_paths = p_config_file->get_value("dependencies", key); + break; + } + } + + current_library_path = entry_lib_path; + current_dependencies = dependency_paths; +} + void GDNativeLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("get_config_file"), &GDNativeLibrary::get_config_file); + ClassDB::bind_method(D_METHOD("set_config_file", "config_file"), &GDNativeLibrary::set_config_file); ClassDB::bind_method(D_METHOD("get_current_library_path"), &GDNativeLibrary::get_current_library_path); ClassDB::bind_method(D_METHOD("get_current_dependencies"), &GDNativeLibrary::get_current_dependencies); @@ -82,6 +243,8 @@ void GDNativeLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix); ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix"); @@ -337,73 +500,7 @@ RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_or *r_error = err; } - lib->set_singleton(config->get_value("general", "singleton", default_singleton)); - lib->set_load_once(config->get_value("general", "load_once", default_load_once)); - lib->set_symbol_prefix(config->get_value("general", "symbol_prefix", default_symbol_prefix)); - lib->set_reloadable(config->get_value("general", "reloadable", default_reloadable)); - - String entry_lib_path; - { - - List<String> entry_keys; - config->get_section_keys("entry", &entry_keys); - - for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) { - String key = E->get(); - - Vector<String> tags = key.split("."); - - bool skip = false; - for (int i = 0; i < tags.size(); i++) { - bool has_feature = OS::get_singleton()->has_feature(tags[i]); - - if (!has_feature) { - skip = true; - break; - } - } - - if (skip) { - continue; - } - - entry_lib_path = config->get_value("entry", key); - break; - } - } - - Vector<String> dependency_paths; - { - - List<String> dependency_keys; - config->get_section_keys("dependencies", &dependency_keys); - - for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) { - String key = E->get(); - - Vector<String> tags = key.split("."); - - bool skip = false; - for (int i = 0; i < tags.size(); i++) { - bool has_feature = OS::get_singleton()->has_feature(tags[i]); - - if (!has_feature) { - skip = true; - break; - } - } - - if (skip) { - continue; - } - - dependency_paths = config->get_value("dependencies", key); - break; - } - } - - lib->current_library_path = entry_lib_path; - lib->current_dependencies = dependency_paths; + lib->set_config_file(config); return lib; } diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index 3298ea950f..b17bb94f1c 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -66,8 +66,14 @@ public: GDNativeLibrary(); ~GDNativeLibrary(); + virtual bool _set(const StringName &p_name, const Variant &p_property); + virtual bool _get(const StringName &p_name, Variant &r_property) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + _FORCE_INLINE_ Ref<ConfigFile> get_config_file() { return config_file; } + void set_config_file(Ref<ConfigFile> p_config_file); + // things that change per-platform // so there are no setters for this _FORCE_INLINE_ String get_current_library_path() const { diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index a8919f7130..f41c3859bd 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5822,6 +5822,23 @@ ] }, { + "name": "godot_nativescript_set_global_type_tag", + "return_type": "void", + "arguments": [ + ["int", "p_idx"], + ["const char *", "p_name"], + ["const void *", "p_type_tag"] + ] + }, + { + "name": "godot_nativescript_get_global_type_tag", + "return_type": "const void *", + "arguments": [ + ["int", "p_idx"], + ["const char *", "p_name"] + ] + }, + { "name": "godot_nativescript_set_type_tag", "return_type": "void", "arguments": [ diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index 747328bc41..cfbe16fa7d 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -214,16 +214,19 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle, // type tag API +void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag); +const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name); + void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag); const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object); // instance binding API typedef struct { - void *(*alloc_instance_binding_data)(void *, godot_object *); - void (*free_instance_binding_data)(void *, void *); + GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *); + GDCALLINGCONV void (*free_instance_binding_data)(void *, void *); void *data; - void (*free_func)(void *); + GDCALLINGCONV void (*free_func)(void *); } godot_instance_binding_functions; int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions); diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h index 671be3bbb9..d1671c014e 100644 --- a/modules/gdnative/include/pluginscript/godot_pluginscript.h +++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h @@ -64,7 +64,7 @@ typedef struct { //this is used by script languages that keep a reference counter of their own //you can make make Ref<> not die when it reaches zero, so deleting the reference //depends entirely from the script. - // Note: You can set thoses function pointer to NULL if not needed. + // Note: You can set those function pointer to NULL if not needed. void (*refcount_incremented)(godot_pluginscript_instance_data *p_data); bool (*refcount_decremented)(godot_pluginscript_instance_data *p_data); // return true if it can die } godot_pluginscript_instance_desc; diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index aea595d0f0..ace2ecac5c 100644 --- a/modules/gdnative/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -313,6 +313,14 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle, signal->get().documentation = *(String *)&p_documentation; } +void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag) { + NativeScriptLanguage::get_singleton()->set_global_type_tag(p_idx, StringName(p_name), p_type_tag); +} + +const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name) { + return NativeScriptLanguage::get_singleton()->get_global_type_tag(p_idx, StringName(p_name)); +} + void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag) { String *s = (String *)p_gdnative_handle; @@ -331,13 +339,11 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object) const Object *o = (Object *)p_object; if (!o->get_script_instance()) { - ERR_EXPLAIN("Attempted to get type tag on an object without a script!"); - ERR_FAIL_V(NULL); + return NULL; } else { NativeScript *script = Object::cast_to<NativeScript>(o->get_script_instance()->get_script().ptr()); if (!script) { - ERR_EXPLAIN("Attempted to get type tag on an object without a nativescript attached"); - ERR_FAIL_V(NULL); + return NULL; } if (script->get_script_desc()) @@ -347,10 +353,6 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object) return NULL; } -#ifdef __cplusplus -} -#endif - int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions) { return NativeScriptLanguage::get_singleton()->register_binding_functions(p_binding_functions); } @@ -362,3 +364,7 @@ void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_i void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object) { return NativeScriptLanguage::get_singleton()->get_instance_binding_data(p_idx, (Object *)p_object); } + +#ifdef __cplusplus +} +#endif diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index f2e9bef467..cf8977f3ea 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -55,12 +55,6 @@ #include "editor/editor_node.h" #endif -// -// -// Script stuff -// -// - void NativeScript::_bind_methods() { ClassDB::bind_method(D_METHOD("set_class_name", "class_name"), &NativeScript::set_class_name); ClassDB::bind_method(D_METHOD("get_class_name"), &NativeScript::get_class_name); @@ -363,14 +357,13 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const { NativeScriptDesc *script_data = get_script_desc(); Set<StringName> existing_properties; + List<PropertyInfo>::Element *original_back = p_list->back(); while (script_data) { - List<PropertyInfo>::Element *insert_position = p_list->front(); - bool insert_before = true; + List<PropertyInfo>::Element *insert_position = original_back; for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) { if (!existing_properties.has(E.key())) { - insert_position = insert_before ? p_list->insert_before(insert_position, E.get().info) : p_list->insert_after(insert_position, E.get().info); - insert_before = false; + insert_position = p_list->insert_after(insert_position, E.get().info); existing_properties.insert(E.key()); } } @@ -528,12 +521,6 @@ NativeScript::~NativeScript() { #endif } - // - // - // ScriptInstance stuff - // - // - #define GET_SCRIPT_DESC() script->get_script_desc() void NativeScriptInstance::_ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount) { @@ -872,15 +859,12 @@ NativeScriptInstance::~NativeScriptInstance() { } } -// -// -// ScriptingLanguage stuff -// -// - NativeScriptLanguage *NativeScriptLanguage::singleton; void NativeScriptLanguage::_unload_stuff(bool p_reload) { + + Map<String, Ref<GDNative> > erase_and_unload; + for (Map<String, Map<StringName, NativeScriptDesc> >::Element *L = library_classes.front(); L; L = L->next()) { String lib_path = L->key(); @@ -916,18 +900,6 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) { gdn = E->get(); } - if (gdn.is_valid() && gdn->get_library().is_valid()) { - Ref<GDNativeLibrary> lib = gdn->get_library(); - void *terminate_fn; - Error err = gdn->get_symbol(lib->get_symbol_prefix() + _terminate_call_name, terminate_fn, true); - - if (err == OK) { - void (*terminate)(void *) = (void (*)(void *))terminate_fn; - - terminate((void *)&lib_path); - } - } - for (Map<StringName, NativeScriptDesc>::Element *C = classes.front(); C; C = C->next()) { // free property stuff first @@ -952,12 +924,34 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) { if (C->get().destroy_func.free_func) C->get().destroy_func.free_func(C->get().destroy_func.method_data); } + + erase_and_unload.insert(lib_path, gdn); + } + + for (Map<String, Ref<GDNative> >::Element *E = erase_and_unload.front(); E; E = E->next()) { + String lib_path = E->key(); + Ref<GDNative> gdn = E->get(); + + library_classes.erase(lib_path); + + if (gdn.is_valid() && gdn->get_library().is_valid()) { + Ref<GDNativeLibrary> lib = gdn->get_library(); + void *terminate_fn; + Error err = gdn->get_symbol(lib->get_symbol_prefix() + _terminate_call_name, terminate_fn, true); + + if (err == OK) { + void (*terminate)(void *) = (void (*)(void *))terminate_fn; + + terminate((void *)&lib_path); + } + } } } NativeScriptLanguage::NativeScriptLanguage() { NativeScriptLanguage::singleton = this; #ifndef NO_THREADS + has_objects_to_register = false; mutex = Mutex::create(); #endif } @@ -1182,8 +1176,11 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec } if (!(*binding_data)[p_idx]) { + + const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name()); + // no binding data yet, soooooo alloc new one \o/ - (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, (godot_object *)p_object); + (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object); } return (*binding_data)[p_idx]; @@ -1225,6 +1222,27 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) { delete &binding_data; } +void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) { + if (!global_type_tags.has(p_idx)) { + global_type_tags.insert(p_idx, HashMap<StringName, const void *>()); + } + + HashMap<StringName, const void *> &tags = global_type_tags[p_idx]; + + tags.set(p_class_name, p_type_tag); +} + +const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_class_name) const { + if (!global_type_tags.has(p_idx)) + return NULL; + + const HashMap<StringName, const void *> &tags = global_type_tags[p_idx]; + + const void *tag = tags.get(p_class_name); + + return tag; +} + #ifndef NO_THREADS void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script) { MutexLock lock(mutex); @@ -1362,6 +1380,7 @@ void NativeReloadNode::_notification(int p_what) { MutexLock lock(NSL->mutex); #endif NSL->_unload_stuff(true); + for (Map<String, Ref<GDNative> >::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) { Ref<GDNative> gdn = L->get(); @@ -1375,7 +1394,6 @@ void NativeReloadNode::_notification(int p_what) { } gdn->terminate(); - NSL->library_classes.erase(L->key()); } unloaded = true; diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 17b6ddc747..68a8126a32 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -36,6 +36,7 @@ #include "core/self_list.h" #include "io/resource_loader.h" #include "io/resource_saver.h" +#include "oa_hash_map.h" #include "ordered_hash_map.h" #include "os/thread_safe.h" #include "scene/main/node.h" @@ -240,6 +241,8 @@ private: Vector<Pair<bool, godot_instance_binding_functions> > binding_functions; Set<Vector<void *> *> binding_instances; + Map<int, HashMap<StringName, const void *> > global_type_tags; + public: // These two maps must only be touched on the main thread Map<String, Map<StringName, NativeScriptDesc> > library_classes; @@ -323,6 +326,9 @@ public: virtual void *alloc_instance_binding_data(Object *p_object); virtual void free_instance_binding_data(void *p_data); + + void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag); + const void *get_global_type_tag(int p_idx, StringName p_class_name) const; }; inline NativeScriptDesc *NativeScript::get_script_desc() const { diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp index 8888f9e157..924abf29df 100644 --- a/modules/gdnative/pluginscript/register_types.cpp +++ b/modules/gdnative/pluginscript/register_types.cpp @@ -64,7 +64,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc) // desc->make_function is not mandatory // desc->complete_code is not mandatory // desc->auto_indent_code is not mandatory - // desc->add_global_constant is not mandatory + ERR_FAIL_COND_V(!desc->add_global_constant, ERR_BUG); // desc->debug_get_error is not mandatory // desc->debug_get_stack_level_count is not mandatory // desc->debug_get_stack_level_line is not mandatory @@ -78,7 +78,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc) // desc->profiling_stop is not mandatory // desc->profiling_get_accumulated_data is not mandatory // desc->profiling_get_frame_data is not mandatory - // desc->frame is not mandatory + // desc->profiling_frame is not mandatory ERR_FAIL_COND_V(!desc->script_desc.init, ERR_BUG); ERR_FAIL_COND_V(!desc->script_desc.finish, ERR_BUG); diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 0dc69c3688..40a435f459 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScript" inherits="Script" category="Core" version="3.1-dev"> +<class name="GDScript" inherits="Script" category="Core" version="3.1"> <brief_description> A script implemented in the GDScript programming language. </brief_description> diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml index 254d968e1b..c205cedef5 100644 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.1-dev"> +<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.1"> <brief_description> State of a function call after yielding. </brief_description> diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml index 1abcc4762f..90935b5c22 100644 --- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml +++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.1-dev"> +<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 1649fb52f2..048948dada 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -181,7 +181,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: //wait, identifier could be a local variable or something else... careful here, must reference properly //as stack may be more interesting to work with - //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases peformance a lot. + //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases performance a lot. const GDScriptParser::IdentifierNode *in = static_cast<const GDScriptParser::IdentifierNode *>(p_expression); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 505562324f..87e1276492 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -60,8 +60,8 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "# var a = 2\n" + "# var b = \"textvar\"\n\n" + "func _ready():\n" + - "%TS%# Called every time the node is added to the scene.\n" + - "%TS%# Initialization here\n" + + "%TS%# Called when the node is added to the scene for the first time.\n" + + "%TS%# Initialization here.\n" + "%TS%pass\n\n" + "#func _process(delta):\n" + "#%TS%# Called every frame. Delta is time since last frame.\n" + @@ -410,13 +410,11 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na String s = "func " + p_name + "("; if (p_args.size()) { - s += " "; for (int i = 0; i < p_args.size(); i++) { if (i > 0) s += ", "; s += p_args[i].get_slice(":", 0); } - s += " "; } s += "):\n" + _get_indentation() + "pass # replace with function body\n"; @@ -432,6 +430,9 @@ struct GDScriptCompletionIdentifier { Ref<GDScript> script; Variant::Type type; Variant value; //im case there is a value, also return it + + GDScriptCompletionIdentifier() : + type(Variant::NIL) {} }; static GDScriptCompletionIdentifier _get_type_from_variant(const Variant &p_variant, bool p_allow_gdnative_class = false) { @@ -553,9 +554,7 @@ static Ref<Reference> _get_parent_class(GDScriptCompletionContext &context) { static GDScriptCompletionIdentifier _get_native_class(GDScriptCompletionContext &context) { - //eeh... GDScriptCompletionIdentifier id; - id.type = Variant::NIL; REF pc = _get_parent_class(context); if (!pc.is_valid()) { @@ -1335,13 +1334,23 @@ static void _find_identifiers_in_block(GDScriptCompletionContext &context, int p for (int i = 0; i < context.block->statements.size(); i++) { - if (context.block->statements[i]->line > p_line) + GDScriptParser::Node *statement = context.block->statements[i]; + if (statement->line > p_line) continue; - if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) { + GDScriptParser::BlockNode::Type statementType = statement->type; + if (statementType == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) { - const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(context.block->statements[i]); + const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(statement); result.insert(lv->name.operator String()); + } else if (statementType == GDScriptParser::BlockNode::TYPE_CONTROL_FLOW) { + + const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(statement); + if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_FOR) { + + const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0]); + result.insert(id->name.operator String()); + } } } } @@ -1513,6 +1522,13 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo result.insert(_type_names[i]); } + List<String> reserved_words; + GDScriptLanguage::get_singleton()->get_reserved_words(&reserved_words); + + for (List<String>::Element *E = reserved_words.front(); E; E = E->next()) { + result.insert(E->get()); + } + //autoload singletons List<PropertyInfo> props; ProjectSettings::get_singleton()->get_property_list(&props); @@ -2633,6 +2649,18 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol context.function = p.get_completion_function(); context.base = p_owner; context.base_path = p_base_path; + + if (context._class && context._class->extends_class.size() > 0) { + bool success = false; + ClassDB::get_integer_constant(context._class->extends_class[0], p_symbol, &success); + if (success) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name = context._class->extends_class[0]; + r_result.class_member = p_symbol; + return OK; + } + } + bool isfunction = false; switch (p.get_completion_type()) { @@ -2852,7 +2880,24 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } else { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + /* + // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope + // We cannot determine the exact nature of the identifier here + // Otherwise these codes would work + StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true); + if (enumName != NULL) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM; + r_result.class_name = "@GlobalScope"; + r_result.class_member = enumName; + return OK; + } + else { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name = "@GlobalScope"; + r_result.class_member = p_symbol; + return OK; + }*/ + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE; r_result.class_name = "@GlobalScope"; r_result.class_member = p_symbol; return OK; @@ -2915,6 +2960,14 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } + StringName enumName = ClassDB::get_integer_constant_enum(t.obj_type, p_symbol, true); + if (enumName != StringName()) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM; + r_result.class_name = t.obj_type; + r_result.class_member = enumName; + return OK; + } + bool success; ClassDB::get_integer_constant(t.obj_type, p_symbol, &success); if (success) { diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index a2f449909f..1c5b8187ca 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -86,7 +86,7 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta o = o->_owner; } - ERR_EXPLAIN("GDScriptCompiler bug.."); + ERR_EXPLAIN("GDScriptCompiler bug..."); ERR_FAIL_V(NULL); } break; case ADDR_TYPE_LOCAL_CONSTANT: { @@ -1311,9 +1311,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } -#endif if (ScriptDebugger::get_singleton()) GDScriptLanguage::get_singleton()->exit_function(); +#endif if (_stack_size) { //free stack @@ -1535,15 +1535,21 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar // then the function did yield again after resuming. if (ret.is_ref()) { GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret); - if (gdfs && gdfs->function == function) + if (gdfs && gdfs->function == function) { completed = false; + gdfs->previous_state = Ref<GDScriptFunctionState>(this); + } } function = NULL; //cleaned up; state.result = Variant(); if (completed) { - emit_signal("completed", ret); + GDScriptFunctionState *state = this; + while (state != NULL) { + state->emit_signal("completed", ret); + state = *(state->previous_state); + } } return ret; @@ -1591,15 +1597,21 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { // then the function did yield again after resuming. if (ret.is_ref()) { GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret); - if (gdfs && gdfs->function == function) + if (gdfs && gdfs->function == function) { completed = false; + gdfs->previous_state = Ref<GDScriptFunctionState>(this); + } } function = NULL; //cleaned up; state.result = Variant(); if (completed) { - emit_signal("completed", ret); + GDScriptFunctionState *state = this; + while (state != NULL) { + state->emit_signal("completed", ret); + state = *(state->previous_state); + } } return ret; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 9310444c7a..dff4bdfaf2 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -234,6 +234,7 @@ class GDScriptFunctionState : public Reference { GDScriptFunction *function; GDScriptFunction::CallState state; Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + Ref<GDScriptFunctionState> previous_state; protected: static void _bind_methods(); diff --git a/modules/gdscript/gdscript_highlighter.cpp b/modules/gdscript/gdscript_highlighter.cpp new file mode 100644 index 0000000000..4e89851bf2 --- /dev/null +++ b/modules/gdscript/gdscript_highlighter.cpp @@ -0,0 +1,262 @@ +/*************************************************************************/ +/* gdscript_highlighter.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "gdscript_highlighter.h" +#include "scene/gui/text_edit.h" + +inline bool _is_symbol(CharType c) { + + return is_symbol(c); +} + +static bool _is_text_char(CharType c) { + + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +static bool _is_whitespace(CharType c) { + return c == '\t' || c == ' '; +} + +static bool _is_char(CharType c) { + + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +} + +static bool _is_number(CharType c) { + return (c >= '0' && c <= '9'); +} + +static bool _is_hex_symbol(CharType c) { + return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) { + Map<int, TextEdit::HighlighterInfo> color_map; + + bool prev_is_char = false; + bool prev_is_number = false; + bool in_keyword = false; + bool in_word = false; + bool in_function_name = false; + bool in_member_variable = false; + bool is_hex_notation = false; + Color keyword_color; + Color color; + + int in_region = text_editor->_is_line_in_region(p_line); + int deregion = 0; + + const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text_editor->_get_line_color_region_info(p_line); + const String &str = text_editor->get_line(p_line); + Color prev_color; + for (int j = 0; j < str.length(); j++) { + TextEdit::HighlighterInfo highlighter_info; + + if (deregion > 0) { + deregion--; + if (deregion == 0) { + in_region = -1; + } + } + + if (deregion != 0) { + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + continue; + } + + color = font_color; + + bool is_char = _is_text_char(str[j]); + bool is_symbol = _is_symbol(str[j]); + bool is_number = _is_number(str[j]); + + // allow ABCDEF in hex notation + if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) { + is_number = true; + } else { + is_hex_notation = false; + } + + // check for dot or underscore or 'x' for hex notation in floating point number + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { + is_number = true; + is_symbol = false; + is_char = false; + + if (str[j] == 'x' && str[j - 1] == '0') { + is_hex_notation = true; + } + } + + if (!in_word && _is_char(str[j]) && !is_number) { + in_word = true; + } + + if ((in_keyword || in_word) && !is_hex_notation) { + is_number = false; + } + + if (is_symbol && str[j] != '.' && in_word) { + in_word = false; + } + + if (is_symbol && cri_map.has(j)) { + const TextEdit::Text::ColorRegionInfo &cri = cri_map[j]; + + if (in_region == -1) { + if (!cri.end) { + in_region = cri.region; + } + } else { + TextEdit::ColorRegion cr = text_editor->_get_color_region(cri.region); + if (in_region == cri.region && !cr.line_only) { //ignore otherwise + if (cri.end || cr.eq) { + deregion = cr.eq ? cr.begin_key.length() : cr.end_key.length(); + } + } + } + } + + if (!is_char) { + in_keyword = false; + } + + if (in_region == -1 && !in_keyword && is_char && !prev_is_char) { + + int to = j; + while (to < str.length() && _is_text_char(str[to])) + to++; + + String word = str.substr(j, to - j); + Color col = Color(); + if (text_editor->has_keyword_color(word)) { + col = text_editor->get_keyword_color(word); + } else if (text_editor->has_member_color(word)) { + col = text_editor->get_member_color(word); + for (int k = j - 1; k >= 0; k--) { + if (str[k] == '.') { + col = Color(); //member indexing not allowed + break; + } else if (str[k] > 32) { + break; + } + } + } + + if (col != Color()) { + in_keyword = true; + keyword_color = col; + } + } + + if (!in_function_name && in_word && !in_keyword) { + + int k = j; + while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k++; + } + + // check for space between name and bracket + while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { + k++; + } + + if (str[k] == '(') { + in_function_name = true; + } + } + + if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) { + int k = j; + while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k--; + } + + if (str[k] == '.') { + in_member_variable = true; + } + } + + if (is_symbol) { + in_function_name = false; + in_member_variable = false; + } + + if (in_region >= 0) + color = text_editor->_get_color_region(in_region).color; + else if (in_keyword) + color = keyword_color; + else if (in_member_variable) + color = member_color; + else if (in_function_name) + color = function_color; + else if (is_symbol) + color = symbol_color; + else if (is_number) + color = number_color; + + prev_is_char = is_char; + prev_is_number = is_number; + + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + } + return color_map; +} + +String GDScriptSyntaxHighlighter::get_name() { + return "GDScript"; +} + +List<String> GDScriptSyntaxHighlighter::get_supported_languages() { + List<String> languages; + languages.push_back("GDScript"); + return languages; +} + +void GDScriptSyntaxHighlighter::_update_cache() { + font_color = text_editor->get_color("font_color"); + symbol_color = text_editor->get_color("symbol_color"); + function_color = text_editor->get_color("function_color"); + number_color = text_editor->get_color("number_color"); + member_color = text_editor->get_color("member_variable_color"); +} + +SyntaxHighlighter *GDScriptSyntaxHighlighter::create() { + return memnew(GDScriptSyntaxHighlighter); +} diff --git a/modules/gdscript/gdscript_highlighter.h b/modules/gdscript/gdscript_highlighter.h new file mode 100644 index 0000000000..ef1bdd4103 --- /dev/null +++ b/modules/gdscript/gdscript_highlighter.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* gdscript_highlighter.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 GDSCRIPT_HIGHLIGHTER_H +#define GDSCRIPT_HIGHLIGHTER_H + +#include "scene/gui/text_edit.h" + +class GDScriptSyntaxHighlighter : public SyntaxHighlighter { +private: + // colours + Color font_color; + Color symbol_color; + Color function_color; + Color built_in_type_color; + Color number_color; + Color member_color; + +public: + static SyntaxHighlighter *create(); + + virtual void _update_cache(); + virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line); + + virtual String get_name(); + virtual List<String> get_supported_languages(); +}; + +#endif // GDSCRIPT_HIGHLIGHTER_H diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index cd752a786c..e7b0700e76 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -461,18 +461,21 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } else if (!for_completion || FileAccess::exists(path)) { res = ResourceLoader::load(path); } - if (!res.is_valid()) { - _set_error("Can't preload resource at path: " + path); - return NULL; - } } else { if (!FileAccess::exists(path)) { _set_error("Can't preload resource at path: " + path); return NULL; + } else if (ScriptCodeCompletionCache::get_singleton()) { + res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); } } + if (!res.is_valid()) { + _set_error("Can't preload resource at path: " + path); + return NULL; + } + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected ')' after 'preload' path"); return NULL; @@ -967,7 +970,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } if (!expr) { - ERR_EXPLAIN("GDScriptParser bug, couldn't figure out what expression is.."); + ERR_EXPLAIN("GDScriptParser bug, couldn't figure out what expression is..."); ERR_FAIL_COND_V(!expr, NULL); } @@ -1302,7 +1305,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr_pos++; if (expr_pos == expression.size()) { //can happen.. - _set_error("Unexpected end of expression.."); + _set_error("Unexpected end of expression..."); return NULL; } } @@ -1321,7 +1324,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } else if (is_ternary) { if (next_op < 1 || next_op >= (expression.size() - 1)) { - _set_error("Parser bug.."); + _set_error("Parser bug..."); ERR_FAIL_V(NULL); } @@ -1340,7 +1343,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (expression[next_op - 1].is_op) { - _set_error("Parser bug.."); + _set_error("Parser bug..."); ERR_FAIL_V(NULL); } @@ -1377,7 +1380,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } else { if (next_op < 1 || next_op >= (expression.size() - 1)) { - _set_error("Parser bug.."); + _set_error("Parser bug..."); ERR_FAIL_V(NULL); } @@ -1387,7 +1390,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (expression[next_op - 1].is_op) { - _set_error("Parser bug.."); + _set_error("Parser bug..."); ERR_FAIL_V(NULL); } @@ -3437,6 +3440,22 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) { tokenizer->advance(); + + String hint_prefix = ""; + bool is_arrayed = false; + + while (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && + tokenizer->get_token_type() == Variant::ARRAY && + tokenizer->get_token(1) == GDScriptTokenizer::TK_COMMA) { + tokenizer->advance(); // Array + tokenizer->advance(); // Comma + if (is_arrayed) { + hint_prefix += itos(Variant::ARRAY) + ":"; + } else { + is_arrayed = true; + } + } + if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) { Variant::Type type = tokenizer->get_token_type(); @@ -3452,28 +3471,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; tokenizer->advance(); - String hint_prefix = ""; - - if (type == Variant::ARRAY && tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { - tokenizer->advance(); - - while (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) { - type = tokenizer->get_token_type(); - - tokenizer->advance(); - - if (type == Variant::ARRAY) { - hint_prefix += itos(Variant::ARRAY) + ":"; - if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { - tokenizer->advance(); - } - } else { - hint_prefix += itos(type); - break; - } - } - } - if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { // hint expected next! tokenizer->advance(); @@ -3827,13 +3824,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } break; } } - if (current_export.type == Variant::ARRAY && !hint_prefix.empty()) { - if (current_export.hint) { - hint_prefix += "/" + itos(current_export.hint); - } - current_export.hint_string = hint_prefix + ":" + current_export.hint_string; - current_export.hint = PROPERTY_HINT_NONE; - } } else { @@ -3920,6 +3910,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } + if (is_arrayed) { + hint_prefix += itos(current_export.type); + if (current_export.hint) { + hint_prefix += "/" + itos(current_export.hint); + } + current_export.hint_string = hint_prefix + ":" + current_export.hint_string; + current_export.hint = PROPERTY_HINT_TYPE_STRING; + current_export.type = Variant::ARRAY; + } + tokenizer->advance(); } @@ -4087,7 +4087,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member._export.type=Variant::DICTIONARY; - } else*/ { + } else*/ + { if (subexpr->type != Node::TYPE_CONSTANT) { @@ -4098,7 +4099,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { ConstantNode *cn = static_cast<ConstantNode *>(subexpr); if (cn->value.get_type() == Variant::NIL) { - _set_error("Can't accept a null constant expression for infering export type."); + _set_error("Can't accept a null constant expression for inferring export type."); return; } member._export.type = cn->value.get_type(); @@ -4234,7 +4235,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } break; case GDScriptTokenizer::TK_PR_ENUM: { - //mutiple constant declarations.. + //multiple constant declarations.. int last_assign = -1; // Incremented by 1 right before the assingment. String enum_name; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 95efcda80f..85c94c3596 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -31,6 +31,7 @@ #include "register_types.h" #include "gdscript.h" +#include "gdscript_highlighter.h" #include "gdscript_tokenizer.h" #include "io/file_access_encrypted.h" #include "io/resource_loader.h" @@ -92,6 +93,7 @@ void register_gdscript_types() { ResourceSaver::add_resource_format_saver(resource_saver_gd); #ifdef TOOLS_ENABLED + ScriptEditor::register_create_syntax_highlighter_function(GDScriptSyntaxHighlighter::create); EditorNode::add_init_callback(_editor_init); #endif } diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 36d4d7cb59..bb652f3bdf 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GridMap" inherits="Spatial" category="Core" version="3.1-dev"> +<class name="GridMap" inherits="Spatial" category="Core" version="3.1"> <brief_description> Node for 3D tile-based maps. </brief_description> diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 34d51b51e2..4b96824dca 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -101,8 +101,8 @@ void GridMapEditor::_menu_option(int p_option) { } if (edit_axis != new_axis) { - int item1 = options->get_popup()->get_item_id(MENU_OPTION_NEXT_LEVEL); - int item2 = options->get_popup()->get_item_id(MENU_OPTION_PREV_LEVEL); + int item1 = options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL); + int item2 = options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL); if (edit_axis == Vector3::AXIS_Y) { options->get_popup()->set_item_text(item1, TTR("Next Plane")); options->get_popup()->set_item_text(item2, TTR("Previous Plane")); @@ -641,12 +641,21 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { - if (pan_gesture->get_command() || pan_gesture->get_shift()) { - const real_t delta = pan_gesture->get_delta().y; - floor->set_value(floor->get_value() + SGN(delta)); + if (pan_gesture->get_alt() && (pan_gesture->get_command() || pan_gesture->get_shift())) { + const real_t delta = pan_gesture->get_delta().y * 0.5; + accumulated_floor_delta += delta; + int step = 0; + if (ABS(accumulated_floor_delta) > 1.0) { + step = SGN(accumulated_floor_delta); + accumulated_floor_delta -= step; + } + if (step) { + floor->set_value(floor->get_value() + step); + } return true; } } + accumulated_floor_delta = 0.0; return false; } @@ -770,7 +779,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) { set_process(true); - Vector3 edited_floor = p_gridmap->get_meta("_editor_floor_"); + Vector3 edited_floor = p_gridmap->has_meta("_editor_floor_") ? p_gridmap->get_meta("_editor_floor_") : Variant(); clip_mode = p_gridmap->has_meta("_editor_clip_") ? ClipMode(p_gridmap->get_meta("_editor_clip_").operator int()) : CLIP_DISABLED; for (int i = 0; i < 3; i++) { @@ -1037,14 +1046,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, KEY_Q); options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, KEY_E); options->get_popup()->add_separator(); - options->get_popup()->add_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED); + options->get_popup()->add_radio_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED); options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_CLIP_DISABLED), true); - options->get_popup()->add_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE); - options->get_popup()->add_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW); + options->get_popup()->add_radio_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE); + options->get_popup()->add_radio_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW); options->get_popup()->add_separator(); - options->get_popup()->add_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z); - options->get_popup()->add_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X); - options->get_popup()->add_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C); + options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z); + options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X); + options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C); options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, KEY_A); @@ -1145,9 +1154,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { for (int k = 0; k < 3; k++) { if (i < 3) - face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[j][(i + k) % 3] = v[k]; else - face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[3 - j][(i + k) % 3] = -v[k]; } } @@ -1247,6 +1256,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { selection.active = false; updating = false; + accumulated_floor_delta = 0.0; } GridMapEditor::~GridMapEditor() { @@ -1259,9 +1269,10 @@ GridMapEditor::~GridMapEditor() { VisualServer::get_singleton()->free(grid_instance[i]); if (cursor_instance.is_valid()) VisualServer::get_singleton()->free(cursor_instance); - if (selection_level_instance[i].is_valid()) { + if (selection_level_instance[i].is_valid()) VisualServer::get_singleton()->free(selection_level_instance[i]); - } + if (selection_level_mesh[i].is_valid()) + VisualServer::get_singleton()->free(selection_level_mesh[i]); } VisualServer::get_singleton()->free(selection_mesh); diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 9651770528..f79d9aefa0 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -76,6 +76,7 @@ class GridMapEditor : public VBoxContainer { Panel *panel; MenuButton *options; SpinBox *floor; + double accumulated_floor_delta; ToolButton *mode_thumbnail; ToolButton *mode_list; HBoxContainer *spatial_editor_hb; diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index 3cc362b5d6..d592c19b97 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -42,14 +42,18 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force ERR_FAIL_COND_V(header != "#?RADIANCE" && header != "#?RGBE", ERR_FILE_UNRECOGNIZED); while (true) { - String format = f->get_token(); + String line = f->get_line(); ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_UNRECOGNIZED); - if (format.begins_with("FORMAT=") && format != "FORMAT=32-bit_rle_rgbe") { - ERR_EXPLAIN("Only 32-bit_rle_rgbe is supported for .hdr files."); - return ERR_FILE_UNRECOGNIZED; - } - if (format == "FORMAT=32-bit_rle_rgbe") + if (line == "") // empty line indicates end of header break; + if (line.begins_with("FORMAT=")) { // leave option to implement other commands + if (line != "FORMAT=32-bit_rle_rgbe") { + ERR_EXPLAIN("Only 32-bit_rle_rgbe is supported for HDR files."); + return ERR_FILE_UNRECOGNIZED; + } + } else if (!line.begins_with("#")) { // not comment + WARN_PRINTS("Ignoring unsupported header information in HDR : " + line); + } } String token = f->get_token(); diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub index b846ae38ad..38198c9105 100755 --- a/modules/mbedtls/SCsub +++ b/modules/mbedtls/SCsub @@ -85,7 +85,7 @@ if env['builtin_mbedtls']: thirdparty_dir = "#thirdparty/mbedtls/library/" thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env_mbed_tls.add_source_files(env.modules_sources, thirdparty_sources) - env_mbed_tls.Append(CPPPATH=["#thirdparty/mbedtls/include/"]) + env_mbed_tls.Prepend(CPPPATH=["#thirdparty/mbedtls/include/"]) # Module sources env_mbed_tls.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp index 4135eb40ff..a63e53ec1f 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbed_tls.cpp @@ -293,28 +293,10 @@ void StreamPeerMbedTLS::initialize_ssl() { mbedtls_debug_set_threshold(1); #endif - String certs_path = GLOBAL_DEF("network/ssl/certificates", ""); - ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); - - if (certs_path != "") { - - FileAccess *f = FileAccess::open(certs_path, FileAccess::READ); - if (f) { - PoolByteArray arr; - int flen = f->get_len(); - arr.resize(flen + 1); - { - PoolByteArray::Write w = arr.write(); - f->get_buffer(w.ptr(), flen); - w[flen] = 0; //end f string - } - - memdelete(f); - - _load_certs(arr); - print_line("Loaded certs from '" + certs_path); - } - } + PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array(); + + if (cert_array.size() > 0) + _load_certs(cert_array); available = true; } diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h index ce17614d85..2b96a194a1 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.h +++ b/modules/mbedtls/stream_peer_mbed_tls.h @@ -32,8 +32,6 @@ #define STREAM_PEER_OPEN_SSL_H #include "io/stream_peer_ssl.h" -#include "os/file_access.h" -#include "project_settings.h" #include "mbedtls/config.h" #include "mbedtls/ctr_drbg.h" diff --git a/modules/mobile_vr/config.py b/modules/mobile_vr/config.py index 4e1155f0c6..aa8ef111d3 100644 --- a/modules/mobile_vr/config.py +++ b/modules/mobile_vr/config.py @@ -1,6 +1,6 @@ def can_build(platform): # should probably change this to only be true on iOS and Android - return True + return False def configure(env): pass diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index d3f2548320..359d654433 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.1-dev"> +<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.1"> <brief_description> Generic mobile VR implementation </brief_description> diff --git a/modules/mono/SCsub b/modules/mono/SCsub index aa8626e6da..a1dfcf6377 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -31,11 +31,18 @@ def make_cs_files_header(src, dst): if i > 0: header.write(', ') header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert(\"' + file + '\", ' \ + inserted_files += '\tr_files.insert("' + file + '", ' \ 'CompressedFile(_cs_' + name + '_compressed_size, ' \ '_cs_' + name + '_uncompressed_size, ' \ '_cs_' + name + '_compressed));\n' header.write(' };\n') + version_file = os.path.join(src, 'VERSION.txt') + with open(version_file, 'r') as content_file: + try: + glue_version = int(content_file.read()) # make sure the format is valid + header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') + except ValueError: + raise ValueError('Invalid C# glue version in: ' + version_file) header.write('\nstruct CompressedFile\n' '{\n' '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' @@ -80,23 +87,23 @@ def find_msbuild_unix(filename): import sys hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == "darwin": + if sys.platform == 'darwin': hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs for hint_dir in hint_dirs: hint_path = os.path.join(hint_dir, filename) if os.path.isfile(hint_path): return hint_path - elif os.path.isfile(hint_path + ".exe"): - return hint_path + ".exe" + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' - for hint_dir in os.environ["PATH"].split(os.pathsep): + for hint_dir in os.environ['PATH'].split(os.pathsep): hint_dir = hint_dir.strip('"') hint_path = os.path.join(hint_dir, filename) if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): return hint_path - if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK): - return hint_path + ".exe" + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' return None @@ -152,7 +159,7 @@ def mono_build_solution(source, target, env): xbuild_fallback = env['xbuild_fallback'] if xbuild_fallback and os.name == 'nt': - print("Option 'xbuild_fallback' not supported on Windows") + print('Option \'xbuild_fallback\' not supported on Windows') xbuild_fallback = False if xbuild_fallback: @@ -202,10 +209,16 @@ def mono_build_solution(source, target, env): copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) +output_dir = Dir('#bin').abspath +assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath -mono_sln_builder = Builder(action = mono_build_solution) +mono_sln_builder = Builder(action=mono_build_solution) env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) env_mono.MonoBuildSolution( - os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'), + os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'), 'editor/GodotSharpTools/GodotSharpTools.sln' ) + +if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir): + rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir) + env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir}) diff --git a/modules/mono/config.py b/modules/mono/config.py index 7c1846dcc2..18d9c67795 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -2,8 +2,9 @@ import imp import os import sys +import subprocess -from SCons.Script import BoolVariable, Environment, Variables +from SCons.Script import BoolVariable, Dir, Environment, PathVariable, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') @@ -29,20 +30,16 @@ def is_enabled(): return False -def copy_file_no_replace(src_dir, dst_dir, name): +def copy_file(src_dir, dst_dir, name): from shutil import copyfile src_path = os.path.join(src_dir, name) dst_path = os.path.join(dst_dir, name) - need_copy = True if not os.path.isdir(dst_dir): os.mkdir(dst_dir) - elif os.path.exists(dst_path): - need_copy = False - if need_copy: - copyfile(src_path, dst_path) + copyfile(src_path, dst_path) def configure(env): @@ -51,18 +48,17 @@ def configure(env): envvars = Variables() envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', PathVariable.PathIsDirCreate)) envvars.Update(env) bits = env['bits'] mono_static = env['mono_static'] + assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] if env['platform'] == 'windows': - if mono_static: - raise RuntimeError('mono-static: Not supported on Windows') - if bits == '32': if os.getenv('MONO32_PREFIX'): mono_root = os.getenv('MONO32_PREFIX') @@ -82,28 +78,48 @@ def configure(env): env.Append(LIBPATH=mono_lib_path) env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + if mono_static: + lib_suffix = Environment()['LIBSUFFIX'] + mono_static_lib_name = 'libmono-static-sgen' + + if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): + raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) - if not mono_lib_name: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + if env.msvc: + env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) - if os.getenv('VCINSTALLDIR'): - env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + env.Append(LINKFLAGS='Mincore' + lib_suffix) + env.Append(LINKFLAGS='msvcrt' + lib_suffix) + env.Append(LINKFLAGS='LIBCMT' + lib_suffix) + env.Append(LINKFLAGS='Psapi' + lib_suffix) + else: + env.Append(LIBS=mono_static_lib_name) else: - env.Append(LIBS=mono_lib_name) + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') - mono_bin_path = os.path.join(mono_root, 'bin') + if not mono_lib_name: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) - mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + if env.msvc: + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) + + mono_bin_path = os.path.join(mono_root, 'bin') + + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') - if not mono_dll_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) + if not mono_dll_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) - copy_file_no_replace(mono_bin_path, 'bin', mono_dll_name + '.dll') + copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') + + copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so' mono_root = '' + mono_lib_path = '' if bits == '32': if os.getenv('MONO32_PREFIX'): @@ -148,7 +164,9 @@ def configure(env): if not mono_so_name: raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) - copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + + copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: if mono_static: raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually') @@ -157,6 +175,7 @@ def configure(env): mono_lib_path = '' mono_so_name = '' + mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() tmpenv = Environment() tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) @@ -172,7 +191,8 @@ def configure(env): if not mono_so_name: raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) - copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + copy_file(os.path.join(mono_prefix, 'lib', 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') env.Append(LINKFLAGS='-rdynamic') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index fb45136575..bbe245951e 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -118,6 +118,8 @@ void CSharpLanguage::init() { #ifdef TOOLS_ENABLED EditorNode::add_init_callback(&gdsharp_editor_init_callback); + + GLOBAL_DEF("mono/export/include_scripts_content", true); #endif } @@ -280,6 +282,15 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("@\" \""); // verbatim string literal } +static String get_base_class_name(const String &p_base_class_name, const String p_class_name) { + + String base_class = p_base_class_name; + if (p_class_name == base_class) { + base_class = "Godot." + base_class; + } + return base_class; +} + Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String script_template = "using " BINDINGS_NAMESPACE ";\n" @@ -294,7 +305,7 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin " public override void _Ready()\n" " {\n" " // Called every time the node is added to the scene.\n" - " // Initialization here\n" + " // Initialization here.\n" " \n" " }\n" "\n" @@ -306,7 +317,8 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin "// }\n" "}\n"; - script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name) .replace("%CLASS_NAME%", p_class_name); Ref<CSharpScript> script; @@ -325,12 +337,24 @@ bool CSharpLanguage::is_using_templates() { void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + src = src.replace("%BASE%", base_class_name) .replace("%CLASS%", p_class_name) .replace("%TS%", _get_indentation()); p_script->set_source_code(src); } +String CSharpLanguage::validate_path(const String &p_path) const { + + String class_name = p_path.get_file().get_basename(); + List<String> keywords; + get_reserved_words(&keywords); + if (keywords.find(class_name)) { + return TTR("Class name can't be a reserved keyword"); + } + return ""; +} + Script *CSharpLanguage::create_script() const { return memnew(CSharpScript); @@ -454,7 +478,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() #ifdef DEBUG_ENABLED // Printing an error here will result in endless recursion, so we must be careful - if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) + if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) return Vector<StackInfo>(); MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr()); @@ -1550,7 +1574,6 @@ bool CSharpScript::_update_exports() { } bool CSharpScript::_update_signals() { -#ifdef TOOLS_ENABLED if (!valid) return false; @@ -1581,8 +1604,6 @@ bool CSharpScript::_update_signals() { } return changed; -#endif - return false; } bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) { @@ -1933,8 +1954,12 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call ScriptInstance *CSharpScript::instance_create(Object *p_this) { - if (!valid) - return NULL; + if (!script_class) { + ERR_EXPLAIN("Cannot find class " + name + " for script " + get_path()); + ERR_FAIL_V(NULL); + } + + ERR_FAIL_COND_V(!valid, NULL); if (!tool && !ScriptServer::is_scripting_enabled()) { #ifdef TOOLS_ENABLED @@ -2024,20 +2049,15 @@ Error CSharpScript::reload(bool p_keep_state) { if (project_assembly) { script_class = project_assembly->get_object_derived_class(name); - if (!script_class) { - ERR_PRINTS("Cannot find class " + name + " for script " + get_path()); - } + valid = script_class != NULL; + + if (script_class) { #ifdef DEBUG_ENABLED - else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path() + "\n") .utf8()); - } #endif - valid = script_class != NULL; - - if (script_class) { tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute)); native = GDMonoUtils::get_class_native_base(script_class); @@ -2135,9 +2155,7 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { } void CSharpScript::update_signals() { -#ifdef TOOLS_ENABLED _update_signals(); -#endif } Ref<Script> CSharpScript::get_base_script() const { @@ -2269,7 +2287,9 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p CRASH_COND(mono_domain_get() == NULL); #endif -#else +#endif + +#ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) { CRASH_COND(Thread::get_caller_id() == Thread::get_main_id()); @@ -2278,14 +2298,20 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p // because this may be called by one of the editor's worker threads. // Attach this thread temporarily to reload the script. - MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); - CRASH_COND(mono_thread == NULL); + if (SCRIPTS_DOMAIN) { + MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + CRASH_COND(mono_thread == NULL); + script->reload(); + mono_thread_detach(mono_thread); + } + + } else { // just reload it normally +#endif script->reload(); - mono_thread_detach(mono_thread); - } else // just reload it normally +#ifdef TOOLS_ENABLED + } #endif - script->reload(); if (r_error) *r_error = OK; diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index ffb1d2e0f4..8666149111 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -90,15 +90,15 @@ class CSharpScript : public Script { Variant::Type type; }; + Map<StringName, Vector<Argument> > _signals; + bool signals_invalidated; + #ifdef TOOLS_ENABLED List<PropertyInfo> exported_members_cache; // members_cache Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache Set<PlaceHolderScriptInstance *> placeholders; bool source_changed_cache; bool exports_invalidated; - Map<StringName, Vector<Argument> > _signals; - bool signals_invalidated; - void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); #endif @@ -294,6 +294,7 @@ public: virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; } + virtual String validate_path(const String &p_path) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml index 0c2bb948ea..082bc30fd8 100644 --- a/modules/mono/doc_classes/@C#.xml +++ b/modules/mono/doc_classes/@C#.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@C#" category="Core" version="3.1-dev"> +<class name="@C#" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 9bd57f1d4d..a1f7399653 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSharpScript" inherits="Script" category="Core" version="3.1-dev"> +<class name="CSharpScript" inherits="Script" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 51f07523e7..985c66464b 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GodotSharp" inherits="Object" category="Core" version="3.1-dev"> +<class name="GodotSharp" inherits="Object" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 952e033565..4c598d4f37 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -70,8 +70,6 @@ #define LOCAL_RET "ret" -#define CS_CLASS_NATIVECALLS "NativeCalls" -#define CS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls" #define CS_FIELD_MEMORYOWN "memoryOwn" #define CS_PARAM_METHODBIND "method" #define CS_PARAM_INSTANCE "ptr" @@ -105,6 +103,8 @@ #define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary" #define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object" +#define BINDINGS_GENERATOR_VERSION UINT32_C(2) + const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; bool BindingsGenerator::verbose_output = false; @@ -248,14 +248,14 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { if (imethod.is_virtual) continue; - const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type); String im_sig; String im_unique_sig; if (p_itype.is_object_type) { im_sig += "IntPtr " CS_PARAM_METHODBIND ", "; - im_unique_sig += imethod.return_type.operator String() + ",IntPtr,IntPtr"; + im_unique_sig += imethod.return_type.cname.operator String() + ",IntPtr,IntPtr"; } im_sig += "IntPtr " CS_PARAM_INSTANCE; @@ -263,7 +263,7 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { // Get arguments information int i = 0; for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) { - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(F->get().type); + const TypeInterface *arg_type = _get_type_or_placeholder(F->get().type); im_sig += ", "; im_sig += arg_type->im_type_in; @@ -529,7 +529,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo "using System.Collections.Generic;\n" "\n"); cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); + cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = "); + cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = "); + cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = "); + cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + cs_icalls_content.push_back("\n"); #define ADD_INTERNAL_CALL(m_icall) \ if (!m_icall.editor_only) { \ @@ -551,7 +559,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); - String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS ".cs"); + String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs"); Error err = _save_file(internal_methods_file, cs_icalls_content); if (err != OK) @@ -626,7 +634,15 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, "using System.Collections.Generic;\n" "\n"); cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); + cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = "); + cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = "); + cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = "); + cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + cs_icalls_content.push_back("\n"); #define ADD_INTERNAL_CALL(m_icall) \ if (m_icall.editor_only) { \ @@ -648,7 +664,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); - String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS_EDITOR ".cs"); + String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs"); Error err = _save_file(internal_methods_file, cs_icalls_content); if (err != OK) @@ -714,7 +730,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.push_back(INDENT1 "public "); - output.push_back(itype.is_singleton ? "static class " : "class "); + bool is_abstract = itype.is_object_type && !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled' + output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class ")); output.push_back(itype.proxy_name); if (itype.is_singleton) { @@ -882,7 +899,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back("\";\n"); output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = "); - output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); + output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); output.push_back("." ICALL_PREFIX); output.push_back(itype.name); output.push_back(SINGLETON_ICALL_SUFFIX "();\n"); @@ -912,7 +929,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // The engine will initialize the pointer field of the managed side before calling the constructor // This is why we only allocate a new native object from the constructor if the pointer field is not set output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = "); - output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); + output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); output.push_back("." + ctor_method); output.push_back("(this);\n" CLOSE_BLOCK_L2); } else { @@ -956,7 +973,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "if (disposed) return;\n" INDENT3 "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN - " = false;\n" INDENT5 CS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR + " = false;\n" INDENT5 BINDINGS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR "(this, " BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3 "this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3 "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2); @@ -1052,12 +1069,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte } if (getter && setter) { - ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG); + ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG); } - StringName proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; + const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; - const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name); + const TypeInterface *prop_itype = _get_type_or_null(proptype_name); ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname)); @@ -1105,9 +1122,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.push_back(getter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = getter->arguments.front()->get(); - if (idx_arg.type != name_cache.type_int) { + if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum - const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type); + const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index)); } else { @@ -1122,9 +1139,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.push_back(setter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = setter->arguments.front()->get(); - if (idx_arg.type != name_cache.type_int) { + if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum - const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type); + const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", "); } else { @@ -1141,7 +1158,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) { - const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); String method_bind_field = "method_bind_" + itos(p_method_bind_count); @@ -1158,7 +1175,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Retrieve information from the arguments for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { const ArgumentInterface &iarg = F->get(); - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); // Add the current arguments to the signature // If the argument has a default value which is not a constant, we will make it Nullable @@ -1229,7 +1246,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf { if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) { p_output.push_back(MEMBER_BEGIN "private static IntPtr "); - p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); + p_output.push_back(method_bind_field + " = " BINDINGS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); p_output.push_back(p_imethod.name); p_output.push_back("\");\n"); } @@ -1310,22 +1327,20 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf const InternalCall *im_icall = match->value(); - String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS; - im_call += "." + im_icall->name + "(" + icall_params + ");\n"; + String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS; + im_call += "." + im_icall->name + "(" + icall_params + ")"; if (p_imethod.arguments.size()) p_output.push_back(cs_in_statements); if (return_type->cname == name_cache.type_void) { - p_output.push_back(im_call); + p_output.push_back(im_call + ";\n"); } else if (return_type->cs_out.empty()) { - p_output.push_back("return " + im_call); + p_output.push_back("return " + im_call + ";\n"); } else { - p_output.push_back(return_type->im_type_out); - p_output.push_back(" " LOCAL_RET " = "); - p_output.push_back(im_call); p_output.push_back(INDENT3); - p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n"); + p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); + p_output.push_back("\n"); } p_output.push_back(CLOSE_BLOCK_L2); @@ -1400,25 +1415,33 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { } output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK); + output.push_back("uint64_t get_core_api_hash() { return "); - output.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n"); + output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "; }\n"); + output.push_back("#ifdef TOOLS_ENABLED\n" "uint64_t get_editor_api_hash() { return "); - output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) + + output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "; }\n#endif // TOOLS_ENABLED\n"); + + output.push_back("uint32_t get_bindings_version() { return "); + output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); + output.push_back("uint32_t get_cs_glue_version() { return "); + output.push_back(String::num_uint64(CS_GLUE_VERSION) + "; }\n"); + output.push_back("void register_generated_icalls() " OPEN_BLOCK); output.push_back("\tgodot_register_header_icalls();"); -#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ - { \ - output.push_back("\tmono_add_internal_call("); \ - output.push_back("\"" BINDINGS_NAMESPACE "."); \ - output.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \ - output.push_back("::"); \ - output.push_back(m_icall.name); \ - output.push_back("\", (void*)"); \ - output.push_back(m_icall.name); \ - output.push_back(");\n"); \ +#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ + { \ + output.push_back("\tmono_add_internal_call("); \ + output.push_back("\"" BINDINGS_NAMESPACE "."); \ + output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \ + output.push_back("::"); \ + output.push_back(m_icall.name); \ + output.push_back("\", (void*)"); \ + output.push_back(m_icall.name); \ + output.push_back(");\n"); \ } bool tools_sequence = false; @@ -1486,6 +1509,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { return OK; } +uint32_t BindingsGenerator::get_version() { + return BINDINGS_GENERATOR_VERSION; +} + +uint32_t BindingsGenerator::get_cs_glue_version() { + return CS_GLUE_VERSION; +} + Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) { FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE); @@ -1507,9 +1538,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte if (p_imethod.is_virtual) return OK; // Ignore - bool ret_void = p_imethod.return_type == name_cache.type_void; + bool ret_void = p_imethod.return_type.cname == name_cache.type_void; - const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); String argc_str = itos(p_imethod.arguments.size()); @@ -1521,16 +1552,16 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte int i = 0; for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { const ArgumentInterface &iarg = F->get(); - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); String c_param_name = "arg" + itos(i + 1); if (p_imethod.is_vararg) { if (i < p_imethod.arguments.size() - 1) { c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name); - c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, "; - c_in_statements += sformat("&%s_in", c_param_name); - c_in_statements += ");\n"; + c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set("; + c_in_statements += itos(i); + c_in_statements += sformat(", &%s_in);\n", c_param_name); } } else { if (i > 0) @@ -1662,42 +1693,49 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte return OK; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const StringName &p_cname) { +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) { - const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_cname); + const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname); if (builtin_type_match) return &builtin_type_match->get(); - const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_cname); + const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname); if (obj_type_match) return &obj_type_match.get(); - const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_cname); + if (p_typeref.is_enum) { + const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname); + + if (enum_match) + return &enum_match->get(); - if (enum_match) - return &enum_match->get(); + // Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead. + const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int); + ERR_FAIL_NULL_V(int_match, NULL); + return &int_match->get(); + } return NULL; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_placeholder(const StringName &p_cname) { +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) { - const TypeInterface *found = _get_type_by_name_or_null(p_cname); + const TypeInterface *found = _get_type_or_null(p_typeref); if (found) return found; - ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_cname.operator String()); + ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_typeref.cname.operator String()); - const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_cname); + const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname); if (match) return &match->get(); TypeInterface placeholder; - TypeInterface::create_placeholder_type(placeholder, p_cname); + TypeInterface::create_placeholder_type(placeholder, p_typeref.cname); return &placeholder_types.insert(placeholder.cname, placeholder)->get(); } @@ -1841,7 +1879,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { // The method Object.free is registered as a virtual method, but without the virtual flag. // This is because this method is not supposed to be overridden, but called. // We assume the return type is void. - imethod.return_type = name_cache.type_void; + imethod.return_type.cname = name_cache.type_void; // Actually, more methods like this may be added in the future, // which could actually will return something different. @@ -1856,21 +1894,22 @@ void BindingsGenerator::_populate_object_type_interfaces() { } else { ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); } - } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant? - imethod.return_type = return_info.class_name; + } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + imethod.return_type.cname = return_info.class_name; + imethod.return_type.is_enum = true; } else if (return_info.class_name != StringName()) { - imethod.return_type = return_info.class_name; + imethod.return_type.cname = return_info.class_name; } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { - imethod.return_type = return_info.hint_string; + imethod.return_type.cname = return_info.hint_string; } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { - imethod.return_type = name_cache.type_Variant; + imethod.return_type.cname = name_cache.type_Variant; } else if (return_info.type == Variant::NIL) { - imethod.return_type = name_cache.type_void; + imethod.return_type.cname = name_cache.type_void; } else { - imethod.return_type = Variant::get_type_name(return_info.type); + imethod.return_type.cname = Variant::get_type_name(return_info.type); } - if (!itype.requires_collections && imethod.return_type == name_cache.type_Dictionary) + if (!itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) itype.requires_collections = true; for (int i = 0; i < argc; i++) { @@ -1879,21 +1918,22 @@ void BindingsGenerator::_populate_object_type_interfaces() { ArgumentInterface iarg; iarg.name = arginfo.name; - if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant? - iarg.type = arginfo.class_name; + if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + iarg.type.cname = arginfo.class_name; + iarg.type.is_enum = true; } else if (arginfo.class_name != StringName()) { - iarg.type = arginfo.class_name; + iarg.type.cname = arginfo.class_name; } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { - iarg.type = arginfo.hint_string; + iarg.type.cname = arginfo.hint_string; } else if (arginfo.type == Variant::NIL) { - iarg.type = name_cache.type_Variant; + iarg.type.cname = name_cache.type_Variant; } else { - iarg.type = Variant::get_type_name(arginfo.type); + iarg.type.cname = Variant::get_type_name(arginfo.type); } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); - if (!itype.requires_collections && iarg.type == name_cache.type_Dictionary) + if (!itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) itype.requires_collections = true; if (m && m->has_default_argument(i)) { @@ -1905,7 +1945,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { if (imethod.is_vararg) { ArgumentInterface ivararg; - ivararg.type = name_cache.type_VarArg; + ivararg.type.cname = name_cache.type_VarArg; ivararg.name = "@args"; imethod.add_argument(ivararg); } @@ -1990,17 +2030,11 @@ void BindingsGenerator::_populate_object_type_interfaces() { itype.enums.push_back(ienum); TypeInterface enum_itype; + enum_itype.is_enum = true; enum_itype.name = itype.name + "." + String(*k); enum_itype.cname = StringName(enum_itype.name); enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name; - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.cs_type = enum_itype.proxy_name; - enum_itype.im_type_in = enum_itype.proxy_name; - enum_itype.im_type_out = enum_itype.proxy_name; - enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[enum_itype.proxy_name]; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); } @@ -2035,7 +2069,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg switch (p_val.get_type()) { case Variant::NIL: - if (ClassDB::class_exists(r_iarg.type)) { + if (ClassDB::class_exists(r_iarg.type.cname)) { // Object type r_iarg.default_argument = "null"; } else { @@ -2048,7 +2082,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = bool(p_val) ? "true" : "false"; break; case Variant::INT: - if (r_iarg.type != name_cache.type_int) { + if (r_iarg.type.cname != name_cache.type_int) { r_iarg.default_argument = "(%s)" + r_iarg.default_argument; } break; @@ -2109,7 +2143,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg default: {} } - if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type == name_cache.type_Variant && r_iarg.default_argument != "null") + if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; } @@ -2128,7 +2162,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_arg_in = "&%s_in"; \ itype.c_type_in = m_type_in; \ itype.cs_in = "ref %s"; \ - itype.cs_out = "return (" #m_type ")%0;"; \ + itype.cs_out = "return (%1)%0;"; \ itype.im_type_out = "object"; \ builtin_types.insert(itype.cname, itype); \ } @@ -2223,7 +2257,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new NodePath(%0);"; + itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; _populate_builtin_type(itype, Variant::NODE_PATH); @@ -2246,7 +2280,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new RID(%0);"; + itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; _populate_builtin_type(itype, Variant::_RID); @@ -2375,11 +2409,11 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant:: iarg.name = pi.name; if (pi.type == Variant::NIL) - iarg.type = name_cache.type_Variant; + iarg.type.cname = name_cache.type_Variant; else - iarg.type = Variant::get_type_name(pi.type); + iarg.type.cname = Variant::get_type_name(pi.type); - if (!r_itype.requires_collections && iarg.type == name_cache.type_Dictionary) + if (!r_itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) r_itype.requires_collections = true; if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0) @@ -2390,12 +2424,12 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant:: if (mi.return_val.type == Variant::NIL) { if (mi.return_val.name != "") - imethod.return_type = name_cache.type_Variant; + imethod.return_type.cname = name_cache.type_Variant; } else { - imethod.return_type = Variant::get_type_name(mi.return_val.type); + imethod.return_type.cname = Variant::get_type_name(mi.return_val.type); } - if (!r_itype.requires_collections && imethod.return_type == name_cache.type_Dictionary) + if (!r_itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) r_itype.requires_collections = true; if (r_itype.class_doc) { @@ -2461,13 +2495,11 @@ void BindingsGenerator::_populate_global_constants() { EnumInterface &ienum = E->get(); TypeInterface enum_itype; - enum_itype = TypeInterface::create_value_type(ienum.cname); - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.im_type_in = enum_itype.name; - enum_itype.im_type_out = enum_itype.name; + enum_itype.is_enum = true; + enum_itype.name = ienum.cname.operator String(); + enum_itype.cname = ienum.cname; + enum_itype.proxy_name = enum_itype.name; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); ienum.prefix = _determine_enum_prefix(ienum); @@ -2488,15 +2520,13 @@ void BindingsGenerator::_populate_global_constants() { hardcoded_enums.push_back("Vector3.Axis"); for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) { // These enums are not generated and must be written manually (e.g.: Vector3.Axis) - // Here, we are assuming core types do not begin with underscore + // Here, we assume core types do not begin with underscore TypeInterface enum_itype; - enum_itype = TypeInterface::create_value_type(E->get()); - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.im_type_in = enum_itype.name; - enum_itype.im_type_out = enum_itype.name; + enum_itype.is_enum = true; + enum_itype.name = E->get().operator String(); + enum_itype.cname = E->get(); + enum_itype.proxy_name = enum_itype.name; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); } } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 9b5a9cea88..5b33a0e53f 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -81,6 +81,15 @@ class BindingsGenerator { const DocData::PropertyDoc *prop_doc; }; + struct TypeReference { + StringName cname; + bool is_enum; + + TypeReference() { + is_enum = false; + } + }; + struct ArgumentInterface { enum DefaultParamMode { CONSTANT, @@ -88,7 +97,8 @@ class BindingsGenerator { NULLABLE_REF }; - StringName type; + TypeReference type; + String name; String default_argument; DefaultParamMode def_param_mode; @@ -110,7 +120,7 @@ class BindingsGenerator { /** * [TypeInterface::name] of the return type */ - StringName return_type; + TypeReference return_type; /** * Determines if the method has a variable number of arguments (VarArg) @@ -146,7 +156,7 @@ class BindingsGenerator { } MethodInterface() { - return_type = BindingsGenerator::get_singleton()->name_cache.type_void; + return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void; is_vararg = false; is_virtual = false; requires_object_call = false; @@ -175,6 +185,7 @@ class BindingsGenerator { ClassDB::APIType api_type; + bool is_enum; bool is_object_type; bool is_singleton; bool is_reference; @@ -276,7 +287,9 @@ class BindingsGenerator { * One or more statements that determine how a variable of this type is returned from a method. * It must contain the return statement(s). * Formatting elements: - * %0 or %s: name of the variable to be returned + * %0: internal method call statement + * %1: [cs_type] of the return type + * %2: [im_type_out] of the return type */ String cs_out; @@ -293,8 +306,6 @@ class BindingsGenerator { /** * Type used for the return type of internal call methods. - * If [cs_out] is not empty and the method return type is not void, - * it is also used for the type of the return variable. */ String im_type_out; @@ -379,10 +390,24 @@ class BindingsGenerator { r_itype.im_type_out = r_itype.proxy_name; } + static void postsetup_enum_type(TypeInterface &r_enum_itype) { + r_enum_itype.c_arg_in = "&%s"; + r_enum_itype.c_type = "int"; + r_enum_itype.c_type_in = "int"; + r_enum_itype.c_type_out = "int"; + r_enum_itype.cs_type = r_enum_itype.proxy_name; + r_enum_itype.cs_in = "(int)%s"; + r_enum_itype.cs_out = "return (%1)%0;"; + r_enum_itype.im_type_in = "int"; + r_enum_itype.im_type_out = "int"; + r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; + } + TypeInterface() { api_type = ClassDB::API_NONE; + is_enum = false; is_object_type = false; is_singleton = false; is_reference = false; @@ -492,6 +517,8 @@ class BindingsGenerator { return "Ref"; else if (p_type.is_object_type) return "Obj"; + else if (p_type.is_enum) + return "int"; return p_type.name; } @@ -501,8 +528,8 @@ class BindingsGenerator { void _generate_header_icalls(); void _generate_method_icalls(const TypeInterface &p_itype); - const TypeInterface *_get_type_by_name_or_null(const StringName &p_cname); - const TypeInterface *_get_type_by_name_or_placeholder(const StringName &p_cname); + const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); + const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype); @@ -536,6 +563,9 @@ public: Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true); Error generate_glue(const String &p_output_dir); + static uint32_t get_version(); + static uint32_t get_cs_glue_version(); + void initialize(); _FORCE_INLINE_ static BindingsGenerator *get_singleton() { diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 6b41b10981..2f2b5768db 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -33,7 +33,6 @@ #include "main/main.h" #include "../godotsharp_dirs.h" -#include "../mono_gd/gd_mono.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" @@ -178,13 +177,15 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s return true; } -bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) { +bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) { String assembly_file = p_assembly_name + ".dll"; String assembly_src = p_src_dir.plus_file(assembly_file); String assembly_dst = p_dst_dir.plus_file(assembly_file); - if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst)) { + if (!FileAccess::exists(assembly_dst) || + FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) || + GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) { DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String xml_file = p_assembly_name + ".xml"; @@ -200,36 +201,49 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & memdelete(da); if (err != OK) { - show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll"); + show_build_error_dialog("Failed to copy " + assembly_file); return false; } + + GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false); } return true; } -bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { +String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) { + + uint64_t api_hash = p_api_type == APIAssembly::API_CORE ? + GDMono::get_singleton()->get_api_core_hash() : + GDMono::get_singleton()->get_api_editor_hash(); + return String::num_uint64(api_hash) + + "_" + String::num_uint64(BindingsGenerator::get_version()) + + "_" + String::num_uint64(BindingsGenerator::get_cs_glue_version()); +} + +bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) { - String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; + String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; String api_build_config = "Release"; EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4); pr.step("Generating " + api_name + " solution"); - uint64_t core_hash = GDMono::get_singleton()->get_api_core_hash(); - uint64_t editor_hash = GDMono::get_singleton()->get_api_editor_hash(); - - String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash)); - String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash)); + String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() + .plus_file(_api_folder_name(APIAssembly::API_CORE)) + .plus_file(API_ASSEMBLY_NAME); + String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() + .plus_file(_api_folder_name(APIAssembly::API_EDITOR)) + .plus_file(EDITOR_API_ASSEMBLY_NAME); - String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir; + String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir; String api_sln_file = api_sln_dir.plus_file(api_name + ".sln"); if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) { String core_api_assembly; - if (p_api_type == API_EDITOR) { + if (p_api_type == APIAssembly::API_EDITOR) { core_api_assembly = core_api_sln_dir.plus_file("bin") .plus_file(api_build_config) .plus_file(API_ASSEMBLY_NAME ".dll"); @@ -242,7 +256,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { BindingsGenerator *gen = BindingsGenerator::get_singleton(); bool gen_verbose = OS::get_singleton()->is_stdout_verbose(); - Error err = p_api_type == API_CORE ? + Error err = p_api_type == APIAssembly::API_CORE ? gen->generate_cs_core_project(api_sln_dir, gen_verbose) : gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose); @@ -275,7 +289,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { // Copy the built assembly to the assemblies directory String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config); - if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name)) + if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type)) return false; pr.step("Done"); @@ -283,22 +297,22 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { return true; } -bool GodotSharpBuilds::build_project_blocking() { +bool GodotSharpBuilds::build_project_blocking(const String &p_config) { if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) return true; // No solution to build - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) return false; - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) return false; EditorProgress pr("mono_project_debug_build", "Building project solution...", 2); pr.step("Building project solution"); - MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), "Tools"); + MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config); if (!GodotSharpBuilds::get_singleton()->build(build_info)) { GodotSharpBuilds::show_build_error_dialog("Failed to build project solution"); return false; @@ -309,6 +323,11 @@ bool GodotSharpBuilds::build_project_blocking() { return true; } +bool GodotSharpBuilds::editor_build_callback() { + + return build_project_blocking("Tools"); +} + GodotSharpBuilds *GodotSharpBuilds::singleton = NULL; void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) { @@ -362,7 +381,7 @@ GodotSharpBuilds::GodotSharpBuilds() { singleton = this; - EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking); + EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback); // Build tool settings EditorSettings *ed_settings = EditorSettings::get_singleton(); @@ -462,6 +481,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { if (ex) { exited = true; + GDMonoUtils::print_unhandled_exception(ex); String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); @@ -482,6 +502,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { if (ex) { exited = true; + GDMonoUtils::print_unhandled_exception(ex); String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); @@ -504,11 +525,10 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { } } -GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) { - - build_info = p_build_info; - build_tab = NULL; - exit_callback = p_callback; - exited = true; - exit_code = -1; +GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) : + build_info(p_build_info), + build_tab(NULL), + exit_callback(p_callback), + exited(true), + exit_code(-1) { } diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h index 5d2390ecd9..27b771e324 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -31,6 +31,7 @@ #ifndef GODOTSHARP_BUILDS_H #define GODOTSHARP_BUILDS_H +#include "../mono_gd/gd_mono.h" #include "mono_bottom_panel.h" #include "mono_build_info.h" @@ -56,17 +57,14 @@ private: HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds; + static String _api_folder_name(APIAssembly::Type p_api_type); + static GodotSharpBuilds *singleton; friend class GDMono; static void _register_internal_calls(); public: - enum APIType { - API_CORE, - API_EDITOR - }; - enum BuildTool { MSBUILD_MONO, #ifdef WINDOWS_ENABLED @@ -89,11 +87,13 @@ public: bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL); static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config); - static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name); + static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type); + + static bool make_api_sln(APIAssembly::Type p_api_type); - static bool make_api_sln(APIType p_api_type); + static bool build_project_blocking(const String &p_config); - static bool build_project_blocking(); + static bool editor_build_callback(); GodotSharpBuilds(); ~GodotSharpBuilds(); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 0ef3adfdd0..998da8bda3 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -41,6 +41,7 @@ #include "../utils/path_utils.h" #include "bindings_generator.h" #include "csharp_project.h" +#include "godotsharp_export.h" #include "net_solution.h" #ifdef WINDOWS_ENABLED @@ -84,10 +85,10 @@ bool GodotSharpEditor::_create_project_solution() { return false; } - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) return false; - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) return false; pr.step(TTR("Done")); @@ -278,10 +279,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { String about_text = String("C# support in Godot Engine is a brand new feature and a work in progress.\n") + "It is at the alpha stage and thus not suitable for use in production.\n\n" + - "As of Godot 3.0, C# support is not feature-complete and can crash in some situations. " + - "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases.\n" + + "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " + + "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " + + "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" + "The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " + - "but not to share them as standalone binaries. This feature is of course high on the priority list and should be available in 3.0.1.\n\n" + + "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" + "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" + " https://github.com/godotengine/godot/issues\n\n" + "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!"; @@ -315,6 +317,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { EditorSettings *ed_settings = EditorSettings::get_singleton(); EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE); ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code")); + + // Export plugin + Ref<GodotSharpExport> godotsharp_export; + godotsharp_export.instance(); + EditorExport::get_singleton()->add_export_plugin(godotsharp_export); } GodotSharpEditor::~GodotSharpEditor() { diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index 81c49aec30..66da814c8b 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -32,7 +32,6 @@ #define GODOTSHARP_EDITOR_H #include "godotsharp_builds.h" - #include "monodevelop_instance.h" class GodotSharpEditor : public Node { diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp new file mode 100644 index 0000000000..cd09e6516a --- /dev/null +++ b/modules/mono/editor/godotsharp_export.cpp @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* godotsharp_export.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "godotsharp_export.h" + +#include "../csharp_script.h" +#include "../godotsharp_defs.h" +#include "../godotsharp_dirs.h" +#include "godotsharp_builds.h" + +void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) { + + if (p_type != CSharpLanguage::get_singleton()->get_type()) + return; + + ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension()); + + // TODO what if the source file is not part of the game's C# project + + if (!GLOBAL_GET("mono/export/include_scripts_content")) { + // We don't want to include the source code on exported games + add_file(p_path, Vector<uint8_t>(), false); + skip(); + } +} + +void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) { + + // TODO right now there is no way to stop the export process with an error + + ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); + ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain()); + + String build_config = p_debug ? "Debug" : "Release"; + + ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); + + // Add API assemblies + + String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); + ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path)); + + String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path)); + + // Add project assembly + + String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name"); + if (project_dll_name.empty()) { + project_dll_name = "UnnamedProject"; + } + + String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll"); + String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll"); + ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path)); + + // Add dependencies + + MonoDomain *prev_domain = mono_domain_get(); + MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); + + ERR_FAIL_COND(!export_domain); + ERR_FAIL_COND(!mono_domain_set(export_domain, false)); + + Map<String, String> dependencies; + dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path()); + + GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true); + + ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); + ERR_FAIL_COND(!scripts_assembly); + + Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies); + + GDMono::get_singleton()->finalize_and_unload_domain(export_domain); + mono_domain_set(prev_domain, false); + + ERR_FAIL_COND(depend_error != OK); + + for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { + String depend_src_path = E->value(); + String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); + ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path)); + } +} + +bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) { + + FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, false); + + Vector<uint8_t> data; + data.resize(f->get_len()); + f->get_buffer(data.ptrw(), data.size()); + + add_file(p_dst_path, data, false); + + return true; +} + +Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) { + + MonoImage *image = p_assembly->get_image(); + + for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { + MonoAssemblyName *ref_aname = aname_prealloc; + mono_assembly_get_assemblyref(image, i, ref_aname); + String ref_name = mono_assembly_name_get_name(ref_aname); + + if (ref_name == "mscorlib" || r_dependencies.find(ref_name)) + continue; + + GDMonoAssembly *ref_assembly = NULL; + if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) { + ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name); + ERR_FAIL_V(ERR_CANT_RESOLVE); + } + + r_dependencies.insert(ref_name, ref_assembly->get_path()); + + Error err = _get_assembly_dependencies(ref_assembly, r_dependencies); + if (err != OK) + return err; + } + + return OK; +} + +GodotSharpExport::GodotSharpExport() { + // MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves. + // There isn't any api to allocate an empty one either, so we need to do it this way. + aname_prealloc = mono_assembly_name_new("whatever"); + mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included) +} + +GodotSharpExport::~GodotSharpExport() { + if (aname_prealloc) + mono_free(aname_prealloc); +} diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h new file mode 100644 index 0000000000..b38db9660c --- /dev/null +++ b/modules/mono/editor/godotsharp_export.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* godotsharp_export.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 GODOTSHARP_EXPORT_H +#define GODOTSHARP_EXPORT_H + +#include <mono/metadata/image.h> + +#include "editor/editor_export.h" + +#include "../mono_gd/gd_mono_header.h" + +class GodotSharpExport : public EditorExportPlugin { + + MonoAssemblyName *aname_prealloc; + + bool _add_assembly(const String &p_src_path, const String &p_dst_path); + + Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies); + +protected: + virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features); + virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags); + +public: + GodotSharpExport(); + ~GodotSharpExport(); +}; + +#endif // GODOTSHARP_EXPORT_H diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 20378a0162..1b5a303835 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -142,7 +142,7 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) { void MonoBottomPanel::_build_project_pressed() { - GodotSharpBuilds::get_singleton()->build_project_blocking(); + GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); MonoReloadNode::get_singleton()->restart_reload_timer(); CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); @@ -407,9 +407,14 @@ void MonoBuildTab::stop_build() { void MonoBuildTab::_issue_activated(int p_idx) { - ERR_FAIL_INDEX(p_idx, issues.size()); + ERR_FAIL_INDEX(p_idx, issues_list->get_item_count()); - const BuildIssue &issue = issues[p_idx]; + // Get correct issue idx from issue list + int issue_idx = this->issues_list->get_item_metadata(p_idx); + + ERR_FAIL_INDEX(issue_idx, issues.size()); + + const BuildIssue &issue = issues[issue_idx]; if (issue.project_file.empty() && issue.file.empty()) return; @@ -437,21 +442,16 @@ void MonoBuildTab::_bind_methods() { ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated); } -MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) { - - build_info = p_build_info; - logs_dir = p_logs_dir; - - build_exited = false; - - issues_list = memnew(ItemList); +MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) : + build_info(p_build_info), + logs_dir(p_logs_dir), + build_exited(false), + issues_list(memnew(ItemList)), + error_count(0), + warning_count(0), + errors_visible(true), + warnings_visible(true) { issues_list->set_v_size_flags(SIZE_EXPAND_FILL); issues_list->connect("item_activated", this, "_issue_activated"); add_child(issues_list); - - error_count = 0; - warning_count = 0; - - errors_visible = true; - warnings_visible = true; } diff --git a/modules/mono/glue/cs_files/AABB.cs b/modules/mono/glue/cs_files/AABB.cs index e6e12f7ba3..39f2d2ed51 100644 --- a/modules/mono/glue/cs_files/AABB.cs +++ b/modules/mono/glue/cs_files/AABB.cs @@ -1,11 +1,15 @@ -using System; - // file: core/math/aabb.h // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/math/aabb.cpp // commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -45,12 +49,12 @@ namespace Godot Vector3 dst_min = with.position; Vector3 dst_max = with.position + with.size; - return ((src_min.x <= dst_min.x) && - (src_max.x > dst_max.x) && - (src_min.y <= dst_min.y) && - (src_max.y > dst_max.y) && - (src_min.z <= dst_min.z) && - (src_max.z > dst_max.z)); + return src_min.x <= dst_min.x && + src_max.x > dst_max.x && + src_min.y <= dst_min.y && + src_max.y > dst_max.y && + src_min.z <= dst_min.z && + src_max.z > dst_max.z; } public AABB Expand(Vector3 to_point) @@ -75,7 +79,7 @@ namespace Godot return new AABB(begin, end - begin); } - public float GetArea() + public real_t GetArea() { return size.x * size.y * size.z; } @@ -107,8 +111,8 @@ namespace Godot public Vector3 GetLongestAxis() { - Vector3 axis = new Vector3(1f, 0f, 0f); - float max_size = size.x; + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = size.x; if (size.y > max_size) { @@ -119,7 +123,6 @@ namespace Godot if (size.z > max_size) { axis = new Vector3(0f, 0f, 1f); - max_size = size.z; } return axis; @@ -127,8 +130,8 @@ namespace Godot public Vector3.Axis GetLongestAxisIndex() { - Vector3.Axis axis = Vector3.Axis.X; - float max_size = size.x; + var axis = Vector3.Axis.X; + real_t max_size = size.x; if (size.y > max_size) { @@ -139,15 +142,14 @@ namespace Godot if (size.z > max_size) { axis = Vector3.Axis.Z; - max_size = size.z; } return axis; } - public float GetLongestAxisSize() + public real_t GetLongestAxisSize() { - float max_size = size.x; + real_t max_size = size.x; if (size.y > max_size) max_size = size.y; @@ -160,8 +162,8 @@ namespace Godot public Vector3 GetShortestAxis() { - Vector3 axis = new Vector3(1f, 0f, 0f); - float max_size = size.x; + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = size.x; if (size.y < max_size) { @@ -172,7 +174,6 @@ namespace Godot if (size.z < max_size) { axis = new Vector3(0f, 0f, 1f); - max_size = size.z; } return axis; @@ -180,8 +181,8 @@ namespace Godot public Vector3.Axis GetShortestAxisIndex() { - Vector3.Axis axis = Vector3.Axis.X; - float max_size = size.x; + var axis = Vector3.Axis.X; + real_t max_size = size.x; if (size.y < max_size) { @@ -192,15 +193,14 @@ namespace Godot if (size.z < max_size) { axis = Vector3.Axis.Z; - max_size = size.z; } return axis; } - public float GetShortestAxisSize() + public real_t GetShortestAxisSize() { - float max_size = size.x; + real_t max_size = size.x; if (size.y < max_size) max_size = size.y; @@ -217,14 +217,14 @@ namespace Godot Vector3 ofs = position + half_extents; return ofs + new Vector3( - (dir.x > 0f) ? -half_extents.x : half_extents.x, - (dir.y > 0f) ? -half_extents.y : half_extents.y, - (dir.z > 0f) ? -half_extents.z : half_extents.z); + dir.x > 0f ? -half_extents.x : half_extents.x, + dir.y > 0f ? -half_extents.y : half_extents.y, + dir.z > 0f ? -half_extents.z : half_extents.z); } - public AABB Grow(float by) + public AABB Grow(real_t by) { - AABB res = this; + var res = this; res.position.x -= by; res.position.y -= by; @@ -277,48 +277,42 @@ namespace Godot { return new AABB(); } - else - { - min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x; - max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x; - } + + min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x; + max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x; if (src_min.y > dst_max.y || src_max.y < dst_min.y) { return new AABB(); } - else - { - min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y; - max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y; - } + + min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y; + max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y; if (src_min.z > dst_max.z || src_max.z < dst_min.z) { return new AABB(); } - else - { - min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z; - max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z; - } + + min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z; + max.z = src_max.z < dst_max.z ? src_max.z : dst_max.z; return new AABB(min, max - min); } public bool Intersects(AABB with) { - if (position.x >= (with.position.x + with.size.x)) + if (position.x >= with.position.x + with.size.x) return false; - if ((position.x + size.x) <= with.position.x) + if (position.x + size.x <= with.position.x) return false; - if (position.y >= (with.position.y + with.size.y)) + if (position.y >= with.position.y + with.size.y) return false; - if ((position.y + size.y) <= with.position.y) + if (position.y + size.y <= with.position.y) return false; - if (position.z >= (with.position.z + with.size.z)) + if (position.z >= with.position.z + with.size.z) return false; - if ((position.z + size.z) <= with.position.z) + if (position.z + size.z <= with.position.z) return false; return true; @@ -335,7 +329,7 @@ namespace Godot new Vector3(position.x + size.x, position.y, position.z), new Vector3(position.x + size.x, position.y, position.z + size.z), new Vector3(position.x + size.x, position.y + size.y, position.z), - new Vector3(position.x + size.x, position.y + size.y, position.z + size.z), + new Vector3(position.x + size.x, position.y + size.y, position.z + size.z) }; bool over = false; @@ -354,23 +348,23 @@ namespace Godot public bool IntersectsSegment(Vector3 from, Vector3 to) { - float min = 0f; - float max = 1f; + real_t min = 0f; + real_t max = 1f; for (int i = 0; i < 3; i++) { - float seg_from = from[i]; - float seg_to = to[i]; - float box_begin = position[i]; - float box_end = box_begin + size[i]; - float cmin, cmax; + real_t seg_from = from[i]; + real_t seg_to = to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; if (seg_from < seg_to) { if (seg_from > box_end || seg_to < box_begin) return false; - float length = seg_to - seg_from; + real_t length = seg_to - seg_from; cmin = seg_from < box_begin ? (box_begin - seg_from) / length : 0f; cmax = seg_to > box_end ? (box_end - seg_from) / length : 1f; } @@ -379,7 +373,7 @@ namespace Godot if (seg_to > box_end || seg_from < box_begin) return false; - float length = seg_to - seg_from; + real_t length = seg_to - seg_from; cmin = seg_from > box_end ? (box_end - seg_from) / length : 0f; cmax = seg_to < box_begin ? (box_begin - seg_from) / length : 1f; } @@ -402,24 +396,25 @@ namespace Godot { Vector3 beg_1 = position; Vector3 beg_2 = with.position; - Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1; - Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2; + var end_1 = new Vector3(size.x, size.y, size.z) + beg_1; + var end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2; - Vector3 min = new Vector3( - (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x, - (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y, - (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z + var min = new Vector3( + beg_1.x < beg_2.x ? beg_1.x : beg_2.x, + beg_1.y < beg_2.y ? beg_1.y : beg_2.y, + beg_1.z < beg_2.z ? beg_1.z : beg_2.z ); - Vector3 max = new Vector3( - (end_1.x > end_2.x) ? end_1.x : end_2.x, - (end_1.y > end_2.y) ? end_1.y : end_2.y, - (end_1.z > end_2.z) ? end_1.z : end_2.z + var max = new Vector3( + end_1.x > end_2.x ? end_1.x : end_2.x, + end_1.y > end_2.y ? end_1.y : end_2.y, + end_1.z > end_2.z ? end_1.z : end_2.z ); return new AABB(min, max - min); } - + + // Constructors public AABB(Vector3 position, Vector3 size) { this.position = position; @@ -460,8 +455,8 @@ namespace Godot { return String.Format("{0} - {1}", new object[] { - this.position.ToString(), - this.size.ToString() + position.ToString(), + size.ToString() }); } @@ -469,8 +464,8 @@ namespace Godot { return String.Format("{0} - {1}", new object[] { - this.position.ToString(format), - this.size.ToString(format) + position.ToString(format), + size.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs index c6cdc069ef..929b13d70c 100644 --- a/modules/mono/glue/cs_files/Basis.cs +++ b/modules/mono/glue/cs_files/Basis.cs @@ -1,5 +1,10 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -13,8 +18,7 @@ namespace Godot new Vector3(0f, 0f, 1f) ); - private static readonly Basis[] orthoBases = new Basis[24] - { + private static readonly Basis[] orthoBases = { new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f), new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f), new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f), @@ -41,9 +45,27 @@ namespace Godot new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f) }; - public Vector3 x; - public Vector3 y; - public Vector3 z; + public Vector3 x + { + get { return GetAxis(0); } + set { SetAxis(0, value); } + } + + public Vector3 y + { + get { return GetAxis(1); } + set { SetAxis(1, value); } + } + + public Vector3 z + { + get { return GetAxis(2); } + set { SetAxis(2, value); } + } + + private Vector3 _x; + private Vector3 _y; + private Vector3 _z; public static Basis Identity { @@ -70,11 +92,11 @@ namespace Godot switch (index) { case 0: - return x; + return _x; case 1: - return y; + return _y; case 2: - return z; + return _z; default: throw new IndexOutOfRangeException(); } @@ -84,13 +106,13 @@ namespace Godot switch (index) { case 0: - x = value; + _x = value; return; case 1: - y = value; + _y = value; return; case 2: - z = value; + _z = value; return; default: throw new IndexOutOfRangeException(); @@ -98,18 +120,18 @@ namespace Godot } } - public float this[int index, int axis] + public real_t this[int index, int axis] { get { switch (index) { case 0: - return x[axis]; + return _x[axis]; case 1: - return y[axis]; + return _y[axis]; case 2: - return z[axis]; + return _z[axis]; default: throw new IndexOutOfRangeException(); } @@ -119,13 +141,13 @@ namespace Godot switch (index) { case 0: - x[axis] = value; + _x[axis] = value; return; case 1: - y[axis] = value; + _y[axis] = value; return; case 2: - z[axis] = value; + _z[axis] = value; return; default: throw new IndexOutOfRangeException(); @@ -143,7 +165,7 @@ namespace Godot ); } - public float Determinant() + public real_t Determinant() { return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) - this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) + @@ -155,14 +177,21 @@ namespace Godot return new Vector3(this[0, axis], this[1, axis], this[2, axis]); } + public void SetAxis(int axis, Vector3 value) + { + this[0, axis] = value.x; + this[1, axis] = value.y; + this[2, axis] = value.z; + } + public Vector3 GetEuler() { - Basis m = this.Orthonormalized(); + Basis m = Orthonormalized(); Vector3 euler; euler.z = 0.0f; - float mxy = m.y[2]; + real_t mxy = m[1, 2]; if (mxy < 1.0f) @@ -170,19 +199,19 @@ namespace Godot if (mxy > -1.0f) { euler.x = Mathf.Asin(-mxy); - euler.y = Mathf.Atan2(m.x[2], m.z[2]); - euler.z = Mathf.Atan2(m.y[0], m.y[1]); + euler.y = Mathf.Atan2(m[0, 2], m[2, 2]); + euler.z = Mathf.Atan2(m[1, 0], m[1, 1]); } else { - euler.x = Mathf.PI * 0.5f; - euler.y = -Mathf.Atan2(-m.x[1], m.x[0]); + euler.x = Mathf.Pi * 0.5f; + euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); } } else { - euler.x = -Mathf.PI * 0.5f; - euler.y = -Mathf.Atan2(m.x[1], m.x[0]); + euler.x = -Mathf.Pi * 0.5f; + euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); } return euler; @@ -190,13 +219,13 @@ namespace Godot public int GetOrthogonalIndex() { - Basis orth = this; + var orth = this; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - float v = orth[i, j]; + real_t v = orth[i, j]; if (v > 0.5f) v = 1.0f; @@ -220,28 +249,27 @@ namespace Godot public Basis Inverse() { - Basis inv = this; + var inv = this; - float[] co = new float[3] - { + real_t[] co = { inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1], inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2], inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0] }; - float det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2]; + real_t det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2]; if (det == 0) { return new Basis ( - float.NaN, float.NaN, float.NaN, - float.NaN, float.NaN, float.NaN, - float.NaN, float.NaN, float.NaN + real_t.NaN, real_t.NaN, real_t.NaN, + real_t.NaN, real_t.NaN, real_t.NaN, + real_t.NaN, real_t.NaN, real_t.NaN ); } - float s = 1.0f / det; + real_t s = 1.0f / det; inv = new Basis ( @@ -266,22 +294,22 @@ namespace Godot Vector3 zAxis = GetAxis(2); xAxis.Normalize(); - yAxis = (yAxis - xAxis * (xAxis.Dot(yAxis))); + yAxis = yAxis - xAxis * xAxis.Dot(yAxis); yAxis.Normalize(); - zAxis = (zAxis - xAxis * (xAxis.Dot(zAxis)) - yAxis * (yAxis.Dot(zAxis))); + zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis); zAxis.Normalize(); - return Basis.CreateFromAxes(xAxis, yAxis, zAxis); + return CreateFromAxes(xAxis, yAxis, zAxis); } - public Basis Rotated(Vector3 axis, float phi) + public Basis Rotated(Vector3 axis, real_t phi) { return new Basis(axis, phi) * this; } public Basis Scaled(Vector3 scale) { - Basis m = this; + var m = this; m[0, 0] *= scale.x; m[0, 1] *= scale.x; @@ -296,26 +324,26 @@ namespace Godot return m; } - public float Tdotx(Vector3 with) + public real_t Tdotx(Vector3 with) { return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2]; } - public float Tdoty(Vector3 with) + public real_t Tdoty(Vector3 with) { return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2]; } - public float Tdotz(Vector3 with) + public real_t Tdotz(Vector3 with) { return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2]; } public Basis Transposed() { - Basis tr = this; + var tr = this; - float temp = this[0, 1]; + real_t temp = this[0, 1]; this[0, 1] = this[1, 0]; this[1, 0] = temp; @@ -344,98 +372,102 @@ namespace Godot { return new Vector3 ( - (this[0, 0] * v.x) + (this[1, 0] * v.y) + (this[2, 0] * v.z), - (this[0, 1] * v.x) + (this[1, 1] * v.y) + (this[2, 1] * v.z), - (this[0, 2] * v.x) + (this[1, 2] * v.y) + (this[2, 2] * v.z) + this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z, + this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z, + this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z ); } public Quat Quat() { - float trace = x[0] + y[1] + z[2]; + real_t trace = _x[0] + _y[1] + _z[2]; if (trace > 0.0f) { - float s = Mathf.Sqrt(trace + 1.0f) * 2f; - float inv_s = 1f / s; + real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; + real_t inv_s = 1f / s; return new Quat( - (z[1] - y[2]) * inv_s, - (x[2] - z[0]) * inv_s, - (y[0] - x[1]) * inv_s, + (_z[1] - _y[2]) * inv_s, + (_x[2] - _z[0]) * inv_s, + (_y[0] - _x[1]) * inv_s, s * 0.25f ); - } else if (x[0] > y[1] && x[0] > z[2]) { - float s = Mathf.Sqrt(x[0] - y[1] - z[2] + 1.0f) * 2f; - float inv_s = 1f / s; - return new Quat( - s * 0.25f, - (x[1] + y[0]) * inv_s, - (x[2] + z[0]) * inv_s, - (z[1] - y[2]) * inv_s - ); - } else if (y[1] > z[2]) { - float s = Mathf.Sqrt(-x[0] + y[1] - z[2] + 1.0f) * 2f; - float inv_s = 1f / s; - return new Quat( - (x[1] + y[0]) * inv_s, - s * 0.25f, - (y[2] + z[1]) * inv_s, - (x[2] - z[0]) * inv_s - ); - } else { - float s = Mathf.Sqrt(-x[0] - y[1] + z[2] + 1.0f) * 2f; - float inv_s = 1f / s; - return new Quat( - (x[2] + z[0]) * inv_s, - (y[2] + z[1]) * inv_s, - s * 0.25f, - (y[0] - x[1]) * inv_s - ); } + + if (_x[0] > _y[1] && _x[0] > _z[2]) { + real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + s * 0.25f, + (_x[1] + _y[0]) * inv_s, + (_x[2] + _z[0]) * inv_s, + (_z[1] - _y[2]) * inv_s + ); + } + + if (_y[1] > _z[2]) { + real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_x[1] + _y[0]) * inv_s, + s * 0.25f, + (_y[2] + _z[1]) * inv_s, + (_x[2] - _z[0]) * inv_s + ); + } else { + real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_x[2] + _z[0]) * inv_s, + (_y[2] + _z[1]) * inv_s, + s * 0.25f, + (_y[0] - _x[1]) * inv_s + ); + } } public Basis(Quat quat) { - float s = 2.0f / quat.LengthSquared(); - - float xs = quat.x * s; - float ys = quat.y * s; - float zs = quat.z * s; - float wx = quat.w * xs; - float wy = quat.w * ys; - float wz = quat.w * zs; - float xx = quat.x * xs; - float xy = quat.x * ys; - float xz = quat.x * zs; - float yy = quat.y * ys; - float yz = quat.y * zs; - float zz = quat.z * zs; - - this.x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); - this.y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); - this.z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); + real_t s = 2.0f / quat.LengthSquared(); + + real_t xs = quat.x * s; + real_t ys = quat.y * s; + real_t zs = quat.z * s; + real_t wx = quat.w * xs; + real_t wy = quat.w * ys; + real_t wz = quat.w * zs; + real_t xx = quat.x * xs; + real_t xy = quat.x * ys; + real_t xz = quat.x * zs; + real_t yy = quat.y * ys; + real_t yz = quat.y * zs; + real_t zz = quat.z * zs; + + _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); + _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); + _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); } - public Basis(Vector3 axis, float phi) + public Basis(Vector3 axis, real_t phi) { - Vector3 axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); + var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); - float cosine = Mathf.Cos(phi); - float sine = Mathf.Sin(phi); + real_t cosine = Mathf.Cos( phi); + real_t sine = Mathf.Sin( phi); - this.x = new Vector3 + _x = new Vector3 ( axis_sq.x + cosine * (1.0f - axis_sq.x), axis.x * axis.y * (1.0f - cosine) - axis.z * sine, axis.z * axis.x * (1.0f - cosine) + axis.y * sine ); - this.y = new Vector3 + _y = new Vector3 ( axis.x * axis.y * (1.0f - cosine) + axis.z * sine, axis_sq.y + cosine * (1.0f - axis_sq.y), axis.y * axis.z * (1.0f - cosine) - axis.x * sine ); - this.z = new Vector3 + _z = new Vector3 ( axis.z * axis.x * (1.0f - cosine) - axis.y * sine, axis.y * axis.z * (1.0f - cosine) + axis.x * sine, @@ -445,16 +477,16 @@ namespace Godot public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) { - this.x = xAxis; - this.y = yAxis; - this.z = zAxis; + _x = xAxis; + _y = yAxis; + _z = zAxis; } - public Basis(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz) + public Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { - this.x = new Vector3(xx, yx, zx); - this.y = new Vector3(xy, yy, zy); - this.z = new Vector3(xz, yz, zz); + _x = new Vector3(xx, xy, xz); + _y = new Vector3(yx, yy, yz); + _z = new Vector3(zx, zy, zz); } public static Basis operator *(Basis left, Basis right) @@ -489,21 +521,21 @@ namespace Godot public bool Equals(Basis other) { - return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z); + return _x.Equals(other[0]) && _y.Equals(other[1]) && _z.Equals(other[2]); } public override int GetHashCode() { - return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode(); + return _x.GetHashCode() ^ _y.GetHashCode() ^ _z.GetHashCode(); } public override string ToString() { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(), - this.y.ToString(), - this.z.ToString() + _x.ToString(), + _y.ToString(), + _z.ToString() }); } @@ -511,9 +543,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format) + _x.ToString(format), + _y.ToString(format), + _z.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs index f9e31e9703..af94bb616e 100644 --- a/modules/mono/glue/cs_files/Color.cs +++ b/modules/mono/glue/cs_files/Color.cs @@ -45,8 +45,8 @@ namespace Godot { get { - float max = Mathf.Max(r, Mathf.Max(g, b)); - float min = Mathf.Min(r, Mathf.Min(g, b)); + float max = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.Min(g, b)); float delta = max - min; @@ -79,8 +79,8 @@ namespace Godot { get { - float max = Mathf.Max(r, Mathf.Max(g, b)); - float min = Mathf.Min(r, Mathf.Min(g, b)); + float max = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.Min(g, b)); float delta = max - min; @@ -96,7 +96,7 @@ namespace Godot { get { - return Mathf.Max(r, Mathf.Max(g, b)); + return Math.Max(r, Math.Max(g, b)); } set { @@ -104,7 +104,7 @@ namespace Godot } } - private static readonly Color black = new Color(0f, 0f, 0f, 1.0f); + private static readonly Color black = new Color(0f, 0f, 0f); public Color Black { @@ -180,7 +180,7 @@ namespace Godot hue += 1.0f; } - saturation = (max == 0) ? 0 : 1f - (1f * min / max); + saturation = max == 0 ? 0 : 1f - 1f * min / max; value = max / 255f; } @@ -232,12 +232,10 @@ namespace Godot { return new Color(0, 0, 0, 0); } - else - { - res.r = (r * a * sa + over.r * over.a) / res.a; - res.g = (g * a * sa + over.g * over.a) / res.a; - res.b = (b * a * sa + over.b * over.a) / res.a; - } + + res.r = (r * a * sa + over.r * over.a) / res.a; + res.g = (g * a * sa + over.g * over.a) / res.a; + res.b = (b * a * sa + over.b * over.a) / res.a; return res; } @@ -265,14 +263,14 @@ namespace Godot ); } - public Color LinearInterpolate(Color b, float t) + public Color LinearInterpolate(Color c, float t) { - Color res = this; + var res = this; - res.r += (t * (b.r - this.r)); - res.g += (t * (b.g - this.g)); - res.b += (t * (b.b - this.b)); - res.a += (t * (b.a - this.a)); + res.r += t * (c.r - r); + res.g += t * (c.g - g); + res.b += t * (c.b - b); + res.a += t * (c.a - a); return res; } @@ -305,7 +303,7 @@ namespace Godot public string ToHtml(bool include_alpha = true) { - String txt = string.Empty; + var txt = string.Empty; txt += _to_hex(r); txt += _to_hex(g); @@ -316,7 +314,8 @@ namespace Godot return txt; } - + + // Constructors public Color(float r, float g, float b, float a = 1.0f) { this.r = r; @@ -327,13 +326,13 @@ namespace Godot public Color(int rgba) { - this.a = (rgba & 0xFF) / 255.0f; + a = (rgba & 0xFF) / 255.0f; rgba >>= 8; - this.b = (rgba & 0xFF) / 255.0f; + b = (rgba & 0xFF) / 255.0f; rgba >>= 8; - this.g = (rgba & 0xFF) / 255.0f; + g = (rgba & 0xFF) / 255.0f; rgba >>= 8; - this.r = (rgba & 0xFF) / 255.0f; + r = (rgba & 0xFF) / 255.0f; } private static int _parse_col(string str, int ofs) @@ -343,7 +342,7 @@ namespace Godot for (int i = 0; i < 2; i++) { int c = str[i + ofs]; - int v = 0; + int v; if (c >= '0' && c <= '9') { @@ -375,9 +374,9 @@ namespace Godot private String _to_hex(float val) { - int v = (int)Mathf.Clamp(val * 255.0f, 0, 255); + var v = (int) Mathf.Clamp(val * 255.0f, 0, 255); - string ret = string.Empty; + var ret = string.Empty; for (int i = 0; i < 2; i++) { @@ -404,7 +403,7 @@ namespace Godot if (color[0] == '#') color = color.Substring(1, color.Length - 1); - bool alpha = false; + bool alpha; if (color.Length == 8) alpha = true; @@ -433,7 +432,7 @@ namespace Godot public static Color Color8(byte r8, byte g8, byte b8, byte a8) { - return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f); + return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f); } public Color(string rgba) @@ -450,7 +449,7 @@ namespace Godot if (rgba[0] == '#') rgba = rgba.Substring(1); - bool alpha = false; + bool alpha; if (rgba.Length == 8) { @@ -512,14 +511,11 @@ namespace Godot if (left.g == right.g) { if (left.b == right.b) - return (left.a < right.a); - else - return (left.b < right.b); - } - else - { - return left.g < right.g; + return left.a < right.a; + return left.b < right.b; } + + return left.g < right.g; } return left.r < right.r; @@ -532,14 +528,11 @@ namespace Godot if (left.g == right.g) { if (left.b == right.b) - return (left.a > right.a); - else - return (left.b > right.b); - } - else - { - return left.g > right.g; + return left.a > right.a; + return left.b > right.b; } + + return left.g > right.g; } return left.r > right.r; @@ -567,24 +560,12 @@ namespace Godot public override string ToString() { - return String.Format("{0},{1},{2},{3}", new object[] - { - this.r.ToString(), - this.g.ToString(), - this.b.ToString(), - this.a.ToString() - }); + return String.Format("{0},{1},{2},{3}", r.ToString(), g.ToString(), b.ToString(), a.ToString()); } public string ToString(string format) { - return String.Format("{0},{1},{2},{3}", new object[] - { - this.r.ToString(format), - this.g.ToString(format), - this.b.ToString(format), - this.a.ToString(format) - }); + return String.Format("{0},{1},{2},{3}", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format)); } } } diff --git a/modules/mono/glue/cs_files/DebuggingUtils.cs b/modules/mono/glue/cs_files/DebuggingUtils.cs index ffaaf00837..b27816084e 100644 --- a/modules/mono/glue/cs_files/DebuggingUtils.cs +++ b/modules/mono/glue/cs_files/DebuggingUtils.cs @@ -14,7 +14,7 @@ namespace Godot else if (type == typeof(void)) sb.Append("void"); else - sb.Append(type.ToString()); + sb.Append(type); sb.Append(" "); } @@ -32,7 +32,7 @@ namespace Godot return; } - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); if (methodBase is MethodInfo) sb.AppendTypeName(((MethodInfo)methodBase).ReturnType); diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs index b335ef55e4..ec1534cb9a 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/cs_files/GD.cs @@ -1,5 +1,7 @@ using System; +// TODO: Add comments describing what this class does. It is not obvious. + namespace Godot { public static partial class GD @@ -89,7 +91,7 @@ namespace Godot public static int[] Range(int length) { - int[] ret = new int[length]; + var ret = new int[length]; for (int i = 0; i < length; i++) { @@ -104,7 +106,7 @@ namespace Godot if (to < from) return new int[0]; - int[] ret = new int[to - from]; + var ret = new int[to - from]; for (int i = from; i < to; i++) { @@ -122,14 +124,14 @@ namespace Godot return new int[0]; // Calculate count - int count = 0; + int count; if (increment > 0) - count = ((to - from - 1) / increment) + 1; + count = (to - from - 1) / increment + 1; else - count = ((from - to - 1) / -increment) + 1; + count = (from - to - 1) / -increment + 1; - int[] ret = new int[count]; + var ret = new int[count]; if (increment > 0) { diff --git a/modules/mono/glue/cs_files/GodotMethodAttribute.cs b/modules/mono/glue/cs_files/GodotMethodAttribute.cs index 21333c8dab..55848769d5 100644 --- a/modules/mono/glue/cs_files/GodotMethodAttribute.cs +++ b/modules/mono/glue/cs_files/GodotMethodAttribute.cs @@ -2,7 +2,7 @@ using System; namespace Godot { - [AttributeUsage(AttributeTargets.Method, Inherited = true)] + [AttributeUsage(AttributeTargets.Method)] internal class GodotMethodAttribute : Attribute { private string methodName; diff --git a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs index eb4d0bed1c..da3c7bac83 100644 --- a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs +++ b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; diff --git a/modules/mono/glue/cs_files/GodotTaskScheduler.cs b/modules/mono/glue/cs_files/GodotTaskScheduler.cs index f587645a49..6bf25a89d2 100644 --- a/modules/mono/glue/cs_files/GodotTaskScheduler.cs +++ b/modules/mono/glue/cs_files/GodotTaskScheduler.cs @@ -36,7 +36,7 @@ namespace Godot TryDequeue(task); } - return base.TryExecuteTask(task); + return TryExecuteTask(task); } protected sealed override bool TryDequeue(Task task) diff --git a/modules/mono/glue/cs_files/IAwaiter.cs b/modules/mono/glue/cs_files/IAwaiter.cs index 73c71b5634..b5aa1a5389 100644 --- a/modules/mono/glue/cs_files/IAwaiter.cs +++ b/modules/mono/glue/cs_files/IAwaiter.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.CompilerServices; namespace Godot diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs index 2bdfb95c51..ff4477cc6c 100644 --- a/modules/mono/glue/cs_files/MarshalUtils.cs +++ b/modules/mono/glue/cs_files/MarshalUtils.cs @@ -7,7 +7,7 @@ namespace Godot { private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values) { - Dictionary<object, object> ret = new Dictionary<object, object>(); + var ret = new Dictionary<object, object>(); for (int i = 0; i < keys.Length; i++) { @@ -19,11 +19,11 @@ namespace Godot private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo) { - Dictionary<object, object>.KeyCollection keys = from.Keys; + var keys = from.Keys; keysTo = new object[keys.Count]; keys.CopyTo(keysTo, 0); - Dictionary<object, object>.ValueCollection values = from.Values; + var values = from.Values; valuesTo = new object[values.Count]; values.CopyTo(valuesTo, 0); } diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index 476396e9a3..0d20a12563 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -1,75 +1,85 @@ using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { - public static class Mathf + public static partial class Mathf { - public const float PI = 3.14159274f; - public const float Epsilon = 1e-06f; + // Define constants with Decimal precision and cast down to double or float. - private const float Deg2RadConst = 0.0174532924f; - private const float Rad2DegConst = 57.29578f; + public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959 + public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979 + public const real_t Inf = real_t.PositiveInfinity; + public const real_t NaN = real_t.NaN; - public static float Abs(float s) + private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433 + private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823 + + public static real_t Abs(real_t s) { return Math.Abs(s); } - public static float Acos(float s) + public static int Abs(int s) { - return (float)Math.Acos(s); + return Math.Abs(s); } - public static float Asin(float s) + public static real_t Acos(real_t s) { - return (float)Math.Asin(s); + return (real_t)Math.Acos(s); } - public static float Atan(float s) + public static real_t Asin(real_t s) { - return (float)Math.Atan(s); + return (real_t)Math.Asin(s); } - public static float Atan2(float x, float y) + public static real_t Atan(real_t s) { - return (float)Math.Atan2(x, y); + return (real_t)Math.Atan(s); } - public static Vector2 Cartesian2Polar(float x, float y) - { - return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); - } + public static real_t Atan2(real_t x, real_t y) + { + return (real_t)Math.Atan2(x, y); + } - public static float Ceil(float s) + public static Vector2 Cartesian2Polar(real_t x, real_t y) { - return (float)Math.Ceiling(s); + return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); } - public static float Clamp(float val, float min, float max) + public static real_t Ceil(real_t s) { - if (val < min) - { - return min; - } - else if (val > max) - { - return max; - } + return (real_t)Math.Ceiling(s); + } - return val; + public static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; } - public static float Cos(float s) + public static real_t Clamp(real_t value, real_t min, real_t max) { - return (float)Math.Cos(s); + return value < min ? min : value > max ? max : value; } - public static float Cosh(float s) + public static real_t Cos(real_t s) { - return (float)Math.Cosh(s); + return (real_t)Math.Cos(s); } - public static int Decimals(float step) + public static real_t Cosh(real_t s) + { + return (real_t)Math.Cosh(s); + } + + public static int Decimals(real_t step) { return Decimals((decimal)step); } @@ -79,12 +89,12 @@ namespace Godot return BitConverter.GetBytes(decimal.GetBits(step)[3])[2]; } - public static float Deg2Rad(float deg) + public static real_t Deg2Rad(real_t deg) { return deg * Deg2RadConst; } - public static float Ease(float s, float curve) + public static real_t Ease(real_t s, real_t curve) { if (s < 0f) { @@ -104,7 +114,8 @@ namespace Godot return Pow(s, curve); } - else if (curve < 0f) + + if (curve < 0f) { if (s < 0.5f) { @@ -117,111 +128,129 @@ namespace Godot return 0f; } - public static float Exp(float s) + public static real_t Exp(real_t s) { - return (float)Math.Exp(s); + return (real_t)Math.Exp(s); } - public static float Floor(float s) + public static real_t Floor(real_t s) { - return (float)Math.Floor(s); + return (real_t)Math.Floor(s); } - public static float Fposmod(float x, float y) + public static real_t Fposmod(real_t x, real_t y) { if (x >= 0f) { return x % y; } - else - { - return y - (-x % y); - } + + return y - -x % y; } - public static float Lerp(float from, float to, float weight) + public static real_t InverseLerp(real_t from, real_t to, real_t weight) + { + return (Clamp(weight, 0f, 1f) - from) / (to - from); + } + + public static bool IsInf(real_t s) + { + return real_t.IsInfinity(s); + } + + public static bool IsNaN(real_t s) + { + return real_t.IsNaN(s); + } + + public static real_t Lerp(real_t from, real_t to, real_t weight) { return from + (to - from) * Clamp(weight, 0f, 1f); } - public static float Log(float s) + public static real_t Log(real_t s) { - return (float)Math.Log(s); + return (real_t)Math.Log(s); } public static int Max(int a, int b) { - return (a > b) ? a : b; + return a > b ? a : b; } - public static float Max(float a, float b) + public static real_t Max(real_t a, real_t b) { - return (a > b) ? a : b; + return a > b ? a : b; } public static int Min(int a, int b) { - return (a < b) ? a : b; + return a < b ? a : b; } - public static float Min(float a, float b) + public static real_t Min(real_t a, real_t b) { - return (a < b) ? a : b; + return a < b ? a : b; } - public static int NearestPo2(int val) + public static int NearestPo2(int value) { - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - return val; + value--; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value++; + return value; } - public static Vector2 Polar2Cartesian(float r, float th) - { - return new Vector2(r * Cos(th), r * Sin(th)); - } + public static Vector2 Polar2Cartesian(real_t r, real_t th) + { + return new Vector2(r * Cos(th), r * Sin(th)); + } - public static float Pow(float x, float y) + public static real_t Pow(real_t x, real_t y) { - return (float)Math.Pow(x, y); + return (real_t)Math.Pow(x, y); } - public static float Rad2Deg(float rad) + public static real_t Rad2Deg(real_t rad) { return rad * Rad2DegConst; } - public static float Round(float s) + public static real_t Round(real_t s) { - return (float)Math.Round(s); + return (real_t)Math.Round(s); } - public static float Sign(float s) + public static int Sign(int s) { - return (s < 0f) ? -1f : 1f; + return s < 0 ? -1 : 1; } - public static float Sin(float s) + public static real_t Sign(real_t s) { - return (float)Math.Sin(s); + return s < 0f ? -1f : 1f; } - public static float Sinh(float s) + public static real_t Sin(real_t s) { - return (float)Math.Sinh(s); + return (real_t)Math.Sin(s); } - public static float Sqrt(float s) + public static real_t Sinh(real_t s) { - return (float)Math.Sqrt(s); + return (real_t)Math.Sinh(s); } - public static float Stepify(float s, float step) + public static real_t Sqrt(real_t s) + { + return (real_t)Math.Sqrt(s); + } + + public static real_t Stepify(real_t s, real_t step) { if (step != 0f) { @@ -231,14 +260,26 @@ namespace Godot return s; } - public static float Tan(float s) + public static real_t Tan(real_t s) + { + return (real_t)Math.Tan(s); + } + + public static real_t Tanh(real_t s) + { + return (real_t)Math.Tanh(s); + } + + public static int Wrap(int value, int min, int max) { - return (float)Math.Tan(s); + int rng = max - min; + return min + ((value - min) % rng + rng) % rng; } - public static float Tanh(float s) + public static real_t Wrap(real_t value, real_t min, real_t max) { - return (float)Math.Tanh(s); + real_t rng = max - min; + return min + ((value - min) % rng + rng) % rng; } } } diff --git a/modules/mono/glue/cs_files/MathfEx.cs b/modules/mono/glue/cs_files/MathfEx.cs new file mode 100644 index 0000000000..739b7fb568 --- /dev/null +++ b/modules/mono/glue/cs_files/MathfEx.cs @@ -0,0 +1,39 @@ +using System; + +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public static partial class Mathf + { + // Define constants with Decimal precision and cast down to double or float. + + public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045 + public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095 + +#if REAL_T_IS_DOUBLE + public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used. +#else + public const real_t Epsilon = 1e-06f; +#endif + + public static int CeilToInt(real_t s) + { + return (int)Math.Ceiling(s); + } + + public static int FloorToInt(real_t s) + { + return (int)Math.Floor(s); + } + + public static int RoundToInt(real_t s) + { + return (int)Math.Round(s); + } + } +}
\ No newline at end of file diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs index b347c0835a..8b92522029 100644 --- a/modules/mono/glue/cs_files/Plane.cs +++ b/modules/mono/glue/cs_files/Plane.cs @@ -1,4 +1,9 @@ using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -6,7 +11,7 @@ namespace Godot { Vector3 normal; - public float x + public real_t x { get { @@ -18,7 +23,7 @@ namespace Godot } } - public float y + public real_t y { get { @@ -30,7 +35,7 @@ namespace Godot } } - public float z + public real_t z { get { @@ -42,7 +47,7 @@ namespace Godot } } - float d; + real_t d; public Vector3 Center { @@ -52,7 +57,7 @@ namespace Godot } } - public float DistanceTo(Vector3 point) + public real_t DistanceTo(Vector3 point) { return normal.Dot(point) - d; } @@ -62,34 +67,34 @@ namespace Godot return normal * d; } - public bool HasPoint(Vector3 point, float epsilon = Mathf.Epsilon) + public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) { - float dist = normal.Dot(point) - d; + real_t dist = normal.Dot(point) - d; return Mathf.Abs(dist) <= epsilon; } public Vector3 Intersect3(Plane b, Plane c) { - float denom = normal.Cross(b.normal).Dot(c.normal); + real_t denom = normal.Cross(b.normal).Dot(c.normal); if (Mathf.Abs(denom) <= Mathf.Epsilon) return new Vector3(); - Vector3 result = (b.normal.Cross(c.normal) * this.d) + - (c.normal.Cross(normal) * b.d) + - (normal.Cross(b.normal) * c.d); + Vector3 result = b.normal.Cross(c.normal) * d + + c.normal.Cross(normal) * b.d + + normal.Cross(b.normal) * c.d; return result / denom; } public Vector3 IntersectRay(Vector3 from, Vector3 dir) { - float den = normal.Dot(dir); + real_t den = normal.Dot(dir); if (Mathf.Abs(den) <= Mathf.Epsilon) return new Vector3(); - float dist = (normal.Dot(from) - d) / den; + real_t dist = (normal.Dot(from) - d) / den; // This is a ray, before the emitting pos (from) does not exist if (dist > Mathf.Epsilon) @@ -101,14 +106,14 @@ namespace Godot public Vector3 IntersectSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; - float den = normal.Dot(segment); + real_t den = normal.Dot(segment); if (Mathf.Abs(den) <= Mathf.Epsilon) return new Vector3(); - float dist = (normal.Dot(begin) - d) / den; + real_t dist = (normal.Dot(begin) - d) / den; - if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon)) + if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) return new Vector3(); return begin + segment * -dist; @@ -121,7 +126,7 @@ namespace Godot public Plane Normalized() { - float len = normal.Length(); + real_t len = normal.Length(); if (len == 0) return new Plane(0, 0, 0, 0); @@ -133,14 +138,14 @@ namespace Godot { return point - normal * DistanceTo(point); } - - public Plane(float a, float b, float c, float d) + + // Constructors + public Plane(real_t a, real_t b, real_t c, real_t d) { normal = new Vector3(a, b, c); this.d = d; } - - public Plane(Vector3 normal, float d) + public Plane(Vector3 normal, real_t d) { this.normal = normal; this.d = d; @@ -192,8 +197,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.normal.ToString(), - this.d.ToString() + normal.ToString(), + d.ToString() }); } @@ -201,8 +206,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.normal.ToString(format), - this.d.ToString(format) + normal.ToString(format), + d.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/cs_files/Quat.cs index c0ac41c5d7..c69c55d997 100644 --- a/modules/mono/glue/cs_files/Quat.cs +++ b/modules/mono/glue/cs_files/Quat.cs @@ -1,5 +1,10 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -8,17 +13,17 @@ namespace Godot { private static readonly Quat identity = new Quat(0f, 0f, 0f, 1f); - public float x; - public float y; - public float z; - public float w; + public real_t x; + public real_t y; + public real_t z; + public real_t w; public static Quat Identity { get { return identity; } } - public float this[int index] + public real_t this[int index] { get { @@ -58,15 +63,15 @@ namespace Godot } } - public Quat CubicSlerp(Quat b, Quat preA, Quat postB, float t) + public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t) { - float t2 = (1.0f - t) * t * 2f; + real_t t2 = (1.0f - t) * t * 2f; Quat sp = Slerp(b, t); Quat sq = preA.Slerpni(postB, t); return sp.Slerpni(sq, t2); } - public float Dot(Quat b) + public real_t Dot(Quat b) { return x * b.x + y * b.y + z * b.z + w * b.w; } @@ -76,12 +81,12 @@ namespace Godot return new Quat(-x, -y, -z, w); } - public float Length() + public real_t Length() { return Mathf.Sqrt(LengthSquared()); } - public float LengthSquared() + public real_t LengthSquared() { return Dot(this); } @@ -91,20 +96,27 @@ namespace Godot return this / Length(); } - public void Set(float x, float y, float z, float w) + public void Set(real_t x, real_t y, real_t z, real_t w) { this.x = x; this.y = y; this.z = z; this.w = w; } + public void Set(Quat q) + { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + } - public Quat Slerp(Quat b, float t) + public Quat Slerp(Quat b, real_t t) { // Calculate cosine - float cosom = x * b.x + y * b.y + z * b.z + w * b.w; + real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w; - float[] to1 = new float[4]; + var to1 = new real_t[4]; // Adjust signs if necessary if (cosom < 0.0) @@ -122,13 +134,13 @@ namespace Godot to1[3] = b.w; } - float sinom, scale0, scale1; + real_t sinom, scale0, scale1; // Calculate coefficients - if ((1.0 - cosom) > Mathf.Epsilon) + if (1.0 - cosom > Mathf.Epsilon) { // Standard case (Slerp) - float omega = Mathf.Acos(cosom); + real_t omega = Mathf.Acos(cosom); sinom = Mathf.Sin(omega); scale0 = Mathf.Sin((1.0f - t) * omega) / sinom; scale1 = Mathf.Sin(t * omega) / sinom; @@ -150,47 +162,56 @@ namespace Godot ); } - public Quat Slerpni(Quat b, float t) + public Quat Slerpni(Quat b, real_t t) { - float dot = this.Dot(b); + real_t dot = Dot(b); if (Mathf.Abs(dot) > 0.9999f) { return this; } - float theta = Mathf.Acos(dot); - float sinT = 1.0f / Mathf.Sin(theta); - float newFactor = Mathf.Sin(t * theta) * sinT; - float invFactor = Mathf.Sin((1.0f - t) * theta) * sinT; + real_t theta = Mathf.Acos(dot); + real_t sinT = 1.0f / Mathf.Sin(theta); + real_t newFactor = Mathf.Sin(t * theta) * sinT; + real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT; return new Quat ( - invFactor * this.x + newFactor * b.x, - invFactor * this.y + newFactor * b.y, - invFactor * this.z + newFactor * b.z, - invFactor * this.w + newFactor * b.w + invFactor * x + newFactor * b.x, + invFactor * y + newFactor * b.y, + invFactor * z + newFactor * b.z, + invFactor * w + newFactor * b.w ); } public Vector3 Xform(Vector3 v) { Quat q = this * v; - q *= this.Inverse(); + q *= Inverse(); return new Vector3(q.x, q.y, q.z); } - public Quat(float x, float y, float z, float w) + // Constructors + public Quat(real_t x, real_t y, real_t z, real_t w) { this.x = x; this.y = y; this.z = z; this.w = w; + } + public Quat(Quat q) + { + x = q.x; + y = q.y; + z = q.z; + w = q.w; } - - public Quat(Vector3 axis, float angle) + + public Quat(Vector3 axis, real_t angle) { - float d = axis.Length(); + real_t d = axis.Length(); + real_t angle_t = angle; if (d == 0f) { @@ -201,12 +222,12 @@ namespace Godot } else { - float s = Mathf.Sin(angle * 0.5f) / d; + real_t s = Mathf.Sin(angle_t * 0.5f) / d; x = axis.x * s; y = axis.y * s; z = axis.z * s; - w = Mathf.Cos(angle * 0.5f); + w = Mathf.Cos(angle_t * 0.5f); } } @@ -258,17 +279,17 @@ namespace Godot ); } - public static Quat operator *(Quat left, float right) + public static Quat operator *(Quat left, real_t right) { return new Quat(left.x * right, left.y * right, left.z * right, left.w * right); } - public static Quat operator *(float left, Quat right) + public static Quat operator *(real_t left, Quat right) { return new Quat(right.x * left, right.y * left, right.z * left, right.w * left); } - public static Quat operator /(Quat left, float right) + public static Quat operator /(Quat left, real_t right) { return left * (1.0f / right); } @@ -305,24 +326,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1}, {2}, {3})", new object[] - { - this.x.ToString(), - this.y.ToString(), - this.z.ToString(), - this.w.ToString() - }); + return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString()); } public string ToString(string format) { - return String.Format("({0}, {1}, {2}, {3})", new object[] - { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format), - this.w.ToString(format) - }); + return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format)); } } } diff --git a/modules/mono/glue/cs_files/Rect2.cs b/modules/mono/glue/cs_files/Rect2.cs index e1fbb65da5..6f16656573 100644 --- a/modules/mono/glue/cs_files/Rect2.cs +++ b/modules/mono/glue/cs_files/Rect2.cs @@ -1,5 +1,10 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -26,14 +31,14 @@ namespace Godot get { return position + size; } } - public float Area + public real_t Area { get { return GetArea(); } } public Rect2 Clip(Rect2 b) { - Rect2 newRect = b; + var newRect = b; if (!Intersects(newRect)) return new Rect2(); @@ -52,14 +57,14 @@ namespace Godot public bool Encloses(Rect2 b) { - return (b.position.x >= position.x) && (b.position.y >= position.y) && - ((b.position.x + b.size.x) < (position.x + size.x)) && - ((b.position.y + b.size.y) < (position.y + size.y)); + return b.position.x >= position.x && b.position.y >= position.y && + b.position.x + b.size.x < position.x + size.x && + b.position.y + b.size.y < position.y + size.y; } public Rect2 Expand(Vector2 to) { - Rect2 expanded = this; + var expanded = this; Vector2 begin = expanded.position; Vector2 end = expanded.position + expanded.size; @@ -80,14 +85,14 @@ namespace Godot return expanded; } - public float GetArea() + public real_t GetArea() { return size.x * size.y; } - public Rect2 Grow(float by) + public Rect2 Grow(real_t by) { - Rect2 g = this; + var g = this; g.position.x -= by; g.position.y -= by; @@ -97,9 +102,9 @@ namespace Godot return g; } - public Rect2 GrowIndividual(float left, float top, float right, float bottom) + public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) { - Rect2 g = this; + var g = this; g.position.x -= left; g.position.y -= top; @@ -109,14 +114,14 @@ namespace Godot return g; } - public Rect2 GrowMargin(Margin margin, float by) + public Rect2 GrowMargin(Margin margin, real_t by) { - Rect2 g = this; + var g = this; - g.GrowIndividual((Margin.Left == margin) ? by : 0, - (Margin.Top == margin) ? by : 0, - (Margin.Right == margin) ? by : 0, - (Margin.Bottom == margin) ? by : 0); + g.GrowIndividual(Margin.Left == margin ? by : 0, + Margin.Top == margin ? by : 0, + Margin.Right == margin ? by : 0, + Margin.Bottom == margin ? by : 0); return g; } @@ -133,9 +138,9 @@ namespace Godot if (point.y < position.y) return false; - if (point.x >= (position.x + size.x)) + if (point.x >= position.x + size.x) return false; - if (point.y >= (position.y + size.y)) + if (point.y >= position.y + size.y) return false; return true; @@ -143,13 +148,13 @@ namespace Godot public bool Intersects(Rect2 b) { - if (position.x > (b.position.x + b.size.x)) + if (position.x > b.position.x + b.size.x) return false; - if ((position.x + size.x) < b.position.x) + if (position.x + size.x < b.position.x) return false; - if (position.y > (b.position.y + b.size.y)) + if (position.y > b.position.y + b.size.y) return false; - if ((position.y + size.y) < b.position.y) + if (position.y + size.y < b.position.y) return false; return true; @@ -169,17 +174,27 @@ namespace Godot return newRect; } - + + // Constructors public Rect2(Vector2 position, Vector2 size) { this.position = position; this.size = size; } - - public Rect2(float x, float y, float width, float height) + public Rect2(Vector2 position, real_t width, real_t height) + { + this.position = position; + size = new Vector2(width, height); + } + public Rect2(real_t x, real_t y, Vector2 size) + { + position = new Vector2(x, y); + this.size = size; + } + public Rect2(real_t x, real_t y, real_t width, real_t height) { - this.position = new Vector2(x, y); - this.size = new Vector2(width, height); + position = new Vector2(x, y); + size = new Vector2(width, height); } public static bool operator ==(Rect2 left, Rect2 right) @@ -216,8 +231,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.position.ToString(), - this.size.ToString() + position.ToString(), + size.ToString() }); } @@ -225,8 +240,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.position.ToString(format), - this.size.ToString(format) + position.ToString(format), + size.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/SignalAttribute.cs b/modules/mono/glue/cs_files/SignalAttribute.cs index d8a6cabb83..3957387be9 100644 --- a/modules/mono/glue/cs_files/SignalAttribute.cs +++ b/modules/mono/glue/cs_files/SignalAttribute.cs @@ -5,8 +5,5 @@ namespace Godot [AttributeUsage(AttributeTargets.Delegate)] public class SignalAttribute : Attribute { - public SignalAttribute() - { - } } } diff --git a/modules/mono/glue/cs_files/SignalAwaiter.cs b/modules/mono/glue/cs_files/SignalAwaiter.cs index 19ccc26e79..c06f6b05c9 100644 --- a/modules/mono/glue/cs_files/SignalAwaiter.cs +++ b/modules/mono/glue/cs_files/SignalAwaiter.cs @@ -4,15 +4,15 @@ namespace Godot { public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]> { - private bool completed = false; - private object[] result = null; - private Action action = null; + private bool completed; + private object[] result; + private Action action; - public SignalAwaiter(Godot.Object source, string signal, Godot.Object target) + public SignalAwaiter(Object source, string signal, Object target) { NativeCalls.godot_icall_Object_connect_signal_awaiter( - Godot.Object.GetPtr(source), - signal, Godot.Object.GetPtr(target), this + Object.GetPtr(source), + signal, Object.GetPtr(target), this ); } diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs index 5c3ceff97d..21090fb68d 100644 --- a/modules/mono/glue/cs_files/StringExtensions.cs +++ b/modules/mono/glue/cs_files/StringExtensions.cs @@ -1,4 +1,5 @@ //using System; + using System; using System.Collections.Generic; using System.Globalization; @@ -43,11 +44,9 @@ namespace Godot { return instance.Substring(prev, i - prev); } - else - { - count++; - prev = i + 1; - } + + count++; + prev = i + 1; } i++; @@ -83,7 +82,7 @@ namespace Godot // </summary> public static string[] Bigrams(this string instance) { - string[] b = new string[instance.Length - 1]; + var b = new string[instance.Length - 1]; for (int i = 0; i < b.Length; i++) { @@ -98,7 +97,7 @@ namespace Godot // </summary> public static string CEscape(this string instance) { - StringBuilder sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(string.Copy(instance)); sb.Replace("\\", "\\\\"); sb.Replace("\a", "\\a"); @@ -120,7 +119,7 @@ namespace Godot // </summary> public static string CUnescape(this string instance) { - StringBuilder sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(string.Copy(instance)); sb.Replace("\\a", "\a"); sb.Replace("\\b", "\b"); @@ -143,7 +142,7 @@ namespace Godot public static string Capitalize(this string instance) { string aux = instance.Replace("_", " ").ToLower(); - string cap = string.Empty; + var cap = string.Empty; for (int i = 0; i < aux.GetSliceCount(" "); i++) { @@ -178,13 +177,13 @@ namespace Godot { if (to[to_idx] == 0 && instance[instance_idx] == 0) return 0; // We're equal - else if (instance[instance_idx] == 0) + if (instance[instance_idx] == 0) return -1; // If this is empty, and the other one is not, then we're less... I think? - else if (to[to_idx] == 0) + if (to[to_idx] == 0) return 1; // Otherwise the other one is smaller... - else if (instance[instance_idx] < to[to_idx]) // More than + if (instance[instance_idx] < to[to_idx]) // More than return -1; - else if (instance[instance_idx] > to[to_idx]) // Less than + if (instance[instance_idx] > to[to_idx]) // Less than return 1; instance_idx++; @@ -260,12 +259,12 @@ namespace Godot { int basepos = instance.Find("://"); - string rs = string.Empty; - string @base = string.Empty; + string rs; + var @base = string.Empty; if (basepos != -1) { - int end = basepos + 3; + var end = basepos + 3; rs = instance.Substring(end, instance.Length); @base = instance.Substring(0, end); } @@ -287,7 +286,7 @@ namespace Godot if (sep == -1) return @base; - return @base + rs.substr(0, sep); + return @base + rs.Substr(0, sep); } // <summary> @@ -312,8 +311,8 @@ namespace Godot int hashv = 5381; int c; - while ((c = (int)instance[index++]) != 0) - hashv = ((hashv << 5) + hashv) + c; // hash * 33 + c + while ((c = instance[index++]) != 0) + hashv = (hashv << 5) + hashv + c; // hash * 33 + c return hashv; } @@ -379,7 +378,7 @@ namespace Godot while (instance[src] != 0 && text[tgt] != 0) { - bool match = false; + bool match; if (case_insensitive) { @@ -455,7 +454,10 @@ namespace Godot return false; // Don't start with number plz } - bool valid_char = (instance[i] >= '0' && instance[i] <= '9') || (instance[i] >= 'a' && instance[i] <= 'z') || (instance[i] >= 'A' && instance[i] <= 'Z') || instance[i] == '_'; + bool valid_char = instance[i] >= '0' && + instance[i] <= '9' || instance[i] >= 'a' && + instance[i] <= 'z' || instance[i] >= 'A' && + instance[i] <= 'Z' || instance[i] == '_'; if (!valid_char) return false; @@ -478,7 +480,7 @@ namespace Godot // </summary> public static bool IsValidIpAddress(this string instance) { - string[] ip = instance.split("."); + string[] ip = instance.Split("."); if (ip.Length != 4) return false; @@ -489,7 +491,7 @@ namespace Godot if (!n.IsValidInteger()) return false; - int val = n.to_int(); + int val = n.ToInt(); if (val < 0 || val > 255) return false; } @@ -502,7 +504,7 @@ namespace Godot // </summary> public static string JsonEscape(this string instance) { - StringBuilder sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(string.Copy(instance)); sb.Replace("\\", "\\\\"); sb.Replace("\b", "\\b"); @@ -551,7 +553,7 @@ namespace Godot case '\0': return instance[0] == 0; case '*': - return ExprMatch(expr + 1, instance, caseSensitive) || (instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive)); + return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive); case '?': return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive); default: @@ -571,7 +573,7 @@ namespace Godot // <summary> // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). // </summary> - public static bool matchn(this string instance, string expr) + public static bool Matchn(this string instance, string expr) { return instance.ExprMatch(expr, false); } @@ -610,13 +612,13 @@ namespace Godot { if (to[to_idx] == 0 && instance[instance_idx] == 0) return 0; // We're equal - else if (instance[instance_idx] == 0) + if (instance[instance_idx] == 0) return -1; // If this is empty, and the other one is not, then we're less... I think? - else if (to[to_idx] == 0) + if (to[to_idx] == 0) return 1; // Otherwise the other one is smaller.. - else if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than + if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than return -1; - else if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than + if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than return 1; instance_idx++; @@ -724,8 +726,7 @@ namespace Godot { if (instance.Length > 0 && instance[instance.Length - 1] == '/') return instance + file; - else - return instance + "/" + file; + return instance + "/" + file; } // <summary> @@ -771,7 +772,7 @@ namespace Godot if (pos < 0) return string.Empty; - return instance.Substring(pos, (instance.Length - pos)); + return instance.Substring(pos, instance.Length - pos); } public static byte[] Sha256Buffer(this string instance) @@ -824,23 +825,23 @@ namespace Godot } } - return (2.0f * inter) / sum; + return 2.0f * inter / sum; } // <summary> // Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". // </summary> - public static string[] split(this string instance, string divisor, bool allow_empty = true) + public static string[] Split(this string instance, string divisor, bool allow_empty = true) { - return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries); + return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries); } // <summary> // Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". // </summary> - public static float[] split_floats(this string instance, string divisor, bool allow_empty = true) + public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true) { - List<float> ret = new List<float>(); + var ret = new List<float>(); int from = 0; int len = instance.Length; @@ -849,7 +850,7 @@ namespace Godot int end = instance.Find(divisor, from); if (end < 0) end = len; - if (allow_empty || (end > from)) + if (allow_empty || end > from) ret.Add(float.Parse(instance.Substring(from))); if (end == len) break; @@ -872,25 +873,22 @@ namespace Godot // <summary> // Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively. // </summary> - public static string strip_edges(this string instance, bool left = true, bool right = true) + public static string StripEdges(this string instance, bool left = true, bool right = true) { if (left) { if (right) return instance.Trim(non_printable); - else - return instance.TrimStart(non_printable); - } - else - { - return instance.TrimEnd(non_printable); + return instance.TrimStart(non_printable); } + + return instance.TrimEnd(non_printable); } // <summary> // Return part of the string from the position [code]from[/code], with length [code]len[/code]. // </summary> - public static string substr(this string instance, int from, int len) + public static string Substr(this string instance, int from, int len) { return instance.Substring(from, len); } @@ -898,7 +896,7 @@ namespace Godot // <summary> // Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters. // </summary> - public static byte[] to_ascii(this string instance) + public static byte[] ToAscii(this string instance) { return Encoding.ASCII.GetBytes(instance); } @@ -906,7 +904,7 @@ namespace Godot // <summary> // Convert a string, containing a decimal number, into a [code]float[/code]. // </summary> - public static float to_float(this string instance) + public static float ToFloat(this string instance) { return float.Parse(instance); } @@ -914,7 +912,7 @@ namespace Godot // <summary> // Convert a string, containing an integer number, into an [code]int[/code]. // </summary> - public static int to_int(this string instance) + public static int ToInt(this string instance) { return int.Parse(instance); } @@ -922,7 +920,7 @@ namespace Godot // <summary> // Return the string converted to lowercase. // </summary> - public static string to_lower(this string instance) + public static string ToLower(this string instance) { return instance.ToLower(); } @@ -930,7 +928,7 @@ namespace Godot // <summary> // Return the string converted to uppercase. // </summary> - public static string to_upper(this string instance) + public static string ToUpper(this string instance) { return instance.ToUpper(); } @@ -938,7 +936,7 @@ namespace Godot // <summary> // Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii(). // </summary> - public static byte[] to_utf8(this string instance) + public static byte[] ToUtf8(this string instance) { return Encoding.UTF8.GetBytes(instance); } @@ -946,7 +944,7 @@ namespace Godot // <summary> // Return a copy of the string with special characters escaped using the XML standard. // </summary> - public static string xml_escape(this string instance) + public static string XmlEscape(this string instance) { return SecurityElement.Escape(instance); } @@ -954,7 +952,7 @@ namespace Godot // <summary> // Return a copy of the string with escaped characters replaced by their meanings according to the XML standard. // </summary> - public static string xml_unescape(this string instance) + public static string XmlUnescape(this string instance) { return SecurityElement.FromString(instance).Text; } diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs index 5214100d36..d1b247a552 100644 --- a/modules/mono/glue/cs_files/Transform.cs +++ b/modules/mono/glue/cs_files/Transform.cs @@ -1,5 +1,10 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -23,8 +28,8 @@ namespace Godot public Transform LookingAt(Vector3 target, Vector3 up) { - Transform t = this; - t.set_look_at(origin, target, up); + var t = this; + t.SetLookAt(origin, target, up); return t; } @@ -33,7 +38,7 @@ namespace Godot return new Transform(basis.Orthonormalized(), origin); } - public Transform Rotated(Vector3 axis, float phi) + public Transform Rotated(Vector3 axis, real_t phi) { return new Transform(new Basis(axis, phi), new Vector3()) * this; } @@ -43,7 +48,7 @@ namespace Godot return new Transform(basis.Scaled(scale), origin * scale); } - public void set_look_at(Vector3 eye, Vector3 target, Vector3 up) + public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) { // Make rotation matrix // Z vector @@ -92,21 +97,22 @@ namespace Godot return new Vector3 ( - (basis[0, 0] * vInv.x) + (basis[1, 0] * vInv.y) + (basis[2, 0] * vInv.z), - (basis[0, 1] * vInv.x) + (basis[1, 1] * vInv.y) + (basis[2, 1] * vInv.z), - (basis[0, 2] * vInv.x) + (basis[1, 2] * vInv.y) + (basis[2, 2] * vInv.z) + basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z, + basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z, + basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z ); } - + + // Constructors public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) { - this.basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); this.origin = origin; } public Transform(Quat quat, Vector3 origin) { - this.basis = new Basis(quat); + basis = new Basis(quat); this.origin = origin; } @@ -157,8 +163,8 @@ namespace Godot { return String.Format("{0} - {1}", new object[] { - this.basis.ToString(), - this.origin.ToString() + basis.ToString(), + origin.ToString() }); } @@ -166,8 +172,8 @@ namespace Godot { return String.Format("{0} - {1}", new object[] { - this.basis.ToString(format), - this.origin.ToString(format) + basis.ToString(format), + origin.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/cs_files/Transform2D.cs index fe7c5b5706..ff5259178b 100644 --- a/modules/mono/glue/cs_files/Transform2D.cs +++ b/modules/mono/glue/cs_files/Transform2D.cs @@ -1,5 +1,10 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -27,7 +32,7 @@ namespace Godot get { return o; } } - public float Rotation + public real_t Rotation { get { return Mathf.Atan2(y.x, o.y); } } @@ -73,7 +78,7 @@ namespace Godot } - public float this[int index, int axis] + public real_t this[int index, int axis] { get { @@ -105,9 +110,9 @@ namespace Godot public Transform2D AffineInverse() { - Transform2D inv = this; + var inv = this; - float det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]; + real_t det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]; if (det == 0) { @@ -119,9 +124,9 @@ namespace Godot ); } - float idet = 1.0f / det; + real_t idet = 1.0f / det; - float temp = this[0, 0]; + real_t temp = this[0, 0]; this[0, 0] = this[1, 1]; this[1, 1] = temp; @@ -143,24 +148,24 @@ namespace Godot return new Vector2(x.Dot(v), y.Dot(v)); } - public Transform2D InterpolateWith(Transform2D m, float c) + public Transform2D InterpolateWith(Transform2D m, real_t c) { - float r1 = Rotation; - float r2 = m.Rotation; + real_t r1 = Rotation; + real_t r2 = m.Rotation; Vector2 s1 = Scale; Vector2 s2 = m.Scale; // Slerp rotation - Vector2 v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); - Vector2 v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); + var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); + var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); - float dot = v1.Dot(v2); + real_t dot = v1.Dot(v2); // Clamp dot to [-1, 1] - dot = (dot < -1.0f) ? -1.0f : ((dot > 1.0f) ? 1.0f : dot); + dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot); - Vector2 v = new Vector2(); + Vector2 v; if (dot > 0.9995f) { @@ -169,7 +174,7 @@ namespace Godot } else { - float angle = c * Mathf.Acos(dot); + real_t angle = c * Mathf.Acos(dot); Vector2 v3 = (v2 - v1 * dot).Normalized(); v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle); } @@ -179,7 +184,7 @@ namespace Godot Vector2 p2 = m.Origin; // Construct matrix - Transform2D res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); + var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); Vector2 scale = s1.LinearInterpolate(s2, c); res.x *= scale; res.y *= scale; @@ -189,10 +194,10 @@ namespace Godot public Transform2D Inverse() { - Transform2D inv = this; + var inv = this; // Swap - float temp = inv.x.y; + real_t temp = inv.x.y; inv.x.y = inv.y.x; inv.y.x = temp; @@ -203,13 +208,13 @@ namespace Godot public Transform2D Orthonormalized() { - Transform2D on = this; + var on = this; Vector2 onX = on.x; Vector2 onY = on.y; onX.Normalize(); - onY = onY - onX * (onX.Dot(onY)); + onY = onY - onX * onX.Dot(onY); onY.Normalize(); on.x = onX; @@ -218,33 +223,33 @@ namespace Godot return on; } - public Transform2D Rotated(float phi) + public Transform2D Rotated(real_t phi) { return this * new Transform2D(phi, new Vector2()); } public Transform2D Scaled(Vector2 scale) { - Transform2D copy = this; + var copy = this; copy.x *= scale; copy.y *= scale; copy.o *= scale; return copy; } - private float Tdotx(Vector2 with) + private real_t Tdotx(Vector2 with) { return this[0, 0] * with[0] + this[1, 0] * with[1]; } - private float Tdoty(Vector2 with) + private real_t Tdoty(Vector2 with) { return this[0, 1] * with[0] + this[1, 1] * with[1]; } public Transform2D Translated(Vector2 offset) { - Transform2D copy = this; + var copy = this; copy.o += copy.BasisXform(offset); return copy; } @@ -259,24 +264,26 @@ namespace Godot Vector2 vInv = v - o; return new Vector2(x.Dot(vInv), y.Dot(vInv)); } - + + // Constructors public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin) { - this.x = xAxis; - this.y = yAxis; - this.o = origin; + x = xAxis; + y = yAxis; + o = origin; } - public Transform2D(float xx, float xy, float yx, float yy, float ox, float oy) + + public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { - this.x = new Vector2(xx, xy); - this.y = new Vector2(yx, yy); - this.o = new Vector2(ox, oy); + x = new Vector2(xx, xy); + y = new Vector2(yx, yy); + o = new Vector2(ox, oy); } - public Transform2D(float rot, Vector2 pos) + public Transform2D(real_t rot, Vector2 pos) { - float cr = Mathf.Cos(rot); - float sr = Mathf.Sin(rot); + real_t cr = Mathf.Cos(rot); + real_t sr = Mathf.Sin(rot); x.x = cr; y.y = cr; x.y = -sr; @@ -288,7 +295,7 @@ namespace Godot { left.o = left.Xform(right.o); - float x0, x1, y0, y1; + real_t x0, x1, y0, y1; x0 = left.Tdotx(right.x); x1 = left.Tdoty(right.x); @@ -337,9 +344,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(), - this.y.ToString(), - this.o.ToString() + x.ToString(), + y.ToString(), + o.ToString() }); } @@ -347,9 +354,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(format), - this.y.ToString(format), - this.o.ToString(format) + x.ToString(format), + y.ToString(format), + o.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt new file mode 100755 index 0000000000..0cfbf08886 --- /dev/null +++ b/modules/mono/glue/cs_files/VERSION.txt @@ -0,0 +1 @@ +2 diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs index 238775bda2..9bc40cf8df 100644 --- a/modules/mono/glue/cs_files/Vector2.cs +++ b/modules/mono/glue/cs_files/Vector2.cs @@ -1,22 +1,26 @@ -using System; -using System.Runtime.InteropServices; - // file: core/math/math_2d.h // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/math/math_2d.cpp // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { [StructLayout(LayoutKind.Sequential)] public struct Vector2 : IEquatable<Vector2> { - public float x; - public float y; + public real_t x; + public real_t y; - public float this[int index] + public real_t this[int index] { get { @@ -48,7 +52,7 @@ namespace Godot internal void Normalize() { - float length = x * x + y * y; + real_t length = x * x + y * y; if (length != 0f) { @@ -58,7 +62,7 @@ namespace Godot } } - private float Cross(Vector2 b) + private real_t Cross(Vector2 b) { return x * b.y - y * b.x; } @@ -68,22 +72,22 @@ namespace Godot return new Vector2(Mathf.Abs(x), Mathf.Abs(y)); } - public float Angle() + public real_t Angle() { return Mathf.Atan2(y, x); } - public float AngleTo(Vector2 to) + public real_t AngleTo(Vector2 to) { return Mathf.Atan2(Cross(to), Dot(to)); } - public float AngleToPoint(Vector2 to) + public real_t AngleToPoint(Vector2 to) { return Mathf.Atan2(x - to.x, y - to.y); } - public float Aspect() + public real_t Aspect() { return x / y; } @@ -93,10 +97,15 @@ namespace Godot return -Reflect(n); } - public Vector2 Clamped(float length) + public Vector2 Ceil() { - Vector2 v = this; - float l = this.Length(); + return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y)); + } + + public Vector2 Clamped(real_t length) + { + var v = this; + real_t l = Length(); if (l > 0 && length < l) { @@ -107,33 +116,33 @@ namespace Godot return v; } - public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, float t) + public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t) { - Vector2 p0 = preA; - Vector2 p1 = this; - Vector2 p2 = b; - Vector2 p3 = postB; + var p0 = preA; + var p1 = this; + var p2 = b; + var p3 = postB; - float t2 = t * t; - float t3 = t2 * t; + real_t t2 = t * t; + real_t t3 = t2 * t; - return 0.5f * ((p1 * 2.0f) + + return 0.5f * (p1 * 2.0f + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } - public float DistanceSquaredTo(Vector2 to) + public real_t DistanceSquaredTo(Vector2 to) { return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); } - public float DistanceTo(Vector2 to) + public real_t DistanceTo(Vector2 to) { return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y)); } - public float Dot(Vector2 with) + public real_t Dot(Vector2 with) { return x * with.x + y * with.y; } @@ -148,29 +157,29 @@ namespace Godot return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; } - public float Length() + public real_t Length() { return Mathf.Sqrt(x * x + y * y); } - public float LengthSquared() + public real_t LengthSquared() { return x * x + y * y; } - public Vector2 LinearInterpolate(Vector2 b, float t) + public Vector2 LinearInterpolate(Vector2 b, real_t t) { - Vector2 res = this; + var res = this; - res.x += (t * (b.x - x)); - res.y += (t * (b.y - y)); + res.x += t * (b.x - x); + res.y += t * (b.y - y); return res; } public Vector2 Normalized() { - Vector2 result = this; + var result = this; result.Normalize(); return result; } @@ -180,12 +189,28 @@ namespace Godot return 2.0f * n * Dot(n) - this; } - public Vector2 Rotated(float phi) + public Vector2 Rotated(real_t phi) { - float rads = Angle() + phi; + real_t rads = Angle() + phi; return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length(); } + public Vector2 Round() + { + return new Vector2(Mathf.Round(x), Mathf.Round(y)); + } + + public void Set(real_t x, real_t y) + { + this.x = x; + this.y = y; + } + public void Set(Vector2 v) + { + x = v.x; + y = v.y; + } + public Vector2 Slide(Vector2 n) { return this - n * Dot(n); @@ -200,12 +225,36 @@ namespace Godot { return new Vector2(y, -x); } - - public Vector2(float x, float y) + + private static readonly Vector2 zero = new Vector2 (0, 0); + private static readonly Vector2 one = new Vector2 (1, 1); + private static readonly Vector2 negOne = new Vector2 (-1, -1); + + private static readonly Vector2 up = new Vector2 (0, 1); + private static readonly Vector2 down = new Vector2 (0, -1); + private static readonly Vector2 right = new Vector2 (1, 0); + private static readonly Vector2 left = new Vector2 (-1, 0); + + public static Vector2 Zero { get { return zero; } } + public static Vector2 One { get { return one; } } + public static Vector2 NegOne { get { return negOne; } } + + public static Vector2 Up { get { return up; } } + public static Vector2 Down { get { return down; } } + public static Vector2 Right { get { return right; } } + public static Vector2 Left { get { return left; } } + + // Constructors + public Vector2(real_t x, real_t y) { this.x = x; this.y = y; } + public Vector2(Vector2 v) + { + x = v.x; + y = v.y; + } public static Vector2 operator +(Vector2 left, Vector2 right) { @@ -228,14 +277,14 @@ namespace Godot return vec; } - public static Vector2 operator *(Vector2 vec, float scale) + public static Vector2 operator *(Vector2 vec, real_t scale) { vec.x *= scale; vec.y *= scale; return vec; } - public static Vector2 operator *(float scale, Vector2 vec) + public static Vector2 operator *(real_t scale, Vector2 vec) { vec.x *= scale; vec.y *= scale; @@ -249,7 +298,7 @@ namespace Godot return left; } - public static Vector2 operator /(Vector2 vec, float scale) + public static Vector2 operator /(Vector2 vec, real_t scale) { vec.x /= scale; vec.y /= scale; @@ -279,10 +328,8 @@ namespace Godot { return left.y < right.y; } - else - { - return left.x < right.x; - } + + return left.x < right.x; } public static bool operator >(Vector2 left, Vector2 right) @@ -291,10 +338,8 @@ namespace Godot { return left.y > right.y; } - else - { - return left.x > right.x; - } + + return left.x > right.x; } public static bool operator <=(Vector2 left, Vector2 right) @@ -303,10 +348,8 @@ namespace Godot { return left.y <= right.y; } - else - { - return left.x <= right.x; - } + + return left.x <= right.x; } public static bool operator >=(Vector2 left, Vector2 right) @@ -315,10 +358,8 @@ namespace Godot { return left.y >= right.y; } - else - { - return left.x >= right.x; - } + + return left.x >= right.x; } public override bool Equals(object obj) @@ -345,8 +386,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.x.ToString(), - this.y.ToString() + x.ToString(), + y.ToString() }); } @@ -354,8 +395,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.x.ToString(format), - this.y.ToString(format) + x.ToString(format), + y.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs index 190caa4b53..57e4494f7e 100644 --- a/modules/mono/glue/cs_files/Vector3.cs +++ b/modules/mono/glue/cs_files/Vector3.cs @@ -1,12 +1,16 @@ -using System; -using System.Runtime.InteropServices; - // file: core/math/vector3.h // commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 // file: core/math/vector3.cpp // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { @@ -20,11 +24,11 @@ namespace Godot Z } - public float x; - public float y; - public float z; + public real_t x; + public real_t y; + public real_t z; - public float this[int index] + public real_t this[int index] { get { @@ -61,7 +65,7 @@ namespace Godot internal void Normalize() { - float length = this.Length(); + real_t length = Length(); if (length == 0f) { @@ -80,7 +84,7 @@ namespace Godot return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z)); } - public float AngleTo(Vector3 to) + public real_t AngleTo(Vector3 to) { return Mathf.Atan2(Cross(to).Length(), Dot(to)); } @@ -99,40 +103,40 @@ namespace Godot { return new Vector3 ( - (y * b.z) - (z * b.y), - (z * b.x) - (x * b.z), - (x * b.y) - (y * b.x) + y * b.z - z * b.y, + z * b.x - x * b.z, + x * b.y - y * b.x ); } - public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, float t) + public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t) { - Vector3 p0 = preA; - Vector3 p1 = this; - Vector3 p2 = b; - Vector3 p3 = postB; + var p0 = preA; + var p1 = this; + var p2 = b; + var p3 = postB; - float t2 = t * t; - float t3 = t2 * t; + real_t t2 = t * t; + real_t t3 = t2 * t; return 0.5f * ( - (p1 * 2.0f) + (-p0 + p2) * t + + p1 * 2.0f + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3 ); } - public float DistanceSquaredTo(Vector3 b) + public real_t DistanceSquaredTo(Vector3 b) { return (b - this).LengthSquared(); } - public float DistanceTo(Vector3 b) + public real_t DistanceTo(Vector3 b) { return (b - this).Length(); } - public float Dot(Vector3 b) + public real_t Dot(Vector3 b) { return x * b.x + y * b.y + z * b.z; } @@ -152,31 +156,31 @@ namespace Godot return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; } - public float Length() + public real_t Length() { - float x2 = x * x; - float y2 = y * y; - float z2 = z * z; + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; return Mathf.Sqrt(x2 + y2 + z2); } - public float LengthSquared() + public real_t LengthSquared() { - float x2 = x * x; - float y2 = y * y; - float z2 = z * z; + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; return x2 + y2 + z2; } - public Vector3 LinearInterpolate(Vector3 b, float t) + public Vector3 LinearInterpolate(Vector3 b, real_t t) { return new Vector3 ( - x + (t * (b.x - x)), - y + (t * (b.y - y)), - z + (t * (b.z - z)) + x + t * (b.x - x), + y + t * (b.y - y), + z + t * (b.z - z) ); } @@ -192,7 +196,7 @@ namespace Godot public Vector3 Normalized() { - Vector3 v = this; + var v = this; v.Normalize(); return v; } @@ -215,11 +219,29 @@ namespace Godot return 2.0f * n * Dot(n) - this; } - public Vector3 Rotated(Vector3 axis, float phi) + public Vector3 Round() + { + return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z)); + } + + public Vector3 Rotated(Vector3 axis, real_t phi) { return new Basis(axis, phi).Xform(this); } + public void Set(real_t x, real_t y, real_t z) + { + this.x = x; + this.y = y; + this.z = z; + } + public void Set(Vector3 v) + { + x = v.x; + y = v.y; + z = v.z; + } + public Vector3 Slide(Vector3 n) { return this - n * Dot(n); @@ -243,13 +265,42 @@ namespace Godot 0f, 0f, z ); } - - public Vector3(float x, float y, float z) + + private static readonly Vector3 zero = new Vector3 (0, 0, 0); + private static readonly Vector3 one = new Vector3 (1, 1, 1); + private static readonly Vector3 negOne = new Vector3 (-1, -1, -1); + + private static readonly Vector3 up = new Vector3 (0, 1, 0); + private static readonly Vector3 down = new Vector3 (0, -1, 0); + private static readonly Vector3 right = new Vector3 (1, 0, 0); + private static readonly Vector3 left = new Vector3 (-1, 0, 0); + private static readonly Vector3 forward = new Vector3 (0, 0, -1); + private static readonly Vector3 back = new Vector3 (0, 0, 1); + + public static Vector3 Zero { get { return zero; } } + public static Vector3 One { get { return one; } } + public static Vector3 NegOne { get { return negOne; } } + + public static Vector3 Up { get { return up; } } + public static Vector3 Down { get { return down; } } + public static Vector3 Right { get { return right; } } + public static Vector3 Left { get { return left; } } + public static Vector3 Forward { get { return forward; } } + public static Vector3 Back { get { return back; } } + + // Constructors + public Vector3(real_t x, real_t y, real_t z) { this.x = x; this.y = y; this.z = z; } + public Vector3(Vector3 v) + { + x = v.x; + y = v.y; + z = v.z; + } public static Vector3 operator +(Vector3 left, Vector3 right) { @@ -275,7 +326,7 @@ namespace Godot return vec; } - public static Vector3 operator *(Vector3 vec, float scale) + public static Vector3 operator *(Vector3 vec, real_t scale) { vec.x *= scale; vec.y *= scale; @@ -283,7 +334,7 @@ namespace Godot return vec; } - public static Vector3 operator *(float scale, Vector3 vec) + public static Vector3 operator *(real_t scale, Vector3 vec) { vec.x *= scale; vec.y *= scale; @@ -299,7 +350,7 @@ namespace Godot return left; } - public static Vector3 operator /(Vector3 vec, float scale) + public static Vector3 operator /(Vector3 vec, real_t scale) { vec.x /= scale; vec.y /= scale; @@ -331,8 +382,7 @@ namespace Godot { if (left.y == right.y) return left.z < right.z; - else - return left.y < right.y; + return left.y < right.y; } return left.x < right.x; @@ -344,8 +394,7 @@ namespace Godot { if (left.y == right.y) return left.z > right.z; - else - return left.y > right.y; + return left.y > right.y; } return left.x > right.x; @@ -357,8 +406,7 @@ namespace Godot { if (left.y == right.y) return left.z <= right.z; - else - return left.y < right.y; + return left.y < right.y; } return left.x < right.x; @@ -370,8 +418,7 @@ namespace Godot { if (left.y == right.y) return left.z >= right.z; - else - return left.y > right.y; + return left.y > right.y; } return left.x > right.x; @@ -401,9 +448,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(), - this.y.ToString(), - this.z.ToString() + x.ToString(), + y.ToString(), + z.ToString() }); } @@ -411,9 +458,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format) + x.ToString(format), + y.ToString(format), + z.ToString(format) }); } } diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index 4c26c3e6bd..f604464e8f 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -39,4 +39,7 @@ #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor" #define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools" +#define BINDINGS_CLASS_NATIVECALLS "NativeCalls" +#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls" + #endif // GODOTSHARP_DEFS_H diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 39474f8cbc..0646580eaa 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -42,7 +42,10 @@ #include "project_settings.h" #include "../csharp_script.h" +#include "../godotsharp_dirs.h" #include "../utils/path_utils.h" +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" #include "gd_mono_utils.h" #ifdef TOOLS_ENABLED @@ -71,7 +74,31 @@ void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { GDMono *GDMono::singleton = NULL; +namespace { + +void setup_runtime_main_args() { + CharString execpath = OS::get_singleton()->get_executable_path().utf8(); + + List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); + + List<CharString> cmdline_args_utf8; + Vector<char *> main_args; + main_args.resize(cmdline_args.size() + 1); + + main_args[0] = execpath.ptrw(); + + int i = 1; + for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) { + CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get(); + main_args[i] = stored.ptrw(); + i++; + } + + mono_runtime_set_main_args(main_args.size(), main_args.ptrw()); +} + #ifdef DEBUG_ENABLED + static bool _wait_for_debugger_msecs(uint32_t p_msecs) { do { @@ -93,9 +120,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) { return mono_is_debugger_attached(); } -#endif -#ifdef DEBUG_ENABLED void gdmono_debug_init() { mono_debug_init(MONO_DEBUG_FORMAT_MONO); @@ -122,8 +147,11 @@ void gdmono_debug_init() { }; mono_jit_parse_options(2, (char **)options); } + #endif +} // namespace + void GDMono::initialize() { ERR_FAIL_NULL(Engine::get_singleton()); @@ -176,6 +204,8 @@ void GDMono::initialize() { GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread()); + setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs + runtime_initialized = true; OS::get_singleton()->print("Mono: Runtime initialized\n"); @@ -208,7 +238,46 @@ void GDMono::initialize() { _register_internal_calls(); // The following assemblies are not required at initialization - _load_all_script_assemblies(); +#ifndef MONO_GLUE_DISABLED + if (_load_api_assemblies()) { + if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { + // Everything is fine with the api assemblies, load the project assembly + _load_project_assembly(); + } else { +#ifdef TOOLS_ENABLED + // The assembly was successfuly loaded, but the full api could not be cached. + // This is most likely an outdated assembly loaded because of an invalid version in the metadata, + // so we invalidate the version in the metadata and unload the script domain. + + if (core_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Core API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } + + if (editor_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Editor API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); + } + + OS::get_singleton()->print("Mono: Proceeding to unload scripts domain because of invalid API assemblies\n"); + + Error err = _unload_scripts_domain(); + if (err != OK) { + WARN_PRINT("Mono: Failed to unload scripts domain"); + } +#else + ERR_PRINT("The loaded API assembly is invalid"); + CRASH_NOW(); +#endif + } + } +#else + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n"); +#endif mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL); @@ -219,7 +288,11 @@ void GDMono::initialize() { namespace GodotSharpBindings { uint64_t get_core_api_hash(); +#ifdef TOOLS_ENABLED uint64_t get_editor_api_hash(); +#endif // TOOLS_ENABLED +uint32_t get_bindings_version(); +uint32_t get_cs_glue_version(); void register_generated_icalls(); } // namespace GodotSharpBindings @@ -271,43 +344,84 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) { return assemblies[domain_id].getptr(p_name); } -bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) { +bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) { + + CRASH_COND(!r_assembly); + + MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); + bool result = load_assembly(p_name, aname, r_assembly, p_refonly); + mono_assembly_name_free(aname); + mono_free(aname); + + return result; +} + +bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { CRASH_COND(!r_assembly); if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8()); + OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...\n").utf8()); MonoImageOpenStatus status = MONO_IMAGE_OK; - MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); - MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false); - mono_assembly_name_free(aname); + MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly); if (!assembly) return false; + ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false); + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); - ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false); ERR_FAIL_COND_V(stored_assembly == NULL, false); - ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false); + *r_assembly = *stored_assembly; if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print(String("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8()); + OS::get_singleton()->print(String("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8()); return true; } +APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) { + APIAssembly::Version api_assembly_version; + + const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ? + BINDINGS_CLASS_NATIVECALLS : + BINDINGS_CLASS_NATIVECALLS_EDITOR; + + GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name); + + if (nativecalls_klass) { + GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash"); + if (api_hash_field) + api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL)); + + GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version"); + if (binds_ver_field) + api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL)); + + GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version"); + if (cs_glue_ver_field) + api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL)); + } + + return api_assembly_version; +} + +String APIAssembly::to_string(APIAssembly::Type p_type) { + return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR"; +} + bool GDMono::_load_corlib_assembly() { if (corlib_assembly) return true; - bool success = _load_assembly("mscorlib", &corlib_assembly); + bool success = load_assembly("mscorlib", &corlib_assembly); if (success) GDMonoUtils::update_corlib_cache(); @@ -317,13 +431,25 @@ bool GDMono::_load_corlib_assembly() { bool GDMono::_load_core_api_assembly() { - if (api_assembly) + if (core_api_assembly) return true; - bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly); +#ifdef TOOLS_ENABLED + if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) + return false; +#endif - if (success) + bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); + + if (success) { +#ifndef MONO_GLUE_DISABLED + APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); + core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; +#endif GDMonoUtils::update_godot_api_cache(); + } return success; } @@ -334,7 +460,23 @@ bool GDMono::_load_editor_api_assembly() { if (editor_api_assembly) return true; - return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); +#ifdef TOOLS_ENABLED + if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) + return false; +#endif + + bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); + + if (success) { +#ifndef MONO_GLUE_DISABLED + APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); + editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; +#endif + } + + return success; } #endif @@ -346,7 +488,7 @@ bool GDMono::_load_editor_tools_assembly() { _GDMONO_SCOPE_DOMAIN_(tools_domain) - return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); + return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); } #endif @@ -360,17 +502,20 @@ bool GDMono::_load_project_assembly() { name = "UnnamedProject"; } - bool success = _load_assembly(name, &project_assembly); + bool success = load_assembly(name, &project_assembly); - if (success) + if (success) { mono_assembly_set_main(project_assembly->get_assembly()); + } else { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->printerr("Mono: Failed to load project assembly\n"); + } return success; } -bool GDMono::_load_all_script_assemblies() { +bool GDMono::_load_api_assemblies() { -#ifndef MONO_GLUE_DISABLED if (!_load_core_api_assembly()) { if (OS::get_singleton()->is_stdout_verbose()) OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n"); @@ -385,20 +530,72 @@ bool GDMono::_load_all_script_assemblies() { #endif } - if (!_load_project_assembly()) { - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->printerr("Mono: Failed to load project assembly\n"); - return false; + return true; +} + +#ifdef TOOLS_ENABLED +String GDMono::_get_api_assembly_metadata_path() { + + return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg"); +} + +void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) { + + String section = APIAssembly::to_string(p_api_type); + String path = _get_api_assembly_metadata_path(); + + Ref<ConfigFile> metadata; + metadata.instance(); + metadata->load(path); + + metadata->set_value(section, "invalidated", p_invalidated); + + String assembly_path = GodotSharpDirs::get_res_assemblies_dir() + .plus_file(p_api_type == APIAssembly::API_CORE ? + API_ASSEMBLY_NAME ".dll" : + EDITOR_API_ASSEMBLY_NAME ".dll"); + + ERR_FAIL_COND(!FileAccess::exists(assembly_path)); + + uint64_t modified_time = FileAccess::get_modified_time(assembly_path); + + metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time)); + + String dir = path.get_base_dir(); + if (!DirAccess::exists(dir)) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND(!da); + Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir)); + ERR_FAIL_COND(err != OK); } - return true; -#else - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n"); + Error save_err = metadata->save(path); + ERR_FAIL_COND(save_err != OK); +} - return true; -#endif +bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) { + + String section = APIAssembly::to_string(p_api_type); + + Ref<ConfigFile> metadata; + metadata.instance(); + metadata->load(_get_api_assembly_metadata_path()); + + String assembly_path = GodotSharpDirs::get_res_assemblies_dir() + .plus_file(p_api_type == APIAssembly::API_CORE ? + API_ASSEMBLY_NAME ".dll" : + EDITOR_API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(assembly_path)) + return false; + + uint64_t modified_time = FileAccess::get_modified_time(assembly_path); + + uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0); + + return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time; } +#endif Error GDMono::_load_scripts_domain() { @@ -441,12 +638,15 @@ Error GDMono::_unload_scripts_domain() { _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); - api_assembly = NULL; + core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; #endif + core_api_assembly_out_of_sync = false; + editor_api_assembly_out_of_sync = false; + MonoDomain *domain = scripts_domain; scripts_domain = NULL; @@ -501,16 +701,80 @@ Error GDMono::reload_scripts_domain() { return err; } - if (!_load_all_script_assemblies()) { - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->printerr("Mono: Failed to load script assemblies\n"); +#ifndef MONO_GLUE_DISABLED + if (!_load_api_assemblies()) { return ERR_CANT_OPEN; } + if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { + // Everything is fine with the api assemblies, load the project assembly + _load_project_assembly(); + } else { + // The assembly was successfuly loaded, but the full api could not be cached. + // This is most likely an outdated assembly loaded because of an invalid version in the metadata, + // so we invalidate the version in the metadata and unload the script domain. + + if (core_api_assembly_out_of_sync) { + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("Core API assembly is in sync, but the cache update failed"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } + + if (editor_api_assembly_out_of_sync) { + metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); + } + + Error err = _unload_scripts_domain(); + if (err != OK) { + WARN_PRINT("Mono: Failed to unload scripts domain"); + } + + return ERR_CANT_RESOLVE; + } + + if (!_load_project_assembly()) + return ERR_CANT_OPEN; +#else + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n"); +#endif + return OK; } #endif +Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { + + CRASH_COND(p_domain == NULL); + + String domain_name = mono_domain_get_friendly_name(p_domain); + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print(String("Mono: Unloading domain `" + domain_name + "`...\n").utf8()); + } + + if (mono_domain_get() != root_domain) + mono_domain_set(root_domain, true); + + mono_gc_collect(mono_gc_max_generation()); + mono_domain_finalize(p_domain, 2000); + mono_gc_collect(mono_gc_max_generation()); + + _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); + + MonoObject *ex = NULL; + mono_domain_try_unload(p_domain, &ex); + + if (ex) { + ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:"); + mono_print_unhandled_exception(ex); + return FAILED; + } + + return OK; +} + GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { MonoImage *image = mono_class_get_image(p_raw_class); @@ -562,8 +826,11 @@ GDMono::GDMono() { tools_domain = NULL; #endif + core_api_assembly_out_of_sync = false; + editor_api_assembly_out_of_sync = false; + corlib_assembly = NULL; - api_assembly = NULL; + core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 67251778c6..5e01152870 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -31,6 +31,8 @@ #ifndef GD_MONO_H #define GD_MONO_H +#include "core/io/config_file.h" + #include "../godotsharp_defs.h" #include "gd_mono_assembly.h" #include "gd_mono_log.h" @@ -39,6 +41,43 @@ #include "../utils/mono_reg_utils.h" #endif +namespace APIAssembly { +enum Type { + API_CORE, + API_EDITOR +}; + +struct Version { + uint64_t godot_api_hash; + uint32_t bindings_version; + uint32_t cs_glue_version; + + bool operator==(const Version &p_other) const { + return godot_api_hash == p_other.godot_api_hash && + bindings_version == p_other.bindings_version && + cs_glue_version == p_other.cs_glue_version; + } + + Version() : + godot_api_hash(0), + bindings_version(0), + cs_glue_version(0) { + } + + Version(uint64_t p_godot_api_hash, + uint32_t p_bindings_version, + uint32_t p_cs_glue_version) : + godot_api_hash(p_godot_api_hash), + bindings_version(p_bindings_version), + cs_glue_version(p_cs_glue_version) { + } + + static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type); +}; + +String to_string(Type p_type); +} // namespace APIAssembly + #define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain() #ifdef TOOLS_ENABLED #define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain() @@ -55,8 +94,11 @@ class GDMono { MonoDomain *tools_domain; #endif + bool core_api_assembly_out_of_sync; + bool editor_api_assembly_out_of_sync; + GDMonoAssembly *corlib_assembly; - GDMonoAssembly *api_assembly; + GDMonoAssembly *core_api_assembly; GDMonoAssembly *project_assembly; #ifdef TOOLS_ENABLED GDMonoAssembly *editor_api_assembly; @@ -75,7 +117,11 @@ class GDMono { #endif bool _load_project_assembly(); - bool _load_all_script_assemblies(); + bool _load_api_assemblies(); + +#ifdef TOOLS_ENABLED + String _get_api_assembly_metadata_path(); +#endif void _register_internal_calls(); @@ -94,8 +140,6 @@ class GDMono { void _initialize_and_check_api_hashes(); #endif - bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly); - GDMonoLog *gdmono_log; #ifdef WINDOWS_ENABLED @@ -113,6 +157,11 @@ public: #endif #endif +#ifdef TOOLS_ENABLED + void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated); + bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type); +#endif + static GDMono *get_singleton() { return singleton; } // Do not use these, unless you know what you're doing @@ -128,7 +177,7 @@ public: #endif _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_api_assembly() const { return api_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; } _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; } #ifdef TOOLS_ENABLED _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; } @@ -145,6 +194,10 @@ public: Error reload_scripts_domain(); #endif + bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false); + bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); + Error finalize_and_unload_domain(MonoDomain *p_domain); + void initialize(); GDMono(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index ef39b8549d..d062d56dcf 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -43,7 +43,23 @@ bool GDMonoAssembly::no_search = false; Vector<String> GDMonoAssembly::search_dirs; -MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) { +MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) { + return GDMonoAssembly::_search_hook(aname, user_data, false); +} + +MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) { + return GDMonoAssembly::_search_hook(aname, user_data, true); +} + +MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { + return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false); +} + +MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { + return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true); +} + +MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) { (void)user_data; // UNUSED @@ -60,7 +76,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d no_search = true; // Avoid the recursion madness String path; - MonoAssembly *res = NULL; + GDMonoAssembly *res = NULL; for (int i = 0; i < search_dirs.size(); i++) { const String &search_dir = search_dirs[i]; @@ -68,44 +84,49 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d if (has_extension) { path = search_dir.plus_file(name); if (FileAccess::exists(path)) { - res = _load_assembly_from(name.get_basename(), path); - break; + res = _load_assembly_from(name.get_basename(), path, refonly); + if (res != NULL) + break; } } else { path = search_dir.plus_file(name + ".dll"); if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path); - break; + res = _load_assembly_from(name, path, refonly); + if (res != NULL) + break; } path = search_dir.plus_file(name + ".exe"); if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path); - break; + res = _load_assembly_from(name, path, refonly); + if (res != NULL) + break; } } } no_search = false; - return res; + return res ? res->get_assembly() : NULL; } -MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { +MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) { (void)user_data; // UNUSED if (search_dirs.empty()) { -#ifdef TOOLS_DOMAIN search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); -#endif search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); search_dirs.push_back(OS::get_singleton()->get_resource_dir()); search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); +#ifdef GD_MONO_EDITOR_ASSEMBLIES_DIR + search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir().plus_file(_MKSTR(GD_MONO_EDITOR_ASSEMBLIES_DIR)).simplify_path()); +#endif const char *rootdir = mono_assembly_getrootdir(); if (rootdir) { search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5")); + search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5").plus_file("Facades")); } if (assemblies_path) { @@ -121,10 +142,11 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") { GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); - if (stored_assembly) return (*stored_assembly)->get_assembly(); + if (stored_assembly) + return (*stored_assembly)->get_assembly(); String path; - MonoAssembly *res = NULL; + GDMonoAssembly *res = NULL; for (int i = 0; i < search_dirs.size(); i++) { const String &search_dir = search_dirs[i]; @@ -132,53 +154,57 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse if (has_extension) { path = search_dir.plus_file(name); if (FileAccess::exists(path)) { - res = _load_assembly_from(name.get_basename(), path); - break; + res = _load_assembly_from(name.get_basename(), path, refonly); + if (res != NULL) + break; } } else { path = search_dir.plus_file(name + ".dll"); if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path); - break; + res = _load_assembly_from(name, path, refonly); + if (res != NULL) + break; } } } - if (res) return res; + return res ? res->get_assembly() : NULL; } return NULL; } -MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) { +GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) { GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path)); - MonoDomain *domain = mono_domain_get(); - - Error err = assembly->load(domain); + Error err = assembly->load(p_refonly); if (err != OK) { memdelete(assembly); ERR_FAIL_V(NULL); } + MonoDomain *domain = mono_domain_get(); GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly); - return assembly->get_assembly(); + return assembly; } void GDMonoAssembly::initialize() { - // TODO refonly as well? - mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL); - mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL); + mono_install_assembly_search_hook(&assembly_search_hook, NULL); + mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL); + mono_install_assembly_preload_hook(&assembly_preload_hook, NULL); + mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL); } -Error GDMonoAssembly::load(MonoDomain *p_domain) { +Error GDMonoAssembly::load(bool p_refonly) { ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE); + refonly = p_refonly; + uint64_t last_modified_time = FileAccess::get_modified_time(path); Vector<uint8_t> data = FileAccess::get_file_as_array(path); @@ -186,14 +212,15 @@ Error GDMonoAssembly::load(MonoDomain *p_domain) { String image_filename(path); - MonoImageOpenStatus status; + MonoImageOpenStatus status = MONO_IMAGE_OK; image = mono_image_open_from_data_with_name( (char *)&data[0], data.size(), - true, &status, false, + true, &status, refonly, image_filename.utf8().get_data()); - ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN); + ERR_FAIL_COND_V(status != MONO_IMAGE_OK, ERR_FILE_CANT_OPEN); + ERR_FAIL_NULL_V(image, ERR_FILE_CANT_OPEN); #ifdef DEBUG_ENABLED String pdb_path(path + ".pdb"); @@ -213,15 +240,10 @@ no_pdb: #endif - assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false); + assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly); ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN); - if (p_domain && mono_image_get_entry_point(image)) { - // TODO should this be removed? do we want to call main? what other effects does this have? - mono_jit_exec(p_domain, assembly, 0, NULL); - } - loaded = true; modified_time = last_modified_time; @@ -373,12 +395,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) return match; } +GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) { + + GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); + if (loaded_asm) + return *loaded_asm; + + no_search = true; + GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly); + no_search = false; + + return res; +} + GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) { loaded = false; gdobject_class_cache_updated = false; name = p_name; path = p_path; + refonly = false; modified_time = 0; assembly = NULL; image = NULL; diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 8a5fa19626..5cf744a5a2 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -71,6 +71,7 @@ class GDMonoAssembly { MonoAssembly *assembly; MonoImage *image; + bool refonly; bool loaded; String name; @@ -90,19 +91,25 @@ class GDMonoAssembly { static bool no_search; static Vector<String> search_dirs; - static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data); - static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); + static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data); + static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data); + static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); + static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); - static MonoAssembly *_load_assembly_from(const String &p_name, const String &p_path); + static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly); + static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly); + + static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly); friend class GDMono; static void initialize(); public: - Error load(MonoDomain *p_domain); + Error load(bool p_refonly); Error wrapper_for_image(MonoImage *p_image); void unload(); + _FORCE_INLINE_ bool is_refonly() const { return refonly; } _FORCE_INLINE_ bool is_loaded() const { return loaded; } _FORCE_INLINE_ MonoImage *get_image() const { return image; } _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; } @@ -115,6 +122,8 @@ public: GDMonoClass *get_object_derived_class(const StringName &p_class); + static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); + GDMonoAssembly(const String &p_name, const String &p_path = String()); ~GDMonoAssembly(); }; diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 8fd437223f..6572408ab5 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -195,9 +195,9 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict); // Transform #define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \ - m_in.basis[0].x, m_in.basis[1].x, m_in.basis[2].x, \ - m_in.basis[0].y, m_in.basis[1].y, m_in.basis[2].y, \ - m_in.basis[0].z, m_in.basis[1].z, m_in.basis[2].z, \ + m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \ + m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \ + m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \ m_in.origin.x, m_in.origin.y, m_in.origin.z \ }; #define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \ diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 42e307cf08..db136a1313 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -143,7 +143,7 @@ void MonoCache::cleanup() { godot_api_cache_updated = false; } -#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) +#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) void update_corlib_cache() { @@ -245,7 +245,7 @@ void update_godot_api_cache() { mono_runtime_object_init(task_scheduler); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); - mono_cache.corlib_cache_updated = true; + mono_cache.godot_api_cache_updated = true; } void clear_cache() { @@ -305,7 +305,7 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) { if (class_name[0] == '_') class_name = class_name.substr(1, class_name.length()); - GDMonoClass *klass = GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); + GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); #ifdef TOOLS_ENABLED if (!klass) { @@ -321,7 +321,7 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) { do { const GDMonoAssembly *assembly = klass->get_assembly(); - if (assembly == GDMono::get_singleton()->get_api_assembly()) + if (assembly == GDMono::get_singleton()->get_core_api_assembly()) return klass; #ifdef TOOLS_ENABLED if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) @@ -420,37 +420,56 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { if (!ScriptDebugger::get_singleton()) return; - GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); - MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr()); + ScriptLanguage::StackInfo separator; + separator.file = ""; + separator.func = "--- " + RTR("End of inner exception stack trace") + " ---"; + separator.line = 0; + + Vector<ScriptLanguage::StackInfo> si; + String exc_msg = ""; + + while (p_exc != NULL) { + GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); + MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr()); - MonoBoolean need_file_info = true; - void *ctor_args[2] = { p_exc, &need_file_info }; + MonoBoolean need_file_info = true; + void *ctor_args[2] = { p_exc, &need_file_info }; - MonoObject *unexpected_exc = NULL; - CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); + MonoObject *unexpected_exc = NULL; + CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); - if (unexpected_exc != NULL) { - mono_print_unhandled_exception(unexpected_exc); + if (unexpected_exc != NULL) { + mono_print_unhandled_exception(unexpected_exc); - if (p_recursion_caution) { - // Called from CSharpLanguage::get_current_stack_info, - // so printing an error here could result in endless recursion - OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed"); - return; - } else { - ERR_FAIL(); + if (p_recursion_caution) { + // Called from CSharpLanguage::get_current_stack_info, + // so printing an error here could result in endless recursion + OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed"); + return; + } else { + ERR_FAIL(); + } } - } - Vector<ScriptLanguage::StackInfo> si; - if (stack_trace != NULL && !p_recursion_caution) - si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace); + Vector<ScriptLanguage::StackInfo> _si; + if (stack_trace != NULL && !p_recursion_caution) { + _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace); + for (int i = _si.size() - 1; i >= 0; i--) + si.insert(0, _si[i]); + } + + exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc); + + GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException"); + p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL; + if (p_exc != NULL) + si.insert(0, separator); + } String file = si.size() ? si[0].file : __FILE__; String func = si.size() ? si[0].func : FUNCTION_STR; int line = si.size() ? si[0].line : __LINE__; String error_msg = "Unhandled exception"; - String exc_msg = GDMonoUtils::get_exception_name_and_message(p_exc); ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si); #endif diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py index 8ddddb3a24..9c188d07a7 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/mono_reg_utils.py @@ -75,7 +75,7 @@ def find_msbuild_tools_path_reg(): vswhere = os.getenv('PROGRAMFILES') vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe' - vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild'] + vswhere_args = ['-latest', '-products', '*', '-requires', 'Microsoft.Component.MSBuild'] try: lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 9bb8da8ac0..7b23cd7579 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -174,6 +174,8 @@ String find_msbuild_tools_path() { List<String> vswhere_args; vswhere_args.push_back("-latest"); + vswhere_args.push_back("-products"); + vswhere_args.push_back("*"); vswhere_args.push_back("-requires"); vswhere_args.push_back("Microsoft.Component.MSBuild"); diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index 76c0b969d8..8174bccdb7 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -536,8 +536,8 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int int p_x, p_y; - int p_modulation[8][16]; - int p_modulation_modes[8][16]; + int p_modulation[8][16] = { { 0 } }; + int p_modulation_modes[8][16] = { { 0 } }; int Mod, DoPT; diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 36de04f293..75e8903ff8 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegEx" inherits="Reference" category="Core" version="3.1-dev"> +<class name="RegEx" inherits="Reference" category="Core" version="3.1"> <brief_description> Class for searching text for patterns using regular expressions. </brief_description> <description> - Regular Expression (or regex) is a compact programming language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explainations on the Internet. + Regular Expression (or regex) is a compact programming language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet. To begin, the RegEx object needs to be compiled with the search pattern using [method compile] before it can be used. [codeblock] var regex = RegEx.new() regex.compile("\\w-(\\d+)") [/codeblock] The search pattern must be escaped first for gdscript before it is escaped for the expression. For example, [code]compile("\\d+")[/code] would be read by RegEx as [code]\d+[/code]. Similarly, [code]compile("\"(?:\\\\.|[^\"])*\"")[/code] would be read as [code]"(?:\\.|[^"])*"[/code] - Using [method search] you can find the pattern within the given text. If a pattern is found, [RegExMatch] is returned and you can retrieve details of the results using fuctions such as [method RegExMatch.get_string] and [method RegExMatch.get_start]. + Using [method search] you can find the pattern within the given text. If a pattern is found, [RegExMatch] is returned and you can retrieve details of the results using functions such as [method RegExMatch.get_string] and [method RegExMatch.get_start]. [codeblock] var regex = RegEx.new() regex.compile("\\w-(\\d+)") diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 550411d3e0..3d070d2786 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegExMatch" inherits="Reference" category="Core" version="3.1-dev"> +<class name="RegExMatch" inherits="Reference" category="Core" version="3.1"> <brief_description> Contains the results of a regex search. </brief_description> diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml index 8d311ae1b8..ac95a7197f 100644 --- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.1-dev"> +<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.1"> <brief_description> OGG Vorbis audio stream driver. </brief_description> diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml index 5872dd35ff..018f4734ec 100644 --- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.1-dev"> +<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp index 16ebfa2832..c8acdb689a 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp @@ -100,6 +100,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin ogg_stream.instance(); ogg_stream->set_data(data); + ERR_FAIL_COND_V(!ogg_stream->get_data().size(), ERR_FILE_CORRUPT); ogg_stream->set_loop(loop); ogg_stream->set_loop_offset(loop_offset); diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/theora/doc_classes/ResourceImporterTheora.xml index 6d2de4a9ad..5fc40a6eba 100644 --- a/modules/theora/doc_classes/ResourceImporterTheora.xml +++ b/modules/theora/doc_classes/ResourceImporterTheora.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1-dev"> +<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index 550844128d..e7c4727332 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.1-dev"> +<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 58c6d73ab2..9e6307c0bf 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -261,14 +261,12 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { /* look for further theora headers */ while (theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&to, &op))) { if (ret < 0) { - fprintf(stderr, "Error parsing Theora stream headers; " - "corrupt stream?\n"); + fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n"); clear(); return; } if (!th_decode_headerin(&ti, &tc, &ts, &op)) { - fprintf(stderr, "Error parsing Theora stream headers; " - "corrupt stream?\n"); + fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n"); clear(); return; } @@ -312,9 +310,15 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { td = th_decode_alloc(&ti, ts); px_fmt = ti.pixel_fmt; switch (ti.pixel_fmt) { - case TH_PF_420: printf(" 4:2:0 video\n"); break; - case TH_PF_422: printf(" 4:2:2 video\n"); break; - case TH_PF_444: printf(" 4:4:4 video\n"); break; + case TH_PF_420: + //printf(" 4:2:0 video\n"); + break; + case TH_PF_422: + //printf(" 4:2:2 video\n"); + break; + case TH_PF_444: + //printf(" 4:4:4 video\n"); + break; case TH_PF_RSVD: default: printf(" video\n (UNKNOWN Chroma sampling!)\n"); @@ -519,7 +523,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) { #else if (file && /*!videobuf_ready && */ no_theora && theora_eos) { #endif - printf("video done, stopping\n"); + //printf("video done, stopping\n"); stop(); return; }; diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 975b294f30..dab186fb57 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScript" inherits="Script" category="Core" version="3.1-dev"> +<class name="VisualScript" inherits="Script" category="Core" version="3.1"> <brief_description> A script implemented in the Visual Script programming environment. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml index b529589b98..793291eca2 100644 --- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A Visual Script node representing a constant from the base types. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 9c2ec3b849..399ba8ef5d 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A Visual Script node used to call built-in functions. </brief_description> @@ -80,7 +80,7 @@ Return the natural logarithm of the input. Note that this is not the typical base-10 logarithm function calculators use. </constant> <constant name="MATH_EXP" value="20" enum="BuiltinFunc"> - Return [b]e[/b] raised to the power of the input. [b]e[/b] sometimes called "Euler's number" is a mathematical constant whose value is approximately 2.71828. + Return the mathematical constant [b]e[/b] raised to the specified power of the input. [b]e[/b] has an approximate value of 2.71828. </constant> <constant name="MATH_ISNAN" value="21" enum="BuiltinFunc"> Return whether the input is NaN (Not a Number) or not. NaN is usually produced by dividing 0 by 0, though other ways exist. diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml index dc946cfcbd..2e3a5b33c0 100644 --- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Gets a constant from a given class. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml index 5c20f27ec2..a4a890ea8a 100644 --- a/modules/visual_script/doc_classes/VisualScriptComment.xml +++ b/modules/visual_script/doc_classes/VisualScriptComment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A Visual Script node used to annotate the script. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml index a3313a43d9..353898c93a 100644 --- a/modules/visual_script/doc_classes/VisualScriptCondition.xml +++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A Visual Script node which branches the flow. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml index 274adb5423..ed633c4135 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Gets a contant's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml index 2efe8d9bd6..14c44c6970 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A Visual Script node which calls a base type constructor. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index 2165c403d4..b8e77a1b0f 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A scripted Visual Script node. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml index f076c70715..d3158df357 100644 --- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml +++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A Visual Script node which deconstructs a base type instance into its parts. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml index 1c4542336f..fc49cfc07b 100644 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.1-dev"> +<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml index 7eea609db4..4bb05525bd 100644 --- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Emits a specified signal. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml index 26196f278e..93d7ce3516 100644 --- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml +++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> A Visual Script node returning a singleton from [@GlobalScope] </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml index 0b93c3092c..343e83cb55 100644 --- a/modules/visual_script/doc_classes/VisualScriptExpression.xml +++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml index 18daa42797..ec8e955cf7 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunction.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml index dc025cfb27..f6116cf539 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index 05e9a6cb81..c75dd0cdbc 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.1-dev"> +<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml index d0db8cbab3..9d43204f02 100644 --- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml index 983d8882a7..73c1f47e1a 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml index bc876900f3..652c29a9ac 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml index 4316bf146d..ab4c23012b 100644 --- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml +++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml index d815476e2c..7090621bd7 100644 --- a/modules/visual_script/doc_classes/VisualScriptIterator.xml +++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Steps through items in a given input. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml index 038fd8c9cd..5c8ee6453c 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Gets a local variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml index fa65a89c4a..f2e6c48907 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Changes a local variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml index 243ab03b49..d456e880b7 100644 --- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Commonly used mathematical constants. </brief_description> <description> - Provides common math constants, such as Pi or Euler's constant, on an output Data port. + Provides common math constants, such as Pi, on an output Data port. [b]Input Ports:[/b] none [b]Output Ports:[/b] @@ -35,7 +35,7 @@ Tau: [code]6.283185[/code] </constant> <constant name="MATH_CONSTANT_E" value="4" enum="MathConstant"> - Natural log: [code]2.718282[/code] + Mathematical constant [code]e[/code], the natural log base: [code]2.718282[/code] </constant> <constant name="MATH_CONSTANT_SQRT2" value="5" enum="MathConstant"> Square root of two: [code]1.414214[/code] diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml index c2c5464047..941a0cd91a 100644 --- a/modules/visual_script/doc_classes/VisualScriptNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.1-dev"> +<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.1"> <brief_description> A node which is part of a [VisualScript]. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml index 68a57191de..e60d50c977 100644 --- a/modules/visual_script/doc_classes/VisualScriptOperator.xml +++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml index 19abc27053..5a2886ccac 100644 --- a/modules/visual_script/doc_classes/VisualScriptPreload.xml +++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Creates a new [Resource] or loads one from the filesystem. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml index 88aac85f59..60cc8fdd4f 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml index ac962a071d..8f29e9d152 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml index a3144582cb..f6300e03f0 100644 --- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml +++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml index ca50f811c3..6095520eff 100644 --- a/modules/visual_script/doc_classes/VisualScriptReturn.xml +++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Exits a function and returns an optional value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml index 3d4bdcfd0a..7704eaba04 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Node reference. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml index c77b674b64..2c2af9262d 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml index f1aad841ca..0731fc77e1 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelect.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Chooses between two input values. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml index c2847fcada..61a73e104c 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelf.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Outputs a reference to the current instance. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml index 0180f34c72..c71e068045 100644 --- a/modules/visual_script/doc_classes/VisualScriptSequence.xml +++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Executes a series of Sequence ports. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index d34d0b7127..46aeebab9c 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml index ea9e6438cf..a00811a29b 100644 --- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml +++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Branches program flow based on a given input's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml index 4bdfeab35b..0bdc4ce89d 100644 --- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml +++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml index 76c218294e..06178a399d 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Gets a variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml index 0262ad5dfb..5969f25060 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Changes a variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml index 46a6ea7a30..b9e7f6a553 100644 --- a/modules/visual_script/doc_classes/VisualScriptWhile.xml +++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> Conditional loop. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml index a1129ffbd7..c4698f746a 100644 --- a/modules/visual_script/doc_classes/VisualScriptYield.xml +++ b/modules/visual_script/doc_classes/VisualScriptYield.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml index ad3a016c0d..b67e4ab1b8 100644 --- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.1-dev"> +<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 2809cff362..11401c0460 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -112,7 +112,9 @@ void register_visual_script_types() { register_visual_script_expression_node(); #ifdef TOOLS_ENABLED + ClassDB::set_current_api(ClassDB::API_EDITOR); ClassDB::register_class<_VisualScriptEditor>(); + ClassDB::set_current_api(ClassDB::API_CORE); vs_editor_singleton = memnew(_VisualScriptEditor); Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", _VisualScriptEditor::get_singleton())); diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 5987fdf5da..03bc4c114a 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -2028,6 +2028,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o function.flow_stack_size = 0; function.pass_stack_size = 0; function.node_count = 0; + Map<StringName, int> local_var_indices; if (function.node < 0) { @@ -2182,7 +2183,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o Ref<VisualScriptNode> node = F->get().node; VisualScriptNodeInstance *instance = instances[F->key()]; - // conect to default values + // connect to default values for (int i = 0; i < instance->input_port_count; i++) { if (instance->input_ports[i] == -1) { @@ -2192,7 +2193,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o } } - // conect to trash + // connect to trash for (int i = 0; i < instance->output_port_count; i++) { if (instance->output_ports[i] == -1) { instance->output_ports[i] = function.trash_pos; //trash is same for all diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 69bb522173..dad9c68312 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -374,12 +374,10 @@ class VisualScriptInstance : public ScriptInstance { int node; int max_stack; int trash_pos; - int return_pos; int flow_stack_size; int pass_stack_size; int node_count; int argument_count; - bool valid; }; Map<StringName, Function> functions; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 69503e631c..eb10c5e99f 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -3236,6 +3236,12 @@ void VisualScriptEditor::_member_option(int p_option) { } } +void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { +} + +void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) { +} + void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 80bbf142d9..72b5e09222 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -246,6 +246,9 @@ protected: static void _bind_methods(); public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void apply_code(); virtual Ref<Script> get_edited_script() const; virtual Vector<String> get_functions(); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 16de04e4cf..d5f9d21348 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -455,7 +455,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { break; } - if (cchar == '-' || (cchar >= '0' && cchar <= '9')) { + if (cchar >= '0' && cchar <= '9') { //a number String num; @@ -466,11 +466,6 @@ Error VisualScriptExpression::_get_token(Token &r_token) { #define READING_DONE 4 int reading = READING_INT; - if (cchar == '-') { - num += '-'; - cchar = GET_CHAR(); - } - CharType c = cchar; bool exp_sign = false; bool exp_beg = false; @@ -1146,7 +1141,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { expr_pos++; if (expr_pos == expression.size()) { //can happen.. - _set_error("Unexpected end of expression.."); + _set_error("Unexpected end of expression..."); return NULL; } } @@ -1166,7 +1161,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { } else { if (next_op < 1 || next_op >= (expression.size() - 1)) { - _set_error("Parser bug.."); + _set_error("Parser bug..."); ERR_FAIL_V(NULL); } @@ -1175,7 +1170,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { if (expression[next_op - 1].is_op) { - _set_error("Parser bug.."); + _set_error("Parser bug..."); ERR_FAIL_V(NULL); } diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index e0b4fde237..c5654a5a20 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -425,7 +425,7 @@ PropertyInfo VisualScriptOperator::get_input_value_port_info(int p_idx) const { } PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const { static const Variant::Type port_types[Variant::OP_MAX] = { - //comparation + //comparison Variant::BOOL, //OP_EQUAL, Variant::BOOL, //OP_NOT_EQUAL, Variant::BOOL, //OP_LESS, @@ -466,7 +466,7 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const { } static const char *op_names[] = { - //comparation + //comparison "Equal", //OP_EQUAL, "NotEqual", //OP_NOT_EQUAL, "Less", //OP_LESS, @@ -506,7 +506,7 @@ String VisualScriptOperator::get_caption() const { String VisualScriptOperator::get_text() const { static const wchar_t *op_names[] = { - //comparation + //comparison L"A = B", //OP_EQUAL, L"A \u2260 B", //OP_NOT_EQUAL, L"A < B", //OP_LESS, diff --git a/modules/webm/SCsub b/modules/webm/SCsub index 2f1a28a54c..33561da098 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -18,6 +18,10 @@ thirdparty_libsimplewebm_sources = [thirdparty_libsimplewebm_dir + file for file env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources) env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"]) +# upstream uses c++11 +if (not env_webm.msvc): + env_webm.Append(CCFLAGS="-std=c++11") + # also requires libogg, libvorbis and libopus if env['builtin_libogg']: env_webm.Append(CPPPATH=["#thirdparty/libogg"]) diff --git a/modules/webm/doc_classes/ResourceImporterWebm.xml b/modules/webm/doc_classes/ResourceImporterWebm.xml index 7c8e1a46b1..0cfab1baf0 100644 --- a/modules/webm/doc_classes/ResourceImporterWebm.xml +++ b/modules/webm/doc_classes/ResourceImporterWebm.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1-dev"> +<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index d52e2324a1..c02a7a8016 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.1-dev"> +<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index 0ebafa022d..b09c232b3c 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -340,7 +340,7 @@ if webm_simd_optimizations == False: env_libvpx.add_source_files(env.modules_sources, libvpx_sources) if webm_cpu_x86: - is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) + is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ) env_libvpx_mmx = env_libvpx.Clone() if cpu_bits == '32' and is_clang_or_gcc: diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index 1a36e05863..b36f1beacd 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -35,7 +35,6 @@ thirdparty_sources = [ "handshake.c", "header.c", "libwebsockets.c", - "minilex.c", "output.c", "pollfd.c", "service.c", @@ -69,11 +68,11 @@ else: env_lws.Append(CPPPATH=[thirdparty_dir]) wrapper_includes = ["#thirdparty/lws/mbedtls_wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] - env_lws.Append(CPPPATH=wrapper_includes) + env_lws.Prepend(CPPPATH=wrapper_includes) if env['builtin_mbedtls']: mbedtls_includes = "#thirdparty/mbedtls/include" - env_lws.Append(CPPPATH=[mbedtls_includes]) + env_lws.Prepend(CPPPATH=[mbedtls_includes]) if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 38fe520fc1..1405fa98b0 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 8801f37007..6c2fa23b53 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 93665e6428..e0b987b4d7 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -148,12 +148,14 @@ void EMWSPeer::close() { IP_Address EMWSPeer::get_connected_host() const { - return IP_Address(); + ERR_EXPLAIN("Not supported in HTML5 export"); + ERR_FAIL_V(IP_Address()); }; uint16_t EMWSPeer::get_connected_port() const { - return 1025; + ERR_EXPLAIN("Not supported in HTML5 export"); + ERR_FAIL_V(0); }; EMWSPeer::EMWSPeer() { diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index a50d1874ba..e06f725265 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index 60e9133225..3eb93e4152 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -58,6 +58,19 @@ PoolVector<String> EMWSServer::get_protocols() const { return out; } +IP_Address EMWSServer::get_peer_address(int p_peer_id) const { + + return IP_Address(); +} + +int EMWSServer::get_peer_port(int p_peer_id) const { + + return 0; +} + +void EMWSServer::disconnect_peer(int p_peer_id) { +} + EMWSServer::EMWSServer() { } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index 59f1d76346..9ec4ce72c8 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -46,6 +46,9 @@ public: bool is_listening() const; bool has_peer(int p_id) const; Ref<WebSocketPeer> get_peer(int p_id) const; + IP_Address get_peer_address(int p_peer_id) const; + int get_peer_port(int p_peer_id) const; + void disconnect_peer(int p_peer_id); virtual void poll(); virtual PoolVector<String> get_protocols() const; diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index 604b1886ad..2220c9adf2 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -31,6 +31,7 @@ #include "lws_client.h" #include "core/io/ip.h" +#include "core/io/stream_peer_ssl.h" Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { @@ -64,6 +65,9 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, info.uid = -1; //info.ws_ping_pong_interval = 5; info.user = _lws_ref; +#if defined(LWS_OPENSSL_SUPPORT) + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; +#endif context = lws_create_context(&info); if (context == NULL) { @@ -87,7 +91,14 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, i.host = hbuf; i.path = pbuf; i.port = p_port; - i.ssl_connection = p_ssl; + + if (p_ssl) { + i.ssl_connection = LCCSCF_USE_SSL; + if (!verify_ssl) + i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; + } else { + i.ssl_connection = 0; + } lws_client_connect_via_info(&i); return OK; @@ -104,6 +115,13 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; switch (reason) { + case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: { + PoolByteArray arr = StreamPeerSSL::get_project_cert_array(); + if (arr.size() > 0) + SSL_CTX_add_client_CA((SSL_CTX *)user, d2i_X509(NULL, &arr.read()[0], arr.size())); + else if (verify_ssl) + WARN_PRINTS("No CA cert specified in project settings, SSL will not work"); + } break; case LWS_CALLBACK_CLIENT_ESTABLISHED: peer->set_wsi(wsi); diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h index 2e082175df..8850683cb5 100644 --- a/modules/websocket/lws_client.h +++ b/modules/websocket/lws_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h index ac0c340aa9..a850a545d3 100644 --- a/modules/websocket/lws_helper.h +++ b/modules/websocket/lws_helper.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -108,7 +108,7 @@ static bool _lws_poll(struct lws_context *context, _LWSRef *ref) { static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { /* the input strings might go away after this call, * we need to copy them. Will clear them when - * detroying the context */ + * destroying the context */ int i; int len = p_names.size(); size_t data_size = sizeof(struct LWSPeer::PeerData); diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index fdaa79f9d4..3855a39aef 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -32,6 +32,14 @@ #include "lws_peer.h" #include "core/io/ip.h" +// Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows +#if !defined(WINDOWS_ENABLED) +#include <netinet/in.h> +#include <sys/socket.h> +#endif + +#include "drivers/unix/socket_helpers.h" + void LWSPeer::set_wsi(struct lws *p_wsi) { wsi = p_wsi; }; @@ -178,12 +186,44 @@ void LWSPeer::close() { IP_Address LWSPeer::get_connected_host() const { - return IP_Address(); + ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); + + IP_Address ip; + int port = 0; + + socklen_t len = 0; + struct sockaddr_storage addr; + + int fd = lws_get_socket_fd(wsi); + ERR_FAIL_COND_V(fd == -1, IP_Address()); + + int ret = getpeername(fd, (struct sockaddr *)&addr, &len); + ERR_FAIL_COND_V(ret != 0, IP_Address()); + + _set_ip_addr_port(ip, port, &addr); + + return ip; }; uint16_t LWSPeer::get_connected_port() const { - return 1025; + ERR_FAIL_COND_V(!is_connected_to_host(), 0); + + IP_Address ip; + int port = 0; + + socklen_t len = 0; + struct sockaddr_storage addr; + + int fd = lws_get_socket_fd(wsi); + ERR_FAIL_COND_V(fd == -1, 0); + + int ret = getpeername(fd, (struct sockaddr *)&addr, &len); + ERR_FAIL_COND_V(ret != 0, 0); + + _set_ip_addr_port(ip, port, &addr); + + return port; }; LWSPeer::LWSPeer() { diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h index 0a62b65d24..e96b38b168 100644 --- a/modules/websocket/lws_peer.h +++ b/modules/websocket/lws_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp index 8a47ba557d..8d13dc7a98 100644 --- a/modules/websocket/lws_server.cpp +++ b/modules/websocket/lws_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -164,6 +164,24 @@ Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const { return _peer_map[p_id]; } +IP_Address LWSServer::get_peer_address(int p_peer_id) const { + ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address()); + + return _peer_map[p_peer_id]->get_connected_host(); +} + +int LWSServer::get_peer_port(int p_peer_id) const { + ERR_FAIL_COND_V(!has_peer(p_peer_id), 0); + + return _peer_map[p_peer_id]->get_connected_port(); +} + +void LWSServer::disconnect_peer(int p_peer_id) { + ERR_FAIL_COND(!has_peer(p_peer_id)); + + get_peer(p_peer_id)->close(); +} + LWSServer::LWSServer() { context = NULL; _lws_ref = NULL; diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h index 5f7ac4850a..9e3fb9b775 100644 --- a/modules/websocket/lws_server.h +++ b/modules/websocket/lws_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -52,6 +52,9 @@ public: bool is_listening() const; bool has_peer(int p_id) const; Ref<WebSocketPeer> get_peer(int p_id) const; + IP_Address get_peer_address(int p_peer_id) const; + int get_peer_port(int p_peer_id) const; + void disconnect_peer(int p_peer_id); virtual void poll() { _lws_poll(); } LWSServer(); diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 39d03ff1f0..721f3cc330 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h index 010d88789b..89ce93e286 100644 --- a/modules/websocket/register_types.h +++ b/modules/websocket/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index f92a386988..7701163085 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -32,6 +32,8 @@ GDCINULL(WebSocketClient); WebSocketClient::WebSocketClient() { + + verify_ssl = true; } WebSocketClient::~WebSocketClient() { @@ -72,6 +74,16 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco return connect_to_host(host, path, port, ssl, p_protocols); } +void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) { + + verify_ssl = p_verify_ssl; +} + +bool WebSocketClient::is_verify_ssl_enabled() const { + + return verify_ssl; +} + bool WebSocketClient::is_server() const { return false; @@ -116,6 +128,10 @@ void WebSocketClient::_on_error() { void WebSocketClient::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host); + ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled); + ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled); + + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 0e87825222..6165f37d40 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -41,12 +41,16 @@ class WebSocketClient : public WebSocketMultiplayerPeer { protected: Ref<WebSocketPeer> _peer; + bool verify_ssl; static void _bind_methods(); public: Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + void set_verify_ssl_enabled(bool p_verify_ssl); + bool is_verify_ssl_enabled() const; + virtual void poll() = 0; virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; virtual void disconnect_from_host() = 0; diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h index b5c2159806..d27fb4d778 100644 --- a/modules/websocket/websocket_macros.h +++ b/modules/websocket/websocket_macros.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp index 8cd4dff38b..b948c439df 100644 --- a/modules/websocket/websocket_multiplayer.cpp +++ b/modules/websocket/websocket_multiplayer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h index e8e795e97f..8edfc5296e 100644 --- a/modules/websocket/websocket_multiplayer.h +++ b/modules/websocket/websocket_multiplayer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index a6fbb4481b..61f783e377 100644 --- a/modules/websocket/websocket_peer.cpp +++ b/modules/websocket/websocket_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -43,6 +43,8 @@ void WebSocketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host); ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet); ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close); + ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host); + ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port); BIND_ENUM_CONSTANT(WRITE_MODE_TEXT); BIND_ENUM_CONSTANT(WRITE_MODE_BINARY); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index f4d8ce3e38..ad451e9cc7 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index ba77019f55..2693b26e47 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -44,6 +44,9 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop); ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); + ClassDB::bind_method(D_METHOD("get_peer_address"), &WebSocketServer::get_peer_address); + ClassDB::bind_method(D_METHOD("get_peer_port"), &WebSocketServer::get_peer_port); + ClassDB::bind_method(D_METHOD("disconnect_peer"), &WebSocketServer::disconnect_peer); ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index db188811fd..64935f8a58 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -52,6 +52,10 @@ public: virtual bool is_server() const; ConnectionStatus get_connection_status() const; + virtual IP_Address get_peer_address(int p_peer_id) const = 0; + virtual int get_peer_port(int p_peer_id) const = 0; + virtual void disconnect_peer(int p_peer_id) = 0; + void _on_peer_packet(int32_t p_peer_id); void _on_connect(int32_t p_peer_id, String p_protocol); void _on_disconnect(int32_t p_peer_id); |