diff --git a/pandatool/src/eggprogs/lwoToEgg.cxx b/pandatool/src/eggprogs/lwoToEgg.cxx index ad1c0aed0c..4a9d411b8d 100644 --- a/pandatool/src/eggprogs/lwoToEgg.cxx +++ b/pandatool/src/eggprogs/lwoToEgg.cxx @@ -62,6 +62,12 @@ run() { } LwoHeader *header = DCAST(LwoHeader, chunk); + if (!header->is_valid()) { + nout << "File " << _input_filename + << " is not recognized as a Lightwave Object file. " + << "Perhaps the version is too recent.\n"; + exit(1); + } _data.set_coordinate_system(_coordinate_system); diff --git a/pandatool/src/lwo/Sources.pp b/pandatool/src/lwo/Sources.pp index 3aae66d3f0..b0e098618e 100644 --- a/pandatool/src/lwo/Sources.pp +++ b/pandatool/src/lwo/Sources.pp @@ -16,8 +16,8 @@ lwoClip.cxx lwoClip.h \ lwoDiscontinuousVertexMap.cxx lwoDiscontinuousVertexMap.h \ lwoGroupChunk.cxx lwoGroupChunk.h \ - lwoHeader.cxx \ - lwoHeader.h lwoInputFile.cxx lwoInputFile.h \ + lwoHeader.I lwoHeader.cxx lwoHeader.h \ + lwoInputFile.I lwoInputFile.cxx lwoInputFile.h \ lwoLayer.h lwoLayer.cxx \ lwoPoints.h lwoPoints.cxx \ lwoPolygons.h lwoPolygons.cxx \ @@ -53,8 +53,8 @@ lwoClip.h \ lwoDiscontinuousVertexMap.h \ lwoGroupChunk.h \ - lwoHeader.h \ - lwoInputFile.h \ + lwoHeader.I lwoHeader.h \ + lwoInputFile.I lwoInputFile.h \ lwoLayer.h \ lwoPoints.h \ lwoPolygons.h \ diff --git a/pandatool/src/lwo/iffId.cxx b/pandatool/src/lwo/iffId.cxx index 45d6b2a3b0..2342fc3391 100644 --- a/pandatool/src/lwo/iffId.cxx +++ b/pandatool/src/lwo/iffId.cxx @@ -4,3 +4,32 @@ //////////////////////////////////////////////////////////////////// #include "iffId.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: IffId::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void IffId:: +output(ostream &out) const { + // If all of the characters are printable, just output them. + if (isprint(_id._c[0]) && isprint(_id._c[1]) && + isprint(_id._c[2]) && isprint(_id._c[3])) { + out << _id._c[0] << _id._c[1] << _id._c[2] << _id._c[3]; + + } else if (isprint(_id._c[0]) && isprint(_id._c[1]) && + isprint(_id._c[2]) && _id._c[3] == '\0') { + // If the last character is 0, output a 3-letter ID. + out << _id._c[0] << _id._c[1] << _id._c[2]; + + } else { + // Otherwise, write out the hex. + out << "0x" << hex << setfill('0'); + for (int i = 0; i < 4; i++) { + out << setw(2) << (int)(unsigned char)_id._c[i]; + } + out << dec << setfill(' '); + } +} diff --git a/pandatool/src/lwo/iffId.h b/pandatool/src/lwo/iffId.h index b578a63c95..207e97c9e4 100644 --- a/pandatool/src/lwo/iffId.h +++ b/pandatool/src/lwo/iffId.h @@ -29,6 +29,8 @@ public: INLINE bool operator < (const IffId &other) const; INLINE string get_name() const; + + void output(ostream &out) const; private: union { @@ -40,7 +42,8 @@ private: #include "iffId.I" INLINE ostream &operator << (ostream &out, const IffId &id) { - return out << id.get_name(); + id.output(out); + return out; } #endif diff --git a/pandatool/src/lwo/iffInputFile.cxx b/pandatool/src/lwo/iffInputFile.cxx index a759eead9f..36c592e9fc 100644 --- a/pandatool/src/lwo/iffInputFile.cxx +++ b/pandatool/src/lwo/iffInputFile.cxx @@ -21,6 +21,7 @@ IffInputFile() { _input = (istream *)NULL; _owns_istream = false; _eof = true; + _unexpected_eof = false; _bytes_read = 0; } @@ -70,6 +71,7 @@ set_input(istream *input, bool owns_istream) { _input = input; _owns_istream = owns_istream; _eof = false; + _unexpected_eof = false; _bytes_read = 0; } @@ -241,7 +243,10 @@ get_chunk() { if (chunk->read_iff(this, end_point)) { if (is_eof()) { - nout << "Unexpected EOF on file.\n"; + if (!_unexpected_eof) { + nout << "Unexpected EOF on file reading " << *chunk << "\n"; + _unexpected_eof = true; + } return (IffChunk *)NULL; } @@ -292,7 +297,10 @@ get_subchunk(IffChunk *context) { if (chunk->read_iff(this, end_point)) { if (is_eof()) { - nout << "Unexpected EOF on file.\n"; + if (!_unexpected_eof) { + nout << "Unexpected EOF on file reading " << *chunk << "\n"; + _unexpected_eof = true; + } return (IffChunk *)NULL; } diff --git a/pandatool/src/lwo/iffInputFile.h b/pandatool/src/lwo/iffInputFile.h index 20c2880c5f..0bf694d980 100644 --- a/pandatool/src/lwo/iffInputFile.h +++ b/pandatool/src/lwo/iffInputFile.h @@ -64,6 +64,7 @@ protected: Filename _filename; bool _owns_istream; bool _eof; + bool _unexpected_eof; size_t _bytes_read; public: diff --git a/pandatool/src/lwo/lwoHeader.I b/pandatool/src/lwo/lwoHeader.I new file mode 100644 index 0000000000..0f2e21591a --- /dev/null +++ b/pandatool/src/lwo/lwoHeader.I @@ -0,0 +1,27 @@ +// Filename: lwoHeader.I +// Created by: drose (24Apr01) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: LwoHeader::is_valid +// Access: Public +// Description: Returns true if the header represents a valid and +// recognized Lightwave header, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool LwoHeader:: +is_valid() const { + return _valid; +} + +//////////////////////////////////////////////////////////////////// +// Function: LwoHeader::get_version +// Access: Public +// Description: Returns the version of the Lightwave file. +//////////////////////////////////////////////////////////////////// +INLINE double LwoHeader:: +get_version() const { + return _version; +} + diff --git a/pandatool/src/lwo/lwoHeader.cxx b/pandatool/src/lwo/lwoHeader.cxx index 4acf169eff..493fb6131f 100644 --- a/pandatool/src/lwo/lwoHeader.cxx +++ b/pandatool/src/lwo/lwoHeader.cxx @@ -4,12 +4,23 @@ //////////////////////////////////////////////////////////////////// #include "lwoHeader.h" -#include "iffInputFile.h" +#include "lwoInputFile.h" #include TypeHandle LwoHeader::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: LwoHeader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LwoHeader:: +LwoHeader() { + _valid = false; + _version = 0.0; +} + //////////////////////////////////////////////////////////////////// // Function: LwoHeader::read_iff // Access: Public, Virtual @@ -22,8 +33,24 @@ TypeHandle LwoHeader::_type_handle; //////////////////////////////////////////////////////////////////// bool LwoHeader:: read_iff(IffInputFile *in, size_t stop_at) { - _lwid = in->get_id(); - read_chunks_iff(in, stop_at); + LwoInputFile *lin = DCAST(LwoInputFile, in); + + _lwid = lin->get_id(); + + if (_lwid == IffId("LWO2")) { + _valid = true; + _version = 6.0; + } else if (_lwid == IffId("LWOB")) { + _valid = true; + _version = 5.0; + } + + if (_valid) { + lin->set_lwo_version(_version); + } + + read_chunks_iff(lin, stop_at); + return true; } diff --git a/pandatool/src/lwo/lwoHeader.h b/pandatool/src/lwo/lwoHeader.h index 14890753cd..0d825adac1 100644 --- a/pandatool/src/lwo/lwoHeader.h +++ b/pandatool/src/lwo/lwoHeader.h @@ -16,11 +16,20 @@ //////////////////////////////////////////////////////////////////// class LwoHeader : public LwoGroupChunk { public: + LwoHeader(); + IffId _lwid; + + INLINE bool is_valid() const; + INLINE double get_version() const; public: virtual bool read_iff(IffInputFile *in, size_t stop_at); virtual void write(ostream &out, int indent_level = 0) const; + +private: + bool _valid; + double _version; public: virtual TypeHandle get_type() const { @@ -40,6 +49,8 @@ private: static TypeHandle _type_handle; }; +#include "lwoHeader.I" + #endif diff --git a/pandatool/src/lwo/lwoInputFile.I b/pandatool/src/lwo/lwoInputFile.I new file mode 100644 index 0000000000..3cefb8e136 --- /dev/null +++ b/pandatool/src/lwo/lwoInputFile.I @@ -0,0 +1,31 @@ +// Filename: lwoInputFile.I +// Created by: drose (24Apr01) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: LwoInputFile::get_lwo_version +// Access: Public +// Description: Returns the version of the Lightwave file being read. +// This is unknown until the header record has been +// read; then it will be set by the header. +//////////////////////////////////////////////////////////////////// +INLINE double LwoInputFile:: +get_lwo_version() const { + return _lwo_version; +} + + +//////////////////////////////////////////////////////////////////// +// Function: LwoInputFile::set_lwo_version +// Access: Public +// Description: Changes the version number reported for the Lightwave +// file. Normally this is only called by LwoHeader as +// it is read. +//////////////////////////////////////////////////////////////////// +INLINE void LwoInputFile:: +set_lwo_version(double lwo_version) { + _lwo_version = lwo_version; +} + diff --git a/pandatool/src/lwo/lwoInputFile.cxx b/pandatool/src/lwo/lwoInputFile.cxx index f905ad8df2..27d1b998cc 100644 --- a/pandatool/src/lwo/lwoInputFile.cxx +++ b/pandatool/src/lwo/lwoInputFile.cxx @@ -120,7 +120,8 @@ make_new_chunk(IffId id) { } else if (id == IffId("POLS")) { return new LwoPolygons; - } else if (id == IffId("TAGS")) { + } else if (id == IffId("TAGS") || + id == IffId("SRFS")) { return new LwoTags; } else if (id == IffId("PTAG")) { diff --git a/pandatool/src/lwo/lwoInputFile.h b/pandatool/src/lwo/lwoInputFile.h index bc20fe7ede..1c172fc5c2 100644 --- a/pandatool/src/lwo/lwoInputFile.h +++ b/pandatool/src/lwo/lwoInputFile.h @@ -22,6 +22,9 @@ public: LwoInputFile(); ~LwoInputFile(); + INLINE double get_lwo_version() const; + INLINE void set_lwo_version(double version); + int get_vx(); LVecBase3f get_vec3(); Filename get_filename(); @@ -29,6 +32,9 @@ public: protected: virtual IffChunk *make_new_chunk(IffId id); +private: + double _lwo_version; + public: virtual TypeHandle get_type() const { return get_class_type(); @@ -47,6 +53,8 @@ private: static TypeHandle _type_handle; }; +#include "lwoInputFile.I" + #endif diff --git a/pandatool/src/lwo/lwoPolygons.cxx b/pandatool/src/lwo/lwoPolygons.cxx index 653b47f1fc..60a0dcec1d 100644 --- a/pandatool/src/lwo/lwoPolygons.cxx +++ b/pandatool/src/lwo/lwoPolygons.cxx @@ -45,23 +45,68 @@ bool LwoPolygons:: read_iff(IffInputFile *in, size_t stop_at) { LwoInputFile *lin = DCAST(LwoInputFile, in); - _polygon_type = lin->get_id(); + if (lin->get_lwo_version() >= 6.0) { + // 6.x style syntax: + // POLS { type[ID4], ( numvert+flags[U2], vert[VX] # numvert )* } - while (lin->get_bytes_read() < stop_at && !lin->is_eof()) { - int nf = lin->get_be_int16(); - int num_vertices = nf & PF_numverts_mask; + _polygon_type = lin->get_id(); - PT(Polygon) poly = new Polygon; - poly->_flags = nf & ~PF_numverts_mask; - - for (int i = 0; i < num_vertices; i++) { - poly->_vertices.push_back(lin->get_vx()); + while (lin->get_bytes_read() < stop_at && !lin->is_eof()) { + int nf = lin->get_be_uint16(); + int num_vertices = nf & PF_numverts_mask; + + PT(Polygon) poly = new Polygon; + poly->_flags = nf & ~PF_numverts_mask; + poly->_surface_index = -1; + + for (int i = 0; i < num_vertices; i++) { + int vindex = lin->get_vx(); + poly->_vertices.push_back(vindex); + } + + _polygons.push_back(poly); } - _polygons.push_back(poly); + } else { + // 5.x style syntax: + // POLS { ( numvert[U2], vert[VX] # numvert, +/-(surf+1)[I2], numdetail[U2]? )* } + _polygon_type = IffId("FACE"); + + int num_decals = 0; + while (lin->get_bytes_read() < stop_at && !lin->is_eof()) { + int num_vertices = lin->get_be_uint16(); + + PT(Polygon) poly = new Polygon; + poly->_flags = 0; + + for (int i = 0; i < num_vertices; i++) { + int vindex = lin->get_vx(); + poly->_vertices.push_back(vindex); + } + + int surface = lin->get_be_int16(); + + if (num_decals > 0) { + // This is a decal polygon of a previous polygon. + num_decals--; + poly->_flags |= PF_decal; + + } else { + if (surface < 0) { + num_decals = lin->get_be_int16(); + surface = -surface; + } + } + + // The surface index is stored +1 to allow signedness to be + // examined. + poly->_surface_index = surface - 1; + + _polygons.push_back(poly); + } } - return (lin->get_bytes_read() == stop_at); + return true; } //////////////////////////////////////////////////////////////////// diff --git a/pandatool/src/lwo/lwoPolygons.h b/pandatool/src/lwo/lwoPolygons.h index f4780c7ed5..04cf3a7f06 100644 --- a/pandatool/src/lwo/lwoPolygons.h +++ b/pandatool/src/lwo/lwoPolygons.h @@ -25,13 +25,24 @@ public: enum PolygonFlags { PF_continuity_1 = 0x0400, PF_continuity_2 = 0x0800, - PF_numverts_mask = 0x03ff + PF_numverts_mask = 0x03ff, + + // This "flag" is stored artificially when reading 5.x LWOB files, + // and indicates that the polygon is a decal of a preceding + // polygon. + PF_decal = 0x0001 }; class Polygon : public ReferenceCount { public: int _flags; vector_int _vertices; + + // This value is only filled in when reading 5.x LWOB files, and + // indicates the surface index of the polygon within a preceding + // SRFS (LwoTags) chunk. For 6.x and later files, this will be + // set to -1. + int _surface_index; }; int get_num_polygons() const; diff --git a/pandatool/src/lwo/lwoTags.h b/pandatool/src/lwo/lwoTags.h index a5d3ea23dc..616a82764d 100644 --- a/pandatool/src/lwo/lwoTags.h +++ b/pandatool/src/lwo/lwoTags.h @@ -17,6 +17,11 @@ // Class : LwoTags // Description : An array of tag strings that will be referenced by // later chunks. +// +// This also serves as an array of surface names to be +// referenced by a later LwoPolygons chunk, in 5.x LWOB +// files. The chunk id can be used to differentiate the +// meaning (TAGS vs. SRFS). //////////////////////////////////////////////////////////////////// class LwoTags : public LwoChunk { public: diff --git a/pandatool/src/lwoegg/cLwoPoints.cxx b/pandatool/src/lwoegg/cLwoPoints.cxx index 6410d23810..16e0b472ec 100644 --- a/pandatool/src/lwoegg/cLwoPoints.cxx +++ b/pandatool/src/lwoegg/cLwoPoints.cxx @@ -7,8 +7,26 @@ #include "lwoToEggConverter.h" #include "cLwoLayer.h" +#include #include +//////////////////////////////////////////////////////////////////// +// Function: CLwoPoints::add_vmap +// Access: Public +// Description: Associated the indicated VertexMap with the points +// set. This may define such niceties as UV coordinates +// or per-vertex color. +//////////////////////////////////////////////////////////////////// +void CLwoPoints:: +add_vmap(const LwoVertexMap *lwo_vmap) { + VMapNames &names = _vmap[lwo_vmap->_map_type]; + bool inserted = names.insert(VMapNames::value_type(lwo_vmap->_name, lwo_vmap)).second; + if (!inserted) { + nout << "Multiple vertex maps on the same points of type " + << lwo_vmap->_map_type << " named " << lwo_vmap->_name << "\n"; + } +} + //////////////////////////////////////////////////////////////////// // Function: CLwoPoints::make_egg // Access: Public diff --git a/pandatool/src/lwoegg/cLwoPoints.h b/pandatool/src/lwoegg/cLwoPoints.h index 8c55b62cbf..b163413fa3 100644 --- a/pandatool/src/lwoegg/cLwoPoints.h +++ b/pandatool/src/lwoegg/cLwoPoints.h @@ -12,7 +12,10 @@ #include #include +#include + class LwoToEggConverter; +class LwoVertexMap; class CLwoLayer; //////////////////////////////////////////////////////////////////// @@ -26,6 +29,8 @@ public: INLINE CLwoPoints(LwoToEggConverter *converter, const LwoPoints *points, CLwoLayer *layer); + void add_vmap(const LwoVertexMap *lwo_vmap); + void make_egg(); void connect_egg(); @@ -33,6 +38,12 @@ public: CPT(LwoPoints) _points; CLwoLayer *_layer; PT(EggVertexPool) _egg_vpool; + + // A number of vertex maps may be associated, by type and then by + // name. + typedef map VMapNames; + typedef map VmapTypes; + VmapTypes _vmap; }; #include "cLwoPoints.I" diff --git a/pandatool/src/lwoegg/cLwoPolygons.cxx b/pandatool/src/lwoegg/cLwoPolygons.cxx index e85731526f..10c84be240 100644 --- a/pandatool/src/lwoegg/cLwoPolygons.cxx +++ b/pandatool/src/lwoegg/cLwoPolygons.cxx @@ -38,8 +38,9 @@ make_egg() { make_faces(); } else { - nout << "Ignoring unknown geometry type " << _polygons->_polygon_type - << "\n"; + nout << "Unknown geometry type " << _polygons->_polygon_type + << "; treating as FACE.\n"; + make_faces(); } } @@ -50,6 +51,8 @@ make_egg() { //////////////////////////////////////////////////////////////////// void CLwoPolygons:: connect_egg() { + nassertv(_points->_layer->_egg_group != (EggGroup *)NULL); + nassertv(_egg_group != (EggGroup *)NULL); _points->_layer->_egg_group->steal_children(*_egg_group); } diff --git a/pandatool/src/lwoegg/lwoToEggConverter.cxx b/pandatool/src/lwoegg/lwoToEggConverter.cxx index 6c09aa6c55..0c7a173d5c 100644 --- a/pandatool/src/lwoegg/lwoToEggConverter.cxx +++ b/pandatool/src/lwoegg/lwoToEggConverter.cxx @@ -12,6 +12,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////// @@ -98,6 +99,7 @@ void LwoToEggConverter:: collect_lwo() { CLwoLayer *last_layer = (CLwoLayer *)NULL; CLwoPoints *last_points = (CLwoPoints *)NULL; + CLwoPolygons *last_polygons = (CLwoPolygons *)NULL; int num_chunks = _lwo_header->get_num_chunks(); for (int i = 0; i < num_chunks; i++) { @@ -115,6 +117,7 @@ collect_lwo() { _layers[number] = layer; last_layer = layer; last_points = (CLwoPoints *)NULL; + last_polygons = (CLwoPolygons *)NULL; } else if (chunk->is_of_type(LwoPoints::get_class_type())) { if (last_layer == (CLwoLayer *)NULL) { @@ -126,6 +129,14 @@ collect_lwo() { _points.push_back(points); last_points = points; + } else if (chunk->is_of_type(LwoVertexMap::get_class_type())) { + if (last_points == (CLwoPoints *)NULL) { + nout << "Vertex map chunk encountered without a preceding points chunk.\n"; + } else { + const LwoVertexMap *lwo_vmap = DCAST(LwoVertexMap, chunk); + last_points->add_vmap(lwo_vmap); + } + } else if (chunk->is_of_type(LwoPolygons::get_class_type())) { if (last_points == (CLwoPoints *)NULL) { nout << "Polygon chunk encountered without a preceding points chunk.\n"; @@ -133,6 +144,7 @@ collect_lwo() { const LwoPolygons *lwo_polygons = DCAST(LwoPolygons, chunk); CLwoPolygons *polygons = new CLwoPolygons(this, lwo_polygons, last_points); _polygons.push_back(polygons); + last_polygons = polygons; } } } @@ -146,6 +158,10 @@ collect_lwo() { //////////////////////////////////////////////////////////////////// void LwoToEggConverter:: make_egg() { + if (_generic_layer != (CLwoLayer *)NULL) { + _generic_layer->make_egg(); + } + Layers::iterator li; for (li = _layers.begin(); li != _layers.end(); ++li) { CLwoLayer *layer = (*li); @@ -174,6 +190,10 @@ make_egg() { //////////////////////////////////////////////////////////////////// void LwoToEggConverter:: connect_egg() { + if (_generic_layer != (CLwoLayer *)NULL) { + _generic_layer->connect_egg(); + } + Layers::iterator li; for (li = _layers.begin(); li != _layers.end(); ++li) { CLwoLayer *layer = (*li);