/* ** ClanLib SDK ** Copyright (c) 1997-2005 The ClanLib Team ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** ** Note: Some of the libraries ClanLib may link to may have additional ** requirements or restrictions. ** ** File Author(s): ** ** Harry Storbacka ** Magnus Norddahl ** James Wynn ** Emanuel Greisen ** (if your name is missing here, please add it) */ #include "Display/display_precomp.h" #include "API/Display/Collision/collision_outline.h" #include "API/Display/Collision/outline_provider_bitmap.h" #include "API/Display/Collision/outline_provider_file.h" #include "API/Display/Collision/outline_circle.h" #include "API/Display/display.h" #include "API/Display/display_window.h" #include "API/Display/graphic_context.h" #include "API/Display/pixel_format.h" #include "API/Core/System/clanstring.h" #include "API/Display/Providers/provider_factory.h" #include "API/Display/pixel_buffer.h" #include "collision_outline_generic.h" #include "../surface_target.h" #include "API/Core/Resources/resource_manager.h" #include "Display/resourcedata_collisionoutline.h" #include "API/Core/System/error.h" ///////////////////////////////////////////////////////////////////////////// // CL_CollisionOutline Construction: CL_CollisionOutline::CL_CollisionOutline() : impl(0) { impl = new CL_CollisionOutline_Generic(); } CL_CollisionOutline::CL_CollisionOutline(const CL_CollisionOutline &other) : impl(0) { impl = new CL_CollisionOutline_Generic(); *this = other; } CL_CollisionOutline::CL_CollisionOutline(std::vector contours, int width, int height) : impl(0) { impl = new CL_CollisionOutline_Generic(); impl->contours = contours; impl->width = width; impl->height = height; } CL_CollisionOutline::CL_CollisionOutline( const std::string &filename, int alpha_limit, CL_OutlineAccuracy accuracy, bool get_insides) : impl(0) { if( CL_String::right(filename, 3) == "out" ) { impl = new CL_CollisionOutline_Generic( new CL_OutlineProviderFile(filename), accuracy_raw ); return; } CL_PixelBuffer pbuf = CL_ProviderFactory::load(filename); if( pbuf.get_format() == CL_PixelFormat::rgba8888 ) { impl = new CL_CollisionOutline_Generic( new CL_OutlineProviderBitmap(pbuf, alpha_limit, get_insides), accuracy); } else { impl = new CL_CollisionOutline_Generic( new CL_OutlineProviderBitmap(pbuf, alpha_limit, get_insides), accuracy_raw); } set_rotation_hotspot(origin_center); } CL_CollisionOutline::CL_CollisionOutline( const std::string &resource_id, CL_ResourceManager *manager ) : impl(0) { resource = manager->get_resource(resource_id); resource.load(); CL_ResourceData_CollisionOutline *data = (CL_ResourceData_CollisionOutline *) resource.get_data("collisionoutline"); if (!data) throw CL_Error("Resource '" + resource_id + "' is not of type 'collisionoutline'"); impl = new CL_CollisionOutline_Generic(); *this = data->get_CollisionOutline(); resource.unload(); } CL_CollisionOutline::CL_CollisionOutline( CL_PixelBuffer pbuf, int alpha_limit, CL_OutlineAccuracy accuracy) : impl(0) { if( pbuf.get_format() == CL_PixelFormat::rgba8888 ) { impl = new CL_CollisionOutline_Generic( new CL_OutlineProviderBitmap(pbuf, alpha_limit), accuracy ); } else { impl = new CL_CollisionOutline_Generic( new CL_OutlineProviderBitmap(pbuf, alpha_limit), accuracy_raw ); } set_rotation_hotspot(origin_center); } CL_CollisionOutline::~CL_CollisionOutline() { delete impl; } ///////////////////////////////////////////////////////////////////////////// // CL_CollisionOutline Attributes: std::vector &CL_CollisionOutline::get_contours() const { return impl->contours; } CL_Circlef CL_CollisionOutline::get_minimum_enclosing_disc() const { return impl->minimum_enclosing_disc; } CL_Pointf CL_CollisionOutline::get_translation() const { return impl->position; } float CL_CollisionOutline::get_angle() const { return impl->angle; } CL_Pointf CL_CollisionOutline::get_scale() const { return impl->scale_factor; } bool CL_CollisionOutline::get_inside_test() const { return impl->do_inside_test; } void CL_CollisionOutline::get_alignment( CL_Origin &origin, float &x, float &y ) const { origin = impl->translation_origin; x = impl->translation_offset.x; y = impl->translation_offset.y; } void CL_CollisionOutline::get_rotation_hotspot( CL_Origin &origin, float &x, float &y) const { origin = impl->rotation_origin; x = impl->rotation_hotspot.x; y = impl->rotation_hotspot.y; } unsigned int CL_CollisionOutline::get_width() const { return impl->width; } unsigned int CL_CollisionOutline::get_height() const { return impl->height; } std::vector &CL_CollisionOutline::get_collision_info() const { return impl->collision_info; } void CL_CollisionOutline::set_collision_info(const std::vector &colinfo) { impl->collision_info = colinfo; } void CL_CollisionOutline::clean_collision_info() { impl->collision_info.clear(); } void CL_CollisionOutline::get_collision_info_state(bool &points, bool &normals, bool &metadata, bool &pendepth) const { points = impl->collision_info_points; normals = impl->collision_info_normals; metadata = impl->collision_info_meta; pendepth = impl->collision_info_pen_depth; } ///////////////////////////////////////////////////////////////////////////// // CL_CollisionOutline Operations: CL_CollisionOutline &CL_CollisionOutline::operator=(const CL_CollisionOutline &other) { if( this == &other ) return *this; delete impl; impl = new CL_CollisionOutline_Generic(); impl->contours = other.get_contours(); impl->do_inside_test = other.get_inside_test(); impl->width = other.get_width(); impl->height = other.get_height(); impl->position = other.get_translation(); impl->scale_factor = other.get_scale(); impl->angle = other.get_angle(); //impl->radius = other.get_radius(); impl->minimum_enclosing_disc = other.get_minimum_enclosing_disc(); bool points, normals, metadata, pendepths; other.get_collision_info_state(points,normals,metadata,pendepths); enable_collision_info(points,normals,metadata,pendepths); CL_Origin origin; float x, y; other.get_alignment(origin,x,y); impl->translation_origin = origin; impl->translation_offset.x = x; impl->translation_offset.y = y; other.get_rotation_hotspot(origin,x,y); impl->rotation_origin = origin; impl->rotation_hotspot.x = x; impl->rotation_hotspot.y = y; return *this; } void CL_CollisionOutline::optimize(unsigned char check_distance, float corner_angle ) { impl->optimize(check_distance, corner_angle); } void CL_CollisionOutline::draw( float x, float y, const CL_Color &color, CL_GraphicContext *gc) { // Get a graphics context if (gc == 0) gc = CL_Display::get_current_window()->get_gc(); // Draw collision outline (Contours are assumed as closed polygons, hence we use line-loop) for(unsigned int i = 0; i < impl->contours.size(); i++) { // Draw the contour unsigned int numpoints = impl->contours[i].points.size(); for(unsigned int s = 0; s < numpoints; s++) { const CL_Pointf &p1 = impl->contours[i].points[s]; const CL_Pointf &p2 = impl->contours[i].points[(s+1) % numpoints]; gc->draw_line( int(x + p1.x+0.5f), int(y + p1.y+0.5f), int(x + p2.x+0.5f), int(y + p2.y+0.5f), color); } // Add points (as oposite color) // TODO: This did not work with SDL, the draw_pixel fails CL_Color colorinv(255-color.get_red(),255-color.get_green(),255-color.get_blue()); for(unsigned int s1 = 0; s1 < numpoints; s1++) { const CL_Pointf &p1 = impl->contours[i].points[s1]; gc->draw_pixel( int(x + p1.x+0.5f), int(y + p1.y+0.5f), colorinv); } } } void CL_CollisionOutline::draw_sub_circles( float x, float y, const CL_Color &color, CL_GraphicContext *gc) { // Get a graphics context if (gc == 0) gc = CL_Display::get_current_window()->get_gc(); // Draw the circles for(unsigned int i = 0; i < impl->contours.size(); i++) { unsigned int numcircles = impl->contours[i].sub_circles.size(); for(unsigned int s = 0; s < numcircles; s++) { CL_Pointf center = impl->contours[i].sub_circles[s].position; float radius = impl->contours[i].sub_circles[s].radius; float numsegments = 16; for(float e = 0; e < numsegments; e++) { float offx1 = cos(M_PI * 2.0 * (e / numsegments))*radius; float offy1 = sin(M_PI * 2.0 * (e / numsegments))*radius; float offx2 = cos(M_PI * 2.0 * ((e+1) / numsegments))*radius; float offy2 = sin(M_PI * 2.0 * ((e+1) / numsegments))*radius; CL_Pointf p1(x + center.x + offx1, y + center.y + offy1); CL_Pointf p2(x + center.x + offx2, y + center.y + offy2); gc->draw_line( int(p1.x+0.5f), int(p1.y+0.5f), int(p2.x+0.5f), int(p2.y+0.5f), color); } } } } void CL_CollisionOutline::draw_smallest_enclosing_disc( float x, float y, const CL_Color &color, CL_GraphicContext *gc) { // Get a graphics context if (gc == 0) gc = CL_Display::get_current_window()->get_gc(); // Draw the smallest enclosing disc CL_Pointf center = impl->minimum_enclosing_disc.position; float radius = impl->minimum_enclosing_disc.radius; float numsegments = 24; // To make it visible if the outline has only one contour, and the contour only has one sub-circle for(float e = 0; e < numsegments; e++) { float offx1 = cos(M_PI * 2.0 * (e / numsegments))*radius; float offy1 = sin(M_PI * 2.0 * (e / numsegments))*radius; float offx2 = cos(M_PI * 2.0 * ((e+1) / numsegments))*radius; float offy2 = sin(M_PI * 2.0 * ((e+1) / numsegments))*radius; CL_Pointf p1(x + center.x + offx1, y + center.y + offy1); CL_Pointf p2(x + center.x + offx2, y + center.y + offy2); gc->draw_line( int(p1.x+0.5f), int(p1.y+0.5f), int(p2.x+0.5f), int(p2.y+0.5f), color); } } void CL_CollisionOutline::set_translation(float x, float y) { impl->set_translation(x,y); } void CL_CollisionOutline::set_scale(float x, float y) { impl->set_scale(x,y); } void CL_CollisionOutline::set_angle(float angle) { impl->set_angle(angle); } void CL_CollisionOutline::rotate(float angle) { impl->rotate(angle); } void CL_CollisionOutline::set_inside_test(bool value) { impl->do_inside_test = value; } void CL_CollisionOutline::enable_collision_info( bool points, bool normals, bool metadata, bool pen_depth) { impl->collision_info_points = points; impl->collision_info_normals = normals; impl->collision_info_meta = metadata; impl->collision_info_pen_depth = pen_depth; impl->collision_info_collect = points || normals || metadata || pen_depth; } void CL_CollisionOutline::calculate_radius() { impl->calculate_radius(); } void CL_CollisionOutline::calculate_sub_circles(float radius_multiplier) { impl->calculate_sub_circles(radius_multiplier); } void CL_CollisionOutline::calculate_smallest_enclosing_discs() { impl->calculate_smallest_enclosing_discs(); } void CL_CollisionOutline::calculate_convex_hulls() { impl->calculate_convex_hulls(); } void CL_CollisionOutline::save(const std::string &filename, CL_OutputSourceProvider *provider) const { impl->save(filename, provider); } bool CL_CollisionOutline::collide(const CL_CollisionOutline &outline, bool remove_old_collision_info) { return impl->collide(outline, remove_old_collision_info); } void CL_CollisionOutline::calculate_penetration_depth(std::vector &collision_info) { CL_CollisionOutline_Generic::calculate_penetration_depth(collision_info); } bool CL_CollisionOutline::point_inside(const CL_Pointf &point) const { return impl->point_inside(point); } void CL_CollisionOutline::set_alignment( CL_Origin origin, float x, float y ) { CL_Origin old_origin = impl->translation_origin; // undo previous translation offset. impl->set_translation(-impl->translation_offset.x, -impl->translation_offset.y, true); // undo previous origin. if( origin != impl->translation_origin ) { float fix_x = 0, fix_y = 0; float width = impl->width/2; float height = impl->height/2; switch( old_origin ) { case origin_top_left: fix_x = -width; fix_y = -height; break; case origin_top_center: fix_x = 0; fix_y = -height; break; case origin_top_right: fix_x = width; fix_y = -height; break; case origin_center_left: fix_x = -width; fix_y = 0; break; case origin_center: fix_x = 0; fix_y = 0; break; case origin_center_right: fix_x = width; fix_y = 0; break; case origin_bottom_left: fix_x = -width; fix_y = height; break; case origin_bottom_center: fix_x = 0; fix_y = height; break; case origin_bottom_right: fix_x = width; fix_y = height; break; } impl->set_translation(fix_x, fix_y, true); // offset outline points impl->rotation_hotspot.x += fix_x; impl->rotation_hotspot.y += fix_y; } impl->translation_origin = origin; impl->translation_offset.x = x; impl->translation_offset.y = y; impl->set_translation(x, y, true); // offset outline points // set new origin offset. if( origin != origin_center ) { float fix_x = 0, fix_y = 0; float width = impl->width/2; float height = impl->height/2; switch( origin ) { case origin_top_left: fix_x = width; fix_y = height; break; case origin_top_center: fix_x = 0; fix_y = height; break; case origin_top_right: fix_x = -width; fix_y = height; break; case origin_center_left: fix_x = width; fix_y = 0; break; case origin_center: fix_x = 0; fix_y = 0; break; case origin_center_right: fix_x = -width; fix_y = 0; break; case origin_bottom_left: fix_x = width; fix_y = -height; break; case origin_bottom_center: fix_x = 0; fix_y = -height; break; case origin_bottom_right: fix_x = -width; fix_y = -height; break; } impl->set_translation(fix_x, fix_y, true); // offset outline points impl->rotation_hotspot.x += fix_x; impl->rotation_hotspot.y += fix_y; } impl->calculate_radius(); } void CL_CollisionOutline::set_rotation_hotspot( CL_Origin origin, float x, float y ) { // undo translation origin CL_Origin trans_origin = impl->translation_origin; set_alignment(origin_center); // undo the rotation of the current rotation hotspot float angle = impl->angle; impl->set_angle(0.0f); float fix_x = 0, fix_y = 0; // set new origin offset. if( origin != origin_center ) { float width = impl->width/2; float height = impl->height/2; switch( origin ) { case origin_top_left: fix_x = -width; fix_y = -height; break; case origin_top_center: fix_x = 0; fix_y = -height; break; case origin_top_right: fix_x = width; fix_y = -height; break; case origin_center_left: fix_x = -width; fix_y = 0; break; case origin_center: fix_x = 0; fix_y = 0; break; case origin_center_right: fix_x = width; fix_y = 0; break; case origin_bottom_left: fix_x = -width; fix_y = height; break; case origin_bottom_center: fix_x = 0; fix_y = height; break; case origin_bottom_right: fix_x = width; fix_y = height; break; } } impl->rotation_origin = origin; impl->rotation_hotspot.x = fix_x + x; impl->rotation_hotspot.y = fix_y + y; // transform data using new rotation hotspot impl->set_angle(angle); set_alignment(trans_origin); impl->calculate_radius(); }