cLwoSurface.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file cLwoSurface.cxx
  10. * @author drose
  11. * @date 2001-04-25
  12. */
  13. #include "cLwoSurface.h"
  14. #include "cLwoSurfaceBlock.h"
  15. #include "cLwoClip.h"
  16. #include "lwoToEggConverter.h"
  17. #include "lwoSurfaceColor.h"
  18. #include "lwoSurfaceParameter.h"
  19. #include "lwoSurfaceSmoothingAngle.h"
  20. #include "lwoSurfaceSidedness.h"
  21. #include "lwoSurfaceBlock.h"
  22. #include "eggPrimitive.h"
  23. #include "string_utils.h"
  24. #include "mathNumbers.h"
  25. #include "dcast.h"
  26. /**
  27. *
  28. */
  29. CLwoSurface::
  30. CLwoSurface(LwoToEggConverter *converter, const LwoSurface *surface) :
  31. _converter(converter),
  32. _surface(surface)
  33. {
  34. _flags = 0;
  35. _rgb.set(1.0, 1.0, 1.0);
  36. _checked_material = false;
  37. _checked_texture = false;
  38. _map_uvs = nullptr;
  39. _block = nullptr;
  40. // Walk through the chunk list, looking for some basic properties.
  41. int num_chunks = _surface->get_num_chunks();
  42. for (int i = 0; i < num_chunks; i++) {
  43. const IffChunk *chunk = _surface->get_chunk(i);
  44. if (chunk->is_of_type(LwoSurfaceColor::get_class_type())) {
  45. const LwoSurfaceColor *color = DCAST(LwoSurfaceColor, chunk);
  46. _flags |= F_rgb;
  47. _rgb = color->_color;
  48. } else if (chunk->is_of_type(LwoSurfaceParameter::get_class_type())) {
  49. const LwoSurfaceParameter *param = DCAST(LwoSurfaceParameter, chunk);
  50. IffId type = param->get_id();
  51. if (type == IffId("DIFF")) {
  52. _flags |= F_diffuse;
  53. _diffuse = param->_value;
  54. } else if (type == IffId("LUMI")) {
  55. _flags |= F_luminosity;
  56. _luminosity = param->_value;
  57. } else if (type == IffId("SPEC")) {
  58. _flags |= F_specular;
  59. _specular = param->_value;
  60. } else if (type == IffId("REFL")) {
  61. _flags |= F_reflection;
  62. _reflection = param->_value;
  63. } else if (type == IffId("TRAN")) {
  64. _flags |= F_transparency;
  65. _transparency = param->_value;
  66. } else if (type == IffId("GLOS")) {
  67. _flags |= F_gloss;
  68. _gloss = param->_value;
  69. } else if (type == IffId("TRNL")) {
  70. _flags |= F_translucency;
  71. _translucency = param->_value;
  72. }
  73. } else if (chunk->is_of_type(LwoSurfaceSmoothingAngle::get_class_type())) {
  74. const LwoSurfaceSmoothingAngle *sa = DCAST(LwoSurfaceSmoothingAngle, chunk);
  75. _flags |= F_smooth_angle;
  76. _smooth_angle = sa->_angle;
  77. } else if (chunk->is_of_type(LwoSurfaceSidedness::get_class_type())) {
  78. const LwoSurfaceSidedness *sn = DCAST(LwoSurfaceSidedness, chunk);
  79. _flags |= F_backface;
  80. _backface = (sn->_sidedness == LwoSurfaceSidedness::S_front_and_back);
  81. } else if (chunk->is_of_type(LwoSurfaceBlock::get_class_type())) {
  82. const LwoSurfaceBlock *lwo_block = DCAST(LwoSurfaceBlock, chunk);
  83. // One of possibly several blocks in the texture that define additional
  84. // fancy rendering properties.
  85. CLwoSurfaceBlock *block = new CLwoSurfaceBlock(_converter, lwo_block);
  86. // We only consider enabled "IMAP" type blocks that affect "COLR".
  87. if (block->_block_type == IffId("IMAP") &&
  88. block->_channel_id == IffId("COLR") &&
  89. block->_enabled) {
  90. // Now save the block with the lowest ordinal.
  91. if (_block == nullptr) {
  92. _block = block;
  93. } else if (block->_ordinal < _block->_ordinal) {
  94. delete _block;
  95. _block = block;
  96. } else {
  97. delete block;
  98. }
  99. } else {
  100. delete block;
  101. }
  102. }
  103. }
  104. // Now get the four-component color, based on combining the RGB and the
  105. // transparency.
  106. _color.set(1.0, 1.0, 1.0, 1.0);
  107. if ((_flags & F_rgb) != 0) {
  108. _color[0] = _rgb[0];
  109. _color[1] = _rgb[1];
  110. _color[2] = _rgb[2];
  111. }
  112. if ((_flags & F_transparency) != 0) {
  113. _color[3] = 1.0 - _transparency;
  114. }
  115. _diffuse_color = _color;
  116. }
  117. /**
  118. *
  119. */
  120. CLwoSurface::
  121. ~CLwoSurface() {
  122. delete _block;
  123. }
  124. /**
  125. * Applies the color, texture, etc. described by the surface to the indicated
  126. * egg primitive.
  127. *
  128. * If the surface defines a smoothing angle, smooth_angle may be updated to
  129. * reflect it if the angle is greater than that specified.
  130. */
  131. void CLwoSurface::
  132. apply_properties(EggPrimitive *egg_prim, vector_PT_EggVertex &egg_vertices,
  133. PN_stdfloat &smooth_angle) {
  134. if (!_surface->_source.empty()) {
  135. // This surface is derived from another surface; apply that one first.
  136. CLwoSurface *parent = _converter->get_surface(_surface->_source);
  137. if (parent != nullptr && parent != this) {
  138. parent->apply_properties(egg_prim, egg_vertices, smooth_angle);
  139. }
  140. }
  141. bool has_texture = check_texture();
  142. bool has_material = check_material();
  143. egg_prim->set_color(_diffuse_color);
  144. if (has_material) {
  145. egg_prim->set_material(_egg_material);
  146. }
  147. if (has_texture) {
  148. egg_prim->set_texture(_egg_texture);
  149. // Assign UV's to the vertices.
  150. generate_uvs(egg_vertices);
  151. }
  152. if ((_flags & F_backface) != 0) {
  153. egg_prim->set_bface_flag(_backface);
  154. }
  155. if ((_flags & F_smooth_angle) != 0) {
  156. smooth_angle = std::max(smooth_angle, _smooth_angle);
  157. }
  158. }
  159. /**
  160. * Checks whether the surface demands a texture or not. Returns true if so,
  161. * false otherwise.
  162. *
  163. * If the surface demands a texture, this also sets up _egg_texture and
  164. * _compute_uvs as appropriate for the texture.
  165. */
  166. bool CLwoSurface::
  167. check_texture() {
  168. if (_checked_texture) {
  169. return (_egg_texture != nullptr);
  170. }
  171. _checked_texture = true;
  172. _egg_texture = nullptr;
  173. _map_uvs = nullptr;
  174. if (_block == nullptr) {
  175. // No texture. Not even a shader block.
  176. return false;
  177. }
  178. int clip_index = _block->_clip_index;
  179. if (clip_index < 0) {
  180. // No image file associated with the texture.
  181. return false;
  182. }
  183. CLwoClip *clip = _converter->get_clip(clip_index);
  184. if (clip == nullptr) {
  185. nout << "No clip image with index " << clip_index << "\n";
  186. return false;
  187. }
  188. if (!clip->is_still_image()) {
  189. // Can't do anything with an animated image right now.
  190. return false;
  191. }
  192. Filename pathname = _converter->convert_model_path(clip->_filename);
  193. _egg_texture = new EggTexture("clip" + format_string(clip_index), pathname);
  194. // Do we need to generate UV's?
  195. switch (_block->_projection_mode) {
  196. case LwoSurfaceBlockProjection::M_planar:
  197. _map_uvs = &CLwoSurface::map_planar;
  198. break;
  199. case LwoSurfaceBlockProjection::M_cylindrical:
  200. _map_uvs = &CLwoSurface::map_cylindrical;
  201. break;
  202. case LwoSurfaceBlockProjection::M_spherical:
  203. _map_uvs = &CLwoSurface::map_spherical;
  204. break;
  205. case LwoSurfaceBlockProjection::M_cubic:
  206. _map_uvs = &CLwoSurface::map_cubic;
  207. break;
  208. case LwoSurfaceBlockProjection::M_front:
  209. // Cannot generate "front" UV's, since this depends on a camera. Is it
  210. // supposed to be updated in real time, like a projected texture?
  211. break;
  212. case LwoSurfaceBlockProjection::M_uv:
  213. // "uv" projection means to use the existing UV's already defined for the
  214. // vertex. This case was already handled in the code that created the
  215. // EggVertex pointers.
  216. break;
  217. };
  218. // Texture overrides the primitive's natural color.
  219. _color[0] = 1.0;
  220. _color[1] = 1.0;
  221. _color[2] = 1.0;
  222. return true;
  223. }
  224. /**
  225. * Checks whether the surface demands a material or not. Returns true if so,
  226. * false otherwise.
  227. */
  228. bool CLwoSurface::
  229. check_material() {
  230. if (_checked_material) {
  231. return (_egg_material != nullptr);
  232. }
  233. _checked_material = true;
  234. _egg_material = nullptr;
  235. if (!_converter->_make_materials) {
  236. // If we aren't making materials, then don't make a material.
  237. return false;
  238. }
  239. _egg_material = new EggMaterial(get_name());
  240. if ((_flags & F_diffuse) != 0) {
  241. _diffuse_color.set(_color[0] * _diffuse,
  242. _color[1] * _diffuse,
  243. _color[2] * _diffuse,
  244. _color[3]);
  245. // We want to avoid setting the diffuse color on the material. We're
  246. // already setting the color explicitly on the object, so there's no need
  247. // to also set a diffuse color on the material, and doing so prevents nice
  248. // features like set_color() and set_color_scale() from working in Panda.
  249. // _egg_material->set_diff(_diffuse_color);
  250. }
  251. if ((_flags & F_luminosity) != 0) {
  252. LColor luminosity(_color[0] * _luminosity,
  253. _color[1] * _luminosity,
  254. _color[2] * _luminosity,
  255. 1.0);
  256. _egg_material->set_emit(luminosity);
  257. }
  258. if ((_flags & F_specular) != 0) {
  259. LColor specular(_color[0] * _specular,
  260. _color[1] * _specular,
  261. _color[2] * _specular,
  262. 1.0);
  263. _egg_material->set_spec(specular);
  264. }
  265. if ((_flags & F_gloss) != 0) {
  266. _egg_material->set_shininess(_gloss * 128.0);
  267. }
  268. return true;
  269. }
  270. /**
  271. * Computes all the UV's for the polygon's vertices, according to the
  272. * _projection_mode defined in the block.
  273. */
  274. void CLwoSurface::
  275. generate_uvs(vector_PT_EggVertex &egg_vertices) {
  276. if (_map_uvs == nullptr) {
  277. return;
  278. }
  279. // To do this properly near seams and singularities (for instance, the back
  280. // seam and the poles of the spherical map), we will need to know the
  281. // polygon's centroid.
  282. LPoint3d centroid(0.0, 0.0, 0.0);
  283. vector_PT_EggVertex::const_iterator vi;
  284. for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
  285. EggVertex *egg_vertex = (*vi);
  286. centroid += egg_vertex->get_pos3();
  287. }
  288. centroid /= (double)egg_vertices.size();
  289. centroid = centroid * _block->_inv_transform;
  290. // Now go back through and actually compute the UV's.
  291. for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
  292. EggVertex *egg_vertex = (*vi);
  293. LPoint3d pos = egg_vertex->get_pos3() * _block->_inv_transform;
  294. LPoint2d uv = (this->*_map_uvs)(pos, centroid);
  295. egg_vertex->set_uv(uv);
  296. }
  297. }
  298. /**
  299. * Computes a UV based on the given point in space, using a planar projection.
  300. */
  301. LPoint2d CLwoSurface::
  302. map_planar(const LPoint3d &pos, const LPoint3d &) const {
  303. // A planar projection is about as easy as can be. We ignore the Y axis,
  304. // and project the point into the XZ plane. Done.
  305. double u = (pos[0] + 0.5);
  306. double v = (pos[2] + 0.5);
  307. return LPoint2d(u, v);
  308. }
  309. /**
  310. * Computes a UV based on the given point in space, using a spherical
  311. * projection.
  312. */
  313. LPoint2d CLwoSurface::
  314. map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const {
  315. // To compute the x position on the frame, we only need to consider the
  316. // angle of the vector about the Y axis. Project the vector into the XZ
  317. // plane to do this.
  318. LVector2d xz_orig(pos[0], pos[2]);
  319. LVector2d xz = xz_orig;
  320. double u_offset = 0.0;
  321. if (xz == LVector2d::zero()) {
  322. // If we have a point on either pole, we've got problems. This point maps
  323. // to the entire bottom edge of the image, so which U value should we
  324. // choose? It does make a difference, especially if we have a number of
  325. // polygons around the south pole that all share the common vertex.
  326. // We choose the U value based on the polygon's centroid.
  327. xz.set(centroid[0], centroid[2]);
  328. } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
  329. // Now, if our polygon crosses the seam along the back of the sphere--that
  330. // is, the point is on the back of the sphere (xz[1] >= 0.0) and not on
  331. // the same side of the XZ plane as the centroid, we've got problems too.
  332. // We need to add an offset to the computed U value, either 1 or -1, to
  333. // keep all the vertices of the polygon on the same side of the seam.
  334. u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
  335. }
  336. // The U value is based on the longitude: the angle about the Y axis.
  337. double u =
  338. (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
  339. // Now rotate the vector into the YZ plane, and the V value is based on the
  340. // latitude: the angle about the X axis.
  341. LVector2d yz(pos[1], xz_orig.length());
  342. double v =
  343. (atan2(yz[0], yz[1]) / MathNumbers::pi + 0.5) * _block->_h_repeat;
  344. return LPoint2d(u, v);
  345. }
  346. /**
  347. * Computes a UV based on the given point in space, using a cylindrical
  348. * projection.
  349. */
  350. LPoint2d CLwoSurface::
  351. map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const {
  352. // This is almost identical to the spherical projection, except for the
  353. // computation of V.
  354. LVector2d xz(pos[0], pos[2]);
  355. double u_offset = 0.0;
  356. if (xz == LVector2d::zero()) {
  357. // Although a cylindrical mapping does not really have a singularity at
  358. // the pole, it's still possible to put a point there, and we'd like to do
  359. // the right thing with the polygon that shares that point. So the
  360. // singularity logic remains.
  361. xz.set(centroid[0], centroid[2]);
  362. } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
  363. // And cylinders do still have a seam at the back.
  364. u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
  365. }
  366. double u =
  367. (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
  368. // For a cylindrical mapping, the V value comes almost directly from Y.
  369. // Easy.
  370. double v = (pos[1] + 0.5);
  371. return LPoint2d(u, v);
  372. }
  373. /**
  374. * Computes a UV based on the given point in space, using a cubic projection.
  375. */
  376. LPoint2d CLwoSurface::
  377. map_cubic(const LPoint3d &pos, const LPoint3d &centroid) const {
  378. // A cubic projection is a planar projection, but we eliminate the dominant
  379. // axis (based on the polygon's centroid) instead of arbitrarily eliminating
  380. // Y.
  381. double x = fabs(centroid[0]);
  382. double y = fabs(centroid[1]);
  383. double z = fabs(centroid[2]);
  384. double u, v;
  385. if (x > y) {
  386. if (x > z) {
  387. // X is dominant.
  388. u = (pos[2] + 0.5);
  389. v = (pos[1] + 0.5);
  390. } else {
  391. // Z is dominant.
  392. u = (pos[0] + 0.5);
  393. v = (pos[1] + 0.5);
  394. }
  395. } else {
  396. if (y > z) {
  397. // Y is dominant.
  398. u = (pos[0] + 0.5);
  399. v = (pos[2] + 0.5);
  400. } else {
  401. // Z is dominant.
  402. u = (pos[0] + 0.5);
  403. v = (pos[1] + 0.5);
  404. }
  405. }
  406. return LPoint2d(u, v);
  407. }