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 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; } }