diff options
author | Magnuz Binder | 2015-05-03 07:50:13 +0200 |
---|---|---|
committer | dahlia | 2015-05-03 23:41:11 -0700 |
commit | 43b8bd0c35c1d7ca0c97a4e64fe255943148d333 (patch) | |
tree | 212cbce67281817d2884924ef8ac0ab9a9dcefd3 /OpenSim | |
parent | Only send parcel layer data around the point of interest. Can be disabled (diff) | |
download | opensim-SC_OLD-43b8bd0c35c1d7ca0c97a4e64fe255943148d333.zip opensim-SC_OLD-43b8bd0c35c1d7ca0c97a4e64fe255943148d333.tar.gz opensim-SC_OLD-43b8bd0c35c1d7ca0c97a4e64fe255943148d333.tar.bz2 opensim-SC_OLD-43b8bd0c35c1d7ca0c97a4e64fe255943148d333.tar.xz |
Implement llCastRay fully, simplified.
Diffstat (limited to 'OpenSim')
-rw-r--r-- | OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 760 |
1 files changed, 759 insertions, 1 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index e20e4c4..cf1bd2b 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | |||
@@ -218,6 +218,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
218 | protected float m_lABB2SitZ0 = -0.25f; | 218 | protected float m_lABB2SitZ0 = -0.25f; |
219 | protected float m_lABB2SitZ1 = 0.25f; | 219 | protected float m_lABB2SitZ1 = 0.25f; |
220 | 220 | ||
221 | protected float m_primSafetyCoeffX = 2.414214f; | ||
222 | protected float m_primSafetyCoeffY = 2.414214f; | ||
223 | protected float m_primSafetyCoeffZ = 1.618034f; | ||
224 | protected float m_floatToleranceInCastRay = 0.000001f; | ||
225 | protected float m_floatTolerance2InCastRay = 0.0001f; | ||
226 | protected int m_maxHitsInCastRay = 16; | ||
227 | protected int m_maxHitsPerPrimInCastRay = 16; | ||
228 | protected int m_maxHitsPerObjectInCastRay = 16; | ||
229 | protected bool m_detectExitsInCastRay = false; | ||
230 | protected bool m_filterPartsInCastRay = false; | ||
231 | protected bool m_doAttachmentsInCastRay = false; | ||
232 | protected bool m_useCastRayV1 = true; | ||
233 | |||
221 | //An array of HTTP/1.1 headers that are not allowed to be used | 234 | //An array of HTTP/1.1 headers that are not allowed to be used |
222 | //as custom headers by llHTTPRequest. | 235 | //as custom headers by llHTTPRequest. |
223 | private string[] HttpStandardHeaders = | 236 | private string[] HttpStandardHeaders = |
@@ -320,6 +333,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
320 | m_lABB1SitZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZcoeff", m_lABB1SitZ1); | 333 | m_lABB1SitZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZcoeff", m_lABB1SitZ1); |
321 | m_lABB2SitZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZconst", m_lABB2SitZ0); | 334 | m_lABB2SitZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZconst", m_lABB2SitZ0); |
322 | m_lABB2SitZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZcoeff", m_lABB2SitZ1); | 335 | m_lABB2SitZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZcoeff", m_lABB2SitZ1); |
336 | m_primSafetyCoeffX = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientX", m_primSafetyCoeffX); | ||
337 | m_primSafetyCoeffY = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientY", m_primSafetyCoeffY); | ||
338 | m_primSafetyCoeffZ = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientZ", m_primSafetyCoeffZ); | ||
339 | m_floatToleranceInCastRay = lslConfig.GetFloat("FloatToleranceInLlCastRay", m_floatToleranceInCastRay); | ||
340 | m_floatTolerance2InCastRay = lslConfig.GetFloat("FloatTolerance2InLlCastRay", m_floatTolerance2InCastRay); | ||
341 | m_maxHitsInCastRay = lslConfig.GetInt("MaxHitsInLlCastRay", m_maxHitsInCastRay); | ||
342 | m_maxHitsPerPrimInCastRay = lslConfig.GetInt("MaxHitsPerPrimInLlCastRay", m_maxHitsPerPrimInCastRay); | ||
343 | m_maxHitsPerObjectInCastRay = lslConfig.GetInt("MaxHitsPerObjectInLlCastRay", m_maxHitsPerObjectInCastRay); | ||
344 | m_detectExitsInCastRay = lslConfig.GetBoolean("DetectExitHitsInLlCastRay", m_detectExitsInCastRay); | ||
345 | m_filterPartsInCastRay = lslConfig.GetBoolean("FilterPartsInLlCastRay", m_filterPartsInCastRay); | ||
346 | m_doAttachmentsInCastRay = lslConfig.GetBoolean("DoAttachmentsInLlCastRay", m_doAttachmentsInCastRay); | ||
347 | m_useCastRayV1 = lslConfig.GetBoolean("UseLlCastRayV1", m_useCastRayV1); | ||
323 | } | 348 | } |
324 | 349 | ||
325 | IConfig smtpConfig = seConfigSource.Configs["SMTP"]; | 350 | IConfig smtpConfig = seConfigSource.Configs["SMTP"]; |
@@ -13738,7 +13763,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
13738 | return contacts[0]; | 13763 | return contacts[0]; |
13739 | } | 13764 | } |
13740 | 13765 | ||
13741 | public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options) | 13766 | public LSL_List llCastRayV1(LSL_Vector start, LSL_Vector end, LSL_List options) |
13742 | { | 13767 | { |
13743 | LSL_List list = new LSL_List(); | 13768 | LSL_List list = new LSL_List(); |
13744 | 13769 | ||
@@ -13929,6 +13954,739 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
13929 | return list; | 13954 | return list; |
13930 | } | 13955 | } |
13931 | 13956 | ||
13957 | /// <summary> | ||
13958 | /// Full implementation of llCastRay similar to SL 2015-04-21. | ||
13959 | /// http://wiki.secondlife.com/wiki/LlCastRay | ||
13960 | /// Uses pure geometry, bounding shapes, meshing and no physics | ||
13961 | /// for prims, sculpts, meshes, avatars and terrain. | ||
13962 | /// Implements all flags, reject types and data flags. | ||
13963 | /// Can handle both objects/groups and prims/parts, by config. | ||
13964 | /// May give poor results with multi-part meshes where "root" | ||
13965 | /// part doesn't dominate, owing to "guessed" bounding boxes. | ||
13966 | /// May sometimes be inaccurate owing to calculation precision | ||
13967 | /// and a bug in libopenmetaverse PrimMesher. | ||
13968 | /// </summary> | ||
13969 | public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options) | ||
13970 | { | ||
13971 | // Use llCastRay v1 if configured | ||
13972 | if (m_useCastRayV1) | ||
13973 | return llCastRayV1(start, end, options); | ||
13974 | |||
13975 | // Initialize | ||
13976 | m_host.AddScriptLPS(1); | ||
13977 | List<RayHit> rayHits = new List<RayHit>(); | ||
13978 | LSL_List result = new LSL_List(); | ||
13979 | float tol = m_floatToleranceInCastRay; | ||
13980 | float tol2 = m_floatTolerance2InCastRay; | ||
13981 | |||
13982 | // Get input options | ||
13983 | int rejectTypes = 0; | ||
13984 | int dataFlags = 0; | ||
13985 | int maxHits = 1; | ||
13986 | bool detectPhantom = false; | ||
13987 | for (int i = 0; i < options.Length; i += 2) | ||
13988 | { | ||
13989 | if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES) | ||
13990 | rejectTypes = options.GetLSLIntegerItem(i + 1); | ||
13991 | else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DATA_FLAGS) | ||
13992 | dataFlags = options.GetLSLIntegerItem(i + 1); | ||
13993 | else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS) | ||
13994 | maxHits = options.GetLSLIntegerItem(i + 1); | ||
13995 | else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM) | ||
13996 | detectPhantom = (options.GetLSLIntegerItem(i + 1) != 0); | ||
13997 | } | ||
13998 | if (maxHits > m_maxHitsInCastRay) | ||
13999 | maxHits = m_maxHitsInCastRay; | ||
14000 | bool rejectAgents = ((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) != 0); | ||
14001 | bool rejectPhysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) != 0); | ||
14002 | bool rejectNonphysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) != 0); | ||
14003 | bool rejectLand = ((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) != 0); | ||
14004 | bool getNormal = ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) != 0); | ||
14005 | bool getRootKey = ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) != 0); | ||
14006 | bool getLinkNum = ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) != 0); | ||
14007 | |||
14008 | // Calculate some basic parameters | ||
14009 | Vector3 ray = end - start; | ||
14010 | float rayLength = ray.Length(); | ||
14011 | |||
14012 | // Try to get a mesher and return failure if none or degenerate ray | ||
14013 | IRendering primMesher = null; | ||
14014 | List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); | ||
14015 | if (renderers.Count < 1 || rayLength < tol) | ||
14016 | { | ||
14017 | result.Add(new LSL_Integer(ScriptBaseClass.RCERR_UNKNOWN)); | ||
14018 | return result; | ||
14019 | } | ||
14020 | primMesher = RenderingLoader.LoadRenderer(renderers[0]); | ||
14021 | |||
14022 | // Used to translate and rotate world so ray is along negative Z axis from origo and | ||
14023 | // calculations mostly simplified to a 2D projecttion on the X-Y plane | ||
14024 | Vector3 posProj = new Vector3(-start); | ||
14025 | Quaternion rotProj = Vector3.RotationBetween(ray, new Vector3(0.0f, 0.0f, -1.0f)); | ||
14026 | Quaternion rotBack = Quaternion.Inverse(rotProj); | ||
14027 | |||
14028 | // Iterate over all objects/groups and prims/parts in region | ||
14029 | World.ForEachSOG( | ||
14030 | delegate(SceneObjectGroup group) | ||
14031 | { | ||
14032 | // Check group filters unless part filters are configured | ||
14033 | bool isPhysical = (group.RootPart != null && group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical); | ||
14034 | bool isNonphysical = !isPhysical; | ||
14035 | bool isPhantom = group.IsPhantom || group.IsVolumeDetect; | ||
14036 | bool isAttachment = group.IsAttachment; | ||
14037 | bool doGroup = true; | ||
14038 | if (isPhysical && rejectPhysical) | ||
14039 | doGroup = false; | ||
14040 | if (isNonphysical && rejectNonphysical) | ||
14041 | doGroup = false; | ||
14042 | if (isPhantom && detectPhantom) | ||
14043 | doGroup = true; | ||
14044 | if (m_filterPartsInCastRay) | ||
14045 | doGroup = true; | ||
14046 | if (isAttachment && !m_doAttachmentsInCastRay) | ||
14047 | doGroup = false; | ||
14048 | // Parse object/group if passed filters | ||
14049 | if (doGroup) | ||
14050 | { | ||
14051 | // Iterate over all prims/parts in object/group | ||
14052 | foreach(SceneObjectPart part in group.Parts) | ||
14053 | { | ||
14054 | // Check part filters if configured | ||
14055 | if (m_filterPartsInCastRay) | ||
14056 | { | ||
14057 | isPhysical = (part.PhysActor != null && part.PhysActor.IsPhysical); | ||
14058 | isNonphysical = !isPhysical; | ||
14059 | isPhantom = ((part.Flags & PrimFlags.Phantom) != 0) || (part.VolumeDetectActive); | ||
14060 | bool doPart = true; | ||
14061 | if (isPhysical && rejectPhysical) | ||
14062 | doPart = false; | ||
14063 | if (isNonphysical && rejectNonphysical) | ||
14064 | doPart = false; | ||
14065 | if (isPhantom && detectPhantom) | ||
14066 | doPart = true; | ||
14067 | if (!doPart) | ||
14068 | continue; | ||
14069 | } | ||
14070 | // Parse prim/part if passed filters | ||
14071 | |||
14072 | // Estimate bounding box from size box | ||
14073 | Vector3 scaleSafe = part.Scale; | ||
14074 | if (!part.Shape.SculptEntry) | ||
14075 | scaleSafe = scaleSafe * (new Vector3(m_primSafetyCoeffX, m_primSafetyCoeffY, m_primSafetyCoeffZ)); | ||
14076 | |||
14077 | // Filter parts by bounding shapes | ||
14078 | Vector3 posPartRel = part.GetWorldPosition() + posProj; | ||
14079 | Vector3 posPartProj = posPartRel * rotProj; | ||
14080 | if (InBoundingShapes(ray, rayLength, scaleSafe, posPartRel, posPartProj, rotProj)) | ||
14081 | { | ||
14082 | // Prepare data needed to check for ray hits | ||
14083 | RayTrans rayTrans = new RayTrans(); | ||
14084 | rayTrans.PartId = part.UUID; | ||
14085 | rayTrans.GroupId = part.ParentGroup.UUID; | ||
14086 | rayTrans.Link = group.PrimCount > 1 ? part.LinkNum : 0; | ||
14087 | rayTrans.Scale = part.Scale; | ||
14088 | rayTrans.PositionPartProj = posPartProj; | ||
14089 | rayTrans.PositionProj = posProj; | ||
14090 | rayTrans.RotationPartProj = rotProj * part.GetWorldRotation(); | ||
14091 | rayTrans.RotationBack = rotBack; | ||
14092 | rayTrans.NeedsEnds = true; | ||
14093 | rayTrans.RayLength = rayLength; | ||
14094 | rayTrans.Tolerance = tol; | ||
14095 | rayTrans.Tolerance2 = tol2; | ||
14096 | |||
14097 | // Make an OMV prim to be able to mesh part | ||
14098 | Primitive omvPrim = part.Shape.ToOmvPrimitive(posPartProj, rayTrans.RotationPartProj); | ||
14099 | byte[] sculptAsset = null; | ||
14100 | if (omvPrim.Sculpt != null) | ||
14101 | sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString()); | ||
14102 | |||
14103 | // When part is mesh, get and check mesh | ||
14104 | if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null) | ||
14105 | { | ||
14106 | AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset); | ||
14107 | FacetedMesh mesh = null; | ||
14108 | FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out mesh); | ||
14109 | meshAsset = null; | ||
14110 | AddRayInFacetedMesh(mesh, rayTrans, ref rayHits); | ||
14111 | mesh = null; | ||
14112 | } | ||
14113 | |||
14114 | // When part is sculpt, create and check mesh | ||
14115 | // Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt. | ||
14116 | else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null) | ||
14117 | { | ||
14118 | IJ2KDecoder imgDecoder = World.RequestModuleInterface<IJ2KDecoder>(); | ||
14119 | if (imgDecoder != null) | ||
14120 | { | ||
14121 | Image sculpt = imgDecoder.DecodeToImage(sculptAsset); | ||
14122 | if (sculpt != null) | ||
14123 | { | ||
14124 | SimpleMesh mesh = primMesher.GenerateSimpleSculptMesh(omvPrim, (Bitmap)sculpt, DetailLevel.Medium); | ||
14125 | sculpt.Dispose(); | ||
14126 | AddRayInSimpleMesh(mesh, rayTrans, ref rayHits); | ||
14127 | mesh = null; | ||
14128 | } | ||
14129 | } | ||
14130 | } | ||
14131 | |||
14132 | // When part is prim, create and check mesh | ||
14133 | else if (omvPrim.Sculpt == null) | ||
14134 | { | ||
14135 | if ( | ||
14136 | omvPrim.PrimData.PathBegin == 0.0 && omvPrim.PrimData.PathEnd == 1.0 && | ||
14137 | omvPrim.PrimData.PathTaperX == 0.0 && omvPrim.PrimData.PathTaperY == 0.0 && | ||
14138 | omvPrim.PrimData.PathSkew == 0.0 && | ||
14139 | omvPrim.PrimData.PathTwist - omvPrim.PrimData.PathTwistBegin == 0.0 | ||
14140 | ) | ||
14141 | rayTrans.NeedsEnds = false; | ||
14142 | SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium); | ||
14143 | AddRayInSimpleMesh(mesh, rayTrans, ref rayHits); | ||
14144 | mesh = null; | ||
14145 | } | ||
14146 | |||
14147 | } | ||
14148 | } | ||
14149 | } | ||
14150 | } | ||
14151 | ); | ||
14152 | |||
14153 | // Check avatar filter | ||
14154 | if (!rejectAgents) | ||
14155 | { | ||
14156 | // Iterate over all avatars in region | ||
14157 | World.ForEachRootScenePresence( | ||
14158 | delegate (ScenePresence sp) | ||
14159 | { | ||
14160 | // Parse avatar | ||
14161 | |||
14162 | // Get bounding box | ||
14163 | Vector3 lower; | ||
14164 | Vector3 upper; | ||
14165 | BoundingBoxOfScenePresence(sp, out lower, out upper); | ||
14166 | Vector3 scale = upper - lower; | ||
14167 | |||
14168 | // Filter avatars by bounding shapes | ||
14169 | Vector3 posPartRel = sp.AbsolutePosition + posProj + (lower + upper) * 0.5f * sp.Rotation; | ||
14170 | Vector3 posPartProj = posPartRel * rotProj; | ||
14171 | if (InBoundingShapes(ray, rayLength, scale, posPartRel, posPartProj, rotProj)) | ||
14172 | { | ||
14173 | // Prepare data needed to check for ray hits | ||
14174 | RayTrans rayTrans = new RayTrans(); | ||
14175 | rayTrans.PartId = sp.UUID; | ||
14176 | rayTrans.GroupId = sp.ParentPart != null ? sp.ParentPart.ParentGroup.UUID : sp.UUID; | ||
14177 | rayTrans.Link = sp.ParentPart != null ? UUID2LinkNumber(sp.ParentPart, sp.UUID) : 0; | ||
14178 | rayTrans.Scale = scale; | ||
14179 | rayTrans.PositionPartProj = posPartProj; | ||
14180 | rayTrans.PositionProj = posProj; | ||
14181 | rayTrans.RotationPartProj = rotProj * sp.Rotation; | ||
14182 | rayTrans.RotationBack = rotBack; | ||
14183 | rayTrans.NeedsEnds = false; | ||
14184 | rayTrans.RayLength = rayLength; | ||
14185 | rayTrans.Tolerance = tol; | ||
14186 | rayTrans.Tolerance2 = tol2; | ||
14187 | |||
14188 | // Make OMV prim, create and check mesh | ||
14189 | Primitive omvPrim = MakeOpenMetaversePrim(scale, posPartProj, rayTrans.RotationPartProj, ScriptBaseClass.PRIM_TYPE_SPHERE); | ||
14190 | SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium); | ||
14191 | AddRayInSimpleMesh(mesh, rayTrans, ref rayHits); | ||
14192 | mesh = null; | ||
14193 | } | ||
14194 | } | ||
14195 | ); | ||
14196 | } | ||
14197 | |||
14198 | // Check terrain filter | ||
14199 | if (!rejectLand) | ||
14200 | { | ||
14201 | // Parse terrain | ||
14202 | |||
14203 | // Mesh terrain and check projected bounding box | ||
14204 | Vector3 posPartProj = posProj * rotProj; | ||
14205 | Quaternion rotPartProj = rotProj; | ||
14206 | Vector3 lower; | ||
14207 | Vector3 upper; | ||
14208 | List<Tri> triangles = TrisFromHeightmapUnderRay(start, end, out lower, out upper); | ||
14209 | Vector3 lowerBox = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); | ||
14210 | Vector3 upperBox = new Vector3(float.MinValue, float.MinValue, float.MinValue); | ||
14211 | int dummy = 0; | ||
14212 | AddBoundingBoxOfSimpleBox(lower, upper, posPartProj, rotPartProj, true, ref lowerBox, ref upperBox, ref dummy); | ||
14213 | if (lowerBox.X <= tol && lowerBox.Y <= tol && lowerBox.Z <= tol && upperBox.X >= -tol && upperBox.Y >= -tol && upperBox.Z >= -rayLength - tol) | ||
14214 | { | ||
14215 | // Prepare data needed to check for ray hits | ||
14216 | RayTrans rayTrans = new RayTrans(); | ||
14217 | rayTrans.PartId = UUID.Zero; | ||
14218 | rayTrans.GroupId = UUID.Zero; | ||
14219 | rayTrans.Link = 0; | ||
14220 | rayTrans.Scale = new Vector3 (1.0f, 1.0f, 1.0f); | ||
14221 | rayTrans.PositionPartProj = posPartProj; | ||
14222 | rayTrans.PositionProj = posProj; | ||
14223 | rayTrans.RotationPartProj = rotPartProj; | ||
14224 | rayTrans.RotationBack = rotBack; | ||
14225 | rayTrans.NeedsEnds = true; | ||
14226 | rayTrans.RayLength = rayLength; | ||
14227 | rayTrans.Tolerance = tol; | ||
14228 | rayTrans.Tolerance2 = tol2; | ||
14229 | |||
14230 | // Check mesh | ||
14231 | AddRayInTris(triangles, rayTrans, ref rayHits); | ||
14232 | triangles = null; | ||
14233 | } | ||
14234 | } | ||
14235 | |||
14236 | // Sort hits by ascending distance | ||
14237 | rayHits.Sort((s1, s2) => s1.Distance.CompareTo(s2.Distance)); | ||
14238 | |||
14239 | // Check excess hits per part and group | ||
14240 | for (int t = 0; t < 2; t++) | ||
14241 | { | ||
14242 | int maxHitsPerType = 0; | ||
14243 | UUID id = UUID.Zero; | ||
14244 | if (t == 0) | ||
14245 | maxHitsPerType = m_maxHitsPerPrimInCastRay; | ||
14246 | else | ||
14247 | maxHitsPerType = m_maxHitsPerObjectInCastRay; | ||
14248 | |||
14249 | // Handle excess hits only when needed | ||
14250 | if (maxHitsPerType < m_maxHitsInCastRay) | ||
14251 | { | ||
14252 | // Find excess hits | ||
14253 | Hashtable hits = new Hashtable(); | ||
14254 | for (int i = rayHits.Count - 1; i >= 0; i--) | ||
14255 | { | ||
14256 | if (t == 0) | ||
14257 | id = rayHits[i].PartId; | ||
14258 | else | ||
14259 | id = rayHits[i].GroupId; | ||
14260 | if (hits.ContainsKey(id)) | ||
14261 | hits[id] = (int)hits[id] + 1; | ||
14262 | else | ||
14263 | hits[id] = 1; | ||
14264 | } | ||
14265 | |||
14266 | // Remove excess hits | ||
14267 | for (int i = rayHits.Count - 1; i >= 0; i--) | ||
14268 | { | ||
14269 | if (t == 0) | ||
14270 | id = rayHits[i].PartId; | ||
14271 | else | ||
14272 | id = rayHits[i].GroupId; | ||
14273 | int hit = (int)hits[id]; | ||
14274 | if (hit > m_maxHitsPerPrimInCastRay) | ||
14275 | { | ||
14276 | rayHits.RemoveAt(i); | ||
14277 | hit--; | ||
14278 | hits[id] = hit; | ||
14279 | } | ||
14280 | } | ||
14281 | } | ||
14282 | } | ||
14283 | |||
14284 | // Parse hits into result list according to data flags | ||
14285 | int hitCount = rayHits.Count; | ||
14286 | if (hitCount > maxHits) | ||
14287 | hitCount = maxHits; | ||
14288 | for (int i = 0; i < hitCount; i++) | ||
14289 | { | ||
14290 | RayHit rayHit = rayHits[i]; | ||
14291 | if (getRootKey) | ||
14292 | result.Add(new LSL_Key(rayHit.GroupId.ToString())); | ||
14293 | else | ||
14294 | result.Add(new LSL_Key(rayHit.PartId.ToString())); | ||
14295 | result.Add(new LSL_Vector(rayHit.Position)); | ||
14296 | if (getLinkNum) | ||
14297 | result.Add(new LSL_Integer(rayHit.Link)); | ||
14298 | if (getNormal) | ||
14299 | result.Add(new LSL_Vector(rayHit.Normal)); | ||
14300 | } | ||
14301 | result.Add(new LSL_Integer(hitCount)); | ||
14302 | return result; | ||
14303 | } | ||
14304 | |||
14305 | /// <summary> | ||
14306 | /// Struct for transmitting parameters required for finding llCastRay ray hits. | ||
14307 | /// </summary> | ||
14308 | public struct RayTrans | ||
14309 | { | ||
14310 | public UUID PartId; | ||
14311 | public UUID GroupId; | ||
14312 | public int Link; | ||
14313 | public Vector3 Scale; | ||
14314 | public Vector3 PositionPartProj; | ||
14315 | public Vector3 PositionProj; | ||
14316 | public Quaternion RotationPartProj; | ||
14317 | public Quaternion RotationBack; | ||
14318 | public bool NeedsEnds; | ||
14319 | public float RayLength; | ||
14320 | public float Tolerance; | ||
14321 | public float Tolerance2; | ||
14322 | } | ||
14323 | |||
14324 | /// <summary> | ||
14325 | /// Struct for llCastRay ray hits. | ||
14326 | /// </summary> | ||
14327 | public struct RayHit | ||
14328 | { | ||
14329 | public UUID PartId; | ||
14330 | public UUID GroupId; | ||
14331 | public int Link; | ||
14332 | public Vector3 Position; | ||
14333 | public Vector3 Normal; | ||
14334 | public float Distance; | ||
14335 | } | ||
14336 | |||
14337 | /// <summary> | ||
14338 | /// Helper to parse SimpleMesh for ray hits. | ||
14339 | /// </summary> | ||
14340 | private void AddRayInSimpleMesh(SimpleMesh mesh, RayTrans rayTrans, ref List<RayHit> rayHits) | ||
14341 | { | ||
14342 | if (mesh != null) | ||
14343 | { | ||
14344 | for (int i = 0; i < mesh.Indices.Count; i += 3) | ||
14345 | { | ||
14346 | Tri triangle = new Tri(); | ||
14347 | triangle.p1 = mesh.Vertices[mesh.Indices[i]].Position; | ||
14348 | triangle.p2 = mesh.Vertices[mesh.Indices[i + 1]].Position; | ||
14349 | triangle.p3 = mesh.Vertices[mesh.Indices[i + 2]].Position; | ||
14350 | AddRayInTri(triangle, rayTrans, ref rayHits); | ||
14351 | } | ||
14352 | } | ||
14353 | } | ||
14354 | |||
14355 | /// <summary> | ||
14356 | /// Helper to parse FacetedMesh for ray hits. | ||
14357 | /// </summary> | ||
14358 | private void AddRayInFacetedMesh(FacetedMesh mesh, RayTrans rayTrans, ref List<RayHit> rayHits) | ||
14359 | { | ||
14360 | if (mesh != null) | ||
14361 | { | ||
14362 | foreach (Face face in mesh.Faces) | ||
14363 | { | ||
14364 | for (int i = 0; i <face.Indices.Count; i += 3) | ||
14365 | { | ||
14366 | Tri triangle = new Tri(); | ||
14367 | triangle.p1 = face.Vertices[face.Indices[i]].Position; | ||
14368 | triangle.p2 = face.Vertices[face.Indices[i + 1]].Position; | ||
14369 | triangle.p3 = face.Vertices[face.Indices[i + 2]].Position; | ||
14370 | AddRayInTri(triangle, rayTrans, ref rayHits); | ||
14371 | } | ||
14372 | } | ||
14373 | } | ||
14374 | } | ||
14375 | |||
14376 | /// <summary> | ||
14377 | /// Helper to parse Tri (triangle) List for ray hits. | ||
14378 | /// </summary> | ||
14379 | private void AddRayInTris(List<Tri> triangles, RayTrans rayTrans, ref List<RayHit> rayHits) | ||
14380 | { | ||
14381 | foreach (Tri triangle in triangles) | ||
14382 | { | ||
14383 | AddRayInTri(triangle, rayTrans, ref rayHits); | ||
14384 | } | ||
14385 | } | ||
14386 | |||
14387 | /// <summary> | ||
14388 | /// Helper to add ray hit in a Tri (triangle). | ||
14389 | /// </summary> | ||
14390 | private void AddRayInTri(Tri triangle, RayTrans rayTrans, ref List<RayHit> rayHits) | ||
14391 | { | ||
14392 | // Check for hit in triangle | ||
14393 | float distance; | ||
14394 | Vector3 posHit; | ||
14395 | Vector3 normal; | ||
14396 | if (HitRayInTri(triangle, rayTrans, out distance, out posHit, out normal)) | ||
14397 | { | ||
14398 | // Project hit part back to normal coordinate system | ||
14399 | Vector3 posPart = rayTrans.PositionPartProj * rayTrans.RotationBack - rayTrans.PositionProj; | ||
14400 | // Hack to circumvent ghost face bug in PrimMesher by removing hits in (ghost) faces plane through shape center | ||
14401 | if (Math.Abs(Vector3.Dot(posPart, normal) - Vector3.Dot(posHit, normal)) < rayTrans.Tolerance && !rayTrans.NeedsEnds) | ||
14402 | return; | ||
14403 | // Remove duplicate hits at triangle edges and intersections | ||
14404 | for (int i = rayHits.Count - 1; i >= 0; i--) | ||
14405 | { | ||
14406 | if (rayHits[i].PartId == rayTrans.PartId && Math.Abs(rayHits[i].Distance - distance) < rayTrans.Tolerance2) | ||
14407 | return; | ||
14408 | } | ||
14409 | |||
14410 | // Build result data set | ||
14411 | RayHit rayHit = new RayHit(); | ||
14412 | rayHit.PartId = rayTrans.PartId; | ||
14413 | rayHit.GroupId = rayTrans.GroupId; | ||
14414 | rayHit.Link = rayTrans.Link; | ||
14415 | rayHit.Position = posHit; | ||
14416 | rayHit.Normal = normal; | ||
14417 | rayHit.Distance = distance; | ||
14418 | rayHits.Add(rayHit); | ||
14419 | } | ||
14420 | } | ||
14421 | |||
14422 | /// <summary> | ||
14423 | /// Helper to find ray hit in a Tri (triangle). | ||
14424 | /// </summary> | ||
14425 | private bool HitRayInTri(Tri triangle, RayTrans rayTrans, out float distance, out Vector3 posHit, out Vector3 normal) | ||
14426 | { | ||
14427 | // Initialize | ||
14428 | distance = 0.0f; | ||
14429 | posHit = Vector3.Zero; | ||
14430 | normal = Vector3.Zero; | ||
14431 | float tol = rayTrans.Tolerance; | ||
14432 | |||
14433 | // Project triangle on X-Y plane | ||
14434 | Vector3 pos1 = triangle.p1 * rayTrans.Scale * rayTrans.RotationPartProj + rayTrans.PositionPartProj; | ||
14435 | Vector3 pos2 = triangle.p2 * rayTrans.Scale * rayTrans.RotationPartProj + rayTrans.PositionPartProj; | ||
14436 | Vector3 pos3 = triangle.p3 * rayTrans.Scale * rayTrans.RotationPartProj + rayTrans.PositionPartProj; | ||
14437 | |||
14438 | // Check if ray/origo inside triangle bounding rectangle | ||
14439 | Vector3 lower = Vector3.Min(pos1, Vector3.Min(pos2, pos3)); | ||
14440 | Vector3 upper = Vector3.Max(pos1, Vector3.Max(pos2, pos3)); | ||
14441 | if (lower.X > tol || lower.Y > tol || lower.Z > tol || upper.X < -tol || upper.Y < -tol || upper.Z < -rayTrans.RayLength - tol) | ||
14442 | return false; | ||
14443 | |||
14444 | // Check if ray/origo inside every edge or reverse "outside" every edge on exit | ||
14445 | float dist; | ||
14446 | bool inside = true; | ||
14447 | bool outside = true; | ||
14448 | Vector3 vec1 = pos2 - pos1; | ||
14449 | dist = pos1.X * vec1.Y - pos1.Y * vec1.X; | ||
14450 | if (dist < -tol) | ||
14451 | inside = false; | ||
14452 | if (dist > tol) | ||
14453 | outside = false; | ||
14454 | Vector3 vec2 = pos3 - pos2; | ||
14455 | dist = pos2.X * vec2.Y - pos2.Y * vec2.X; | ||
14456 | if (dist < -tol) | ||
14457 | inside = false; | ||
14458 | if (dist > tol) | ||
14459 | outside = false; | ||
14460 | Vector3 vec3 = pos1 - pos3; | ||
14461 | dist = pos3.X * vec3.Y - pos3.Y * vec3.X; | ||
14462 | if (dist < -tol) | ||
14463 | inside = false; | ||
14464 | if (dist > tol) | ||
14465 | outside = false; | ||
14466 | |||
14467 | // Skip if ray/origo outside | ||
14468 | if (!inside && !(outside && m_detectExitsInCastRay)) | ||
14469 | return false; | ||
14470 | |||
14471 | // Calculate normal | ||
14472 | Vector3 normalProj = Vector3.Cross(vec1, vec2); | ||
14473 | float normalLength = normalProj.Length(); | ||
14474 | // Skip if degenerate triangle | ||
14475 | if (normalLength < tol) | ||
14476 | return false; | ||
14477 | normalProj = normalProj / normalLength; | ||
14478 | // Skip if ray parallell to triangle plane | ||
14479 | if (Math.Abs(normalProj.Z) < tol) | ||
14480 | return false; | ||
14481 | |||
14482 | // Calculate distance | ||
14483 | distance = Vector3.Dot(normalProj, pos2) / normalProj.Z * -1.0f; | ||
14484 | // Skip if outside ray | ||
14485 | if (distance < -tol || distance > rayTrans.RayLength + tol) | ||
14486 | return false; | ||
14487 | |||
14488 | // Calculate projected hit position | ||
14489 | Vector3 posHitProj = new Vector3(0.0f, 0.0f, -distance); | ||
14490 | // Project hit back to normal coordinate system | ||
14491 | posHit = posHitProj * rayTrans.RotationBack - rayTrans.PositionProj; | ||
14492 | normal = normalProj * rayTrans.RotationBack; | ||
14493 | return true; | ||
14494 | } | ||
14495 | |||
14496 | /// <summary> | ||
14497 | /// Helper to parse selected parts of HeightMap into a Tri (triangle) List and calculate bounding box. | ||
14498 | /// </summary> | ||
14499 | private List<Tri> TrisFromHeightmapUnderRay(Vector3 posStart, Vector3 posEnd, out Vector3 lower, out Vector3 upper) | ||
14500 | { | ||
14501 | // Get bounding X-Y rectangle of terrain under ray | ||
14502 | lower = Vector3.Min(posStart, posEnd); | ||
14503 | upper = Vector3.Max(posStart, posEnd); | ||
14504 | lower.X = (float)Math.Floor(lower.X); | ||
14505 | lower.Y = (float)Math.Floor(lower.Y); | ||
14506 | float zLower = float.MaxValue; | ||
14507 | upper.X = (float)Math.Ceiling(upper.X); | ||
14508 | upper.Y = (float)Math.Ceiling(upper.Y); | ||
14509 | float zUpper = float.MinValue; | ||
14510 | |||
14511 | // Initialize Tri (triangle) List | ||
14512 | List<Tri> triangles = new List<Tri>(); | ||
14513 | |||
14514 | // Set parsing lane direction to major ray X-Y axis | ||
14515 | Vector3 vec = posEnd - posStart; | ||
14516 | float xAbs = Math.Abs(vec.X); | ||
14517 | float yAbs = Math.Abs(vec.Y); | ||
14518 | bool bigX = true; | ||
14519 | if (yAbs > xAbs) | ||
14520 | { | ||
14521 | bigX = false; | ||
14522 | vec = vec / yAbs; | ||
14523 | } | ||
14524 | else if (xAbs > yAbs || xAbs > 0.0f) | ||
14525 | vec = vec / xAbs; | ||
14526 | else | ||
14527 | vec = new Vector3(1.0f, 1.0f, 0.0f); | ||
14528 | |||
14529 | // Simplify by start parsing in lower end of lane | ||
14530 | if ((bigX && vec.X < 0.0f) || (!bigX && vec.Y < 0.0f)) | ||
14531 | { | ||
14532 | Vector3 posTemp = posStart; | ||
14533 | posStart = posEnd; | ||
14534 | posEnd = posTemp; | ||
14535 | vec = vec * -1.0f; | ||
14536 | } | ||
14537 | |||
14538 | // First 1x1 rectangle under ray | ||
14539 | float xFloorOld = 0.0f; | ||
14540 | float yFloorOld = 0.0f; | ||
14541 | Vector3 pos = posStart; | ||
14542 | float xFloor = (float)Math.Floor(pos.X); | ||
14543 | float yFloor = (float)Math.Floor(pos.Y); | ||
14544 | AddTrisFromHeightmap(xFloor, yFloor, ref triangles, ref zLower, ref zUpper); | ||
14545 | |||
14546 | // Parse every remaining 1x1 rectangle under ray | ||
14547 | while (pos != posEnd) | ||
14548 | { | ||
14549 | // Next 1x1 rectangle under ray | ||
14550 | xFloorOld = xFloor; | ||
14551 | yFloorOld = yFloor; | ||
14552 | pos = pos + vec; | ||
14553 | |||
14554 | // Clip position to 1x1 rectangle border | ||
14555 | xFloor = (float)Math.Floor(pos.X); | ||
14556 | yFloor = (float)Math.Floor(pos.Y); | ||
14557 | if (bigX && pos.X > xFloor) | ||
14558 | { | ||
14559 | pos.Y -= vec.Y * (pos.X - xFloor); | ||
14560 | pos.X = xFloor; | ||
14561 | } | ||
14562 | else if (!bigX && pos.Y > yFloor) | ||
14563 | { | ||
14564 | pos.X -= vec.X * (pos.Y - yFloor); | ||
14565 | pos.Y = yFloor; | ||
14566 | } | ||
14567 | |||
14568 | // Last 1x1 rectangle under ray | ||
14569 | if ((bigX && pos.X >= posEnd.X) || (!bigX && pos.Y >= posEnd.Y)) | ||
14570 | { | ||
14571 | pos = posEnd; | ||
14572 | xFloor = (float)Math.Floor(pos.X); | ||
14573 | yFloor = (float)Math.Floor(pos.Y); | ||
14574 | } | ||
14575 | |||
14576 | // Add new 1x1 rectangle in lane | ||
14577 | if ((bigX && xFloor != xFloorOld) || (!bigX && yFloor != yFloorOld)) | ||
14578 | AddTrisFromHeightmap(xFloor, yFloor, ref triangles, ref zLower, ref zUpper); | ||
14579 | // Add last 1x1 rectangle in old lane at lane shift | ||
14580 | if (bigX && yFloor != yFloorOld) | ||
14581 | AddTrisFromHeightmap(xFloor, yFloorOld, ref triangles, ref zLower, ref zUpper); | ||
14582 | if (!bigX && xFloor != xFloorOld) | ||
14583 | AddTrisFromHeightmap(xFloorOld, yFloor, ref triangles, ref zLower, ref zUpper); | ||
14584 | } | ||
14585 | |||
14586 | // Finalize bounding box Z | ||
14587 | lower.Z = zLower; | ||
14588 | upper.Z = zUpper; | ||
14589 | |||
14590 | // Done and returning Tri (triangle)List | ||
14591 | return triangles; | ||
14592 | } | ||
14593 | |||
14594 | /// <summary> | ||
14595 | /// Helper to add HeightMap squares into Tri (triangle) List and adjust bounding box. | ||
14596 | /// </summary> | ||
14597 | private void AddTrisFromHeightmap(float xPos, float yPos, ref List<Tri> triangles, ref float zLower, ref float zUpper) | ||
14598 | { | ||
14599 | int xInt = (int)xPos; | ||
14600 | int yInt = (int)yPos; | ||
14601 | |||
14602 | // Corner 1 of 1x1 rectangle | ||
14603 | int x = Util.Clamp<int>(xInt+1, 0, World.Heightmap.Width - 1); | ||
14604 | int y = Util.Clamp<int>(yInt+1, 0, World.Heightmap.Height - 1); | ||
14605 | Vector3 pos1 = new Vector3(x, y, (float)World.Heightmap[x, y]); | ||
14606 | // Adjust bounding box | ||
14607 | zLower = Math.Min(zLower, pos1.Z); | ||
14608 | zUpper = Math.Max(zUpper, pos1.Z); | ||
14609 | |||
14610 | // Corner 2 of 1x1 rectangle | ||
14611 | x = Util.Clamp<int>(xInt, 0, World.Heightmap.Width - 1); | ||
14612 | y = Util.Clamp<int>(yInt+1, 0, World.Heightmap.Height - 1); | ||
14613 | Vector3 pos2 = new Vector3(x, y, (float)World.Heightmap[x, y]); | ||
14614 | // Adjust bounding box | ||
14615 | zLower = Math.Min(zLower, pos1.Z); | ||
14616 | zUpper = Math.Max(zUpper, pos1.Z); | ||
14617 | |||
14618 | // Corner 3 of 1x1 rectangle | ||
14619 | x = Util.Clamp<int>(xInt, 0, World.Heightmap.Width - 1); | ||
14620 | y = Util.Clamp<int>(yInt, 0, World.Heightmap.Height - 1); | ||
14621 | Vector3 pos3 = new Vector3(x, y, (float)World.Heightmap[x, y]); | ||
14622 | // Adjust bounding box | ||
14623 | zLower = Math.Min(zLower, pos1.Z); | ||
14624 | zUpper = Math.Max(zUpper, pos1.Z); | ||
14625 | |||
14626 | // Corner 4 of 1x1 rectangle | ||
14627 | x = Util.Clamp<int>(xInt+1, 0, World.Heightmap.Width - 1); | ||
14628 | y = Util.Clamp<int>(yInt, 0, World.Heightmap.Height - 1); | ||
14629 | Vector3 pos4 = new Vector3(x, y, (float)World.Heightmap[x, y]); | ||
14630 | // Adjust bounding box | ||
14631 | zLower = Math.Min(zLower, pos1.Z); | ||
14632 | zUpper = Math.Max(zUpper, pos1.Z); | ||
14633 | |||
14634 | // Add triangle 1 | ||
14635 | Tri triangle1 = new Tri(); | ||
14636 | triangle1.p1 = pos1; | ||
14637 | triangle1.p2 = pos2; | ||
14638 | triangle1.p3 = pos3; | ||
14639 | triangles.Add(triangle1); | ||
14640 | |||
14641 | // Add triangle 2 | ||
14642 | Tri triangle2 = new Tri(); | ||
14643 | triangle2.p1 = pos3; | ||
14644 | triangle2.p2 = pos4; | ||
14645 | triangle2.p3 = pos1; | ||
14646 | triangles.Add(triangle2); | ||
14647 | } | ||
14648 | |||
14649 | /// <summary> | ||
14650 | /// Helper to check if a ray intersects bounding shapes. | ||
14651 | /// </summary> | ||
14652 | private bool InBoundingShapes(Vector3 ray, float rayLength, Vector3 scale, Vector3 posPartRel, Vector3 posPartProj, Quaternion rotProj) | ||
14653 | { | ||
14654 | float tol = m_floatToleranceInCastRay; | ||
14655 | |||
14656 | // Check if ray intersects projected bounding box | ||
14657 | Vector3 lowerBox = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); | ||
14658 | Vector3 upperBox = new Vector3(float.MinValue, float.MinValue, float.MinValue); | ||
14659 | int dummy = 0; | ||
14660 | AddBoundingBoxOfSimpleBox(scale * -0.5f, scale * 0.5f, posPartProj, rotProj, true, ref lowerBox, ref upperBox, ref dummy); | ||
14661 | if (lowerBox.X > tol || lowerBox.Y > tol || lowerBox.Z > tol || upperBox.X < -tol || upperBox.Y < -tol || upperBox.Z < -rayLength - tol) | ||
14662 | return false; | ||
14663 | |||
14664 | // Passed bounding shape filters, so return true | ||
14665 | return true; | ||
14666 | } | ||
14667 | |||
14668 | /// <summary> | ||
14669 | /// Helper to get link number for a UUID. | ||
14670 | /// </summary> | ||
14671 | private int UUID2LinkNumber(SceneObjectPart part, UUID id) | ||
14672 | { | ||
14673 | SceneObjectGroup group = part.ParentGroup; | ||
14674 | if (group != null) | ||
14675 | { | ||
14676 | // Parse every link for UUID | ||
14677 | int linkCount = group.PrimCount + group.GetSittingAvatarsCount(); | ||
14678 | for (int link = linkCount; link > 0; link--) | ||
14679 | { | ||
14680 | ISceneEntity entity = GetLinkEntity(part, link); | ||
14681 | // Return link number if UUID match | ||
14682 | if (entity != null && entity.UUID == id) | ||
14683 | return link; | ||
14684 | } | ||
14685 | } | ||
14686 | // Return link number 0 if no links or UUID matches | ||
14687 | return 0; | ||
14688 | } | ||
14689 | |||
13932 | public LSL_Integer llManageEstateAccess(int action, string avatar) | 14690 | public LSL_Integer llManageEstateAccess(int action, string avatar) |
13933 | { | 14691 | { |
13934 | m_host.AddScriptLPS(1); | 14692 | m_host.AddScriptLPS(1); |