예제 #1
0
    /// <summary>
    /// Finish adding vertices to this JeloClosedShape, and choose whether to convert into local space.
    /// JelloClosedShape.winding and JelloClosedShape.Triangles will be set.
    /// Make sure there are no duplicate points before calling this. use JelloShapeTools.RemoveDuplicatePoints().
    /// </summary>
    /// <param name="recenter">whether to convert the positions of the JelloClosedShape into local space.</param>
    ///
    /// <dl class="example"><dt>Example</dt></dl>
    /// ~~~{.c}
    /// //Create a closed shape square
    /// JelloClosedShape shape = new JelloClosedShape();
    ///
    /// shape.begin();
    ///
    /// shape.addPoint(new Vector2(-1,1));	//top left
    /// shape.addPoint(new Vector2(-1,1));	//top right
    /// shape.addPoint(new Vector2(-1,1));	//bottom right
    /// shape.addPoint(new Vector2(-1,1));	//bottom left
    ///
    /// shape.finish();
    /// ~~~
    public void finish(bool recenter = true)
    {
        if (mInternalVertices != null && mInternalVertices.Length > 0)
        {
            //dont allow duplicate points
            mInternalVertices = JelloShapeTools.RemoveDuplicatePoints(mInternalVertices);

            //dont allow points outside of the perimiter
            for (int i = 0; i < mInternalVertices.Length; i++)
            {
                if (!JelloShapeTools.Contains(mEdgeVertices, mInternalVertices[i]))
                {
                    mInternalVertices[i] = Vector2.one * Mathf.Infinity;
                }
            }
            //dont allow points on the perimiter. (this will also remove any null points)
            mInternalVertices = JelloShapeTools.RemovePointsOnPerimeter(mEdgeVertices, mInternalVertices);
        }

        mCenter = JelloShapeTools.FindCenter(mEdgeVertices);

        if (recenter)
        {
            // now subtract this from each element, to get proper "local" coordinates.
            for (int i = 0; i < mEdgeVertices.Length; i++)
            {
                mEdgeVertices[i] -= mCenter;
            }
            if (mInternalVertices != null)
            {
                for (int i = 0; i < mInternalVertices.Length; i++)
                {
                    mInternalVertices[i] -= mCenter;
                }
            }
        }

        if (JelloShapeTools.HasClockwiseWinding(mEdgeVertices))
        {
            winding = Winding.Clockwise;
        }
        else
        {
            winding = Winding.CounterClockwise;
        }



        Triangulate();
    }
예제 #2
0
    /// <summary>
    /// Initializes a new JelloClosedShape.
    /// Constructed from an existing list of vertices.
    /// </summary>
    /// <param name="edgeVertices">The edge vertices to construct this JelloClosedShape from.</param>
    /// <param name="internalVertices">The internal vertices to construct this JelloClosedShape from.</param>
    /// <param name="recenter">Whether to convert the vertices into local space.</param>
    ///
    /// <dl class="example"><dt>Example</dt></dl>
    /// ~~~{.c}
    /// //Create a closed shape square
    /// Vector2[] square = new Vector2[4];
    /// square[0] = new Vector2(-1,1);	//top left
    /// square[1] = new Vector2(1,1);	//top right
    /// square[2] = new Vector2(1,-1);	//bottom right
    /// square[3] = new Vector2(-1,-1);	//bottom left
    ///
    /// JelloClosedShape shape = new JelloClosedShape(square);
    /// ~~~
    public JelloClosedShape(Vector2[] edgeVertices, Vector2[] internalVertices = null, bool recenter = true)
    {
        mEdgeVertices = new Vector2[edgeVertices.Length];

        for (int i = 0; i < mEdgeVertices.Length; i++)
        {
            mEdgeVertices[i] = edgeVertices[i];
        }

        if (internalVertices != null)
        {
            bool[] validity = new bool[internalVertices.Length];
            int    num      = 0;
            for (int i = 0; i < internalVertices.Length; i++)
            {
                if (JelloShapeTools.Contains(mEdgeVertices, internalVertices[i]))
                {
                    validity[i] = true;
                    num++;
                }
                else
                {
                    validity[i] = false;
                }
            }

            mInternalVertices = new Vector2[num];
            num = 0;
            for (int i = 0; i < internalVertices.Length; i++)
            {
                if (validity[i])
                {
                    mInternalVertices[num] = internalVertices[i];
                    num++;
                }
            }
        }
        else
        {
            mInternalVertices = new Vector2[0];
        }

        finish(recenter);
    }
예제 #3
0
    /// <summary>
    /// Change the positions of the vertices of the JelloClosedShape. Be sure to call JelloClosedShape.finish() after this.
    /// Will fail if number of vertices do not match.
    /// </summary>
    /// <param name="edgeVertices">The new edge vertex positions.</param>
    /// <param name="internalVertices">The new internal vertex positions.</param>
    ///
    /// <dl class="example"><dt>Example</dt></dl>
    /// ~~~{.c}
    /// //create a closed shape in the form of a square and then change it into a rectangle
    /// Vector2[] square;
    /// Vector2[] rectangle;
    ///
    /// JelloClosedShape shape = new JelloClosedShape(square);
    ///
    /// shape.changeVertices(rectangle, new Vector2[0], true);
    /// ~~~
    public void changeVertices(Vector2[] edgeVertices, Vector2[] internalVertices)
    {
        if (edgeVertices.Length == mEdgeVertices.Length)
        {
            for (int i = 0; i < edgeVertices.Length; i++)
            {
                mEdgeVertices[i] = edgeVertices[i];
            }

            bool[] valid = new bool[internalVertices.Length];
            int    num   = 0;

            for (int i = 0; i < internalVertices.Length; i++)
            {
                if (JelloShapeTools.Contains(mEdgeVertices, internalVertices[i]))
                {
                    valid[i] = true;
                    num++;
                }
                else
                {
                    valid[i] = false;
                }
            }

            mInternalVertices = new Vector2[num];

            num = 0;
            for (int i = 0; i < internalVertices.Length; i++)
            {
                if (valid[i])
                {
                    mInternalVertices[num] = internalVertices[i];
                    num++;
                }
            }
        }
        else
        {
            Debug.LogWarning("new vertices count less than current vertices count");
        }
    }
예제 #4
0
	public void DrawPointMasses(JelloBody body, bool editable)
	{
		Handles.color = new Color(0.75f, 0.75f, 0.2f, 0.5f);
		
		for(int i = 0; i < body.Shape.EdgeVertexCount; i++)
		{
			Vector2 pos = body.transform.TransformPoint (body.Shape.EdgeVertices[i]);
			if(editable)
			{
				int hot = GUIUtility.hotControl;
				
				Handles.FreeMoveHandle(pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f, Vector3.zero, Handles.DotCap);
				if(GUIUtility.hotControl != hot)
				{
					if(currentSubEditor == 1)//point mass editor!
					{
						subEditors[currentSubEditor].SetEditIndex(i);
						Repaint();
					}
				}
			}
			else
			{
				Handles.color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
				Handles.DotCap(3, pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f);
			}
		}

		for(int i = 0; i < body.Shape.InternalVertexCount; i++)
		{
			Handles.color = new Color(0.75f, 0f, 0.75f, 0.5f);
			Vector2 pos = body.transform.TransformPoint (body.Shape.InternalVertices[i]);
			
			if(editable)
			{
				int hot = GUIUtility.hotControl;

				EditorGUI.BeginChangeCheck();
				pos = Handles.FreeMoveHandle(pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f, Vector3.zero, Handles.DotCap);
				if(EditorGUI.EndChangeCheck())
				{
					if(!JelloShapeTools.Contains(body.Shape.EdgeVertices, body.transform.InverseTransformPoint(pos)) || JelloShapeTools.PointOnPerimeter(body.Shape.EdgeVertices, body.transform.InverseTransformPoint(pos)))
					{
						JelloClosedShape newShape = new JelloClosedShape(body.Shape.EdgeVertices, null, false);

						for(int a = 0; a < body.Shape.InternalVertexCount; a++)
						{
							//dont add this point
							if(a == i)
								continue;

							newShape.addInternalVertex(body.Shape.InternalVertices[a]);
						}

						newShape.finish(false);

						body.smartSetShape(newShape, JelloBody.ShapeSettingOptions.MovePointMasses, smartShapeSettingOptions);

						EditorUtility.SetDirty(body);
						GUIUtility.hotControl = 0;
						subEditors[currentSubEditor].SetEditIndex(-1);
						Repaint();
						break;
					}

					body.Shape.changeInternalVertexPosition(i, body.transform.InverseTransformPoint(pos));
					body.Shape.finish(false);

					EditorUtility.SetDirty(body);

				}
				if(GUIUtility.hotControl != hot && GUIUtility.hotControl != 0)
				{

					if(currentSubEditor == 1)//point mass editor!
					{
						subEditors[currentSubEditor].SetEditIndex(i + body.EdgePointMassCount);
						Repaint();
					}
				}
			}
			else
			{
				Handles.color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
				Handles.DotCap(3, pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f);
			}

	
		}

	}
	/// <summary>
	/// Processes the pull.
	/// </summary>
	/// <returns>IEnumerator.</returns>
	IEnumerator ProcessPull()
	{
		//Grab the closest point mass and process adjacent points.
		GrabPointMass();

		//could be true later if you are still pulling the body, but do not have a specific point grabbed.
		//this would happen if when you are holding down the mouse button, its position is within the jello body.
		bool waitingForNewGrab = false;

		//keep processing the pull as long as the mouse button is depressed.
		while(Input.GetMouseButton(0))
		{
//			if(requireGrounded && !grounded)
//				break;

			//wake up the body if its being grabbed.
			body.IsAwake = true;

			//find the mouses current position in world space.
			mousePosInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);

			//If our mouse position is outside of the body's transformed base shape
			if(!JelloShapeTools.Contains(body.Shape.EdgeVertices, body.transform.InverseTransformPoint(mousePosInWorld)))
			{
				//if our mouse position was inside of the base shape last step, grab a new point mass.
				if(waitingForNewGrab)
				{
					GrabPointMass();
					waitingForNewGrab = false;
				}

				//the base shape position (local and global) respective to the selected point mass.
				Vector2 pt = body.Shape.EdgeVertices[pmIndex];
				Vector2 ptGlobal = (Vector2)body.transform.TransformPoint(pt);

				//if we want the body to rotate to align with the pull
				if(rotate)
				{
					//find the difference in angle between the two vector
					Vector2 dir1 = mousePosInWorld - body.Position; 																						//body position to mouse position
					Vector2 dir2 = (Vector2)body.transform.TransformPoint(body.Shape.EdgeVertices[pmIndex]) - body.Position; 	//body position to xformed base shape position
					float ang = Vector2.Angle(dir1, dir2);

					//correct our body angle only a bit at a time for smooth rotations.
					ang = Mathf.Clamp(ang, 0f, ang * rotateSpeed * Time.fixedDeltaTime);
					
					if(JelloVectorTools.CrossProduct(dir1, dir2) < 0f)
						ang *=  -1f;
					body.Angle -= ang;
				}
				else
				{
					//we dont want the body to rotate to align and will constrain the point mass to a cone.

					//find the two shape positions next to our selected shape position.
					Vector2 prev = body.Shape.EdgeVertices[pmIndex > 0 ? pmIndex - 1 : body.Shape.EdgeVertexCount - 1];
					Vector2 next = body.Shape.EdgeVertices[pmIndex + 1 < body.Shape.EdgeVertexCount ? pmIndex + 1: 0];

					//vectors to/from adjacent ponts
					Vector2 fromPrev = pt - prev;
					Vector2 toNext = next - pt;
					//normal created by adjacent vectors
					//this is the bisector of the angle created by the prev to pt to next vectors
					//and will be used as the bisector of the constraining cone.
					Vector2 ptNorm = JelloVectorTools.getPerpendicular(fromPrev + toNext);
					//correct normal direction by shape winding.
					ptNorm = body.Shape.winding == JelloClosedShape.Winding.Clockwise ? ptNorm : -ptNorm;

					//convert to global coordinates
					ptNorm = (Vector2)body.transform.TransformDirection(ptNorm);
					//find the angle between the our mouse and the bisector.
					float ang = Vector2.Angle (ptNorm, mousePosInWorld - ptGlobal);

					//if we exceed the constraint of the cone.
					if(ang > coneAngle * 0.5f) //0.5 because the bisector cuts the cone angle in half.
					{
						//find the vector representing the edge of the cone
						Vector2 limitVector;
						if(JelloVectorTools.CrossProduct (ptNorm, mousePosInWorld - ptGlobal) < 0f )//which side of the bisector are we on
							limitVector = JelloVectorTools.rotateVector(ptNorm, -coneAngle * 0.5f);
						else
							limitVector = JelloVectorTools.rotateVector(ptNorm, coneAngle * 0.5f);

						//move our position to the closest point on the limit vector. Handle max pull back at the same time.
						mousePosInWorld = JelloVectorTools.getClosestPointOnSegment(mousePosInWorld, ptGlobal, ptGlobal + limitVector.normalized * maxPullBack);
					}
				}

				//how far away from xformed base shape position we are.
				pullBackDistance = (mousePosInWorld - ptGlobal).sqrMagnitude;
				if(pullBackDistance != 0f)
				{
					//if we exceed the max pullback, set to the max pullback.
					if(pullBackDistance > maxPullBack * maxPullBack)
					{
						mousePosInWorld = ptGlobal + (mousePosInWorld - ptGlobal).normalized * maxPullBack; // still do this when angle is not right.
						pullBackDistance = maxPullBack * maxPullBack;
					}
				}

				//explicitly set the selected pointmass position and velocity.
				pointmass.Position = mousePosInWorld;
				pointmass.velocity = Vector2.zero;	
			}
			else//our mouse is down, but is inside the perimeter of the body.
			{
				ReleasePointMass();//release the selected point mass and restore its ajacent point masses
				waitingForNewGrab = true;//wait for a new grab. would occur if the mouse was dragged outside of the body again. 
			}
				
			//keep the body still while being pulled.
			body.Position = position;

			//sync up with fixed update
			yield return new WaitForFixedUpdate();
		}

		//mouse button has been released!
		//release the selected point mass.
		ReleasePointMass();
		//release the body and apply force if we had a point mass selected at the time.
		ReleaseBody(!waitingForNewGrab);
	}