Panda3D
Loading...
Searching...
No Matches
cLwoSurface.cxx
Go to the documentation of this file.
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
14#include "cLwoSurface.h"
15#include "cLwoSurfaceBlock.h"
16#include "cLwoClip.h"
17#include "lwoToEggConverter.h"
18
19#include "lwoSurfaceColor.h"
20#include "lwoSurfaceParameter.h"
22#include "lwoSurfaceSidedness.h"
23#include "lwoSurfaceBlock.h"
24#include "eggPrimitive.h"
25#include "string_utils.h"
26#include "mathNumbers.h"
27#include "dcast.h"
28
29
30/**
31 *
32 */
33CLwoSurface::
34CLwoSurface(LwoToEggConverter *converter, const LwoSurface *surface) :
35 _converter(converter),
36 _surface(surface)
37{
38 _flags = 0;
39 _rgb.set(1.0, 1.0, 1.0);
40 _checked_material = false;
41 _checked_texture = false;
42 _map_uvs = nullptr;
43 _block = nullptr;
44
45 // Walk through the chunk list, looking for some basic properties.
46 int num_chunks = _surface->get_num_chunks();
47 for (int i = 0; i < num_chunks; i++) {
48 const IffChunk *chunk = _surface->get_chunk(i);
49
50 if (chunk->is_of_type(LwoSurfaceColor::get_class_type())) {
51 const LwoSurfaceColor *color = DCAST(LwoSurfaceColor, chunk);
52 _flags |= F_rgb;
53 _rgb = color->_color;
54
55 } else if (chunk->is_of_type(LwoSurfaceParameter::get_class_type())) {
56 const LwoSurfaceParameter *param = DCAST(LwoSurfaceParameter, chunk);
57 IffId type = param->get_id();
58
59 if (type == IffId("DIFF")) {
60 _flags |= F_diffuse;
61 _diffuse = param->_value;
62
63 } else if (type == IffId("LUMI")) {
64 _flags |= F_luminosity;
65 _luminosity = param->_value;
66
67 } else if (type == IffId("SPEC")) {
68 _flags |= F_specular;
69 _specular = param->_value;
70
71 } else if (type == IffId("REFL")) {
72 _flags |= F_reflection;
73 _reflection = param->_value;
74
75 } else if (type == IffId("TRAN")) {
76 _flags |= F_transparency;
77 _transparency = param->_value;
78
79 } else if (type == IffId("GLOS")) {
80 _flags |= F_gloss;
81 _gloss = param->_value;
82
83 } else if (type == IffId("TRNL")) {
84 _flags |= F_translucency;
85 _translucency = param->_value;
86 }
87
88 } else if (chunk->is_of_type(LwoSurfaceSmoothingAngle::get_class_type())) {
89 const LwoSurfaceSmoothingAngle *sa = DCAST(LwoSurfaceSmoothingAngle, chunk);
90 _flags |= F_smooth_angle;
91 _smooth_angle = sa->_angle;
92
93 } else if (chunk->is_of_type(LwoSurfaceSidedness::get_class_type())) {
94 const LwoSurfaceSidedness *sn = DCAST(LwoSurfaceSidedness, chunk);
95 _flags |= F_backface;
96 _backface = (sn->_sidedness == LwoSurfaceSidedness::S_front_and_back);
97
98 } else if (chunk->is_of_type(LwoSurfaceBlock::get_class_type())) {
99 const LwoSurfaceBlock *lwo_block = DCAST(LwoSurfaceBlock, chunk);
100 // One of possibly several blocks in the texture that define additional
101 // fancy rendering properties.
102
103 CLwoSurfaceBlock *block = new CLwoSurfaceBlock(_converter, lwo_block);
104
105 // We only consider enabled "IMAP" type blocks that affect "COLR".
106 if (block->_block_type == IffId("IMAP") &&
107 block->_channel_id == IffId("COLR") &&
108 block->_enabled) {
109 // Now save the block with the lowest ordinal.
110 if (_block == nullptr) {
111 _block = block;
112
113 } else if (block->_ordinal < _block->_ordinal) {
114 delete _block;
115 _block = block;
116
117 } else {
118 delete block;
119 }
120
121 } else {
122 delete block;
123 }
124 }
125 }
126
127 // Now get the four-component color, based on combining the RGB and the
128 // transparency.
129 _color.set(1.0, 1.0, 1.0, 1.0);
130
131 if ((_flags & F_rgb) != 0) {
132 _color[0] = _rgb[0];
133 _color[1] = _rgb[1];
134 _color[2] = _rgb[2];
135 }
136
137 if ((_flags & F_transparency) != 0) {
138 _color[3] = 1.0 - _transparency;
139 }
140
141 _diffuse_color = _color;
142}
143
144/**
145 *
146 */
147CLwoSurface::
148~CLwoSurface() {
149 if (_block != nullptr) {
150 delete _block;
151 }
152}
153
154/**
155 * Applies the color, texture, etc. described by the surface to the indicated
156 * egg primitive.
157 *
158 * If the surface defines a smoothing angle, smooth_angle may be updated to
159 * reflect it if the angle is greater than that specified.
160 */
162apply_properties(EggPrimitive *egg_prim, vector_PT_EggVertex &egg_vertices,
163 PN_stdfloat &smooth_angle) {
164 if (!_surface->_source.empty()) {
165 // This surface is derived from another surface; apply that one first.
166 CLwoSurface *parent = _converter->get_surface(_surface->_source);
167 if (parent != nullptr && parent != this) {
168 parent->apply_properties(egg_prim, egg_vertices, smooth_angle);
169 }
170 }
171
172 bool has_texture = check_texture();
173 bool has_material = check_material();
174
175 egg_prim->set_color(_diffuse_color);
176
177 if (has_material) {
178 egg_prim->set_material(_egg_material);
179 }
180
181 if (has_texture) {
182 egg_prim->set_texture(_egg_texture);
183
184 // Assign UV's to the vertices.
185 generate_uvs(egg_vertices);
186 }
187
188 if ((_flags & F_backface) != 0) {
189 egg_prim->set_bface_flag(_backface);
190 }
191
192 if ((_flags & F_smooth_angle) != 0) {
193 smooth_angle = std::max(smooth_angle, _smooth_angle);
194 }
195}
196
197/**
198 * Checks whether the surface demands a texture or not. Returns true if so,
199 * false otherwise.
200 *
201 * If the surface demands a texture, this also sets up _egg_texture and
202 * _compute_uvs as appropriate for the texture.
203 */
206 if (_checked_texture) {
207 return (_egg_texture != nullptr);
208 }
209 _checked_texture = true;
210 _egg_texture = nullptr;
211 _map_uvs = nullptr;
212
213 if (_block == nullptr) {
214 // No texture. Not even a shader block.
215 return false;
216 }
217
218 int clip_index = _block->_clip_index;
219 if (clip_index < 0) {
220 // No image file associated with the texture.
221 return false;
222 }
223
224 CLwoClip *clip = _converter->get_clip(clip_index);
225 if (clip == nullptr) {
226 nout << "No clip image with index " << clip_index << "\n";
227 return false;
228 }
229
230 if (!clip->is_still_image()) {
231 // Can't do anything with an animated image right now.
232 return false;
233 }
234
235 Filename pathname = _converter->convert_model_path(clip->_filename);
236
237 _egg_texture = new EggTexture("clip" + format_string(clip_index), pathname);
238
239 // Do we need to generate UV's?
240 switch (_block->_projection_mode) {
241 case LwoSurfaceBlockProjection::M_planar:
242 _map_uvs = &CLwoSurface::map_planar;
243 break;
244
245 case LwoSurfaceBlockProjection::M_cylindrical:
246 _map_uvs = &CLwoSurface::map_cylindrical;
247 break;
248
249 case LwoSurfaceBlockProjection::M_spherical:
250 _map_uvs = &CLwoSurface::map_spherical;
251 break;
252
253 case LwoSurfaceBlockProjection::M_cubic:
254 _map_uvs = &CLwoSurface::map_cubic;
255 break;
256
257 case LwoSurfaceBlockProjection::M_front:
258 // Cannot generate "front" UV's, since this depends on a camera. Is it
259 // supposed to be updated in real time, like a projected texture?
260 break;
261
262 case LwoSurfaceBlockProjection::M_uv:
263 // "uv" projection means to use the existing UV's already defined for the
264 // vertex. This case was already handled in the code that created the
265 // EggVertex pointers.
266 break;
267 };
268
269 // Texture overrides the primitive's natural color.
270 _color[0] = 1.0;
271 _color[1] = 1.0;
272 _color[2] = 1.0;
273
274 return true;
275}
276
277/**
278 * Checks whether the surface demands a material or not. Returns true if so,
279 * false otherwise.
280 */
283 if (_checked_material) {
284 return (_egg_material != nullptr);
285 }
286 _checked_material = true;
287 _egg_material = nullptr;
288
289 if (!_converter->_make_materials) {
290 // If we aren't making materials, then don't make a material.
291 return false;
292 }
293
294 _egg_material = new EggMaterial(get_name());
295
296 if ((_flags & F_diffuse) != 0) {
297 _diffuse_color.set(_color[0] * _diffuse,
298 _color[1] * _diffuse,
299 _color[2] * _diffuse,
300 _color[3]);
301 // We want to avoid setting the diffuse color on the material. We're
302 // already setting the color explicitly on the object, so there's no need
303 // to also set a diffuse color on the material, and doing so prevents nice
304 // features like set_color() and set_color_scale() from working in Panda.
305
306 // _egg_material->set_diff(_diffuse_color);
307 }
308
309 if ((_flags & F_luminosity) != 0) {
310 LColor luminosity(_color[0] * _luminosity,
311 _color[1] * _luminosity,
312 _color[2] * _luminosity,
313 1.0);
314 _egg_material->set_emit(luminosity);
315 }
316
317 if ((_flags & F_specular) != 0) {
318 LColor specular(_color[0] * _specular,
319 _color[1] * _specular,
320 _color[2] * _specular,
321 1.0);
322 _egg_material->set_spec(specular);
323 }
324
325 if ((_flags & F_gloss) != 0) {
326 _egg_material->set_shininess(_gloss * 128.0);
327 }
328
329 return true;
330}
331
332
333/**
334 * Computes all the UV's for the polygon's vertices, according to the
335 * _projection_mode defined in the block.
336 */
337void CLwoSurface::
338generate_uvs(vector_PT_EggVertex &egg_vertices) {
339 if (_map_uvs == nullptr) {
340 return;
341 }
342
343 // To do this properly near seams and singularities (for instance, the back
344 // seam and the poles of the spherical map), we will need to know the
345 // polygon's centroid.
346 LPoint3d centroid(0.0, 0.0, 0.0);
347
348 vector_PT_EggVertex::const_iterator vi;
349 for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
350 EggVertex *egg_vertex = (*vi);
351 centroid += egg_vertex->get_pos3();
352 }
353
354 centroid /= (double)egg_vertices.size();
355 centroid = centroid * _block->_inv_transform;
356
357 // Now go back through and actually compute the UV's.
358 for (vi = egg_vertices.begin(); vi != egg_vertices.end(); ++vi) {
359 EggVertex *egg_vertex = (*vi);
360 LPoint3d pos = egg_vertex->get_pos3() * _block->_inv_transform;
361 LPoint2d uv = (this->*_map_uvs)(pos, centroid);
362 egg_vertex->set_uv(uv);
363 }
364}
365
366/**
367 * Computes a UV based on the given point in space, using a planar projection.
368 */
369LPoint2d CLwoSurface::
370map_planar(const LPoint3d &pos, const LPoint3d &) const {
371 // A planar projection is about as easy as can be. We ignore the Y axis,
372 // and project the point into the XZ plane. Done.
373 double u = (pos[0] + 0.5);
374 double v = (pos[2] + 0.5);
375
376 return LPoint2d(u, v);
377}
378
379/**
380 * Computes a UV based on the given point in space, using a spherical
381 * projection.
382 */
383LPoint2d CLwoSurface::
384map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const {
385 // To compute the x position on the frame, we only need to consider the
386 // angle of the vector about the Y axis. Project the vector into the XZ
387 // plane to do this.
388
389 LVector2d xz_orig(pos[0], pos[2]);
390 LVector2d xz = xz_orig;
391 double u_offset = 0.0;
392
393 if (xz == LVector2d::zero()) {
394 // If we have a point on either pole, we've got problems. This point maps
395 // to the entire bottom edge of the image, so which U value should we
396 // choose? It does make a difference, especially if we have a number of
397 // polygons around the south pole that all share the common vertex.
398
399 // We choose the U value based on the polygon's centroid.
400 xz.set(centroid[0], centroid[2]);
401
402 } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
403 // Now, if our polygon crosses the seam along the back of the sphere--that
404 // is, the point is on the back of the sphere (xz[1] >= 0.0) and not on
405 // the same side of the XZ plane as the centroid, we've got problems too.
406 // We need to add an offset to the computed U value, either 1 or -1, to
407 // keep all the vertices of the polygon on the same side of the seam.
408
409 u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
410 }
411
412 // The U value is based on the longitude: the angle about the Y axis.
413 double u =
414 (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
415
416 // Now rotate the vector into the YZ plane, and the V value is based on the
417 // latitude: the angle about the X axis.
418 LVector2d yz(pos[1], xz_orig.length());
419 double v =
420 (atan2(yz[0], yz[1]) / MathNumbers::pi + 0.5) * _block->_h_repeat;
421
422 return LPoint2d(u, v);
423}
424
425/**
426 * Computes a UV based on the given point in space, using a cylindrical
427 * projection.
428 */
429LPoint2d CLwoSurface::
430map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const {
431 // This is almost identical to the spherical projection, except for the
432 // computation of V.
433
434 LVector2d xz(pos[0], pos[2]);
435 double u_offset = 0.0;
436
437 if (xz == LVector2d::zero()) {
438 // Although a cylindrical mapping does not really have a singularity at
439 // the pole, it's still possible to put a point there, and we'd like to do
440 // the right thing with the polygon that shares that point. So the
441 // singularity logic remains.
442 xz.set(centroid[0], centroid[2]);
443
444 } else if (xz[1] >= 0.0 && ((xz[0] < 0.0) != (centroid[0] < 0.))) {
445 // And cylinders do still have a seam at the back.
446 u_offset = (xz[0] < 0.0) ? 1.0 : -1.0;
447 }
448
449 double u =
450 (atan2(xz[0], -xz[1]) / (2.0 * MathNumbers::pi) + 0.5 + u_offset) * _block->_w_repeat;
451
452 // For a cylindrical mapping, the V value comes almost directly from Y.
453 // Easy.
454 double v = (pos[1] + 0.5);
455
456 return LPoint2d(u, v);
457}
458
459/**
460 * Computes a UV based on the given point in space, using a cubic projection.
461 */
462LPoint2d CLwoSurface::
463map_cubic(const LPoint3d &pos, const LPoint3d &centroid) const {
464 // A cubic projection is a planar projection, but we eliminate the dominant
465 // axis (based on the polygon's centroid) instead of arbitrarily eliminating
466 // Y.
467
468 double x = fabs(centroid[0]);
469 double y = fabs(centroid[1]);
470 double z = fabs(centroid[2]);
471
472 double u, v;
473
474 if (x > y) {
475 if (x > z) {
476 // X is dominant.
477 u = (pos[2] + 0.5);
478 v = (pos[1] + 0.5);
479 } else {
480 // Z is dominant.
481 u = (pos[0] + 0.5);
482 v = (pos[1] + 0.5);
483 }
484 } else {
485 if (y > z) {
486 // Y is dominant.
487 u = (pos[0] + 0.5);
488 v = (pos[2] + 0.5);
489 } else {
490 // Z is dominant.
491 u = (pos[0] + 0.5);
492 v = (pos[1] + 0.5);
493 }
494 }
495
496 return LPoint2d(u, v);
497}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is a wrapper around LwoClip and stores additional information useful during the conversion...
Definition cLwoClip.h:29
bool is_still_image() const
Returns true if this clip represents a still image, as opposed to an animated image.
Definition cLwoClip.I:29
This class is a wrapper around LwoSurfaceBlock and stores additional information useful during the co...
This class is a wrapper around LwoSurface and stores additional information useful during the convers...
Definition cLwoSurface.h:39
bool check_material()
Checks whether the surface demands a material or not.
const std::string & get_name() const
Returns the name of the surface.
Definition cLwoSurface.I:19
bool check_texture()
Checks whether the surface demands a texture or not.
void apply_properties(EggPrimitive *egg_prim, vector_PT_EggVertex &egg_vertices, PN_stdfloat &smooth_angle)
Applies the color, texture, etc.
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
set_bface_flag
Sets the backfacing flag of the polygon.
void set_texture(EggTexture *texture)
Replaces the current list of textures with the indicated texture.
set_material
Applies the indicated material to the primitive.
Defines a texture map that may be applied to geometry.
Definition eggTexture.h:30
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition eggVertex.h:39
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition eggVertex.I:131
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition eggVertex.I:193
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
The basic kind of record in an EA "IFF" file, which the LightWave object file is based on.
Definition iffChunk.h:30
IffId get_id() const
Returns the ID associated with this chunk.
Definition iffChunk.I:25
A four-byte chunk ID appearing in an "IFF" file.
Definition iffId.h:26
A texture layer or shader, part of a LwoSurface chunk.
Records the base color of a surface, as an entry within a LwoSurface chunk.
Records some parameter value of a surface material, as an entry within a LwoSurface chunk.
Records whether polygons are frontfacing only or backfacing also.
Indicates the maximum angle (in radians) between adjacent polygons that should be smooth-shaded.
Describes the shading attributes of a surface.
Definition lwoSurface.h:25
This class supervises the construction of an EggData structure from the data represented by the LwoHe...
CLwoSurface * get_surface(const std::string &name) const
Returns a pointer to the surface definition with the given name, or NULL if there is no such surface.
CLwoClip * get_clip(int number) const
Returns a pointer to the clip with the given index number, or NULL if there is no such clip.
Filename convert_model_path(const Filename &orig_filename)
Converts the indicated model filename to a relative or absolute or whatever filename,...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.