From d9e45332022b18b50f00353588afd4bbe3392870 Mon Sep 17 00:00:00 2001 From: Teravus Ovares Date: Sun, 13 Jan 2008 07:14:54 +0000 Subject: * Fixed an overflow in the land manager * Did some goofy math undoing in the Sim Stats Reporter * More reduction to the amount of calls per second to UnManaged ODE code * Added a significant amount of comments to ODE --- OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | 220 ++++++++++++++++++++++---- 1 file changed, 188 insertions(+), 32 deletions(-) (limited to 'OpenSim/Region/Physics/OdePlugin/OdePlugin.cs') diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index 17dbd0a..366f30b 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs @@ -86,6 +86,7 @@ namespace OpenSim.Region.Physics.OdePlugin private IntPtr contactgroup; private IntPtr LandGeom = (IntPtr) 0; private double[] _heightmap; + private float[] _origheightmap; private d.NearCallback nearCallback; public d.TriCallback triCallback; public d.TriArrayCallback triArrayCallback; @@ -115,6 +116,12 @@ namespace OpenSim.Region.Physics.OdePlugin public IMesher mesher; + + /// + /// Initiailizes the scene + /// Sets many properties that ODE requires to be stable + /// These settings need to be tweaked 'exactly' right or weird stuff happens. + /// public OdeScene() { nearCallback = near; @@ -128,17 +135,27 @@ namespace OpenSim.Region.Physics.OdePlugin contact.surface.soft_cfm = 0.00003f; */ + // Centeral contact friction and bounce contact.surface.mu = 250.0f; contact.surface.bounce = 0.2f; + // Terrain contact friction and Bounce + // This is the *non* moving version. Use this when an avatar + // isn't moving to keep it in place better TerrainContact.surface.mode |= d.ContactFlags.SoftERP; TerrainContact.surface.mu = 550.0f; TerrainContact.surface.bounce = 0.1f; TerrainContact.surface.soft_erp = 0.1025f; + // Prim contact friction and bounce + // THis is the *non* moving version of friction and bounce + // Use this when an avatar comes in contact with a prim + // and is moving AvatarMovementprimContact.surface.mu = 150.0f; AvatarMovementprimContact.surface.bounce = 0.1f; + // Terrain contact friction bounce and various error correcting calculations + // Use this when an avatar is in contact with the terrain and moving. AvatarMovementTerrainContact.surface.mode |= d.ContactFlags.SoftERP; AvatarMovementTerrainContact.surface.mu = 150.0f; AvatarMovementTerrainContact.surface.bounce = 0.1f; @@ -146,6 +163,7 @@ namespace OpenSim.Region.Physics.OdePlugin lock (OdeLock) { + // Creat the world and the first space world = d.WorldCreate(); space = d.HashSpaceCreate(IntPtr.Zero); d.HashSpaceSetLevels(space, -4, 128); @@ -153,15 +171,25 @@ namespace OpenSim.Region.Physics.OdePlugin //contactgroup + // Set the gravity,, don't disable things automatically (we set it explicitly on some things) + d.WorldSetGravity(world, 0.0f, 0.0f, -10.0f); d.WorldSetAutoDisableFlag(world, false); d.WorldSetContactSurfaceLayer(world, 0.001f); + + // Set how many steps we go without running collision testing + // This is in addition to the step size. + // Essentially Steps * m_physicsiterations d.WorldSetQuickStepNumIterations(world, m_physicsiterations); - d.WorldSetContactMaxCorrectingVel(world, 1000.0f); + ///d.WorldSetContactMaxCorrectingVel(world, 1000.0f); } + // zero out a heightmap array float array (single dimention [flattened])) _heightmap = new double[514*514]; + + // Zero out the prim spaces array (we split our space into smaller spaces so + // we can hit test less. for (int i = 0; i < staticPrimspace.GetLength(0); i++) { for (int j = 0; j < staticPrimspace.GetLength(1); j++) @@ -171,19 +199,35 @@ namespace OpenSim.Region.Physics.OdePlugin } } + // Initialize the mesh plugin public override void Initialise(IMesher meshmerizer) { mesher = meshmerizer; } + /// + /// Debug space message for printing the space that a prim/avatar is in. + /// + /// + /// Returns which split up space the given position is in. public string whichspaceamIin(PhysicsVector pos) { return calculateSpaceForGeom(pos).ToString(); } + /// + /// This is our near callback. A geometry is near a body + /// + /// The space that contains the geoms. Remember, spaces are also geoms + /// a geometry or space + /// another geometry or space private void near(IntPtr space, IntPtr g1, IntPtr g2) { // no lock here! It's invoked from within Simulate(), which is thread-locked + + // Test if we're collidng a geom with a space. + // If so we have to drill down into the space recursively + if (d.GeomIsSpace(g1) || d.GeomIsSpace(g2)) { // Separating static prim geometry spaces. @@ -192,7 +236,7 @@ namespace OpenSim.Region.Physics.OdePlugin // contact points in the space d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback); - //Colliding a space or a geom with a space or a geom. + //Colliding a space or a geom with a space or a geom. so drill down //Collide all geoms in each space.. //if (d.GeomIsSpace(g1)) d.SpaceCollide(g1, IntPtr.Zero, nearCallback); @@ -226,12 +270,13 @@ namespace OpenSim.Region.Physics.OdePlugin name2 = "null"; } - if (id == d.GeomClassID.TriMeshClass) - { + //if (id == d.GeomClassID.TriMeshClass) + //{ // MainLog.Instance.Verbose("near: A collision was detected between {1} and {2}", 0, name1, name2); //System.Console.WriteLine("near: A collision was detected between {1} and {2}", 0, name1, name2); - } + //} + // Figure out how many contact points we have int count = 0; try { @@ -244,13 +289,15 @@ namespace OpenSim.Region.Physics.OdePlugin base.TriggerPhysicsBasedRestart(); } + PhysicsActor p1; + PhysicsActor p2; + for (int i = 0; i < count; i++) { IntPtr joint; // If we're colliding with terrain, use 'TerrainContact' instead of contact. // allows us to have different settings - PhysicsActor p1; - PhysicsActor p2; + if (!actor_name_map.TryGetValue(g1, out p1)) { @@ -267,14 +314,14 @@ namespace OpenSim.Region.Physics.OdePlugin switch (p1.PhysicsActorType) { - case (int) ActorTypes.Agent: + case (int)ActorTypes.Agent: p2.CollidingObj = true; break; - case (int) ActorTypes.Prim: + case (int)ActorTypes.Prim: if (p2.Velocity.X > 0 || p2.Velocity.Y > 0 || p2.Velocity.Z > 0) p2.CollidingObj = true; break; - case (int) ActorTypes.Unknown: + case (int)ActorTypes.Unknown: p2.CollidingGround = true; break; default: @@ -288,11 +335,15 @@ namespace OpenSim.Region.Physics.OdePlugin if (contacts[i].depth >= 0.08f) { + /* This is disabled at the moment only because it needs more tweaking + It will eventually be uncommented + if (contacts[i].depth >= 1.00f) { //MainLog.Instance.Debug("PHYSICS",contacts[i].depth.ToString()); } - // If you interpenetrate a prim with an agent + + //If you interpenetrate a prim with an agent if ((p2.PhysicsActorType == (int) ActorTypes.Agent && p1.PhysicsActorType == (int) ActorTypes.Prim) || (p1.PhysicsActorType == (int) ActorTypes.Agent && @@ -300,35 +351,37 @@ namespace OpenSim.Region.Physics.OdePlugin { if (p2.PhysicsActorType == (int) ActorTypes.Agent) { - //p2.CollidingObj = true; - //contacts[i].depth = 0.003f; - //p2.Velocity = p2.Velocity + new PhysicsVector(0, 0, 2.5f); - //OdeCharacter character = (OdeCharacter) p2; - //character.SetPidStatus(true); - //contacts[i].pos = new d.Vector3(contacts[i].pos.X + (p1.Size.X / 2), contacts[i].pos.Y + (p1.Size.Y / 2), contacts[i].pos.Z + (p1.Size.Z / 2)); + p2.CollidingObj = true; + contacts[i].depth = 0.003f; + p2.Velocity = p2.Velocity + new PhysicsVector(0, 0, 2.5f); + OdeCharacter character = (OdeCharacter) p2; + character.SetPidStatus(true); + contacts[i].pos = new d.Vector3(contacts[i].pos.X + (p1.Size.X / 2), contacts[i].pos.Y + (p1.Size.Y / 2), contacts[i].pos.Z + (p1.Size.Z / 2)); } else { - //contacts[i].depth = 0.0000000f; + contacts[i].depth = 0.0000000f; } if (p1.PhysicsActorType == (int) ActorTypes.Agent) { - //p1.CollidingObj = true; - //contacts[i].depth = 0.003f; - //p1.Velocity = p1.Velocity + new PhysicsVector(0, 0, 2.5f); - //contacts[i].pos = new d.Vector3(contacts[i].pos.X + (p2.Size.X / 2), contacts[i].pos.Y + (p2.Size.Y / 2), contacts[i].pos.Z + (p2.Size.Z / 2)); - //OdeCharacter character = (OdeCharacter)p1; - //character.SetPidStatus(true); + p1.CollidingObj = true; + contacts[i].depth = 0.003f; + p1.Velocity = p1.Velocity + new PhysicsVector(0, 0, 2.5f); + contacts[i].pos = new d.Vector3(contacts[i].pos.X + (p2.Size.X / 2), contacts[i].pos.Y + (p2.Size.Y / 2), contacts[i].pos.Z + (p2.Size.Z / 2)); + OdeCharacter character = (OdeCharacter)p1; + character.SetPidStatus(true); } else { - //contacts[i].depth = 0.0000000f; + contacts[i].depth = 0.0000000f; } } + */ + // If you interpenetrate a prim with another prim if (p1.PhysicsActorType == (int) ActorTypes.Prim && p2.PhysicsActorType == (int) ActorTypes.Prim) { @@ -337,6 +390,7 @@ namespace OpenSim.Region.Physics.OdePlugin } if (contacts[i].depth >= 1.00f) { + OpenSim.Framework.Console.MainLog.Instance.Verbose("P", contacts[i].depth.ToString()); if ((p2.PhysicsActorType == (int) ActorTypes.Agent && p1.PhysicsActorType == (int) ActorTypes.Unknown) || (p1.PhysicsActorType == (int) ActorTypes.Agent && @@ -347,7 +401,7 @@ namespace OpenSim.Region.Physics.OdePlugin OdeCharacter character = (OdeCharacter) p2; //p2.CollidingObj = true; - contacts[i].depth = 0.003f; + contacts[i].depth = 0.00000003f; p2.Velocity = p2.Velocity + new PhysicsVector(0, 0, 0.5f); contacts[i].pos = new d.Vector3(contacts[i].pos.X + (p1.Size.X/2), @@ -363,7 +417,7 @@ namespace OpenSim.Region.Physics.OdePlugin OdeCharacter character = (OdeCharacter)p1; //p2.CollidingObj = true; - contacts[i].depth = 0.003f; + contacts[i].depth = 0.00000003f; p1.Velocity = p1.Velocity + new PhysicsVector(0, 0, 0.5f); contacts[i].pos = new d.Vector3(contacts[i].pos.X + (p1.Size.X/2), @@ -383,30 +437,39 @@ namespace OpenSim.Region.Physics.OdePlugin if (contacts[i].depth >= 0f) { + // If we're collidng against terrain if (name1 == "Terrain" || name2 == "Terrain") { + // If we're moving if ((p2.PhysicsActorType == (int) ActorTypes.Agent) && (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) { + // Use the movement terrain contact AvatarMovementTerrainContact.geom = contacts[i]; joint = d.JointCreateContact(world, contactgroup, ref AvatarMovementTerrainContact); } else { + // Use the non moving terrain contact TerrainContact.geom = contacts[i]; joint = d.JointCreateContact(world, contactgroup, ref TerrainContact); } } else { + // we're colliding with prim or avatar + + // check if we're moving if ((p2.PhysicsActorType == (int) ActorTypes.Agent) && (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) { + // Use the Movement prim contact AvatarMovementprimContact.geom = contacts[i]; joint = d.JointCreateContact(world, contactgroup, ref AvatarMovementprimContact); } else { + // Use the non movement contact contact.geom = contacts[i]; joint = d.JointCreateContact(world, contactgroup, ref contact); } @@ -416,6 +479,11 @@ namespace OpenSim.Region.Physics.OdePlugin if (count > 3) { + // If there are more then 3 contact points, it's likely + // that we've got a pile of objects + // + // We don't want to send out hundreds of terse updates over and over again + // so lets throttle them and send them again after it's somewhat sorted out. p2.ThrottleUpdates = true; } //System.Console.WriteLine(count.ToString()); @@ -423,16 +491,43 @@ namespace OpenSim.Region.Physics.OdePlugin } } } + private float GetTerrainHeightAtXY(float x, float y) + { + return (float)_origheightmap[(int) y*256 + (int) x]; + + } + + /// + /// This is our collision testing routine in ODE + /// + /// private void collision_optimized(float timeStep) { foreach (OdeCharacter chr in _characters) { + // Reset the collision values to false + // since we don't know if we're colliding yet + chr.IsColliding = false; chr.CollidingGround = false; chr.CollidingObj = false; + + // test the avatar's geometry for collision with the space + // This will return near and the space that they are the closest to + // And we'll run this again against the avatar and the space segment + // This will return with a bunch of possible objects in the space segment + // and we'll run it again on all of them. + d.SpaceCollide2(space, chr.Shell, IntPtr.Zero, nearCallback); + //float terrainheight = GetTerrainHeightAtXY(chr.Position.X, chr.Position.Y); + //if (chr.Position.Z + (chr.Velocity.Z * timeStep) < terrainheight + 10) + //{ + //chr.Position.Z = terrainheight + 10.0f; + //forcedZ = true; + //} } + // If the sim is running slow this frame, // don't process collision for prim! if (timeStep < (m_SkipFramesAtms/3)) @@ -469,6 +564,8 @@ namespace OpenSim.Region.Physics.OdePlugin // This if may not need to be there.. it might be skipped anyway. if (d.BodyIsEnabled(chr.Body)) { + // Collide test the prims with the terrain.. since if you don't do this, + // next frame, all of the physical prim in the scene will awaken and explode upwards d.SpaceCollide2(LandGeom, chr.prim_geom, IntPtr.Zero, nearCallback); } } @@ -509,6 +606,15 @@ namespace OpenSim.Region.Physics.OdePlugin } } + /// + /// This is called from within simulate but outside the locked portion + /// We need to do our own locking here + /// Essentially, we need to remove the prim from our space segment, whatever segment it's in. + /// + /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory + /// that the space was using. + /// + /// public void RemovePrimThreadLocked(OdePrim prim) { lock (OdeLock) @@ -567,7 +673,10 @@ namespace OpenSim.Region.Physics.OdePlugin _prims.Remove(prim); } } - + /// + /// Takes a space pointer and zeros out the array we're using to hold the spaces + /// + /// public void resetSpaceArrayItemToZero(IntPtr space) { for (int x = 0; x < staticPrimspace.GetLength(0); x++) @@ -585,15 +694,25 @@ namespace OpenSim.Region.Physics.OdePlugin staticPrimspace[arrayitemX, arrayitemY] = IntPtr.Zero; } + /// + /// Called when a static prim moves. Allocates a space for the prim based on it's position + /// + /// the pointer to the geom that moved + /// the position that the geom moved to + /// a pointer to the space it was in before it was moved. + /// a pointer to the new space it's in public IntPtr recalculateSpaceForGeom(IntPtr geom, PhysicsVector pos, IntPtr currentspace) { - //Todo recalculate space the prim is in. + // Called from setting the Position and Size of an ODEPrim so // it's already in locked space. // we don't want to remove the main space // we don't need to test physical here because this function should // never be called if the prim is physical(active) + + // All physical prim end up in the root space + if (currentspace != space) { if (d.SpaceQuery(currentspace, geom) && currentspace != (IntPtr) 0) @@ -707,6 +826,12 @@ namespace OpenSim.Region.Physics.OdePlugin return newspace; } + /// + /// Creates a new space at X Y + /// + /// + /// + /// A pointer to the created space public IntPtr createprimspace(int iprimspaceArrItemX, int iprimspaceArrItemY) { // creating a new space for prim and inserting it into main space. @@ -715,6 +840,11 @@ namespace OpenSim.Region.Physics.OdePlugin return staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY]; } + /// + /// Calculates the space the prim should be in by it's position + /// + /// + /// a pointer to the space. This could be a new space or reused space. public IntPtr calculateSpaceForGeom(PhysicsVector pos) { int[] xyspace = calculateSpaceArrayItemFromPos(pos); @@ -725,6 +855,11 @@ namespace OpenSim.Region.Physics.OdePlugin return locationbasedspace; } + /// + /// Holds the space allocation logic + /// + /// + /// an array item based on the position public int[] calculateSpaceArrayItemFromPos(PhysicsVector pos) { int[] returnint = new int[2]; @@ -837,7 +972,11 @@ namespace OpenSim.Region.Physics.OdePlugin return 1; } - + /// + /// Routine to figure out if we need to mesh this prim with our mesher + /// + /// + /// public bool needsMeshing(PrimitiveBaseShape pbs) { if (pbs.ProfileHollow != 0) @@ -879,6 +1018,12 @@ namespace OpenSim.Region.Physics.OdePlugin return result; } + /// + /// Called after our prim properties are set Scale, position etc. + /// We use this event queue like method to keep changes to the physical scene occuring in the threadlocked mutex + /// This assures us that we have no race conditions + /// + /// public override void AddPhysicsActorTaint(PhysicsActor prim) { if (prim is OdePrim) @@ -889,6 +1034,15 @@ namespace OpenSim.Region.Physics.OdePlugin } } + /// + /// This is our main simulate loop + /// It's thread locked by a Mutex in the scene. + /// It holds Collisions, it instructs ODE to step through the physical reactions + /// It moves the objects around in memory + /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup) + /// + /// + /// public override float Simulate(float timeStep) { float fps = 0; @@ -904,7 +1058,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (step_time >= m_SkipFramesAtms) { - // Instead of trying to catch up, it'll do one physics frame only + // Instead of trying to catch up, it'll do 5 physics frames only step_time = ODE_STEPSIZE; m_physicsiterations = 5; } @@ -960,6 +1114,7 @@ namespace OpenSim.Region.Physics.OdePlugin { actor.UpdatePositionAndVelocity(); } + bool processedtaints = false; foreach (OdePrim prim in _taintedPrim) { @@ -970,6 +1125,7 @@ namespace OpenSim.Region.Physics.OdePlugin } processedtaints = true; } + if (processedtaints) _taintedPrim = new List(); @@ -1152,7 +1308,7 @@ namespace OpenSim.Region.Physics.OdePlugin // this._heightmap[i] = (double)heightMap[i]; // dbm (danx0r) -- heightmap x,y must be swapped for Ode (should fix ODE, but for now...) // also, creating a buffer zone of one extra sample all around - + _origheightmap = heightMap; const uint heightmapWidth = m_regionWidth + 2; const uint heightmapHeight = m_regionHeight + 2; const uint heightmapWidthSamples = 2*m_regionWidth + 2; -- cgit v1.1