Browse Source

Make heightmap shape usable from PhysicsServer

- Fixed bad size check
- Fixed bad member initialization
- Removed unused cell_size (Bullet expects us to use localScaling)
- Accept precomputed min/max height, will be calculated if not provided
Marc Gilleron 7 years ago
parent
commit
a66e1af168
2 changed files with 47 additions and 14 deletions
  1. 43 11
      modules/bullet/shape_bullet.cpp
  2. 4 3
      modules/bullet/shape_bullet.h

+ 43 - 11
modules/bullet/shape_bullet.cpp

@@ -125,14 +125,13 @@ btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMes
 	}
 }
 
-btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
 	const btScalar ignoredHeightScale(1);
-	const btScalar fieldHeight(500); // Meters
 	const int YAxis = 1; // 0=X, 1=Y, 2=Z
 	const bool flipQuadEdges = false;
 	const void *heightsPtr = p_heights.read().ptr();
 
-	return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges));
+	return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges));
 }
 
 btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) {
@@ -387,19 +386,44 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
 	Dictionary d = p_data;
 	ERR_FAIL_COND(!d.has("width"));
 	ERR_FAIL_COND(!d.has("depth"));
-	ERR_FAIL_COND(!d.has("cell_size"));
 	ERR_FAIL_COND(!d.has("heights"));
 
+	real_t l_min_height = 0.0;
+	real_t l_max_height = 0.0;
+
+	// If specified, min and max height will be used as precomputed values
+	if (d.has("min_height"))
+		l_min_height = d["min_height"];
+	if (d.has("max_height"))
+		l_max_height = d["max_height"];
+
+	ERR_FAIL_COND(l_min_height > l_max_height);
+
 	int l_width = d["width"];
 	int l_depth = d["depth"];
-	real_t l_cell_size = d["cell_size"];
 	PoolVector<real_t> l_heights = d["heights"];
 
 	ERR_FAIL_COND(l_width <= 0);
 	ERR_FAIL_COND(l_depth <= 0);
-	ERR_FAIL_COND(l_cell_size <= CMP_EPSILON);
-	ERR_FAIL_COND(l_heights.size() != (width * depth));
-	setup(heights, width, depth, cell_size);
+	ERR_FAIL_COND(l_heights.size() != (l_width * l_depth));
+
+	// Compute min and max heights if not specified.
+	if (!d.has("min_height") && !d.has("max_height")) {
+
+		PoolVector<real_t>::Read r = heights.read();
+		int heights_size = heights.size();
+
+		for (int i = 0; i < heights_size; ++i) {
+			real_t h = r[i];
+
+			if (h < l_min_height)
+				l_min_height = h;
+			else if (h > l_max_height)
+				l_max_height = h;
+		}
+	}
+
+	setup(l_heights, l_width, l_depth, l_min_height, l_max_height);
 }
 
 Variant HeightMapShapeBullet::get_data() const {
@@ -410,8 +434,14 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const {
 	return PhysicsServer::SHAPE_HEIGHTMAP;
 }
 
-void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
+	// TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes
+
 	{ // Copy
+
+		// TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields
+		// without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data).
+
 		const int heights_size = p_heights.size();
 		heights.resize(heights_size);
 		PoolVector<real_t>::Read p_heights_r = p_heights.read();
@@ -420,14 +450,16 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int
 			heights_w[i] = p_heights_r[i];
 		}
 	}
+
 	width = p_width;
 	depth = p_depth;
-	cell_size = p_cell_size;
+	min_height = p_min_height;
+	max_height = p_max_height;
 	notifyShapeChanged();
 }
 
 btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
-	btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, cell_size));
+	btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
 	cs->setLocalScaling(p_implicit_scale);
 	prepare(cs);
 	cs->setMargin(p_margin);

+ 4 - 3
modules/bullet/shape_bullet.h

@@ -85,7 +85,7 @@ public:
 	/// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
 	static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
 	static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
-	static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+	static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
 	static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope);
 };
 
@@ -199,7 +199,8 @@ public:
 	PoolVector<real_t> heights;
 	int width;
 	int depth;
-	real_t cell_size;
+	real_t min_height;
+	real_t max_height;
 
 	HeightMapShapeBullet();
 
@@ -209,7 +210,7 @@ public:
 	virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
 
 private:
-	void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+	void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
 };
 
 class RayShapeBullet : public ShapeBullet {