Implement llCastRay fully, simplified.

inv-download
Magnuz Binder 2015-05-03 07:50:13 +02:00 committed by dahlia
parent 30f9e5372e
commit 43b8bd0c35
2 changed files with 797 additions and 1 deletions

View File

@ -218,6 +218,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected float m_lABB2SitZ0 = -0.25f; protected float m_lABB2SitZ0 = -0.25f;
protected float m_lABB2SitZ1 = 0.25f; protected float m_lABB2SitZ1 = 0.25f;
protected float m_primSafetyCoeffX = 2.414214f;
protected float m_primSafetyCoeffY = 2.414214f;
protected float m_primSafetyCoeffZ = 1.618034f;
protected float m_floatToleranceInCastRay = 0.000001f;
protected float m_floatTolerance2InCastRay = 0.0001f;
protected int m_maxHitsInCastRay = 16;
protected int m_maxHitsPerPrimInCastRay = 16;
protected int m_maxHitsPerObjectInCastRay = 16;
protected bool m_detectExitsInCastRay = false;
protected bool m_filterPartsInCastRay = false;
protected bool m_doAttachmentsInCastRay = false;
protected bool m_useCastRayV1 = true;
//An array of HTTP/1.1 headers that are not allowed to be used //An array of HTTP/1.1 headers that are not allowed to be used
//as custom headers by llHTTPRequest. //as custom headers by llHTTPRequest.
private string[] HttpStandardHeaders = private string[] HttpStandardHeaders =
@ -320,6 +333,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
m_lABB1SitZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZcoeff", m_lABB1SitZ1); m_lABB1SitZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZcoeff", m_lABB1SitZ1);
m_lABB2SitZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZconst", m_lABB2SitZ0); m_lABB2SitZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZconst", m_lABB2SitZ0);
m_lABB2SitZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZcoeff", m_lABB2SitZ1); m_lABB2SitZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZcoeff", m_lABB2SitZ1);
m_primSafetyCoeffX = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientX", m_primSafetyCoeffX);
m_primSafetyCoeffY = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientY", m_primSafetyCoeffY);
m_primSafetyCoeffZ = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientZ", m_primSafetyCoeffZ);
m_floatToleranceInCastRay = lslConfig.GetFloat("FloatToleranceInLlCastRay", m_floatToleranceInCastRay);
m_floatTolerance2InCastRay = lslConfig.GetFloat("FloatTolerance2InLlCastRay", m_floatTolerance2InCastRay);
m_maxHitsInCastRay = lslConfig.GetInt("MaxHitsInLlCastRay", m_maxHitsInCastRay);
m_maxHitsPerPrimInCastRay = lslConfig.GetInt("MaxHitsPerPrimInLlCastRay", m_maxHitsPerPrimInCastRay);
m_maxHitsPerObjectInCastRay = lslConfig.GetInt("MaxHitsPerObjectInLlCastRay", m_maxHitsPerObjectInCastRay);
m_detectExitsInCastRay = lslConfig.GetBoolean("DetectExitHitsInLlCastRay", m_detectExitsInCastRay);
m_filterPartsInCastRay = lslConfig.GetBoolean("FilterPartsInLlCastRay", m_filterPartsInCastRay);
m_doAttachmentsInCastRay = lslConfig.GetBoolean("DoAttachmentsInLlCastRay", m_doAttachmentsInCastRay);
m_useCastRayV1 = lslConfig.GetBoolean("UseLlCastRayV1", m_useCastRayV1);
} }
IConfig smtpConfig = seConfigSource.Configs["SMTP"]; IConfig smtpConfig = seConfigSource.Configs["SMTP"];
@ -13738,7 +13763,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return contacts[0]; return contacts[0];
} }
public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options) public LSL_List llCastRayV1(LSL_Vector start, LSL_Vector end, LSL_List options)
{ {
LSL_List list = new LSL_List(); LSL_List list = new LSL_List();
@ -13929,6 +13954,739 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return list; return list;
} }
/// <summary>
/// Full implementation of llCastRay similar to SL 2015-04-21.
/// http://wiki.secondlife.com/wiki/LlCastRay
/// Uses pure geometry, bounding shapes, meshing and no physics
/// for prims, sculpts, meshes, avatars and terrain.
/// Implements all flags, reject types and data flags.
/// Can handle both objects/groups and prims/parts, by config.
/// May give poor results with multi-part meshes where "root"
/// part doesn't dominate, owing to "guessed" bounding boxes.
/// May sometimes be inaccurate owing to calculation precision
/// and a bug in libopenmetaverse PrimMesher.
/// </summary>
public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options)
{
// Use llCastRay v1 if configured
if (m_useCastRayV1)
return llCastRayV1(start, end, options);
// Initialize
m_host.AddScriptLPS(1);
List<RayHit> rayHits = new List<RayHit>();
LSL_List result = new LSL_List();
float tol = m_floatToleranceInCastRay;
float tol2 = m_floatTolerance2InCastRay;
// Get input options
int rejectTypes = 0;
int dataFlags = 0;
int maxHits = 1;
bool detectPhantom = false;
for (int i = 0; i < options.Length; i += 2)
{
if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES)
rejectTypes = options.GetLSLIntegerItem(i + 1);
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DATA_FLAGS)
dataFlags = options.GetLSLIntegerItem(i + 1);
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS)
maxHits = options.GetLSLIntegerItem(i + 1);
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM)
detectPhantom = (options.GetLSLIntegerItem(i + 1) != 0);
}
if (maxHits > m_maxHitsInCastRay)
maxHits = m_maxHitsInCastRay;
bool rejectAgents = ((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) != 0);
bool rejectPhysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) != 0);
bool rejectNonphysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) != 0);
bool rejectLand = ((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) != 0);
bool getNormal = ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) != 0);
bool getRootKey = ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) != 0);
bool getLinkNum = ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) != 0);
// Calculate some basic parameters
Vector3 ray = end - start;
float rayLength = ray.Length();
// Try to get a mesher and return failure if none or degenerate ray
IRendering primMesher = null;
List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
if (renderers.Count < 1 || rayLength < tol)
{
result.Add(new LSL_Integer(ScriptBaseClass.RCERR_UNKNOWN));
return result;
}
primMesher = RenderingLoader.LoadRenderer(renderers[0]);
// Used to translate and rotate world so ray is along negative Z axis from origo and
// calculations mostly simplified to a 2D projecttion on the X-Y plane
Vector3 posProj = new Vector3(-start);
Quaternion rotProj = Vector3.RotationBetween(ray, new Vector3(0.0f, 0.0f, -1.0f));
Quaternion rotBack = Quaternion.Inverse(rotProj);
// Iterate over all objects/groups and prims/parts in region
World.ForEachSOG(
delegate(SceneObjectGroup group)
{
// Check group filters unless part filters are configured
bool isPhysical = (group.RootPart != null && group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical);
bool isNonphysical = !isPhysical;
bool isPhantom = group.IsPhantom || group.IsVolumeDetect;
bool isAttachment = group.IsAttachment;
bool doGroup = true;
if (isPhysical && rejectPhysical)
doGroup = false;
if (isNonphysical && rejectNonphysical)
doGroup = false;
if (isPhantom && detectPhantom)
doGroup = true;
if (m_filterPartsInCastRay)
doGroup = true;
if (isAttachment && !m_doAttachmentsInCastRay)
doGroup = false;
// Parse object/group if passed filters
if (doGroup)
{
// Iterate over all prims/parts in object/group
foreach(SceneObjectPart part in group.Parts)
{
// Check part filters if configured
if (m_filterPartsInCastRay)
{
isPhysical = (part.PhysActor != null && part.PhysActor.IsPhysical);
isNonphysical = !isPhysical;
isPhantom = ((part.Flags & PrimFlags.Phantom) != 0) || (part.VolumeDetectActive);
bool doPart = true;
if (isPhysical && rejectPhysical)
doPart = false;
if (isNonphysical && rejectNonphysical)
doPart = false;
if (isPhantom && detectPhantom)
doPart = true;
if (!doPart)
continue;
}
// Parse prim/part if passed filters
// Estimate bounding box from size box
Vector3 scaleSafe = part.Scale;
if (!part.Shape.SculptEntry)
scaleSafe = scaleSafe * (new Vector3(m_primSafetyCoeffX, m_primSafetyCoeffY, m_primSafetyCoeffZ));
// Filter parts by bounding shapes
Vector3 posPartRel = part.GetWorldPosition() + posProj;
Vector3 posPartProj = posPartRel * rotProj;
if (InBoundingShapes(ray, rayLength, scaleSafe, posPartRel, posPartProj, rotProj))
{
// Prepare data needed to check for ray hits
RayTrans rayTrans = new RayTrans();
rayTrans.PartId = part.UUID;
rayTrans.GroupId = part.ParentGroup.UUID;
rayTrans.Link = group.PrimCount > 1 ? part.LinkNum : 0;
rayTrans.Scale = part.Scale;
rayTrans.PositionPartProj = posPartProj;
rayTrans.PositionProj = posProj;
rayTrans.RotationPartProj = rotProj * part.GetWorldRotation();
rayTrans.RotationBack = rotBack;
rayTrans.NeedsEnds = true;
rayTrans.RayLength = rayLength;
rayTrans.Tolerance = tol;
rayTrans.Tolerance2 = tol2;
// Make an OMV prim to be able to mesh part
Primitive omvPrim = part.Shape.ToOmvPrimitive(posPartProj, rayTrans.RotationPartProj);
byte[] sculptAsset = null;
if (omvPrim.Sculpt != null)
sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
// When part is mesh, get and check mesh
if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null)
{
AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
FacetedMesh mesh = null;
FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out mesh);
meshAsset = null;
AddRayInFacetedMesh(mesh, rayTrans, ref rayHits);
mesh = null;
}
// When part is sculpt, create and check mesh
// Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt.
else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null)
{
IJ2KDecoder imgDecoder = World.RequestModuleInterface<IJ2KDecoder>();
if (imgDecoder != null)
{
Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
if (sculpt != null)
{
SimpleMesh mesh = primMesher.GenerateSimpleSculptMesh(omvPrim, (Bitmap)sculpt, DetailLevel.Medium);
sculpt.Dispose();
AddRayInSimpleMesh(mesh, rayTrans, ref rayHits);
mesh = null;
}
}
}
// When part is prim, create and check mesh
else if (omvPrim.Sculpt == null)
{
if (
omvPrim.PrimData.PathBegin == 0.0 && omvPrim.PrimData.PathEnd == 1.0 &&
omvPrim.PrimData.PathTaperX == 0.0 && omvPrim.PrimData.PathTaperY == 0.0 &&
omvPrim.PrimData.PathSkew == 0.0 &&
omvPrim.PrimData.PathTwist - omvPrim.PrimData.PathTwistBegin == 0.0
)
rayTrans.NeedsEnds = false;
SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium);
AddRayInSimpleMesh(mesh, rayTrans, ref rayHits);
mesh = null;
}
}
}
}
}
);
// Check avatar filter
if (!rejectAgents)
{
// Iterate over all avatars in region
World.ForEachRootScenePresence(
delegate (ScenePresence sp)
{
// Parse avatar
// Get bounding box
Vector3 lower;
Vector3 upper;
BoundingBoxOfScenePresence(sp, out lower, out upper);
Vector3 scale = upper - lower;
// Filter avatars by bounding shapes
Vector3 posPartRel = sp.AbsolutePosition + posProj + (lower + upper) * 0.5f * sp.Rotation;
Vector3 posPartProj = posPartRel * rotProj;
if (InBoundingShapes(ray, rayLength, scale, posPartRel, posPartProj, rotProj))
{
// Prepare data needed to check for ray hits
RayTrans rayTrans = new RayTrans();
rayTrans.PartId = sp.UUID;
rayTrans.GroupId = sp.ParentPart != null ? sp.ParentPart.ParentGroup.UUID : sp.UUID;
rayTrans.Link = sp.ParentPart != null ? UUID2LinkNumber(sp.ParentPart, sp.UUID) : 0;
rayTrans.Scale = scale;
rayTrans.PositionPartProj = posPartProj;
rayTrans.PositionProj = posProj;
rayTrans.RotationPartProj = rotProj * sp.Rotation;
rayTrans.RotationBack = rotBack;
rayTrans.NeedsEnds = false;
rayTrans.RayLength = rayLength;
rayTrans.Tolerance = tol;
rayTrans.Tolerance2 = tol2;
// Make OMV prim, create and check mesh
Primitive omvPrim = MakeOpenMetaversePrim(scale, posPartProj, rayTrans.RotationPartProj, ScriptBaseClass.PRIM_TYPE_SPHERE);
SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium);
AddRayInSimpleMesh(mesh, rayTrans, ref rayHits);
mesh = null;
}
}
);
}
// Check terrain filter
if (!rejectLand)
{
// Parse terrain
// Mesh terrain and check projected bounding box
Vector3 posPartProj = posProj * rotProj;
Quaternion rotPartProj = rotProj;
Vector3 lower;
Vector3 upper;
List<Tri> triangles = TrisFromHeightmapUnderRay(start, end, out lower, out upper);
Vector3 lowerBox = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 upperBox = new Vector3(float.MinValue, float.MinValue, float.MinValue);
int dummy = 0;
AddBoundingBoxOfSimpleBox(lower, upper, posPartProj, rotPartProj, true, ref lowerBox, ref upperBox, ref dummy);
if (lowerBox.X <= tol && lowerBox.Y <= tol && lowerBox.Z <= tol && upperBox.X >= -tol && upperBox.Y >= -tol && upperBox.Z >= -rayLength - tol)
{
// Prepare data needed to check for ray hits
RayTrans rayTrans = new RayTrans();
rayTrans.PartId = UUID.Zero;
rayTrans.GroupId = UUID.Zero;
rayTrans.Link = 0;
rayTrans.Scale = new Vector3 (1.0f, 1.0f, 1.0f);
rayTrans.PositionPartProj = posPartProj;
rayTrans.PositionProj = posProj;
rayTrans.RotationPartProj = rotPartProj;
rayTrans.RotationBack = rotBack;
rayTrans.NeedsEnds = true;
rayTrans.RayLength = rayLength;
rayTrans.Tolerance = tol;
rayTrans.Tolerance2 = tol2;
// Check mesh
AddRayInTris(triangles, rayTrans, ref rayHits);
triangles = null;
}
}
// Sort hits by ascending distance
rayHits.Sort((s1, s2) => s1.Distance.CompareTo(s2.Distance));
// Check excess hits per part and group
for (int t = 0; t < 2; t++)
{
int maxHitsPerType = 0;
UUID id = UUID.Zero;
if (t == 0)
maxHitsPerType = m_maxHitsPerPrimInCastRay;
else
maxHitsPerType = m_maxHitsPerObjectInCastRay;
// Handle excess hits only when needed
if (maxHitsPerType < m_maxHitsInCastRay)
{
// Find excess hits
Hashtable hits = new Hashtable();
for (int i = rayHits.Count - 1; i >= 0; i--)
{
if (t == 0)
id = rayHits[i].PartId;
else
id = rayHits[i].GroupId;
if (hits.ContainsKey(id))
hits[id] = (int)hits[id] + 1;
else
hits[id] = 1;
}
// Remove excess hits
for (int i = rayHits.Count - 1; i >= 0; i--)
{
if (t == 0)
id = rayHits[i].PartId;
else
id = rayHits[i].GroupId;
int hit = (int)hits[id];
if (hit > m_maxHitsPerPrimInCastRay)
{
rayHits.RemoveAt(i);
hit--;
hits[id] = hit;
}
}
}
}
// Parse hits into result list according to data flags
int hitCount = rayHits.Count;
if (hitCount > maxHits)
hitCount = maxHits;
for (int i = 0; i < hitCount; i++)
{
RayHit rayHit = rayHits[i];
if (getRootKey)
result.Add(new LSL_Key(rayHit.GroupId.ToString()));
else
result.Add(new LSL_Key(rayHit.PartId.ToString()));
result.Add(new LSL_Vector(rayHit.Position));
if (getLinkNum)
result.Add(new LSL_Integer(rayHit.Link));
if (getNormal)
result.Add(new LSL_Vector(rayHit.Normal));
}
result.Add(new LSL_Integer(hitCount));
return result;
}
/// <summary>
/// Struct for transmitting parameters required for finding llCastRay ray hits.
/// </summary>
public struct RayTrans
{
public UUID PartId;
public UUID GroupId;
public int Link;
public Vector3 Scale;
public Vector3 PositionPartProj;
public Vector3 PositionProj;
public Quaternion RotationPartProj;
public Quaternion RotationBack;
public bool NeedsEnds;
public float RayLength;
public float Tolerance;
public float Tolerance2;
}
/// <summary>
/// Struct for llCastRay ray hits.
/// </summary>
public struct RayHit
{
public UUID PartId;
public UUID GroupId;
public int Link;
public Vector3 Position;
public Vector3 Normal;
public float Distance;
}
/// <summary>
/// Helper to parse SimpleMesh for ray hits.
/// </summary>
private void AddRayInSimpleMesh(SimpleMesh mesh, RayTrans rayTrans, ref List<RayHit> rayHits)
{
if (mesh != null)
{
for (int i = 0; i < mesh.Indices.Count; i += 3)
{
Tri triangle = new Tri();
triangle.p1 = mesh.Vertices[mesh.Indices[i]].Position;
triangle.p2 = mesh.Vertices[mesh.Indices[i + 1]].Position;
triangle.p3 = mesh.Vertices[mesh.Indices[i + 2]].Position;
AddRayInTri(triangle, rayTrans, ref rayHits);
}
}
}
/// <summary>
/// Helper to parse FacetedMesh for ray hits.
/// </summary>
private void AddRayInFacetedMesh(FacetedMesh mesh, RayTrans rayTrans, ref List<RayHit> rayHits)
{
if (mesh != null)
{
foreach (Face face in mesh.Faces)
{
for (int i = 0; i <face.Indices.Count; i += 3)
{
Tri triangle = new Tri();
triangle.p1 = face.Vertices[face.Indices[i]].Position;
triangle.p2 = face.Vertices[face.Indices[i + 1]].Position;
triangle.p3 = face.Vertices[face.Indices[i + 2]].Position;
AddRayInTri(triangle, rayTrans, ref rayHits);
}
}
}
}
/// <summary>
/// Helper to parse Tri (triangle) List for ray hits.
/// </summary>
private void AddRayInTris(List<Tri> triangles, RayTrans rayTrans, ref List<RayHit> rayHits)
{
foreach (Tri triangle in triangles)
{
AddRayInTri(triangle, rayTrans, ref rayHits);
}
}
/// <summary>
/// Helper to add ray hit in a Tri (triangle).
/// </summary>
private void AddRayInTri(Tri triangle, RayTrans rayTrans, ref List<RayHit> rayHits)
{
// Check for hit in triangle
float distance;
Vector3 posHit;
Vector3 normal;
if (HitRayInTri(triangle, rayTrans, out distance, out posHit, out normal))
{
// Project hit part back to normal coordinate system
Vector3 posPart = rayTrans.PositionPartProj * rayTrans.RotationBack - rayTrans.PositionProj;
// Hack to circumvent ghost face bug in PrimMesher by removing hits in (ghost) faces plane through shape center
if (Math.Abs(Vector3.Dot(posPart, normal) - Vector3.Dot(posHit, normal)) < rayTrans.Tolerance && !rayTrans.NeedsEnds)
return;
// Remove duplicate hits at triangle edges and intersections
for (int i = rayHits.Count - 1; i >= 0; i--)
{
if (rayHits[i].PartId == rayTrans.PartId && Math.Abs(rayHits[i].Distance - distance) < rayTrans.Tolerance2)
return;
}
// Build result data set
RayHit rayHit = new RayHit();
rayHit.PartId = rayTrans.PartId;
rayHit.GroupId = rayTrans.GroupId;
rayHit.Link = rayTrans.Link;
rayHit.Position = posHit;
rayHit.Normal = normal;
rayHit.Distance = distance;
rayHits.Add(rayHit);
}
}
/// <summary>
/// Helper to find ray hit in a Tri (triangle).
/// </summary>
private bool HitRayInTri(Tri triangle, RayTrans rayTrans, out float distance, out Vector3 posHit, out Vector3 normal)
{
// Initialize
distance = 0.0f;
posHit = Vector3.Zero;
normal = Vector3.Zero;
float tol = rayTrans.Tolerance;
// Project triangle on X-Y plane
Vector3 pos1 = triangle.p1 * rayTrans.Scale * rayTrans.RotationPartProj + rayTrans.PositionPartProj;
Vector3 pos2 = triangle.p2 * rayTrans.Scale * rayTrans.RotationPartProj + rayTrans.PositionPartProj;
Vector3 pos3 = triangle.p3 * rayTrans.Scale * rayTrans.RotationPartProj + rayTrans.PositionPartProj;
// Check if ray/origo inside triangle bounding rectangle
Vector3 lower = Vector3.Min(pos1, Vector3.Min(pos2, pos3));
Vector3 upper = Vector3.Max(pos1, Vector3.Max(pos2, pos3));
if (lower.X > tol || lower.Y > tol || lower.Z > tol || upper.X < -tol || upper.Y < -tol || upper.Z < -rayTrans.RayLength - tol)
return false;
// Check if ray/origo inside every edge or reverse "outside" every edge on exit
float dist;
bool inside = true;
bool outside = true;
Vector3 vec1 = pos2 - pos1;
dist = pos1.X * vec1.Y - pos1.Y * vec1.X;
if (dist < -tol)
inside = false;
if (dist > tol)
outside = false;
Vector3 vec2 = pos3 - pos2;
dist = pos2.X * vec2.Y - pos2.Y * vec2.X;
if (dist < -tol)
inside = false;
if (dist > tol)
outside = false;
Vector3 vec3 = pos1 - pos3;
dist = pos3.X * vec3.Y - pos3.Y * vec3.X;
if (dist < -tol)
inside = false;
if (dist > tol)
outside = false;
// Skip if ray/origo outside
if (!inside && !(outside && m_detectExitsInCastRay))
return false;
// Calculate normal
Vector3 normalProj = Vector3.Cross(vec1, vec2);
float normalLength = normalProj.Length();
// Skip if degenerate triangle
if (normalLength < tol)
return false;
normalProj = normalProj / normalLength;
// Skip if ray parallell to triangle plane
if (Math.Abs(normalProj.Z) < tol)
return false;
// Calculate distance
distance = Vector3.Dot(normalProj, pos2) / normalProj.Z * -1.0f;
// Skip if outside ray
if (distance < -tol || distance > rayTrans.RayLength + tol)
return false;
// Calculate projected hit position
Vector3 posHitProj = new Vector3(0.0f, 0.0f, -distance);
// Project hit back to normal coordinate system
posHit = posHitProj * rayTrans.RotationBack - rayTrans.PositionProj;
normal = normalProj * rayTrans.RotationBack;
return true;
}
/// <summary>
/// Helper to parse selected parts of HeightMap into a Tri (triangle) List and calculate bounding box.
/// </summary>
private List<Tri> TrisFromHeightmapUnderRay(Vector3 posStart, Vector3 posEnd, out Vector3 lower, out Vector3 upper)
{
// Get bounding X-Y rectangle of terrain under ray
lower = Vector3.Min(posStart, posEnd);
upper = Vector3.Max(posStart, posEnd);
lower.X = (float)Math.Floor(lower.X);
lower.Y = (float)Math.Floor(lower.Y);
float zLower = float.MaxValue;
upper.X = (float)Math.Ceiling(upper.X);
upper.Y = (float)Math.Ceiling(upper.Y);
float zUpper = float.MinValue;
// Initialize Tri (triangle) List
List<Tri> triangles = new List<Tri>();
// Set parsing lane direction to major ray X-Y axis
Vector3 vec = posEnd - posStart;
float xAbs = Math.Abs(vec.X);
float yAbs = Math.Abs(vec.Y);
bool bigX = true;
if (yAbs > xAbs)
{
bigX = false;
vec = vec / yAbs;
}
else if (xAbs > yAbs || xAbs > 0.0f)
vec = vec / xAbs;
else
vec = new Vector3(1.0f, 1.0f, 0.0f);
// Simplify by start parsing in lower end of lane
if ((bigX && vec.X < 0.0f) || (!bigX && vec.Y < 0.0f))
{
Vector3 posTemp = posStart;
posStart = posEnd;
posEnd = posTemp;
vec = vec * -1.0f;
}
// First 1x1 rectangle under ray
float xFloorOld = 0.0f;
float yFloorOld = 0.0f;
Vector3 pos = posStart;
float xFloor = (float)Math.Floor(pos.X);
float yFloor = (float)Math.Floor(pos.Y);
AddTrisFromHeightmap(xFloor, yFloor, ref triangles, ref zLower, ref zUpper);
// Parse every remaining 1x1 rectangle under ray
while (pos != posEnd)
{
// Next 1x1 rectangle under ray
xFloorOld = xFloor;
yFloorOld = yFloor;
pos = pos + vec;
// Clip position to 1x1 rectangle border
xFloor = (float)Math.Floor(pos.X);
yFloor = (float)Math.Floor(pos.Y);
if (bigX && pos.X > xFloor)
{
pos.Y -= vec.Y * (pos.X - xFloor);
pos.X = xFloor;
}
else if (!bigX && pos.Y > yFloor)
{
pos.X -= vec.X * (pos.Y - yFloor);
pos.Y = yFloor;
}
// Last 1x1 rectangle under ray
if ((bigX && pos.X >= posEnd.X) || (!bigX && pos.Y >= posEnd.Y))
{
pos = posEnd;
xFloor = (float)Math.Floor(pos.X);
yFloor = (float)Math.Floor(pos.Y);
}
// Add new 1x1 rectangle in lane
if ((bigX && xFloor != xFloorOld) || (!bigX && yFloor != yFloorOld))
AddTrisFromHeightmap(xFloor, yFloor, ref triangles, ref zLower, ref zUpper);
// Add last 1x1 rectangle in old lane at lane shift
if (bigX && yFloor != yFloorOld)
AddTrisFromHeightmap(xFloor, yFloorOld, ref triangles, ref zLower, ref zUpper);
if (!bigX && xFloor != xFloorOld)
AddTrisFromHeightmap(xFloorOld, yFloor, ref triangles, ref zLower, ref zUpper);
}
// Finalize bounding box Z
lower.Z = zLower;
upper.Z = zUpper;
// Done and returning Tri (triangle)List
return triangles;
}
/// <summary>
/// Helper to add HeightMap squares into Tri (triangle) List and adjust bounding box.
/// </summary>
private void AddTrisFromHeightmap(float xPos, float yPos, ref List<Tri> triangles, ref float zLower, ref float zUpper)
{
int xInt = (int)xPos;
int yInt = (int)yPos;
// Corner 1 of 1x1 rectangle
int x = Util.Clamp<int>(xInt+1, 0, World.Heightmap.Width - 1);
int y = Util.Clamp<int>(yInt+1, 0, World.Heightmap.Height - 1);
Vector3 pos1 = new Vector3(x, y, (float)World.Heightmap[x, y]);
// Adjust bounding box
zLower = Math.Min(zLower, pos1.Z);
zUpper = Math.Max(zUpper, pos1.Z);
// Corner 2 of 1x1 rectangle
x = Util.Clamp<int>(xInt, 0, World.Heightmap.Width - 1);
y = Util.Clamp<int>(yInt+1, 0, World.Heightmap.Height - 1);
Vector3 pos2 = new Vector3(x, y, (float)World.Heightmap[x, y]);
// Adjust bounding box
zLower = Math.Min(zLower, pos1.Z);
zUpper = Math.Max(zUpper, pos1.Z);
// Corner 3 of 1x1 rectangle
x = Util.Clamp<int>(xInt, 0, World.Heightmap.Width - 1);
y = Util.Clamp<int>(yInt, 0, World.Heightmap.Height - 1);
Vector3 pos3 = new Vector3(x, y, (float)World.Heightmap[x, y]);
// Adjust bounding box
zLower = Math.Min(zLower, pos1.Z);
zUpper = Math.Max(zUpper, pos1.Z);
// Corner 4 of 1x1 rectangle
x = Util.Clamp<int>(xInt+1, 0, World.Heightmap.Width - 1);
y = Util.Clamp<int>(yInt, 0, World.Heightmap.Height - 1);
Vector3 pos4 = new Vector3(x, y, (float)World.Heightmap[x, y]);
// Adjust bounding box
zLower = Math.Min(zLower, pos1.Z);
zUpper = Math.Max(zUpper, pos1.Z);
// Add triangle 1
Tri triangle1 = new Tri();
triangle1.p1 = pos1;
triangle1.p2 = pos2;
triangle1.p3 = pos3;
triangles.Add(triangle1);
// Add triangle 2
Tri triangle2 = new Tri();
triangle2.p1 = pos3;
triangle2.p2 = pos4;
triangle2.p3 = pos1;
triangles.Add(triangle2);
}
/// <summary>
/// Helper to check if a ray intersects bounding shapes.
/// </summary>
private bool InBoundingShapes(Vector3 ray, float rayLength, Vector3 scale, Vector3 posPartRel, Vector3 posPartProj, Quaternion rotProj)
{
float tol = m_floatToleranceInCastRay;
// Check if ray intersects projected bounding box
Vector3 lowerBox = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 upperBox = new Vector3(float.MinValue, float.MinValue, float.MinValue);
int dummy = 0;
AddBoundingBoxOfSimpleBox(scale * -0.5f, scale * 0.5f, posPartProj, rotProj, true, ref lowerBox, ref upperBox, ref dummy);
if (lowerBox.X > tol || lowerBox.Y > tol || lowerBox.Z > tol || upperBox.X < -tol || upperBox.Y < -tol || upperBox.Z < -rayLength - tol)
return false;
// Passed bounding shape filters, so return true
return true;
}
/// <summary>
/// Helper to get link number for a UUID.
/// </summary>
private int UUID2LinkNumber(SceneObjectPart part, UUID id)
{
SceneObjectGroup group = part.ParentGroup;
if (group != null)
{
// Parse every link for UUID
int linkCount = group.PrimCount + group.GetSittingAvatarsCount();
for (int link = linkCount; link > 0; link--)
{
ISceneEntity entity = GetLinkEntity(part, link);
// Return link number if UUID match
if (entity != null && entity.UUID == id)
return link;
}
}
// Return link number 0 if no links or UUID matches
return 0;
}
public LSL_Integer llManageEstateAccess(int action, string avatar) public LSL_Integer llManageEstateAccess(int action, string avatar)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);

View File

@ -1477,6 +1477,44 @@
; Avatar bounding box, upper Z value, coefficient to multiply with avatar height, when sitting ; Avatar bounding box, upper Z value, coefficient to multiply with avatar height, when sitting
UpperAvatarBoundingBoxSittingZcoeff = 0.25 UpperAvatarBoundingBoxSittingZcoeff = 0.25
; Safety coefficient for max bounding box from prim size box X coordinate
; Worst case is twisted and sheared box, 1+sqrt(2)
PrimBoundingBoxSafetyCoefficientX = 2.414214
; Safety coefficient for max bounding box from prim size box Y coordinate
; Worst case is twisted and sheared box, 1+sqrt(2)
PrimBoundingBoxSafetyCoefficientY = 2.414214
; Safety coefficient for max bounding box from prim size box Z coordinate
; Worst case is twisted tube, 0.5+sqrt(1.25)
PrimBoundingBoxSafetyCoefficientZ = 1.618034
; Accepted calculation precision error in calculations in llCastRay
FloatToleranceInLlCastRay = 0.000001
; Accepted distance difference between duplicate hits in llCastRay
FloatTolerance2InLlCastRay = 0.0001
; Maximum number of returned hits from llCastRay
MaxHitsInLlCastRay = 16
; Maximum number of returned hits per prim from llCastRay
MaxHitsPerPrimInLlCastRay = 16
; Maximum number of returned hits per object from llCastRay
MaxHitsPerObjectInLlCastRay = 16
; Report ray intersections with surfaces on exits from a prim as hits in llCastRay if true
DetectExitHitsInLlCastRay = false
; Filter on parts instead of groups in llCastRay if true
FilterPartsInLlCastRay = false
; Detect attachments in llCastRay if true
DoAttachmentsInLlCastRay = false
; Use legacy version 1 of llCastRay if true
UseLlCastRayV1 = true
[DataSnapshot] [DataSnapshot]
; The following set of configs pertains to search. ; The following set of configs pertains to search.