mesh_storage.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /**************************************************************************/
  2. /* mesh_storage.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "mesh_storage.h"
  31. #include "core/math/transform_interpolator.h"
  32. #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
  33. #include "core/config/project_settings.h"
  34. #endif
  35. RID RendererMeshStorage::multimesh_allocate() {
  36. return _multimesh_allocate();
  37. }
  38. void RendererMeshStorage::multimesh_initialize(RID p_rid) {
  39. _multimesh_initialize(p_rid);
  40. }
  41. void RendererMeshStorage::multimesh_free(RID p_rid) {
  42. _multimesh_free(p_rid);
  43. }
  44. void RendererMeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data, bool p_use_indirect) {
  45. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  46. if (mmi) {
  47. mmi->_transform_format = p_transform_format;
  48. mmi->_use_colors = p_use_colors;
  49. mmi->_use_custom_data = p_use_custom_data;
  50. mmi->_num_instances = p_instances;
  51. mmi->_vf_size_xform = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12;
  52. mmi->_vf_size_color = p_use_colors ? 4 : 0;
  53. mmi->_vf_size_data = p_use_custom_data ? 4 : 0;
  54. mmi->_stride = mmi->_vf_size_xform + mmi->_vf_size_color + mmi->_vf_size_data;
  55. int size_in_floats = p_instances * mmi->_stride;
  56. mmi->_data_curr.resize_initialized(size_in_floats);
  57. mmi->_data_prev.resize_initialized(size_in_floats);
  58. mmi->_data_interpolated.resize_initialized(size_in_floats);
  59. }
  60. _multimesh_allocate_data(p_multimesh, p_instances, p_transform_format, p_use_colors, p_use_custom_data, p_use_indirect);
  61. }
  62. int RendererMeshStorage::multimesh_get_instance_count(RID p_multimesh) const {
  63. return _multimesh_get_instance_count(p_multimesh);
  64. }
  65. void RendererMeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
  66. _multimesh_set_mesh(p_multimesh, p_mesh);
  67. }
  68. void RendererMeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) {
  69. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  70. if (mmi && mmi->interpolated) {
  71. ERR_FAIL_COND(p_index >= mmi->_num_instances);
  72. ERR_FAIL_COND(mmi->_vf_size_xform != 12);
  73. int start = p_index * mmi->_stride;
  74. float *ptr = mmi->_data_curr.ptrw();
  75. ptr += start;
  76. const Transform3D &t = p_transform;
  77. ptr[0] = t.basis.rows[0][0];
  78. ptr[1] = t.basis.rows[0][1];
  79. ptr[2] = t.basis.rows[0][2];
  80. ptr[3] = t.origin.x;
  81. ptr[4] = t.basis.rows[1][0];
  82. ptr[5] = t.basis.rows[1][1];
  83. ptr[6] = t.basis.rows[1][2];
  84. ptr[7] = t.origin.y;
  85. ptr[8] = t.basis.rows[2][0];
  86. ptr[9] = t.basis.rows[2][1];
  87. ptr[10] = t.basis.rows[2][2];
  88. ptr[11] = t.origin.z;
  89. _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
  90. #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
  91. if (!Engine::get_singleton()->is_in_physics_frame()) {
  92. PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues");
  93. }
  94. #endif
  95. return;
  96. }
  97. _multimesh_instance_set_transform(p_multimesh, p_index, p_transform);
  98. }
  99. void RendererMeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
  100. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  101. if (mmi && mmi->interpolated) {
  102. ERR_FAIL_COND(p_index >= mmi->_num_instances);
  103. ERR_FAIL_COND(mmi->_vf_size_xform != 8);
  104. int start = p_index * mmi->_stride;
  105. float *ptr = mmi->_data_curr.ptrw();
  106. ptr += start;
  107. const Transform2D &t = p_transform;
  108. ptr[0] = t.columns[0][0];
  109. ptr[1] = t.columns[1][0];
  110. ptr[2] = 0;
  111. ptr[3] = t.columns[2][0];
  112. ptr[4] = t.columns[0][1];
  113. ptr[5] = t.columns[1][1];
  114. ptr[6] = 0;
  115. ptr[7] = t.columns[2][1];
  116. _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
  117. #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
  118. if (!Engine::get_singleton()->is_in_physics_frame()) {
  119. PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues");
  120. }
  121. #endif
  122. return;
  123. }
  124. _multimesh_instance_set_transform_2d(p_multimesh, p_index, p_transform);
  125. }
  126. void RendererMeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
  127. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  128. if (mmi && mmi->interpolated) {
  129. ERR_FAIL_COND(p_index >= mmi->_num_instances);
  130. ERR_FAIL_COND(mmi->_vf_size_color == 0);
  131. int start = (p_index * mmi->_stride) + mmi->_vf_size_xform;
  132. float *ptr = mmi->_data_curr.ptrw();
  133. ptr += start;
  134. if (mmi->_vf_size_color == 4) {
  135. for (int n = 0; n < 4; n++) {
  136. ptr[n] = p_color.components[n];
  137. }
  138. } else {
  139. #ifdef DEV_ENABLED
  140. // The options are currently 4 or zero, but just in case this changes in future...
  141. ERR_FAIL_COND(mmi->_vf_size_color != 0);
  142. #endif
  143. }
  144. _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
  145. return;
  146. }
  147. _multimesh_instance_set_color(p_multimesh, p_index, p_color);
  148. }
  149. void RendererMeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
  150. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  151. if (mmi && mmi->interpolated) {
  152. ERR_FAIL_COND(p_index >= mmi->_num_instances);
  153. ERR_FAIL_COND(mmi->_vf_size_data == 0);
  154. int start = (p_index * mmi->_stride) + mmi->_vf_size_xform + mmi->_vf_size_color;
  155. float *ptr = mmi->_data_curr.ptrw();
  156. ptr += start;
  157. if (mmi->_vf_size_data == 4) {
  158. for (int n = 0; n < 4; n++) {
  159. ptr[n] = p_color.components[n];
  160. }
  161. } else {
  162. #ifdef DEV_ENABLED
  163. // The options are currently 4 or zero, but just in case this changes in future...
  164. ERR_FAIL_COND(mmi->_vf_size_data != 0);
  165. #endif
  166. }
  167. _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
  168. return;
  169. }
  170. _multimesh_instance_set_custom_data(p_multimesh, p_index, p_color);
  171. }
  172. void RendererMeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
  173. _multimesh_set_custom_aabb(p_multimesh, p_aabb);
  174. }
  175. AABB RendererMeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
  176. return _multimesh_get_custom_aabb(p_multimesh);
  177. }
  178. RID RendererMeshStorage::multimesh_get_mesh(RID p_multimesh) const {
  179. return _multimesh_get_mesh(p_multimesh);
  180. }
  181. Transform3D RendererMeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
  182. return _multimesh_instance_get_transform(p_multimesh, p_index);
  183. }
  184. Transform2D RendererMeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
  185. return _multimesh_instance_get_transform_2d(p_multimesh, p_index);
  186. }
  187. Color RendererMeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const {
  188. return _multimesh_instance_get_color(p_multimesh, p_index);
  189. }
  190. Color RendererMeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
  191. return _multimesh_instance_get_custom_data(p_multimesh, p_index);
  192. }
  193. void RendererMeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
  194. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  195. if (mmi && mmi->interpolated) {
  196. ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size()));
  197. mmi->_data_curr = p_buffer;
  198. _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
  199. #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
  200. if (!Engine::get_singleton()->is_in_physics_frame()) {
  201. PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues");
  202. }
  203. #endif
  204. return;
  205. }
  206. _multimesh_set_buffer(p_multimesh, p_buffer);
  207. }
  208. RID RendererMeshStorage::multimesh_get_command_buffer_rd_rid(RID p_multimesh) const {
  209. return _multimesh_get_command_buffer_rd_rid(p_multimesh);
  210. }
  211. RID RendererMeshStorage::multimesh_get_buffer_rd_rid(RID p_multimesh) const {
  212. return _multimesh_get_buffer_rd_rid(p_multimesh);
  213. }
  214. Vector<float> RendererMeshStorage::multimesh_get_buffer(RID p_multimesh) const {
  215. return _multimesh_get_buffer(p_multimesh);
  216. }
  217. void RendererMeshStorage::multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer, const Vector<float> &p_buffer_prev) {
  218. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  219. if (mmi) {
  220. ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer for current frame should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size()));
  221. ERR_FAIL_COND_MSG(p_buffer_prev.size() != mmi->_data_prev.size(), vformat("Buffer for previous frame should have %d elements, got %d instead.", mmi->_data_prev.size(), p_buffer_prev.size()));
  222. // We are assuming that mmi->interpolated is the case. (Can possibly assert this?)
  223. // Even if this flag hasn't been set - just calling this function suggests interpolation is desired.
  224. mmi->_data_prev = p_buffer_prev;
  225. mmi->_data_curr = p_buffer;
  226. _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
  227. #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
  228. if (!Engine::get_singleton()->is_in_physics_frame()) {
  229. PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues");
  230. }
  231. #endif
  232. }
  233. }
  234. void RendererMeshStorage::multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated) {
  235. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  236. if (mmi) {
  237. if (p_interpolated == mmi->interpolated) {
  238. return;
  239. }
  240. mmi->interpolated = p_interpolated;
  241. // If we are turning on physics interpolation, as a convenience,
  242. // we want to get the current buffer data from the backend,
  243. // and reset all the instances.
  244. if (p_interpolated) {
  245. mmi->_data_curr = _multimesh_get_buffer(p_multimesh);
  246. mmi->_data_prev = mmi->_data_curr;
  247. mmi->_data_interpolated = mmi->_data_curr;
  248. }
  249. }
  250. }
  251. void RendererMeshStorage::multimesh_set_physics_interpolation_quality(RID p_multimesh, RS::MultimeshPhysicsInterpolationQuality p_quality) {
  252. ERR_FAIL_COND((p_quality < 0) || (p_quality > 1));
  253. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  254. if (mmi) {
  255. mmi->quality = (int)p_quality;
  256. }
  257. }
  258. void RendererMeshStorage::multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index) {
  259. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  260. if (mmi) {
  261. ERR_FAIL_INDEX(p_index, mmi->_num_instances);
  262. float *w = mmi->_data_prev.ptrw();
  263. const float *r = mmi->_data_curr.ptr();
  264. int start = p_index * mmi->_stride;
  265. for (int n = 0; n < mmi->_stride; n++) {
  266. w[start + n] = r[start + n];
  267. }
  268. }
  269. }
  270. void RendererMeshStorage::multimesh_instances_reset_physics_interpolation(RID p_multimesh) {
  271. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh);
  272. if (mmi && mmi->_data_curr.size()) {
  273. // We don't want to invoke COW here, so copy the data directly.
  274. ERR_FAIL_COND(mmi->_data_prev.size() != mmi->_data_curr.size());
  275. float *w = mmi->_data_prev.ptrw();
  276. const float *r = mmi->_data_curr.ptr();
  277. memcpy(w, r, sizeof(float) * mmi->_data_curr.size());
  278. }
  279. }
  280. void RendererMeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
  281. return _multimesh_set_visible_instances(p_multimesh, p_visible);
  282. }
  283. int RendererMeshStorage::multimesh_get_visible_instances(RID p_multimesh) const {
  284. return _multimesh_get_visible_instances(p_multimesh);
  285. }
  286. AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) {
  287. return _multimesh_get_aabb(p_multimesh);
  288. }
  289. void RendererMeshStorage::_multimesh_add_to_interpolation_lists(RID p_multimesh, MultiMeshInterpolator &r_mmi) {
  290. if (!r_mmi.on_interpolate_update_list) {
  291. r_mmi.on_interpolate_update_list = true;
  292. _interpolation_data.multimesh_interpolate_update_list.push_back(p_multimesh);
  293. }
  294. if (!r_mmi.on_transform_update_list) {
  295. r_mmi.on_transform_update_list = true;
  296. _interpolation_data.multimesh_transform_update_list_curr->push_back(p_multimesh);
  297. }
  298. }
  299. void RendererMeshStorage::InterpolationData::notify_free_multimesh(RID p_rid) {
  300. // If the instance was on any of the lists, remove.
  301. multimesh_interpolate_update_list.erase_multiple_unordered(p_rid);
  302. multimesh_transform_update_lists[0].erase_multiple_unordered(p_rid);
  303. multimesh_transform_update_lists[1].erase_multiple_unordered(p_rid);
  304. }
  305. void RendererMeshStorage::update_interpolation_tick(bool p_process) {
  306. // Detect any that were on the previous transform list that are no longer active,
  307. // we should remove them from the interpolate list.
  308. for (unsigned int n = 0; n < _interpolation_data.multimesh_transform_update_list_prev->size(); n++) {
  309. const RID &rid = (*_interpolation_data.multimesh_transform_update_list_prev)[n];
  310. bool active = true;
  311. // No longer active? (Either the instance deleted or no longer being transformed.)
  312. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid);
  313. if (mmi && !mmi->on_transform_update_list) {
  314. active = false;
  315. mmi->on_interpolate_update_list = false;
  316. // Make sure the most recent transform is set...
  317. mmi->_data_interpolated = mmi->_data_curr; // TODO: Copy data rather than use Packed = function?
  318. // ... and that both prev and current are the same, just in case of any interpolations.
  319. mmi->_data_prev = mmi->_data_curr;
  320. // Update the actual stable buffer to the backend.
  321. _multimesh_set_buffer(rid, mmi->_data_interpolated);
  322. }
  323. if (!mmi) {
  324. active = false;
  325. }
  326. if (!active) {
  327. _interpolation_data.multimesh_interpolate_update_list.erase(rid);
  328. }
  329. }
  330. if (p_process) {
  331. for (unsigned int i = 0; i < _interpolation_data.multimesh_transform_update_list_curr->size(); i++) {
  332. const RID &rid = (*_interpolation_data.multimesh_transform_update_list_curr)[i];
  333. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid);
  334. if (mmi) {
  335. // Reset for next tick.
  336. mmi->on_transform_update_list = false;
  337. mmi->_data_prev = mmi->_data_curr;
  338. }
  339. }
  340. }
  341. // If any have left the transform list, remove from the interpolate list.
  342. // We maintain a mirror list for the transform updates, so we can detect when an instance
  343. // is no longer being transformed, and remove it from the interpolate list.
  344. SWAP(_interpolation_data.multimesh_transform_update_list_curr, _interpolation_data.multimesh_transform_update_list_prev);
  345. // Prepare for the next iteration.
  346. _interpolation_data.multimesh_transform_update_list_curr->clear();
  347. }
  348. void RendererMeshStorage::update_interpolation_frame(bool p_process) {
  349. if (p_process) {
  350. // Only need 32 bits for interpolation, don't use real_t.
  351. float f = Engine::get_singleton()->get_physics_interpolation_fraction();
  352. for (unsigned int c = 0; c < _interpolation_data.multimesh_interpolate_update_list.size(); c++) {
  353. const RID &rid = _interpolation_data.multimesh_interpolate_update_list[c];
  354. // We could use the TransformInterpolator here to slerp transforms, but that might be too expensive,
  355. // so just using a Basis lerp for now.
  356. MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid);
  357. if (mmi) {
  358. // Make sure arrays are the correct size.
  359. DEV_ASSERT(mmi->_data_prev.size() == mmi->_data_curr.size());
  360. if (mmi->_data_interpolated.size() < mmi->_data_curr.size()) {
  361. mmi->_data_interpolated.resize(mmi->_data_curr.size());
  362. }
  363. DEV_ASSERT(mmi->_data_interpolated.size() >= mmi->_data_curr.size());
  364. DEV_ASSERT((mmi->_data_curr.size() % mmi->_stride) == 0);
  365. int num = mmi->_data_curr.size() / mmi->_stride;
  366. const float *pf_prev = mmi->_data_prev.ptr();
  367. const float *pf_curr = mmi->_data_curr.ptr();
  368. float *pf_int = mmi->_data_interpolated.ptrw();
  369. bool use_lerp = mmi->quality == 0;
  370. // Temporary transform (needed for swizzling).
  371. Transform3D tp, tc, tr; // (transform prev, curr and result)
  372. // Test for cache friendliness versus doing branchless.
  373. for (int n = 0; n < num; n++) {
  374. // Transform.
  375. if (use_lerp) {
  376. for (int i = 0; i < mmi->_vf_size_xform; i++) {
  377. pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f);
  378. }
  379. } else {
  380. // Silly swizzling, this will slow things down.
  381. // No idea why it is using this format...
  382. // ... maybe due to the shader.
  383. tp.basis.rows[0][0] = pf_prev[0];
  384. tp.basis.rows[0][1] = pf_prev[1];
  385. tp.basis.rows[0][2] = pf_prev[2];
  386. tp.basis.rows[1][0] = pf_prev[4];
  387. tp.basis.rows[1][1] = pf_prev[5];
  388. tp.basis.rows[1][2] = pf_prev[6];
  389. tp.basis.rows[2][0] = pf_prev[8];
  390. tp.basis.rows[2][1] = pf_prev[9];
  391. tp.basis.rows[2][2] = pf_prev[10];
  392. tp.origin.x = pf_prev[3];
  393. tp.origin.y = pf_prev[7];
  394. tp.origin.z = pf_prev[11];
  395. tc.basis.rows[0][0] = pf_curr[0];
  396. tc.basis.rows[0][1] = pf_curr[1];
  397. tc.basis.rows[0][2] = pf_curr[2];
  398. tc.basis.rows[1][0] = pf_curr[4];
  399. tc.basis.rows[1][1] = pf_curr[5];
  400. tc.basis.rows[1][2] = pf_curr[6];
  401. tc.basis.rows[2][0] = pf_curr[8];
  402. tc.basis.rows[2][1] = pf_curr[9];
  403. tc.basis.rows[2][2] = pf_curr[10];
  404. tc.origin.x = pf_curr[3];
  405. tc.origin.y = pf_curr[7];
  406. tc.origin.z = pf_curr[11];
  407. TransformInterpolator::interpolate_transform_3d(tp, tc, tr, f);
  408. pf_int[0] = tr.basis.rows[0][0];
  409. pf_int[1] = tr.basis.rows[0][1];
  410. pf_int[2] = tr.basis.rows[0][2];
  411. pf_int[4] = tr.basis.rows[1][0];
  412. pf_int[5] = tr.basis.rows[1][1];
  413. pf_int[6] = tr.basis.rows[1][2];
  414. pf_int[8] = tr.basis.rows[2][0];
  415. pf_int[9] = tr.basis.rows[2][1];
  416. pf_int[10] = tr.basis.rows[2][2];
  417. pf_int[3] = tr.origin.x;
  418. pf_int[7] = tr.origin.y;
  419. pf_int[11] = tr.origin.z;
  420. }
  421. pf_prev += mmi->_vf_size_xform;
  422. pf_curr += mmi->_vf_size_xform;
  423. pf_int += mmi->_vf_size_xform;
  424. // Color.
  425. if (mmi->_vf_size_color == 4) {
  426. for (int i = 0; i < 4; i++) {
  427. pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f);
  428. }
  429. pf_prev += 4;
  430. pf_curr += 4;
  431. pf_int += 4;
  432. }
  433. // Custom data.
  434. if (mmi->_vf_size_data == 4) {
  435. for (int i = 0; i < 4; i++) {
  436. pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f);
  437. }
  438. pf_prev += 4;
  439. pf_curr += 4;
  440. pf_int += 4;
  441. }
  442. }
  443. _multimesh_set_buffer(rid, mmi->_data_interpolated);
  444. // TODO: Make sure AABBs are constantly up to date through the interpolation?
  445. // NYI.
  446. }
  447. }
  448. }
  449. }