2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* collision_polygon_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
2017-01-01 22:01:57 +01:00
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
2017-04-08 00:11:42 +02:00
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
2014-02-10 02:10:30 +01:00
/* */
/* 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 "collision_polygon_2d.h"
# include "collision_object_2d.h"
# include "scene/resources/concave_polygon_shape_2d.h"
# include "scene/resources/convex_polygon_shape_2d.h"
2016-01-01 00:23:34 +01:00
# include "triangulator.h"
2014-02-10 02:10:30 +01:00
void CollisionPolygon2D : : _add_to_collision_object ( Object * p_obj ) {
2015-09-16 03:07:03 +02:00
if ( unparenting | | ! can_update_body )
2015-03-23 15:31:03 +01:00
return ;
2014-02-10 02:10:30 +01:00
CollisionObject2D * co = p_obj - > cast_to < CollisionObject2D > ( ) ;
ERR_FAIL_COND ( ! co ) ;
2017-03-05 16:44:50 +01:00
if ( polygon . size ( ) = = 0 )
2014-02-10 02:10:30 +01:00
return ;
2017-03-05 16:44:50 +01:00
bool solids = build_mode = = BUILD_SOLIDS ;
2014-02-10 02:10:30 +01:00
if ( solids ) {
//here comes the sun, lalalala
//decompose concave into multiple convex polygons and add them
2017-03-05 16:44:50 +01:00
Vector < Vector < Vector2 > > decomp = _decompose_in_convex ( ) ;
shape_from = co - > get_shape_count ( ) ;
for ( int i = 0 ; i < decomp . size ( ) ; i + + ) {
Ref < ConvexPolygonShape2D > convex = memnew ( ConvexPolygonShape2D ) ;
2014-02-10 02:10:30 +01:00
convex - > set_points ( decomp [ i ] ) ;
2017-03-05 16:44:50 +01:00
co - > add_shape ( convex , get_transform ( ) ) ;
2014-12-20 19:30:06 +01:00
if ( trigger )
2017-03-05 16:44:50 +01:00
co - > set_shape_as_trigger ( co - > get_shape_count ( ) - 1 , true ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
shape_to = co - > get_shape_count ( ) - 1 ;
if ( shape_to < shape_from ) {
shape_from = - 1 ;
shape_to = - 1 ;
2015-09-16 03:07:03 +02:00
}
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
Ref < ConcavePolygonShape2D > concave = memnew ( ConcavePolygonShape2D ) ;
2014-02-10 02:10:30 +01:00
2017-01-07 22:25:37 +01:00
PoolVector < Vector2 > segments ;
2017-03-05 16:44:50 +01:00
segments . resize ( polygon . size ( ) * 2 ) ;
PoolVector < Vector2 > : : Write w = segments . write ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < polygon . size ( ) ; i + + ) {
w [ ( i < < 1 ) + 0 ] = polygon [ i ] ;
w [ ( i < < 1 ) + 1 ] = polygon [ ( i + 1 ) % polygon . size ( ) ] ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
w = PoolVector < Vector2 > : : Write ( ) ;
2014-02-10 02:10:30 +01:00
concave - > set_segments ( segments ) ;
2017-03-05 16:44:50 +01:00
co - > add_shape ( concave , get_transform ( ) ) ;
2014-12-20 19:30:06 +01:00
if ( trigger )
2017-03-05 16:44:50 +01:00
co - > set_shape_as_trigger ( co - > get_shape_count ( ) - 1 , true ) ;
2015-09-16 03:07:03 +02:00
2017-03-05 16:44:50 +01:00
shape_from = co - > get_shape_count ( ) - 1 ;
shape_to = co - > get_shape_count ( ) - 1 ;
2014-02-10 02:10:30 +01:00
}
//co->add_shape(shape,get_transform());
}
void CollisionPolygon2D : : _update_parent ( ) {
2015-09-16 03:07:03 +02:00
if ( ! can_update_body )
return ;
2014-02-10 02:10:30 +01:00
Node * parent = get_parent ( ) ;
if ( ! parent )
return ;
CollisionObject2D * co = parent - > cast_to < CollisionObject2D > ( ) ;
if ( ! co )
return ;
co - > _update_shapes_from_children ( ) ;
}
2017-03-05 16:44:50 +01:00
Vector < Vector < Vector2 > > CollisionPolygon2D : : _decompose_in_convex ( ) {
2016-01-01 00:23:34 +01:00
2017-03-05 16:44:50 +01:00
Vector < Vector < Vector2 > > decomp ;
2016-01-01 00:23:34 +01:00
#if 0
//fast but imprecise triangulator, gave us problems
decomp = Geometry : : decompose_polygon ( polygon ) ;
# else
2017-03-05 16:44:50 +01:00
List < TriangulatorPoly > in_poly , out_poly ;
2016-01-01 00:23:34 +01:00
TriangulatorPoly inp ;
inp . Init ( polygon . size ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < polygon . size ( ) ; i + + ) {
inp . GetPoint ( i ) = polygon [ i ] ;
2016-01-01 00:23:34 +01:00
}
inp . SetOrientation ( TRIANGULATOR_CCW ) ;
in_poly . push_back ( inp ) ;
TriangulatorPartition tpart ;
2017-03-05 16:44:50 +01:00
if ( tpart . ConvexPartition_HM ( & in_poly , & out_poly ) = = 0 ) { //failed!
2016-01-01 00:23:34 +01:00
ERR_PRINT ( " Convex decomposing failed! " ) ;
return decomp ;
}
decomp . resize ( out_poly . size ( ) ) ;
2017-03-05 16:44:50 +01:00
int idx = 0 ;
2016-01-01 00:23:34 +01:00
2017-03-05 16:44:50 +01:00
for ( List < TriangulatorPoly > : : Element * I = out_poly . front ( ) ; I ; I = I - > next ( ) ) {
2016-01-01 00:23:34 +01:00
2017-03-05 16:44:50 +01:00
TriangulatorPoly & tp = I - > get ( ) ;
2016-01-01 00:23:34 +01:00
decomp [ idx ] . resize ( tp . GetNumPoints ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < tp . GetNumPoints ( ) ; i + + ) {
2016-01-01 00:23:34 +01:00
2017-03-05 16:44:50 +01:00
decomp [ idx ] [ i ] = tp . GetPoint ( i ) ;
2016-01-01 00:23:34 +01:00
}
idx + + ;
}
# endif
return decomp ;
}
2014-02-10 02:10:30 +01:00
void CollisionPolygon2D : : _notification ( int p_what ) {
2017-03-05 16:44:50 +01:00
switch ( p_what ) {
2015-03-23 15:31:03 +01:00
case NOTIFICATION_ENTER_TREE : {
2017-03-05 16:44:50 +01:00
unparenting = false ;
can_update_body = get_tree ( ) - > is_editor_hint ( ) ;
2015-12-12 12:11:36 +01:00
if ( ! get_tree ( ) - > is_editor_hint ( ) ) {
//display above all else
set_z_as_relative ( false ) ;
2017-03-05 16:44:50 +01:00
set_z ( VS : : CANVAS_ITEM_Z_MAX - 1 ) ;
2015-12-12 12:11:36 +01:00
}
2015-09-16 03:07:03 +02:00
} break ;
case NOTIFICATION_EXIT_TREE : {
2017-03-05 16:44:50 +01:00
can_update_body = false ;
2015-03-23 15:31:03 +01:00
} break ;
2014-02-10 02:10:30 +01:00
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED : {
2014-11-06 01:20:42 +01:00
if ( ! is_inside_tree ( ) )
2014-02-10 02:10:30 +01:00
break ;
2015-09-16 03:07:03 +02:00
if ( can_update_body ) {
_update_parent ( ) ;
2017-03-05 16:44:50 +01:00
} else if ( shape_from > = 0 & & shape_to > = 0 ) {
2015-09-16 03:07:03 +02:00
CollisionObject2D * co = get_parent ( ) - > cast_to < CollisionObject2D > ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = shape_from ; i < = shape_to ; i + + ) {
co - > set_shape_transform ( i , get_transform ( ) ) ;
2015-09-16 03:07:03 +02:00
}
}
2014-02-10 02:10:30 +01:00
} break ;
case NOTIFICATION_DRAW : {
2015-09-20 18:03:46 +02:00
if ( ! get_tree ( ) - > is_editor_hint ( ) & & ! get_tree ( ) - > is_debugging_collisions_hint ( ) ) {
break ;
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < polygon . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
Vector2 p = polygon [ i ] ;
2017-03-05 16:44:50 +01:00
Vector2 n = polygon [ ( i + 1 ) % polygon . size ( ) ] ;
draw_line ( p , n , Color ( 0.9 , 0.2 , 0.0 , 0.8 ) , 3 ) ;
2014-02-10 02:10:30 +01:00
}
2016-01-11 03:09:05 +01:00
# define DEBUG_DECOMPOSE
2017-03-05 16:44:50 +01:00
# if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE)
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Vector < Vector < Vector2 > > decomp = _decompose_in_convex ( ) ;
2016-01-01 00:23:34 +01:00
2017-03-05 16:44:50 +01:00
Color c ( 0.4 , 0.9 , 0.1 ) ;
for ( int i = 0 ; i < decomp . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
c . set_hsv ( Math : : fmod ( c . get_h ( ) + 0.738 , 1 ) , c . get_s ( ) , c . get_v ( ) , 0.5 ) ;
draw_colored_polygon ( decomp [ i ] , c ) ;
2014-02-10 02:10:30 +01:00
}
2015-09-20 18:03:46 +02:00
# else
2017-03-05 16:44:50 +01:00
draw_colored_polygon ( polygon , get_tree ( ) - > get_debug_collisions_color ( ) ) ;
2014-02-10 02:10:30 +01:00
# endif
2015-09-20 18:03:46 +02:00
2014-02-10 02:10:30 +01:00
} break ;
2015-03-23 15:31:03 +01:00
case NOTIFICATION_UNPARENTED : {
unparenting = true ;
_update_parent ( ) ;
} break ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
void CollisionPolygon2D : : set_polygon ( const Vector < Point2 > & p_polygon ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
polygon = p_polygon ;
2014-02-10 02:10:30 +01:00
2015-09-16 03:07:03 +02:00
if ( can_update_body ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < polygon . size ( ) ; i + + ) {
if ( i = = 0 )
aabb = Rect2 ( polygon [ i ] , Size2 ( ) ) ;
2015-09-16 03:07:03 +02:00
else
aabb . expand_to ( polygon [ i ] ) ;
}
2017-03-05 16:44:50 +01:00
if ( aabb = = Rect2 ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
aabb = Rect2 ( - 10 , - 10 , 20 , 20 ) ;
2015-09-16 03:07:03 +02:00
} else {
2017-03-05 16:44:50 +01:00
aabb . pos - = aabb . size * 0.3 ;
aabb . size + = aabb . size * 0.6 ;
2015-09-16 03:07:03 +02:00
}
_update_parent ( ) ;
2014-02-10 02:10:30 +01:00
}
update ( ) ;
2017-01-22 14:09:41 +01:00
update_configuration_warning ( ) ;
2014-02-10 02:10:30 +01:00
}
Vector < Point2 > CollisionPolygon2D : : get_polygon ( ) const {
return polygon ;
}
void CollisionPolygon2D : : set_build_mode ( BuildMode p_mode ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_mode , 2 ) ;
build_mode = p_mode ;
2014-09-17 02:19:54 +02:00
_update_parent ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
CollisionPolygon2D : : BuildMode CollisionPolygon2D : : get_build_mode ( ) const {
2014-02-10 02:10:30 +01:00
return build_mode ;
}
Rect2 CollisionPolygon2D : : get_item_rect ( ) const {
return aabb ;
}
2014-12-20 19:30:06 +01:00
void CollisionPolygon2D : : set_trigger ( bool p_trigger ) {
2017-03-05 16:44:50 +01:00
trigger = p_trigger ;
2014-12-20 19:30:06 +01:00
_update_parent ( ) ;
2017-03-05 16:44:50 +01:00
if ( ! can_update_body & & is_inside_tree ( ) & & shape_from > = 0 & & shape_to > = 0 ) {
2015-09-16 03:07:03 +02:00
CollisionObject2D * co = get_parent ( ) - > cast_to < CollisionObject2D > ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = shape_from ; i < = shape_to ; i + + ) {
co - > set_shape_as_trigger ( i , p_trigger ) ;
2015-09-16 03:07:03 +02:00
}
}
2014-12-20 19:30:06 +01:00
}
2017-03-05 16:44:50 +01:00
bool CollisionPolygon2D : : is_trigger ( ) const {
2014-12-20 19:30:06 +01:00
return trigger ;
}
2017-03-05 16:44:50 +01:00
void CollisionPolygon2D : : _set_shape_range ( const Vector2 & p_range ) {
2014-12-20 19:30:06 +01:00
2017-03-05 16:44:50 +01:00
shape_from = p_range . x ;
shape_to = p_range . y ;
2015-09-16 03:07:03 +02:00
}
Vector2 CollisionPolygon2D : : _get_shape_range ( ) const {
2017-03-05 16:44:50 +01:00
return Vector2 ( shape_from , shape_to ) ;
2015-09-16 03:07:03 +02:00
}
2016-05-17 23:27:15 +02:00
String CollisionPolygon2D : : get_configuration_warning ( ) const {
if ( ! get_parent ( ) - > cast_to < CollisionObject2D > ( ) ) {
return TTR ( " CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape. " ) ;
}
if ( polygon . empty ( ) ) {
return TTR ( " An empty CollisionPolygon2D has no effect on collision. " ) ;
}
return String ( ) ;
}
2014-02-10 02:10:30 +01:00
void CollisionPolygon2D : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " _add_to_collision_object " ) , & CollisionPolygon2D : : _add_to_collision_object ) ;
ClassDB : : bind_method ( D_METHOD ( " set_polygon " , " polygon " ) , & CollisionPolygon2D : : set_polygon ) ;
ClassDB : : bind_method ( D_METHOD ( " get_polygon " ) , & CollisionPolygon2D : : get_polygon ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_build_mode " , " build_mode " ) , & CollisionPolygon2D : : set_build_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_build_mode " ) , & CollisionPolygon2D : : get_build_mode ) ;
2014-12-20 19:30:06 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_trigger " , " trigger " ) , & CollisionPolygon2D : : set_trigger ) ;
ClassDB : : bind_method ( D_METHOD ( " is_trigger " ) , & CollisionPolygon2D : : is_trigger ) ;
2015-09-16 03:07:03 +02:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " _set_shape_range " , " shape_range " ) , & CollisionPolygon2D : : _set_shape_range ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_shape_range " ) , & CollisionPolygon2D : : _get_shape_range ) ;
2015-09-16 03:07:03 +02:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_collision_object_first_shape " ) , & CollisionPolygon2D : : get_collision_object_first_shape ) ;
ClassDB : : bind_method ( D_METHOD ( " get_collision_object_last_shape " ) , & CollisionPolygon2D : : get_collision_object_last_shape ) ;
2015-09-16 03:07:03 +02:00
2017-03-05 16:44:50 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " build_mode " , PROPERTY_HINT_ENUM , " Solids,Segments " ) , " set_build_mode " , " get_build_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : POOL_VECTOR2_ARRAY , " polygon " ) , " set_polygon " , " get_polygon " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : VECTOR2 , " shape_range " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NOEDITOR ) , " _set_shape_range " , " _get_shape_range " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " trigger " ) , " set_trigger " , " is_trigger " ) ;
2014-02-10 02:10:30 +01:00
}
CollisionPolygon2D : : CollisionPolygon2D ( ) {
2017-03-05 16:44:50 +01:00
aabb = Rect2 ( - 10 , - 10 , 20 , 20 ) ;
build_mode = BUILD_SOLIDS ;
trigger = false ;
unparenting = false ;
shape_from = - 1 ;
shape_to = - 1 ;
can_update_body = false ;
2015-09-16 03:07:03 +02:00
set_notify_local_transform ( true ) ;
2014-02-10 02:10:30 +01:00
}