From 34cbc738a84b7946a87e8bfd3bb04869519b3dee Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Tue, 20 Nov 2012 20:36:49 -0800 Subject: BulletSim: enablement and debugging of mesh terrain. --- .../Region/Physics/BulletSPlugin/BSTerrainMesh.cs | 229 ++++++++++++++++++++- 1 file changed, 220 insertions(+), 9 deletions(-) (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index 387c78b..bbb014a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -44,30 +44,241 @@ public sealed class BSTerrainMesh : BSTerrainPhys { static string LogHeader = "[BULLETSIM TERRAIN MESH]"; - public BSTerrainMesh(BSScene physicsScene, uint id, Vector3 regionSize) - : base(physicsScene) + private float[] m_savedHeightMap; + int m_sizeX; + int m_sizeY; + + BulletShape m_terrainShape; + BulletBody m_terrainBody; + + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) + : base(physicsScene, regionBase, id) { } - public BSTerrainMesh(BSScene physicsScene /* parameters for making mesh */) - : base(physicsScene) + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */) + : base(physicsScene, regionBase, id) { } + // Create terrain mesh from a heightmap. + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, + Vector3 minCoords, Vector3 maxCoords) + : base(physicsScene, regionBase, id) + { + int indicesCount; + int[] indices; + int verticesCount; + float[] vertices; + + m_savedHeightMap = initialMap; + + m_sizeX = (int)(maxCoords.X - minCoords.X); + m_sizeY = (int)(maxCoords.Y - minCoords.Y); + + if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap, m_sizeX, m_sizeY, + (float)m_sizeX, (float)m_sizeY, + Vector3.Zero, 1.0f, + out indicesCount, out indices, out verticesCount, out vertices)) + { + // DISASTER!! + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID); + PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); + // Something is very messed up and a crash is in our future. + return; + } + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,afterConvertHeightmapToMesh,ver={1},ind={2}", + ID, verticesCount, indicesCount); + + m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, + indicesCount, indices, verticesCount, vertices), + PhysicsShapeType.SHAPE_MESH); + if (m_terrainShape.ptr == IntPtr.Zero) + { + // DISASTER!! + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID); + physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); + // Something is very messed up and a crash is in our future. + return; + } + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,afterCreateShape,shape={1}", ID, m_terrainShape); + + // The terrain object initial position is at the center of the object + Vector3 centerPos; + centerPos.X = minCoords.X + (m_sizeX / 2f); + centerPos.Y = minCoords.Y + (m_sizeY / 2f); + centerPos.Z = minCoords.Z + ((maxCoords.Z - minCoords.Z) / 2f); + Quaternion rot = Quaternion.Identity; + + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,creatingBody,centerPos={1},rot={2}", ID, centerPos, rot); + m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( + m_terrainShape.ptr, ID, centerPos, rot)); + if (m_terrainBody.ptr == IntPtr.Zero) + { + // DISASTER!! + physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); + // Something is very messed up and a crash is in our future. + return; + } + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,afterCreateBody,body={1}", ID, m_terrainBody); + + // Set current terrain attributes + BulletSimAPI.SetFriction2(m_terrainBody.ptr, PhysicsScene.Params.terrainFriction); + BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, PhysicsScene.Params.terrainHitFraction); + BulletSimAPI.SetRestitution2(m_terrainBody.ptr, PhysicsScene.Params.terrainRestitution); + BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); + + // Static objects are not very massive. + BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero); + + // Return the new terrain to the world of physical objects + BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); + + // redo its bounding box now that it is in the world + BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr); + + BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr, + (uint)CollisionFilterGroups.TerrainFilter, + (uint)CollisionFilterGroups.TerrainMask); + + // Make it so the terrain will not move or be considered for movement. + BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION); + } + public override void Dispose() { - return; + if (m_terrainBody.ptr != IntPtr.Zero) + { + BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); + // Frees both the body and the shape. + BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr); + } } public override float GetHeightAtXYZ(Vector3 pos) { - return 12345f; + // For the moment use the saved heightmap to get the terrain height. + // TODO: raycast downward to find the true terrain below the position. + float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; + + int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X; + try + { + ret = m_savedHeightMap[mapIndex]; + } + catch + { + // Sometimes they give us wonky values of X and Y. Give a warning and return something. + PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", + LogHeader, TerrainBase, pos); + ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; + } + return ret; } - public override Vector3 TerrainBase + // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). + // Return 'true' if successfully created. + public static bool ConvertHeightmapToMesh( + BSScene physicsScene, + float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap + float extentX, float extentY, // zero based range for output vertices + Vector3 extentBase, // base to be added to all vertices + float magnification, // number of vertices to create between heightMap coords + out int indicesCountO, out int[] indicesO, + out int verticesCountO, out float[] verticesO) { - get { return Vector3.Zero; } - } + bool ret = false; + + int indicesCount = 0; + int verticesCount = 0; + int[] indices = new int[0]; + float[] vertices = new float[0]; + // Simple mesh creation which assumes magnification == 1, sizeX == extentX and sizeY == extentY. + // TODO: do a more general solution that scales, adds new vertices and smoothes the result. + + try + { + // One vertice per heightmap value plus the vertices off the top and bottom edge. + int totalVertices = (sizeX + 1) * (sizeY + 1); + vertices = new float[totalVertices * 3]; + int totalIndices = sizeX * sizeY * 6; + indices = new int[totalIndices]; + + physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2}", + BSScene.DetailLogZero, totalVertices, totalIndices); + float magX = (float)sizeX / extentX; + float magY = (float)sizeY / extentY; + // Note that sizeX+1 vertices are created since there is land between this and the next region. + for (int yy = 0; yy <= sizeY; yy++) + { + for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we got through sizeX + 1 times + { + int offset = yy * sizeX + xx; + // Extend the height from the height from the last row or column + if (yy == sizeY) offset -= sizeX; + if (xx == sizeX) offset -= 1; + float height = heightMap[offset]; + vertices[verticesCount + 0] = (float)xx * magX + extentBase.X; + vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y; + vertices[verticesCount + 2] = height + extentBase.Z; + if (physicsScene.PhysicsLogging.Enabled && verticesCount < 900) // DEBUG DEBUG DEBUG + { + Vector3 genVertex = new Vector3( + vertices[verticesCount + 0], + vertices[verticesCount + 1], + vertices[verticesCount + 2]); + physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,ii={1},vertex={2}", + BSScene.DetailLogZero, verticesCount/3, genVertex); + } + verticesCount += 3; + } + } + verticesCount = verticesCount / 3; + physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeVerts,verCount={1}", + BSScene.DetailLogZero, verticesCount); + + for (int yy = 0; yy < sizeY; yy++) + { + for (int xx = 0; xx < sizeX; xx++) + { + int offset = yy * sizeX + xx; + // Each vertices is presumed to be the upper left corner of a box of two triangles + indices[indicesCount + 0] = offset; + indices[indicesCount + 1] = offset + 1; + indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column + indices[indicesCount + 3] = offset + 1; + indices[indicesCount + 4] = offset + sizeX + 2; + indices[indicesCount + 5] = offset + sizeX + 1; + if (indicesCount < (300 * 6)) // DEBUG DEBUG DEBUG + physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,i0={1},i1={2},i2={3},i3={4},i4={5},i5={6}", // DEEBUG DEBUG DEBUG + BSScene.DetailLogZero, + indices[indicesCount + 0], + indices[indicesCount + 1], + indices[indicesCount + 2], + indices[indicesCount + 3], + indices[indicesCount + 4], + indices[indicesCount + 5] + ); + indicesCount += 6; + } + } + physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeIndices,indCount={1}", // DEEBUG DEBUG DEBUG + LogHeader, indicesCount); // DEBUG + ret = true; + } + catch (Exception e) + { + physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. Base={1}, e={2}", + LogHeader, extentBase, e); + } + + indicesCountO = indicesCount; + indicesO = indices; + verticesCountO = verticesCount; + verticesO = vertices; + + return ret; + } } } -- cgit v1.1