BulletSim: add mesh representation. Use meshes for static objects and switch to hulls for physical objects.

bulletsim
Robert Adams 2011-08-26 15:51:21 -07:00 committed by Mic Bowman
parent 23f10f1d22
commit 21708b832b
7 changed files with 237 additions and 136 deletions

View File

@ -45,6 +45,7 @@ public sealed class BSPrim : PhysicsActor
private IMesh _mesh;
private PrimitiveBaseShape _pbs;
private ShapeData.PhysicsShapeType _shapeType;
private ulong _meshKey;
private ulong _hullKey;
private List<ConvexResult> _hulls;
@ -117,6 +118,7 @@ public sealed class BSPrim : PhysicsActor
_rotationalVelocity = OMV.Vector3.Zero;
_angularVelocity = OMV.Vector3.Zero;
_hullKey = 0;
_meshKey = 0;
_pbs = pbs;
_isPhysical = pisPhysical;
_isVolumeDetect = false;
@ -147,6 +149,7 @@ public sealed class BSPrim : PhysicsActor
_scene.RemoveVehiclePrim(this); // just to make sure
_scene.TaintedObject(delegate()
{
// everything in the C# world will get garbage collected. Tell the C++ world to free stuff.
BulletSimAPI.DestroyObject(_scene.WorldID, _localID);
});
}
@ -283,11 +286,6 @@ public sealed class BSPrim : PhysicsActor
set { _parentPrim = value; }
}
public ulong HullKey
{
get { return _hullKey; }
}
// return true if we are the root of a linkset (there are children to manage)
public bool IsRootOfLinkset
{
@ -463,7 +461,18 @@ public sealed class BSPrim : PhysicsActor
private void SetObjectDynamic()
{
// non-physical things work best with a mass of zero
_mass = IsStatic ? 0f : CalculateMass();
if (IsStatic)
{
_mass = 0f;
}
else
{
_mass = CalculateMass();
// If it's dynamic, make sure the hull has been created for it
// This shouldn't do much work if the object had previously been built
RecreateGeomAndObject();
}
BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), _mass);
// m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}, mass={4}", LogHeader, _localID, IsStatic, IsSolid, _mass);
}
@ -896,26 +905,19 @@ public sealed class BSPrim : PhysicsActor
#endregion Mass Calculation
// Create the geometry information in Bullet for later use
// The objects needs a hull if it's physical otherwise a mesh is enough
// No locking here because this is done when we know physics is not simulating
private void CreateGeom()
{
// Since we're recreating new, get rid of any previously generated shape
if (_hullKey != 0)
{
// m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey);
BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey);
_hullKey = 0;
_hulls.Clear();
}
if (_mesh == null)
// if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used
private void CreateGeom(bool forceRebuild)
{
// the mesher thought this was too simple to mesh. Use a native Bullet collision shape.
if (!_scene.NeedsMeshing(_pbs))
{
if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
{
if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z)
{
// m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to sphere of size {1}", LogHeader, _size);
// m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size);
_shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
// Bullet native objects are scaled by the Bullet engine so pass the size in
_scale = _size;
@ -923,13 +925,106 @@ public sealed class BSPrim : PhysicsActor
}
else
{
// m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size);
// m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size);
_shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX;
_scale = _size;
}
}
else
{
if (IsPhysical)
{
if (forceRebuild || _hullKey == 0)
{
// physical objects require a hull for interaction.
// This will create the mesh if it doesn't already exist
CreateGeomHull();
}
}
else
{
if (forceRebuild || _meshKey == 0)
{
// Static (non-physical) objects only need a mesh for bumping into
CreateGeomMesh();
}
}
}
}
// No locking here because this is done when we know physics is not simulating
private void CreateGeomMesh()
{
ulong newMeshKey = (ulong)_pbs.GetHashCode();
// if this new shape is the same as last time, don't recreate the mesh
if (_meshKey == newMeshKey) return;
// Since we're recreating new, get rid of any previously generated shape
if (_meshKey != 0)
{
// m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _meshKey);
BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey);
_mesh = null;
_meshKey = 0;
}
_meshKey = newMeshKey;
int lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD;
// always pass false for physicalness as this creates some sort of bounding box which we don't need
_mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, lod, false);
int[] indices = _mesh.getIndexListAsInt();
List<OMV.Vector3> vertices = _mesh.getVertexList();
float[] verticesAsFloats = new float[vertices.Count * 3];
int vi = 0;
foreach (OMV.Vector3 vv in vertices)
{
// m_log.DebugFormat("{0}: {1}: <{2:0.00}, {3:0.00}, {4:0.00}>", LogHeader, vi / 3, vv.X, vv.Y, vv.Z);
verticesAsFloats[vi++] = vv.X;
verticesAsFloats[vi++] = vv.Y;
verticesAsFloats[vi++] = vv.Z;
}
// m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
// LogHeader, _localID, _meshKey, indices.Length, vertices.Count);
BulletSimAPI.CreateMesh(_scene.WorldID, _meshKey, indices.GetLength(0), indices,
vertices.Count, verticesAsFloats);
_shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH;
// meshes are already scaled by the meshmerizer
_scale = new OMV.Vector3(1f, 1f, 1f);
return;
}
// No locking here because this is done when we know physics is not simulating
private void CreateGeomHull()
{
ulong newHullKey = (ulong)_pbs.GetHashCode();
// if the hull hasn't changed, don't rebuild it
if (newHullKey == _hullKey) return;
// Since we're recreating new, get rid of any previously generated shape
if (_hullKey != 0)
{
// m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey);
BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey);
_hullKey = 0;
_hulls.Clear();
BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey);
_mesh = null; // the mesh cannot match either
_meshKey = 0;
}
_hullKey = newHullKey;
if (_meshKey != _hullKey)
{
// if the underlying mesh has changed, rebuild it
CreateGeomMesh();
}
int[] indices = _mesh.getIndexListAsInt();
List<OMV.Vector3> vertices = _mesh.getVertexList();
@ -1009,13 +1104,11 @@ public sealed class BSPrim : PhysicsActor
}
// create the hull definition in Bullet
_hullKey = (ulong)_pbs.GetHashCode();
// m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, _localID, _hullKey, hullCount);
BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls);
_shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL;
// meshes are already scaled by the meshmerizer
_scale = new OMV.Vector3(1f, 1f, 1f);
}
return;
}
@ -1040,6 +1133,7 @@ public sealed class BSPrim : PhysicsActor
else
{
// simple object
// the mesh or hull must have already been created in Bullet
ShapeData shape;
FillShapeInfo(out shape);
BulletSimAPI.CreateObject(_scene.WorldID, shape);
@ -1081,7 +1175,8 @@ public sealed class BSPrim : PhysicsActor
shape.Scale = _scale;
shape.Mass = _isPhysical ? _mass : 0f;
shape.Buoyancy = _buoyancy;
shape.MeshKey = _hullKey;
shape.HullKey = _hullKey;
shape.MeshKey = _meshKey;
shape.Friction = _friction;
shape.Restitution = _restitution;
shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
@ -1098,13 +1193,13 @@ public sealed class BSPrim : PhysicsActor
// remove any constraints that might be in place
foreach (BSPrim prim in _childrenPrims)
{
// m_log.DebugFormat("{0}: CreateObject: RemoveConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
// m_log.DebugFormat("{0}: CreateLinkset: RemoveConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, prim.LocalID);
}
// create constraints between the root prim and each of the children
foreach (BSPrim prim in _childrenPrims)
{
// m_log.DebugFormat("{0}: CreateObject: AddConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
// m_log.DebugFormat("{0}: CreateLinkset: AddConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
// Zero motion for children so they don't interpolate
prim.ZeroMotion();
@ -1132,20 +1227,7 @@ public sealed class BSPrim : PhysicsActor
// No locking here because this is done when the physics engine is not simulating
private void RecreateGeomAndObject()
{
// If this object is complex or we are the root of a linkset, build a mesh.
// The root of a linkset must be a mesh so we can create the linked compound object.
// if (_scene.NeedsMeshing(_pbs) || IsRootOfLinkset )
if (_scene.NeedsMeshing(_pbs)) // linksets with constraints don't need a root mesh
{
// m_log.DebugFormat("{0}: RecreateGeomAndObject: creating mesh", LogHeader);
_mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, _scene.MeshLOD, _isPhysical);
}
else
{
// implement the shape with a Bullet native shape.
_mesh = null;
}
CreateGeom();
CreateGeom(true);
CreateObject();
return;
}

View File

@ -37,7 +37,6 @@ using OpenMetaverse;
using OpenSim.Region.Framework;
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
// Parameterize BulletSim. Pass a structure of parameters to the C++ code. Capsule size, friction, ...
// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight)
// Test sculpties
// Compute physics FPS reasonably
@ -52,8 +51,6 @@ using OpenSim.Region.Framework;
// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions)
// Implement LockAngularMotion
// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
// Built Galton board (lots of MoveTo's) and some slats were not positioned correctly (mistakes scattered)
// No mistakes with ODE. Shape creation race condition?
// Does NeedsMeshing() really need to exclude all the different shapes?
//
namespace OpenSim.Region.Physics.BulletSPlugin
@ -81,6 +78,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{
get { return m_meshLOD; }
}
private int m_sculptLOD;
public int SculptLOD
{
get { return m_sculptLOD; }
}
private int m_maxSubSteps;
private float m_fixedTimeStep;
@ -187,7 +189,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
_meshSculptedPrim = true; // mesh sculpted prims
_forceSimplePrimMeshing = false; // use complex meshing if called for
m_meshLOD = 32;
m_meshLOD = 8;
m_sculptLOD = 32;
m_maxSubSteps = 10;
m_fixedTimeStep = 1f / 60f;
@ -229,6 +232,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
_forceSimplePrimMeshing = pConfig.GetBoolean("ForceSimplePrimMeshing", _forceSimplePrimMeshing);
m_meshLOD = pConfig.GetInt("MeshLevelOfDetail", m_meshLOD);
m_sculptLOD = pConfig.GetInt("SculptLevelOfDetail", m_sculptLOD);
m_maxSubSteps = pConfig.GetInt("MaxSubSteps", m_maxSubSteps);
m_fixedTimeStep = pConfig.GetFloat("FixedTimeStep", m_fixedTimeStep);
@ -489,7 +493,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// can use an internal representation for the prim
if (!_forceSimplePrimMeshing)
{
// m_log.DebugFormat("{0}: NeedsMeshing: simple mesh: profshape={1}, curve={2}", LogHeader, pbs.ProfileShape, pbs.PathCurve);
if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
|| (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
&& pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
@ -663,7 +666,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
#region Runtime settable parameters
public static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[]
{
new PhysParameterEntry("MeshLOD", "Level of detail to render meshes (Power of two. Default 32)"),
new PhysParameterEntry("MeshLOD", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)"),
new PhysParameterEntry("SculptLOD", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)"),
new PhysParameterEntry("MaxSubStep", "In simulation step, maximum number of substeps"),
new PhysParameterEntry("FixedTimeStep", "In simulation step, seconds of one substep (1/60)"),
new PhysParameterEntry("MaxObjectMass", "Maximum object mass (10000.01)"),
@ -712,6 +716,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
switch (lparm)
{
case "meshlod": m_meshLOD = (int)val; break;
case "sculptlod": m_sculptLOD = (int)val; break;
case "maxsubstep": m_maxSubSteps = (int)val; break;
case "fixedtimestep": m_fixedTimeStep = val; break;
case "maxobjectmass": m_maximumObjectMass = val; break;
@ -812,6 +817,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
switch (parm.ToLower())
{
case "meshlod": val = (float)m_meshLOD; break;
case "sculptlod": val = (float)m_sculptLOD; break;
case "maxsubstep": val = (float)m_maxSubSteps; break;
case "fixedtimestep": val = m_fixedTimeStep; break;
case "maxobjectmass": val = m_maximumObjectMass; break;

View File

@ -49,7 +49,8 @@ public struct ShapeData
SHAPE_CONE = 2,
SHAPE_CYLINDER = 3,
SHAPE_SPHERE = 4,
SHAPE_HULL = 5
SHAPE_MESH = 5,
SHAPE_HULL = 6
};
public uint ID;
public PhysicsShapeType Type;
@ -59,6 +60,7 @@ public struct ShapeData
public Vector3 Scale;
public float Mass;
public float Buoyancy;
public System.UInt64 HullKey;
public System.UInt64 MeshKey;
public float Friction;
public float Restitution;
@ -164,13 +166,22 @@ public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSte
out IntPtr collidersPtr);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateHull(uint worldID, System.UInt64 meshKey, int hullCount,
[MarshalAs(UnmanagedType.LPArray)] float[] hulls
public static extern bool CreateHull(uint worldID, System.UInt64 meshKey,
int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls
);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateMesh(uint worldID, System.UInt64 meshKey,
int indexCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices,
int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices
);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DestroyHull(uint worldID, System.UInt64 meshKey);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateObject(uint worldID, ShapeData shapeData);

Binary file not shown.

Binary file not shown.

View File

@ -766,8 +766,10 @@
; If 'true', force simple prims (box and sphere) to be meshed
ForceSimplePrimMeshing = false
; level of detail for physical meshes. 32,16,8 or 4 with 32 being full detail
MeshLevelOfDetail = 8
; number^2 non-physical level of detail of the sculpt texture. 32x32 - 1024 verticies
MeshLevelOfDetail = 32
SculptLevelOfDetail = 32
; Bullet step parameters
MaxSubSteps = 10;

Binary file not shown.