/* * Copyright (c) Contributors, https://github.com/jonc/osboids * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 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 objects at this position /// /// Initializes a new instance of the class. /// /// /// Scene. /// /// /// Centre. /// /// /// Width. /// /// /// Depth. /// /// /// Height. /// /// public FlowField (Scene scene, int minX, int maxX, int minY, int maxY, int minZ, int maxZ) { m_scene = scene; m_startX = Math.Max (BUFFER, minX); m_startY = Math.Max (BUFFER, minY); m_startZ = Math.Max (BUFFER, minZ); m_endX = Math.Min (Util.SCENE_SIZE - BUFFER, maxX); m_endY = Math.Min (Util.SCENE_SIZE - BUFFER, maxY); m_endZ = Math.Min (Util.SCENE_SIZE - BUFFER, maxZ); // build the flow field over the given bounds Initialize (); } /// /// build a flow field on the scene at the specified centre /// position in the scene and of extent given by width, depth and height. /// 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()) { //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); Vector3 pos = sog.AbsolutePosition; minX = Convert.ToInt32 (fminX + pos.X); maxX = Convert.ToInt32 (fmaxX + pos.X); minY = Convert.ToInt32 (fminY + pos.Y); maxY = Convert.ToInt32 (fmaxX + pos.Y); minZ = Convert.ToInt32 (fminZ + pos.Z); maxZ = Convert.ToInt32 (fmaxZ + pos.Z); for (int x = minX; x < maxX; x++) { for (int y = minY; y < maxY; y++) { for (int z = minZ; z < maxZ; z++) { if( inBounds(x,y,z) ) { m_field [x, y, z] = sog.UUID; } else { Console.WriteLine(sog.Name + " OOB at " + sog.AbsolutePosition + " -> " + x + " " + y + " " + z); } } } } } } public bool ContainsPoint (Vector3 p) { return p.X > m_startX && p.X < m_endX && p.Y > m_startY && p.Y < m_endY && p.Z > m_startZ && p.Z < m_endZ; } private bool inBounds (int x, int y, int z) { return x >= 0 && x < 256 && y >= 0 && y < 256 && z >= 0; } #if false public Vector3 AdjustVelocity (Boid boid, float lookAheadDist) { Vector3 normVel = Vector3.Normalize (boid.Velocity); Vector3 loc = boid.Location; Vector3 inFront = loc + normVel * lookAheadDist; Vector3 adjustedDestintation = FieldStrength (loc, boid.Size, inFront); Vector3 newVel = Vector3.Normalize (adjustedDestintation - loc) * Vector3.Mag (boid.Velocity); float mOrigVel = Vector3.Mag(boid.Velocity); float mNewVel = Vector3.Mag(newVel); if( mNewVel != 0f && mNewVel > mOrigVel ) { newVel *= mOrigVel / mNewVel; } return newVel; } #endif public Vector3 FieldStrength (Vector3 currentPos, Vector3 size, Vector3 targetPos) { float length = size.X/2; float width = size.Y/2; float height = size.Z/2; //keep us in bounds targetPos.X = Math.Min( targetPos.X, m_endX - length ); targetPos.X = Math.Max(targetPos.X, m_startX + length); targetPos.Y = Math.Min( targetPos.Y, m_endY - width ); targetPos.Y = Math.Max(targetPos.Y, m_startY + width); targetPos.Z = Math.Min( targetPos.Z, m_endZ - height ); targetPos.Z = Math.Max(targetPos.Z, m_startZ + height); int count = 0; //now get the field strength at the inbounds position UUID collider = LookUp (targetPos); while (collider != UUID.Zero && count < 100) { count++; if (collider == TERRAIN) { // ground height at currentPos and dest averaged float h1 = m_scene.GetGroundHeight (currentPos.X, currentPos.Y); float h2 = m_scene.GetGroundHeight (targetPos.X, targetPos.Y); float h = (h1 + h2) / 2; targetPos.Z = h + height; } else if (collider == EDGE) { //keep us in bounds targetPos.X = Math.Min( targetPos.X, m_endX - length ); targetPos.X = Math.Max(targetPos.X, m_startX + length); targetPos.Y = Math.Min( targetPos.Y, m_endY - width ); targetPos.Y = Math.Max(targetPos.Y, m_startY + width); targetPos.Z = Math.Min( targetPos.Z, m_endZ - height ); targetPos.Z = Math.Max(targetPos.Z, m_startZ + height); } 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); Vector3 pos = sog.AbsolutePosition; //keep us out of the sog // adjust up/down first if necessary // then turn left or right if (targetPos.Z > sogMinZ + pos.Z) targetPos.Z = (sogMinZ + pos.Z) - height; if (targetPos.Z < sogMaxZ + pos.Z) targetPos.Z = (sogMaxZ + pos.Z) + height; if (targetPos.X > sogMinX + pos.X) targetPos.X = (sogMinX + pos.X) - length; if (targetPos.Y > sogMinY + pos.Y) targetPos.Y = (sogMinY + pos.Y) - width; if (targetPos.X < sogMaxX + pos.X) targetPos.X = (sogMaxX + pos.X) + length; if (targetPos.Y < sogMaxY + pos.Y) targetPos.Y = (sogMaxY + pos.Y) + width; } } // we what is at the new target position collider = LookUp (targetPos); } return targetPos; } public UUID LookUp (Vector3 loc) { return m_field [(int)loc.X, (int)loc.Y, (int)loc.Z]; } public override string ToString () { return string.Format ("[FlowField]" + Environment.NewLine + "startX = {0}" + Environment.NewLine + "endX = {1}" + Environment.NewLine + "startY = {2}" + Environment.NewLine + "endY = {3}" + Environment.NewLine + "startZ = {4}" + Environment.NewLine + "endZ = {5}", m_startX, m_endX, m_startY, m_endY, m_startZ, m_endZ); } } }