BulletSim: add mesh representation. Use meshes for static objects and switch to hulls for physical objects.
parent
23f10f1d22
commit
21708b832b
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
@ -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.
Loading…
Reference in New Issue