Skip to content

Commit a56e960

Browse files
committed
Merge pull request #71233 from PrecisionRender/barycentric-coords
Add ability to get face index and barycentric coordinates from raycast
2 parents f9f5041 + af7f787 commit a56e960

16 files changed

Lines changed: 78 additions & 30 deletions

core/core_bind.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,11 @@ Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point
961961
return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s);
962962
}
963963

964+
Vector3 Geometry3D::get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
965+
Vector3 res = ::Geometry3D::triangle_get_barycentric_coords(p_v0, p_v1, p_v2, p_point);
966+
return res;
967+
}
968+
964969
Variant Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
965970
Vector3 res;
966971
if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) {
@@ -1034,6 +1039,8 @@ void Geometry3D::_bind_methods() {
10341039

10351040
ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped);
10361041

1042+
ClassDB::bind_method(D_METHOD("get_triangle_barycentric_coords", "point", "a", "b", "c"), &Geometry3D::get_triangle_barycentric_coords);
1043+
10371044
ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &Geometry3D::ray_intersects_triangle);
10381045
ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &Geometry3D::segment_intersects_triangle);
10391046
ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &Geometry3D::segment_intersects_sphere);

core/core_bind.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ class Geometry3D : public Object {
326326
Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2);
327327
Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b);
328328
Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b);
329+
Vector3 get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
329330
Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
330331
Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
331332

doc/classes/Geometry3D.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@
7373
Given the two 3D segments ([param p1], [param p2]) and ([param q1], [param q2]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector3Array] that contains this point on ([param p1], [param p2]) as well the accompanying point on ([param q1], [param q2]).
7474
</description>
7575
</method>
76+
<method name="get_triangle_barycentric_coords">
77+
<return type="Vector3" />
78+
<param index="0" name="point" type="Vector3" />
79+
<param index="1" name="a" type="Vector3" />
80+
<param index="2" name="b" type="Vector3" />
81+
<param index="3" name="c" type="Vector3" />
82+
<description>
83+
Returns a [Vector3] containing weights based on how close a 3D position ([param point]) is to a triangle's different vertices ([param a], [param b] and [param c]). This is useful for interpolating between the data of different vertices in a triangle. One example use case is using this to smoothly rotate over a mesh instead of relying solely on face normals.
84+
[url=https://en.wikipedia.org/wiki/Barycentric_coordinate_system]Here is a more detailed explanation of barycentric coordinates.[/url]
85+
</description>
86+
</method>
7687
<method name="ray_intersects_triangle">
7788
<return type="Variant" />
7889
<param index="0" name="from" type="Vector3" />

doc/classes/PhysicsDirectSpaceState3D.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
[code]collider_id[/code]: The colliding object's ID.
6868
[code]normal[/code]: The object's surface normal at the intersection point, or [code]Vector3(0, 0, 0)[/code] if the ray starts inside the shape and [member PhysicsRayQueryParameters3D.hit_from_inside] is [code]true[/code].
6969
[code]position[/code]: The intersection point.
70+
[code]face_index[/code]: The face index at the intersection point.
71+
[b]Note:[/b] Returns a valid number only if the intersected shape is a [ConcavePolygonShape3D]. Otherwise, [code]-1[/code] is returned.
7072
[code]rid[/code]: The intersecting object's [RID].
7173
[code]shape[/code]: The shape index of the colliding shape.
7274
If the ray did not intersect anything, then an empty dictionary is returned instead.

doc/classes/RayCast3D.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]).
6060
</description>
6161
</method>
62+
<method name="get_collision_face_index" qualifiers="const">
63+
<return type="int" />
64+
<description>
65+
Returns the collision object's face index at the collision point, or [code]-1[/code] if the shape intersecting the ray is not a [ConcavePolygonShape3D].
66+
</description>
67+
</method>
6268
<method name="get_collision_mask_value" qualifiers="const">
6369
<return type="bool" />
6470
<param index="0" name="layer_number" type="int" />

scene/3d/ray_cast_3d.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ Vector3 RayCast3D::get_collision_normal() const {
104104
return collision_normal;
105105
}
106106

107+
int RayCast3D::get_collision_face_index() const {
108+
return collision_face_index;
109+
}
110+
107111
void RayCast3D::set_enabled(bool p_enabled) {
108112
enabled = p_enabled;
109113
update_gizmos();
@@ -232,6 +236,7 @@ void RayCast3D::_update_raycast_state() {
232236
against_rid = rr.rid;
233237
collision_point = rr.position;
234238
collision_normal = rr.normal;
239+
collision_face_index = rr.face_index;
235240
against_shape = rr.shape;
236241
} else {
237242
collided = false;
@@ -321,6 +326,7 @@ void RayCast3D::_bind_methods() {
321326
ClassDB::bind_method(D_METHOD("get_collider_shape"), &RayCast3D::get_collider_shape);
322327
ClassDB::bind_method(D_METHOD("get_collision_point"), &RayCast3D::get_collision_point);
323328
ClassDB::bind_method(D_METHOD("get_collision_normal"), &RayCast3D::get_collision_normal);
329+
ClassDB::bind_method(D_METHOD("get_collision_face_index"), &RayCast3D::get_collision_face_index);
324330

325331
ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &RayCast3D::add_exception_rid);
326332
ClassDB::bind_method(D_METHOD("add_exception", "node"), &RayCast3D::add_exception);

scene/3d/ray_cast_3d.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class RayCast3D : public Node3D {
4545
int against_shape = 0;
4646
Vector3 collision_point;
4747
Vector3 collision_normal;
48+
int collision_face_index = -1;
4849

4950
Vector3 target_position = Vector3(0, -1, 0);
5051
HashSet<RID> exclude;
@@ -122,6 +123,7 @@ class RayCast3D : public Node3D {
122123
int get_collider_shape() const;
123124
Vector3 get_collision_point() const;
124125
Vector3 get_collision_normal() const;
126+
int get_collision_face_index() const;
125127

126128
void add_exception_rid(const RID &p_rid);
127129
void add_exception(const CollisionObject3D *p_node);

servers/physics_3d/godot_body_pair_3d.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ void GodotBodyPair3D::validate_contacts() {
168168
// adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
169169
bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, const Transform3D &p_xform_A, GodotBody3D *p_B, int p_shape_B, const Transform3D &p_xform_B) {
170170
GodotShape3D *shape_A_ptr = p_A->get_shape(p_shape_A);
171-
GodotShape3D *shape_B_ptr = p_B->get_shape(p_shape_B);
172171

173172
Vector3 motion = p_A->get_linear_velocity() * p_step;
174173
real_t mlen = motion.length();
@@ -221,7 +220,8 @@ bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A,
221220
Vector3 local_to = from_inv.xform(to);
222221

223222
Vector3 rpos, rnorm;
224-
if (shape_B_ptr->intersect_segment(local_from, local_to, rpos, rnorm, true)) {
223+
int fi = -1;
224+
if (p_B->get_shape(p_shape_B)->intersect_segment(local_from, local_to, rpos, rnorm, fi, true)) {
225225
float hit_length = local_from.distance_to(rpos);
226226
if (hit_length < segment_hit_length) {
227227
segment_support_idx = i;

servers/physics_3d/godot_collision_solver_3d.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ bool GodotCollisionSolver3D::solve_separation_ray(const GodotShape3D *p_shape_A,
106106
to = ai.xform(to);
107107

108108
Vector3 p, n;
109-
if (!p_shape_B->intersect_segment(from, to, p, n, true)) {
109+
int fi = -1;
110+
if (!p_shape_B->intersect_segment(from, to, p, n, fi, true)) {
110111
return false;
111112
}
112113

servers/physics_3d/godot_shape_3d.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Vector3 GodotWorldBoundaryShape3D::get_support(const Vector3 &p_normal) const {
126126
return p_normal * 1e15;
127127
}
128128

129-
bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
129+
bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
130130
bool inters = plane.intersects_segment(p_begin, p_end, &r_result);
131131
if (inters) {
132132
r_normal = plane.normal;
@@ -207,7 +207,7 @@ void GodotSeparationRayShape3D::get_supports(const Vector3 &p_normal, int p_max,
207207
}
208208
}
209209

210-
bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
210+
bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
211211
return false; //simply not possible
212212
}
213213

@@ -275,7 +275,7 @@ void GodotSphereShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector
275275
r_type = FEATURE_POINT;
276276
}
277277

278-
bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
278+
bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
279279
return Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(), radius, &r_result, &r_normal);
280280
}
281281

@@ -417,7 +417,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *
417417
r_supports[0] = point;
418418
}
419419

420-
bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
420+
bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
421421
AABB aabb_ext(-half_extents, half_extents * 2.0);
422422

423423
return aabb_ext.intersects_segment(p_begin, p_end, &r_result, &r_normal);
@@ -551,7 +551,7 @@ void GodotCapsuleShape3D::get_supports(const Vector3 &p_normal, int p_max, Vecto
551551
}
552552
}
553553

554-
bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
554+
bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
555555
Vector3 norm = (p_end - p_begin).normalized();
556556
real_t min_d = 1e20;
557557

@@ -743,7 +743,7 @@ void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vect
743743
}
744744
}
745745

746-
bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
746+
bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
747747
return Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &r_result, &r_normal, 1);
748748
}
749749

@@ -975,7 +975,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max,
975975
r_type = FEATURE_POINT;
976976
}
977977

978-
bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
978+
bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
979979
const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();
980980
int fc = mesh.faces.size();
981981

@@ -1252,7 +1252,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3
12521252
r_supports[0] = vertex[vert_support_idx];
12531253
}
12541254

1255-
bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
1255+
bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
12561256
bool c = Geometry3D::segment_intersects_triangle(p_begin, p_end, vertex[0], vertex[1], vertex[2], &r_result);
12571257
if (c) {
12581258
r_normal = Plane(vertex[0], vertex[1], vertex[2]).normal;
@@ -1362,12 +1362,14 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_
13621362

13631363
Vector3 res;
13641364
Vector3 normal;
1365-
if (face->intersect_segment(p_params->from, p_params->to, res, normal, true)) {
1365+
int face_index = params_bvh->face_index;
1366+
if (face->intersect_segment(p_params->from, p_params->to, res, normal, face_index, true)) {
13661367
real_t d = p_params->dir.dot(res) - p_params->dir.dot(p_params->from);
13671368
if ((d > 0) && (d < p_params->min_d)) {
13681369
p_params->min_d = d;
13691370
p_params->result = res;
13701371
p_params->normal = normal;
1372+
p_params->face_index = face_index;
13711373
p_params->collisions++;
13721374
}
13731375
}
@@ -1381,7 +1383,7 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_
13811383
}
13821384
}
13831385

1384-
bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
1386+
bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
13851387
if (faces.size() == 0) {
13861388
return false;
13871389
}
@@ -1411,6 +1413,7 @@ bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const
14111413
if (params.collisions > 0) {
14121414
r_result = params.result;
14131415
r_normal = params.normal;
1416+
r_face_index = params.face_index;
14141417
return true;
14151418
} else {
14161419
return false;
@@ -1734,9 +1737,11 @@ struct _HeightmapGridCullState {
17341737
_FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) {
17351738
Vector3 res;
17361739
Vector3 normal;
1737-
if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, true)) {
1740+
int fi = -1;
1741+
if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, fi, true)) {
17381742
p_params.result = res;
17391743
p_params.normal = normal;
1744+
17401745
return true;
17411746
}
17421747

@@ -1940,7 +1945,7 @@ bool GodotHeightMapShape3D::_intersect_grid_segment(ProcessFunction &p_process,
19401945
return false;
19411946
}
19421947

1943-
bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const {
1948+
bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
19441949
if (heights.is_empty()) {
19451950
return false;
19461951
}

0 commit comments

Comments
 (0)