diff options
Diffstat (limited to 'modules')
35 files changed, 672 insertions, 275 deletions
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index bc60c9cb6b..cbf30c8a2e 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -77,7 +77,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo PhysicsDirectSpaceState::ShapeResult &result = m_results[count]; - result.shape = convexResult.m_localShapeInfo->m_shapePart; + result.shape = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID result.rid = gObj->get_self(); result.collider_id = gObj->get_instance_id(); result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id); @@ -122,7 +122,7 @@ bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) btScalar GodotClosestConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace) { btScalar res = btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); - m_shapePart = convexResult.m_localShapeInfo->m_shapePart; + m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID return res; } @@ -242,3 +242,21 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp return cp.getDistance(); } + +void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth) { + + if (depth < 0) { + // Has penetration + if (m_most_penetrated_distance > depth) { + + 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; + } + } +} diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 68dff5b12a..ba5142676b 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -88,7 +88,7 @@ public: struct GodotClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: const Set<RID> *m_exclude; - int m_shapePart; + int m_shapeId; GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude) : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld), m_exclude(p_exclude) {} @@ -149,4 +149,31 @@ public: virtual btScalar addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1); }; +struct GodotDeepPenetrationContactResultCallback : public btManifoldResult { + btVector3 m_pointNormalWorld; + 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) {} + + void reset() { + m_pointCollisionObject = NULL; + m_most_penetrated_distance = 1e20; + } + + bool hasHit() { + return m_pointCollisionObject; + } + + virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorld, btScalar depth); +}; #endif // GODOT_RESULT_CALLBACKS_H diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 9df01aee3e..853906063b 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -115,12 +115,13 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); - btConvexShape *btConvex = dynamic_cast<btConvexShape *>(shape->create_bt_shape()); - if (!btConvex) { - bulletdelete(btConvex); + btCollisionShape *btShape = shape->create_bt_shape(); + if (!btShape->isConvex()) { + bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return 0; } + btConvexShape *btConvex = static_cast<btConvexShape *>(btShape); btVector3 scale_with_margin; G_TO_B(p_xform.basis.get_scale(), scale_with_margin); @@ -147,12 +148,13 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask, ShapeRestInfo *r_info) { ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); - btConvexShape *bt_convex_shape = dynamic_cast<btConvexShape *>(shape->create_bt_shape()); - if (!bt_convex_shape) { - bulletdelete(bt_convex_shape); + btCollisionShape *btShape = shape->create_bt_shape(); + if (!btShape->isConvex()) { + bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return 0; } + btConvexShape *bt_convex_shape = static_cast<btConvexShape *>(btShape); btVector3 bt_motion; G_TO_B(p_motion, bt_motion); @@ -174,16 +176,18 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult, 0.002); if (btResult.hasHit()) { - if (btCollisionObject::CO_RIGID_BODY == btResult.m_hitCollisionObject->getInternalType()) { - B_TO_G(static_cast<const btRigidBody *>(btResult.m_hitCollisionObject)->getVelocityInLocalPoint(btResult.m_hitPointWorld), r_info->linear_velocity); - } - CollisionObjectBullet *collision_object = static_cast<CollisionObjectBullet *>(btResult.m_hitCollisionObject->getUserPointer()); p_closest_safe = p_closest_unsafe = btResult.m_closestHitFraction; - B_TO_G(btResult.m_hitPointWorld, r_info->point); - B_TO_G(btResult.m_hitNormalWorld, r_info->normal); - r_info->rid = collision_object->get_self(); - r_info->collider_id = collision_object->get_instance_id(); - r_info->shape = btResult.m_shapePart; + if (r_info) { + if (btCollisionObject::CO_RIGID_BODY == btResult.m_hitCollisionObject->getInternalType()) { + B_TO_G(static_cast<const btRigidBody *>(btResult.m_hitCollisionObject)->getVelocityInLocalPoint(btResult.m_hitPointWorld), r_info->linear_velocity); + } + CollisionObjectBullet *collision_object = static_cast<CollisionObjectBullet *>(btResult.m_hitCollisionObject->getUserPointer()); + B_TO_G(btResult.m_hitPointWorld, r_info->point); + B_TO_G(btResult.m_hitNormalWorld, r_info->normal); + r_info->rid = collision_object->get_self(); + r_info->collider_id = collision_object->get_instance_id(); + r_info->shape = btResult.m_shapeId; + } } bulletdelete(bt_convex_shape); @@ -197,12 +201,13 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform & ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); - btConvexShape *btConvex = dynamic_cast<btConvexShape *>(shape->create_bt_shape()); - if (!btConvex) { - bulletdelete(btConvex); + btCollisionShape *btShape = shape->create_bt_shape(); + if (!btShape->isConvex()) { + bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return 0; } + btConvexShape *btConvex = static_cast<btConvexShape *>(btShape); btVector3 scale_with_margin; G_TO_B(p_shape_xform.basis.get_scale(), scale_with_margin); @@ -231,12 +236,13 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); - btConvexShape *btConvex = dynamic_cast<btConvexShape *>(shape->create_bt_shape()); - if (!btConvex) { - bulletdelete(btConvex); + btCollisionShape *btShape = shape->create_bt_shape(); + if (!btShape->isConvex()) { + bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return 0; } + btConvexShape *btConvex = static_cast<btConvexShape *>(btShape); btVector3 scale_with_margin; G_TO_B(p_shape_xform.basis.get_scale() + Vector3(p_margin, p_margin, p_margin), scale_with_margin); @@ -777,7 +783,8 @@ void SpaceBullet::check_body_collision() { void SpaceBullet::update_gravity() { btVector3 btGravity; G_TO_B(gravityDirection * gravityMagnitude, btGravity); - dynamicsWorld->setGravity(btGravity); + //dynamicsWorld->setGravity(btGravity); + dynamicsWorld->setGravity(btVector3(0, 0, 0)); if (soft_body_world_info) { soft_body_world_info->m_gravity = btGravity; } @@ -877,11 +884,11 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f continue; } - btConvexShape *convex_shape_test(dynamic_cast<btConvexShape *>(p_body->get_bt_shape(shIndex))); - if (!convex_shape_test) { + if (!p_body->get_bt_shape(shIndex)->isConvex()) { // Skip no convex shape continue; } + btConvexShape *convex_shape_test(static_cast<btConvexShape *>(p_body->get_bt_shape(shIndex))); btTransform shape_world_from; G_TO_B(p_body->get_shape_transform(shIndex), shape_world_from); @@ -910,26 +917,26 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f { /// Phase three - Recover + contact test with margin - RecoverResult recover_result; + RecoverResult r_recover_result; - hasPenetration = recover_from_penetration(p_body, body_safe_position, recovered_motion, &recover_result); + hasPenetration = recover_from_penetration(p_body, body_safe_position, recovered_motion, &r_recover_result); if (r_result) { B_TO_G(recovered_motion + recover_initial_position, r_result->motion); if (hasPenetration) { - const btRigidBody *btRigid = static_cast<const btRigidBody *>(recover_result.other_collision_object); + const btRigidBody *btRigid = static_cast<const btRigidBody *>(r_recover_result.other_collision_object); CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer()); r_result->remainder = p_motion - r_result->motion; // is the remaining movements - B_TO_G(recover_result.pointWorld, r_result->collision_point); - B_TO_G(recover_result.pointNormalWorld, r_result->collision_normal); - B_TO_G(btRigid->getVelocityInLocalPoint(recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot + B_TO_G(r_recover_result.pointWorld, r_result->collision_point); + B_TO_G(r_recover_result.pointNormalWorld, r_result->collision_normal); + B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot r_result->collider = collisionObject->get_self(); r_result->collider_id = collisionObject->get_instance_id(); - r_result->collider_shape = recover_result.other_compound_shape_index; - r_result->collision_local_shape = recover_result.local_shape_most_recovered; + r_result->collider_shape = r_recover_result.other_compound_shape_index; + r_result->collision_local_shape = r_recover_result.local_shape_most_recovered; //{ /// Add manifold point to manage collisions // btPersistentManifold* manifold = dynamicsWorld->getDispatcher()->getNewManifold(p_body->getBtBody(), btRigid); @@ -995,7 +1002,7 @@ public: } }; -bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btVector3 &out_recover_position, RecoverResult *recover_result) { +bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btVector3 &r_recover_position, RecoverResult *r_recover_result) { RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask()); @@ -1005,9 +1012,6 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran // Broad phase support btVector3 minAabb, maxAabb; - // GJK support - btGjkPairDetector::ClosestPointInput gjk_input; - bool penetration = false; // For each shape @@ -1022,7 +1026,7 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran body_shape_position = p_body_position * kin_shape.transform; body_shape_position_recovered = body_shape_position; - body_shape_position_recovered.getOrigin() += out_recover_position; + body_shape_position_recovered.getOrigin() += r_recover_position; kin_shape.shape->getAabb(body_shape_position_recovered, minAabb, maxAabb); dynamicsWorld->getBroadphase()->aabbTest(minAabb, maxAabb, recover_broad_result); @@ -1032,66 +1036,33 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object())) continue; - if (otherObject->getCollisionShape()->isCompound()) { /// Execute GJK test against all shapes + if (otherObject->getCollisionShape()->isCompound()) { // Each convex shape btCompoundShape *cs = static_cast<btCompoundShape *>(otherObject->getCollisionShape()); for (int x = cs->getNumChildShapes() - 1; 0 <= x; --x) { - if (!cs->getChildShape(x)->isConvex()) - continue; + if (cs->getChildShape(x)->isConvex()) { + if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(cs->getChildShape(x)), otherObject, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), r_recover_position, r_recover_result)) { - // Initialize GJK input - gjk_input.m_transformA = body_shape_position; - gjk_input.m_transformA.getOrigin() += out_recover_position; - gjk_input.m_transformB = otherObject->getWorldTransform() * cs->getChildTransform(x); + penetration = true; + } + } else { + if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(x), p_body->get_bt_collision_object(), otherObject, kinIndex, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), r_recover_position, r_recover_result)) { - // Perform GJK test - btPointCollector result; - btGjkPairDetector gjk_pair_detector(kin_shape.shape, static_cast<const btConvexShape *>(cs->getChildShape(x)), gjk_simplex_solver, gjk_epa_pen_solver); - gjk_pair_detector.getClosestPoints(gjk_input, result, 0); - if (0 > result.m_distance) { - // Has penetration - out_recover_position += result.m_normalOnBInWorld * (result.m_distance * -1); - penetration = true; - - if (recover_result) { - - recover_result->hasPenetration = true; - recover_result->other_collision_object = otherObject; - recover_result->other_compound_shape_index = x; - recover_result->penetration_distance = result.m_distance; - recover_result->pointNormalWorld = result.m_normalOnBInWorld; - recover_result->pointWorld = result.m_pointInWorld; + penetration = true; } } } - } else if (otherObject->getCollisionShape()->isConvex()) { /// Execute GJK test against object shape + if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(otherObject->getCollisionShape()), otherObject, 0, body_shape_position, otherObject->getWorldTransform(), r_recover_position, r_recover_result)) { - // Initialize GJK input - gjk_input.m_transformA = body_shape_position; - gjk_input.m_transformA.getOrigin() += out_recover_position; - gjk_input.m_transformB = otherObject->getWorldTransform(); - - // Perform GJK test - btPointCollector result; - btGjkPairDetector gjk_pair_detector(kin_shape.shape, static_cast<const btConvexShape *>(otherObject->getCollisionShape()), gjk_simplex_solver, gjk_epa_pen_solver); - gjk_pair_detector.getClosestPoints(gjk_input, result, 0); - if (0 > result.m_distance) { - // Has penetration - out_recover_position += result.m_normalOnBInWorld * (result.m_distance * -1); penetration = true; + } + } else { + if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, body_shape_position, otherObject->getWorldTransform(), r_recover_position, r_recover_result)) { - if (recover_result) { - - recover_result->hasPenetration = true; - recover_result->other_collision_object = otherObject; - recover_result->other_compound_shape_index = 0; - recover_result->penetration_distance = result.m_distance; - recover_result->pointNormalWorld = result.m_normalOnBInWorld; - recover_result->pointWorld = result.m_pointInWorld; - } + penetration = true; } } } @@ -1099,3 +1070,70 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran return penetration; } + +bool SpaceBullet::RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btVector3 &r_recover_position, RecoverResult *r_recover_result) { + + // Initialize GJK input + btGjkPairDetector::ClosestPointInput gjk_input; + gjk_input.m_transformA = p_transformA; + gjk_input.m_transformA.getOrigin() += r_recover_position; + gjk_input.m_transformB = p_transformB; + + // Perform GJK test + btPointCollector result; + btGjkPairDetector gjk_pair_detector(p_shapeA, p_shapeB, gjk_simplex_solver, gjk_epa_pen_solver); + gjk_pair_detector.getClosestPoints(gjk_input, result, 0); + if (0 > result.m_distance) { + // Has penetration + r_recover_position += result.m_normalOnBInWorld * (result.m_distance * -1); + + if (r_recover_result) { + + r_recover_result->hasPenetration = true; + r_recover_result->other_collision_object = p_objectB; + r_recover_result->other_compound_shape_index = p_shapeId_B; + r_recover_result->penetration_distance = result.m_distance; + r_recover_result->pointNormalWorld = result.m_normalOnBInWorld; + r_recover_result->pointWorld = result.m_pointInWorld; + } + return true; + } + return false; +} + +bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btVector3 &r_recover_position, RecoverResult *r_recover_result) { + + /// Contact test + + btTransform p_recovered_transformA(p_transformA); + p_recovered_transformA.getOrigin() += r_recover_position; + + btCollisionObjectWrapper obA(NULL, p_shapeA, p_objectA, p_recovered_transformA, -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); + if (algorithm) { + GodotDeepPenetrationContactResultCallback contactPointResult(&obA, &obB); + //discrete collision detection query + algorithm->processCollision(&obA, &obB, dynamicsWorld->getDispatchInfo(), &contactPointResult); + + algorithm->~btCollisionAlgorithm(); + dispatcher->freeCollisionAlgorithm(algorithm); + + if (contactPointResult.hasHit()) { + r_recover_position += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * -1); + + if (r_recover_result) { + + r_recover_result->hasPenetration = true; + r_recover_result->other_collision_object = p_objectB; + r_recover_result->other_compound_shape_index = p_shapeId_B; + r_recover_result->penetration_distance = contactPointResult.m_penetration_distance; + r_recover_result->pointNormalWorld = contactPointResult.m_pointNormalWorld; + r_recover_result->pointWorld = contactPointResult.m_pointWorld; + } + return true; + } + } + return false; +} diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index d9206f8046..9acac9a7d6 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -189,6 +189,12 @@ private: : hasPenetration(false) {} }; - bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_from, btVector3 &out_recover_position, RecoverResult *recover_result = NULL); + bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_from, btVector3 &r_recover_position, RecoverResult *r_recover_result = NULL); + /// This is an API that recover a kinematic object from penetration + /// This allow only Convex Convex test and it always use GJK algorithm, With this API we don't benefit of Bullet special accelerated functions + bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btVector3 &r_recover_position, RecoverResult *r_recover_result); + /// This is an API that recover a kinematic object from penetration + /// Using this we leave Bullet to select the best algorithm, For example GJK in case we have Convex Convex, or a Bullet accelerated algorithm + bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btVector3 &r_recover_position, RecoverResult *r_recover_result); }; #endif diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index c50886ad3c..1e18ec0d18 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -505,7 +505,7 @@ uint32_t NetworkedMultiplayerENet::_gen_unique_id() const { hash = hash_djb2_one_32( (uint32_t)OS::get_singleton()->get_unix_time(), hash); hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_data_dir().hash64(), hash); + (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 ); diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index dc7d23bbd7..941df41694 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -129,7 +129,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f PoolVector<uint8_t>::Read r = img->get_data().read(); int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps() ? -1 : 0); - int mmc = p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0; + int mmc = 1 + (p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0); PoolVector<uint8_t> dst_data; dst_data.resize(target_size); @@ -155,7 +155,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f print_line("begin encoding, format: " + Image::get_format_name(etc_format)); uint64_t t = OS::get_singleton()->get_ticks_msec(); - for (int i = 0; i < mmc + 1; i++) { + for (int i = 0; i < mmc; i++) { // convert source image to internal etc2comp format (which is equivalent to Image::FORMAT_RGBAF) // NOTE: We can alternatively add a case to Image::convert to handle Image::FORMAT_RGBAF conversion. int mipmap_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0; @@ -163,9 +163,9 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f const uint8_t *src = &r[mipmap_ofs]; Etc::ColorFloatRGBA *src_rgba_f = new Etc::ColorFloatRGBA[mipmap_w * mipmap_h]; - for (int i = 0; i < mipmap_w * mipmap_h; i++) { - int si = i * 4; // RGBA8 - src_rgba_f[i] = Etc::ColorFloatRGBA::ConvertFromRGBA8(src[si], src[si + 1], src[si + 2], src[si + 3]); + for (int j = 0; j < mipmap_w * mipmap_h; j++) { + int si = j * 4; // RGBA8 + src_rgba_f[j] = Etc::ColorFloatRGBA::ConvertFromRGBA8(src[si], src[si + 1], src[si + 2], src[si + 3]); } unsigned char *etc_data = NULL; @@ -173,15 +173,17 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f unsigned int extended_width = 0, extended_height = 0; Etc::Encode((float *)src_rgba_f, mipmap_w, mipmap_h, etc2comp_etc_format, error_metric, effort, num_cpus, num_cpus, &etc_data, &etc_data_len, &extended_width, &extended_height, &encoding_time); + CRASH_COND(wofs + etc_data_len > target_size); memcpy(&w[wofs], etc_data, etc_data_len); wofs += etc_data_len; delete[] etc_data; delete[] src_rgba_f; } + print_line("time encoding: " + rtos(OS::get_singleton()->get_ticks_msec() - t)); - p_img->create(imgw, imgh, mmc > 1 ? true : false, etc_format, dst_data); + p_img->create(imgw, imgh, p_img->has_mipmaps(), etc_format, dst_data); } static void _compress_etc1(Image *p_img, float p_lossy_quality) { diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 66b8d5cbdd..485bf4b9df 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -49,19 +49,6 @@ def _build_gdnative_api_struct_header(api): 'extern "C" {', '#endif', '', - 'typedef struct godot_gdnative_api_version {', - '\tunsigned int major;', - '\tunsigned int minor;', - '} godot_gdnative_api_version;', - '', - 'typedef struct godot_gdnative_api_struct godot_gdnative_api_struct;', - '', - 'struct godot_gdnative_api_struct {', - '\tunsigned int type;', - '\tgodot_gdnative_api_version version;', - '\tconst godot_gdnative_api_struct *next;', - '};', - '', 'enum GDNATIVE_API_TYPES {', '\tGDNATIVE_' + api['core']['type'] + ',' ] diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 44d6dffc85..de118043ca 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -64,7 +64,6 @@ void GDNativeLibrary::_bind_methods() { 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); - ClassDB::bind_method(D_METHOD("is_current_library_statically_linked"), &GDNativeLibrary::is_current_library_statically_linked); ClassDB::bind_method(D_METHOD("should_load_once"), &GDNativeLibrary::should_load_once); ClassDB::bind_method(D_METHOD("is_singleton"), &GDNativeLibrary::is_singleton); @@ -109,6 +108,9 @@ Ref<GDNativeLibrary> GDNative::get_library() { return library; } +extern "C" void _gdnative_report_version_mismatch(const godot_object *p_library, const char *p_ext, godot_gdnative_api_version p_want, godot_gdnative_api_version p_have); +extern "C" void _gdnative_report_loading_error(const godot_object *p_library, const char *p_what); + bool GDNative::initialize() { if (library.is_null()) { ERR_PRINT("No library set, can't initialize GDNative object"); @@ -116,12 +118,12 @@ bool GDNative::initialize() { } String lib_path = library->get_current_library_path(); - if (lib_path.empty() && !library->is_current_library_statically_linked()) { + if (lib_path.empty()) { ERR_PRINT("No library set for this platform"); return false; } #ifdef IPHONE_ENABLED - String path = lib_path.replace("res://", "dylibs/"); + String path = ""; #else String path = ProjectSettings::get_singleton()->globalize_path(lib_path); #endif @@ -137,7 +139,7 @@ bool GDNative::initialize() { } Error err = OS::get_singleton()->open_dynamic_library(path, native_handle); - if (err != OK && !library->is_current_library_statically_linked()) { + if (err != OK) { return false; } @@ -146,13 +148,12 @@ bool GDNative::initialize() { // we cheat here a little bit. you saw nothing initialized = true; - err = get_symbol(library->get_symbol_prefix() + init_symbol, library_init); + err = get_symbol(library->get_symbol_prefix() + init_symbol, library_init, false); initialized = false; if (err || !library_init) { - if (!library->is_current_library_statically_linked()) - OS::get_singleton()->close_dynamic_library(native_handle); + OS::get_singleton()->close_dynamic_library(native_handle); native_handle = NULL; ERR_PRINT("Failed to obtain godot_gdnative_init symbol"); return false; @@ -168,6 +169,8 @@ bool GDNative::initialize() { options.core_api_hash = ClassDB::get_api_hash(ClassDB::API_CORE); options.editor_api_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); options.no_api_hash = ClassDB::get_api_hash(ClassDB::API_NONE); + options.report_version_mismatch = &_gdnative_report_version_mismatch; + options.report_loading_error = &_gdnative_report_loading_error; options.gd_native_library = (godot_object *)(get_library().ptr()); options.active_library_path = (godot_string *)&path; @@ -277,7 +280,7 @@ Variant GDNative::call_native(StringName p_native_call_type, StringName p_proced return *(Variant *)&result; } -Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle) { +Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional) { if (!initialized) { ERR_PRINT("No valid library handle, can't get symbol from GDNative object"); @@ -288,7 +291,7 @@ Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle) { native_handle, p_procedure_name, r_handle, - true); + p_optional); return result; } @@ -369,40 +372,8 @@ RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_or } } - bool is_statically_linked = false; - { - - List<String> static_linking_keys; - config->get_section_keys("static_linking", &static_linking_keys); - - for (List<String>::Element *E = static_linking_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; - } - - is_statically_linked = config->get_value("static_linking", key); - break; - } - } - lib->current_library_path = entry_lib_path; lib->current_dependencies = dependency_paths; - lib->current_library_statically_linked = is_statically_linked; return lib; } diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index 061dff9267..bb260bdd1b 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -55,7 +55,6 @@ class GDNativeLibrary : public Resource { String current_library_path; Vector<String> current_dependencies; - bool current_library_statically_linked; bool singleton; bool load_once; @@ -75,9 +74,6 @@ public: _FORCE_INLINE_ Vector<String> get_current_dependencies() const { return current_dependencies; } - _FORCE_INLINE_ bool is_current_library_statically_linked() const { - return current_library_statically_linked; - } // things that are a property of the library itself, not platform specific _FORCE_INLINE_ bool should_load_once() const { @@ -103,12 +99,10 @@ public: static void _bind_methods(); }; -typedef godot_variant (*native_call_cb)(void *, godot_array *); - struct GDNativeCallRegistry { static GDNativeCallRegistry *singleton; - inline GDNativeCallRegistry *get_singleton() { + inline static GDNativeCallRegistry *get_singleton() { return singleton; } @@ -147,7 +141,7 @@ public: Variant call_native(StringName p_native_call_type, StringName p_procedure_name, Array p_arguments = Array()); - Error get_symbol(StringName p_procedure_name, void *&r_handle); + Error get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional = true); }; class GDNativeLibraryResourceLoader : public ResourceFormatLoader { diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index e0d9514985..8351c43574 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -302,6 +302,17 @@ void GDAPI godot_array_sort_custom(godot_array *p_self, godot_object *p_obj, con self->sort_custom((Object *)p_obj, *func); } +godot_int GDAPI godot_array_bsearch(godot_array *p_self, const godot_variant *p_value, const godot_bool p_before) { + Array *self = (Array *)p_self; + return self->bsearch((const Variant *)p_value, p_before); +} + +godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_variant *p_value, godot_object *p_obj, const godot_string *p_func, const godot_bool p_before) { + Array *self = (Array *)p_self; + const String *func = (const String *)p_func; + return self->bsearch_custom((const Variant *)p_value, (Object *)p_obj, *func, p_before); +} + void GDAPI godot_array_destroy(godot_array *p_self) { ((Array *)p_self)->~Array(); } diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp index 6dfa7ec20b..92a88e354b 100644 --- a/modules/gdnative/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative/gdnative.cpp @@ -36,6 +36,8 @@ #include "os/os.h" #include "variant.h" +#include "modules/gdnative/gdnative.h" + #ifdef __cplusplus extern "C" { #endif @@ -113,6 +115,10 @@ godot_dictionary GDAPI godot_get_global_constants() { } // System functions +void GDAPI godot_register_native_call_type(const char *p_call_type, native_call_cb p_callback) { + GDNativeCallRegistry::get_singleton()->register_native_call_type(StringName(p_call_type), p_callback); +} + void GDAPI *godot_alloc(int p_bytes) { return memalloc(p_bytes); } @@ -137,6 +143,32 @@ void GDAPI godot_print(const godot_string *p_message) { print_line(*(String *)p_message); } +void _gdnative_report_version_mismatch(const godot_object *p_library, const char *p_ext, godot_gdnative_api_version p_want, godot_gdnative_api_version p_have) { + String message = "Error loading GDNative file "; + GDNativeLibrary *library = (GDNativeLibrary *)p_library; + + message += library->get_current_library_path() + ": Extension \"" + p_ext + "\" can't be loaded.\n"; + + Dictionary versions; + versions["have_major"] = p_have.major; + versions["have_minor"] = p_have.minor; + versions["want_major"] = p_want.major; + versions["want_minor"] = p_want.minor; + + message += String("Got version {have_major}.{have_minor} but needs {want_major}.{want_minor}!").format(versions); + + _err_print_error("gdnative_init", library->get_current_library_path().utf8().ptr(), 0, message.utf8().ptr()); +} + +void _gdnative_report_loading_error(const godot_object *p_library, const char *p_what) { + String message = "Error loading GDNative file "; + GDNativeLibrary *library = (GDNativeLibrary *)p_library; + + message += library->get_current_library_path() + ": " + p_what; + + _err_print_error("gdnative_init", library->get_current_library_path().utf8().ptr(), 0, message.utf8().ptr()); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 781b8754bd..67a037736c 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -89,11 +89,6 @@ wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, cons return self->operator[](p_idx); } -const char GDAPI *godot_string_c_str(const godot_string *p_self) { - const String *self = (const String *)p_self; - return self->utf8().get_data(); -} - const wchar_t GDAPI *godot_string_unicode_str(const godot_string *p_self) { const String *self = (const String *)p_self; return self->c_str(); diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 877c65dfb9..0438a196cf 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -2680,6 +2680,26 @@ ] }, { + "name": "godot_array_bsearch", + "return_type": "godot_int", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"], + ["const godot_bool", "p_before"] + ] + }, + { + "name": "godot_array_bsearch_custom", + "return_type": "godot_int", + "arguments": [ + ["godot_array *", "p_self"], + ["const godot_variant *", "p_value"], + ["godot_object *", "p_obj"], + ["const godot_string *", "p_func"], + ["const godot_bool", "p_before"] + ] + }, + { "name": "godot_array_destroy", "return_type": "void", "arguments": [ @@ -4362,13 +4382,6 @@ ] }, { - "name": "godot_string_c_str", - "return_type": "const char *", - "arguments": [ - ["const godot_string *", "p_self"] - ] - }, - { "name": "godot_string_unicode_str", "return_type": "const wchar_t *", "arguments": [ @@ -5556,6 +5569,14 @@ ] }, { + "name": "godot_register_native_call_type", + "return_type": "void", + "arguments": [ + ["const char *", "call_type"], + ["native_call_cb", "p_callback"] + ] + }, + { "name": "godot_alloc", "return_type": "void *", "arguments": [ diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 01ae61e280..484ffd10ba 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -124,6 +124,10 @@ void GDAPI godot_array_sort(godot_array *p_self); void GDAPI godot_array_sort_custom(godot_array *p_self, godot_object *p_obj, const godot_string *p_func); +godot_int GDAPI godot_array_bsearch(godot_array *p_self, const godot_variant *p_value, const godot_bool p_before); + +godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_variant *p_value, godot_object *p_obj, const godot_string *p_func, const godot_bool p_before); + void GDAPI godot_array_destroy(godot_array *p_self); #ifdef __cplusplus diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h index 38a42ab658..6e69d43469 100644 --- a/modules/gdnative/include/gdnative/gdnative.h +++ b/modules/gdnative/include/gdnative/gdnative.h @@ -53,7 +53,7 @@ extern "C" { // This is for libraries *using* the header, NOT GODOT EXPOSING STUFF!! #ifdef _WIN32 -#define GDN_EXPORT +#define GDN_EXPORT __declspec(dllexport) #else #define GDN_EXPORT #endif @@ -229,13 +229,28 @@ void GDAPI godot_method_bind_ptrcall(godot_method_bind *p_method_bind, godot_obj godot_variant GDAPI godot_method_bind_call(godot_method_bind *p_method_bind, godot_object *p_instance, const godot_variant **p_args, const int p_arg_count, godot_variant_call_error *p_call_error); ////// Script API -struct godot_gdnative_api_struct; // Forward declaration +typedef struct godot_gdnative_api_version { + unsigned int major; + unsigned int minor; +} godot_gdnative_api_version; + +typedef struct godot_gdnative_api_struct godot_gdnative_api_struct; + +struct godot_gdnative_api_struct { + unsigned int type; + godot_gdnative_api_version version; + const godot_gdnative_api_struct *next; +}; + +#define GDNATIVE_VERSION_COMPATIBLE(want, have) (want.major == have.major && want.minor <= have.minor) typedef struct { godot_bool in_editor; uint64_t core_api_hash; uint64_t editor_api_hash; uint64_t no_api_hash; + void (*report_version_mismatch)(const godot_object *p_library, const char *p_what, godot_gdnative_api_version p_want, godot_gdnative_api_version p_have); + void (*report_loading_error)(const godot_object *p_library, const char *p_what); godot_object *gd_native_library; // pointer to GDNativeLibrary that is being initialized const struct godot_gdnative_core_api_struct *api_struct; const godot_string *active_library_path; @@ -259,6 +274,9 @@ typedef godot_variant (*godot_gdnative_procedure_fn)(godot_array *); ////// System Functions +typedef godot_variant (*native_call_cb)(void *, godot_array *); +void GDAPI godot_register_native_call_type(const char *p_call_type, native_call_cb p_callback); + //using these will help Godot track how much memory is in use in debug mode void GDAPI *godot_alloc(int p_bytes); void GDAPI *godot_realloc(void *p_ptr, int p_bytes); diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h index cca3eb2672..10358ceade 100644 --- a/modules/gdnative/include/gdnative/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -68,7 +68,6 @@ void GDAPI godot_string_get_data(const godot_string *p_self, char *p_dest, int * wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx); wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx); -const char GDAPI *godot_string_c_str(const godot_string *p_self); const wchar_t GDAPI *godot_string_unicode_str(const godot_string *p_self); godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b); diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 8af643df50..34099bf528 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -123,6 +123,11 @@ protected: virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features); }; +struct LibrarySymbol { + char *name; + bool is_required; +}; + void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) { if (p_type != "GDNativeLibrary") { return; @@ -136,7 +141,6 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty Ref<ConfigFile> config = lib->get_config_file(); - String entry_lib_path; { List<String> entry_keys; @@ -161,14 +165,12 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty continue; } - entry_lib_path = config->get_value("entry", key); - break; + String entry_lib_path = config->get_value("entry", key); + add_shared_object(entry_lib_path, tags); } } - Vector<String> dependency_paths; { - List<String> dependency_keys; config->get_section_keys("dependencies", &dependency_keys); @@ -191,47 +193,54 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty continue; } - dependency_paths = config->get_value("dependencies", key); - break; + Vector<String> dependency_paths = config->get_value("dependencies", key); + for (int i = 0; i < dependency_paths.size(); i++) { + add_shared_object(dependency_paths[i], tags); + } } } - bool is_statically_linked = false; - { - - List<String> static_linking_keys; - config->get_section_keys("static_linking", &static_linking_keys); - - for (List<String>::Element *E = static_linking_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 = p_features.has(tags[i]); - - if (!has_feature) { - skip = true; - break; + if (p_features.has("iOS")) { + // Register symbols in the "fake" dynamic lookup table, because dlsym does not work well on iOS. + LibrarySymbol expected_symbols[] = { + { "gdnative_init", true }, + { "gdnative_terminate", false }, + { "nativescript_init", false }, + { "nativescript_frame", false }, + { "nativescript_thread_enter", false }, + { "nativescript_thread_exit", false }, + { "gdnative_singleton", false } + }; + String declare_pattern = "extern \"C\" void $name(void)$weak;\n"; + String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n" + "extern void add_ios_init_callback(void (*cb)());\n"; + String linker_flags = ""; + for (int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { + String full_name = lib->get_symbol_prefix() + expected_symbols[i].name; + String code = declare_pattern.replace("$name", full_name); + code = code.replace("$weak", expected_symbols[i].is_required ? "" : " __attribute__((weak))"); + additional_code += code; + + if (!expected_symbols[i].is_required) { + if (linker_flags.length() > 0) { + linker_flags += " "; } + linker_flags += "-Wl,-U,_" + full_name; } - - if (skip) { - continue; - } - - is_statically_linked = config->get_value("static_linking", key); - break; } - } - if (!is_statically_linked) - add_shared_object(entry_lib_path); + additional_code += String("void $prefixinit() {\n").replace("$prefix", lib->get_symbol_prefix()); + String register_pattern = " if (&$name) register_dynamic_symbol((char *)\"$name\", (void *)$name);\n"; + for (int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { + String full_name = lib->get_symbol_prefix() + expected_symbols[i].name; + additional_code += register_pattern.replace("$name", full_name); + } + additional_code += "}\n"; + additional_code += String("struct $prefixstruct {$prefixstruct() {add_ios_init_callback($prefixinit);}};\n").replace("$prefix", lib->get_symbol_prefix()); + additional_code += String("$prefixstruct $prefixstruct_instance;\n").replace("$prefix", lib->get_symbol_prefix()); - for (int i = 0; i < dependency_paths.size(); i++) { - add_shared_object(dependency_paths[i]); + add_ios_cpp_code(additional_code); + add_ios_linker_flags(linker_flags); } } @@ -271,9 +280,7 @@ void register_gdnative_types() { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - EditorNode::add_init_callback(editor_init_callback); - } + EditorNode::add_init_callback(editor_init_callback); #endif ClassDB::register_class<GDNativeLibrary>(); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 55ea8a5f24..41a810ff00 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -100,7 +100,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco #endif instance->owner->set_script_instance(instance); -/* STEP 2, INITIALIZE AND CONSRTUCT */ + /* STEP 2, INITIALIZE AND CONSRTUCT */ #ifndef NO_THREADS GDScriptLanguage::singleton->lock->lock(); @@ -615,6 +615,23 @@ ScriptLanguage *GDScript::get_language() const { return GDScriptLanguage::get_singleton(); } +void GDScript::get_constants(Map<StringName, Variant> *p_constants) { + + if (p_constants) { + for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { + (*p_constants)[E->key()] = E->value(); + } + } +} + +void GDScript::get_members(Set<StringName> *p_members) { + if (p_members) { + for (Set<StringName>::Element *E = members.front(); E; E = E->next()) { + p_members->insert(E->get()); + } + } +} + Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { GDScript *top = this; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 3f6f431938..6e5d59ad0e 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -198,6 +198,9 @@ public: return -1; } + virtual void get_constants(Map<StringName, Variant> *p_constants); + virtual void get_members(Set<StringName> *p_members); + GDScript(); ~GDScript(); }; @@ -219,7 +222,7 @@ class GDScriptInstance : public ScriptInstance { void _ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount); public: - _FORCE_INLINE_ Object *get_owner() { return owner; } + virtual Object *get_owner() { return owner; } virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; @@ -407,7 +410,8 @@ public: virtual String debug_get_stack_level_source(int p_level) const; virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); + virtual ScriptInstance *debug_get_stack_level_instance(int p_level); + virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1); virtual void reload_all_scripts(); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 3121a61436..4cd6472b7f 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1686,21 +1686,44 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons base_class = p->subclasses[base]; break; } + + if (p->constants.has(base)) { + + base_class = p->constants[base]; + if (base_class.is_null()) { + _set_error("Constant is not a class: " + base, p_class); + return ERR_SCRIPT_FAILED; + } + break; + } + p = p->_owner; } if (base_class.is_valid()) { + String ident = base; + for (int i = 1; i < p_class->extends_class.size(); i++) { String subclass = p_class->extends_class[i]; + ident += ("." + subclass); + if (base_class->subclasses.has(subclass)) { base_class = base_class->subclasses[subclass]; + } else if (base_class->constants.has(subclass)) { + + Ref<GDScript> new_base_class = base_class->constants[subclass]; + if (new_base_class.is_null()) { + _set_error("Constant is not a class: " + ident, p_class); + return ERR_SCRIPT_FAILED; + } + base_class = new_base_class; } else { - _set_error("Could not find subclass: " + subclass, p_class); + _set_error("Could not find subclass: " + ident, p_class); return ERR_FILE_NOT_FOUND; } } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index a74b8a8483..5a76acea6e 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -33,7 +33,7 @@ #include "gdscript_compiler.h" #include "global_constants.h" #include "os/file_access.h" -#include "project_settings.h" +#include "core/engine.h" #ifdef TOOLS_ENABLED #include "editor/editor_file_system.h" @@ -280,10 +280,62 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> * p_values->push_back(instance->debug_get_member_by_index(E->get().index)); } } -void GDScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { - //no globals are really reachable in gdscript +ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) { + + ERR_FAIL_COND_V(_debug_parse_err_line >= 0, NULL); + ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, NULL); + + int l = _debug_call_stack_pos - p_level - 1; + ScriptInstance *instance = _call_stack[l].instance; + + return instance; } + +void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { + + const Map<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map(); + const Variant *globals = GDScriptLanguage::get_singleton()->get_global_array(); + + List<Pair<String, Variant> > cinfo; + get_public_constants(&cinfo); + + for (const Map<StringName, int>::Element *E = name_idx.front(); E; E = E->next()) { + + if (ClassDB::class_exists(E->key()) || Engine::get_singleton()->has_singleton(E->key())) + continue; + + bool is_script_constant = false; + for (List<Pair<String, Variant> >::Element *CE = cinfo.front(); CE; CE = CE->next()) { + if (CE->get().first == E->key()) { + is_script_constant = true; + break; + } + } + if (is_script_constant) + continue; + + const Variant &var = globals[E->value()]; + if (Object *obj = var) { + if (Object::cast_to<GDScriptNativeClass>(obj)) + continue; + } + + bool skip = false; + for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) { + if (E->key() == GlobalConstants::get_global_constant_name(i)) { + skip = true; + break; + } + } + if (skip) + continue; + + p_globals->push_back(E->key()); + p_values->push_back(var); + } +} + String GDScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { if (_debug_parse_err_line >= 0) @@ -1743,7 +1795,7 @@ static void _find_type_arguments(GDScriptCompletionContext &context, const GDScr } } else { -//regular method + //regular method #if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED) if (p_argidx < m->get_argument_count()) { diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index c6066ceefb..ca0a9582a7 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -84,6 +84,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "rad2deg", "linear2db", "db2linear", + "polar2cartesian", + "cartesian2polar", "wrapi", "wrapf", "max", @@ -408,6 +410,22 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(0); r_ret = Math::db2linear((double)*p_args[0]); } break; + case MATH_POLAR2CARTESIAN: { + VALIDATE_ARG_COUNT(2); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double r = *p_args[0]; + double th = *p_args[1]; + r_ret = Vector2(r * Math::cos(th), r * Math::sin(th)); + } break; + case MATH_CARTESIAN2POLAR: { + VALIDATE_ARG_COUNT(2); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double x = *p_args[0]; + double y = *p_args[1]; + r_ret = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } break; case MATH_WRAP: { VALIDATE_ARG_COUNT(3); r_ret = Math::wrapi((int64_t)*p_args[0], (int64_t)*p_args[1], (int64_t)*p_args[2]); @@ -1296,6 +1314,8 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { case MATH_RAD2DEG: case MATH_LINEAR2DB: case MATH_DB2LINEAR: + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: case MATH_WRAP: case MATH_WRAPF: case LOGIC_MAX: @@ -1526,6 +1546,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::REAL; return mi; } break; + case MATH_POLAR2CARTESIAN: { + MethodInfo mi("polar2cartesian", PropertyInfo(Variant::REAL, "r"), PropertyInfo(Variant::REAL, "th")); + mi.return_val.type = Variant::VECTOR2; + return mi; + } break; + case MATH_CARTESIAN2POLAR: { + MethodInfo mi("cartesian2polar", PropertyInfo(Variant::REAL, "x"), PropertyInfo(Variant::REAL, "y")); + mi.return_val.type = Variant::VECTOR2; + return mi; + } break; case MATH_WRAP: { MethodInfo mi("wrapi", PropertyInfo(Variant::INT, "value"), PropertyInfo(Variant::INT, "min"), PropertyInfo(Variant::INT, "max")); mi.return_val.type = Variant::INT; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index ecbede83a8..d1c5815cec 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -75,6 +75,8 @@ public: MATH_RAD2DEG, MATH_LINEAR2DB, MATH_DB2LINEAR, + MATH_POLAR2CARTESIAN, + MATH_CARTESIAN2POLAR, MATH_WRAP, MATH_WRAPF, LOGIC_MAX, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 29b9865b1d..bee9ef1998 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2971,18 +2971,37 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) { } while (true) { - if (tokenizer->get_token() != GDScriptTokenizer::TK_IDENTIFIER) { - _set_error("Invalid 'extends' syntax, expected string constant (path) and/or identifier (parent class)."); - return; - } + switch (tokenizer->get_token()) { + + case GDScriptTokenizer::TK_IDENTIFIER: { + + StringName identifier = tokenizer->get_token_identifier(); + p_class->extends_class.push_back(identifier); + } + break; - StringName identifier = tokenizer->get_token_identifier(); - p_class->extends_class.push_back(identifier); + case GDScriptTokenizer::TK_PERIOD: + break; + + default: { + + _set_error("Invalid 'extends' syntax, expected string constant (path) and/or identifier (parent class)."); + return; + } + } tokenizer->advance(1); - if (tokenizer->get_token() != GDScriptTokenizer::TK_PERIOD) - return; + + switch (tokenizer->get_token()) { + + case GDScriptTokenizer::TK_IDENTIFIER: + case GDScriptTokenizer::TK_PERIOD: + continue; + + default: + return; + } } } @@ -3758,22 +3777,82 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.hint = PROPERTY_HINT_NONE; } - } else if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER) { + } else { - String identifier = tokenizer->get_token_identifier(); - if (!ClassDB::is_parent_class(identifier, "Resource")) { + parenthesis++; + Node *subexpr = _parse_and_reduce_expression(p_class, true, true); + if (!subexpr) { + if (_recover_from_completion()) { + break; + } + return; + } + parenthesis--; + if (subexpr->type != Node::TYPE_CONSTANT) { current_export = PropertyInfo(); - _set_error("Export hint not a type or resource."); + _set_error("Expected a constant expression."); } - current_export.type = Variant::OBJECT; - current_export.hint = PROPERTY_HINT_RESOURCE_TYPE; - current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + Variant constant = static_cast<ConstantNode *>(subexpr)->value; - current_export.hint_string = identifier; + if (constant.get_type() == Variant::OBJECT) { + GDScriptNativeClass *native_class = Object::cast_to<GDScriptNativeClass>(constant); - tokenizer->advance(); + if (native_class && ClassDB::is_parent_class(native_class->get_name(), "Resource")) { + current_export.type = Variant::OBJECT; + current_export.hint = PROPERTY_HINT_RESOURCE_TYPE; + current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + + current_export.hint_string = native_class->get_name(); + + } else { + current_export = PropertyInfo(); + _set_error("Export hint not a resource type."); + } + } else if (constant.get_type() == Variant::DICTIONARY) { + // Enumeration + bool is_flags = false; + + if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { + tokenizer->advance(); + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") { + is_flags = true; + tokenizer->advance(); + } else { + current_export = PropertyInfo(); + _set_error("Expected 'FLAGS' after comma."); + } + } + + current_export.type = Variant::INT; + current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM; + Dictionary enum_values = constant; + + List<Variant> keys; + enum_values.get_key_list(&keys); + + bool first = true; + for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + if (enum_values[E->get()].get_type() == Variant::INT) { + if (!first) + current_export.hint_string += ","; + else + first = false; + + current_export.hint_string += E->get().operator String().camelcase_to_underscore(true).capitalize().xml_escape(); + if (!is_flags) { + current_export.hint_string += ":"; + current_export.hint_string += enum_values[E->get()].operator String().xml_escape(); + } + } + } + } else { + current_export = PropertyInfo(); + _set_error("Expected type for export."); + return; + } } if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 491adb31ee..3a5d0fd3fc 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -623,6 +623,16 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu return do_input_action(p_camera, mm->get_position(), false); } + 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)); + return true; + } + } + return false; } diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index cb0eb1acdd..37e6e5bbe3 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -35,6 +35,11 @@ namespace Godot return (float)Math.Atan2(x, y); } + public static Vector2 cartesian2polar(float x, float y) + { + return new Vector2(sqrt(x * x + y * y), atan2(y, x)); + } + public static float ceil(float s) { return (float)Math.Ceiling(s); @@ -176,6 +181,11 @@ namespace Godot return val; } + public static Vector2 polar2cartesian(float r, float th) + { + return new Vector2(r * cos(th), r * sin(th)); + } + public static float pow(float x, float y) { return (float)Math.Pow(x, y); diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 7cc2168b70..a0c2508b0d 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -57,7 +57,7 @@ String _get_expected_build_config() { String _get_mono_user_dir() { #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton()) { - return EditorSettings::get_singleton()->get_settings_path().plus_file("mono"); + return EditorSettings::get_singleton()->get_data_dir().plus_file("mono"); } else { String settings_path; @@ -68,19 +68,13 @@ String _get_mono_user_dir() { // contain yourself settings_path = exe_dir.plus_file("editor_data"); } else { - if (OS::get_singleton()->has_environment("APPDATA")) { - String app_data = OS::get_singleton()->get_environment("APPDATA").replace("\\", "/"); - settings_path = app_data.plus_file(String(_MKSTR(VERSION_SHORT_NAME)).capitalize()); - } else if (OS::get_singleton()->has_environment("HOME")) { - String home = OS::get_singleton()->get_environment("HOME"); - settings_path = home.plus_file("." + String(_MKSTR(VERSION_SHORT_NAME)).to_lower()); - } + settings_path = OS::get_singleton()->get_data_path().plus_file(OS::get_singleton()->get_godot_dir_name()); } return settings_path.plus_file("mono"); } #else - return OS::get_singleton()->get_data_dir().plus_file("mono"); + return OS::get_singleton()->get_user_data_dir().plus_file("mono"); #endif } diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 1bee1115a6..eb34f9dd3f 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -41,7 +41,7 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) { #define SET_FROM_STRUCT_AND_BREAK(m_type) \ { \ - const m_type &val = p_value.operator m_type(); \ + const m_type &val = p_value.operator ::m_type(); \ MARSHALLED_OUT(m_type, val, raw); \ mono_field_set_value(p_object, mono_field, raw); \ break; \ diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 9c415951bb..8bc2bb5096 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -36,7 +36,7 @@ namespace GDMonoMarshal { #define RETURN_BOXED_STRUCT(m_t, m_var_in) \ { \ - const m_t &m_in = m_var_in->operator m_t(); \ + const m_t &m_in = m_var_in->operator ::m_t(); \ MARSHALLED_OUT(m_t, m_in, raw); \ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_t), raw); \ } diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 354febf89a..8c6951fea2 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -4,7 +4,7 @@ Contains the results of a regex search. </brief_description> <description> - Contains the results of a single regex match returned by [method RegEx.search] and [method.RegEx.search_all]. It can be used to find the position and range of the match and its capturing groups, and it can extract its sub-string for you. + Contains the results of a single regex match returned by [method RegEx.search] and [method RegEx.search_all]. It can be used to find the position and range of the match and its capturing groups, and it can extract its sub-string for you. </description> <tutorials> </tutorials> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 27231574d7..c45c8d2b64 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -151,68 +151,74 @@ <constant name="MATH_DB2LINEAR" value="39"> Convert the input from decibel volume to linear volume. </constant> - <constant name="MATH_WRAP" value="40"> + <constant name="MATH_POLAR2CARTESIAN" value="40"> + Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (x and y axis). </constant> - <constant name="MATH_WRAPF" value="41"> + <constant name="MATH_CARTESIAN2POLAR" value="41"> + Converts a 2D point expressed in the cartesian coordinate system (x and y axis) to the polar coordinate system (a distance from the origin and an angle). </constant> - <constant name="LOGIC_MAX" value="42"> + <constant name="MATH_WRAP" value="42"> + </constant> + <constant name="MATH_WRAPF" value="43"> + </constant> + <constant name="LOGIC_MAX" value="44"> Return the greater of the two numbers, also known as their maximum. </constant> - <constant name="LOGIC_MIN" value="43"> + <constant name="LOGIC_MIN" value="45"> Return the lesser of the two numbers, also known as their minimum. </constant> - <constant name="LOGIC_CLAMP" value="44"> + <constant name="LOGIC_CLAMP" value="46"> Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to `min(max(input, range_low), range_high)` </constant> - <constant name="LOGIC_NEAREST_PO2" value="45"> + <constant name="LOGIC_NEAREST_PO2" value="46"> Return the nearest power of 2 to the input. </constant> - <constant name="OBJ_WEAKREF" value="46"> + <constant name="OBJ_WEAKREF" value="47"> Create a [WeakRef] from the input. </constant> - <constant name="FUNC_FUNCREF" value="47"> + <constant name="FUNC_FUNCREF" value="48"> Create a [FuncRef] from the input. </constant> - <constant name="TYPE_CONVERT" value="48"> + <constant name="TYPE_CONVERT" value="49"> Convert between types. </constant> - <constant name="TYPE_OF" value="49"> + <constant name="TYPE_OF" value="50"> Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned. </constant> - <constant name="TYPE_EXISTS" value="50"> + <constant name="TYPE_EXISTS" value="51"> Checks if a type is registered in the [ClassDB]. </constant> - <constant name="TEXT_CHAR" value="51"> + <constant name="TEXT_CHAR" value="52"> Return a character with the given ascii value. </constant> - <constant name="TEXT_STR" value="52"> + <constant name="TEXT_STR" value="53"> Convert the input to a string. </constant> - <constant name="TEXT_PRINT" value="53"> + <constant name="TEXT_PRINT" value="54"> Print the given string to the output window. </constant> - <constant name="TEXT_PRINTERR" value="54"> + <constant name="TEXT_PRINTERR" value="55"> Print the given string to the standard error output. </constant> - <constant name="TEXT_PRINTRAW" value="55"> + <constant name="TEXT_PRINTRAW" value="56"> Print the given string to the standard output, without adding a newline. </constant> - <constant name="VAR_TO_STR" value="56"> + <constant name="VAR_TO_STR" value="57"> Serialize a [Variant] to a string. </constant> - <constant name="STR_TO_VAR" value="57"> + <constant name="STR_TO_VAR" value="58"> Deserialize a [Variant] from a string serialized using [VAR_TO_STR]. </constant> - <constant name="VAR_TO_BYTES" value="58"> + <constant name="VAR_TO_BYTES" value="59"> Serialize a [Variant] to a [PoolByteArray]. </constant> - <constant name="BYTES_TO_VAR" value="59"> + <constant name="BYTES_TO_VAR" value="60"> Deserialize a [Variant] from a [PoolByteArray] serialized using [VAR_TO_BYTES]. </constant> - <constant name="COLORN" value="60"> + <constant name="COLORN" value="61"> Return the [Color] with the given name and alpha ranging from 0 to 1. Note: names are defined in color_names.inc. </constant> - <constant name="FUNC_MAX" value="61"> + <constant name="FUNC_MAX" value="62"> The maximum value the [member function] property can have. </constant> </constants> diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 8f7fe58bee..32f7519125 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -78,6 +78,8 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "rad2deg", "linear2db", "db2linear", + "polar2cartesian", + "cartesian2polar", "wrapi", "wrapf", "max", @@ -191,6 +193,8 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case MATH_EASE: case MATH_STEPIFY: case MATH_RANDOM: + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: case LOGIC_MAX: case LOGIC_MIN: case FUNC_FUNCREF: @@ -368,6 +372,18 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const case MATH_DB2LINEAR: { return PropertyInfo(Variant::REAL, "db"); } break; + case MATH_POLAR2CARTESIAN: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "r"); + else + return PropertyInfo(Variant::REAL, "th"); + } break; + case MATH_CARTESIAN2POLAR: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "x"); + else + return PropertyInfo(Variant::REAL, "y"); + } break; case MATH_WRAP: { if (p_idx == 0) return PropertyInfo(Variant::INT, "value"); @@ -573,6 +589,10 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_DB2LINEAR: { t = Variant::REAL; } break; + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: { + t = Variant::VECTOR2; + } break; case MATH_WRAP: { t = Variant::INT; } break; @@ -922,6 +942,20 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(0); *r_return = Math::db2linear((double)*p_inputs[0]); } break; + case VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double r = *p_inputs[0]; + double th = *p_inputs[1]; + *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); + } break; + case VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double x = *p_inputs[0]; + double y = *p_inputs[1]; + *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } break; case VisualScriptBuiltinFunc::MATH_WRAP: { VALIDATE_ARG_NUM(0); VALIDATE_ARG_NUM(1); @@ -1294,6 +1328,8 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(MATH_RAD2DEG); BIND_ENUM_CONSTANT(MATH_LINEAR2DB); BIND_ENUM_CONSTANT(MATH_DB2LINEAR); + BIND_ENUM_CONSTANT(MATH_POLAR2CARTESIAN); + BIND_ENUM_CONSTANT(MATH_CARTESIAN2POLAR); BIND_ENUM_CONSTANT(MATH_WRAP); BIND_ENUM_CONSTANT(MATH_WRAPF); BIND_ENUM_CONSTANT(LOGIC_MAX); @@ -1381,6 +1417,8 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/polar2cartesian", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/cartesian2polar", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 34a2825938..54dc997b38 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -77,6 +77,8 @@ public: MATH_RAD2DEG, MATH_LINEAR2DB, MATH_DB2LINEAR, + MATH_POLAR2CARTESIAN, + MATH_CARTESIAN2POLAR, MATH_WRAP, MATH_WRAPF, LOGIC_MAX, diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 83b8d8da2d..2318149ca5 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1389,7 +1389,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant & if (String(d["type"]) == "obj_property") { #ifdef OSX_ENABLED - const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Meta to drop a Getter. Hold Shift to drop a generic signature.")); + const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Getter. Hold Shift to drop a generic signature."), find_keycode_name(KEY_META))); #else const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature.")); #endif @@ -1398,7 +1398,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant & if (String(d["type"]) == "nodes") { #ifdef OSX_ENABLED - const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Meta to drop a simple reference to the node.")); + const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a simple reference to the node."), find_keycode_name(KEY_META))); #else const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a simple reference to the node.")); #endif @@ -1407,7 +1407,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant & if (String(d["type"]) == "visual_script_variable_drag") { #ifdef OSX_ENABLED - const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Meta to drop a Variable Setter.")); + const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Variable Setter."), find_keycode_name(KEY_META))); #else const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Variable Setter.")); #endif diff --git a/modules/webm/config.py b/modules/webm/config.py index 0374bb36f7..dcae4447d5 100644 --- a/modules/webm/config.py +++ b/modules/webm/config.py @@ -1,5 +1,5 @@ def can_build(platform): - return True + return platform != 'iphone' def configure(env): pass |