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

Last change on this file since 238 was 238, checked in by epyon, 11 years ago
  • mesh data interface and usage
  • new wavefront importer (old pending removal)
  • updates to test projects
File size: 12.0 KB
RevLine 
[136]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"
[138]8#include "nv/io/std_stream.hh"
[136]9#include <sstream>
10
11using namespace nv;
12
[238]13struct obj_vertex_vt
14{
15        vec3 position;
16        vec2 texcoord;
17
18        obj_vertex_vt( vec3 a_position, vec2 a_texcoord )
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
[136]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
55        obj_reader();
56        bool read_stream( std::istream& stream );
[238]57        virtual size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count ) = 0;
58        virtual size_t raw_size() { return 0; }
59        virtual const uint8* raw_pointer() { return nullptr; }
60        virtual void calculate_tangents() {}
[136]61
62        virtual ~obj_reader(){}
63};
64
65obj_reader::obj_reader()
66{
67        // push in dummy 0-index objects for faster indexing
68        v.push_back( vec3() );
69        n.push_back( vec3() );
70        t.push_back( vec2() );
71        size = 0;
72}
73
74bool obj_reader::read_stream( std::istream& stream )
75{
76        f32 x, y, z;
77
78        while ( std::getline( stream, line ) )
79        {
80                if ( line.length() < 3 || line[0] == '#' )
81                {
82                        continue;
83                }
84
85                std::istringstream ss(line);
86                ss >> cmd;
87
88                if ( cmd == "v" )
89                {
90                        ss >> x >> y >> z;
91                        v.push_back( vec3( x, y, z ) );
92                        continue;
93                }
94
95                if ( cmd == "vn" )
96                {
97                        ss >> x >> y >> z;
98                        n.push_back( vec3( x, y, z ) );
99                        continue;
100                }
101
102                if ( cmd == "vt" )
103                {
104                        ss >> x >> y;
105                        t.push_back( vec2( x, 1.0f - y ) );
106                        continue;
107                }
108
109                if ( cmd == "f" )
110                {
111                        ss >> cmd;
112
113                        uint32 vis[8];
114                        uint32 tis[8];
115                        uint32 nis[8];
116                        bool   normals = false;
117                        uint32 count = 0;
118
119                        while ( !ss.fail() )
120                        {
121                                char ch;
122
123                                std::istringstream ss2( cmd );
124                                ss2 >> vis[count] >> ch;
125                                ss2 >> tis[count] >> ch;
126                                if ( ch == '/')
127                                {
128                                        normals = true;
129                                        ss2 >> nis[count];
130                                }
131
132                                ss >> cmd;
133                                count++;
134                        }
135
136                        size += add_face( vis, tis, normals ? nis : nullptr, count );
137                        continue;
138                }
139
140                if ( cmd == "g" || cmd == "s" )
141                {
142                        // ignored
143                        continue;
144                }
145
146                // unknown command
147        }
148
149        return true;
150}
151
152
153struct mesh_obj_reader : public obj_reader
154{
[224]155        mesh_obj_reader( mesh_data_creator* m ) : m_mesh( m ) {}
[238]156        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count );
[147]157        virtual void calculate_tangents();
[136]158
[224]159        mesh_data_creator* m_mesh;
[136]160};
161
[204]162size_t mesh_obj_reader::add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
[136]163{
164        if ( count < 3 )
165        {
166                // TODO : report error?
167                return 0;
168        }
169
170        // TODO : support if normals not present;
171
[224]172        std::vector< vec3 >& vp = m_mesh->get_positions();
173        std::vector< vec3 >& vn = m_mesh->get_normals();
174        std::vector< vec2 >& vt = m_mesh->get_texcoords();
[136]175
176        std::size_t result = 0;
177
178        // Simple triangulation - obj's shouldn't have more than quads anyway
179        for ( size_t i = 2; i < count; ++i )
180        {
181                result++;
182                vp.push_back( v[ vi[ 0 ] ] );   vt.push_back( t[ ti[ 0 ] ] );   vn.push_back( n[ ni[ 0 ] ] );
183                vp.push_back( v[ vi[ i-1 ] ] ); vt.push_back( t[ ti[ i-1 ] ] ); vn.push_back( n[ ni[ i-1 ] ] );
184                vp.push_back( v[ vi[ i ] ] );   vt.push_back( t[ ti[ i ] ] );   vn.push_back( n[ ni[ i ] ] );
185        }
186
187        return result;
188}
189
[147]190// based on http://www.terathon.com/code/tangent.html
191void mesh_obj_reader::calculate_tangents()
[136]192{
[224]193        const std::vector< vec3 >& vp = m_mesh->get_positions();
194        const std::vector< vec2 >& vt = m_mesh->get_texcoords();
195        const std::vector< vec3 >& vn = m_mesh->get_normals();
196        std::vector< vec3 >& tg = m_mesh->get_tangents();
[136]197
[204]198        size_t count  = vp.size();
199        size_t tcount = count / 3;
[147]200
201        std::vector< vec3 > tan1( count );
202        std::vector< vec3 > tan2( count );
203        tg.resize( count );
204
[204]205        for (size_t a = 0; a < tcount; ++a )
[147]206        {
[204]207                size_t i1 = a * 3;
208                size_t i2 = a * 3 + 1;
209                size_t i3 = a * 3 + 2;
[147]210
[148]211                // TODO: simplify
212
[147]213                const vec3& v1 = vp[i1];
214                const vec3& v2 = vp[i2];
215                const vec3& v3 = vp[i3];
216
217                const vec2& w1 = vt[i1];
218                const vec2& w2 = vt[i2];
219                const vec2& w3 = vt[i3];
220
221                vec3 xyz1 = v2 - v1;
222                vec3 xyz2 = v3 - v1;
[198]223                //vec2 st1  = w2 - w1;
224                //vec2 st2  = w3 - w1;
[147]225
226                float s1 = w2.x - w1.x;
227                float t1 = w2.y - w1.y;
228                float s2 = w3.x - w1.x;
229                float t2 = w3.y - w1.y;
230
[225]231                float stst = s1 * t2 - s2 * t1;
232                float r = 0.0f;
233                if (stst > 0.0f || stst < 0.0f) r = 1.0f / stst;
[147]234
235                vec3 sdir = ( t2 * xyz1 - t1 * xyz2 ) * r;
236                vec3 tdir = ( s1 * xyz2 - s2 * xyz1 ) * r;
237
238                // the += below obviously doesn't make sense in this case, but I'll
239                // leave it here for when I move to indices
240                tan1[i1] += sdir;
241                tan1[i2] += sdir;
242                tan1[i3] += sdir;
243
[224]244                // tan2 not needed anymore??
[147]245                tan2[i1] += tdir;
246                tan2[i2] += tdir;
247                tan2[i3] += tdir;
248        }
249
250        for (std::size_t a = 0; a < count; ++a )
251        {
252                const vec3& n = vn[a];
253                const vec3& t = tan1[a];
[225]254                if ( ! (t.x == 0.0f && t.y == 0.0f && t.z == 0.0f) )
255                        tg[a] = vec3( glm::normalize(t - n * glm::dot( n, t )) );
256                        //tg[a][3] =    (glm::dot(glm::cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
[147]257        }
258
[136]259}
260
[147]261nv::obj_loader::obj_loader( bool tangents )
262        : m_mesh( nullptr ), m_tangents( tangents )
263{
264
265}
266
[136]267nv::obj_loader::~obj_loader()
268{
269        delete m_mesh;
270}
271
272bool nv::obj_loader::load( stream& source )
273{
[238]274        if ( m_mesh != nullptr ) delete m_mesh;
[224]275        mesh_data_creator creator;
276        mesh_obj_reader reader( &creator );
[138]277        std_stream sstream( &source );
[136]278        reader.read_stream( sstream );
279        m_size = reader.size;
[147]280        if ( m_tangents )
281        {
282                reader.calculate_tangents();
283        }
[224]284        m_mesh = creator.release();
[136]285        return true;
286}
[238]287
288struct mesh_data_reader_vt : public obj_reader
289{
290        mesh_data_reader_vt()  {}
291        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32*, size_t count )
292        {
293                if ( count < 3 ) return 0; // TODO : report error?
294                // TODO : support if normals not present;
295                std::size_t result = 0;
296                // Simple triangulation - obj's shouldn't have more than quads anyway
297                for ( size_t i = 2; i < count; ++i )
298                {
299                        result++;
300                        m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ] );
301                        m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ] );
302                        m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ] );
303                }
304                return result;
305        }
306        std::vector< obj_vertex_vt > m_data;
307        virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vt ); }
308        virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
309};
310
311struct mesh_data_reader_vtn : public obj_reader
312{
313        mesh_data_reader_vtn()  {}
314        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
315        {
316                if ( count < 3 ) return 0; // TODO : report error?
317                // TODO : support if normals not present;
318                std::size_t result = 0;
319                // Simple triangulation - obj's shouldn't have more than quads anyway
320                for ( size_t i = 2; i < count; ++i )
321                {
322                        result++;
323                        m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
324                        m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
325                        m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
326                }
327                return result;
328        }
329        std::vector< obj_vertex_vtn > m_data;
330        virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vtn ); }
331        virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
332};
333
334struct mesh_data_reader_vtnt : public obj_reader
335{
336        mesh_data_reader_vtnt()  {}
337        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
338        {
339                if ( count < 3 ) return 0; // TODO : report error?
340                // TODO : support if normals not present;
341                std::size_t result = 0;
342                // Simple triangulation - obj's shouldn't have more than quads anyway
343                for ( size_t i = 2; i < count; ++i )
344                {
345                        result++;
346                        m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
347                        m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
348                        m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
349                }
350                return result;
351        }
352        std::vector< obj_vertex_vtnt > m_data;
353        virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vtnt ); }
354        virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
355
356        // based on http://www.terathon.com/code/tangent.html
357        void calculate_tangents()
358        {
359                //              const std::vector< vec3 >& vp = m_mesh->get_positions();
360                //              const std::vector< vec2 >& vt = m_mesh->get_texcoords();
361                //              const std::vector< vec3 >& vn = m_mesh->get_normals();
362                //              std::vector< vec3 >& tg = m_mesh->get_tangents();
363
364                size_t count  = m_data.size();
365                size_t tcount = count / 3;
366
367                std::vector< vec3 > tan1( count );
368                std::vector< vec3 > tan2( count );
369
370                for (size_t a = 0; a < tcount; ++a )
371                {
372                        size_t i1 = a * 3;
373                        size_t i2 = a * 3 + 1;
374                        size_t i3 = a * 3 + 2;
375                        obj_vertex_vtnt& vtx1 = m_data[ i1 ];
376                        obj_vertex_vtnt& vtx2 = m_data[ i2 ];
377                        obj_vertex_vtnt& vtx3 = m_data[ i3 ];
378
379                        // TODO: simplify
380                        vec3 xyz1 = vtx2.position - vtx1.position;
381                        vec3 xyz2 = vtx3.position - vtx1.position;
382                        //vec2 st1  = w2 - w1;
383                        //vec2 st2  = w3 - w1;
384
385                        float s1 = vtx2.texcoord.x - vtx1.texcoord.x;
386                        float t1 = vtx2.texcoord.y - vtx1.texcoord.y;
387                        float s2 = vtx3.texcoord.x - vtx1.texcoord.x;
388                        float t2 = vtx3.texcoord.y - vtx1.texcoord.y;
389
390                        float stst = s1 * t2 - s2 * t1;
391                        float r = 0.0f;
392                        if (stst > 0.0f || stst < 0.0f) r = 1.0f / stst;
393
394                        vec3 sdir = ( t2 * xyz1 - t1 * xyz2 ) * r;
395                        vec3 tdir = ( s1 * xyz2 - s2 * xyz1 ) * r;
396
397                        // the += below obviously doesn't make sense in this case, but I'll
398                        // leave it here for when I move to indices
399                        tan1[i1] += sdir;
400                        tan1[i2] += sdir;
401                        tan1[i3] += sdir;
402
403                        // tan2 not needed anymore??
404                        tan2[i1] += tdir;
405                        tan2[i2] += tdir;
406                        tan2[i3] += tdir;
407                }
408
409                for (std::size_t a = 0; a < count; ++a )
410                {
411                        const vec3& n = m_data[a].normal;
412                        const vec3& t = tan1[a];
413                        if ( ! (t.x == 0.0f && t.y == 0.0f && t.z == 0.0f) )
414                        {
415                                m_data[a].tangent    = vec4( glm::normalize(t - n * glm::dot( n, t )), 0.0f );
416                                m_data[a].tangent[3] = (glm::dot(glm::cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
417                        }
418                }
419
420        }
421
422
423};
424
425nv::wavefront_loader::wavefront_loader( bool normals /*= true*/, bool tangents /*= false */ )
426        : m_normals( normals ), m_tangents( tangents ), m_mesh( nullptr )
427{
428        if ( normals )
429        {
430                if ( tangents )
431                        m_descriptor.initialize<obj_vertex_vtnt>();
432                else
433                        m_descriptor.initialize<obj_vertex_vtn>();
434        }
435        else
436                m_descriptor.initialize<obj_vertex_vt>();
437}
438
439bool nv::wavefront_loader::load( stream& source )
440{
441        if ( m_mesh ) delete m_mesh;
442       
443        obj_reader* reader = nullptr;
444        if ( m_normals )
445        {
446                if ( m_tangents )
447                        reader = new mesh_data_reader_vtnt();
448                else
449                        reader = new mesh_data_reader_vtn();
450        }
451        else
452                reader = new mesh_data_reader_vt();
453        std_stream sstream( &source );
454        reader->read_stream( sstream );
455
456        if ( m_tangents )
457        {
458                reader->calculate_tangents();
459        }
460       
461
462        mesh_raw_channel* channel = new mesh_raw_channel();
463        nv::uint8* data = nullptr;
464        if ( reader->raw_size() > 0 )
465        {
466                data = new uint8[ reader->raw_size() ];
467                std::copy_n( reader->raw_pointer(), reader->raw_size(), data );
468        }
469        channel->data  = data;
470        channel->desc  = m_descriptor;
471        channel->count = reader->size * 3;
472        channel->size  = reader->raw_size();
473        delete reader;
474
475        m_mesh = new mesh_data();
476        m_mesh->add_channel( channel );
477        return true;
478
479}
480
481mesh_data* nv::wavefront_loader::release_mesh_data()
482{
483        mesh_data* result = m_mesh;
484        m_mesh = nullptr;
485        return result;
486}
487
488nv::wavefront_loader::~wavefront_loader()
489{
490        if ( m_mesh ) delete m_mesh;
491}
Note: See TracBrowser for help on using the repository browser.