PhysicsRigidBody.cpp 21 KB

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