Attached Files | 0001-Implement-llCastRay-V2.patch [^] (41,281 bytes) 2015-05-03 15:06 [Show Content] [Hide Content]From 3dfb8d80698cc061e0ba27966e8958dd866d8807 Mon Sep 17 00:00:00 2001
From: Magnuz Binder <magnuz@magnuz-se.com>
Date: Mon, 4 May 2015 00:03:22 +0200
Subject: [PATCH] Implement llCastRay V2.
---
.../Shared/Api/Implementation/LSL_Api.cs | 759 +++++++++++++++++++++
bin/OpenSimDefaults.ini | 43 ++
2 files changed, 802 insertions(+)
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
index e20e4c4..58b3930 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
@@ -218,6 +218,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected float m_lABB2SitZ0 = -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 bool m_useCastRayV2 = false;
+ protected int RC_USE_V2 = 512;
+ 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;
+
//An array of HTTP/1.1 headers that are not allowed to be used
//as custom headers by llHTTPRequest.
private string[] HttpStandardHeaders =
@@ -320,6 +334,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
m_lABB1SitZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZcoeff", m_lABB1SitZ1);
m_lABB2SitZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZconst", m_lABB2SitZ0);
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_useCastRayV2 = lslConfig.GetBoolean("UseLlCastRayV2", m_useCastRayV2);
+ RC_USE_V2 = lslConfig.GetInt("RC_USE_V2", RC_USE_V2);
+ 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);
}
IConfig smtpConfig = seConfigSource.Configs["SMTP"];
@@ -13767,6 +13794,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
rejectTypes = options.GetLSLIntegerItem(i + 1);
}
+ // Use llCastRay v2 if configured or requested
+ if (m_useCastRayV2 || (dataFlags & RC_USE_V2) == RC_USE_V2)
+ return llCastRayV2(start, end, options);
+
if (count > 16)
count = 16;
@@ -13929,6 +13960,734 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return list;
}
+ /// <summary>
+ /// 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 sometimes be inaccurate owing to calculation precision
+ /// and a bug in libopenmetaverse PrimMesher.
+ /// </summary>
+ public LSL_List llCastRayV2(LSL_Vector start, LSL_Vector end, LSL_List options)
+ {
+ // Initialize
+ // Keep AddScriptLPS commented while called from llCastRay
+ // 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, degenerate ray, or max 0 hits
+ IRendering primMesher = null;
+ List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
+ if (renderers.Count < 1 || rayLength < tol || m_maxHitsInCastRay < 1)
+ {
+ 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)
{
m_host.AddScriptLPS(1);
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini
index 55ca32f..b967fa9 100644
--- a/bin/OpenSimDefaults.ini
+++ b/bin/OpenSimDefaults.ini
@@ -1477,6 +1477,49 @@
; Avatar bounding box, upper Z value, coefficient to multiply with avatar height, when sitting
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
+
+ ; Use new version 2 of llCastRay as default if true
+ ; This gives better accuracy and speed on some servers, but may be slower on other servers
+ UseLlCastRayV2 = false
+
+ ; Irregular bit flag to use after RC_DATA_FLAGS in calls to llCastRay to use version 2
+ ; Bit flags RC_GET_NORMAL = 1, RC_GET_ROOT_KEY = 2 and RC_GET_LINK_NUM = 4 are already in regular use
+ RC_USE_V2 = 512
+
+ ; 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
[DataSnapshot]
; The following set of configs pertains to search.
--
1.9.1
llCastRayTest3.lsl.txt [^] (6,361 bytes) 2015-05-03 22:39 [Show Content] [Hide Content]integer dialog_channel;
integer dialog_handle;
list cmd_opts = ["-", "No agents", "No phys.", "No nonphys.", "No land", "Normal", "Root key", "Link num.", "Use V2", "Phantom", "Cast ray", "Info", "Reset"];
list cmd_masks = [0, RC_REJECT_AGENTS, RC_REJECT_PHYSICAL, RC_REJECT_NONPHYSICAL, RC_REJECT_LAND, RC_GET_NORMAL, RC_GET_ROOT_KEY, RC_GET_LINK_NUM, 512, TRUE, 0, 0, 0];
integer no_idx = 0;
integer info_idx = 11;
integer reset_idx = 12;
integer castray_idx = 10;
integer phantom_idx = 9;
list reject_idxs = [1, 2, 3, 4];
list data_idxs = [5, 6, 7, 8];
list stride_idxs = [5, 7];
list dialog_idxs = [10, 11, 12, 5, 6, 7, 1, 2, 3, 4, 9, 8];
list cmd_flags;
default
{
state_entry()
{
cmd_flags = [FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE];
llOwnerSay("Script "+llGetScriptName()+" running.");
//llOwnerSay(llDumpList2String(llGetPrimitiveParams([PRIM_TEXTURE, 0]), ";\n"));
//return;
llSetPrimitiveParams([
PRIM_NAME, "llCastRay Tester",
PRIM_DESC, "script object",
PRIM_TYPE, PRIM_TYPE_CYLINDER, PRIM_HOLE_DEFAULT, <0.0,1.0,0.0>, 0.0, ZERO_VECTOR, <1.0,1.0,0.0>, ZERO_VECTOR,
PRIM_SLICE, <0.0,1.0,0.0>,
PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_PRIM,
PRIM_MATERIAL, PRIM_MATERIAL_PLASTIC,
PRIM_PHYSICS, FALSE,
PRIM_TEMP_ON_REZ, FALSE,
PRIM_PHANTOM, TRUE,
PRIM_ROTATION, ZERO_ROTATION,
PRIM_SIZE, <0.02,0.02,2.0>,
PRIM_TEXT, "", <1.0,0.8,0.0>, 1.0,
PRIM_FLEXIBLE, FALSE, 0, 0.0, 0.0, 0.0, 0.0, ZERO_VECTOR,
PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 1.0, 0.0, 0.0,
PRIM_OMEGA, <0.0,0.0,1.0>, 0.0, 0.0,
PRIM_TEXTURE, ALL_SIDES, TEXTURE_BLANK, <1.0,1.0,0.0>, ZERO_VECTOR, 0.0,
PRIM_COLOR, 0, <1.0,1.0,0.0>, 1.0,
PRIM_COLOR, 1, <0.4,0.4,1.0>, 1.0,
PRIM_COLOR, 2, <1.0,0.0,0.0>, 1.0,
PRIM_BUMP_SHINY, ALL_SIDES, PRIM_SHINY_NONE, PRIM_BUMP_NONE,
PRIM_FULLBRIGHT, 0, TRUE,
PRIM_FULLBRIGHT, 1, FALSE,
PRIM_FULLBRIGHT, 2, TRUE,
PRIM_GLOW, ALL_SIDES, 0.0
]);
dialog_channel = llFloor(-1-llFrand(2147483647));
dialog_handle = llListen(dialog_channel, "", NULL_KEY, "");
}
touch_start(integer num)
{
llMessageLinked(LINK_THIS, 0, "Menu", llDetectedKey(0));
}
listen(integer channel, string name, key id, string str)
{
llMessageLinked(LINK_THIS, 0, str, id);
}
link_message(integer from_link, integer num, string str, key id)
{
integer i_hi;
integer i;
string cmd_opt = llDumpList2String(llParseString2List(str, ["[", "]"], []), "");
integer c = llListFindList(cmd_opts, [cmd_opt]);
if ( c == no_idx )
;
else if ( c == reset_idx )
llResetScript();
else if ( c == info_idx )
llInstantMessage(id, "Info:\nEdit to change length, position and rotation of this marker.\nRay goes from red end to yellow end of this marker.\nClick button 'Cast ray' to perform a cast ray.\nClick button 'Info' for this message.\nClick button 'Reset' to reset.\nClick other buttons to toggle settings [on] or off.");
else if ( c == castray_idx ) {
list params = llGetPrimitiveParams([PRIM_SLICE, PRIM_SIZE, PRIM_POSITION, PRIM_ROTATION]);
vector slice = (vector)llList2String(params, 0);
vector size = (vector)llList2String(params, 1);
vector pos = (vector)llList2String(params, 2);
rotation rot = (rotation)llList2String(params, 3);
float len1 = size.z*(slice.x-0.5);
float len2 = size.z*(slice.y-0.5);
vector pos1 = <0.0,0.0,len1>;
vector pos2 = <0.0,0.0,len2>;
pos1 = pos+pos1*rot;
pos2 = pos+pos2*rot;
integer reject_types = 0;
i_hi = llGetListLength(reject_idxs);
for ( i = 0; i < i_hi; i++ ) {
c = (integer)llList2String(reject_idxs, i);
reject_types = reject_types | (integer)llList2String(cmd_masks, c)*(integer)llList2String(cmd_flags, c);
}
integer data_flags = 0;
i_hi = llGetListLength(data_idxs);
for ( i = 0; i < i_hi; i++ ) {
c = (integer)llList2String(data_idxs, i);
data_flags = data_flags | (integer)llList2String(cmd_masks, c)*(integer)llList2String(cmd_flags, c);
}
integer max_hits = 16;
integer detect_phantom = (integer)llList2String(cmd_masks, phantom_idx)*(integer)llList2String(cmd_flags, phantom_idx);
params = [RC_REJECT_TYPES, reject_types, RC_DATA_FLAGS, data_flags, RC_MAX_HITS, max_hits, RC_DETECT_PHANTOM, detect_phantom];
integer stride = 2;
i_hi = llGetListLength(stride_idxs);
for ( i = 0; i < i_hi; i++ ) {
c = (integer)llList2String(stride_idxs, i);
stride = stride+(integer)llList2String(cmd_flags, c);
}
float time = llGetTime();
list hits = llCastRay(pos1, pos2, params);
time = llGetTime()-time;
hits += [time];
i_hi = llGetListLength(hits)-2;
for ( i = 0; i < i_hi; i += stride )
llOwnerSay("hit "+(string)(i/stride+1)+":\n"+llDumpList2String(llList2List(hits, i, i+stride-1), ";\n"));
llOwnerSay("hits and time:\n"+llDumpList2String(llList2List(hits, i, i+1), ";\n"));
}
else {
integer set = TRUE;
if ( cmd_opt != str )
set = FALSE;
if ( ~c )
cmd_flags = llListReplaceList(cmd_flags, [set], c, c);
}
string dialog_text = "Select or deselect flag or action.";
list dialog_opts = [];
i_hi = llGetListLength(dialog_idxs);
for ( i = 0; i < i_hi; i++ ) {
c = (integer)llList2String(dialog_idxs, i);
cmd_opt = llList2String(cmd_opts, c);
if ( (integer)llList2String(cmd_flags, c) )
cmd_opt = "["+cmd_opt+"]";
dialog_opts += cmd_opt;
}
llDialog(id, dialog_text, dialog_opts, dialog_channel);
}
}
0001-Patch-llCastRay-fully-simplified-to-V3.patch [^] (42,169 bytes) 2015-05-10 11:43 [Show Content] [Hide Content]From 35fb77b7af53ea87d5f1a949660f6e2c86fbc0b9 Mon Sep 17 00:00:00 2001
From: Magnuz Binder <magnuz@magnuz-se.com>
Date: Sun, 10 May 2015 20:01:50 +0200
Subject: [PATCH] Patch llCastRay fully simplified to V3.
---
.../Shared/Api/Implementation/LSL_Api.cs | 429 +++++++++++----------
bin/OpenSimDefaults.ini | 46 ++-
2 files changed, 248 insertions(+), 227 deletions(-)
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
index c90f015..089a5a8 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
@@ -221,15 +221,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
protected float m_primSafetyCoeffX = 2.414214f;
protected float m_primSafetyCoeffY = 2.414214f;
protected float m_primSafetyCoeffZ = 1.618034f;
+ protected bool m_useCastRayV3 = false;
protected float m_floatToleranceInCastRay = 0.000001f;
protected float m_floatTolerance2InCastRay = 0.0001f;
+ protected DetailLevel m_primLodInCastRay = DetailLevel.Medium;
+ protected DetailLevel m_sculptLodInCastRay = DetailLevel.Medium;
+ protected DetailLevel m_meshLodInCastRay = DetailLevel.Highest;
+ protected DetailLevel m_avatarLodInCastRay = DetailLevel.Medium;
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
//as custom headers by llHTTPRequest.
@@ -336,15 +340,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
m_primSafetyCoeffX = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientX", m_primSafetyCoeffX);
m_primSafetyCoeffY = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientY", m_primSafetyCoeffY);
m_primSafetyCoeffZ = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientZ", m_primSafetyCoeffZ);
+ m_useCastRayV3 = lslConfig.GetBoolean("UseLlCastRayV3", m_useCastRayV3);
m_floatToleranceInCastRay = lslConfig.GetFloat("FloatToleranceInLlCastRay", m_floatToleranceInCastRay);
m_floatTolerance2InCastRay = lslConfig.GetFloat("FloatTolerance2InLlCastRay", m_floatTolerance2InCastRay);
+ m_primLodInCastRay = (DetailLevel)lslConfig.GetInt("PrimDetailLevelInLlCastRay", (int)m_primLodInCastRay);
+ m_sculptLodInCastRay = (DetailLevel)lslConfig.GetInt("SculptDetailLevelInLlCastRay", (int)m_sculptLodInCastRay);
+ m_meshLodInCastRay = (DetailLevel)lslConfig.GetInt("MeshDetailLevelInLlCastRay", (int)m_meshLodInCastRay);
+ m_avatarLodInCastRay = (DetailLevel)lslConfig.GetInt("AvatarDetailLevelInLlCastRay", (int)m_avatarLodInCastRay);
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"];
@@ -13811,8 +13819,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return contacts[0];
}
- public LSL_List llCastRayV1(LSL_Vector start, LSL_Vector end, LSL_List options)
+ public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options)
{
+ // Use llCastRay V3 if configured
+ if (m_useCastRayV3)
+ return llCastRayV3(start, end, options);
+
LSL_List list = new LSL_List();
m_host.AddScriptLPS(1);
@@ -14003,29 +14015,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
/// <summary>
- /// Full implementation of llCastRay similar to SL 2015-04-21.
+ /// 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.
+ /// May sometimes be inaccurate owing to calculation precision,
+ /// meshing detail level and a bug in libopenmetaverse PrimMesher.
/// </summary>
- public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options)
+ public LSL_List llCastRayV3(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;
+ Vector3 pos1Ray = start;
+ Vector3 pos2Ray = end;
// Get input options
int rejectTypes = 0;
@@ -14054,25 +14061,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
bool getLinkNum = ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) != 0);
// Calculate some basic parameters
- Vector3 ray = end - start;
- float rayLength = ray.Length();
+ Vector3 vecRay = pos2Ray - pos1Ray;
+ float rayLength = vecRay.Length();
- // Try to get a mesher and return failure if none or degenerate ray
+ // Try to get a mesher and return failure if none, degenerate ray, or max 0 hits
IRendering primMesher = null;
List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
- if (renderers.Count < 1 || rayLength < tol)
+ if (renderers.Count < 1 || rayLength < tol || m_maxHitsInCastRay < 1)
{
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)
@@ -14115,51 +14116,51 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
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));
+ // Parse prim/part and project ray if passed filters
+ Vector3 scalePart = part.Scale;
+ Vector3 posPart = part.GetWorldPosition();
+ Quaternion rotPart = part.GetWorldRotation();
+ Quaternion rotPartInv = Quaternion.Inverse(rotPart);
+ Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart;
+ Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart;
- // Filter parts by bounding shapes
- Vector3 posPartRel = part.GetWorldPosition() + posProj;
- Vector3 posPartProj = posPartRel * rotProj;
- if (InBoundingShapes(ray, rayLength, scaleSafe, posPartRel, posPartProj, rotProj))
+ // Filter parts by shape bounding boxes
+ Vector3 shapeBoxMax = new Vector3(0.5f, 0.5f, 0.5f);
+ if (!part.Shape.SculptEntry)
+ shapeBoxMax = shapeBoxMax * (new Vector3(m_primSafetyCoeffX, m_primSafetyCoeffY, m_primSafetyCoeffZ));
+ shapeBoxMax = shapeBoxMax + (new Vector3(tol, tol, tol));
+ if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax))
{
// 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;
+ rayTrans.ScalePart = scalePart;
+ rayTrans.PositionPart = posPart;
+ rayTrans.RotationPart = rotPart;
+ rayTrans.ShapeNeedsEnds = true;
+ rayTrans.Position1Ray = pos1Ray;
+ rayTrans.Position1RayProj = pos1RayProj;
+ rayTrans.VectorRayProj = pos2RayProj - pos1RayProj;
// Make an OMV prim to be able to mesh part
- Primitive omvPrim = part.Shape.ToOmvPrimitive(posPartProj, rayTrans.RotationPartProj);
+ Primitive omvPrim = part.Shape.ToOmvPrimitive(posPart, rotPart);
byte[] sculptAsset = null;
if (omvPrim.Sculpt != null)
sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
+ FacetedMesh mesh = null;
- // When part is mesh, get and check mesh
+ // When part is mesh, get mesh and check for hits
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);
+ FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, m_meshLodInCastRay, out mesh);
meshAsset = null;
- AddRayInFacetedMesh(mesh, rayTrans, ref rayHits);
- mesh = null;
}
- // When part is sculpt, create and check mesh
+ // When part is sculpt, create mesh and check for hits
// 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)
{
@@ -14169,15 +14170,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
if (sculpt != null)
{
- SimpleMesh mesh = primMesher.GenerateSimpleSculptMesh(omvPrim, (Bitmap)sculpt, DetailLevel.Medium);
+ mesh = primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, m_sculptLodInCastRay);
sculpt.Dispose();
- AddRayInSimpleMesh(mesh, rayTrans, ref rayHits);
- mesh = null;
}
}
}
- // When part is prim, create and check mesh
+ // When part is prim, create mesh and check for hits
else if (omvPrim.Sculpt == null)
{
if (
@@ -14186,12 +14185,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
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;
+ rayTrans.ShapeNeedsEnds = false;
+ mesh = primMesher.GenerateFacetedMesh(omvPrim, m_primLodInCastRay);
}
+ // Check mesh for ray hits
+ AddRayInFacetedMesh(mesh, rayTrans, ref rayHits);
+ mesh = null;
}
}
}
@@ -14205,38 +14205,43 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
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))
+ // Parse avatar
+ Vector3 scalePart = upper - lower;
+ Vector3 posPart = sp.AbsolutePosition;
+ Quaternion rotPart = sp.GetWorldRotation();
+ Quaternion rotPartInv = Quaternion.Inverse(rotPart);
+ posPart = posPart + (lower + upper) * 0.5f * rotPart;
+ // Project ray
+ Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart;
+ Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart;
+
+ // Filter avatars by shape bounding boxes
+ Vector3 shapeBoxMax = new Vector3(0.5f + tol, 0.5f + tol, 0.5f + tol);
+ if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax))
{
// 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;
+ rayTrans.ScalePart = scalePart;
+ rayTrans.PositionPart = posPart;
+ rayTrans.RotationPart = rotPart;
+ rayTrans.ShapeNeedsEnds = false;
+ rayTrans.Position1Ray = pos1Ray;
+ rayTrans.Position1RayProj = pos1RayProj;
+ rayTrans.VectorRayProj = pos2RayProj - pos1RayProj;
// 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);
+ PrimitiveBaseShape prim = PrimitiveBaseShape.CreateSphere();
+ prim.Scale = scalePart;
+ Primitive omvPrim = prim.ToOmvPrimitive(posPart, rotPart);
+ FacetedMesh mesh = primMesher.GenerateFacetedMesh(omvPrim, m_meshLodInCastRay);
+ AddRayInFacetedMesh(mesh, rayTrans, ref rayHits);
mesh = null;
}
}
@@ -14248,32 +14253,26 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{
// Parse terrain
- // Mesh terrain and check projected bounding box
- Vector3 posPartProj = posProj * rotProj;
- Quaternion rotPartProj = rotProj;
+ // Mesh terrain and check bounding box
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)
+ List<Tri> triangles = TrisFromHeightmapUnderRay(pos1Ray, pos2Ray, out lower, out upper);
+ lower.Z -= tol;
+ upper.Z += tol;
+ if ((pos1Ray.Z >= lower.Z || pos2Ray.Z >= lower.Z) && (pos1Ray.Z <= upper.Z || pos2Ray.Z <= upper.Z))
{
// 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;
+ rayTrans.ScalePart = new Vector3 (1.0f, 1.0f, 1.0f);
+ rayTrans.PositionPart = Vector3.Zero;
+ rayTrans.RotationPart = Quaternion.Identity;
+ rayTrans.ShapeNeedsEnds = true;
+ rayTrans.Position1Ray = pos1Ray;
+ rayTrans.Position1RayProj = pos1Ray;
+ rayTrans.VectorRayProj = vecRay;
// Check mesh
AddRayInTris(triangles, rayTrans, ref rayHits);
@@ -14358,15 +14357,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
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;
+ public Vector3 ScalePart;
+ public Vector3 PositionPart;
+ public Quaternion RotationPart;
+ public bool ShapeNeedsEnds;
+ public Vector3 Position1Ray;
+ public Vector3 Position1RayProj;
+ public Vector3 VectorRayProj;
}
/// <summary>
@@ -14383,21 +14380,63 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
/// <summary>
- /// Helper to parse SimpleMesh for ray hits.
+ /// Helper to check if a ray intersects a shape bounding box.
/// </summary>
- private void AddRayInSimpleMesh(SimpleMesh mesh, RayTrans rayTrans, ref List<RayHit> rayHits)
- {
- if (mesh != null)
+ private bool RayIntersectsShapeBox(Vector3 pos1RayProj, Vector3 pos2RayProj, Vector3 shapeBoxMax)
+ {
+ // Skip if ray can't intersect bounding box;
+ Vector3 rayBoxProjMin = Vector3.Min(pos1RayProj, pos2RayProj);
+ Vector3 rayBoxProjMax = Vector3.Max(pos1RayProj, pos2RayProj);
+ if (
+ rayBoxProjMin.X > shapeBoxMax.X || rayBoxProjMin.Y > shapeBoxMax.Y || rayBoxProjMin.Z > shapeBoxMax.Z ||
+ rayBoxProjMax.X < -shapeBoxMax.X || rayBoxProjMax.Y < -shapeBoxMax.Y || rayBoxProjMax.Z < -shapeBoxMax.Z
+ )
+ return false;
+
+ // Check if ray intersect any bounding box side
+ int sign = 0;
+ float dist = 0.0f;
+ Vector3 posProj = Vector3.Zero;
+ Vector3 vecRayProj = pos2RayProj - pos1RayProj;
+
+ // Check both X sides unless ray is parallell to them
+ if (Math.Abs(vecRayProj.X) > m_floatToleranceInCastRay)
+ {
+ for (sign = -1; sign <= 1; sign += 2)
+ {
+ dist = ((float)sign * shapeBoxMax.X - pos1RayProj.X) / vecRayProj.X;
+ posProj = pos1RayProj + vecRayProj * dist;
+ if (Math.Abs(posProj.Y) <= shapeBoxMax.Y && Math.Abs(posProj.Z) <= shapeBoxMax.Z)
+ return true;
+ }
+ }
+
+ // Check both Y sides unless ray is parallell to them
+ if (Math.Abs(vecRayProj.Y) > m_floatToleranceInCastRay)
{
- for (int i = 0; i < mesh.Indices.Count; i += 3)
+ for (sign = -1; sign <= 1; sign += 2)
{
- 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);
+ dist = ((float)sign * shapeBoxMax.Y - pos1RayProj.Y) / vecRayProj.Y;
+ posProj = pos1RayProj + vecRayProj * dist;
+ if (Math.Abs(posProj.X) <= shapeBoxMax.X && Math.Abs(posProj.Z) <= shapeBoxMax.Z)
+ return true;
}
}
+
+ // Check both Z sides unless ray is parallell to them
+ if (Math.Abs(vecRayProj.Z) > m_floatToleranceInCastRay)
+ {
+ for (sign = -1; sign <= 1; sign += 2)
+ {
+ dist = ((float)sign * shapeBoxMax.Z - pos1RayProj.Z) / vecRayProj.Z;
+ posProj = pos1RayProj + vecRayProj * dist;
+ if (Math.Abs(posProj.X) <= shapeBoxMax.X && Math.Abs(posProj.Y) <= shapeBoxMax.Y)
+ return true;
+ }
+ }
+
+ // No hits on bounding box so return false
+ return false;
}
/// <summary>
@@ -14409,7 +14448,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{
foreach (Face face in mesh.Faces)
{
- for (int i = 0; i <face.Indices.Count; i += 3)
+ for (int i = 0; i < face.Indices.Count; i += 3)
{
Tri triangle = new Tri();
triangle.p1 = face.Vertices[face.Indices[i]].Position;
@@ -14435,23 +14474,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// <summary>
/// Helper to add ray hit in a Tri (triangle).
/// </summary>
- private void AddRayInTri(Tri triangle, RayTrans rayTrans, ref List<RayHit> rayHits)
+ private void AddRayInTri(Tri triProj, 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)
+ Vector3 posHitProj;
+ Vector3 normalProj;
+ if (HitRayInTri(triProj, rayTrans.Position1RayProj, rayTrans.VectorRayProj, out posHitProj, out normalProj))
+ {
+ // Hack to circumvent ghost face bug in PrimMesher by removing hits in (ghost) face plane through shape center
+ if (Math.Abs(Vector3.Dot(posHitProj, normalProj)) < m_floatToleranceInCastRay && !rayTrans.ShapeNeedsEnds)
return;
- // Remove duplicate hits at triangle edges and intersections
+
+ // Transform hit and normal to region coordinate system
+ Vector3 posHit = rayTrans.PositionPart + (posHitProj * rayTrans.ScalePart) * rayTrans.RotationPart;
+ Vector3 normal = Vector3.Normalize((normalProj * rayTrans.ScalePart) * rayTrans.RotationPart);
+
+ // Remove duplicate hits at triangle intersections
+ float distance = Vector3.Distance(rayTrans.Position1Ray, posHit);
for (int i = rayHits.Count - 1; i >= 0; i--)
{
- if (rayHits[i].PartId == rayTrans.PartId && Math.Abs(rayHits[i].Distance - distance) < rayTrans.Tolerance2)
+ if (rayHits[i].PartId != rayTrans.PartId)
+ break;
+ if (Math.Abs(rayHits[i].Distance - distance) < m_floatTolerance2InCastRay)
return;
}
@@ -14468,76 +14512,56 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
/// <summary>
- /// Helper to find ray hit in a Tri (triangle).
+ /// Helper to find ray hit in triangle
/// </summary>
- private bool HitRayInTri(Tri triangle, RayTrans rayTrans, out float distance, out Vector3 posHit, out Vector3 normal)
+ bool HitRayInTri(Tri triProj, Vector3 pos1RayProj, Vector3 vecRayProj, out Vector3 posHitProj, out Vector3 normalProj)
{
- // 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;
+ float tol = m_floatToleranceInCastRay;
+ posHitProj = Vector3.Zero;
+
+ // Calculate triangle edge vectors
+ Vector3 vec1Proj = triProj.p2 - triProj.p1;
+ Vector3 vec2Proj = triProj.p3 - triProj.p2;
+ Vector3 vec3Proj = triProj.p1 - triProj.p3;
- // 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))
+ // Calculate triangle normal
+ normalProj = Vector3.Cross(vec1Proj, vec2Proj);
+
+ // Skip if degenerate triangle or ray parallell with triangle plane
+ float divisor = Vector3.Dot(vecRayProj, normalProj);
+ if (Math.Abs(divisor) < tol)
return false;
- // Calculate normal
- Vector3 normalProj = Vector3.Cross(vec1, vec2);
- float normalLength = normalProj.Length();
- // Skip if degenerate triangle
- if (normalLength < tol)
+ // Skip if exit and not configured to detect
+ if (divisor > tol && !m_detectExitsInCastRay)
return false;
- normalProj = normalProj / normalLength;
- // Skip if ray parallell to triangle plane
- if (Math.Abs(normalProj.Z) < tol)
+
+ // Skip if outside ray ends
+ float distanceProj = Vector3.Dot(triProj.p1 - pos1RayProj, normalProj) / divisor;
+ if (distanceProj < -tol || distanceProj > 1 + 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)
+ // Calculate hit position in triangle
+ posHitProj = pos1RayProj + vecRayProj * distanceProj;
+
+ // Skip if outside triangle bounding box
+ Vector3 triProjMin = Vector3.Min(Vector3.Min(triProj.p1, triProj.p2), triProj.p3);
+ Vector3 triProjMax = Vector3.Max(Vector3.Max(triProj.p1, triProj.p2), triProj.p3);
+ if (
+ posHitProj.X < triProjMin.X - tol || posHitProj.Y < triProjMin.Y - tol || posHitProj.Z < triProjMin.Z - tol ||
+ posHitProj.X > triProjMax.X + tol || posHitProj.Y > triProjMax.Y + tol || posHitProj.Z > triProjMax.Z + 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;
+ // Skip if outside triangle
+ if (
+ Vector3.Dot(Vector3.Cross(vec1Proj, normalProj), posHitProj - triProj.p1) > tol ||
+ Vector3.Dot(Vector3.Cross(vec2Proj, normalProj), posHitProj - triProj.p2) > tol ||
+ Vector3.Dot(Vector3.Cross(vec3Proj, normalProj), posHitProj - triProj.p3) > tol
+ )
+ return false;
+
+ // Return hit
return true;
}
@@ -14660,24 +14684,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
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);
+ zLower = Math.Min(zLower, pos2.Z);
+ zUpper = Math.Max(zUpper, pos2.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);
+ zLower = Math.Min(zLower, pos3.Z);
+ zUpper = Math.Max(zUpper, pos3.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);
+ zLower = Math.Min(zLower, pos4.Z);
+ zUpper = Math.Max(zUpper, pos4.Z);
// Add triangle 1
Tri triangle1 = new Tri();
@@ -14695,25 +14719,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
/// <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)
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini
index 275c207..2c17022 100644
--- a/bin/OpenSimDefaults.ini
+++ b/bin/OpenSimDefaults.ini
@@ -1397,11 +1397,6 @@
; Maximum number of external urls that scripts can set up in this simulator (e.g. via llRequestURL())
max_external_urls_per_simulator = 100
- ; Use version 1 of llCastRay as default if true. If set to false, the new
- ; version of llCastRay will be used. This gives better accuracy but
- ; uses more CPU and may may be slow on some servers
- UseLlCastRayV1 = true
-
; Use size boxes instead of meshed prims, sculpts and mesh when calculating bounding boxes.
; Speeds up calculations but can make them inaccurate, in some cases very inaccurate.
UseSimpleBoxesInGetBoundingBox = false
@@ -1494,32 +1489,52 @@
; Worst case is twisted tube, 0.5+sqrt(1.25)
PrimBoundingBoxSafetyCoefficientZ = 1.618034
- ; Accepted calculation precision error in calculations in llCastRay
+ ; Use llCastRay V3 if true
+ ; Gives better accuracy and can be faster on some servers, but slower on others,
+ ; compared to previous version of llCastRay
+ ; Generates geometry meshes and can therefore use much system resources
+ UseLlCastRayV3 = false
+
+ ; Accepted calculation precision error in calculations in llCastRay V3
FloatToleranceInLlCastRay = 0.000001
- ; Accepted distance difference between duplicate hits in llCastRay
+ ; Accepted distance difference between duplicate hits in llCastRay V3
FloatTolerance2InLlCastRay = 0.0001
- ; Maximum number of returned hits from llCastRay
+ ; Detail level when rendering prims in llCastRay V3
+ ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call
+ PrimDetailLevelInLlCastRay = 1
+
+ ; Detail level when rendering sculpts in llCastRay V3
+ ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call
+ SculptDetailLevelInLlCastRay = 1
+
+ ; Detail level when rendering meshes in llCastRay V3
+ ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call
+ MeshDetailLevelInLlCastRay = 3
+
+ ; Detail level when rendering avatar capsules in llCastRay V3
+ ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call
+ AvatarDetailLevelInLlCastRay = 1
+
+ ; Maximum number of returned hits from llCastRay V3
MaxHitsInLlCastRay = 16
- ; Maximum number of returned hits per prim from llCastRay
+ ; Maximum number of returned hits per prim from llCastRay V3
MaxHitsPerPrimInLlCastRay = 16
- ; Maximum number of returned hits per object from llCastRay
+ ; Maximum number of returned hits per object from llCastRay V3
MaxHitsPerObjectInLlCastRay = 16
- ; Report ray intersections with surfaces on exits from a prim as hits in llCastRay if true
+ ; Report ray intersections with surfaces on exits from a prim as hits in llCastRay V3 if true
DetectExitHitsInLlCastRay = false
- ; Filter on parts instead of groups in llCastRay if true
+ ; Filter on parts instead of groups in llCastRay V3 if true
FilterPartsInLlCastRay = false
- ; Detect attachments in llCastRay if true
+ ; Detect attachments in llCastRay V3 if true
DoAttachmentsInLlCastRay = false
- ; Use legacy version 1 of llCastRay if true
- UseLlCastRayV1 = true
[DataSnapshot]
; The following set of configs pertains to search.
@@ -1547,6 +1562,7 @@
; data service
;DATA_SRV_MISearch = "http://metaverseink.com/cgi-bin/register.py"
+
[Economy]
; These economy values get used in the BetaGridLikeMoneyModule. - This module is for demonstration only -
; The default economy module only implements just enough to allow free actions (transfer of objects, etc).
--
1.9.1
|