Beispiel #1
0
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;
	}
}
Beispiel #2
0
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;
	}
}