OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
BSTerrainMesh.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyrightD
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 using System;
28 using System.Collections.Generic;
29 using System.Text;
30 
31 using OpenSim.Framework;
32 using OpenSim.Region.Framework;
33 using OpenSim.Region.PhysicsModules.SharedBase;
34 
35 using Nini.Config;
36 using log4net;
37 
38 using OpenMetaverse;
39 
40 namespace OpenSim.Region.PhysicsModule.BulletS
41 {
42 public sealed class BSTerrainMesh : BSTerrainPhys
43 {
44  static string LogHeader = "[BULLETSIM TERRAIN MESH]";
45 
46  private float[] m_savedHeightMap;
47  int m_sizeX;
48  int m_sizeY;
49 
50  BulletShape m_terrainShape;
51  BulletBody m_terrainBody;
52 
53  public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
54  : base(physicsScene, regionBase, id)
55  {
56  }
57 
58  public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
59  : base(physicsScene, regionBase, id)
60  {
61  }
62 
63  // Create terrain mesh from a heightmap.
64  public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
65  Vector3 minCoords, Vector3 maxCoords)
66  : base(physicsScene, regionBase, id)
67  {
68  int indicesCount;
69  int[] indices;
70  int verticesCount;
71  float[] vertices;
72 
73  m_savedHeightMap = initialMap;
74 
75  m_sizeX = (int)(maxCoords.X - minCoords.X);
76  m_sizeY = (int)(maxCoords.Y - minCoords.Y);
77 
78  bool meshCreationSuccess = false;
79  if (BSParam.TerrainMeshMagnification == 1)
80  {
81  // If a magnification of one, use the old routine that is tried and true.
82  meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(m_physicsScene,
83  initialMap, m_sizeX, m_sizeY, // input size
84  Vector3.Zero, // base for mesh
85  out indicesCount, out indices, out verticesCount, out vertices);
86  }
87  else
88  {
89  // Other magnifications use the newer routine
90  meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(m_physicsScene,
91  initialMap, m_sizeX, m_sizeY, // input size
92  BSParam.TerrainMeshMagnification,
93  physicsScene.TerrainManager.DefaultRegionSize,
94  Vector3.Zero, // base for mesh
95  out indicesCount, out indices, out verticesCount, out vertices);
96  }
97  if (!meshCreationSuccess)
98  {
99  // DISASTER!!
100  m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID);
101  m_physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
102  // Something is very messed up and a crash is in our future.
103  return;
104  }
105 
106  m_physicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}",
107  BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length);
108 
109  m_terrainShape = m_physicsScene.PE.CreateMeshShape(m_physicsScene.World, indicesCount, indices, verticesCount, vertices);
110  if (!m_terrainShape.HasPhysicalShape)
111  {
112  // DISASTER!!
113  m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID);
114  m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
115  // Something is very messed up and a crash is in our future.
116  return;
117  }
118 
119  Vector3 pos = regionBase;
120  Quaternion rot = Quaternion.Identity;
121 
122  m_terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot);
123  if (!m_terrainBody.HasPhysicalBody)
124  {
125  // DISASTER!!
126  m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
127  // Something is very messed up and a crash is in our future.
128  return;
129  }
130  physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin);
131 
132  // Set current terrain attributes
133  m_physicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction);
134  m_physicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction);
135  m_physicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution);
136  m_physicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold);
137  m_physicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT);
138 
139  // Static objects are not very massive.
140  m_physicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero);
141 
142  // Put the new terrain to the world of physical objects
143  m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_terrainBody);
144 
145  // Redo its bounding box now that it is in the world
146  m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_terrainBody);
147 
148  m_terrainBody.collisionType = CollisionType.Terrain;
149  m_terrainBody.ApplyCollisionMask(m_physicsScene);
150 
151  if (BSParam.UseSingleSidedMeshes)
152  {
153  m_physicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id);
154  m_physicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
155  }
156 
157  // Make it so the terrain will not move or be considered for movement.
158  m_physicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION);
159  }
160 
161  public override void Dispose()
162  {
163  if (m_terrainBody.HasPhysicalBody)
164  {
165  m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_terrainBody);
166  // Frees both the body and the shape.
167  m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_terrainBody);
168  m_terrainBody.Clear();
169  m_terrainShape.Clear();
170  }
171  }
172 
173  public override float GetTerrainHeightAtXYZ(Vector3 pos)
174  {
175  // For the moment use the saved heightmap to get the terrain height.
176  // TODO: raycast downward to find the true terrain below the position.
177  float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
178 
179  int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
180  try
181  {
182  ret = m_savedHeightMap[mapIndex];
183  }
184  catch
185  {
186  // Sometimes they give us wonky values of X and Y. Give a warning and return something.
187  m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
188  LogHeader, TerrainBase, pos);
189  ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
190  }
191  return ret;
192  }
193 
194  // The passed position is relative to the base of the region.
195  public override float GetWaterLevelAtXYZ(Vector3 pos)
196  {
197  return m_physicsScene.SimpleWaterLevel;
198  }
199 
200  // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
201  // Return 'true' if successfully created.
202  public static bool ConvertHeightmapToMesh( BSScene physicsScene,
203  float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
204  Vector3 extentBase, // base to be added to all vertices
205  out int indicesCountO, out int[] indicesO,
206  out int verticesCountO, out float[] verticesO)
207  {
208  bool ret = false;
209 
210  int indicesCount = 0;
211  int verticesCount = 0;
212  int[] indices = new int[0];
213  float[] vertices = new float[0];
214 
215  // Simple mesh creation which assumes magnification == 1.
216  // TODO: do a more general solution that scales, adds new vertices and smoothes the result.
217 
218  // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
219  // from zero to <= sizeX). The triangle indices are then generated as two triangles
220  // per heightmap point. There are sizeX by sizeY of these squares. The extra row and
221  // column of vertices are used to complete the triangles of the last row and column
222  // of the heightmap.
223  try
224  {
225  // One vertice per heightmap value plus the vertices off the side and bottom edge.
226  int totalVertices = (sizeX + 1) * (sizeY + 1);
227  vertices = new float[totalVertices * 3];
228  int totalIndices = sizeX * sizeY * 6;
229  indices = new int[totalIndices];
230 
231  if (physicsScene != null)
232  physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3}",
233  BSScene.DetailLogZero, totalVertices, totalIndices, extentBase);
234  float minHeight = float.MaxValue;
235  // Note that sizeX+1 vertices are created since there is land between this and the next region.
236  for (int yy = 0; yy <= sizeY; yy++)
237  {
238  for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
239  {
240  int offset = yy * sizeX + xx;
241  // Extend the height with the height from the last row or column
242  if (yy == sizeY) offset -= sizeX;
243  if (xx == sizeX) offset -= 1;
244  float height = heightMap[offset];
245  minHeight = Math.Min(minHeight, height);
246  vertices[verticesCount + 0] = (float)xx + extentBase.X;
247  vertices[verticesCount + 1] = (float)yy + extentBase.Y;
248  vertices[verticesCount + 2] = height + extentBase.Z;
249  verticesCount += 3;
250  }
251  }
252  verticesCount = verticesCount / 3;
253 
254  for (int yy = 0; yy < sizeY; yy++)
255  {
256  for (int xx = 0; xx < sizeX; xx++)
257  {
258  int offset = yy * (sizeX + 1) + xx;
259  // Each vertices is presumed to be the upper left corner of a box of two triangles
260  indices[indicesCount + 0] = offset;
261  indices[indicesCount + 1] = offset + 1;
262  indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
263  indices[indicesCount + 3] = offset + 1;
264  indices[indicesCount + 4] = offset + sizeX + 2;
265  indices[indicesCount + 5] = offset + sizeX + 1;
266  indicesCount += 6;
267  }
268  }
269 
270  ret = true;
271  }
272  catch (Exception e)
273  {
274  if (physicsScene != null)
275  physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
276  LogHeader, physicsScene.RegionName, extentBase, e);
277  }
278 
279  indicesCountO = indicesCount;
280  indicesO = indices;
281  verticesCountO = verticesCount;
282  verticesO = vertices;
283 
284  return ret;
285  }
286 
287  private class HeightMapGetter
288  {
289  private float[] m_heightMap;
290  private int m_sizeX;
291  private int m_sizeY;
292  public HeightMapGetter(float[] pHeightMap, int pSizeX, int pSizeY)
293  {
294  m_heightMap = pHeightMap;
295  m_sizeX = pSizeX;
296  m_sizeY = pSizeY;
297  }
298  // The heightmap is extended as an infinite plane at the last height
299  public float GetHeight(int xx, int yy)
300  {
301  int offset = 0;
302  // Extend the height with the height from the last row or column
303  if (yy >= m_sizeY)
304  if (xx >= m_sizeX)
305  offset = (m_sizeY - 1) * m_sizeX + (m_sizeX - 1);
306  else
307  offset = (m_sizeY - 1) * m_sizeX + xx;
308  else
309  if (xx >= m_sizeX)
310  offset = yy * m_sizeX + (m_sizeX - 1);
311  else
312  offset = yy * m_sizeX + xx;
313 
314  return m_heightMap[offset];
315  }
316  }
317 
318  // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
319  // Version that handles magnification.
320  // Return 'true' if successfully created.
321  public static bool ConvertHeightmapToMesh2( BSScene physicsScene,
322  float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
323  int magnification, // number of vertices per heighmap step
324  Vector3 extent, // dimensions of the output mesh
325  Vector3 extentBase, // base to be added to all vertices
326  out int indicesCountO, out int[] indicesO,
327  out int verticesCountO, out float[] verticesO)
328  {
329  bool ret = false;
330 
331  int indicesCount = 0;
332  int verticesCount = 0;
333  int[] indices = new int[0];
334  float[] vertices = new float[0];
335 
336  HeightMapGetter hmap = new HeightMapGetter(heightMap, sizeX, sizeY);
337 
338  // The vertices dimension of the output mesh
339  int meshX = sizeX * magnification;
340  int meshY = sizeY * magnification;
341  // The output size of one mesh step
342  float meshXStep = extent.X / meshX;
343  float meshYStep = extent.Y / meshY;
344 
345  // Create an array of vertices that is meshX+1 by meshY+1 (note the loop
346  // from zero to <= meshX). The triangle indices are then generated as two triangles
347  // per heightmap point. There are meshX by meshY of these squares. The extra row and
348  // column of vertices are used to complete the triangles of the last row and column
349  // of the heightmap.
350  try
351  {
352  // Vertices for the output heightmap plus one on the side and bottom to complete triangles
353  int totalVertices = (meshX + 1) * (meshY + 1);
354  vertices = new float[totalVertices * 3];
355  int totalIndices = meshX * meshY * 6;
356  indices = new int[totalIndices];
357 
358  if (physicsScene != null)
359  physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,inSize={1},outSize={2},totVert={3},totInd={4},extentBase={5}",
360  BSScene.DetailLogZero, new Vector2(sizeX, sizeY), new Vector2(meshX, meshY),
361  totalVertices, totalIndices, extentBase);
362 
363  float minHeight = float.MaxValue;
364  // Note that sizeX+1 vertices are created since there is land between this and the next region.
365  // Loop through the output vertices and compute the mediun height in between the input vertices
366  for (int yy = 0; yy <= meshY; yy++)
367  {
368  for (int xx = 0; xx <= meshX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
369  {
370  float offsetY = (float)yy * (float)sizeY / (float)meshY; // The Y that is closest to the mesh point
371  int stepY = (int)offsetY;
372  float fractionalY = offsetY - (float)stepY;
373  float offsetX = (float)xx * (float)sizeX / (float)meshX; // The X that is closest to the mesh point
374  int stepX = (int)offsetX;
375  float fractionalX = offsetX - (float)stepX;
376 
377  // physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,xx={1},yy={2},offX={3},stepX={4},fractX={5},offY={6},stepY={7},fractY={8}",
378  // BSScene.DetailLogZero, xx, yy, offsetX, stepX, fractionalX, offsetY, stepY, fractionalY);
379 
380  // get the four corners of the heightmap square the mesh point is in
381  float heightUL = hmap.GetHeight(stepX , stepY );
382  float heightUR = hmap.GetHeight(stepX + 1, stepY );
383  float heightLL = hmap.GetHeight(stepX , stepY + 1);
384  float heightLR = hmap.GetHeight(stepX + 1, stepY + 1);
385 
386  // bilinear interplolation
387  float height = heightUL * (1 - fractionalX) * (1 - fractionalY)
388  + heightUR * fractionalX * (1 - fractionalY)
389  + heightLL * (1 - fractionalX) * fractionalY
390  + heightLR * fractionalX * fractionalY;
391 
392  // physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,heightUL={1},heightUR={2},heightLL={3},heightLR={4},heightMap={5}",
393  // BSScene.DetailLogZero, heightUL, heightUR, heightLL, heightLR, height);
394 
395  minHeight = Math.Min(minHeight, height);
396 
397  vertices[verticesCount + 0] = (float)xx * meshXStep + extentBase.X;
398  vertices[verticesCount + 1] = (float)yy * meshYStep + extentBase.Y;
399  vertices[verticesCount + 2] = height + extentBase.Z;
400  verticesCount += 3;
401  }
402  }
403  // The number of vertices generated
404  verticesCount /= 3;
405 
406  // Loop through all the heightmap squares and create indices for the two triangles for that square
407  for (int yy = 0; yy < meshY; yy++)
408  {
409  for (int xx = 0; xx < meshX; xx++)
410  {
411  int offset = yy * (meshX + 1) + xx;
412  // Each vertices is presumed to be the upper left corner of a box of two triangles
413  indices[indicesCount + 0] = offset;
414  indices[indicesCount + 1] = offset + 1;
415  indices[indicesCount + 2] = offset + meshX + 1; // accounting for the extra column
416  indices[indicesCount + 3] = offset + 1;
417  indices[indicesCount + 4] = offset + meshX + 2;
418  indices[indicesCount + 5] = offset + meshX + 1;
419  indicesCount += 6;
420  }
421  }
422 
423  ret = true;
424  }
425  catch (Exception e)
426  {
427  if (physicsScene != null)
428  physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
429  LogHeader, physicsScene.RegionName, extentBase, e);
430  }
431 
432  indicesCountO = indicesCount;
433  indicesO = indices;
434  verticesCountO = verticesCount;
435  verticesO = vertices;
436 
437  return ret;
438  }
439 }
440 }
BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
static bool ConvertHeightmapToMesh(BSScene physicsScene, float[] heightMap, int sizeX, int sizeY, Vector3 extentBase, out int indicesCountO, out int[] indicesO, out int verticesCountO, out float[] verticesO)
BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id)
static bool ConvertHeightmapToMesh2(BSScene physicsScene, float[] heightMap, int sizeX, int sizeY, int magnification, Vector3 extent, Vector3 extentBase, out int indicesCountO, out int[] indicesO, out int verticesCountO, out float[] verticesO)
BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, Vector3 minCoords, Vector3 maxCoords)