pfmFile.cxx 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201
  1. // Filename: pfmFile.cxx
  2. // Created by: drose (23Dec10)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) Carnegie Mellon University. All rights reserved.
  8. //
  9. // All use of this software is subject to the terms of the revised BSD
  10. // license. You should have received a copy of this license along
  11. // with this source code in a file named "LICENSE."
  12. //
  13. ////////////////////////////////////////////////////////////////////
  14. #include "config_grutil.h"
  15. #include "pfmFile.h"
  16. #include "virtualFileSystem.h"
  17. #include "pandaFileStream.h"
  18. #include "littleEndian.h"
  19. #include "bigEndian.h"
  20. #include "cmath.h"
  21. #include "geomNode.h"
  22. #include "geom.h"
  23. #include "geomVertexData.h"
  24. #include "geomVertexFormat.h"
  25. #include "geomPoints.h"
  26. #include "geomTriangles.h"
  27. #include "geomVertexWriter.h"
  28. #include "lens.h"
  29. #include "look_at.h"
  30. ////////////////////////////////////////////////////////////////////
  31. // Function: PfmFile::Constructor
  32. // Access: Published
  33. // Description:
  34. ////////////////////////////////////////////////////////////////////
  35. PfmFile::
  36. PfmFile() {
  37. _zero_special = false;
  38. _vis_inverse = false;
  39. _vis_2d = false;
  40. clear();
  41. }
  42. ////////////////////////////////////////////////////////////////////
  43. // Function: PfmFile::Copy Constructor
  44. // Access: Published
  45. // Description:
  46. ////////////////////////////////////////////////////////////////////
  47. PfmFile::
  48. PfmFile(const PfmFile &copy) :
  49. _table(copy._table),
  50. _x_size(copy._x_size),
  51. _y_size(copy._y_size),
  52. _scale(copy._scale),
  53. _num_channels(copy._num_channels),
  54. _zero_special(copy._zero_special),
  55. _vis_inverse(copy._vis_inverse),
  56. _vis_2d(copy._vis_2d)
  57. {
  58. }
  59. ////////////////////////////////////////////////////////////////////
  60. // Function: PfmFile::Copy Assignment
  61. // Access: Published
  62. // Description:
  63. ////////////////////////////////////////////////////////////////////
  64. void PfmFile::
  65. operator = (const PfmFile &copy) {
  66. _table = copy._table;
  67. _x_size = copy._x_size;
  68. _y_size = copy._y_size;
  69. _scale = copy._scale;
  70. _num_channels = copy._num_channels;
  71. _zero_special = copy._zero_special;
  72. _vis_inverse = copy._vis_inverse;
  73. _vis_2d = copy._vis_2d;
  74. }
  75. ////////////////////////////////////////////////////////////////////
  76. // Function: PfmFile::clear
  77. // Access: Published
  78. // Description: Eliminates all data in the file.
  79. ////////////////////////////////////////////////////////////////////
  80. void PfmFile::
  81. clear() {
  82. _x_size = 0;
  83. _y_size = 0;
  84. _scale = 1.0;
  85. _num_channels = 3;
  86. _table.clear();
  87. }
  88. ////////////////////////////////////////////////////////////////////
  89. // Function: PfmFile::clear
  90. // Access: Published
  91. // Description: Resets to an empty table with a specific size.
  92. ////////////////////////////////////////////////////////////////////
  93. void PfmFile::
  94. clear(int x_size, int y_size, int num_channels) {
  95. nassertv(num_channels == 1 || num_channels == 3);
  96. nassertv(x_size >= 0 && y_size >= 0);
  97. _x_size = x_size;
  98. _y_size = y_size;
  99. _scale = 1.0;
  100. _num_channels = _num_channels;
  101. _table.clear();
  102. int size = _x_size * _y_size;
  103. _table.insert(_table.end(), size, LPoint3::zero());
  104. }
  105. ////////////////////////////////////////////////////////////////////
  106. // Function: PfmFile::read
  107. // Access: Published
  108. // Description: Reads the PFM data from the indicated file, returning
  109. // true on success, false on failure.
  110. ////////////////////////////////////////////////////////////////////
  111. bool PfmFile::
  112. read(const Filename &fullpath) {
  113. VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
  114. Filename filename = Filename::binary_filename(fullpath);
  115. PT(VirtualFile) file = vfs->get_file(filename);
  116. if (file == (VirtualFile *)NULL) {
  117. // No such file.
  118. grutil_cat.error()
  119. << "Could not find " << fullpath << "\n";
  120. return false;
  121. }
  122. if (grutil_cat.is_debug()) {
  123. grutil_cat.debug()
  124. << "Reading PFM file " << filename << "\n";
  125. }
  126. istream *in = file->open_read_file(true);
  127. bool success = read(*in);
  128. vfs->close_read_file(in);
  129. return success;
  130. }
  131. ////////////////////////////////////////////////////////////////////
  132. // Function: PfmFile::read
  133. // Access: Published
  134. // Description: Reads the PFM data from the indicated stream,
  135. // returning true on success, false on failure.
  136. ////////////////////////////////////////////////////////////////////
  137. bool PfmFile::
  138. read(istream &in) {
  139. clear();
  140. string identifier;
  141. in >> identifier;
  142. if (identifier == "PF") {
  143. _num_channels = 3;
  144. } else if (identifier == "Pf") {
  145. _num_channels = 1;
  146. } else {
  147. grutil_cat.error()
  148. << "Not a pfm file.\n";
  149. return false;
  150. }
  151. int width, height;
  152. PN_stdfloat scale;
  153. in >> width >> height >> scale;
  154. if (!in) {
  155. grutil_cat.error()
  156. << "Error parsing pfm header.\n";
  157. return false;
  158. }
  159. // Skip the last newline/whitespace character before the raw data
  160. // begins.
  161. in.get();
  162. bool little_endian = false;
  163. if (scale < 0) {
  164. scale = -scale;
  165. little_endian = true;
  166. }
  167. if (pfm_force_littleendian) {
  168. little_endian = true;
  169. }
  170. if (pfm_reverse_dimensions) {
  171. int t = width;
  172. width = height;
  173. height = t;
  174. }
  175. _x_size = width;
  176. _y_size = height;
  177. _scale = scale;
  178. // So far, so good. Now read the data.
  179. int size = _x_size * _y_size;
  180. _table.reserve(size);
  181. if (little_endian) {
  182. for (int i = 0; i < size; ++i) {
  183. LPoint3 point = LPoint3::zero();
  184. for (int ci = 0; ci < _num_channels; ++ci) {
  185. PN_float32 data;
  186. in.read((char *)&data, sizeof(data));
  187. LittleEndian value(&data, sizeof(data));
  188. PN_float32 result;
  189. value.store_value(&result, sizeof(result));
  190. if (!cnan(result)) {
  191. point[ci] = result;
  192. }
  193. }
  194. _table.push_back(point);
  195. }
  196. } else {
  197. for (int i = 0; i < size; ++i) {
  198. LPoint3 point = LPoint3::zero();
  199. for (int ci = 0; ci < _num_channels; ++ci) {
  200. PN_float32 data;
  201. in.read((char *)&data, sizeof(data));
  202. BigEndian value(&data, sizeof(data));
  203. PN_float32 result;
  204. value.store_value(&result, sizeof(result));
  205. if (!cnan(result)) {
  206. point[ci] = result;
  207. }
  208. }
  209. _table.push_back(point);
  210. }
  211. }
  212. if (in.fail() && !in.eof()) {
  213. return false;
  214. }
  215. nassertr(sizeof(PN_float32) == 4, false);
  216. return true;
  217. }
  218. ////////////////////////////////////////////////////////////////////
  219. // Function: PfmFile::write
  220. // Access: Published
  221. // Description: Writes the PFM data to the indicated file, returning
  222. // true on success, false on failure.
  223. ////////////////////////////////////////////////////////////////////
  224. bool PfmFile::
  225. write(const Filename &fullpath) {
  226. Filename filename = Filename::binary_filename(fullpath);
  227. pofstream out;
  228. if (!filename.open_write(out)) {
  229. grutil_cat.error()
  230. << "Unable to open " << filename << "\n";
  231. return false;
  232. }
  233. if (grutil_cat.is_debug()) {
  234. grutil_cat.debug()
  235. << "Writing PFM file " << filename << "\n";
  236. }
  237. return write(out);
  238. }
  239. ////////////////////////////////////////////////////////////////////
  240. // Function: PfmFile::write
  241. // Access: Published
  242. // Description: Writes the PFM data to the indicated stream,
  243. // returning true on success, false on failure.
  244. ////////////////////////////////////////////////////////////////////
  245. bool PfmFile::
  246. write(ostream &out) {
  247. nassertr(is_valid(), false);
  248. if (_num_channels == 1) {
  249. out << "Pf\n";
  250. } else {
  251. out << "PF\n";
  252. }
  253. out << _x_size << " " << _y_size << "\n";
  254. PN_stdfloat scale = cabs(_scale);
  255. if (scale == 0.0f) {
  256. scale = 1.0f;
  257. }
  258. #ifndef WORDS_BIGENDIAN
  259. // Little-endian computers must write a negative scale to indicate
  260. // the little-endian nature of the output.
  261. scale = -scale;
  262. #endif
  263. out << scale << "\n";
  264. int size = _x_size * _y_size;
  265. for (int i = 0; i < size; ++i) {
  266. const LPoint3 &point = _table[i];
  267. for (int ci = 0; ci < _num_channels; ++ci) {
  268. PN_float32 data = point[ci];
  269. out.write((const char *)&data, sizeof(data));
  270. }
  271. }
  272. if (out.fail()) {
  273. return false;
  274. }
  275. nassertr(sizeof(PN_float32) == 4, false);
  276. return true;
  277. }
  278. ////////////////////////////////////////////////////////////////////
  279. // Function: PfmFile::calc_average_point
  280. // Access: Published
  281. // Description: Computes the unweighted average point of all points
  282. // within the box centered at (x, y) with the indicated
  283. // Manhattan-distance radius. Missing points are
  284. // assigned the value of their nearest neighbor.
  285. // Returns true if successful, or false if the point
  286. // value cannot be determined.
  287. ////////////////////////////////////////////////////////////////////
  288. bool PfmFile::
  289. calc_average_point(LPoint3 &result, PN_stdfloat x, PN_stdfloat y, PN_stdfloat radius) const {
  290. result = LPoint3::zero();
  291. int min_x = int(ceil(x - radius));
  292. int min_y = int(ceil(y - radius));
  293. int max_x = int(floor(x + radius));
  294. int max_y = int(floor(y + radius));
  295. // We first construct a mini-grid of x_size by y_size integer values
  296. // to index into the main table. This indirection allows us to fill
  297. // in the holes in the mini-grid with the nearest known values
  298. // before we compute the average.
  299. int x_size = max_x - min_x + 1;
  300. int y_size = max_y - min_y + 1;
  301. int size = x_size * y_size;
  302. if (size == 0) {
  303. return false;
  304. }
  305. pvector<MiniGridCell> mini_grid;
  306. mini_grid.insert(mini_grid.end(), size, MiniGridCell());
  307. // Now collect the known data points and apply them to the
  308. // mini-grid.
  309. min_x = max(min_x, 0);
  310. min_y = max(min_y, 0);
  311. max_x = min(max_x, _x_size - 1);
  312. max_y = min(max_y, _y_size - 1);
  313. bool got_any = false;
  314. int xi, yi;
  315. for (yi = min_y; yi <= max_y; ++yi) {
  316. for (xi = min_x; xi <= max_x; ++xi) {
  317. const LPoint3 &p = _table[yi * _x_size + xi];
  318. if (_zero_special && p == LPoint3::zero()) {
  319. continue;
  320. }
  321. int gi = (yi - min_y) * y_size + (xi - min_x);
  322. nassertr(gi >= 0 && gi < size, false);
  323. mini_grid[gi]._ti = yi * _x_size + xi;
  324. mini_grid[gi]._dist = 0;
  325. got_any = true;
  326. }
  327. }
  328. if (!got_any) {
  329. return false;
  330. }
  331. // Now recursively fill in any missing holes in the mini-grid.
  332. for (yi = 0; yi < y_size; ++yi) {
  333. for (xi = 0; xi < x_size; ++xi) {
  334. int gi = yi * x_size + xi;
  335. if (mini_grid[gi]._dist == 0) {
  336. int ti = mini_grid[gi]._ti;
  337. fill_mini_grid(&mini_grid[0], x_size, y_size, xi + 1, yi, 1, ti);
  338. fill_mini_grid(&mini_grid[0], x_size, y_size, xi - 1, yi, 1, ti);
  339. fill_mini_grid(&mini_grid[0], x_size, y_size, xi, yi + 1, 1, ti);
  340. fill_mini_grid(&mini_grid[0], x_size, y_size, xi, yi - 1, 1, ti);
  341. }
  342. }
  343. }
  344. // Now the mini-grid is completely filled, so we can compute the
  345. // average.
  346. for (int gi = 0; gi < size; ++gi) {
  347. int ti = mini_grid[gi]._ti;
  348. nassertr(ti >= 0 && ti < (int)_table.size(), false);
  349. result += _table[ti];
  350. }
  351. result /= PN_stdfloat(size);
  352. return true;
  353. }
  354. ////////////////////////////////////////////////////////////////////
  355. // Function: PfmFile::calc_min_max
  356. // Access: Published
  357. // Description: Calculates the minimum and maximum x, y, and z depth
  358. // component values, representing the bounding box of
  359. // depth values, and places them in the indicated
  360. // vectors. Returns true if successful, false if the
  361. // mesh contains no points.
  362. ////////////////////////////////////////////////////////////////////
  363. bool PfmFile::
  364. calc_min_max(LVecBase3 &min_depth, LVecBase3 &max_depth) const {
  365. bool any_points = false;
  366. min_depth = LVecBase3::zero();
  367. max_depth = LVecBase3::zero();
  368. Table::const_iterator ti;
  369. for (ti = _table.begin(); ti != _table.end(); ++ti) {
  370. const LPoint3 &p = (*ti);
  371. if (_zero_special && p == LPoint3::zero()) {
  372. continue;
  373. }
  374. if (!any_points) {
  375. min_depth = p;
  376. max_depth = p;
  377. any_points = true;
  378. } else {
  379. min_depth[0] = min(min_depth[0], p[0]);
  380. min_depth[1] = min(min_depth[1], p[1]);
  381. min_depth[2] = min(min_depth[2], p[2]);
  382. max_depth[0] = max(max_depth[0], p[0]);
  383. max_depth[1] = max(max_depth[1], p[1]);
  384. max_depth[2] = max(max_depth[2], p[2]);
  385. }
  386. }
  387. return any_points;
  388. }
  389. ////////////////////////////////////////////////////////////////////
  390. // Function: PfmFile::resize
  391. // Access: Published
  392. // Description: Applies a simple filter to resample the pfm file
  393. // in-place to the indicated size. Don't confuse this
  394. // with applying a scale to all of the points via
  395. // xform().
  396. ////////////////////////////////////////////////////////////////////
  397. void PfmFile::
  398. resize(int new_x_size, int new_y_size) {
  399. if (_x_size == 0 || _y_size == 0 || new_x_size == 0 || new_y_size == 0) {
  400. clear(new_x_size, new_y_size, _num_channels);
  401. return;
  402. }
  403. if (new_x_size == _x_size && new_y_size == _y_size) {
  404. return;
  405. }
  406. Table new_data;
  407. new_data.reserve(new_x_size * new_y_size);
  408. PN_stdfloat from_x0, from_x1, from_y0, from_y1;
  409. PN_stdfloat x_scale = 1.0;
  410. PN_stdfloat y_scale = 1.0;
  411. if (new_x_size > 1) {
  412. x_scale = (PN_stdfloat)(_x_size - 1) / (PN_stdfloat)(new_x_size - 1);
  413. }
  414. if (new_y_size > 1) {
  415. y_scale = (PN_stdfloat)(_y_size - 1) / (PN_stdfloat)(new_y_size - 1);
  416. }
  417. from_y0 = 0.0;
  418. for (int to_y = 0; to_y < new_y_size; ++to_y) {
  419. from_y1 = (to_y + 0.5) * y_scale;
  420. from_y1 = min(from_y1, (PN_stdfloat) _y_size);
  421. from_x0 = 0.0;
  422. for (int to_x = 0; to_x < new_x_size; ++to_x) {
  423. from_x1 = (to_x + 0.5) * x_scale;
  424. from_x1 = min(from_x1, (PN_stdfloat) _x_size);
  425. // Now the box from (from_x0, from_y0) - (from_x1, from_y1)
  426. // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
  427. LPoint3 result;
  428. box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
  429. new_data.push_back(result);
  430. from_x0 = from_x1;
  431. }
  432. from_y0 = from_y1;
  433. }
  434. _table.swap(new_data);
  435. _x_size = new_x_size;
  436. _y_size = new_y_size;
  437. }
  438. ////////////////////////////////////////////////////////////////////
  439. // Function: PfmFile::reverse_rows
  440. // Access: Published
  441. // Description: Performs an in-place reversal of the row (y) data.
  442. ////////////////////////////////////////////////////////////////////
  443. void PfmFile::
  444. reverse_rows() {
  445. nassertv(is_valid());
  446. Table reversed;
  447. reversed.reserve(_table.size());
  448. for (int yi = 0; yi < _y_size; ++yi) {
  449. int source_yi = _y_size - 1 - yi;
  450. int start = source_yi * _x_size;
  451. reversed.insert(reversed.end(),
  452. _table.begin() + start, _table.begin() + start + _x_size);
  453. }
  454. nassertv(reversed.size() == _table.size());
  455. _table.swap(reversed);
  456. }
  457. ////////////////////////////////////////////////////////////////////
  458. // Function: PfmFile::xform
  459. // Access: Published
  460. // Description: Applies the indicated transform matrix to all points
  461. // in-place.
  462. ////////////////////////////////////////////////////////////////////
  463. void PfmFile::
  464. xform(const LMatrix4 &transform) {
  465. nassertv(is_valid());
  466. Table::iterator ti;
  467. for (ti = _table.begin(); ti != _table.end(); ++ti) {
  468. if (_zero_special && (*ti) == LPoint3::zero()) {
  469. continue;
  470. }
  471. (*ti) = (*ti) * transform;
  472. }
  473. }
  474. ////////////////////////////////////////////////////////////////////
  475. // Function: PfmFile::project
  476. // Access: Published
  477. // Description: Adjusts each (x, y, z) point of the Pfm file by
  478. // projecting it through the indicated lens, converting
  479. // each point to a (u, v, w) texture coordinate. The
  480. // resulting file can be generated to a mesh (with
  481. // set_vis_inverse(true) and generate_vis_mesh())
  482. // that will apply the lens distortion to an arbitrary
  483. // texture image.
  484. ////////////////////////////////////////////////////////////////////
  485. void PfmFile::
  486. project(const Lens *lens) {
  487. nassertv(is_valid());
  488. static LMatrix4 to_uv(0.5, 0.0, 0.0, 0.0,
  489. 0.0, 0.5, 0.0, 0.0,
  490. 0.0, 0.0, 1.0, 0.0,
  491. 0.5, 0.5, 0.0, 1.0);
  492. Table::iterator ti;
  493. for (ti = _table.begin(); ti != _table.end(); ++ti) {
  494. if (_zero_special && (*ti) == LPoint3::zero()) {
  495. continue;
  496. }
  497. LPoint3 &p = (*ti);
  498. LPoint3 film;
  499. lens->project(p, film);
  500. p = to_uv.xform_point(film);
  501. }
  502. }
  503. ////////////////////////////////////////////////////////////////////
  504. // Function: PfmFile::merge
  505. // Access: Published
  506. // Description: Wherever there is missing data in this PfmFile (that
  507. // is, wherever has_point() returns false), copy data
  508. // from the other PfmFile, which must be exactly the
  509. // same dimensions as this one.
  510. ////////////////////////////////////////////////////////////////////
  511. void PfmFile::
  512. merge(const PfmFile &other) {
  513. nassertv(is_valid() && other.is_valid());
  514. nassertv(other._x_size == _x_size && other._y_size == _y_size);
  515. if (!_zero_special) {
  516. // Trivial no-op.
  517. return;
  518. }
  519. for (size_t i = 0; i < _table.size(); ++i) {
  520. if (_table[i] == LPoint3::zero()) {
  521. _table[i] = other._table[i];
  522. }
  523. }
  524. }
  525. ////////////////////////////////////////////////////////////////////
  526. // Function: PfmFile::compute_planar_bounds
  527. // Access: Published
  528. // Description: This version of this method exists for temporary
  529. // backward compatibility only.
  530. ////////////////////////////////////////////////////////////////////
  531. PT(BoundingHexahedron) PfmFile::
  532. compute_planar_bounds(PN_stdfloat point_dist, PN_stdfloat sample_radius) const {
  533. return compute_planar_bounds(LPoint2(0.5, 0.5), point_dist, sample_radius, false);
  534. }
  535. ////////////////////////////////////////////////////////////////////
  536. // Function: PfmFile::compute_planar_bounds
  537. // Access: Published
  538. // Description: Computes the minmax bounding volume of the points in
  539. // 3-D space, assuming the points represent a
  540. // mostly-planar surface.
  541. //
  542. // This algorithm works by sampling the (square)
  543. // sample_radius pixels at the four point_dist corners
  544. // around the center (cx - pd, cx + pd) and so on, to
  545. // approximate the plane of the surface. Then all of
  546. // the points are projected into that plane and the
  547. // bounding volume of the entire mesh within that plane
  548. // is determined. If points_only is true, the bounding
  549. // volume of only those four points is determined.
  550. //
  551. // center, point_dist and sample_radius are in UV space,
  552. // i.e. in the range 0..1.
  553. ////////////////////////////////////////////////////////////////////
  554. PT(BoundingHexahedron) PfmFile::
  555. compute_planar_bounds(const LPoint2 &center, PN_stdfloat point_dist, PN_stdfloat sample_radius, bool points_only) const {
  556. LPoint3 p0, p1, p2, p3;
  557. compute_sample_point(p0, center[0] + point_dist, center[1] - point_dist, sample_radius);
  558. compute_sample_point(p1, center[0] + point_dist, center[1] + point_dist, sample_radius);
  559. compute_sample_point(p2, center[0] - point_dist, center[1] + point_dist, sample_radius);
  560. compute_sample_point(p3, center[0] - point_dist, center[1] - point_dist, sample_radius);
  561. LPoint3 normal;
  562. normal[0] = p0[1] * p1[2] - p0[2] * p1[1];
  563. normal[1] = p0[2] * p1[0] - p0[0] * p1[2];
  564. normal[2] = p0[0] * p1[1] - p0[1] * p1[0];
  565. normal[0] += p1[1] * p2[2] - p1[2] * p2[1];
  566. normal[1] += p1[2] * p2[0] - p1[0] * p2[2];
  567. normal[2] += p1[0] * p2[1] - p1[1] * p2[0];
  568. normal[0] += p2[1] * p3[2] - p2[2] * p3[1];
  569. normal[1] += p2[2] * p3[0] - p2[0] * p3[2];
  570. normal[2] += p2[0] * p3[1] - p2[1] * p3[0];
  571. normal[0] += p3[1] * p0[2] - p3[2] * p0[1];
  572. normal[1] += p3[2] * p0[0] - p3[0] * p0[2];
  573. normal[2] += p3[0] * p0[1] - p3[1] * p0[0];
  574. normal.normalize();
  575. LVector3 up = (p1 - p0) + (p2 - p3);
  576. LPoint3 pcenter = ((p0 + p1 + p2 + p3) * 0.25);
  577. // Compute the transform necessary to rotate all of the points into
  578. // the Y = 0 plane.
  579. LMatrix4 rotate;
  580. look_at(rotate, normal, up);
  581. LMatrix4 rinv;
  582. rinv.invert_from(rotate);
  583. LPoint3 trans = pcenter * rinv;
  584. rinv.set_row(3, -trans);
  585. rotate.invert_from(rinv);
  586. // Now determine the minmax.
  587. PN_stdfloat min_x, min_y, min_z, max_x, max_y, max_z;
  588. bool got_point = false;
  589. if (points_only) {
  590. LPoint3 points[4] = {
  591. p0 * rinv,
  592. p1 * rinv,
  593. p2 * rinv,
  594. p3 * rinv,
  595. };
  596. for (int i = 0; i < 4; ++i) {
  597. const LPoint3 &point = points[i];
  598. if (!got_point) {
  599. min_x = point[0];
  600. min_y = point[1];
  601. min_z = point[2];
  602. max_x = point[0];
  603. max_y = point[1];
  604. max_z = point[2];
  605. got_point = true;
  606. } else {
  607. min_x = min(min_x, point[0]);
  608. min_y = min(min_y, point[1]);
  609. min_z = min(min_z, point[2]);
  610. max_x = max(max_x, point[0]);
  611. max_y = max(max_y, point[1]);
  612. max_z = max(max_z, point[2]);
  613. }
  614. }
  615. } else {
  616. Table::const_iterator ti;
  617. for (ti = _table.begin(); ti != _table.end(); ++ti) {
  618. if (_zero_special && (*ti) == LPoint3::zero()) {
  619. continue;
  620. }
  621. LPoint3 point = (*ti) * rinv;
  622. if (!got_point) {
  623. min_x = point[0];
  624. min_y = point[1];
  625. min_z = point[2];
  626. max_x = point[0];
  627. max_y = point[1];
  628. max_z = point[2];
  629. got_point = true;
  630. } else {
  631. min_x = min(min_x, point[0]);
  632. min_y = min(min_y, point[1]);
  633. min_z = min(min_z, point[2]);
  634. max_x = max(max_x, point[0]);
  635. max_y = max(max_y, point[1]);
  636. max_z = max(max_z, point[2]);
  637. }
  638. }
  639. }
  640. PT(BoundingHexahedron) bounds = new BoundingHexahedron
  641. (LPoint3(min_x, min_y, min_z), LPoint3(max_x, min_y, min_z),
  642. LPoint3(min_x, min_y, max_z), LPoint3(max_x, min_y, max_z),
  643. LPoint3(min_x, max_y, min_z), LPoint3(max_x, max_y, min_z),
  644. LPoint3(min_x, max_y, max_z), LPoint3(max_x, max_y, max_z));
  645. // Rotate the bounding volume back into the original space of the
  646. // screen.
  647. bounds->xform(rotate);
  648. return bounds;
  649. }
  650. ////////////////////////////////////////////////////////////////////
  651. // Function: PfmFile::compute_sample_point
  652. // Access: Published
  653. // Description: Computes the average of all the point within
  654. // sample_radius (manhattan distance) and the indicated
  655. // point.
  656. //
  657. // The point coordinates are given in UV space, in the
  658. // range 0..1.
  659. ////////////////////////////////////////////////////////////////////
  660. void PfmFile::
  661. compute_sample_point(LPoint3 &result,
  662. PN_stdfloat x, PN_stdfloat y, PN_stdfloat sample_radius) const {
  663. x *= _x_size;
  664. y *= _y_size;
  665. PN_stdfloat xr = sample_radius * _x_size;
  666. PN_stdfloat yr = sample_radius * _y_size;
  667. box_filter_region(result, x - xr, y - yr, x + xr, y + yr);
  668. }
  669. ////////////////////////////////////////////////////////////////////
  670. // Function: PfmFile::generate_vis_points
  671. // Access: Published
  672. // Description: Creates a point cloud with the points of the pfm as
  673. // 3-d coordinates in space, and texture coordinates
  674. // ranging from 0 .. 1 based on the position within the
  675. // pfm grid.
  676. ////////////////////////////////////////////////////////////////////
  677. NodePath PfmFile::
  678. generate_vis_points() const {
  679. nassertr(is_valid(), NodePath());
  680. CPT(GeomVertexFormat) format;
  681. if (_vis_inverse) {
  682. if (_vis_2d) {
  683. format = GeomVertexFormat::get_v3t2();
  684. } else {
  685. // We need a 3-d texture coordinate if we're inverting the vis
  686. // and it's 3-d.
  687. GeomVertexArrayFormat *v3t3 = new GeomVertexArrayFormat
  688. (InternalName::get_vertex(), 3,
  689. Geom::NT_stdfloat, Geom::C_point,
  690. InternalName::get_texcoord(), 3,
  691. Geom::NT_stdfloat, Geom::C_texcoord);
  692. format = GeomVertexFormat::register_format(v3t3);
  693. }
  694. } else {
  695. format = GeomVertexFormat::get_v3t2();
  696. }
  697. PT(GeomVertexData) vdata = new GeomVertexData
  698. ("points", format, Geom::UH_static);
  699. vdata->set_num_rows(_x_size * _y_size);
  700. GeomVertexWriter vertex(vdata, InternalName::get_vertex());
  701. GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
  702. LPoint2 uv_scale(1.0, 1.0);
  703. if (_x_size > 1) {
  704. uv_scale[0] = 1.0f / PN_stdfloat(_x_size - 1);
  705. }
  706. if (_y_size > 1) {
  707. uv_scale[1] = 1.0f / PN_stdfloat(_y_size - 1);
  708. }
  709. for (int yi = 0; yi < _y_size; ++yi) {
  710. for (int xi = 0; xi < _x_size; ++xi) {
  711. const LPoint3 &point = get_point(xi, yi);
  712. LPoint2 uv(PN_stdfloat(xi) * uv_scale[0],
  713. PN_stdfloat(yi) * uv_scale[1]);
  714. if (_vis_inverse) {
  715. vertex.add_data2(uv);
  716. texcoord.add_data3(point);
  717. } else if (_vis_2d) {
  718. vertex.add_data2(point[0], point[1]);
  719. texcoord.add_data2(uv);
  720. } else {
  721. vertex.add_data3(point);
  722. texcoord.add_data2(uv);
  723. }
  724. }
  725. }
  726. PT(Geom) geom = new Geom(vdata);
  727. PT(GeomPoints) points = new GeomPoints(Geom::UH_static);
  728. points->add_next_vertices(_x_size * _y_size);
  729. geom->add_primitive(points);
  730. PT(GeomNode) gnode = new GeomNode("");
  731. gnode->add_geom(geom);
  732. return NodePath(gnode);
  733. }
  734. ////////////////////////////////////////////////////////////////////
  735. // Function: PfmFile::generate_vis_mesh
  736. // Access: Published
  737. // Description: Creates a triangle mesh with the points of the pfm as
  738. // 3-d coordinates in space, and texture coordinates
  739. // ranging from 0 .. 1 based on the position within the
  740. // pfm grid.
  741. ////////////////////////////////////////////////////////////////////
  742. NodePath PfmFile::
  743. generate_vis_mesh(MeshFace face) const {
  744. nassertr(is_valid(), NodePath());
  745. nassertr(face != 0, NodePath());
  746. if (_x_size == 1 || _y_size == 1) {
  747. // Can't generate a 1-d mesh, so generate points in this case.
  748. return generate_vis_points();
  749. }
  750. PT(GeomNode) gnode = new GeomNode("");
  751. if (face & MF_front) {
  752. make_vis_mesh_geom(gnode, false);
  753. }
  754. if (face & MF_back) {
  755. make_vis_mesh_geom(gnode, true);
  756. }
  757. return NodePath(gnode);
  758. }
  759. ////////////////////////////////////////////////////////////////////
  760. // Function: PfmFile::make_vis_mesh_geom
  761. // Access: Private
  762. // Description: Returns a triangle mesh for the pfm. If inverted is
  763. // true, the mesh is facing the opposite direction.
  764. ////////////////////////////////////////////////////////////////////
  765. void PfmFile::
  766. make_vis_mesh_geom(GeomNode *gnode, bool inverted) const {
  767. int num_x_cells = 1;
  768. int num_y_cells = 1;
  769. int x_size = _x_size;
  770. int y_size = _y_size;
  771. // This is the number of independent vertices we will require.
  772. int num_vertices = x_size * y_size;
  773. if (num_vertices == 0) {
  774. // Trivial no-op.
  775. return;
  776. }
  777. // This is the max number of vertex indices we might add to the
  778. // GeomTriangles. (We might actually add fewer than this due to
  779. // omitting the occasional missing data point.)
  780. int max_indices = (x_size - 1) * (y_size - 1) * 6;
  781. while (num_vertices > pfm_vis_max_vertices || max_indices > pfm_vis_max_indices) {
  782. // Too many vertices in one mesh. Subdivide the mesh into smaller
  783. // pieces.
  784. if (num_x_cells > num_y_cells) {
  785. ++num_y_cells;
  786. } else {
  787. ++num_x_cells;
  788. }
  789. x_size = (_x_size + num_x_cells - 1) / num_x_cells + 1;
  790. y_size = (_y_size + num_y_cells - 1) / num_y_cells + 1;
  791. num_vertices = x_size * y_size;
  792. max_indices = (x_size - 1) * (y_size - 1) * 6;
  793. }
  794. // OK, now we know how many cells we need.
  795. if (grutil_cat.is_debug()) {
  796. grutil_cat.debug()
  797. << "Generating mesh with " << num_x_cells << " x " << num_y_cells
  798. << " pieces.\n";
  799. }
  800. PT(GeomVertexArrayFormat) array_format;
  801. if (_vis_2d) {
  802. // No normals needed if we're just generating a 2-d mesh.
  803. array_format = new GeomVertexArrayFormat
  804. (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
  805. InternalName::get_texcoord(), 2, Geom::NT_stdfloat, Geom::C_texcoord);
  806. } else {
  807. if (_vis_inverse) {
  808. // We need a 3-d texture coordinate if we're inverting the vis
  809. // and it's 3-d. But we still don't need normals in that case.
  810. array_format = new GeomVertexArrayFormat
  811. (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
  812. InternalName::get_texcoord(), 3, Geom::NT_stdfloat, Geom::C_texcoord);
  813. } else {
  814. // Otherwise, we only need a 2-d texture coordinate, and we do
  815. // want normals.
  816. array_format = new GeomVertexArrayFormat
  817. (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
  818. InternalName::get_normal(), 3, Geom::NT_stdfloat, Geom::C_vector,
  819. InternalName::get_texcoord(), 2, Geom::NT_stdfloat, Geom::C_texcoord);
  820. }
  821. }
  822. if (_flat_texcoord_name != (InternalName *)NULL) {
  823. // We need an additional texcoord column for the flat texcoords.
  824. array_format->add_column(_flat_texcoord_name, 2,
  825. Geom::NT_stdfloat, Geom::C_texcoord);
  826. }
  827. CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(array_format);
  828. for (int yci = 0; yci < num_y_cells; ++yci) {
  829. int y_begin = (yci * _y_size) / num_y_cells;
  830. int y_end = ((yci + 1) * _y_size) / num_y_cells;
  831. // Include the first vertex from the next strip in this strip's
  832. // vertices, so we are connected.
  833. y_end = min(y_end + 1, _y_size);
  834. y_size = y_end - y_begin;
  835. if (y_size == 0) {
  836. continue;
  837. }
  838. for (int xci = 0; xci < num_x_cells; ++xci) {
  839. int x_begin = (xci * _x_size) / num_x_cells;
  840. int x_end = ((xci + 1) * _x_size) / num_x_cells;
  841. x_end = min(x_end + 1, _x_size);
  842. x_size = x_end - x_begin;
  843. if (x_size == 0) {
  844. continue;
  845. }
  846. num_vertices = x_size * y_size;
  847. max_indices = (x_size - 1) * (y_size - 1) * 6;
  848. ostringstream mesh_name;
  849. mesh_name << "mesh_" << xci << "_" << yci;
  850. PT(GeomVertexData) vdata = new GeomVertexData
  851. (mesh_name.str(), format, Geom::UH_static);
  852. vdata->set_num_rows(num_vertices);
  853. GeomVertexWriter vertex(vdata, InternalName::get_vertex());
  854. GeomVertexWriter normal(vdata, InternalName::get_normal());
  855. GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
  856. GeomVertexWriter texcoord2(vdata, _flat_texcoord_name);
  857. for (int yi = y_begin; yi < y_end; ++yi) {
  858. for (int xi = x_begin; xi < x_end; ++xi) {
  859. const LPoint3 &point = get_point(xi, yi);
  860. LPoint2 uv(PN_stdfloat(xi) / PN_stdfloat(_x_size - 1),
  861. PN_stdfloat(yi) / PN_stdfloat(_y_size - 1));
  862. if (_vis_inverse) {
  863. vertex.add_data2(uv);
  864. texcoord.add_data3(point);
  865. } else if (_vis_2d) {
  866. vertex.add_data2(point[0], point[1]);
  867. texcoord.add_data2(uv);
  868. } else {
  869. vertex.add_data3(point);
  870. texcoord.add_data2(uv);
  871. // Calculate the normal based on two neighboring vertices.
  872. LPoint3 v[3];
  873. v[0] = get_point(xi, yi);
  874. if (xi + 1 < _x_size) {
  875. v[1] = get_point(xi + 1, yi);
  876. } else {
  877. v[1] = v[0];
  878. v[0] = get_point(xi - 1, yi);
  879. }
  880. if (yi + 1 < _y_size) {
  881. v[2] = get_point(xi, yi + 1);
  882. } else {
  883. v[2] = v[0];
  884. v[0] = get_point(xi, yi - 1);
  885. }
  886. LVector3 n = LVector3::zero();
  887. for (int i = 0; i < 3; ++i) {
  888. const LPoint3 &v0 = v[i];
  889. const LPoint3 &v1 = v[(i + 1) % 3];
  890. n[0] += v0[1] * v1[2] - v0[2] * v1[1];
  891. n[1] += v0[2] * v1[0] - v0[0] * v1[2];
  892. n[2] += v0[0] * v1[1] - v0[1] * v1[0];
  893. }
  894. n.normalize();
  895. nassertv(!n.is_nan());
  896. if (inverted) {
  897. n = -n;
  898. }
  899. normal.add_data3(n);
  900. }
  901. if (_flat_texcoord_name != (InternalName *)NULL) {
  902. texcoord2.add_data2(uv);
  903. }
  904. }
  905. }
  906. PT(Geom) geom = new Geom(vdata);
  907. PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
  908. tris->reserve_num_vertices(max_indices);
  909. for (int yi = y_begin; yi < y_end - 1; ++yi) {
  910. for (int xi = x_begin; xi < x_end - 1; ++xi) {
  911. if (_zero_special) {
  912. if (get_point(xi, yi) == LPoint3::zero() ||
  913. get_point(xi, yi + 1) == LPoint3::zero() ||
  914. get_point(xi + 1, yi + 1) == LPoint3::zero() ||
  915. get_point(xi + 1, yi) == LPoint3::zero()) {
  916. continue;
  917. }
  918. }
  919. int xi0 = xi - x_begin;
  920. int yi0 = yi - y_begin;
  921. int vi0 = ((xi0) + (yi0) * x_size);
  922. int vi1 = ((xi0) + (yi0 + 1) * x_size);
  923. int vi2 = ((xi0 + 1) + (yi0 + 1) * x_size);
  924. int vi3 = ((xi0 + 1) + (yi0) * x_size);
  925. if (inverted) {
  926. tris->add_vertices(vi2, vi0, vi1);
  927. tris->close_primitive();
  928. tris->add_vertices(vi3, vi0, vi2);
  929. tris->close_primitive();
  930. } else {
  931. tris->add_vertices(vi2, vi1, vi0);
  932. tris->close_primitive();
  933. tris->add_vertices(vi3, vi2, vi0);
  934. tris->close_primitive();
  935. }
  936. }
  937. }
  938. geom->add_primitive(tris);
  939. gnode->add_geom(geom);
  940. }
  941. }
  942. }
  943. ////////////////////////////////////////////////////////////////////
  944. // Function: PfmFile::box_filter_region
  945. // Access: Private
  946. // Description: Averages all the points in the rectangle from x0
  947. // .. y0 to x1 .. y1 into result. The region may be
  948. // defined by floating-point boundaries; the result will
  949. // be weighted by the degree of coverage of each
  950. // included point.
  951. ////////////////////////////////////////////////////////////////////
  952. void PfmFile::
  953. box_filter_region(LPoint3 &result,
  954. PN_stdfloat x0, PN_stdfloat y0, PN_stdfloat x1, PN_stdfloat y1) const {
  955. result = LPoint3::zero();
  956. PN_stdfloat coverage = 0.0;
  957. if (x1 < x0 || y1 < y0) {
  958. return;
  959. }
  960. nassertv(y0 >= 0.0 && y1 >= 0.0);
  961. int y = (int)y0;
  962. // Get the first (partial) row
  963. box_filter_line(result, coverage, x0, y, x1, (PN_stdfloat)(y+1)-y0);
  964. int y_last = (int)y1;
  965. if (y < y_last) {
  966. y++;
  967. while (y < y_last) {
  968. // Get each consecutive (complete) row
  969. box_filter_line(result, coverage, x0, y, x1, 1.0);
  970. y++;
  971. }
  972. // Get the final (partial) row
  973. PN_stdfloat y_contrib = y1 - (PN_stdfloat)y_last;
  974. if (y_contrib > 0.0001) {
  975. box_filter_line(result, coverage, x0, y, x1, y_contrib);
  976. }
  977. }
  978. if (coverage != 0.0) {
  979. result /= coverage;
  980. }
  981. }
  982. ////////////////////////////////////////////////////////////////////
  983. // Function: PfmFile::box_filter_line
  984. // Access: Private
  985. // Description:
  986. ////////////////////////////////////////////////////////////////////
  987. void PfmFile::
  988. box_filter_line(LPoint3 &result, PN_stdfloat &coverage,
  989. PN_stdfloat x0, int y, PN_stdfloat x1, PN_stdfloat y_contrib) const {
  990. int x = (int)x0;
  991. // Get the first (partial) xel
  992. box_filter_point(result, coverage, x, y, (PN_stdfloat)(x+1)-x0, y_contrib);
  993. int x_last = (int)x1;
  994. if (x < x_last) {
  995. x++;
  996. while (x < x_last) {
  997. // Get each consecutive (complete) xel
  998. box_filter_point(result, coverage, x, y, 1.0, y_contrib);
  999. x++;
  1000. }
  1001. // Get the final (partial) xel
  1002. PN_stdfloat x_contrib = x1 - (PN_stdfloat)x_last;
  1003. if (x_contrib > 0.0001) {
  1004. box_filter_point(result, coverage, x, y, x_contrib, y_contrib);
  1005. }
  1006. }
  1007. }
  1008. ////////////////////////////////////////////////////////////////////
  1009. // Function: PfmFile::box_filter_point
  1010. // Access: Private
  1011. // Description:
  1012. ////////////////////////////////////////////////////////////////////
  1013. void PfmFile::
  1014. box_filter_point(LPoint3 &result, PN_stdfloat &coverage,
  1015. int x, int y, PN_stdfloat x_contrib, PN_stdfloat y_contrib) const {
  1016. const LPoint3 &point = get_point(x, y);
  1017. if (_zero_special && point == LPoint3::zero()) {
  1018. return;
  1019. }
  1020. PN_stdfloat contrib = x_contrib * y_contrib;
  1021. result += point * contrib;
  1022. coverage += contrib;
  1023. }
  1024. ////////////////////////////////////////////////////////////////////
  1025. // Function: PfmFile::fill_mini_grid
  1026. // Access: Private
  1027. // Description: A support function for calc_average_point(), this
  1028. // recursively fills in the holes in the mini_grid data
  1029. // with the index to the nearest value.
  1030. ////////////////////////////////////////////////////////////////////
  1031. void PfmFile::
  1032. fill_mini_grid(MiniGridCell *mini_grid, int x_size, int y_size,
  1033. int xi, int yi, int dist, int ti) const {
  1034. if (xi < 0 || xi >= x_size || yi < 0 || yi >= y_size) {
  1035. // Out of bounds.
  1036. return;
  1037. }
  1038. int gi = yi * x_size + xi;
  1039. if (mini_grid[gi]._dist == -1 || mini_grid[gi]._dist > dist) {
  1040. // Here's an undefined value that we need to populate.
  1041. mini_grid[gi]._dist = dist;
  1042. mini_grid[gi]._ti = ti;
  1043. fill_mini_grid(mini_grid, x_size, y_size, xi + 1, yi, dist + 1, ti);
  1044. fill_mini_grid(mini_grid, x_size, y_size, xi - 1, yi, dist + 1, ti);
  1045. fill_mini_grid(mini_grid, x_size, y_size, xi, yi + 1, dist + 1, ti);
  1046. fill_mini_grid(mini_grid, x_size, y_size, xi, yi - 1, dist + 1, ti);
  1047. }
  1048. }