source: trunk/src/formats/obj_loader.cc @ 278

Last change on this file since 278 was 240, checked in by epyon, 11 years ago
  • multiple mesh per file interface for all mesh loaders added
  • multiple mesh per file obj and md5 loader support added
File size: 8.1 KB
Line 
1// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
2// http://chaosforge.org/
3//
4// This file is part of NV Libraries.
5// For conditions of distribution and use, see copyright notice in nv.hh
6
7#include "nv/formats/obj_loader.hh"
8#include "nv/io/std_stream.hh"
9#include <sstream>
10
11using namespace nv;
12
13struct obj_vertex_vt
14{
15        vec3 position;
16        vec2 texcoord;
17
18        obj_vertex_vt( vec3 a_position, vec2 a_texcoord, vec3 )
19                : position( a_position ), texcoord( a_texcoord ) {}
20};
21
22struct obj_vertex_vtn
23{
24        vec3 position;
25        vec2 texcoord;
26        vec3 normal;
27
28        obj_vertex_vtn( vec3 a_position, vec2 a_texcoord, vec3 a_normal )
29                : position( a_position ), texcoord( a_texcoord ), normal( a_normal ) {}
30};
31
32
33struct obj_vertex_vtnt
34{
35        vec3 position;
36        vec2 texcoord;
37        vec3 normal;
38        vec4 tangent;
39
40        obj_vertex_vtnt( vec3 a_position, vec2 a_texcoord, vec3 a_normal )
41                : position( a_position ), texcoord( a_texcoord ), normal( a_normal ) {}
42};
43
44struct obj_reader
45{
46        std::vector< vec3 > v;
47        std::vector< vec3 > n;
48        std::vector< vec2 > t;
49
50        std::string line;
51        std::string cmd;
52
53        std::size_t size;
54        bool        eof;
55
56        obj_reader();
57        bool read_stream( std::istream& stream );
58        virtual size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count ) = 0;
59        virtual size_t raw_size() const = 0;
60        virtual void reset() = 0;
61        virtual const uint8* raw_pointer() const = 0;
62        virtual void calculate_tangents() {}
63
64        virtual ~obj_reader(){}
65};
66
67obj_reader::obj_reader()
68{
69        // push in dummy 0-index objects for faster indexing
70        v.push_back( vec3() );
71        n.push_back( vec3() );
72        t.push_back( vec2() );
73        size = 0;
74        eof = false;
75}
76
77bool obj_reader::read_stream( std::istream& stream )
78{
79        bool added_faces = false;
80        f32 x, y, z;
81        if ( eof ) return false;
82
83        while ( std::getline( stream, line ) )
84        {
85                if ( line.length() < 3 || line[0] == '#' )
86                {
87                        continue;
88                }
89
90                std::istringstream ss(line);
91                ss >> cmd;
92
93                if ( cmd == "v" )
94                {
95                        ss >> x >> y >> z;
96                        v.push_back( vec3( x, y, z ) );
97                        continue;
98                }
99
100                if ( cmd == "vn" )
101                {
102                        ss >> x >> y >> z;
103                        n.push_back( vec3( x, y, z ) );
104                        continue;
105                }
106
107                if ( cmd == "vt" )
108                {
109                        ss >> x >> y;
110                        t.push_back( vec2( x, 1.0f - y ) );
111                        continue;
112                }
113
114                if ( cmd == "f" )
115                {
116                        added_faces = true;
117                        ss >> cmd;
118
119                        uint32 vis[8];
120                        uint32 tis[8];
121                        uint32 nis[8];
122                        bool   normals = false;
123                        uint32 count = 0;
124
125                        while ( !ss.fail() )
126                        {
127                                char ch;
128
129                                std::istringstream ss2( cmd );
130                                ss2 >> vis[count] >> ch;
131                                ss2 >> tis[count] >> ch;
132                                if ( ch == '/')
133                                {
134                                        normals = true;
135                                        ss2 >> nis[count];
136                                }
137
138                                ss >> cmd;
139                                count++;
140                        }
141
142                        size += add_face( vis, tis, normals ? nis : nullptr, count );
143                        continue;
144                }
145
146                if ( cmd == "g" )
147                {
148                        if (added_faces) return true;
149                        continue;
150                }
151
152                if ( cmd == "s" )
153                {
154                        continue;
155                }
156
157                // unknown command
158        }
159
160        eof = true;
161        return true;
162}
163
164template < typename VTX >
165struct mesh_data_reader : public obj_reader
166{
167        mesh_data_reader( bool normals ) : m_normals( normals ) {}
168        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
169        {
170                if ( count < 3 ) return 0; // TODO : report error?
171
172                // TODO : support if normals not present;
173                vec3 nullvec;
174                std::size_t result = 0;
175                // Simple triangulation - obj's shouldn't have more than quads anyway
176
177                if ( m_normals )
178                {
179                        for ( size_t i = 2; i < count; ++i )
180                        {
181                                result++;
182                                m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
183                                m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
184                                m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
185                        }
186                }
187                else
188                {
189                        for ( size_t i = 2; i < count; ++i )
190                        {
191                                result++;
192                                m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], nullvec );
193                                m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], nullvec );
194                                m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], nullvec );
195                        }
196                }
197                return result;
198        }
199        bool m_normals;
200        std::vector< VTX > m_data;
201        virtual void reset() { m_data.clear(); }
202        virtual size_t raw_size() const { return m_data.size() * sizeof( VTX ); }
203        virtual const uint8* raw_pointer() const { return (const uint8*)m_data.data(); }
204};
205
206
207struct mesh_data_reader_vt : public mesh_data_reader< obj_vertex_vt >
208{
209        mesh_data_reader_vt() : mesh_data_reader( false ) {}
210};
211
212struct mesh_data_reader_vtn : public mesh_data_reader< obj_vertex_vtn >
213{
214        mesh_data_reader_vtn() : mesh_data_reader( true ) {}
215};
216
217struct mesh_data_reader_vtnt : public mesh_data_reader< obj_vertex_vtnt >
218{
219        mesh_data_reader_vtnt() : mesh_data_reader( true ) {}
220
221        // based on http://www.terathon.com/code/tangent.html
222        void calculate_tangents()
223        {
224                //              const std::vector< vec3 >& vp = m_mesh->get_positions();
225                //              const std::vector< vec2 >& vt = m_mesh->get_texcoords();
226                //              const std::vector< vec3 >& vn = m_mesh->get_normals();
227                //              std::vector< vec3 >& tg = m_mesh->get_tangents();
228
229                size_t count  = m_data.size();
230                size_t tcount = count / 3;
231
232                std::vector< vec3 > tan1( count );
233                std::vector< vec3 > tan2( count );
234
235                for (size_t a = 0; a < tcount; ++a )
236                {
237                        size_t i1 = a * 3;
238                        size_t i2 = a * 3 + 1;
239                        size_t i3 = a * 3 + 2;
240                        obj_vertex_vtnt& vtx1 = m_data[ i1 ];
241                        obj_vertex_vtnt& vtx2 = m_data[ i2 ];
242                        obj_vertex_vtnt& vtx3 = m_data[ i3 ];
243
244                        // TODO: simplify
245                        vec3 xyz1 = vtx2.position - vtx1.position;
246                        vec3 xyz2 = vtx3.position - vtx1.position;
247                        //vec2 st1  = w2 - w1;
248                        //vec2 st2  = w3 - w1;
249
250                        float s1 = vtx2.texcoord.x - vtx1.texcoord.x;
251                        float t1 = vtx2.texcoord.y - vtx1.texcoord.y;
252                        float s2 = vtx3.texcoord.x - vtx1.texcoord.x;
253                        float t2 = vtx3.texcoord.y - vtx1.texcoord.y;
254
255                        float stst = s1 * t2 - s2 * t1;
256                        float r = 0.0f;
257                        if (stst > 0.0f || stst < 0.0f) r = 1.0f / stst;
258
259                        vec3 sdir = ( t2 * xyz1 - t1 * xyz2 ) * r;
260                        vec3 tdir = ( s1 * xyz2 - s2 * xyz1 ) * r;
261
262                        // the += below obviously doesn't make sense in this case, but I'll
263                        // leave it here for when I move to indices
264                        tan1[i1] += sdir;
265                        tan1[i2] += sdir;
266                        tan1[i3] += sdir;
267
268                        // tan2 not needed anymore??
269                        tan2[i1] += tdir;
270                        tan2[i2] += tdir;
271                        tan2[i3] += tdir;
272                }
273
274                for (std::size_t a = 0; a < count; ++a )
275                {
276                        const vec3& n = m_data[a].normal;
277                        const vec3& t = tan1[a];
278                        if ( ! (t.x == 0.0f && t.y == 0.0f && t.z == 0.0f) )
279                        {
280                                m_data[a].tangent    = vec4( glm::normalize(t - n * glm::dot( n, t )), 0.0f );
281                                m_data[a].tangent[3] = (glm::dot(glm::cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
282                        }
283                }
284
285        }
286
287
288};
289
290nv::obj_loader::obj_loader( bool normals /*= true*/, bool tangents /*= false */ )
291        : m_normals( normals ), m_tangents( tangents )
292{
293        if ( normals )
294        {
295                if ( tangents )
296                        m_descriptor.initialize<obj_vertex_vtnt>();
297                else
298                        m_descriptor.initialize<obj_vertex_vtn>();
299        }
300        else
301                m_descriptor.initialize<obj_vertex_vt>();
302}
303
304bool nv::obj_loader::load( stream& source )
305{
306        obj_reader* reader = nullptr;
307        if ( m_normals )
308        {
309                if ( m_tangents )
310                        reader = new mesh_data_reader_vtnt();
311                else
312                        reader = new mesh_data_reader_vtn();
313        }
314        else
315                reader = new mesh_data_reader_vt();
316        std_stream sstream( &source );
317
318        while ( reader->read_stream( sstream ) )
319        {
320                if ( m_tangents )
321                {
322                        reader->calculate_tangents();
323                }
324       
325                mesh_raw_channel* channel = new mesh_raw_channel();
326                nv::uint8* data = nullptr;
327
328                if ( reader->raw_size() > 0 )
329                {
330                        data = new uint8[ reader->raw_size() ];
331                        std::copy_n( reader->raw_pointer(), reader->raw_size(), data );
332                }
333                channel->data  = data;
334                channel->desc  = m_descriptor;
335                channel->count = reader->size * 3;
336                channel->size  = reader->raw_size();
337
338                mesh_data* mesh = new mesh_data();
339                mesh->add_channel( channel );
340                m_meshes.push_back( mesh );
341
342                reader->reset();
343        }
344        delete reader;
345        return true;
346
347}
348
349mesh_data* nv::obj_loader::release_mesh_data( size_t index )
350{
351        mesh_data* result = m_meshes[ index ];
352        m_meshes[ index ] = nullptr;
353        return result;
354}
355
356nv::obj_loader::~obj_loader()
357{
358        for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
359}
Note: See TracBrowser for help on using the repository browser.