public bool SetAxisEnable(int pIndex, bool pAxisEnable) { PhysicsScene.DetailLog("{0},BSConstraintSpring.SetEnable,obj1ID={1},obj2ID={2},indx={3},enable={4}", m_body1.ID, m_body1.ID, m_body2.ID, pIndex, pAxisEnable); PhysicsScene.PE.SpringEnable(m_constraint, pIndex, BSParam.NumericBool(pAxisEnable)); return(true); }
// Set parameter on a specific or all instances. // Return 'false' if not able to set the parameter. // Setting the value in the m_params block will change the value the physics engine // will use the next time since it's pinned and shared memory. // Some of the values require calling into the physics engine to get the new // value activated ('terrainFriction' for instance). public bool SetPhysicsParameter(string parm, float val, uint localID) { bool ret = false; BSParam.ParameterDefn theParam; if (BSParam.TryGetParameter(parm, out theParam)) { theParam.setter(this, parm, localID, val); ret = true; } return(ret); }
// All default parameter values are set here. There should be no values set in the // variable definitions. private void GetInitialParameterValues(IConfigSource config) { ConfigurationParameters parms = new ConfigurationParameters(); UnmanagedParams[0] = parms; BSParam.SetParameterDefaultValues(this); if (config != null) { // If there are specifications in the ini file, use those values IConfig pConfig = config.Configs["BulletSim"]; if (pConfig != null) { BSParam.SetParameterConfigurationValues(this, pConfig); // There are two Bullet implementations to choose from BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged"); // Very detailed logging for physics debugging // TODO: the boolean values can be moved to the normal parameter processing. m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false); m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false); // Very detailed logging for vehicle debugging VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false); // Do any replacements in the parameters m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); } else { // Nothing in the configuration INI file so assume unmanaged and other defaults. BulletEngineName = "BulletUnmanaged"; m_physicsLoggingEnabled = false; VehicleLoggingEnabled = false; } // The material characteristics. BSMaterials.InitializeFromDefaults(Params); if (pConfig != null) { // Let the user add new and interesting material property values. BSMaterials.InitializefromParameters(pConfig); } } }
// Get parameter. // Return 'false' if not able to get the parameter. public bool GetPhysicsParameter(string parm, out string value) { string val = String.Empty; bool ret = false; BSParam.ParameterDefnBase theParam; if (BSParam.TryGetParameter(parm, out theParam)) { val = theParam.GetValue(this); ret = true; } value = val; return(ret); }
// Get parameter. // Return 'false' if not able to get the parameter. public bool GetPhysicsParameter(string parm, out float value) { float val = 0f; bool ret = false; BSParam.ParameterDefn theParam; if (BSParam.TryGetParameter(parm, out theParam)) { val = theParam.getter(this); ret = true; } value = val; return(ret); }
// Set parameter on a specific or all instances. // Return 'false' if not able to set the parameter. // Setting the value in the m_params block will change the value the physics engine // will use the next time since it's pinned and shared memory. // Some of the values require calling into the physics engine to get the new // value activated ('terrainFriction' for instance). public bool SetPhysicsParameter(string parm, string val, uint localID) { bool ret = false; BSParam.ParameterDefnBase theParam; if (BSParam.TryGetParameter(parm, out theParam)) { // Set the value in the C# code theParam.SetValue(this, val); // Optionally set the parameter in the unmanaged code if (theParam.HasSetOnObject) { // update all the localIDs specified // If the local ID is APPLY_TO_NONE, just change the default value // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs // If the localID is a specific object, apply the parameter change to only that object List <uint> objectIDs = new List <uint>(); switch (localID) { case PhysParameterEntry.APPLY_TO_NONE: // This will cause a call into the physical world if some operation is specified (SetOnObject). objectIDs.Add(TERRAIN_ID); TaintedUpdateParameter(parm, objectIDs, val); break; case PhysParameterEntry.APPLY_TO_ALL: lock (PhysObjects) objectIDs = new List <uint>(PhysObjects.Keys); TaintedUpdateParameter(parm, objectIDs, val); break; default: // setting only one localID objectIDs.Add(localID); TaintedUpdateParameter(parm, objectIDs, val); break; } } ret = true; } return(ret); }
// Reset this constraint making sure it has all its internal structures // recomputed and is enabled and ready to go. public virtual bool RecomputeConstraintVariables(float mass) { bool ret = false; if (m_enabled) { ret = CalculateTransforms(); if (ret) { // Setting an object's mass to zero (making it static like when it's selected) // automatically disables the constraints. // If the link is enabled, be sure to set the constraint itself to enabled. BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, BSParam.NumericBool(true)); } else { m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID); } } return(ret); }
// schedule the actual updating of the paramter to when the phys engine is not busy private void TaintedUpdateParameter(string parm, List <uint> lIDs, float val) { float xval = val; List <uint> xlIDs = lIDs; string xparm = parm; TaintedObject("BSScene.UpdateParameterSet", delegate() { BSParam.ParameterDefn thisParam; if (BSParam.TryGetParameter(xparm, out thisParam)) { if (thisParam.onObject != null) { foreach (uint lID in xlIDs) { BSPhysObject theObject = null; PhysObjects.TryGetValue(lID, out theObject); thisParam.onObject(this, theObject, xval); } } } }); }
// All default parameter values are set here. There should be no values set in the // variable definitions. private void GetInitialParameterValues(IConfigSource config) { ConfigurationParameters parms = new ConfigurationParameters(); UnmanagedParams[0] = parms; BSParam.SetParameterDefaultValues(this); if (config != null) { // If there are specifications in the ini file, use those values IConfig pConfig = config.Configs["BulletSim"]; if (pConfig != null) { BSParam.SetParameterConfigurationValues(this, pConfig); // Very detailed logging for physics debugging m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false); // Very detailed logging for vehicle debugging VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false); // Do any replacements in the parameters m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); } // The material characteristics. BSMaterials.InitializeFromDefaults(Params); if (pConfig != null) { // Let the user add new and interesting material property values. BSMaterials.InitializefromParameters(pConfig); } } }
// schedule the actual updating of the paramter to when the phys engine is not busy private void TaintedUpdateParameter(string parm, List <uint> lIDs, string val) { string xval = val; List <uint> xlIDs = lIDs; string xparm = parm; TaintedObject("BSScene.UpdateParameterSet", delegate() { BSParam.ParameterDefnBase thisParam; if (BSParam.TryGetParameter(xparm, out thisParam)) { if (thisParam.HasSetOnObject) { foreach (uint lID in xlIDs) { BSPhysObject theObject = null; if (PhysObjects.TryGetValue(lID, out theObject)) { thisParam.SetOnObject(this, theObject); } } } } }); }
private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim) { // Zero motion for children so they don't interpolate childPrim.ZeroMotion(true); // Relative position normalized to the root prim // Essentually a vector pointing from center of rootPrim to center of childPrim OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position; // real world coordinate of midpoint between the two objects OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", rootPrim.LocalID, rootPrim.LocalID, rootPrim.PhysBody.AddrString, childPrim.LocalID, childPrim.PhysBody.AddrString, rootPrim.Position, childPrim.Position, midPoint); // create a constraint that allows no freedom of movement between the two objects // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 BSConstraint6Dof constrain = new BSConstraint6Dof( PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true); // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true ); /* NOTE: below is an attempt to build constraint with full frame computation, etc. * Using the midpoint is easier since it lets the Bullet code manipulate the transforms * of the objects. * Code left for future programmers. * // ================================================================================== * // relative position normalized to the root prim * OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); * OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation; * * // relative rotation of the child to the parent * OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; * OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); * * DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); * BS6DofConstraint constrain = new BS6DofConstraint( * PhysicsScene.World, rootPrim.Body, childPrim.Body, * OMV.Vector3.Zero, * OMV.Quaternion.Inverse(rootPrim.Orientation), * OMV.Vector3.Zero, * OMV.Quaternion.Inverse(childPrim.Orientation), * true, * true * ); * // ================================================================================== */ PhysicsScene.Constraints.AddConstraint(constrain); // zero linear and angular limits makes the objects unable to move in relation to each other constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); // tweek the constraint to increase stability constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset)); constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor), BSParam.LinkConstraintTransMotorMaxVel, BSParam.LinkConstraintTransMotorMaxForce); constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP); if (BSParam.LinkConstraintSolverIterations != 0f) { constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations); } return(constrain); }
// Get the list of parameters this physics engine supports public PhysParameterEntry[] GetParameterList() { BSParam.BuildParameterTable(); return(BSParam.SettableParameters); }
private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { BulletShape newShape = new BulletShape(); IntPtr hullPtr = IntPtr.Zero; if (BSParam.ShouldUseBulletHACD) { // Build the hull shape from an existing mesh shape. // The mesh should have already been created in Bullet. physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID); BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); if (meshShape.physShapeInfo.HasPhysicalShape) { HACDParams parms; parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; parms.minClusters = BSParam.BHullMinClusters; parms.compacityWeight = BSParam.BHullCompacityWeight; parms.volumeWeight = BSParam.BHullVolumeWeight; parms.concavity = BSParam.BHullConcavity; parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); // Now done with the mesh shape. meshShape.Dereference(physicsScene); } physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); } if (!newShape.HasPhysicalShape) { // Build a new hull in the physical world using the C# HACD algorigthm. // Pass true for physicalness as this prevents the creation of bounding box which is not needed IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); if (meshData != null) { if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) { // Release the fetched asset data once it has been used. pbs.SculptData = new byte[0]; prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; } int[] indices = meshData.getIndexListAsInt(); List <OMV.Vector3> vertices = meshData.getVertexList(); //format conversion from IMesh format to DecompDesc format List <int> convIndices = new List <int>(); List <float3> convVertices = new List <float3>(); for (int ii = 0; ii < indices.GetLength(0); ii++) { convIndices.Add(indices[ii]); } foreach (OMV.Vector3 vv in vertices) { convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); } uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) { // Simple primitive shapes we know are convex so they are better implemented with // fewer hulls. // Check for simple shape (prim without cuts) and reduce split parameter if so. if (BSShapeCollection.PrimHasNoCuts(pbs)) { maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; } } // setup and do convex hull conversion m_hulls = new List <ConvexResult>(); DecompDesc dcomp = new DecompDesc(); dcomp.mIndices = convIndices; dcomp.mVertices = convVertices; dcomp.mDepth = maxDepthSplit; dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); // create the hull into the _hulls variable convexBuilder.process(dcomp); physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); // Convert the vertices and indices for passing to unmanaged. // The hull information is passed as a large floating point array. // The format is: // convHulls[0] = number of hulls // convHulls[1] = number of vertices in first hull // convHulls[2] = hull centroid X coordinate // convHulls[3] = hull centroid Y coordinate // convHulls[4] = hull centroid Z coordinate // convHulls[5] = first hull vertex X // convHulls[6] = first hull vertex Y // convHulls[7] = first hull vertex Z // convHulls[8] = second hull vertex X // ... // convHulls[n] = number of vertices in second hull // convHulls[n+1] = second hull centroid X coordinate // ... // // TODO: is is very inefficient. Someday change the convex hull generator to return // data structures that do not need to be converted in order to pass to Bullet. // And maybe put the values directly into pinned memory rather than marshaling. int hullCount = m_hulls.Count; int totalVertices = 1; // include one for the count of the hulls foreach (ConvexResult cr in m_hulls) { totalVertices += 4; // add four for the vertex count and centroid totalVertices += cr.HullIndices.Count * 3; // we pass just triangles } float[] convHulls = new float[totalVertices]; convHulls[0] = (float)hullCount; int jj = 1; foreach (ConvexResult cr in m_hulls) { // copy vertices for index access float3[] verts = new float3[cr.HullVertices.Count]; int kk = 0; foreach (float3 ff in cr.HullVertices) { verts[kk++] = ff; } // add to the array one hull's worth of data convHulls[jj++] = cr.HullIndices.Count; convHulls[jj++] = 0f; // centroid x,y,z convHulls[jj++] = 0f; convHulls[jj++] = 0f; foreach (int ind in cr.HullIndices) { convHulls[jj++] = verts[ind].x; convHulls[jj++] = verts[ind].y; convHulls[jj++] = verts[ind].z; } } // create the hull data structure in Bullet newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); } newShape.shapeKey = newHullKey; } return(newShape); }