PhysicsRigidBody.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. #include "Base.h"
  2. #include "Game.h"
  3. #include "Image.h"
  4. #include "PhysicsController.h"
  5. #include "PhysicsMotionState.h"
  6. #include "PhysicsRigidBody.h"
  7. #include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
  8. namespace gameplay
  9. {
  10. // Internal values used for creating mesh, heightfield, and capsule rigid bodies.
  11. #define SHAPE_MESH ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 1))
  12. #define SHAPE_HEIGHTFIELD ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 2))
  13. #define SHAPE_CAPSULE ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 3))
  14. // Helper function for calculating heights from heightmap (image) or heightfield data.
  15. static float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y);
  16. PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass,
  17. float friction, float restitution, float linearDamping, float angularDamping)
  18. : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
  19. _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
  20. _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
  21. {
  22. // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
  23. Vector3 scale;
  24. node->getWorldMatrix().getScale(&scale);
  25. switch (type)
  26. {
  27. case SHAPE_BOX:
  28. {
  29. const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
  30. _shape = Game::getInstance()->getPhysicsController()->createBox(box.min, box.max, scale);
  31. break;
  32. }
  33. case SHAPE_SPHERE:
  34. {
  35. const BoundingSphere& sphere = node->getModel()->getMesh()->getBoundingSphere();
  36. _shape = Game::getInstance()->getPhysicsController()->createSphere(sphere.radius, scale);
  37. break;
  38. }
  39. case SHAPE_MESH:
  40. {
  41. _shape = Game::getInstance()->getPhysicsController()->createMesh(this, scale);
  42. break;
  43. }
  44. }
  45. // Use the center of the bounding sphere as the center of mass offset.
  46. Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
  47. c.x *= scale.x;
  48. c.y *= scale.y;
  49. c.z *= scale.z;
  50. c.negate();
  51. // Create the Bullet rigid body (we don't apply center of mass offsets on mesh rigid bodies).
  52. if (c.lengthSquared() > MATH_EPSILON && type != SHAPE_MESH)
  53. _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
  54. else
  55. _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
  56. // Add the rigid body to the physics world.
  57. Game::getInstance()->getPhysicsController()->addRigidBody(this);
  58. }
  59. PhysicsRigidBody::PhysicsRigidBody(Node* node, Image* image, float mass,
  60. float friction, float restitution, float linearDamping, float angularDamping)
  61. : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
  62. _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
  63. _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
  64. {
  65. // Get the width, length and minimum and maximum height of the heightfield.
  66. const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
  67. float width = box.max.x - box.min.x;
  68. float minHeight = box.min.y;
  69. float maxHeight = box.max.y;
  70. float length = box.max.z - box.min.z;
  71. // Get the size in bytes of a pixel (we ensure that the image's
  72. // pixel format is actually supported before calling this constructor).
  73. unsigned int pixelSize = 0;
  74. switch (image->getFormat())
  75. {
  76. case Image::RGB:
  77. pixelSize = 3;
  78. break;
  79. case Image::RGBA:
  80. pixelSize = 4;
  81. break;
  82. }
  83. // Calculate the heights for each pixel.
  84. float* data = new float[image->getWidth() * image->getHeight()];
  85. for (unsigned int x = 0; x < image->getWidth(); x++)
  86. {
  87. for (unsigned int y = 0; y < image->getHeight(); y++)
  88. {
  89. data[x + y * image->getWidth()] = ((((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 0]) +
  90. ((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 1]) +
  91. ((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 2])) / 768.0f) * (maxHeight - minHeight) + minHeight;
  92. }
  93. }
  94. // Generate the heightmap data needed for physics (one height per world unit).
  95. unsigned int sizeWidth = width;
  96. unsigned int sizeHeight = length;
  97. _width = sizeWidth + 1;
  98. _height = sizeHeight + 1;
  99. _heightfieldData = new float[_width * _height];
  100. unsigned int heightIndex = 0;
  101. float widthImageFactor = (float)(image->getWidth() - 1) / sizeWidth;
  102. float heightImageFactor = (float)(image->getHeight() - 1) / sizeHeight;
  103. float x = 0.0f;
  104. float z = 0.0f;
  105. for (unsigned int row = 0, z = 0.0f; row <= sizeHeight; row++, z += 1.0f)
  106. {
  107. for (unsigned int col = 0, x = 0.0f; col <= sizeWidth; col++, x += 1.0f)
  108. {
  109. heightIndex = row * _width + col;
  110. _heightfieldData[heightIndex] = calculateHeight(data, image->getWidth(), image->getHeight(), x * widthImageFactor, (sizeHeight - z) * heightImageFactor);
  111. }
  112. }
  113. SAFE_DELETE_ARRAY(data);
  114. // Create the heightfield collision shape.
  115. _shape = bullet_new<btHeightfieldTerrainShape>(_width, _height, _heightfieldData,
  116. 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);
  117. // Offset the heightmap's center of mass according to the way that Bullet calculates the origin
  118. // of its heightfield collision shape; see documentation for the btHeightfieldTerrainShape for more info.
  119. Vector3 s;
  120. node->getWorldMatrix().getScale(&s);
  121. Vector3 c (0.0f, -(maxHeight - (0.5f * (maxHeight - minHeight))) / s.y, 0.0f);
  122. // Create the Bullet rigid body.
  123. if (c.lengthSquared() > MATH_EPSILON)
  124. _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
  125. else
  126. _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
  127. // Add the rigid body to the physics world.
  128. Game::getInstance()->getPhysicsController()->addRigidBody(this);
  129. // Add the rigid body as a listener on the node's transform.
  130. _node->addListener(this);
  131. }
  132. PhysicsRigidBody::PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction,
  133. float restitution, float linearDamping, float angularDamping)
  134. : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
  135. _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
  136. _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
  137. {
  138. // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
  139. Vector3 scale;
  140. node->getWorldMatrix().getScale(&scale);
  141. // Create the capsule collision shape.
  142. _shape = Game::getInstance()->getPhysicsController()->createCapsule(radius, height);
  143. // Use the center of the bounding sphere as the center of mass offset.
  144. Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
  145. c.x *= scale.x;
  146. c.y *= scale.y;
  147. c.z *= scale.z;
  148. c.negate();
  149. // Create the Bullet rigid body.
  150. if (c.lengthSquared() > MATH_EPSILON)
  151. _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
  152. else
  153. _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
  154. // Add the rigid body to the physics world.
  155. Game::getInstance()->getPhysicsController()->addRigidBody(this);
  156. }
  157. PhysicsRigidBody::~PhysicsRigidBody()
  158. {
  159. // Clean up all constraints linked to this rigid body.
  160. PhysicsConstraint* ptr = NULL;
  161. while (_constraints.size() > 0)
  162. {
  163. ptr = _constraints.back();
  164. _constraints.pop_back();
  165. SAFE_DELETE(ptr);
  166. }
  167. // Clean up the rigid body and its related objects.
  168. if (_body)
  169. {
  170. if (_body->getMotionState())
  171. delete _body->getMotionState();
  172. Game::getInstance()->getPhysicsController()->removeRigidBody(this);
  173. SAFE_DELETE(_body);
  174. }
  175. SAFE_DELETE(_listeners);
  176. SAFE_DELETE(_angularVelocity);
  177. SAFE_DELETE(_anisotropicFriction);
  178. SAFE_DELETE(_gravity);
  179. SAFE_DELETE(_linearVelocity);
  180. SAFE_DELETE_ARRAY(_vertexData);
  181. for (unsigned int i = 0; i < _indexData.size(); i++)
  182. {
  183. SAFE_DELETE_ARRAY(_indexData[i]);
  184. }
  185. SAFE_DELETE_ARRAY(_heightfieldData);
  186. SAFE_DELETE(_inverse);
  187. }
  188. void PhysicsRigidBody::addCollisionListener(Listener* listener, PhysicsRigidBody* body)
  189. {
  190. Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, body);
  191. }
  192. void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
  193. {
  194. // If the force is significant enough, activate the rigid body
  195. // to make sure that it isn't sleeping and apply the force.
  196. if (force.lengthSquared() > MATH_EPSILON)
  197. {
  198. _body->activate();
  199. if (relativePosition)
  200. _body->applyForce(BV(force), BV(*relativePosition));
  201. else
  202. _body->applyCentralForce(BV(force));
  203. }
  204. }
  205. void PhysicsRigidBody::applyImpulse(const Vector3& impulse, const Vector3* relativePosition)
  206. {
  207. // If the impulse is significant enough, activate the rigid body
  208. // to make sure that it isn't sleeping and apply the impulse.
  209. if (impulse.lengthSquared() > MATH_EPSILON)
  210. {
  211. _body->activate();
  212. if (relativePosition)
  213. {
  214. _body->applyImpulse(BV(impulse), BV(*relativePosition));
  215. }
  216. else
  217. _body->applyCentralImpulse(BV(impulse));
  218. }
  219. }
  220. void PhysicsRigidBody::applyTorque(const Vector3& torque)
  221. {
  222. // If the torque is significant enough, activate the rigid body
  223. // to make sure that it isn't sleeping and apply the torque.
  224. if (torque.lengthSquared() > MATH_EPSILON)
  225. {
  226. _body->activate();
  227. _body->applyTorque(BV(torque));
  228. }
  229. }
  230. void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
  231. {
  232. // If the torque impulse is significant enough, activate the rigid body
  233. // to make sure that it isn't sleeping and apply the torque impulse.
  234. if (torque.lengthSquared() > MATH_EPSILON)
  235. {
  236. _body->activate();
  237. _body->applyTorqueImpulse(BV(torque));
  238. }
  239. }
  240. bool PhysicsRigidBody::collidesWith(PhysicsRigidBody* body)
  241. {
  242. static CollidesWithCallback callback;
  243. callback.result = false;
  244. Game::getInstance()->getPhysicsController()->_world->contactPairTest(_body, body->_body, callback);
  245. return callback.result;
  246. }
  247. PhysicsRigidBody* PhysicsRigidBody::create(Node* node, const char* filePath)
  248. {
  249. assert(filePath);
  250. // Load the rigid body properties from file.
  251. Properties* properties = Properties::create(filePath);
  252. assert(properties);
  253. if (properties == NULL)
  254. {
  255. WARN_VARG("Failed to load rigid body file: %s", filePath);
  256. return NULL;
  257. }
  258. PhysicsRigidBody* body = create(node, properties->getNextNamespace());
  259. SAFE_DELETE(properties);
  260. return body;
  261. }
  262. PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
  263. {
  264. // Check if the properties is valid and has a valid namespace.
  265. assert(properties);
  266. if (!properties || !(strcmp(properties->getNamespace(), "rigidbody") == 0))
  267. {
  268. WARN("Failed to load rigid body from properties object: must be non-null object and have namespace equal to \'rigidbody\'.");
  269. return NULL;
  270. }
  271. // Set values to their defaults.
  272. PhysicsRigidBody::Type type = PhysicsRigidBody::SHAPE_NONE;
  273. float mass = 0.0;
  274. float friction = 0.5;
  275. float restitution = 0.0;
  276. float linearDamping = 0.0;
  277. float angularDamping = 0.0;
  278. bool kinematic = false;
  279. Vector3* gravity = NULL;
  280. Vector3* anisotropicFriction = NULL;
  281. const char* imagePath = NULL;
  282. float radius = -1.0f;
  283. float height = -1.0f;
  284. // Load the defined properties.
  285. properties->rewind();
  286. const char* name;
  287. while (name = properties->getNextProperty())
  288. {
  289. if (strcmp(name, "type") == 0)
  290. {
  291. std::string typeStr = properties->getString();
  292. if (typeStr == "BOX")
  293. type = SHAPE_BOX;
  294. else if (typeStr == "SPHERE")
  295. type = SHAPE_SPHERE;
  296. else if (typeStr == "MESH")
  297. type = SHAPE_MESH;
  298. else if (typeStr == "HEIGHTFIELD")
  299. type = SHAPE_HEIGHTFIELD;
  300. else if (typeStr == "CAPSULE")
  301. type = SHAPE_CAPSULE;
  302. else
  303. {
  304. WARN_VARG("Could not create rigid body; unsupported value for rigid body type: '%s'.", typeStr.c_str());
  305. return NULL;
  306. }
  307. }
  308. else if (strcmp(name, "mass") == 0)
  309. {
  310. mass = properties->getFloat();
  311. }
  312. else if (strcmp(name, "friction") == 0)
  313. {
  314. friction = properties->getFloat();
  315. }
  316. else if (strcmp(name, "restitution") == 0)
  317. {
  318. restitution = properties->getFloat();
  319. }
  320. else if (strcmp(name, "linearDamping") == 0)
  321. {
  322. linearDamping = properties->getFloat();
  323. }
  324. else if (strcmp(name, "angularDamping") == 0)
  325. {
  326. angularDamping = properties->getFloat();
  327. }
  328. else if (strcmp(name, "kinematic") == 0)
  329. {
  330. kinematic = properties->getBool();
  331. }
  332. else if (strcmp(name, "gravity") == 0)
  333. {
  334. gravity = new Vector3();
  335. properties->getVector3(NULL, gravity);
  336. }
  337. else if (strcmp(name, "anisotropicFriction") == 0)
  338. {
  339. anisotropicFriction = new Vector3();
  340. properties->getVector3(NULL, anisotropicFriction);
  341. }
  342. else if (strcmp(name, "image") == 0)
  343. {
  344. imagePath = properties->getString();
  345. }
  346. else if (strcmp(name, "radius") == 0)
  347. {
  348. radius = properties->getFloat();
  349. }
  350. else if (strcmp(name, "height") == 0)
  351. {
  352. height = properties->getFloat();
  353. }
  354. }
  355. // If the rigid body type is equal to mesh, check that the node's mesh's primitive type is supported.
  356. if (type == SHAPE_MESH)
  357. {
  358. Mesh* mesh = node->getModel()->getMesh();
  359. switch (mesh->getPrimitiveType())
  360. {
  361. case Mesh::TRIANGLES:
  362. break;
  363. case Mesh::LINES:
  364. case Mesh::LINE_STRIP:
  365. case Mesh::POINTS:
  366. case Mesh::TRIANGLE_STRIP:
  367. WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
  368. SAFE_DELETE(gravity);
  369. SAFE_DELETE(anisotropicFriction);
  370. return NULL;
  371. }
  372. }
  373. // Create the rigid body.
  374. PhysicsRigidBody* body = NULL;
  375. switch (type)
  376. {
  377. case SHAPE_HEIGHTFIELD:
  378. if (imagePath == NULL)
  379. {
  380. WARN("Heightfield rigid body requires an image path.");
  381. }
  382. else
  383. {
  384. // Load the image data from the given file path.
  385. Image* image = Image::create(imagePath);
  386. if (!image)
  387. return NULL;
  388. // Ensure that the image's pixel format is supported.
  389. switch (image->getFormat())
  390. {
  391. case Image::RGB:
  392. case Image::RGBA:
  393. break;
  394. default:
  395. WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
  396. return NULL;
  397. }
  398. body = new PhysicsRigidBody(node, image, mass, friction, restitution, linearDamping, angularDamping);
  399. SAFE_RELEASE(image);
  400. }
  401. break;
  402. case SHAPE_CAPSULE:
  403. if (radius == -1.0f || height == -1.0f)
  404. {
  405. WARN("Both 'radius' and 'height' must be specified for a capsule rigid body.");
  406. }
  407. else
  408. {
  409. body = new PhysicsRigidBody(node, radius, height, mass, friction, restitution, linearDamping, angularDamping);
  410. }
  411. break;
  412. default:
  413. body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
  414. break;
  415. }
  416. // Set any initially defined properties.
  417. if (kinematic)
  418. body->setKinematic(kinematic);
  419. if (gravity)
  420. body->setGravity(*gravity);
  421. if (anisotropicFriction)
  422. body->setAnisotropicFriction(*anisotropicFriction);
  423. // Clean up any loaded properties that are on the heap.
  424. SAFE_DELETE(gravity);
  425. SAFE_DELETE(anisotropicFriction);
  426. return body;
  427. }
  428. float PhysicsRigidBody::getHeight(float x, float y) const
  429. {
  430. // This function is only supported for heightfield rigid bodies.
  431. if (_shape->getShapeType() != TERRAIN_SHAPE_PROXYTYPE)
  432. {
  433. WARN("Attempting to get the height of a non-heightfield rigid body.");
  434. return 0.0f;
  435. }
  436. // Calculate the correct x, y position relative to the heightfield data.
  437. if (_inverseIsDirty)
  438. {
  439. if (_inverse == NULL)
  440. _inverse = new Matrix();
  441. _node->getWorldMatrix().invert(_inverse);
  442. _inverseIsDirty = false;
  443. }
  444. Vector3 v = (*_inverse) * Vector3(x, 0.0f, y);
  445. x = (v.x + (0.5f * (_width - 1))) * _width / (_width - 1);
  446. y = (v.z + (0.5f * (_height - 1))) * _height / (_height - 1);
  447. // Check that the x, y position is within the bounds.
  448. if (x < 0.0f || x > _width || y < 0.0f || y > _height)
  449. {
  450. WARN_VARG("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, _width, _height);
  451. return 0.0f;
  452. }
  453. return calculateHeight(_heightfieldData, _width, _height, x, y);
  454. }
  455. btRigidBody* PhysicsRigidBody::createRigidBodyInternal(btCollisionShape* shape, float mass, Node* node,
  456. float friction, float restitution, float linearDamping, float angularDamping,
  457. const Vector3* centerOfMassOffset)
  458. {
  459. // If the mass is non-zero, then the object is dynamic so we calculate the local
  460. // inertia. However, if the collision shape is a triangle mesh, we don't calculate
  461. // inertia since Bullet doesn't currently support this.
  462. btVector3 localInertia(0.0, 0.0, 0.0);
  463. if (mass != 0.0 && shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE)
  464. shape->calculateLocalInertia(mass, localInertia);
  465. // Create the Bullet physics rigid body object.
  466. PhysicsMotionState* motionState = new PhysicsMotionState(node, centerOfMassOffset);
  467. btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, motionState, shape, localInertia);
  468. rbInfo.m_friction = friction;
  469. rbInfo.m_restitution = restitution;
  470. rbInfo.m_linearDamping = linearDamping;
  471. rbInfo.m_angularDamping = angularDamping;
  472. btRigidBody* body = bullet_new<btRigidBody>(rbInfo);
  473. return body;
  474. }
  475. void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
  476. {
  477. _constraints.push_back(constraint);
  478. }
  479. void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
  480. {
  481. for (unsigned int i = 0; i < _constraints.size(); i++)
  482. {
  483. if (_constraints[i] == constraint)
  484. {
  485. _constraints.erase(_constraints.begin() + i);
  486. break;
  487. }
  488. }
  489. }
  490. bool PhysicsRigidBody::supportsConstraints()
  491. {
  492. return _shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE && _shape->getShapeType() != TERRAIN_SHAPE_PROXYTYPE;
  493. }
  494. void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
  495. {
  496. _inverseIsDirty = true;
  497. }
  498. PhysicsRigidBody::CollisionPair::CollisionPair(PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
  499. : _rbA(rbA), _rbB(rbB)
  500. {
  501. // Unused
  502. }
  503. PhysicsRigidBody::Listener::~Listener()
  504. {
  505. // Unused
  506. }
  507. btScalar PhysicsRigidBody::CollidesWithCallback::addSingleResult(btManifoldPoint& cp,
  508. const btCollisionObject* a, int partIdA, int indexA,
  509. const btCollisionObject* b, int partIdB, int indexB)
  510. {
  511. result = true;
  512. return 0.0f;
  513. }
  514. float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
  515. {
  516. unsigned int x1 = x;
  517. unsigned int y1 = y;
  518. unsigned int x2 = x1 + 1;
  519. unsigned int y2 = y1 + 1;
  520. float tmp;
  521. float xFactor = modf(x, &tmp);
  522. float yFactor = modf(y, &tmp);
  523. float xFactorI = 1.0f - xFactor;
  524. float yFactorI = 1.0f - yFactor;
  525. if (x2 >= width && y2 >= height)
  526. {
  527. return data[x1 + y1 * width];
  528. }
  529. else if (x2 >= width)
  530. {
  531. return data[x1 + y1 * width] * yFactorI + data[x1 + y2 * width] * yFactor;
  532. }
  533. else if (y2 >= height)
  534. {
  535. return data[x1 + y1 * width] * xFactorI + data[x2 + y1 * width] * xFactor;
  536. }
  537. else
  538. {
  539. return data[x1 + y1 * width] * xFactorI * yFactorI + data[x1 + y2 * width] * xFactorI * yFactor +
  540. data[x2 + y2 * width] * xFactor * yFactor + data[x2 + y1 * width] * xFactor * yFactorI;
  541. }
  542. }
  543. }