public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options)
{
Vector3 dir = new Vector3((float)(end - start).x, (float)(end - start).y, (float)(end - start).z);
Vector3 startvector = new Vector3((float)start.x, (float)start.y, (float)start.z);
Vector3 endvector = new Vector3((float)end.x, (float)end.y, (float)end.z);
int count = 0;
int detectPhantom = 0;
int dataFlags = 0;
int rejectTypes = 0;
for (int i = 0; i < options.Length; i += 2)
{
if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS)
{
count = options.GetLSLIntegerItem(i + 1);
}
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM)
{
detectPhantom = 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_REJECT_TYPES)
{
rejectTypes = options.GetLSLIntegerItem(i + 1);
}
}
LSL_List list = new LSL_List();
List<ContactResult> results = World.PhysicsScene.RaycastWorld(startvector, dir, dir.Length(), count);
double distance = Util.GetDistanceTo(startvector, endvector);
if (distance == 0)
distance = 0.001;
Vector3 posToCheck = startvector;
ITerrainChannel channel = World.RequestModuleInterface<ITerrainChannel>();
List<IScenePresence> presences = new List<IScenePresence>(World.Entities.GetPresences(startvector, (float)distance));
bool checkTerrain = !((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) == ScriptBaseClass.RC_REJECT_LAND);
bool checkAgents = !((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) == ScriptBaseClass.RC_REJECT_AGENTS);
bool checkNonPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) == ScriptBaseClass.RC_REJECT_NONPHYSICAL);
bool checkPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) == ScriptBaseClass.RC_REJECT_PHYSICAL);
for (float i = 0; i <= distance; i += 0.1f)
{
posToCheck = startvector + (dir * (i / (float)distance));
float groundHeight = channel[(int)(posToCheck.X + startvector.X), (int)(posToCheck.Y + startvector.Y)];
if (checkTerrain && groundHeight > posToCheck.Z)
{
ContactResult result = new ContactResult { ConsumerID = 0, Depth = 0, Normal = Vector3.Zero, Pos = posToCheck };
results.Add(result);
checkTerrain = false;
}
if (checkAgents)
{
for (int presenceCount = 0; presenceCount < presences.Count; presenceCount++)
{
IScenePresence sp = presences[presenceCount];
if (sp.AbsolutePosition.ApproxEquals(posToCheck, sp.PhysicsActor.Size.X))
{
ContactResult result = new ContactResult
{
ConsumerID = sp.LocalId,
Depth = 0,
Normal = Vector3.Zero,
Pos = posToCheck
};
results.Add(result);
presences.RemoveAt(presenceCount);
if (presenceCount > 0)
presenceCount--; //Reset its position since we removed this one
}
}
}
}
int refcount = 0;
List<ContactResult> newResults = new List<ContactResult>();
foreach (ContactResult result in results)
{
foreach (ContactResult r in newResults)
if (r.ConsumerID == result.ConsumerID)
newResults.Add(result);
}
castRaySort(startvector, ref newResults);
foreach (ContactResult result in newResults)
{
if ((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) == ScriptBaseClass.RC_REJECT_LAND &&
result.ConsumerID == 0)
continue;
IEntity entity = World.GetSceneObjectPart(result.ConsumerID);
if (entity == null && (rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) != ScriptBaseClass.RC_REJECT_AGENTS)
entity = World.GetScenePresence(result.ConsumerID); //Only check if we should be looking for agents
if (entity == null)
{
list.Add(UUID.Zero);
if ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) == ScriptBaseClass.RC_GET_LINK_NUM)
list.Add(0);
list.Add(result.Pos);
if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL)
list.Add(result.Normal);
refcount++;
continue; //Can't find it, so add UUID.Zero
}
/*if (detectPhantom == 0 && intersection.obj is ISceneChildEntity &&
((ISceneChildEntity)intersection.obj).PhysActor == null)
continue;*/
//Can't do this ATM, physics engine knows only of non phantom objects
if (entity is ISceneChildEntity && ((ISceneChildEntity)entity).PhysActor != null && ((ISceneChildEntity)entity).PhysActor.IsPhysical)
{
if (!checkPhysical)
continue;
}
else if (entity is ISceneChildEntity)
if (!checkNonPhysical)
continue;
refcount++;
if ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) == ScriptBaseClass.RC_GET_ROOT_KEY && entity is ISceneChildEntity)
list.Add(((ISceneChildEntity)entity).ParentEntity.UUID);
else
list.Add(entity.UUID);
if ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) == ScriptBaseClass.RC_GET_LINK_NUM)
if (entity is ISceneChildEntity)
list.Add(entity.LinkNum);
else
list.Add(0);
list.Add(result.Pos);
if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL)
list.Add(result.Normal);
}
list.Add(refcount); //The status code, either the # of contacts, RCERR_SIM_PERF_LOW, or RCERR_CAST_TIME_EXCEEDED
return list;
}