// Blob Object, Brian Richardson @ GG 2008
// Based based on article/code by Mick West (http://cowboyprogramming.com/2007/01/05/blob-physics/)
// To add it to your project, add these XML blocks to your level.xml file.
//
//
// data/images/face.png
// true
//
//
// data/images/ears.png
// true
//
//
// Note: This assumes that you're adding the BlobComponent to StartGame2D, if you've changed
// the namespace, update this as well.
//
//
//
//
// false
// false
//
//
// 0
//
//
// false
// 0
//
// 10.000
// 10.000
//
//
// 0.000
// 0.000
//
// 0
// true
// true
// 1
// false
// false
//
// 0.000000
// 0.000000
//
//
//
//
// Notes:
// This was a quick hack, it doesn't respect TorqueX's collision system. To do this, you'd create a new CollisionConstraint that
// would query Torque's collision info.
//
// You can only add forces to all particles in a VerletSystem. To do something more useful, it'd be smart to have start/end
// indices for each blob, then you could apply forces to single blobs.
//
// This was a quick hack, ;) I'm sure there are other bad things going on in here.
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GarageGames.Torque.Core;
using GarageGames.Torque.Util;
using GarageGames.Torque.Sim;
using GarageGames.Torque.T2D;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.MathUtil;
using GarageGames.Torque.GUI;
using GarageGames.Torque.GFX;
using GarageGames.Torque.Materials;
using GarageGames.Torque.RenderManager;
using GarageGames.Torque.Platform;
namespace StarterGame2D
{
public interface IVerletConstraint
{
void satisfy(VerletPoint vp);
Vector3 getForce(VerletPoint vp);
void debugRender(VerletPoint vp, ref GFXVertexFormat.PCTTBN[] vertices, ref int curIndex);
int vertsNeeded();
}
public class SemiRigidConstraint : IVerletConstraint
{
private VerletPoint mOtherVerlet;
private float mMin, mMid, mMax, mForce;
private bool mRender;
public SemiRigidConstraint(VerletPoint p_other_point, float min, float mid, float max, float force, bool render)
{
mOtherVerlet = p_other_point;
mMin = min;
mMid = mid;
mMax = max;
mForce = force;
mRender = render;
}
//
// IVerletConstraint
//
public void satisfy(VerletPoint vp)
{
Vector3 toMe = vp.position - mOtherVerlet.position;
Vector3 mid = (vp.position + mOtherVerlet.position) / 2.0f;
if (toMe.Length() < 0.00001f)
{
toMe = Vector3.Right;
}
float radius = toMe.Length();
radius = Math.Max(radius, mMin);
radius = Math.Min(radius, mMax);
toMe.Normalize();
toMe *= radius;
vp.position = mid + toMe / 2.0f;
mOtherVerlet.position = mid - toMe / 2.0f;
}
public Vector3 getForce(VerletPoint vp)
{
Vector3 toMe = vp.position - mOtherVerlet.position;
if (toMe.Length() < 0.00001f)
{
toMe = Vector3.Right;
}
toMe.Normalize();
Vector3 mid = mOtherVerlet.position + toMe * mMid;
Vector3 toMid = mid - vp.position;
return toMid * mForce;
}
public void debugRender(VerletPoint vp, ref GFXVertexFormat.PCTTBN[] vertices, ref int curIndex)
{
if (!mRender)
return;
vertices[curIndex] = new GFXVertexFormat.PCTTBN();
vertices[curIndex].Position = new Vector3(vp.position.X, vp.position.Y, vp.position.Z);
vertices[curIndex].Color = new Color(0,0,0);
curIndex++;
vertices[curIndex] = new GFXVertexFormat.PCTTBN();
vertices[curIndex].Position = new Vector3(mOtherVerlet.position.X, mOtherVerlet.position.Y, mOtherVerlet.position.Z);
vertices[curIndex].Color = new Color(0, 0, 0);
curIndex++;
}
public int vertsNeeded()
{
if (mRender)
return 2;
else
return 0;
}
}
public class LineConstraint : IVerletConstraint
{
public enum ConstraintType
{
Above,
Below,
Left,
Right
}
protected float mVal;
protected ConstraintType mType;
public LineConstraint(float v, ConstraintType ctype)
{
mVal = v;
mType = ctype;
}
//
// IVerletConstraint
//
virtual public void satisfy(VerletPoint vp)
{
switch (mType)
{
case ConstraintType.Above :
{
if (vp.position.Y > mVal)
{
vp.position = new Vector3(vp.position.X, mVal, vp.position.Z);
}
}
break;
case ConstraintType.Below:
{
if (vp.position.Y < mVal)
{
vp.position = new Vector3(vp.position.X, mVal, vp.position.Z);
}
}
break;
case ConstraintType.Right:
{
if (vp.position.X < mVal)
{
vp.position = new Vector3(mVal, vp.position.Y, vp.position.Z);
}
}
break;
case ConstraintType.Left:
{
if (vp.position.X > mVal)
{
vp.position = new Vector3(mVal, vp.position.Y, vp.position.Z);
}
}
break;
}
}
public Vector3 getForce(VerletPoint vp)
{
return Vector3.Zero;
}
public void debugRender(VerletPoint vp, ref GFXVertexFormat.PCTTBN[] vertices, ref int curIndex)
{
}
public int vertsNeeded()
{
return 0;
}
}
public class VerletPoint
{
public VerletPoint(Vector3 pos)
{
mPos = pos;
mLastPos = pos;
mMass = 1;
mConstraints = new List(); ;
mCollisionConstraints = new List();
}
public int numConstraints()
{
return mConstraints.Count;
}
public int vertsNeeded()
{
int verts = 0;
foreach (IVerletConstraint con in mConstraints)
verts += con.vertsNeeded();
return verts;
}
public void debugRender(ref GFXVertexFormat.PCTTBN[] vertices, ref int curIndex)
{
foreach (IVerletConstraint con in mConstraints)
con.debugRender(this, ref vertices, ref curIndex);
}
public void addConstraint(IVerletConstraint constraint)
{
mConstraints.Add(constraint);
}
public void addCollisionConstraint(IVerletConstraint constraint)
{
mCollisionConstraints.Add(constraint);
}
public void gatherForces()
{
foreach (IVerletConstraint c in mConstraints)
mForce += c.getForce(this);
}
public void integrate(float dt)
{
Vector3 last = mPos;
mPos += (mPos - mLastPos) + mForce / mMass * dt * dt;
mLastPos = last;
}
public void satisfyConstraints()
{
foreach (IVerletConstraint con in mConstraints)
con.satisfy(this);
}
public void satisfyCollisionConstraints()
{
foreach (IVerletConstraint con in mCollisionConstraints)
con.satisfy(this);
}
public Vector3 position
{
get { return mPos; }
set { mPos = value; }
}
public Vector3 LastPosition
{
get { return mLastPos; }
}
public Vector3 Force
{
get { return mForce; }
set { mForce = value; }
}
public float Mass
{
get { return mMass; }
}
private Vector3 mForce;
private float mMass;
private Vector3 mPos;
private Vector3 mLastPos;
private List mConstraints;
private List mCollisionConstraints;
private List mCollisionConstraints2;
}
public class VerletSystem
{
VerletSystem()
{
mVerletPoints = new List();
}
public VerletPoint createPoint(Vector3 pos)
{
VerletPoint vp = new VerletPoint(pos);
mVerletPoints.Add(vp);
return vp;
}
static public VerletSystem get()
{
if (mSystem != null)
return mSystem;
mSystem = new VerletSystem();
return mSystem;
}
static private VerletSystem mSystem;
private List mVerletPoints;
public List Points
{
get { return mVerletPoints; }
}
public int numVerts()
{
int ret = 0;
foreach (VerletPoint v in Points)
{
ret += v.vertsNeeded();
}
return ret;
}
public Box3F bounds()
{
Box3F ret;
ret.Min = new Vector3(1000, 1000, 1000);
ret.Max = new Vector3(-1000, -1000, -1000);
foreach (VerletPoint v in Points)
{
ret.Min = Vector3.Min(ret.Min, v.position);
ret.Max = Vector3.Max(ret.Max, v.position);
}
return ret;
}
static public void SemiRigidConstraint(VerletPoint p1, VerletPoint p2, float min, float mid, float max, float force, bool render)
{
SemiRigidConstraint p_r1 = new SemiRigidConstraint(p2, min, mid, max, force, render);
p1.addConstraint(p_r1);
SemiRigidConstraint p_r2 = new SemiRigidConstraint(p1, min, mid, max, force, render);
p2.addConstraint(p_r2);
}
public void integrate(Vector3 addForce)
{
Vector3 gravity = new Vector3(0, 1, 0);
// Figure our our forces
foreach (VerletPoint v in mVerletPoints)
{
v.Force = gravity * v.Mass;
v.Force += addForce;
// Add other forces like jumping, etc..
v.gatherForces();
}
foreach (VerletPoint v in mVerletPoints)
{
v.integrate(0.016666f);
}
foreach (VerletPoint v in mVerletPoints)
{
v.satisfyConstraints();
}
foreach (VerletPoint v in mVerletPoints)
{
v.satisfyCollisionConstraints();
}
}
}
[TorqueXmlSchemaType]
public class BlobComponent : T2DSceneObject
{
//======================================================
#region Static methods, fields, constructors
#endregion
///
/// Specifies the Sprite's Texture
///
public RenderMaterial FaceMaterial
{
// forward access to quad object
get { return _facequad.Material; }
set { _facequad.Material = value; }
}
public RenderMaterial EarMaterial
{
// forward access to quad object
get { return _earquad.Material; }
set { _earquad.Material = value; }
}
//======================================================
#region Constructors
private RenderMaterial _material;
public BlobComponent()
{
GarageGames.Torque.Materials.SimpleMaterial seffect = new GarageGames.Torque.Materials.SimpleMaterial();
_material = seffect;
}
#endregion
//======================================================
#region Public properties, operators, constants, and enums
#endregion
//======================================================
#region Public methods
private int count = 0;
private Vector3 mForce;
public override void ProcessTick(Move move, float dt)
{
base.ProcessTick(move, dt);
//if (count++ < 10)
// return;
count = 0;
// Do verlet integration
for (int i = 0; i < 3; i++ )
VerletSystem.get().integrate(mForce);
mForce = Vector3.Zero;
}
public void setMoveForce(Vector3 f)
{
mForce = f;
}
public override void InterpolateTick(float k)
{
// todo: interpolate between ticks as needed here
}
private Resource _vb;
private int numVerts;
private void _CreateVB()
{
if (!_vb.IsNull)
return;
Assert.Fatal(_vb.IsNull, "doh");
numVerts = VerletSystem.get().numVerts();
int sizeInBytes = numVerts * GFXVertexFormat.VertexSize;
_vb = ResourceManager.Instance.CreateVertexBuffer(ResourceProfiles.ManualStaticVBProfile, sizeInBytes);
}
private void _FillVB()
{
Assert.Fatal(!_vb.IsNull, "doh");
// fill in vertex array
int sizeInBytes = numVerts * GFXVertexFormat.VertexSize;
GFXVertexFormat.PCTTBN[] vertices = TorqueUtil.GetScratchArray(numVerts);
int curIndex = 0;
foreach (VerletPoint p in VerletSystem.get().Points)
{
p.debugRender(ref vertices, ref curIndex);
}
_vb.Instance.SetData(vertices, 0, numVerts);
}
public override void Render(SceneRenderState srs)
{
// Render stuff
_CreateVB();
_FillVB();
Box3F b = VerletSystem.get().bounds();
Vector3 scaleVec = (b.Max - b.Min) / 2.0f;
Vector3 faceScaleVec = scaleVec;
faceScaleVec *= 0.8f; // fudge
Matrix scale = Matrix.CreateScale(faceScaleVec);
Matrix trans = Matrix.CreateTranslation(b.Center);
Matrix objToWorld = scale * trans;
_facequad.Render(objToWorld, 1.0f, srs);
GraphicsDevice d3d = srs.Gfx.Device;
RenderInstance ri = SceneRenderer.RenderManager.AllocateInstance();
ri.Type = RenderInstance.RenderInstanceType.Mesh2D;
ri.ObjectTransform = Matrix.Identity;
ri.VertexBuffer = _vb.Instance;
ri.PrimitiveType = PrimitiveType.LineList;
ri.VertexSize = GFXVertexFormat.VertexSize;
ri.VertexDeclaration = GFXVertexFormat.GetVertexDeclaration(d3d);
ri.VertexCount = numVerts;
ri.BaseVertex = 0;
ri.PrimitiveCount = numVerts - 1;
ri.Opacity = VisibilityLevel;
ri.UTextureAddressMode = TextureAddressMode.Clamp;
ri.VTextureAddressMode = TextureAddressMode.Clamp;
ri.Material = _material;
SceneRenderer.RenderManager.AddInstance(ri);
Vector3 earScaleVec = scaleVec;
earScaleVec.Y = 5.0f;
Vector3 eartrans = b.Center;
eartrans.Y -= scaleVec.Y * 0.65f;
scale = Matrix.CreateScale(earScaleVec);
trans = Matrix.CreateTranslation(eartrans);
objToWorld = scale * trans;
_earquad.Render(objToWorld, 1.0f, srs);
base.Render(srs);
}
public override bool OnRegister()
{
if (!base.OnRegister())
return false;
SetTicking();
createBlob(40, new Vector3(0.0f, 0.0f, 0.0f), 8.0f, 10.0f, 1.0f, 8.0f);
return true;
}
public void createBlob(int segments, Vector3 pos, float inner, float outer, float force, float inner_force)
{
float angle_step = 2.0f * (float) Math.PI / (float) segments;
float outer_segment_length = 2.0f * outer * (float) Math.Sin(angle_step / 2.0);
float inner_segment_length = 2.0f * inner * (float) Math.Sin(angle_step / 2.0);
float ring_gap = outer-inner;
float yBounds = 30.0f;
LineConstraint lowerLine = new LineConstraint(yBounds, LineConstraint.ConstraintType.Above);
LineConstraint upperLine = new LineConstraint(-yBounds, LineConstraint.ConstraintType.Below);
float xBounds = 45.0f;
LineConstraint leftLine = new LineConstraint(xBounds, LineConstraint.ConstraintType.Left);
LineConstraint rightLine = new LineConstraint(-xBounds, LineConstraint.ConstraintType.Right);
List points = new List();
VerletPoint mid = VerletSystem.get().createPoint(pos);
// create outer point (which lie on a circle)
for (int i=0;i 0.0f)
Game.Instance.Exit();
}
private void _SetupInputMap(TorqueObject player, int playerIndex, String gamePad, String keyboard)
{
// Set player as the controllable object
PlayerManager.Instance.GetPlayer(playerIndex).ControlObject = player;
// Get input map for this player and configure it
InputMap inputMap = PlayerManager.Instance.GetPlayer(playerIndex).InputMap;
int gamepadId = InputManager.Instance.FindDevice(gamePad);
if (gamepadId >= 0)
{
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftThumbX, MoveMapTypes.StickAnalogHorizontal, 0);
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftThumbY, MoveMapTypes.StickAnalogVertical, 0);
inputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.Back, _OnBackButton);
}
// keyboard controls
int keyboardId = InputManager.Instance.FindDevice(keyboard);
if (keyboardId >= 0)
{
inputMap.BindMove(keyboardId, (int)Keys.Right, MoveMapTypes.StickDigitalRight, 0);
inputMap.BindMove(keyboardId, (int)Keys.Left, MoveMapTypes.StickDigitalLeft, 0);
inputMap.BindMove(keyboardId, (int)Keys.Up, MoveMapTypes.StickDigitalUp, 0);
inputMap.BindMove(keyboardId, (int)Keys.Down, MoveMapTypes.StickDigitalDown, 0);
// WASD
inputMap.BindMove(keyboardId, (int)Keys.D, MoveMapTypes.StickDigitalRight, 0);
inputMap.BindMove(keyboardId, (int)Keys.A, MoveMapTypes.StickDigitalLeft, 0);
inputMap.BindMove(keyboardId, (int)Keys.W, MoveMapTypes.StickDigitalUp, 0);
inputMap.BindMove(keyboardId, (int)Keys.S, MoveMapTypes.StickDigitalDown, 0);
}
}
#endregion
//======================================================
#region Private, protected, internal fields
BlobComponent _blob;
int _playerNumber = 0;
#endregion
}
}