bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Matrix32& p_shape_xform,const Vector2& p_motion,float p_margin,ShapeRestInfo *r_info, const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask) { Shape2DSW *shape = static_cast<Physics2DServerSW*>(Physics2DServer::get_singleton())->shape_owner.get(p_shape); ERR_FAIL_COND_V(!shape,0); Rect2 aabb = p_shape_xform.xform(shape->get_aabb()); aabb=aabb.merge(Rect2(aabb.pos+p_motion,aabb.size)); //motion aabb=aabb.grow(p_margin); int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,Space2DSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results); _RestCallbackData rcd; rcd.best_len=0; rcd.best_object=NULL; rcd.best_shape=0; for(int i=0;i<amount;i++) { if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask)) continue; const CollisionObject2DSW *col_obj=space->intersection_query_results[i]; int shape_idx=space->intersection_query_subindex_results[i]; if (p_exclude.has( col_obj->get_self() )) continue; rcd.object=col_obj; rcd.shape=shape_idx; bool sc = CollisionSolver2DSW::solve(shape,p_shape_xform,p_motion,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2() ,_rest_cbk_result,&rcd,NULL,p_margin); if (!sc) continue; } if (rcd.best_len==0) return false; r_info->collider_id=rcd.best_object->get_instance_id(); r_info->shape=rcd.best_shape; r_info->normal=rcd.best_normal; r_info->point=rcd.best_contact; r_info->rid=rcd.best_object->get_self(); if (rcd.best_object->get_type()==CollisionObject2DSW::TYPE_BODY) { const Body2DSW *body = static_cast<const Body2DSW*>(rcd.best_object); Vector2 rel_vec = r_info->point-body->get_transform().get_origin(); r_info->linear_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity(); } else { r_info->linear_velocity=Vector2(); } return true; }
bool Physics2DDirectSpaceStateSW::collide_shape(RID p_shape, const Matrix32& p_shape_xform,const Vector2& p_motion,float p_margin,Vector2 *r_results,int p_result_max,int &r_result_count, const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask) { if (p_result_max<=0) return 0; Shape2DSW *shape = static_cast<Physics2DServerSW*>(Physics2DServer::get_singleton())->shape_owner.get(p_shape); ERR_FAIL_COND_V(!shape,0); Rect2 aabb = p_shape_xform.xform(shape->get_aabb()); aabb=aabb.merge(Rect2(aabb.pos+p_motion,aabb.size)); //motion aabb=aabb.grow(p_margin); int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,Space2DSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results); bool collided=false; int cc=0; r_result_count=0; Physics2DServerSW::CollCbkData cbk; cbk.max=p_result_max; cbk.amount=0; cbk.ptr=r_results; CollisionSolver2DSW::CallbackResult cbkres=NULL; Physics2DServerSW::CollCbkData *cbkptr=NULL; if (p_result_max>0) { cbkptr=&cbk; cbkres=Physics2DServerSW::_shape_col_cbk; } for(int i=0;i<amount;i++) { if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask)) continue; const CollisionObject2DSW *col_obj=space->intersection_query_results[i]; int shape_idx=space->intersection_query_subindex_results[i]; if (p_exclude.has( col_obj->get_self() )) continue; if (CollisionSolver2DSW::solve(shape,p_shape_xform,p_motion,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2(),cbkres,cbkptr,NULL,p_margin)) { collided=true; } } r_result_count=cbk.amount; return collided; }
int Physics2DDirectSpaceStateSW::intersect_shape(const RID& p_shape, const Matrix32& p_xform,const Vector2& p_motion,float p_margin,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask) { if (p_result_max<=0) return 0; Shape2DSW *shape = static_cast<Physics2DServerSW*>(Physics2DServer::get_singleton())->shape_owner.get(p_shape); ERR_FAIL_COND_V(!shape,0); Rect2 aabb = p_xform.xform(shape->get_aabb()); aabb=aabb.grow(p_margin); int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,Space2DSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results); bool collided=false; int cc=0; for(int i=0; i<amount; i++) { if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask)) continue; if (p_exclude.has( space->intersection_query_results[i]->get_self())) continue; const CollisionObject2DSW *col_obj=space->intersection_query_results[i]; int shape_idx=space->intersection_query_subindex_results[i]; if (!CollisionSolver2DSW::solve(shape,p_xform,p_motion,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2(),NULL,NULL,NULL,p_margin)) continue; r_results[cc].collider_id=col_obj->get_instance_id(); if (r_results[cc].collider_id!=0) r_results[cc].collider=ObjectDB::get_instance(r_results[cc].collider_id); r_results[cc].rid=col_obj->get_self(); r_results[cc].shape=shape_idx; r_results[cc].metadata=col_obj->get_shape_metadata(shape_idx); cc++; } return cc; }
void TileMapEditor::_canvas_draw() { if (!node) return; Matrix32 cell_xf = node->get_cell_transform(); Matrix32 xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform(); Matrix32 xform_inv = xform.affine_inverse(); Size2 screen_size=canvas_item_editor->get_size(); { Rect2 aabb; aabb.pos=node->world_to_map(xform_inv.xform(Vector2())); aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(0,screen_size.height)))); aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(screen_size.width,0)))); aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size))); Rect2i si=aabb.grow(1.0); if (node->get_half_offset()!=TileMap::HALF_OFFSET_X) { int max_lines=2000; //avoid crash if size too smal for (int i=(si.pos.x)-1;i<=(si.pos.x+si.size.x);i++) { Vector2 from = xform.xform(node->map_to_world(Vector2(i,si.pos.y))); Vector2 to = xform.xform(node->map_to_world(Vector2(i,si.pos.y+si.size.y+1))); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); if (max_lines--==0) break; } } else { int max_lines=10000; //avoid crash if size too smal for (int i=(si.pos.x)-1;i<=(si.pos.x+si.size.x);i++) { for (int j=(si.pos.y)-1;j<=(si.pos.y+si.size.y);j++) { Vector2 ofs; if (ABS(j)&1) { ofs=cell_xf[0]*0.5; } Vector2 from = xform.xform(node->map_to_world(Vector2(i,j),true)+ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(i,j+1),true)+ofs); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); if (max_lines--==0) break; } } } int max_lines=10000; //avoid crash if size too smal if (node->get_half_offset()!=TileMap::HALF_OFFSET_Y) { for (int i=(si.pos.y)-1;i<=(si.pos.y+si.size.y);i++) { Vector2 from = xform.xform(node->map_to_world(Vector2(si.pos.x,i))); Vector2 to = xform.xform(node->map_to_world(Vector2(si.pos.x+si.size.x+1,i))); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); if (max_lines--==0) break; } } else { for (int i=(si.pos.y)-1;i<=(si.pos.y+si.size.y);i++) { for (int j=(si.pos.x)-1;j<=(si.pos.x+si.size.x);j++) { Vector2 ofs; if (ABS(j)&1) { ofs=cell_xf[1]*0.5; } Vector2 from = xform.xform(node->map_to_world(Vector2(j,i),true)+ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(j+1,i),true)+ofs); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); if (max_lines--==0) break; } } } } if (selection_active) { Vector<Vector2> points; points.push_back( xform.xform( node->map_to_world(( rectangle.pos ) ))); points.push_back( xform.xform( node->map_to_world((rectangle.pos+Point2(rectangle.size.x+1,0)) ) )); points.push_back( xform.xform( node->map_to_world((rectangle.pos+Point2(rectangle.size.x+1,rectangle.size.y+1)) ) )); points.push_back( xform.xform( node->map_to_world((rectangle.pos+Point2(0,rectangle.size.y+1)) ) )); canvas_item_editor->draw_colored_polygon(points, Color(0.2,0.8,1,0.4)); } if (mouse_over){ Vector2 endpoints[4]={ node->map_to_world(over_tile, true), node->map_to_world((over_tile+Point2(1,0)), true), node->map_to_world((over_tile+Point2(1,1)), true), node->map_to_world((over_tile+Point2(0,1)), true) }; for (int i=0;i<4;i++) { if (node->get_half_offset()==TileMap::HALF_OFFSET_X && ABS(over_tile.y)&1) endpoints[i]+=cell_xf[0]*0.5; if (node->get_half_offset()==TileMap::HALF_OFFSET_Y && ABS(over_tile.x)&1) endpoints[i]+=cell_xf[1]*0.5; endpoints[i]=xform.xform(endpoints[i]); } Color col; if (node->get_cell(over_tile.x,over_tile.y)!=TileMap::INVALID_CELL) col=Color(0.2,0.8,1.0,0.8); else col=Color(1.0,0.4,0.2,0.8); for (int i=0;i<4;i++) canvas_item_editor->draw_line(endpoints[i],endpoints[(i+1)%4],col,2); if (tool==TOOL_SELECTING || tool==TOOL_PICKING || tool==TOOL_BUCKET) { return; } if (tool==TOOL_LINE_PAINT) { if (paint_undo.empty()) return; int id = get_selected_tile(); if (id==TileMap::INVALID_CELL) return; for (Map<Point2i, CellOp>::Element *E=paint_undo.front();E;E=E->next()) { _draw_cell(id, E->key(), flip_h, flip_v, transpose, xform); } } else if (tool==TOOL_RECTANGLE_PAINT) { int id = get_selected_tile(); if (id==TileMap::INVALID_CELL) return; for (int i=rectangle.pos.y;i<=rectangle.pos.y+rectangle.size.y;i++) { for (int j=rectangle.pos.x;j<=rectangle.pos.x+rectangle.size.x;j++) { _draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform); } } } else if (tool==TOOL_DUPLICATING) { if (copydata.empty()) return; Ref<TileSet> ts = node->get_tileset(); if (ts.is_null()) return; Point2 ofs = over_tile-rectangle.pos; for (List<TileData>::Element *E=copydata.front();E;E=E->next()) { if (!ts->has_tile(E->get().cell)) continue; TileData tcd = E->get(); _draw_cell(tcd.cell, tcd.pos+ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform); } Rect2i duplicate=rectangle; duplicate.pos=over_tile; Vector<Vector2> points; points.push_back( xform.xform( node->map_to_world(duplicate.pos ) )); points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(duplicate.size.x+1,0)) ) )); points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(duplicate.size.x+1,duplicate.size.y+1))) )); points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(0,duplicate.size.y+1))) )); canvas_item_editor->draw_colored_polygon(points, Color(0.2,1.0,0.8,0.2)); } else { int st = get_selected_tile(); if (st==TileMap::INVALID_CELL) return; _draw_cell(st, over_tile, flip_h, flip_v, transpose, xform); } } }
bool Physics2DDirectSpaceStateSW::cast_motion(const RID& p_shape, const Matrix32& p_xform,const Vector2& p_motion,float p_margin,float &p_closest_safe,float &p_closest_unsafe, const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask) { Shape2DSW *shape = static_cast<Physics2DServerSW*>(Physics2DServer::get_singleton())->shape_owner.get(p_shape); ERR_FAIL_COND_V(!shape,false); Rect2 aabb = p_xform.xform(shape->get_aabb()); aabb=aabb.merge(Rect2(aabb.pos+p_motion,aabb.size)); //motion aabb=aabb.grow(p_margin); //if (p_motion!=Vector2()) // print_line(p_motion); int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,Space2DSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results); float best_safe=1; float best_unsafe=1; for(int i=0; i<amount; i++) { if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask)) continue; if (p_exclude.has( space->intersection_query_results[i]->get_self())) continue; //ignore excluded const CollisionObject2DSW *col_obj=space->intersection_query_results[i]; int shape_idx=space->intersection_query_subindex_results[i]; /*if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) { const Body2DSW *body=static_cast<const Body2DSW*>(col_obj); if (body->get_one_way_collision_direction()!=Vector2() && p_motion.dot(body->get_one_way_collision_direction())<=CMP_EPSILON) { print_line("failed in motion dir"); continue; } }*/ Matrix32 col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx); //test initial overlap, does it collide if going all the way? if (!CollisionSolver2DSW::solve(shape,p_xform,p_motion,col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,p_margin)) { continue; } //test initial overlap if (CollisionSolver2DSW::solve(shape,p_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,p_margin)) { if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) { //if one way collision direction ignore initial overlap const Body2DSW *body=static_cast<const Body2DSW*>(col_obj); if (body->get_one_way_collision_direction()!=Vector2()) { continue; } } return false; } //just do kinematic solving float low=0; float hi=1; Vector2 mnormal=p_motion.normalized(); for(int i=0; i<8; i++) { //steps should be customizable.. Matrix32 xfa = p_xform; float ofs = (low+hi)*0.5; Vector2 sep=mnormal; //important optimization for this to work fast enough bool collided = CollisionSolver2DSW::solve(shape,p_xform,p_motion*ofs,col_obj->get_shape(shape_idx),col_obj_xform,Vector2(),NULL,NULL,&sep,p_margin); if (collided) { hi=ofs; } else { low=ofs; } } if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) { const Body2DSW *body=static_cast<const Body2DSW*>(col_obj); if (body->get_one_way_collision_direction()!=Vector2()) { Vector2 cd[2]; Physics2DServerSW::CollCbkData cbk; cbk.max=1; cbk.amount=0; cbk.ptr=cd; cbk.valid_dir=body->get_one_way_collision_direction(); cbk.valid_depth=body->get_one_way_collision_max_depth(); Vector2 sep=mnormal; //important optimization for this to work fast enough bool collided = CollisionSolver2DSW::solve(shape,p_xform,p_motion*(hi+space->contact_max_allowed_penetration),col_obj->get_shape(shape_idx),col_obj_xform,Vector2(),Physics2DServerSW::_shape_col_cbk,&cbk,&sep,p_margin); if (!collided || cbk.amount==0) { continue; } } } if (low<best_safe) { best_safe=low; best_unsafe=hi; } } p_closest_safe=best_safe; p_closest_unsafe=best_unsafe; return true; }
bool Physics2DDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_layer, uint32_t p_object_type_mask) { Shape2DSW *shape = Physics2DServerSW::singletonsw->shape_owner.get(p_shape); ERR_FAIL_COND_V(!shape, false); Rect2 aabb = p_xform.xform(shape->get_aabb()); aabb = aabb.merge(Rect2(aabb.position + p_motion, aabb.size)); //motion aabb = aabb.grow(p_margin); /* if (p_motion!=Vector2()) print_line(p_motion); */ int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, Space2DSW::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results); real_t best_safe = 1; real_t best_unsafe = 1; for (int i = 0; i < amount; i++) { if (!_match_object_type_query(space->intersection_query_results[i], p_collision_layer, p_object_type_mask)) continue; if (p_exclude.has(space->intersection_query_results[i]->get_self())) continue; //ignore excluded const CollisionObject2DSW *col_obj = space->intersection_query_results[i]; int shape_idx = space->intersection_query_subindex_results[i]; /*if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) { const Body2DSW *body=static_cast<const Body2DSW*>(col_obj); if (body->get_one_way_collision_direction()!=Vector2() && p_motion.dot(body->get_one_way_collision_direction())<=CMP_EPSILON) { print_line("failed in motion dir"); continue; } }*/ Transform2D col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx); //test initial overlap, does it collide if going all the way? if (!CollisionSolver2DSW::solve(shape, p_xform, p_motion, col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), NULL, NULL, NULL, p_margin)) { continue; } //test initial overlap if (CollisionSolver2DSW::solve(shape, p_xform, Vector2(), col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), NULL, NULL, NULL, p_margin)) { return false; } //just do kinematic solving real_t low = 0; real_t hi = 1; Vector2 mnormal = p_motion.normalized(); for (int i = 0; i < 8; i++) { //steps should be customizable.. real_t ofs = (low + hi) * 0.5; Vector2 sep = mnormal; //important optimization for this to work fast enough bool collided = CollisionSolver2DSW::solve(shape, p_xform, p_motion * ofs, col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), NULL, NULL, &sep, p_margin); if (collided) { hi = ofs; } else { low = ofs; } } if (low < best_safe) { best_safe = low; best_unsafe = hi; } } p_closest_safe = best_safe; p_closest_unsafe = best_unsafe; return true; }
void TileMapEditor::_canvas_draw() { if (!node) return; Size2 cell_size=node->get_cell_size(); Matrix32 cell_xf = node->get_cell_transform(); Matrix32 xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform(); Matrix32 xform_inv = xform.affine_inverse(); Size2 screen_size=canvas_item_editor->get_size(); { Rect2 aabb; aabb.pos=node->world_to_map(xform_inv.xform(Vector2())); aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(0,screen_size.height)))); aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(screen_size.width,0)))); aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size))); Rect2i si=aabb.grow(1.0); if (node->get_half_offset()!=TileMap::HALF_OFFSET_X) { for(int i=(si.pos.x)-1;i<=(si.pos.x+si.size.x);i++) { Vector2 from = xform.xform(node->map_to_world(Vector2(i,si.pos.y))); Vector2 to = xform.xform(node->map_to_world(Vector2(i,si.pos.y+si.size.y+1))); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); } } else { for(int i=(si.pos.x)-1;i<=(si.pos.x+si.size.x);i++) { for(int j=(si.pos.y)-1;j<=(si.pos.y+si.size.y);j++) { Vector2 ofs; if (ABS(j)&1) { ofs=cell_xf[0]*0.5; } Vector2 from = xform.xform(node->map_to_world(Vector2(i,j),true)+ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(i,j+1),true)+ofs); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); } } } if (node->get_half_offset()!=TileMap::HALF_OFFSET_Y) { for(int i=(si.pos.y)-1;i<=(si.pos.y+si.size.y);i++) { Vector2 from = xform.xform(node->map_to_world(Vector2(si.pos.x,i))); Vector2 to = xform.xform(node->map_to_world(Vector2(si.pos.x+si.size.x+1,i))); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); } } else { for(int i=(si.pos.y)-1;i<=(si.pos.y+si.size.y);i++) { for(int j=(si.pos.x)-1;j<=(si.pos.x+si.size.x);j++) { Vector2 ofs; if (ABS(j)&1) { ofs=cell_xf[1]*0.5; } Vector2 from = xform.xform(node->map_to_world(Vector2(j,i),true)+ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(j+1,i),true)+ofs); Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(from,to,col,1); } } } /* for(int i=(si.pos.y/cell_size.y)-1;i<=(si.pos.y+si.size.y)/cell_size.y;i++) { int ofs = i*cell_size.y; Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); canvas_item_editor->draw_line(xform.xform(Point2(si.pos.x,ofs)),xform.xform(Point2(si.pos.x+si.size.x,ofs)),col,1);*/ } if (selection_active) { Vector<Vector2> points; points.push_back( xform.xform( node->map_to_world(( selection.pos ) ))); points.push_back( xform.xform( node->map_to_world((selection.pos+Point2(selection.size.x+1,0)) ) )); points.push_back( xform.xform( node->map_to_world((selection.pos+Point2(selection.size.x+1,selection.size.y+1)) ) )); points.push_back( xform.xform( node->map_to_world((selection.pos+Point2(0,selection.size.y+1)) ) )); Color col=Color(0.2,0.8,1,0.4); canvas_item_editor->draw_colored_polygon(points,col); } if (mouse_over){ Vector2 endpoints[4]={ ( node->map_to_world(over_tile,true) ) , ( node->map_to_world((over_tile+Point2(1,0)),true ) ), ( node->map_to_world((over_tile+Point2(1,1)),true ) ), ( node->map_to_world((over_tile+Point2(0,1)),true ) ) }; for(int i=0;i<4;i++) { if (node->get_half_offset()==TileMap::HALF_OFFSET_X && ABS(over_tile.y)&1) endpoints[i]+=cell_xf[0]*0.5; if (node->get_half_offset()==TileMap::HALF_OFFSET_Y && ABS(over_tile.x)&1) endpoints[i]+=cell_xf[1]*0.5; endpoints[i]=xform.xform(endpoints[i]); } Color col; if (node->get_cell(over_tile.x,over_tile.y)!=TileMap::INVALID_CELL) col=Color(0.2,0.8,1.0,0.8); else col=Color(1.0,0.4,0.2,0.8); for(int i=0;i<4;i++) canvas_item_editor->draw_line(endpoints[i],endpoints[(i+1)%4],col,2); if (tool==TOOL_DUPLICATING) { Rect2i duplicate=selection; duplicate.pos=over_tile; Vector<Vector2> points; points.push_back( xform.xform( node->map_to_world(duplicate.pos ) )); points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(duplicate.size.x+1,0)) ) )); points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(duplicate.size.x+1,duplicate.size.y+1))) )); points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(0,duplicate.size.y+1))) )); Color col=Color(0.2,1.0,0.8,0.4); canvas_item_editor->draw_colored_polygon(points,col); } else { Ref<TileSet> ts = node->get_tileset(); if (ts.is_valid()) { int st = get_selected_tile(); if (ts->has_tile(st)) { Ref<Texture> t = ts->tile_get_texture(st); if (t.is_valid()) { Vector2 from = node->map_to_world(over_tile)+node->get_cell_draw_offset(); Rect2 r = ts->tile_get_region(st); Size2 sc = xform.get_scale(); if (mirror_x->is_pressed()) sc.x*=-1.0; if (mirror_y->is_pressed()) sc.y*=-1.0; Rect2 rect; if (r==Rect2()) { rect=Rect2(from,t->get_size()); } else { rect=Rect2(from,r.get_size()); } if (node->get_tile_origin()==TileMap::TILE_ORIGIN_TOP_LEFT) { rect.pos+=ts->tile_get_texture_offset(st); } else if (node->get_tile_origin()==TileMap::TILE_ORIGIN_CENTER) { rect.pos+=node->get_cell_size()/2; Vector2 s = r.size; Vector2 center = (s/2) - ts->tile_get_texture_offset(st); if (mirror_x->is_pressed()) rect.pos.x-=s.x-center.x; else rect.pos.x-=center.x; if (mirror_y->is_pressed()) rect.pos.y-=s.y-center.y; else rect.pos.y-=center.y; } rect.pos=xform.xform(rect.pos); rect.size*=sc; if (r==Rect2()) { canvas_item_editor->draw_texture_rect(t,rect,false,Color(1,1,1,0.5),transpose->is_pressed()); } else { canvas_item_editor->draw_texture_rect_region(t,rect,r,Color(1,1,1,0.5),transpose->is_pressed()); } } } } } } }
void CollisionPolygonEditor::_polygon_draw() { if (!node) return; Vector<Vector2> poly; if (wip_active) poly=wip; else poly=node->get_polygon(); int len = poly.size(); float depth = node->get_depth()*0.5; imgeom->clear(); imgeom->set_material_override(line_material); imgeom->begin(Mesh::PRIMITIVE_LINES,Ref<Texture>()); Rect2 rect; for(int i=0;i<poly.size();i++) { Vector2 p,p2; p = i==edited_point ? edited_point_pos : poly[i]; if ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point)) p2=edited_point_pos; else p2 = poly[(i+1)%poly.size()]; if (i==0) rect.pos=p; else rect.expand_to(p); Vector3 point = Vector3(p.x,p.y,depth); Vector3 next_point = Vector3(p2.x,p2.y,depth); imgeom->set_color(Color(1,0.3,0.1,0.8)); imgeom->add_vertex(point); imgeom->set_color(Color(1,0.3,0.1,0.8)); imgeom->add_vertex(next_point); //Color col=Color(1,0.3,0.1,0.8); //vpc->draw_line(point,next_point,col,2); //vpc->draw_texture(handle,point-handle->get_size()*0.5); } rect=rect.grow(1); AABB r; r.pos.x=rect.pos.x; r.pos.y=rect.pos.y; r.pos.z=depth; r.size.x=rect.size.x; r.size.y=rect.size.y; r.size.z=0; imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(0.3,0,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(0.0,0.3,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)-Vector3(0.3,0,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)+Vector3(0,0.3,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)-Vector3(0,0.3,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)+Vector3(0.3,0,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+r.size); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+r.size-Vector3(0.3,0,0)); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+r.size); imgeom->set_color(Color(0.8,0.8,0.8,0.2)); imgeom->add_vertex(r.pos+r.size-Vector3(0.0,0.3,0)); imgeom->end(); while(m->get_surface_count()) { m->surface_remove(0); } if (poly.size()==0) return; Array a; a.resize(Mesh::ARRAY_MAX); DVector<Vector3> va; { va.resize(poly.size()); DVector<Vector3>::Write w=va.write(); for(int i=0;i<poly.size();i++) { Vector2 p,p2; p = i==edited_point ? edited_point_pos : poly[i]; Vector3 point = Vector3(p.x,p.y,depth); w[i]=point; } } a[Mesh::ARRAY_VERTEX]=va; m->add_surface(Mesh::PRIMITIVE_POINTS,a); m->surface_set_material(0,handle_material); }
void Polygon2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { if (polygon.size() < 3) return; Skeleton2D *skeleton_node = NULL; if (has_node(skeleton)) { skeleton_node = Object::cast_to<Skeleton2D>(get_node(skeleton)); } ObjectID new_skeleton_id = 0; if (skeleton_node) { VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton()); new_skeleton_id = skeleton_node->get_instance_id(); } else { VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID()); } if (new_skeleton_id != current_skeleton_id) { Object *old_skeleton = ObjectDB::get_instance(current_skeleton_id); if (old_skeleton) { old_skeleton->disconnect("bone_setup_changed", this, "_skeleton_bone_setup_changed"); } if (skeleton_node) { skeleton_node->connect("bone_setup_changed", this, "_skeleton_bone_setup_changed"); } current_skeleton_id = new_skeleton_id; } Vector<Vector2> points; Vector<Vector2> uvs; Vector<int> bones; Vector<float> weights; int len = polygon.size(); if ((invert || polygons.size() == 0) && internal_vertices > 0) { //if no polygons are around, internal vertices must not be drawn, else let them be len -= internal_vertices; } if (len <= 0) { return; } points.resize(len); { PoolVector<Vector2>::Read polyr = polygon.read(); for (int i = 0; i < len; i++) { points.write[i] = polyr[i] + offset; } } if (invert) { Rect2 bounds; int highest_idx = -1; float highest_y = -1e20; float sum = 0; for (int i = 0; i < len; i++) { if (i == 0) bounds.position = points[i]; else bounds.expand_to(points[i]); if (points[i].y > highest_y) { highest_idx = i; highest_y = points[i].y; } int ni = (i + 1) % len; sum += (points[ni].x - points[i].x) * (points[ni].y + points[i].y); } bounds = bounds.grow(invert_border); Vector2 ep[7] = { Vector2(points[highest_idx].x, points[highest_idx].y + invert_border), Vector2(bounds.position + bounds.size), Vector2(bounds.position + Vector2(bounds.size.x, 0)), Vector2(bounds.position), Vector2(bounds.position + Vector2(0, bounds.size.y)), Vector2(points[highest_idx].x - CMP_EPSILON, points[highest_idx].y + invert_border), Vector2(points[highest_idx].x - CMP_EPSILON, points[highest_idx].y), }; if (sum > 0) { SWAP(ep[1], ep[4]); SWAP(ep[2], ep[3]); SWAP(ep[5], ep[0]); SWAP(ep[6], points.write[highest_idx]); } points.resize(points.size() + 7); for (int i = points.size() - 1; i >= highest_idx + 7; i--) { points.write[i] = points[i - 7]; } for (int i = 0; i < 7; i++) { points.write[highest_idx + i + 1] = ep[i]; } len = points.size(); } if (texture.is_valid()) { Transform2D texmat(tex_rot, tex_ofs); texmat.scale(tex_scale); Size2 tex_size = texture->get_size(); uvs.resize(len); if (points.size() == uv.size()) { PoolVector<Vector2>::Read uvr = uv.read(); for (int i = 0; i < len; i++) { uvs.write[i] = texmat.xform(uvr[i]) / tex_size; } } else { for (int i = 0; i < len; i++) { uvs.write[i] = texmat.xform(points[i]) / tex_size; } } } if (skeleton_node && !invert && bone_weights.size()) { //a skeleton is set! fill indices and weights int vc = len; bones.resize(vc * 4); weights.resize(vc * 4); int *bonesw = bones.ptrw(); float *weightsw = weights.ptrw(); for (int i = 0; i < vc * 4; i++) { bonesw[i] = 0; weightsw[i] = 0; } for (int i = 0; i < bone_weights.size(); i++) { if (bone_weights[i].weights.size() != points.size()) { continue; //different number of vertices, sorry not using. } if (!skeleton_node->has_node(bone_weights[i].path)) { continue; //node does not exist } Bone2D *bone = Object::cast_to<Bone2D>(skeleton_node->get_node(bone_weights[i].path)); if (!bone) { continue; } int bone_index = bone->get_index_in_skeleton(); PoolVector<float>::Read r = bone_weights[i].weights.read(); for (int j = 0; j < vc; j++) { if (r[j] == 0.0) continue; //weight is unpainted, skip //find an index with a weight for (int k = 0; k < 4; k++) { if (weightsw[j * 4 + k] < r[j]) { //this is less than this weight, insert weight! for (int l = 3; l > k; l--) { weightsw[j * 4 + l] = weightsw[j * 4 + l - 1]; bonesw[j * 4 + l] = bonesw[j * 4 + l - 1]; } weightsw[j * 4 + k] = r[j]; bonesw[j * 4 + k] = bone_index; break; } } } } //normalize the weights for (int i = 0; i < vc; i++) { float tw = 0; for (int j = 0; j < 4; j++) { tw += weightsw[i * 4 + j]; } if (tw == 0) continue; //unpainted, do nothing //normalize for (int j = 0; j < 4; j++) { weightsw[i * 4 + j] /= tw; } } } Vector<Color> colors; if (vertex_colors.size() == points.size()) { colors.resize(len); PoolVector<Color>::Read color_r = vertex_colors.read(); for (int i = 0; i < len; i++) { colors.write[i] = color_r[i]; } } else { colors.push_back(color); } // Vector<int> indices = Geometry::triangulate_polygon(points); // VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID()); if (invert || polygons.size() == 0) { Vector<int> indices = Geometry::triangulate_polygon(points); VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID()); } else { //draw individual polygons Vector<int> total_indices; for (int i = 0; i < polygons.size(); i++) { PoolVector<int> src_indices = polygons[i]; int ic = src_indices.size(); if (ic < 3) continue; PoolVector<int>::Read r = src_indices.read(); Vector<Vector2> tmp_points; tmp_points.resize(ic); for (int j = 0; j < ic; j++) { int idx = r[j]; ERR_CONTINUE(idx < 0 || idx >= points.size()); tmp_points.write[j] = points[r[j]]; } Vector<int> indices = Geometry::triangulate_polygon(tmp_points); int ic2 = indices.size(); const int *r2 = indices.ptr(); int bic = total_indices.size(); total_indices.resize(bic + ic2); int *w2 = total_indices.ptrw(); for (int j = 0; j < ic2; j++) { w2[j + bic] = r[r2[j]]; } } if (total_indices.size()) { VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), total_indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID()); } #if 0 //use splits Vector<int> loop; int sc = splits.size(); PoolVector<int>::Read r = splits.read(); print_line("has splits, amount " + itos(splits.size())); Vector<Vector<int> > loops; // find a point that can be used to begin, must not be in a split, and have to the left and right the same one // like this one -> x---o // \ / \ . // o---o int base_point = -1; { int current_point = -1; int base_point_prev_split = -1; for (int i = 0; i < points.size(); i++) { //find if this point is in a split int split_index = -1; bool has_prev_split = false; int min_dist_to_end = 0x7FFFFFFF; for (int j = 0; j < sc; j += 2) { int split_pos = -1; int split_end = -1; if (r[j + 0] == i) { //found split in first point split_pos = r[j + 0]; split_end = r[j + 1]; } else if (r[j + 1] == i) { //found split in second point split_pos = r[j + 1]; split_end = r[j + 0]; } if (split_pos == split_end) { continue; //either nothing found or begin == end, this not a split in either case } if (j == base_point_prev_split) { has_prev_split = true; } //compute distance from split pos to split end in current traversal direction int dist_to_end = split_end > split_pos ? split_end - split_pos : (last - split_pos + split_end); if (dist_to_end < min_dist_to_end) { //always keep the valid split with the least distance to the loop min_dist_to_end = dist_to_end; split_index = j; } } if (split_index == -1) { current_point = i; //no split here, we are testing this point } else if (has_prev_split) { base_point = current_point; // there is a split and it contains the previous visited split, success break; } else { //invalidate current point and keep split current_point = -1; base_point_prev_split = split_index; } } } print_line("found base point: " + itos(base_point)); if (base_point != -1) { int point = base_point; int last = base_point; //go through all the points, find splits do { int split; int last_dist_to_end = -1; //maximum valid distance to end do { loop.push_back(point); //push current point split = -1; int end = -1; int max_dist_to_end = 0; //find if this point is in a split for (int j = 0; j < sc; j += 2) { int split_pos = -1; int split_end = -1; if (r[j + 0] == point) { //match first split index split_pos = r[j + 0]; split_end = r[j + 1]; } else if (r[j + 1] == point) { //match second split index split_pos = r[j + 1]; split_end = r[j + 0]; } if (split_pos == split_end) { continue; //either nothing found or begin == end, this not a split in either case } //compute distance from split pos to split end int dist_to_end = split_end > split_pos ? split_end - split_pos : (points.size() - split_pos + split_end); if (last_dist_to_end != -1 && dist_to_end >= last_dist_to_end) { //distance must be shorter than in last iteration, means we've tested this before so ignore continue; } else if (dist_to_end > max_dist_to_end) { //always keep the valid point with the most distance (as long as it's valid) max_dist_to_end = dist_to_end; split = split_pos; end = split_end; } } if (split != -1) { //found a split! int from = end; //add points until last is reached while (true) { //find if point is in a split loop.push_back(from); if (from == last) { break; } from++; if (from >= points.size()) { //wrap if reached end from = 0; } if (from == loop[0]) { break; //end because we reached split source } } loops.push_back(loop); //done with this loop loop.clear(); last_dist_to_end = max_dist_to_end; last = end; //algorithm can safely finish in this split point } } while (split != -1); } while (point != last); } if (loop.size() >=2 ) { //points remained //points remain loop.push_back(last); //no splits found, use last loops.push_back(loop); } print_line("total loops: " + itos(loops.size())); if (loops.size()) { //loops found Vector<int> indices; for (int i = 0; i < loops.size(); i++) { Vector<int> loop = loops[i]; Vector<Vector2> vertices; vertices.resize(loop.size()); for (int j = 0; j < vertices.size(); j++) { vertices.write[j] = points[loop[j]]; } Vector<int> sub_indices = Geometry::triangulate_polygon(vertices); int from = indices.size(); indices.resize(from + sub_indices.size()); for (int j = 0; j < sub_indices.size(); j++) { indices.write[from + j] = loop[sub_indices[j]]; } } VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID()); } #endif } } break; } }
void GradientEdit::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { if (!picker->is_connected("color_changed", this, "_color_changed")) { picker->connect("color_changed", this, "_color_changed"); } } if (p_what == NOTIFICATION_DRAW) { int w = get_size().x; int h = get_size().y; if (w == 0 || h == 0) return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size int total_w = get_size().width - get_size().height - SPACING; //Draw checker pattern for ramp _draw_checker(0, 0, total_w, h); //Draw color ramp Gradient::Point prev; prev.offset = 0; if (points.size() == 0) prev.color = Color(0, 0, 0); //Draw black rectangle if we have no points else prev.color = points[0].color; //Extend color of first point to the beginning. for (int i = -1; i < points.size(); i++) { Gradient::Point next; //If there is no next point if (i + 1 == points.size()) { if (points.size() == 0) next.color = Color(0, 0, 0); //Draw black rectangle if we have no points else next.color = points[i].color; //Extend color of last point to the end. next.offset = 1; } else { next = points[i + 1]; } if (prev.offset == next.offset) { prev = next; continue; } Vector<Vector2> points; Vector<Color> colors; points.push_back(Vector2(prev.offset * total_w, h)); points.push_back(Vector2(prev.offset * total_w, 0)); points.push_back(Vector2(next.offset * total_w, 0)); points.push_back(Vector2(next.offset * total_w, h)); colors.push_back(prev.color); colors.push_back(prev.color); colors.push_back(next.color); colors.push_back(next.color); draw_primitive(points, colors, Vector<Point2>()); prev = next; } //Draw point markers for (int i = 0; i < points.size(); i++) { Color col = points[i].color.contrasted(); col.a = 0.9; draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col); Rect2 rect = Rect2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2, POINT_WIDTH, h / 2); draw_rect(rect, points[i].color, true); draw_rect(rect, col, false); if (grabbed == i) { rect = rect.grow(-1); if (has_focus()) { draw_rect(rect, Color(1, 0, 0, 0.9), false); } else { draw_rect(rect, Color(0.6, 0, 0, 0.9), false); } rect = rect.grow(-1); draw_rect(rect, col, false); } } //Draw "button" for color selector _draw_checker(total_w + SPACING, 0, h, h); if (grabbed != -1) { //Draw with selection color draw_rect(Rect2(total_w + SPACING, 0, h, h), points[grabbed].color); } else { //if no color selected draw grey color with 'X' on top. draw_rect(Rect2(total_w + SPACING, 0, h, h), Color(0.5, 0.5, 0.5, 1)); draw_line(Vector2(total_w + SPACING, 0), Vector2(total_w + SPACING + h, h), Color(1, 1, 1, 0.6)); draw_line(Vector2(total_w + SPACING, h), Vector2(total_w + SPACING + h, 0), Color(1, 1, 1, 0.6)); } //Draw borders around color ramp if in focus if (has_focus()) { draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6)); draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6)); draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); } } if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { if (!is_visible()) { grabbing = false; } } }
bool Physics2DDirectSpaceStateSW::cast_motion(const RID& p_shape, const Matrix32& p_xform,const Vector2& p_motion,float p_margin,float &p_closest_safe,float &p_closest_unsafe, const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask) { Shape2DSW *shape = static_cast<Physics2DServerSW*>(Physics2DServer::get_singleton())->shape_owner.get(p_shape); ERR_FAIL_COND_V(!shape,false); Rect2 aabb = p_xform.xform(shape->get_aabb()); aabb=aabb.merge(Rect2(aabb.pos+p_motion,aabb.size)); //motion aabb=aabb.grow(p_margin); //if (p_motion!=Vector2()) // print_line(p_motion); int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,Space2DSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results); float best_safe=1; float best_unsafe=1; for(int i=0;i<amount;i++) { if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask)) continue; if (p_exclude.has( space->intersection_query_results[i]->get_self())) continue; //ignore excluded const CollisionObject2DSW *col_obj=space->intersection_query_results[i]; int shape_idx=space->intersection_query_subindex_results[i]; Matrix32 col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx); //test initial overlap, does it collide if going all the way? if (!CollisionSolver2DSW::solve(shape,p_xform,p_motion,col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,p_margin)) { continue; } //test initial overlap if (CollisionSolver2DSW::solve(shape,p_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,p_margin)) { return false; } //just do kinematic solving float low=0; float hi=1; Vector2 mnormal=p_motion.normalized(); for(int i=0;i<8;i++) { //steps should be customizable.. Matrix32 xfa = p_xform; float ofs = (low+hi)*0.5; Vector2 sep=mnormal; //important optimization for this to work fast enough bool collided = CollisionSolver2DSW::solve(shape,p_xform,p_motion*ofs,col_obj->get_shape(shape_idx),col_obj_xform,Vector2(),NULL,NULL,&sep,p_margin); if (collided) { hi=ofs; } else { low=ofs; } } if (low<best_safe) { best_safe=low; best_unsafe=hi; } } p_closest_safe=best_safe; p_closest_unsafe=best_unsafe; return true; }
void Polygon2D::_notification(int p_what) { switch(p_what) { case NOTIFICATION_DRAW: { if (polygon.size()<3) return; Vector<Vector2> points; Vector<Vector2> uvs; points.resize(polygon.size()); int len = points.size(); { DVector<Vector2>::Read polyr =polygon.read(); for(int i=0;i<len;i++) { points[i]=polyr[i]+offset; } } if (invert) { Rect2 bounds; int highest_idx=-1; float highest_y=-1e20; float sum=0; for(int i=0;i<len;i++) { if (i==0) bounds.pos=points[i]; else bounds.expand_to(points[i]); if (points[i].y>highest_y) { highest_idx=i; highest_y=points[i].y; } int ni=(i+1)%len; sum+=(points[ni].x-points[i].x)*(points[ni].y+points[i].y); } bounds=bounds.grow(invert_border); Vector2 ep[7]={ Vector2(points[highest_idx].x,points[highest_idx].y+invert_border), Vector2(bounds.pos+bounds.size), Vector2(bounds.pos+Vector2(bounds.size.x,0)), Vector2(bounds.pos), Vector2(bounds.pos+Vector2(0,bounds.size.y)), Vector2(points[highest_idx].x-CMP_EPSILON,points[highest_idx].y+invert_border), Vector2(points[highest_idx].x-CMP_EPSILON,points[highest_idx].y), }; if (sum>0) { SWAP(ep[1],ep[4]); SWAP(ep[2],ep[3]); SWAP(ep[5],ep[0]); SWAP(ep[6],points[highest_idx]); } points.resize(points.size()+7); for(int i=points.size()-1;i>=highest_idx+7;i--) { points[i]=points[i-7]; } for(int i=0;i<7;i++) { points[highest_idx+i+1]=ep[i]; } len=points.size(); } if (texture.is_valid()) { Matrix32 texmat(tex_rot,tex_ofs); texmat.scale(tex_scale); Size2 tex_size=Vector2(1,1); tex_size=texture->get_size(); uvs.resize(points.size()); if (points.size()==uv.size()) { DVector<Vector2>::Read uvr = uv.read(); for(int i=0;i<len;i++) { uvs[i]=texmat.xform(uvr[i])/tex_size; } } else { for(int i=0;i<len;i++) { uvs[i]=texmat.xform(points[i])/tex_size; } } } Vector<Color> colors; colors.push_back(color); Vector<int> indices = Geometry::triangulate_polygon(points); VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(),indices,points,colors,uvs,texture.is_valid()?texture->get_rid():RID()); } break; } }