Visualization Library 2.0.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
ioMD2.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
32 #include "ioMD2.hpp"
33 #include <vlCore/Time.hpp>
34 #include <vlCore/Log.hpp>
35 #include <vlCore/Say.hpp>
37 #include <vlCore/FileSystem.hpp>
40 
41 using namespace vl;
42 
43 namespace
44 {
45  // MD2 structures and defines
46  class LoaderMD2
47  {
48  public:
49  LoaderMD2(): verbose(false) {}
50 
51  enum {
52  MD2_MAX_TRIANGLES = 4096,
53  MD2_MAX_VERTICES = 2048,
54  MD2_MAX_TEXCOORDS = 2048,
55  MD2_MAX_FRAMES = 512,
56  MD2_MAX_SKINS = 32,
57  MD2_MAX_FRAMESIZE = MD2_MAX_VERTICES * 4 + 128
58  };
59 
60  class md2_header_info
61  {
62  public:
63  int magic;
64  int version;
65  int skinWidth;
66  int skinHeight;
67  int frameSize;
68  int numSkins;
69  int numVertices;
70  int numTexCoords;
71  int numTriangles;
72  int numGlCommands;
73  int numFrames;
74  int offsetSkins;
75  int offsetTexCoords;
76  int offsetTriangles;
77  int offsetFrames;
78  int offsetGlCommands;
79  int offsetEnd;
80  };
81 
82  class md2_vertex_info
83  {
84  public:
85  unsigned char vertex[3];
86  unsigned char light_norm_index;
87  };
88 
89  class md2_triangle_info
90  {
91  public:
92  short vert_idx[3];
93  short uv_idx[3];
94  };
95 
96  class md2_uv_info
97  {
98  public:
99  short u, v;
100  };
101 
102  class md2_frame_info
103  {
104  public:
105  float scale[3];
106  float translate[3];
107  char name[16];
108  md2_vertex_info vertices[1];
109  };
110 
111  class md2_skin_info
112  {
113  public:
114  unsigned char name[64];
115  };
116 
117  md2_header_info header;
118  std::vector<md2_frame_info*> md2_frame;
119  std::vector<md2_uv_info> md2_uv;
120  std::vector<md2_triangle_info> md2_triangle;
121  std::vector<md2_skin_info> md2_skin;
122  bool verbose;
123  };
124 }
125 //-----------------------------------------------------------------------------
127 {
128  ref<VirtualFile> file = defFileSystem()->locateFile(path);
129 
130  if (file)
131  return loadMD2( file.get() );
132  else
133  {
134  Log::error( Say("Could not locate '%s'.\n") << path );
135  return NULL;
136  }
137 }
138 //-----------------------------------------------------------------------------
140 {
142  ref<Geometry> geometry = new Geometry;
143  res_db->resources().push_back( geometry.get() );
144 
145  if (!file->open(OM_ReadOnly))
146  {
147  Log::error( Say("Error opening '%s'\n") << file->path() );
148  return NULL;
149  }
150 
151  LoaderMD2 loader;
152  file->read( &loader.header, sizeof(loader.header) );
153 
154  if (loader.verbose)
155  {
156  Log::print( Say("tris %n:\n") << loader.header.numTriangles);
157  Log::print( Say("verts %n:\n") << loader.header.numVertices);
158  Log::print( Say("uvs %n:\n") << loader.header.numTexCoords);
159  Log::print( Say("offs skins %n:\n") << loader.header.offsetSkins);
160  Log::print( Say("offs end %n:\n") << loader.header.offsetEnd);
161  Log::print( Say("offs frames %n:\n") << loader.header.offsetFrames);
162  Log::print( Say("offs gl comm %n:\n") << loader.header.offsetGlCommands);
163  Log::print( Say("offs tex coor %n:\n") << loader.header.offsetTexCoords);
164  Log::print( Say("offs tri %n:\n") << loader.header.offsetTriangles);
165  Log::print( Say("skinh %n:\n") << loader.header.skinHeight);
166  Log::print( Say("skinw %n:\n") << loader.header.skinWidth);
167  }
168 
169  // load data into memory
170  file->seekSet(loader.header.offsetFrames);
171  loader.md2_frame.resize(loader.header.numFrames);
172  for(unsigned i=0; i<loader.md2_frame.size(); ++i)
173  {
174  loader.md2_frame[i] = (LoaderMD2::md2_frame_info*)malloc(loader.header.frameSize * sizeof(char) );
175  file->read(loader.md2_frame[i], loader.header.frameSize);
176  }
177 
178  // uv
179  loader.md2_uv.resize(loader.header.numTexCoords);
180  file->seekSet( loader.header.offsetTexCoords );
181  file->read(&loader.md2_uv[0], loader.header.numTexCoords*sizeof(LoaderMD2::md2_uv_info) );
182 
183  // triangles
184  loader.md2_triangle.resize(loader.header.numTriangles);
185  file->seekSet(loader.header.offsetTriangles );
186  file->read(&loader.md2_triangle[0], loader.header.numTriangles*sizeof(LoaderMD2::md2_triangle_info) );
187 
188  // textures
189  if (loader.header.numSkins)
190  {
191  loader.md2_skin.resize(loader.header.numSkins);
192  file->seekSet( loader.header.offsetSkins );
193  file->read(&loader.md2_skin[0], loader.header.numSkins*sizeof(LoaderMD2::md2_skin_info) );
194  }
195 
196  // fclose(fin);
197  file->close();
198 
199  // conversion
200 
201  std::vector< ref<ArrayFloat3> > vertex_frames;
202  std::vector< ref<ArrayFloat3> > normal_frames;
203 
204  // allocate frames
205  for(int i=0; i<loader.header.numFrames; ++i)
206  {
207  vertex_frames.push_back( new ArrayFloat3 );
208  vertex_frames[i]->resize( 3 * loader.header.numTriangles );
209  // normals are computed later
210  }
211 
213  ref<ArrayFloat2> tex_coords = new ArrayFloat2;
214  tex_coords->resize( 3 * loader.header.numTriangles );
215  polygons->indexBuffer()->resize( 3 * loader.header.numTriangles );
216  geometry->setTexCoordArray(0, tex_coords.get());
217  geometry->drawCalls().push_back( polygons.get() );
218 
219  int vert_idx = 0;
220  VL_CHECK( (int)loader.md2_triangle.size() == loader.header.numTriangles )
221  for(int itri=0; itri<loader.header.numTriangles; itri++)
222  {
223  for( int ivert=3; ivert--; ++vert_idx )
224  {
225  // add uv
226  float u = (float)loader.md2_uv[ loader.md2_triangle[itri].uv_idx[ivert] ].u / loader.header.skinWidth;
227  float v = 1.0f - (float)loader.md2_uv[ loader.md2_triangle[itri].uv_idx[ivert] ].v / loader.header.skinHeight;
228  tex_coords->at(vert_idx) = fvec2(u, v);
229 
230  // add vert
231  for(int iframe=0; iframe<loader.header.numFrames; iframe++)
232  {
233  fvec3 vec;
234  vec.x() = loader.md2_frame[iframe]->vertices[ loader.md2_triangle[itri].vert_idx[ivert] ].vertex[0] * loader.md2_frame[iframe]->scale[0] + loader.md2_frame[iframe]->translate[0];
235  vec.y() = loader.md2_frame[iframe]->vertices[ loader.md2_triangle[itri].vert_idx[ivert] ].vertex[2] * loader.md2_frame[iframe]->scale[2] + loader.md2_frame[iframe]->translate[2];
236  vec.z() = -1 *(loader.md2_frame[iframe]->vertices[ loader.md2_triangle[itri].vert_idx[ivert] ].vertex[1] * loader.md2_frame[iframe]->scale[1] + loader.md2_frame[iframe]->translate[1]);
237  vertex_frames[iframe]->at( vert_idx ) = vec;
238  }
239 
240  // add index
241  polygons->indexBuffer()->at( vert_idx ) = vert_idx;
242  }
243  }
244 
245  for(int iframe=0; iframe<loader.header.numFrames; iframe++)
246  free(loader.md2_frame[iframe]);
247 
248  // remove double vertices using the first vertex frame
249  geometry->setVertexArray( vertex_frames[0].get() );
250  geometry->setNormalArray( NULL ); // don't count the normals
251  geometry->setTexCoordArray( 0, tex_coords.get() ); // count the texture coords
252 
253  // this takes away the 66% of the vertices!
254  DoubleVertexRemover remover;
255  remover.removeDoubles( geometry.get() );
256 
257  // install the newly created and simplified arrays
258  vertex_frames[0] = cast<ArrayFloat3>(geometry->vertexArray()); VL_CHECK(vertex_frames[0]);
259  tex_coords = cast<ArrayFloat2>(geometry->texCoordArray(0)); VL_CHECK(tex_coords);
260  polygons = cast<DrawElementsUInt>(geometry->drawCalls().at(0)); VL_CHECK(polygons);
261 
262  // simplify the remaining frames based on the translation table remover.oldToNewIndexMap()
263  for(int iframe=1; iframe<loader.header.numFrames; ++iframe)
264  {
265  ArrayFloat3* new_vertex_frame = new ArrayFloat3;
266  new_vertex_frame->resize( vertex_frames[0]->size() );
267 
268  for(size_t ivert=0; ivert<vertex_frames[iframe]->size(); ++ivert)
269  {
270  VL_CHECK( remover.mapOldToNew()[ivert] < new_vertex_frame->size() )
271  new_vertex_frame->at( remover.mapOldToNew()[ivert] ) = vertex_frames[iframe]->at(ivert);
272  }
273  vertex_frames[iframe] = new_vertex_frame;
274  }
275 
276  // compute normals
277  normal_frames.resize( loader.header.numFrames );
278  for(int iframe=0; iframe<loader.header.numFrames; iframe++)
279  {
280  geometry->setVertexArray( vertex_frames[iframe].get() );
281  geometry->computeNormals();
282  normal_frames[iframe] = cast<ArrayFloat3>(geometry->normalArray()); VL_CHECK(normal_frames[iframe]);
283  VL_CHECK( normal_frames[iframe] )
284  }
285 
286  for(unsigned i=0; i<vertex_frames.size(); ++i)
287  {
288  vertex_frames[i]->setObjectName("vertex_frame");
289  res_db->resources().push_back(vertex_frames[i].get());
290  }
291 
292  for(unsigned i=0; i<normal_frames.size(); ++i)
293  {
294  normal_frames[i]->setObjectName("normal_frame");
295  res_db->resources().push_back(normal_frames[i].get());
296  }
297 
298  return res_db;
299 }
300 //-----------------------------------------------------------------------------
301 
302 /* Quick MD2 Animation Map
303  * stand 0,39
304  * run 40,45
305  * attack 46,53
306  * wave 112, 122
307  * die 190 197
308  */
309 
long long read(void *buffer, long long byte_count)
Reads byte_count bytes from a file. Returns the number of bytes actually read.
Definition: VirtualFile.cpp:82
VLCORE_EXPORT FileSystem * defFileSystem()
Returns the default FileSystem used by VisualizationLibrary.
Definition: pimpl.cpp:97
const T * get() const
Definition: Object.hpp:128
An abstract class representing a file.
Definition: VirtualFile.hpp:60
A simple String formatting class.
Definition: Say.hpp:124
The String class implements an advanced UTF16 (Unicode BMP) string manipulation engine.
Definition: String.hpp:62
const T_Scalar & z() const
Definition: Vector3.hpp:91
const ArrayAbstract * vertexArray() const
Conventional vertex array.
Definition: Geometry.hpp:248
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
void setVertexArray(ArrayAbstract *data)
Conventional vertex array.
Definition: Geometry.cpp:155
VLGRAPHICS_EXPORT ref< ResourceDatabase > loadMD2(const String &path)
Definition: ioMD2.cpp:126
Removes from a Geometry the vertices with the same attributes.
void removeDoubles(Geometry *geom)
virtual ref< VirtualFile > locateFile(const String &full_path, const String &alternate_path=String()) const
Looks for a VirtualFile on the disk and in the currently active FileSystem.
Definition: FileSystem.cpp:61
const std::vector< u32 > & mapOldToNew() const
void resize(size_t dim)
Definition: Array.hpp:233
void setNormalArray(ArrayAbstract *data)
Conventional normal array.
Definition: Geometry.cpp:164
virtual void close()=0
Closes the file.
const String & path() const
Returns the path of the file.
Definition: VirtualFile.hpp:98
size_t size() const
Returns the number of elements of an array.
Definition: Array.hpp:235
The Geometry class is a Renderable that implements a polygonal mesh made of polygons, lines and points.
Definition: Geometry.hpp:66
Visualization Library main namespace.
Vector2< float > fvec2
A 2 components vector with float precision.
Definition: Vector2.hpp:282
const std::vector< ref< Object > > & resources() const
See DrawElements.
static void print(const String &message)
Application message for the user.
Definition: Log.cpp:136
const T_Scalar & y() const
Definition: Vector3.hpp:90
virtual bool open(EOpenMode mode)=0
Opens the file in the specified mode.
void computeNormals(bool verbose=false)
Computes the normals in a "smooth" way, i.e.
Definition: Geometry.cpp:269
void setTexCoordArray(int tex_unit, ArrayAbstract *data)
Conventional texture coords arrays.
Definition: Geometry.cpp:222
#define NULL
Definition: OpenGLDefs.hpp:81
arr_type * indexBuffer()
The BufferObject containing the indices used to render.
bool seekSet(long long offset)
Changes the current read/write position of a file.
T_VectorType & at(size_t i)
Definition: Array.hpp:255
const T_Scalar & x() const
Definition: Vector3.hpp:89
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
const ArrayAbstract * texCoordArray(int tex_unit) const
Conventional texture coords arrays.
Definition: Geometry.hpp:278
An array of vl::fvec3.
Definition: Array.hpp:414
An array of vl::fvec2.
Definition: Array.hpp:412
#define VL_CHECK(expr)
Definition: checks.hpp:73
const ArrayAbstract * normalArray() const
Conventional normal array.
Definition: Geometry.hpp:254
The ResourceDatabase class contains and manipulates a set of resources.
Collection< DrawCall > & drawCalls()
Returns the list of DrawCall objects bound to a Geometry.
Definition: Geometry.hpp:102