move terrain geom to own ode space. Limit range on raycast if includes

terrain until ode doesn't eat all stack. Add a pre-simulation method to do
pending actors changes (except mesh assets still not ready to use), to be
optionaly called before firing heartbeat. [UNTESTED]
avinationmerge
UbitUmarov 2012-10-09 00:18:39 +01:00
parent a1fcfe8677
commit 3bf7201fd4
4 changed files with 235 additions and 89 deletions

View File

@ -246,6 +246,9 @@ namespace OpenSim.Region.Physics.Manager
public abstract void AddPhysicsActorTaint(PhysicsActor prim); public abstract void AddPhysicsActorTaint(PhysicsActor prim);
public virtual void PrepareSimulation() { }
/// <summary> /// <summary>
/// Perform a simulation of the current physics scene over the given timestep. /// Perform a simulation of the current physics scene over the given timestep.
/// </summary> /// </summary>

View File

@ -1325,12 +1325,48 @@ namespace OpenSim.Region.Physics.OdePlugin
} }
} }
private void SetGeom(IntPtr geom)
{
prim_geom = geom;
//Console.WriteLine("SetGeom to " + prim_geom + " for " + Name);
if (prim_geom != IntPtr.Zero)
{
if (m_NoColide)
{
d.GeomSetCategoryBits(prim_geom, 0);
if (m_isphysical)
{
d.GeomSetCollideBits(prim_geom, (uint)CollisionCategories.Land);
}
else
{
d.GeomSetCollideBits(prim_geom, 0);
d.GeomDisable(prim_geom);
}
}
else
{
d.GeomSetCategoryBits(prim_geom, (uint)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (uint)m_collisionFlags);
}
UpdatePrimBodyData();
_parent_scene.actor_name_map[prim_geom] = this;
}
else
m_log.Warn("Setting bad Geom");
}
private bool GetMeshGeom() private bool GetMeshGeom()
{ {
IntPtr vertices, indices; IntPtr vertices, indices;
int vertexCount, indexCount; int vertexCount, indexCount;
int vertexStride, triStride; int vertexStride, triStride;
IMesh mesh = m_mesh; IMesh mesh = m_mesh;
if (mesh == null) if (mesh == null)
@ -1356,6 +1392,9 @@ namespace OpenSim.Region.Physics.OdePlugin
m_mesh = null; m_mesh = null;
return false; return false;
} }
IntPtr geo = IntPtr.Zero;
try try
{ {
_triMeshData = d.GeomTriMeshDataCreate(); _triMeshData = d.GeomTriMeshDataCreate();
@ -1363,7 +1402,7 @@ namespace OpenSim.Region.Physics.OdePlugin
d.GeomTriMeshDataBuildSimple(_triMeshData, vertices, vertexStride, vertexCount, indices, indexCount, triStride); d.GeomTriMeshDataBuildSimple(_triMeshData, vertices, vertexStride, vertexCount, indices, indexCount, triStride);
d.GeomTriMeshDataPreprocess(_triMeshData); d.GeomTriMeshDataPreprocess(_triMeshData);
prim_geom = d.CreateTriMesh(m_targetSpace, _triMeshData, null, null, null); geo = d.CreateTriMesh(m_targetSpace, _triMeshData, null, null, null);
} }
catch (Exception e) catch (Exception e)
@ -1380,15 +1419,15 @@ namespace OpenSim.Region.Physics.OdePlugin
} }
} }
_triMeshData = IntPtr.Zero; _triMeshData = IntPtr.Zero;
prim_geom = IntPtr.Zero;
m_hasOBB = false; m_hasOBB = false;
m_OBBOffset = Vector3.Zero; m_OBBOffset = Vector3.Zero;
m_OBB = _size * 0.5f; m_OBB = _size * 0.5f;
m_physCost = 0.1f; m_physCost = 0.1f;
m_streamCost = 1.0f; m_streamCost = 1.0f;
_parent_scene.mesher.ReleaseMesh(mesh); _parent_scene.mesher.ReleaseMesh(mesh);
m_meshState = MeshState.AssetFailed; m_meshState = MeshState.MeshFailed;
m_mesh = null; m_mesh = null;
return false; return false;
} }
@ -1397,12 +1436,13 @@ namespace OpenSim.Region.Physics.OdePlugin
// todo // todo
m_streamCost = 1.0f; m_streamCost = 1.0f;
SetGeom(geo);
return true; return true;
} }
private void CreateGeom() private void CreateGeom()
{ {
IntPtr geo = IntPtr.Zero;
bool hasMesh = false; bool hasMesh = false;
m_NoColide = false; m_NoColide = false;
@ -1418,8 +1458,11 @@ namespace OpenSim.Region.Physics.OdePlugin
m_NoColide = true; m_NoColide = true;
} }
if (!hasMesh) if (!hasMesh)
{ {
IntPtr geo = IntPtr.Zero;
if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1 if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1
&& _size.X == _size.Y && _size.Y == _size.Z) && _size.X == _size.Y && _size.Y == _size.Z)
{ // it's a sphere { // it's a sphere
@ -1447,7 +1490,7 @@ namespace OpenSim.Region.Physics.OdePlugin
} }
m_physCost = 0.1f; m_physCost = 0.1f;
m_streamCost = 1.0f; m_streamCost = 1.0f;
prim_geom = geo; SetGeom(geo);
} }
} }
@ -2740,8 +2783,6 @@ namespace OpenSim.Region.Physics.OdePlugin
{ {
} }
private void changeAddPhysRep(ODEPhysRepData repData) private void changeAddPhysRep(ODEPhysRepData repData)
{ {
_size = repData.size; //?? _size = repData.size; //??
@ -2763,10 +2804,6 @@ namespace OpenSim.Region.Physics.OdePlugin
if (prim_geom != IntPtr.Zero) if (prim_geom != IntPtr.Zero)
{ {
UpdatePrimBodyData();
_parent_scene.actor_name_map[prim_geom] = this;
d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
d.Quaternion myrot = new d.Quaternion(); d.Quaternion myrot = new d.Quaternion();
myrot.X = _orientation.X; myrot.X = _orientation.X;
@ -2774,23 +2811,23 @@ namespace OpenSim.Region.Physics.OdePlugin
myrot.Z = _orientation.Z; myrot.Z = _orientation.Z;
myrot.W = _orientation.W; myrot.W = _orientation.W;
d.GeomSetQuaternion(prim_geom, ref myrot); d.GeomSetQuaternion(prim_geom, ref myrot);
}
if (!m_isphysical) if (!m_isphysical)
{ {
SetInStaticSpace(this); SetInStaticSpace(this);
UpdateCollisionCatFlags(); UpdateCollisionCatFlags();
ApplyCollisionCatFlags(); ApplyCollisionCatFlags();
} }
else else
MakeBody(); MakeBody();
if ((m_meshState & MeshState.NeedMask) != 0) if ((m_meshState & MeshState.NeedMask) != 0)
{ {
repData.size = _size; repData.size = _size;
repData.pbs = _pbs; repData.pbs = _pbs;
repData.shapetype = m_shapetype; repData.shapetype = m_shapetype;
_parent_scene.m_meshWorker.RequestMesh(repData); _parent_scene.m_meshWorker.RequestMesh(repData);
}
} }
} }
@ -2831,14 +2868,10 @@ namespace OpenSim.Region.Physics.OdePlugin
primVolume = repData.volume; primVolume = repData.volume;
CreateGeom(); CreateGeom();
if (prim_geom != IntPtr.Zero) if (prim_geom != IntPtr.Zero)
{ {
UpdatePrimBodyData();
_parent_scene.actor_name_map[prim_geom] = this;
d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
d.Quaternion myrot = new d.Quaternion(); d.Quaternion myrot = new d.Quaternion();
myrot.X = _orientation.X; myrot.X = _orientation.X;
@ -2846,30 +2879,29 @@ namespace OpenSim.Region.Physics.OdePlugin
myrot.Z = _orientation.Z; myrot.Z = _orientation.Z;
myrot.W = _orientation.W; myrot.W = _orientation.W;
d.GeomSetQuaternion(prim_geom, ref myrot); d.GeomSetQuaternion(prim_geom, ref myrot);
if (m_isphysical)
{
if (chp)
{
if (parent != null)
{
parent.MakeBody();
}
}
else
MakeBody();
}
else
{
SetInStaticSpace(this);
UpdateCollisionCatFlags();
ApplyCollisionCatFlags();
}
resetCollisionAccounting();
} }
if (m_isphysical)
{
if (chp)
{
if (parent != null)
{
parent.MakeBody();
}
}
else
MakeBody();
}
else
{
SetInStaticSpace(this);
UpdateCollisionCatFlags();
ApplyCollisionCatFlags();
}
resetCollisionAccounting();
if ((m_meshState & MeshState.NeedMask) != 0) if ((m_meshState & MeshState.NeedMask) != 0)
{ {
repData.size = _size; repData.size = _size;

View File

@ -129,7 +129,7 @@ namespace OpenSim.Region.Physics.OdePlugin
req.length = length; req.length = length;
req.Normal = direction; req.Normal = direction;
req.Origin = position; req.Origin = position;
req.filter = RayFilterFlags.AllPrims; req.filter = RayFilterFlags.AllPrims | RayFilterFlags.land;
m_PendingRequests.Enqueue(req); m_PendingRequests.Enqueue(req);
} }
@ -261,6 +261,12 @@ namespace OpenSim.Region.Physics.OdePlugin
closestHit = ((CurrentRayFilter & RayFilterFlags.ClosestHit) == 0 ? 0 : 1); closestHit = ((CurrentRayFilter & RayFilterFlags.ClosestHit) == 0 ? 0 : 1);
backfacecull = ((CurrentRayFilter & RayFilterFlags.BackFaceCull) == 0 ? 0 : 1); backfacecull = ((CurrentRayFilter & RayFilterFlags.BackFaceCull) == 0 ? 0 : 1);
// current ode land to ray collisions is very bad
// so for now limit its range badly
if (req.length > 30.0f && (CurrentRayFilter & RayFilterFlags.land) != 0)
req.length = 30.0f;
d.GeomRaySetLength(ray, req.length); d.GeomRaySetLength(ray, req.length);
d.GeomRaySet(ray, req.Origin.X, req.Origin.Y, req.Origin.Z, req.Normal.X, req.Normal.Y, req.Normal.Z); d.GeomRaySet(ray, req.Origin.X, req.Origin.Y, req.Origin.Z, req.Normal.X, req.Normal.Y, req.Normal.Z);
d.GeomRaySetParams(ray, 0, backfacecull); d.GeomRaySetParams(ray, 0, backfacecull);
@ -288,7 +294,10 @@ namespace OpenSim.Region.Physics.OdePlugin
catflags |= CollisionCategories.Water; catflags |= CollisionCategories.Water;
if (catflags != 0) if (catflags != 0)
{
d.GeomSetCollideBits(ray, (uint)catflags);
doSpaceRay(req); doSpaceRay(req);
}
} }
else else
{ {
@ -314,7 +323,8 @@ namespace OpenSim.Region.Physics.OdePlugin
/// ///
private const RayFilterFlags FilterActiveSpace = RayFilterFlags.agent | RayFilterFlags.physical | RayFilterFlags.LSLPhanton; private const RayFilterFlags FilterActiveSpace = RayFilterFlags.agent | RayFilterFlags.physical | RayFilterFlags.LSLPhanton;
private const RayFilterFlags FilterStaticSpace = RayFilterFlags.water | RayFilterFlags.land | RayFilterFlags.nonphysical | RayFilterFlags.LSLPhanton; // private const RayFilterFlags FilterStaticSpace = RayFilterFlags.water | RayFilterFlags.land | RayFilterFlags.nonphysical | RayFilterFlags.LSLPhanton;
private const RayFilterFlags FilterStaticSpace = RayFilterFlags.water | RayFilterFlags.nonphysical | RayFilterFlags.LSLPhanton;
private void doSpaceRay(ODERayRequest req) private void doSpaceRay(ODERayRequest req)
{ {
@ -323,6 +333,8 @@ namespace OpenSim.Region.Physics.OdePlugin
d.SpaceCollide2(ray, m_scene.ActiveSpace, IntPtr.Zero, nearCallback); d.SpaceCollide2(ray, m_scene.ActiveSpace, IntPtr.Zero, nearCallback);
if ((CurrentRayFilter & FilterStaticSpace) != 0 && (m_contactResults.Count < CurrentMaxCount)) if ((CurrentRayFilter & FilterStaticSpace) != 0 && (m_contactResults.Count < CurrentMaxCount))
d.SpaceCollide2(ray, m_scene.StaticSpace, IntPtr.Zero, nearCallback); d.SpaceCollide2(ray, m_scene.StaticSpace, IntPtr.Zero, nearCallback);
if ((CurrentRayFilter & RayFilterFlags.land) != 0 && (m_contactResults.Count < CurrentMaxCount))
d.SpaceCollide2(ray, m_scene.GroundSpace, IntPtr.Zero, nearCallback);
if (req.callbackMethod is RaycastCallback) if (req.callbackMethod is RaycastCallback)
{ {

View File

@ -300,6 +300,7 @@ namespace OpenSim.Region.Physics.OdePlugin
public IntPtr TopSpace; // the global space public IntPtr TopSpace; // the global space
public IntPtr ActiveSpace; // space for active prims public IntPtr ActiveSpace; // space for active prims
public IntPtr StaticSpace; // space for the static things around public IntPtr StaticSpace; // space for the static things around
public IntPtr GroundSpace; // space for ground
// some speedup variables // some speedup variables
private int spaceGridMaxX; private int spaceGridMaxX;
@ -372,6 +373,7 @@ namespace OpenSim.Region.Physics.OdePlugin
// now the major subspaces // now the major subspaces
ActiveSpace = d.HashSpaceCreate(TopSpace); ActiveSpace = d.HashSpaceCreate(TopSpace);
StaticSpace = d.HashSpaceCreate(TopSpace); StaticSpace = d.HashSpaceCreate(TopSpace);
GroundSpace = d.HashSpaceCreate(TopSpace);
} }
catch catch
{ {
@ -381,10 +383,12 @@ namespace OpenSim.Region.Physics.OdePlugin
d.HashSpaceSetLevels(TopSpace, -2, 8); d.HashSpaceSetLevels(TopSpace, -2, 8);
d.HashSpaceSetLevels(ActiveSpace, -2, 8); d.HashSpaceSetLevels(ActiveSpace, -2, 8);
d.HashSpaceSetLevels(StaticSpace, -2, 8); d.HashSpaceSetLevels(StaticSpace, -2, 8);
d.HashSpaceSetLevels(GroundSpace, 0, 8);
// demote to second level // demote to second level
d.SpaceSetSublevel(ActiveSpace, 1); d.SpaceSetSublevel(ActiveSpace, 1);
d.SpaceSetSublevel(StaticSpace, 1); d.SpaceSetSublevel(StaticSpace, 1);
d.SpaceSetSublevel(GroundSpace, 1);
d.GeomSetCategoryBits(ActiveSpace, (uint)(CollisionCategories.Space | d.GeomSetCategoryBits(ActiveSpace, (uint)(CollisionCategories.Space |
CollisionCategories.Geom | CollisionCategories.Geom |
@ -402,6 +406,8 @@ namespace OpenSim.Region.Physics.OdePlugin
)); ));
d.GeomSetCollideBits(StaticSpace, 0); d.GeomSetCollideBits(StaticSpace, 0);
d.GeomSetCategoryBits(GroundSpace, (uint)(CollisionCategories.Land));
d.GeomSetCollideBits(GroundSpace, 0);
contactgroup = d.JointGroupCreate(0); contactgroup = d.JointGroupCreate(0);
//contactgroup //contactgroup
@ -1210,6 +1216,7 @@ namespace OpenSim.Region.Physics.OdePlugin
chr.CollidingObj = false; chr.CollidingObj = false;
// do colisions with static space // do colisions with static space
d.SpaceCollide2(StaticSpace, chr.Shell, IntPtr.Zero, nearCallback); d.SpaceCollide2(StaticSpace, chr.Shell, IntPtr.Zero, nearCallback);
// no coll with gnd
} }
} }
catch (AccessViolationException) catch (AccessViolationException)
@ -1238,7 +1245,10 @@ namespace OpenSim.Region.Physics.OdePlugin
if (!prm.m_outbounds) if (!prm.m_outbounds)
{ {
if (d.BodyIsEnabled(prm.Body)) if (d.BodyIsEnabled(prm.Body))
{
d.SpaceCollide2(StaticSpace, prm.collide_geom, IntPtr.Zero, nearCallback); d.SpaceCollide2(StaticSpace, prm.collide_geom, IntPtr.Zero, nearCallback);
d.SpaceCollide2(GroundSpace, prm.collide_geom, IntPtr.Zero, nearCallback);
}
} }
} }
} }
@ -1595,8 +1605,52 @@ namespace OpenSim.Region.Physics.OdePlugin
/// </summary> /// </summary>
/// <param name="prim"></param> /// <param name="prim"></param>
public override void AddPhysicsActorTaint(PhysicsActor prim) public override void AddPhysicsActorTaint(PhysicsActor prim)
{
}
// does all pending changes generated during region load process
public override void PrepareSimulation()
{
lock (OdeLock)
{ {
if (world == IntPtr.Zero)
{
ChangesQueue.Clear();
return;
}
ODEchangeitem item;
int donechanges = 0;
if (ChangesQueue.Count > 0)
{
m_log.InfoFormat("[ODE] start processing pending actor operations");
int tstart = Util.EnvironmentTickCount();
while (ChangesQueue.Dequeue(out item))
{
if (item.actor != null)
{
try
{
if (item.actor is OdeCharacter)
((OdeCharacter)item.actor).DoAChange(item.what, item.arg);
else if (((OdePrim)item.actor).DoAChange(item.what, item.arg))
RemovePrimThreadLocked((OdePrim)item.actor);
}
catch
{
m_log.WarnFormat("[PHYSICS]: Operation failed for a actor {0} {1}",
item.actor.Name, item.what.ToString());
}
}
donechanges++;
}
int time = Util.EnvironmentTickCountSubtract(tstart);
m_log.InfoFormat("[ODE] finished {0} operations in {1}ms", donechanges, time);
}
} }
}
/// <summary> /// <summary>
/// This is our main simulate loop /// This is our main simulate loop
@ -1642,7 +1696,40 @@ namespace OpenSim.Region.Physics.OdePlugin
lock(OdeLock) lock(OdeLock)
{ {
if (world == IntPtr.Zero) if (world == IntPtr.Zero)
{
ChangesQueue.Clear();
return 0; return 0;
}
ODEchangeitem item;
if (ChangesQueue.Count > 0)
{
int ttmpstart = Util.EnvironmentTickCount();
int ttmp;
while (ChangesQueue.Dequeue(out item))
{
if (item.actor != null)
{
try
{
if (item.actor is OdeCharacter)
((OdeCharacter)item.actor).DoAChange(item.what, item.arg);
else if (((OdePrim)item.actor).DoAChange(item.what, item.arg))
RemovePrimThreadLocked((OdePrim)item.actor);
}
catch
{
m_log.WarnFormat("[PHYSICS]: doChange failed for a actor {0} {1}",
item.actor.Name, item.what.ToString());
}
}
ttmp = Util.EnvironmentTickCountSubtract(ttmpstart);
if (ttmp > 20)
break;
}
}
d.WorldSetQuickStepNumIterations(world, curphysiteractions); d.WorldSetQuickStepNumIterations(world, curphysiteractions);
@ -1653,35 +1740,6 @@ namespace OpenSim.Region.Physics.OdePlugin
// clear pointer/counter to contacts to pass into joints // clear pointer/counter to contacts to pass into joints
m_global_contactcount = 0; m_global_contactcount = 0;
ODEchangeitem item;
if(ChangesQueue.Count >0)
{
int ttmpstart = Util.EnvironmentTickCount();
int ttmp;
while(ChangesQueue.Dequeue(out item))
{
if (item.actor != null)
{
try
{
if (item.actor is OdeCharacter)
((OdeCharacter)item.actor).DoAChange(item.what, item.arg);
else if (((OdePrim)item.actor).DoAChange(item.what, item.arg))
RemovePrimThreadLocked((OdePrim)item.actor);
}
catch
{
m_log.WarnFormat("[PHYSICS]: doChange failed for a actor {0} {1}",
item.actor.Name, item.what.ToString());
}
}
ttmp = Util.EnvironmentTickCountSubtract(ttmpstart);
if (ttmp > 20)
break;
}
}
// Move characters // Move characters
lock (_characters) lock (_characters)
@ -1813,10 +1871,47 @@ namespace OpenSim.Region.Physics.OdePlugin
mesher.ExpireReleaseMeshs(); mesher.ExpireReleaseMeshs();
m_lastMeshExpire = now; m_lastMeshExpire = now;
} }
// information block running in debug only
/* /*
int nactivegeoms = d.SpaceGetNumGeoms(ActiveSpace); int ntopactivegeoms = d.SpaceGetNumGeoms(ActiveSpace);
int nstaticgeoms = d.SpaceGetNumGeoms(StaticSpace); int ntopstaticgeoms = d.SpaceGetNumGeoms(StaticSpace);
int ngroundgeoms = d.SpaceGetNumGeoms(GroundSpace);
int nactivegeoms = 0;
int nactivespaces = 0;
int nstaticgeoms = 0;
int nstaticspaces = 0;
IntPtr sp;
for (int i = 0; i < ntopactivegeoms; i++)
{
sp = d.SpaceGetGeom(ActiveSpace, i);
if (d.GeomIsSpace(sp))
{
nactivespaces++;
nactivegeoms += d.SpaceGetNumGeoms(sp);
}
else
nactivegeoms++;
}
for (int i = 0; i < ntopstaticgeoms; i++)
{
sp = d.SpaceGetGeom(StaticSpace, i);
if (d.GeomIsSpace(sp))
{
nstaticspaces++;
nstaticgeoms += d.SpaceGetNumGeoms(sp);
}
else
nstaticgeoms++;
}
int ntopgeoms = d.SpaceGetNumGeoms(TopSpace); int ntopgeoms = d.SpaceGetNumGeoms(TopSpace);
int totgeoms = nstaticgeoms + nactivegeoms + ngroundgeoms + 1; // one ray
int nbodies = d.NTotalBodies; int nbodies = d.NTotalBodies;
int ngeoms = d.NTotalGeoms; int ngeoms = d.NTotalGeoms;
*/ */
@ -2113,7 +2208,9 @@ namespace OpenSim.Region.Physics.OdePlugin
offset, thickness, wrap); offset, thickness, wrap);
d.GeomHeightfieldDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1); d.GeomHeightfieldDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
GroundGeom = d.CreateHeightfield(StaticSpace, HeightmapData, 1);
GroundGeom = d.CreateHeightfield(GroundSpace, HeightmapData, 1);
if (GroundGeom != IntPtr.Zero) if (GroundGeom != IntPtr.Zero)
{ {
d.GeomSetCategoryBits(GroundGeom, (uint)(CollisionCategories.Land)); d.GeomSetCategoryBits(GroundGeom, (uint)(CollisionCategories.Land));
@ -2234,12 +2331,13 @@ namespace OpenSim.Region.Physics.OdePlugin
thickness, wrap); thickness, wrap);
// d.GeomUbitTerrainDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1); // d.GeomUbitTerrainDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
GroundGeom = d.CreateUbitTerrain(StaticSpace, HeightmapData, 1); GroundGeom = d.CreateUbitTerrain(GroundSpace, HeightmapData, 1);
if (GroundGeom != IntPtr.Zero) if (GroundGeom != IntPtr.Zero)
{ {
d.GeomSetCategoryBits(GroundGeom, (uint)(CollisionCategories.Land)); d.GeomSetCategoryBits(GroundGeom, (uint)(CollisionCategories.Land));
d.GeomSetCollideBits(GroundGeom, 0); d.GeomSetCollideBits(GroundGeom, 0);
PhysicsActor pa = new NullPhysicsActor(); PhysicsActor pa = new NullPhysicsActor();
pa.Name = "Terrain"; pa.Name = "Terrain";
pa.PhysicsActorType = (int)ActorTypes.Ground; pa.PhysicsActorType = (int)ActorTypes.Ground;
@ -2455,6 +2553,7 @@ namespace OpenSim.Region.Physics.OdePlugin
d.GeomDestroy(GroundGeom); d.GeomDestroy(GroundGeom);
} }
RegionTerrain.Clear(); RegionTerrain.Clear();
if (TerrainHeightFieldHeightsHandlers.Count > 0) if (TerrainHeightFieldHeightsHandlers.Count > 0)