2008-08-24 05:21:42 +00:00
using System ;
using System.Collections.Generic ;
using System.Threading ;
2008-10-05 22:05:18 +00:00
using ExtensionLoader ;
2008-08-24 05:21:42 +00:00
using OpenMetaverse ;
using OpenMetaverse.Rendering ;
using OpenMetaverse.Packets ;
namespace Simian.Extensions
{
2008-10-29 20:11:28 +00:00
public class ObjectManager : IExtension < Simian >
2008-08-24 05:21:42 +00:00
{
2008-10-29 20:11:28 +00:00
Simian server ;
2008-09-04 21:09:44 +00:00
2008-08-24 05:21:42 +00:00
2008-10-29 20:11:28 +00:00
public ObjectManager ( )
2008-08-24 05:21:42 +00:00
{
}
2008-10-29 20:11:28 +00:00
public void Start ( Simian server )
2008-08-24 05:21:42 +00:00
{
2008-10-29 20:11:28 +00:00
this . server = server ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectAdd , new PacketCallback ( ObjectAddHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectDuplicate , new PacketCallback ( ObjectDuplicateHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectSelect , new PacketCallback ( ObjectSelectHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectDeselect , new PacketCallback ( ObjectDeselectHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectLink , new PacketCallback ( ObjectLinkHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectDelink , new PacketCallback ( ObjectDelinkHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectShape , new PacketCallback ( ObjectShapeHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectFlagUpdate , new PacketCallback ( ObjectFlagUpdateHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectExtraParams , new PacketCallback ( ObjectExtraParamsHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . ObjectImage , new PacketCallback ( ObjectImageHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . DeRezObject , new PacketCallback ( DeRezObjectHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . MultipleObjectUpdate , new PacketCallback ( MultipleObjectUpdateHandler ) ) ;
server . UDP . RegisterPacketCallback ( PacketType . RequestObjectPropertiesFamily , new PacketCallback ( RequestObjectPropertiesFamilyHandler ) ) ;
2008-08-24 05:21:42 +00:00
}
public void Stop ( )
{
}
void ObjectAddHandler ( Packet packet , Agent agent )
{
ObjectAddPacket add = ( ObjectAddPacket ) packet ;
Vector3 position = Vector3 . Zero ;
Vector3 scale = add . ObjectData . Scale ;
PCode pcode = ( PCode ) add . ObjectData . PCode ;
PrimFlags flags = ( PrimFlags ) add . ObjectData . AddFlags ;
bool bypassRaycast = ( add . ObjectData . BypassRaycast = = 1 ) ;
bool rayEndIsIntersection = ( add . ObjectData . RayEndIsIntersection = = 1 ) ;
#region Position Calculation
if ( rayEndIsIntersection )
{
// HACK: Blindly trust where the client tells us to place
position = add . ObjectData . RayEnd ;
}
else
{
if ( add . ObjectData . RayTargetID ! = UUID . Zero )
{
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( add . ObjectData . RayTargetID , out obj ) )
2008-08-24 05:21:42 +00:00
{
// Test for a collision with the specified object
position = ObjectCollisionTest ( add . ObjectData . RayStart , add . ObjectData . RayEnd , obj ) ;
}
}
if ( position = = Vector3 . Zero )
{
// Test for a collision with the entire scene
position = FullSceneCollisionTest ( add . ObjectData . RayStart , add . ObjectData . RayEnd ) ;
}
}
2008-08-28 02:38:32 +00:00
// Position lies on the face of another surface, either terrain of an object.
// Back up along the ray so we are not colliding with the mesh.
// HACK: This is really cheesy and should be done by a collision system
Vector3 rayDir = Vector3 . Normalize ( add . ObjectData . RayEnd - add . ObjectData . RayStart ) ;
position - = rayDir * scale ;
2008-08-27 23:41:25 +00:00
2008-08-24 05:21:42 +00:00
#endregion Position Calculation
#region Foliage Handling
// Set all foliage to phantom
if ( pcode = = PCode . Grass | | pcode = = PCode . Tree | | pcode = = PCode . NewTree )
{
flags | = PrimFlags . Phantom ;
if ( pcode ! = PCode . Grass )
{
// Resize based on the foliage type
Tree tree = ( Tree ) add . ObjectData . State ;
switch ( tree )
{
case Tree . Cypress1 :
case Tree . Cypress2 :
scale = new Vector3 ( 4f , 4f , 10f ) ;
break ;
default :
scale = new Vector3 ( 4f , 4f , 4f ) ;
break ;
}
}
}
#endregion Foliage Handling
// Create an object
Primitive prim = new Primitive ( ) ;
prim . Flags =
PrimFlags . ObjectModify |
PrimFlags . ObjectCopy |
PrimFlags . ObjectAnyOwner |
PrimFlags . ObjectMove |
PrimFlags . ObjectTransfer |
PrimFlags . ObjectOwnerModify ;
// TODO: Security check
prim . GroupID = add . AgentData . GroupID ;
prim . ID = UUID . Random ( ) ;
prim . MediaURL = String . Empty ;
2008-12-21 02:09:54 +00:00
prim . OwnerID = agent . Avatar . ID ;
2008-08-24 05:21:42 +00:00
prim . Position = position ;
prim . PrimData . Material = ( Material ) add . ObjectData . Material ;
prim . PrimData . PathCurve = ( PathCurve ) add . ObjectData . PathCurve ;
prim . PrimData . ProfileCurve = ( ProfileCurve ) add . ObjectData . ProfileCurve ;
prim . PrimData . PathBegin = Primitive . UnpackBeginCut ( add . ObjectData . PathBegin ) ;
prim . PrimData . PathEnd = Primitive . UnpackEndCut ( add . ObjectData . PathEnd ) ;
prim . PrimData . PathScaleX = Primitive . UnpackPathScale ( add . ObjectData . PathScaleX ) ;
prim . PrimData . PathScaleY = Primitive . UnpackPathScale ( add . ObjectData . PathScaleY ) ;
prim . PrimData . PathShearX = Primitive . UnpackPathShear ( ( sbyte ) add . ObjectData . PathShearX ) ;
prim . PrimData . PathShearY = Primitive . UnpackPathShear ( ( sbyte ) add . ObjectData . PathShearY ) ;
prim . PrimData . PathTwist = Primitive . UnpackPathTwist ( add . ObjectData . PathTwist ) ;
prim . PrimData . PathTwistBegin = Primitive . UnpackPathTwist ( add . ObjectData . PathTwistBegin ) ;
prim . PrimData . PathRadiusOffset = Primitive . UnpackPathTwist ( add . ObjectData . PathRadiusOffset ) ;
prim . PrimData . PathTaperX = Primitive . UnpackPathTaper ( add . ObjectData . PathTaperX ) ;
prim . PrimData . PathTaperY = Primitive . UnpackPathTaper ( add . ObjectData . PathTaperY ) ;
prim . PrimData . PathRevolutions = Primitive . UnpackPathRevolutions ( add . ObjectData . PathRevolutions ) ;
prim . PrimData . PathSkew = Primitive . UnpackPathTwist ( add . ObjectData . PathSkew ) ;
prim . PrimData . ProfileBegin = Primitive . UnpackBeginCut ( add . ObjectData . ProfileBegin ) ;
prim . PrimData . ProfileEnd = Primitive . UnpackEndCut ( add . ObjectData . ProfileEnd ) ;
prim . PrimData . ProfileHollow = Primitive . UnpackProfileHollow ( add . ObjectData . ProfileHollow ) ;
prim . PrimData . PCode = pcode ;
2009-02-03 01:59:46 +00:00
prim . Properties = new Primitive . ObjectProperties ( ) ;
2008-08-24 05:21:42 +00:00
prim . Properties . CreationDate = DateTime . Now ;
2008-12-21 02:09:54 +00:00
prim . Properties . CreatorID = agent . Avatar . ID ;
2008-08-24 05:21:42 +00:00
prim . Properties . Description = String . Empty ;
prim . Properties . GroupID = add . AgentData . GroupID ;
2008-12-21 02:09:54 +00:00
prim . Properties . LastOwnerID = agent . Avatar . ID ;
2008-08-24 05:21:42 +00:00
prim . Properties . Name = "New Object" ;
prim . Properties . ObjectID = prim . ID ;
prim . Properties . OwnerID = prim . OwnerID ;
prim . Properties . Permissions = Permissions . FullPermissions ;
prim . Properties . SalePrice = 10 ;
2009-02-02 21:33:21 +00:00
prim . RegionHandle = server . Scene . RegionHandle ;
2008-08-24 05:21:42 +00:00
prim . Rotation = add . ObjectData . Rotation ;
prim . Scale = scale ;
prim . Textures = new Primitive . TextureEntry ( Primitive . TextureEntry . WHITE_TEXTURE ) ;
prim . TextColor = Color4 . Black ;
// Add this prim to the object database
2008-10-29 20:11:28 +00:00
SimulationObject simObj = new SimulationObject ( prim , server ) ;
2008-12-16 08:43:27 +00:00
server . Scene . ObjectAdd ( this , simObj , flags ) ;
2008-09-14 08:12:11 +00:00
}
void ObjectDuplicateHandler ( Packet packet , Agent agent )
{
ObjectDuplicatePacket duplicate = ( ObjectDuplicatePacket ) packet ;
PrimFlags flags = ( PrimFlags ) duplicate . SharedData . DuplicateFlags ;
Vector3 offset = duplicate . SharedData . Offset ;
for ( int i = 0 ; i < duplicate . ObjectData . Length ; i + + )
{
uint dupeID = duplicate . ObjectData [ i ] . ObjectLocalID ;
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( dupeID , out obj ) )
2008-09-14 08:12:11 +00:00
{
SimulationObject newObj = new SimulationObject ( obj ) ;
newObj . Prim . Position + = offset ;
newObj . Prim . ID = UUID . Random ( ) ;
2008-12-16 08:43:27 +00:00
server . Scene . ObjectAdd ( this , newObj , flags ) ;
2008-09-14 08:12:11 +00:00
}
else
{
Logger . Log ( "ObjectDuplicate sent for missing object " + dupeID ,
Helpers . LogLevel . Warning ) ;
KillObjectPacket kill = new KillObjectPacket ( ) ;
kill . ObjectData = new KillObjectPacket . ObjectDataBlock [ 1 ] ;
kill . ObjectData [ 0 ] = new KillObjectPacket . ObjectDataBlock ( ) ;
kill . ObjectData [ 0 ] . ID = dupeID ;
2008-12-21 02:09:54 +00:00
server . UDP . SendPacket ( agent . Avatar . ID , kill , PacketCategory . State ) ;
2008-09-14 08:12:11 +00:00
}
}
2008-08-24 05:21:42 +00:00
}
void ObjectSelectHandler ( Packet packet , Agent agent )
{
ObjectSelectPacket select = ( ObjectSelectPacket ) packet ;
for ( int i = 0 ; i < select . ObjectData . Length ; i + + )
{
2008-10-03 22:14:18 +00:00
ObjectPropertiesPacket properties = new ObjectPropertiesPacket ( ) ;
properties . ObjectData = new ObjectPropertiesPacket . ObjectDataBlock [ 1 ] ;
properties . ObjectData [ 0 ] = new ObjectPropertiesPacket . ObjectDataBlock ( ) ;
2008-08-24 05:21:42 +00:00
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( select . ObjectData [ i ] . ObjectLocalID , out obj ) )
2008-08-24 05:21:42 +00:00
{
2008-09-14 08:12:11 +00:00
//Logger.DebugLog("Selecting object " + obj.Prim.LocalID);
2008-12-18 21:45:38 +00:00
if ( obj . Prim . Properties ! = null )
{
properties . ObjectData [ 0 ] . BaseMask = ( uint ) obj . Prim . Properties . Permissions . BaseMask ;
properties . ObjectData [ 0 ] . CreationDate = Utils . DateTimeToUnixTime ( obj . Prim . Properties . CreationDate ) ;
properties . ObjectData [ 0 ] . CreatorID = obj . Prim . Properties . CreatorID ;
properties . ObjectData [ 0 ] . Description = Utils . StringToBytes ( obj . Prim . Properties . Description ) ;
properties . ObjectData [ 0 ] . EveryoneMask = ( uint ) obj . Prim . Properties . Permissions . EveryoneMask ;
properties . ObjectData [ 0 ] . GroupID = obj . Prim . Properties . GroupID ;
properties . ObjectData [ 0 ] . GroupMask = ( uint ) obj . Prim . Properties . Permissions . GroupMask ;
properties . ObjectData [ 0 ] . LastOwnerID = obj . Prim . Properties . LastOwnerID ;
properties . ObjectData [ 0 ] . Name = Utils . StringToBytes ( obj . Prim . Properties . Name ) ;
properties . ObjectData [ 0 ] . NextOwnerMask = ( uint ) obj . Prim . Properties . Permissions . NextOwnerMask ;
properties . ObjectData [ 0 ] . ObjectID = obj . Prim . ID ;
properties . ObjectData [ 0 ] . OwnerID = obj . Prim . Properties . OwnerID ;
properties . ObjectData [ 0 ] . OwnerMask = ( uint ) obj . Prim . Properties . Permissions . OwnerMask ;
properties . ObjectData [ 0 ] . OwnershipCost = obj . Prim . Properties . OwnershipCost ;
properties . ObjectData [ 0 ] . SalePrice = obj . Prim . Properties . SalePrice ;
properties . ObjectData [ 0 ] . SaleType = ( byte ) obj . Prim . Properties . SaleType ;
properties . ObjectData [ 0 ] . SitName = new byte [ 0 ] ; // FIXME: Finish these
properties . ObjectData [ 0 ] . TextureID = new byte [ 0 ] ;
properties . ObjectData [ 0 ] . TouchName = new byte [ 0 ] ;
}
else
{
properties . ObjectData [ 0 ] . BaseMask = ( uint ) PermissionMask . All ;
properties . ObjectData [ 0 ] . CreationDate = Utils . DateTimeToUnixTime ( DateTime . Now ) ;
2008-12-21 02:09:54 +00:00
properties . ObjectData [ 0 ] . CreatorID = agent . Avatar . ID ;
2008-12-18 21:45:38 +00:00
properties . ObjectData [ 0 ] . Description = Utils . StringToBytes ( String . Empty ) ;
properties . ObjectData [ 0 ] . EveryoneMask = ( uint ) PermissionMask . All ;
properties . ObjectData [ 0 ] . GroupID = UUID . Zero ;
properties . ObjectData [ 0 ] . GroupMask = ( uint ) PermissionMask . All ;
properties . ObjectData [ 0 ] . LastOwnerID = UUID . Zero ;
properties . ObjectData [ 0 ] . Name = Utils . StringToBytes ( String . Empty ) ;
properties . ObjectData [ 0 ] . NextOwnerMask = ( uint ) PermissionMask . All ;
properties . ObjectData [ 0 ] . ObjectID = obj . Prim . ID ;
2008-12-21 02:09:54 +00:00
properties . ObjectData [ 0 ] . OwnerID = agent . Avatar . ID ;
2008-12-18 21:45:38 +00:00
properties . ObjectData [ 0 ] . OwnerMask = ( uint ) PermissionMask . All ;
properties . ObjectData [ 0 ] . OwnershipCost = 0 ;
properties . ObjectData [ 0 ] . SalePrice = 0 ;
properties . ObjectData [ 0 ] . SaleType = ( byte ) SaleType . Not ;
properties . ObjectData [ 0 ] . SitName = new byte [ 0 ] ;
properties . ObjectData [ 0 ] . TextureID = new byte [ 0 ] ;
properties . ObjectData [ 0 ] . TouchName = new byte [ 0 ] ;
}
2008-10-03 22:14:18 +00:00
2008-12-21 02:09:54 +00:00
server . UDP . SendPacket ( agent . Avatar . ID , properties , PacketCategory . Transaction ) ;
2008-08-24 05:21:42 +00:00
}
2008-09-04 21:09:44 +00:00
else
{
Logger . Log ( "ObjectSelect sent for missing object " + select . ObjectData [ i ] . ObjectLocalID ,
Helpers . LogLevel . Warning ) ;
2008-10-03 22:14:18 +00:00
properties . ObjectData [ 0 ] . Description = new byte [ 0 ] ;
properties . ObjectData [ 0 ] . Name = new byte [ 0 ] ;
properties . ObjectData [ 0 ] . SitName = new byte [ 0 ] ;
properties . ObjectData [ 0 ] . TextureID = new byte [ 0 ] ;
properties . ObjectData [ 0 ] . TouchName = new byte [ 0 ] ;
2008-09-04 21:09:44 +00:00
KillObjectPacket kill = new KillObjectPacket ( ) ;
kill . ObjectData = new KillObjectPacket . ObjectDataBlock [ 1 ] ;
kill . ObjectData [ 0 ] = new KillObjectPacket . ObjectDataBlock ( ) ;
kill . ObjectData [ 0 ] . ID = select . ObjectData [ i ] . ObjectLocalID ;
2008-12-21 02:09:54 +00:00
server . UDP . SendPacket ( agent . Avatar . ID , kill , PacketCategory . State ) ;
2008-09-04 21:09:44 +00:00
}
2008-08-24 05:21:42 +00:00
}
2008-10-03 22:14:18 +00:00
2008-08-24 05:21:42 +00:00
}
2008-08-28 02:38:32 +00:00
void ObjectDeselectHandler ( Packet packet , Agent agent )
{
ObjectDeselectPacket deselect = ( ObjectDeselectPacket ) packet ;
2008-09-14 08:12:11 +00:00
for ( int i = 0 ; i < deselect . ObjectData . Length ; i + + )
{
uint localID = deselect . ObjectData [ i ] . ObjectLocalID ;
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( localID , out obj ) )
2008-09-14 08:12:11 +00:00
{
//Logger.DebugLog("Deselecting object " + obj.Prim.LocalID);
}
}
2008-08-28 02:38:32 +00:00
// TODO: Do we need this at all?
}
2008-08-30 22:48:37 +00:00
void ObjectLinkHandler ( Packet packet , Agent agent )
{
ObjectLinkPacket link = ( ObjectLinkPacket ) packet ;
List < SimulationObject > linkSet = new List < SimulationObject > ( ) ;
2008-09-14 08:12:11 +00:00
for ( int i = 0 ; i < link . ObjectData . Length ; i + + )
2008-08-30 22:48:37 +00:00
{
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( ! server . Scene . TryGetObject ( link . ObjectData [ i ] . ObjectLocalID , out obj ) )
2008-08-30 22:48:37 +00:00
{
//TODO: send an error message
return ;
}
2008-12-21 02:09:54 +00:00
else if ( obj . Prim . OwnerID ! = agent . Avatar . ID )
2008-08-30 22:48:37 +00:00
{
//TODO: send an error message
return ;
}
else
{
linkSet . Add ( obj ) ;
}
}
for ( int i = 0 ; i < linkSet . Count ; i + + )
{
linkSet [ i ] . LinkNumber = i + 1 ;
2008-08-31 00:44:11 +00:00
2008-10-03 22:14:18 +00:00
ObjectUpdatePacket update = new ObjectUpdatePacket ( ) ;
2009-02-02 21:33:21 +00:00
update . RegionData . RegionHandle = server . Scene . RegionHandle ;
2008-10-03 22:14:18 +00:00
update . RegionData . TimeDilation = UInt16 . MaxValue ;
update . ObjectData = new ObjectUpdatePacket . ObjectDataBlock [ 1 ] ;
2009-02-02 21:33:21 +00:00
update . ObjectData [ 0 ] = SimulationObject . BuildUpdateBlock ( linkSet [ i ] . Prim , server . Scene . RegionHandle , linkSet [ i ] . Prim . Flags ) ;
2008-08-30 22:48:37 +00:00
2008-08-31 02:03:13 +00:00
if ( linkSet [ i ] . Prim . ParentID > 0 )
{
2008-10-12 14:49:23 +00:00
//previously linked children
2008-08-31 02:03:13 +00:00
SimulationObject parent ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( linkSet [ i ] . Prim . ParentID , out parent ) )
2008-08-31 02:03:13 +00:00
{
//re-add old root orientation
2008-10-12 14:49:23 +00:00
linkSet [ i ] . Prim . Position = parent . Prim . Position + Vector3 . Transform ( linkSet [ i ] . Prim . Position , Matrix4 . CreateFromQuaternion ( parent . Prim . Rotation ) ) ;
2008-08-31 02:03:13 +00:00
linkSet [ i ] . Prim . Rotation * = parent . Prim . Rotation ;
}
}
2008-08-31 00:44:11 +00:00
if ( i > 0 )
2008-08-30 22:48:37 +00:00
{
2008-08-31 00:44:11 +00:00
//subtract root prim orientation
2008-10-12 14:49:23 +00:00
linkSet [ i ] . Prim . Position = Vector3 . Transform ( linkSet [ i ] . Prim . Position - linkSet [ 0 ] . Prim . Position , Matrix4 . CreateFromQuaternion ( Quaternion . Identity / linkSet [ 0 ] . Prim . Rotation ) ) ;
2008-08-31 00:44:11 +00:00
linkSet [ i ] . Prim . Rotation / = linkSet [ 0 ] . Prim . Rotation ;
//set parent ID
2008-10-03 22:14:18 +00:00
update . ObjectData [ 0 ] . ParentID = linkSet [ 0 ] . Prim . LocalID ;
2008-08-30 22:48:37 +00:00
}
2008-09-14 08:12:11 +00:00
else
{
2008-10-12 14:49:23 +00:00
//root prim
2008-10-03 22:14:18 +00:00
update . ObjectData [ 0 ] . ParentID = 0 ;
2008-09-14 08:12:11 +00:00
}
2008-08-31 00:44:11 +00:00
2008-10-03 22:14:18 +00:00
update . ObjectData [ 0 ] . ObjectData = SimulationObject . BuildObjectData (
2008-09-14 08:12:11 +00:00
linkSet [ i ] . Prim . Position , linkSet [ i ] . Prim . Rotation ,
Vector3 . Zero , Vector3 . Zero , Vector3 . Zero ) ;
2008-10-03 22:14:18 +00:00
2008-10-29 20:11:28 +00:00
server . UDP . BroadcastPacket ( update , PacketCategory . State ) ;
2008-08-30 22:48:37 +00:00
}
}
void ObjectDelinkHandler ( Packet packet , Agent agent )
{
2008-08-31 00:44:11 +00:00
ObjectDelinkPacket delink = ( ObjectDelinkPacket ) packet ;
List < SimulationObject > linkSet = new List < SimulationObject > ( ) ;
for ( int i = 0 ; i < delink . ObjectData . Length ; i + + )
{
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( ! server . Scene . TryGetObject ( delink . ObjectData [ i ] . ObjectLocalID , out obj ) )
2008-08-31 00:44:11 +00:00
{
//TODO: send an error message
return ;
}
2008-12-21 02:09:54 +00:00
else if ( obj . Prim . OwnerID ! = agent . Avatar . ID )
2008-08-31 00:44:11 +00:00
{
//TODO: send an error message
return ;
}
else
{
linkSet . Add ( obj ) ;
}
}
ObjectUpdatePacket update = new ObjectUpdatePacket ( ) ;
2009-02-02 21:33:21 +00:00
update . RegionData . RegionHandle = server . Scene . RegionHandle ;
2008-08-31 00:44:11 +00:00
update . RegionData . TimeDilation = UInt16 . MaxValue ;
update . ObjectData = new ObjectUpdatePacket . ObjectDataBlock [ linkSet . Count ] ;
for ( int i = 0 ; i < linkSet . Count ; i + + )
{
2008-09-04 21:09:44 +00:00
update . ObjectData [ i ] = SimulationObject . BuildUpdateBlock ( linkSet [ i ] . Prim ,
2009-02-02 21:33:21 +00:00
server . Scene . RegionHandle , linkSet [ i ] . Prim . Flags ) ;
2008-08-31 00:44:11 +00:00
update . ObjectData [ i ] . ParentID = 0 ;
linkSet [ i ] . LinkNumber = 0 ;
//add root prim orientation to child prims
if ( i > 0 )
{
2008-10-12 14:49:23 +00:00
linkSet [ i ] . Prim . Position = linkSet [ 0 ] . Prim . Position + Vector3 . Transform ( linkSet [ i ] . Prim . Position , Matrix4 . CreateFromQuaternion ( linkSet [ 0 ] . Prim . Rotation ) ) ;
2008-08-31 00:44:11 +00:00
linkSet [ i ] . Prim . Rotation * = linkSet [ 0 ] . Prim . Rotation ;
}
update . ObjectData [ i ] . ObjectData = SimulationObject . BuildObjectData (
2008-09-14 08:12:11 +00:00
linkSet [ i ] . Prim . Position , linkSet [ i ] . Prim . Rotation ,
Vector3 . Zero , Vector3 . Zero , Vector3 . Zero ) ;
2008-08-31 00:44:11 +00:00
}
2008-10-29 20:11:28 +00:00
server . UDP . BroadcastPacket ( update , PacketCategory . State ) ;
2008-10-12 14:49:23 +00:00
}
2008-08-30 22:48:37 +00:00
2008-08-28 02:55:37 +00:00
void ObjectShapeHandler ( Packet packet , Agent agent )
{
ObjectShapePacket shape = ( ObjectShapePacket ) packet ;
for ( int i = 0 ; i < shape . ObjectData . Length ; i + + )
{
ObjectShapePacket . ObjectDataBlock block = shape . ObjectData [ i ] ;
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( block . ObjectLocalID , out obj ) )
2008-08-28 02:55:37 +00:00
{
2008-09-08 18:23:16 +00:00
Primitive . ConstructionData data = obj . Prim . PrimData ;
data . PathBegin = Primitive . UnpackBeginCut ( block . PathBegin ) ;
data . PathCurve = ( PathCurve ) block . PathCurve ;
data . PathEnd = Primitive . UnpackEndCut ( block . PathEnd ) ;
data . PathRadiusOffset = Primitive . UnpackPathTwist ( block . PathRadiusOffset ) ;
data . PathRevolutions = Primitive . UnpackPathRevolutions ( block . PathRevolutions ) ;
data . PathScaleX = Primitive . UnpackPathScale ( block . PathScaleX ) ;
data . PathScaleY = Primitive . UnpackPathScale ( block . PathScaleY ) ;
data . PathShearX = Primitive . UnpackPathShear ( ( sbyte ) block . PathShearX ) ;
data . PathShearY = Primitive . UnpackPathShear ( ( sbyte ) block . PathShearY ) ;
data . PathSkew = Primitive . UnpackPathTwist ( block . PathSkew ) ;
data . PathTaperX = Primitive . UnpackPathTaper ( block . PathTaperX ) ;
data . PathTaperY = Primitive . UnpackPathTaper ( block . PathTaperY ) ;
data . PathTwist = Primitive . UnpackPathTwist ( block . PathTwist ) ;
data . PathTwistBegin = Primitive . UnpackPathTwist ( block . PathTwistBegin ) ;
data . ProfileBegin = Primitive . UnpackBeginCut ( block . ProfileBegin ) ;
data . profileCurve = block . ProfileCurve ;
data . ProfileEnd = Primitive . UnpackEndCut ( block . ProfileEnd ) ;
data . ProfileHollow = Primitive . UnpackProfileHollow ( block . ProfileHollow ) ;
2008-12-17 03:49:42 +00:00
server . Scene . ObjectModify ( this , obj . Prim . LocalID , data ) ;
2008-08-28 02:55:37 +00:00
}
else
{
Logger . Log ( "Got an ObjectShape packet for unknown object " + block . ObjectLocalID ,
Helpers . LogLevel . Warning ) ;
}
}
}
2008-09-06 18:02:38 +00:00
void ObjectFlagUpdateHandler ( Packet packet , Agent agent )
{
ObjectFlagUpdatePacket update = ( ObjectFlagUpdatePacket ) packet ;
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( update . AgentData . ObjectLocalID , out obj ) )
2008-09-06 18:02:38 +00:00
{
2008-09-08 18:23:16 +00:00
PrimFlags flags = obj . Prim . Flags ;
2008-09-06 18:02:38 +00:00
if ( update . AgentData . CastsShadows )
2008-09-08 18:23:16 +00:00
flags | = PrimFlags . CastShadows ;
2008-09-06 18:02:38 +00:00
else
2008-09-08 18:23:16 +00:00
flags & = ~ PrimFlags . CastShadows ;
2008-09-06 18:02:38 +00:00
if ( update . AgentData . IsPhantom )
2008-09-08 18:23:16 +00:00
flags | = PrimFlags . Phantom ;
2008-09-06 18:02:38 +00:00
else
2008-09-08 18:23:16 +00:00
flags & = ~ PrimFlags . Phantom ;
2008-09-06 18:02:38 +00:00
if ( update . AgentData . IsTemporary )
2008-09-08 18:23:16 +00:00
flags | = PrimFlags . Temporary ;
2008-09-06 18:02:38 +00:00
else
2008-09-08 18:23:16 +00:00
flags & = ~ PrimFlags . Temporary ;
2008-09-06 18:02:38 +00:00
if ( update . AgentData . UsePhysics )
2008-09-08 18:23:16 +00:00
flags | = PrimFlags . Physics ;
2008-09-06 18:02:38 +00:00
else
2008-09-08 18:23:16 +00:00
flags & = ~ PrimFlags . Physics ;
2008-09-06 18:02:38 +00:00
2008-10-29 20:11:28 +00:00
server . Scene . ObjectFlags ( this , obj , flags ) ;
2008-09-06 18:02:38 +00:00
}
else
{
Logger . Log ( "Got an ObjectFlagUpdate packet for unknown object " + update . AgentData . ObjectLocalID ,
Helpers . LogLevel . Warning ) ;
}
}
2008-09-28 21:28:10 +00:00
void ObjectExtraParamsHandler ( Packet packet , Agent agent )
{
ObjectExtraParamsPacket extra = ( ObjectExtraParamsPacket ) packet ;
for ( int i = 0 ; i < extra . ObjectData . Length ; i + + )
{
ObjectExtraParamsPacket . ObjectDataBlock block = extra . ObjectData [ i ] ;
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( block . ObjectLocalID , out obj ) )
2008-09-28 21:28:10 +00:00
{
ExtraParamType type = ( ExtraParamType ) block . ParamType ;
}
}
}
void ObjectImageHandler ( Packet packet , Agent agent )
{
2008-10-03 08:11:32 +00:00
ObjectImagePacket image = ( ObjectImagePacket ) packet ;
for ( int i = 0 ; i < image . ObjectData . Length ; i + + )
{
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( image . ObjectData [ i ] . ObjectLocalID , out obj ) )
server . Scene . ObjectImage ( this , obj ,
2008-10-03 08:11:32 +00:00
Utils . BytesToString ( image . ObjectData [ i ] . MediaURL ) ,
new Primitive . TextureEntry ( image . ObjectData [ i ] . TextureEntry , 0 , image . ObjectData [ i ] . TextureEntry . Length ) ) ;
}
2008-09-28 21:28:10 +00:00
}
2008-08-27 23:27:48 +00:00
void DeRezObjectHandler ( Packet packet , Agent agent )
{
DeRezObjectPacket derez = ( DeRezObjectPacket ) packet ;
DeRezDestination destination = ( DeRezDestination ) derez . AgentBlock . Destination ;
// TODO: Check permissions
2008-08-28 22:35:19 +00:00
for ( int i = 0 ; i < derez . ObjectData . Length ; i + + )
2008-08-27 23:27:48 +00:00
{
2008-08-28 22:35:19 +00:00
uint localID = derez . ObjectData [ i ] . ObjectLocalID ;
2008-08-27 23:27:48 +00:00
2008-08-28 22:35:19 +00:00
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( localID , out obj ) )
2008-08-28 22:35:19 +00:00
{
switch ( destination )
2008-08-27 23:27:48 +00:00
{
2008-08-28 22:35:19 +00:00
case DeRezDestination . AgentInventorySave :
Logger . Log ( "DeRezObject: Got an AgentInventorySave, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . AgentInventoryCopy :
Logger . Log ( "DeRezObject: Got an AgentInventorySave, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . TaskInventory :
Logger . Log ( "DeRezObject: Got a TaskInventory, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . Attachment :
Logger . Log ( "DeRezObject: Got an Attachment, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . AgentInventoryTake :
Logger . Log ( "DeRezObject: Got an AgentInventoryTake, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . ForceToGodInventory :
Logger . Log ( "DeRezObject: Got a ForceToGodInventory, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . TrashFolder :
InventoryObject invObj ;
2008-12-21 02:09:54 +00:00
if ( server . Inventory . TryGetInventory ( agent . Avatar . ID , derez . AgentBlock . DestinationID , out invObj ) & &
2008-10-06 22:34:38 +00:00
invObj is InventoryFolder )
2008-08-28 22:35:19 +00:00
{
// FIXME: Handle children
InventoryFolder trash = ( InventoryFolder ) invObj ;
2008-12-21 02:09:54 +00:00
server . Inventory . CreateItem ( agent . Avatar . ID , obj . Prim . Properties . Name , obj . Prim . Properties . Description , InventoryType . Object ,
AssetType . Object , obj . Prim . ID , trash . ID , PermissionMask . All , PermissionMask . All , agent . Avatar . ID ,
2008-10-09 04:50:03 +00:00
obj . Prim . Properties . CreatorID , derez . AgentBlock . TransactionID , 0 , true ) ;
2008-12-17 03:49:42 +00:00
server . Scene . ObjectRemove ( this , obj . Prim . LocalID ) ;
2008-08-28 22:35:19 +00:00
Logger . DebugLog ( String . Format ( "Derezzed prim {0} to agent inventory trash" , obj . Prim . LocalID ) ) ;
}
else
{
Logger . Log ( "DeRezObject: Got a TrashFolder with an invalid trash folder: " +
2008-08-27 23:27:48 +00:00
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
2008-08-28 22:35:19 +00:00
}
break ;
case DeRezDestination . AttachmentToInventory :
Logger . Log ( "DeRezObject: Got an AttachmentToInventory, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . AttachmentExists :
Logger . Log ( "DeRezObject: Got an AttachmentExists, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . ReturnToOwner :
Logger . Log ( "DeRezObject: Got a ReturnToOwner, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
case DeRezDestination . ReturnToLastOwner :
Logger . Log ( "DeRezObject: Got a ReturnToLastOwner, DestID: " +
derez . AgentBlock . DestinationID . ToString ( ) , Helpers . LogLevel . Warning ) ;
break ;
2008-08-27 23:27:48 +00:00
}
}
}
}
2008-08-28 02:38:32 +00:00
void MultipleObjectUpdateHandler ( Packet packet , Agent agent )
{
MultipleObjectUpdatePacket update = ( MultipleObjectUpdatePacket ) packet ;
for ( int i = 0 ; i < update . ObjectData . Length ; i + + )
{
2008-12-17 03:49:42 +00:00
bool scaled = false ;
2008-08-28 02:38:32 +00:00
MultipleObjectUpdatePacket . ObjectDataBlock block = update . ObjectData [ i ] ;
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( block . ObjectLocalID , out obj ) )
2008-08-28 02:38:32 +00:00
{
UpdateType type = ( UpdateType ) block . Type ;
bool linked = ( ( type & UpdateType . Linked ) ! = 0 ) ;
int pos = 0 ;
2008-09-08 18:23:16 +00:00
Vector3 position = obj . Prim . Position ;
Quaternion rotation = obj . Prim . Rotation ;
Vector3 scale = obj . Prim . Scale ;
2008-08-28 02:38:32 +00:00
if ( ( type & UpdateType . Position ) ! = 0 )
{
2008-09-08 18:23:16 +00:00
position = new Vector3 ( block . Data , pos ) ;
2008-08-28 02:38:32 +00:00
pos + = 12 ;
}
if ( ( type & UpdateType . Rotation ) ! = 0 )
{
2008-09-08 18:23:16 +00:00
rotation = new Quaternion ( block . Data , pos , true ) ;
2008-08-28 02:38:32 +00:00
pos + = 12 ;
}
if ( ( type & UpdateType . Scale ) ! = 0 )
{
2008-12-17 03:49:42 +00:00
scaled = true ;
2008-09-08 18:23:16 +00:00
scale = new Vector3 ( block . Data , pos ) ;
2008-08-28 02:38:32 +00:00
pos + = 12 ;
2008-09-06 18:02:38 +00:00
// FIXME: Use this in linksets
2008-08-28 02:38:32 +00:00
bool uniform = ( ( type & UpdateType . Uniform ) ! = 0 ) ;
}
2008-12-17 03:49:42 +00:00
if ( scaled )
{
obj . Prim . Position = position ;
obj . Prim . Rotation = rotation ;
obj . Prim . Scale = scale ;
server . Scene . ObjectAdd ( this , obj , PrimFlags . None ) ;
}
else
{
server . Scene . ObjectTransform ( this , obj . Prim . LocalID , position , rotation ,
obj . Prim . Velocity , obj . Prim . Acceleration , obj . Prim . AngularVelocity ) ;
}
2008-08-28 02:38:32 +00:00
}
else
{
// Ghosted prim, send a kill message to this agent
// FIXME: Handle children
KillObjectPacket kill = new KillObjectPacket ( ) ;
kill . ObjectData = new KillObjectPacket . ObjectDataBlock [ 1 ] ;
kill . ObjectData [ 0 ] = new KillObjectPacket . ObjectDataBlock ( ) ;
kill . ObjectData [ 0 ] . ID = block . ObjectLocalID ;
2008-12-21 02:09:54 +00:00
server . UDP . SendPacket ( agent . Avatar . ID , kill , PacketCategory . State ) ;
2008-08-28 02:38:32 +00:00
}
}
}
void RequestObjectPropertiesFamilyHandler ( Packet packet , Agent agent )
{
RequestObjectPropertiesFamilyPacket request = ( RequestObjectPropertiesFamilyPacket ) packet ;
ReportType type = ( ReportType ) request . ObjectData . RequestFlags ;
SimulationObject obj ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( request . ObjectData . ObjectID , out obj ) )
2008-08-28 02:38:32 +00:00
{
ObjectPropertiesFamilyPacket props = new ObjectPropertiesFamilyPacket ( ) ;
2008-12-18 21:45:38 +00:00
if ( obj . Prim . Properties ! = null )
{
props . ObjectData . BaseMask = ( uint ) obj . Prim . Properties . Permissions . BaseMask ;
props . ObjectData . Category = ( uint ) obj . Prim . Properties . Category ;
props . ObjectData . Description = Utils . StringToBytes ( obj . Prim . Properties . Description ) ;
props . ObjectData . EveryoneMask = ( uint ) obj . Prim . Properties . Permissions . EveryoneMask ;
props . ObjectData . GroupID = obj . Prim . Properties . GroupID ;
props . ObjectData . GroupMask = ( uint ) obj . Prim . Properties . Permissions . GroupMask ;
props . ObjectData . LastOwnerID = obj . Prim . Properties . LastOwnerID ;
props . ObjectData . Name = Utils . StringToBytes ( obj . Prim . Properties . Name ) ;
props . ObjectData . NextOwnerMask = ( uint ) obj . Prim . Properties . Permissions . NextOwnerMask ;
props . ObjectData . ObjectID = obj . Prim . ID ;
props . ObjectData . OwnerID = obj . Prim . Properties . OwnerID ;
props . ObjectData . OwnerMask = ( uint ) obj . Prim . Properties . Permissions . OwnerMask ;
props . ObjectData . OwnershipCost = obj . Prim . Properties . OwnershipCost ;
props . ObjectData . RequestFlags = ( uint ) type ;
props . ObjectData . SalePrice = obj . Prim . Properties . SalePrice ;
props . ObjectData . SaleType = ( byte ) obj . Prim . Properties . SaleType ;
}
else
{
// Make up some default properties for this prim
props . ObjectData . BaseMask = ( uint ) PermissionMask . All ;
props . ObjectData . Category = ( uint ) ObjectCategory . None ;
props . ObjectData . Description = Utils . StringToBytes ( String . Empty ) ;
props . ObjectData . EveryoneMask = ( uint ) PermissionMask . All ;
props . ObjectData . GroupID = UUID . Zero ;
props . ObjectData . GroupMask = ( uint ) PermissionMask . All ;
props . ObjectData . LastOwnerID = UUID . Zero ;
props . ObjectData . Name = Utils . StringToBytes ( String . Empty ) ;
props . ObjectData . NextOwnerMask = ( uint ) PermissionMask . All ;
props . ObjectData . ObjectID = obj . Prim . ID ;
2008-12-21 02:09:54 +00:00
props . ObjectData . OwnerID = agent . Avatar . ID ;
2008-12-18 21:45:38 +00:00
props . ObjectData . OwnerMask = ( uint ) PermissionMask . All ;
props . ObjectData . OwnershipCost = 0 ;
props . ObjectData . RequestFlags = ( uint ) ReportType . None ;
props . ObjectData . SalePrice = 0 ;
props . ObjectData . SaleType = ( byte ) SaleType . Not ;
}
2008-08-28 02:38:32 +00:00
2008-12-21 02:09:54 +00:00
server . UDP . SendPacket ( agent . Avatar . ID , props , PacketCategory . Transaction ) ;
2008-08-28 02:38:32 +00:00
}
else
{
Logger . Log ( "RequestObjectPropertiesFamily sent for unknown object " +
request . ObjectData . ObjectID . ToString ( ) , Helpers . LogLevel . Warning ) ;
}
}
2008-08-24 05:21:42 +00:00
Vector3 FullSceneCollisionTest ( Vector3 rayStart , Vector3 rayEnd )
{
// HACK: For now
Logger . DebugLog ( "Full scene collision test was requested, ignoring" ) ;
return rayEnd ;
}
Vector3 ObjectCollisionTest ( Vector3 rayStart , Vector3 rayEnd , SimulationObject obj )
{
const float OO_THREE = 1f / 3f ;
Vector3 closestPoint = rayEnd ;
if ( rayStart = = rayEnd )
{
2008-08-28 02:38:32 +00:00
Logger . DebugLog ( "RayStart is equal to RayEnd, returning given location" ) ;
2008-08-24 05:21:42 +00:00
return closestPoint ;
}
Vector3 direction = Vector3 . Normalize ( rayEnd - rayStart ) ;
Ray ray = new Ray ( rayStart , direction ) ;
// Get the mesh that has been transformed into world-space
SimpleMesh mesh = null ;
2008-08-28 02:38:32 +00:00
if ( obj . Prim . ParentID ! = 0 )
2008-08-24 05:21:42 +00:00
{
SimulationObject parent ;
2008-10-29 20:11:28 +00:00
if ( server . Scene . TryGetObject ( obj . Prim . ParentID , out parent ) )
2008-08-24 05:21:42 +00:00
mesh = obj . GetWorldMesh ( DetailLevel . Low , parent ) ;
}
else
{
mesh = obj . GetWorldMesh ( DetailLevel . Low , null ) ;
}
if ( mesh ! = null )
{
// Iterate through all of the triangles in the mesh, doing a ray-triangle intersection
float closestDistance = Single . MaxValue ;
for ( int i = 0 ; i < mesh . Indices . Count ; i + = 3 )
{
2008-08-28 02:38:32 +00:00
Vector3 point0 = mesh . Vertices [ mesh . Indices [ i + 0 ] ] . Position ;
Vector3 point1 = mesh . Vertices [ mesh . Indices [ i + 1 ] ] . Position ;
Vector3 point2 = mesh . Vertices [ mesh . Indices [ i + 2 ] ] . Position ;
if ( RayTriangleIntersection ( rayStart , direction , point0 , point1 , point2 ) )
2008-08-24 05:21:42 +00:00
{
2008-08-28 02:38:32 +00:00
// HACK: Find the barycenter of this triangle. Would be better to have
// RayTriangleIntersection return the exact collision point
Vector3 center = ( point0 + point1 + point2 ) * OO_THREE ;
2008-08-24 05:21:42 +00:00
Logger . DebugLog ( "Collision hit with triangle at " + center ) ;
if ( ( center - rayStart ) . Length ( ) < closestDistance )
closestPoint = center ;
}
}
}
return closestPoint ;
}
2008-08-24 22:42:14 +00:00
/// <summary>
/// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
/// </summary>
2008-08-27 23:27:48 +00:00
/// <param name="origin">Origin point of the ray</param>
/// <param name="direction">Unit vector representing the direction of the ray</param>
/// <param name="vert0">Position of the first triangle corner</param>
/// <param name="vert1">Position of the second triangle corner</param>
/// <param name="vert2">Position of the third triangle corner</param>
/// <returns>True if the ray passes through the triangle, otherwise false</returns>
2008-08-24 05:21:42 +00:00
bool RayTriangleIntersection ( Vector3 origin , Vector3 direction , Vector3 vert0 , Vector3 vert1 , Vector3 vert2 )
{
2008-08-24 22:42:14 +00:00
const float EPSILON = 0.00001f ;
Vector3 edge1 , edge2 , pvec ;
float determinant , invDeterminant ;
// Find vectors for two edges sharing vert0
edge1 = vert1 - vert0 ;
edge2 = vert2 - vert0 ;
// Begin calculating the determinant
pvec = Vector3 . Cross ( direction , edge2 ) ;
// If the determinant is near zero, ray lies in plane of triangle
determinant = Vector3 . Dot ( edge1 , pvec ) ;
if ( determinant > - EPSILON & & determinant < EPSILON )
return false ;
invDeterminant = 1f / determinant ;
2008-08-24 05:21:42 +00:00
2008-08-24 22:42:14 +00:00
// Calculate distance from vert0 to ray origin
Vector3 tvec = origin - vert0 ;
// Calculate U parameter and test bounds
float u = Vector3 . Dot ( tvec , pvec ) * invDeterminant ;
if ( u < 0.0f | | u > 1.0f )
return false ;
// Prepare to test V parameter
Vector3 qvec = Vector3 . Cross ( tvec , edge1 ) ;
// Calculate V parameter and test bounds
float v = Vector3 . Dot ( direction , qvec ) * invDeterminant ;
if ( v < 0.0f | | u + v > 1.0f )
return false ;
//t = Vector3.Dot(edge2, qvec) * invDeterminant;
return true ;
}
2008-08-30 22:48:37 +00:00
2008-08-24 05:21:42 +00:00
}
}