194 lines
5.7 KiB
C#
194 lines
5.7 KiB
C#
using System;
|
|
using OpenMetaverse;
|
|
using OpenSim.Region.Framework.Scenes;
|
|
|
|
namespace Flocking
|
|
{
|
|
public class FlowField
|
|
{
|
|
private const int BUFFER = 5;
|
|
private Scene m_scene;
|
|
private float m_startX;
|
|
private float m_startY;
|
|
private float m_startZ;
|
|
private float m_endX;
|
|
private float m_endY;
|
|
private float m_endZ;
|
|
private UUID TERRAIN = UUID.Random ();
|
|
private UUID EDGE = UUID.Random ();
|
|
private UUID[,,] m_field = new UUID[256, 256, 256]; // field of the object at this position
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Flocking.FlowField"/> class.
|
|
/// </summary>
|
|
/// <param name='scene'>
|
|
/// Scene.
|
|
/// </param>
|
|
/// <param name='centre'>
|
|
/// Centre.
|
|
/// </param>
|
|
/// <param name='width'>
|
|
/// Width.
|
|
/// </param>
|
|
/// <param name='depth'>
|
|
/// Depth.
|
|
/// </param>
|
|
/// <param name='height'>
|
|
/// Height.
|
|
/// </param>
|
|
///
|
|
public FlowField (Scene scene, Vector3 centre, int width, int depth, int height)
|
|
{
|
|
m_scene = scene;
|
|
|
|
m_startX = Math.Max (BUFFER, centre.X - width / 2f);
|
|
m_startY = Math.Max (BUFFER, centre.Y - depth / 2f);
|
|
m_startZ = Math.Max (BUFFER, centre.Z - height / 2f);
|
|
m_endX = Math.Min (Util.SCENE_SIZE - BUFFER, centre.X + width / 2f);
|
|
m_endY = Math.Min (Util.SCENE_SIZE - BUFFER, centre.Y + depth / 2f);
|
|
m_endZ = Math.Min (Util.SCENE_SIZE - BUFFER, centre.Z + height / 2f);
|
|
|
|
// build the flow field over the given bounds
|
|
Initialize ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// build a flow field on the scene at the specified centre
|
|
/// position in the scene and of extent given by width, depth and height.
|
|
/// </summary>
|
|
public void Initialize ()
|
|
{
|
|
|
|
//fill in the boundaries
|
|
for (int x = 0; x < 256; x++) {
|
|
for (int y = 0; y < 256; y++) {
|
|
m_field [x, y, 0] = EDGE;
|
|
m_field [x, y, 255] = EDGE;
|
|
}
|
|
}
|
|
for (int x = 0; x < 256; x++) {
|
|
for (int z = 0; z < 256; z++) {
|
|
m_field [x, 0, z] = EDGE;
|
|
m_field [x, 255, z] = EDGE;
|
|
}
|
|
}
|
|
for (int y = 0; y < 256; y++) {
|
|
for (int z = 0; z < 256; z++) {
|
|
m_field [0, y, z] = EDGE;
|
|
m_field [255, y, z] = EDGE;
|
|
}
|
|
}
|
|
|
|
//fill in the terrain
|
|
for (int x = 0; x < 256; x++) {
|
|
for (int y = 0; y < 256; y++) {
|
|
int zMax = Convert.ToInt32 (m_scene.GetGroundHeight (x, y));
|
|
for (int z = 1; z < zMax; z++) {
|
|
m_field [x, y, z] = TERRAIN;
|
|
}
|
|
}
|
|
}
|
|
foreach (SceneObjectGroup sog in m_scene.Entities.GetAllByType<SceneObjectGroup>()) {
|
|
//todo: ignore phantom
|
|
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
|
|
int maxX, minX, maxY, minY, maxZ, minZ;
|
|
sog.GetAxisAlignedBoundingBoxRaw (out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ);
|
|
|
|
minX = Convert.ToInt32 (fminX);
|
|
maxX = Convert.ToInt32 (fmaxX);
|
|
minY = Convert.ToInt32 (fminY);
|
|
maxY = Convert.ToInt32 (fmaxX);
|
|
minZ = Convert.ToInt32 (fminZ);
|
|
maxZ = Convert.ToInt32 (fmaxZ);
|
|
|
|
for (int x = minX; x < maxX; x++) {
|
|
for (int y = minY; y < maxY; y++) {
|
|
for (int z = minZ; z < maxZ; z++) {
|
|
m_field [x, y, z] = sog.UUID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Vector3 AdjustVelocity (Boid boid, float lookAheadDist)
|
|
{
|
|
Vector3 normVel = Vector3.Normalize (boid.Velocity);
|
|
Vector3 loc = boid.Location;
|
|
Vector3 inFront = loc + normVel * lookAheadDist;
|
|
|
|
Vector3 adjustedDestintation = inFront + FieldStrength (loc, boid.Size, inFront);
|
|
Vector3 newVel = Vector3.Normalize (adjustedDestintation - loc) * Vector3.Mag (boid.Velocity);
|
|
return newVel;
|
|
}
|
|
|
|
public Vector3 FieldStrength (Vector3 current, Vector3 size, Vector3 inFront)
|
|
{
|
|
Vector3 retVal = Vector3.Zero;
|
|
float length = size.X/2;
|
|
float width = size.Y/2;
|
|
float height = size.Z/2;
|
|
|
|
//keep us in bounds
|
|
if (inFront.X > m_endX)
|
|
retVal.X -= inFront.X - m_endX - length;
|
|
if (inFront.Y > m_endY)
|
|
retVal.Y -= inFront.Y - m_endY - width;
|
|
if (inFront.Z > m_endZ)
|
|
retVal.Z -= inFront.Z - m_endZ - height;
|
|
if (inFront.X < m_startX)
|
|
retVal.X += m_startX - inFront.X + length;
|
|
if (inFront.Y < m_startY)
|
|
retVal.Y += m_startY - inFront.Y + width;
|
|
if (inFront.Z < m_startZ)
|
|
retVal.Z += m_startZ - inFront.Z + height;
|
|
|
|
//now get the field strength at the inbounds position
|
|
UUID collider = LookUp (inFront + retVal);
|
|
while (collider != UUID.Zero) {
|
|
if (collider == TERRAIN) {
|
|
// ground height at current and dest averaged
|
|
float h1 = m_scene.GetGroundHeight (current.X, current.Y);
|
|
float h2 = m_scene.GetGroundHeight (inFront.X, inFront.Y);
|
|
float h = (h1 + h2) / 2;
|
|
retVal.Z += h;
|
|
} else if (collider == EDGE) {
|
|
// we ain't ever going to hit these
|
|
} else {
|
|
//we have hit a SOG
|
|
SceneObjectGroup sog = m_scene.GetSceneObjectPart (collider).ParentGroup;
|
|
if (sog == null) {
|
|
Console.WriteLine (collider);
|
|
} else {
|
|
float sogMinX, sogMinY, sogMinZ, sogMaxX, sogMaxY, sogMaxZ;
|
|
sog.GetAxisAlignedBoundingBoxRaw (out sogMinX, out sogMaxX, out sogMinY, out sogMaxY, out sogMinZ, out sogMaxZ);
|
|
//keep us out of the sog
|
|
if (inFront.X > sogMinX)
|
|
retVal.X -= inFront.X - sogMinX - length;
|
|
if (inFront.Y > sogMinY)
|
|
retVal.Y -= inFront.Y - sogMinY - width;
|
|
if (inFront.Z > sogMinZ)
|
|
retVal.Z -= inFront.Z - sogMinZ - height;
|
|
if (inFront.X < sogMaxX)
|
|
retVal.X += sogMaxX - inFront.X + length;
|
|
if (inFront.Y < sogMaxY)
|
|
retVal.Y += sogMaxY - inFront.Y + width;
|
|
if (inFront.Z < sogMaxZ)
|
|
retVal.Z += sogMaxZ - inFront.Z + height;
|
|
}
|
|
}
|
|
collider = LookUp (inFront + retVal);
|
|
//inFront += retVal;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
public UUID LookUp (Vector3 loc)
|
|
{
|
|
return m_field [(int)loc.X, (int)loc.Y, (int)loc.Z];
|
|
}
|
|
}
|
|
}
|
|
|