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

Last change on this file since 239 was 239, checked in by epyon, 11 years ago
  • massive update of mesh handling
  • universal mesh handling routines
  • removed a lot of legacy code
  • significantly streamlined MD5 loading
  • all tests updated to new features
File size: 8.7 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;
[239]58        virtual size_t raw_size() const = 0;
59        virtual const uint8* raw_pointer() const = 0;
[238]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
153
[238]154struct mesh_data_reader_vt : public obj_reader
155{
156        mesh_data_reader_vt()  {}
157        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32*, size_t count )
158        {
159                if ( count < 3 ) return 0; // TODO : report error?
160                // TODO : support if normals not present;
161                std::size_t result = 0;
162                // Simple triangulation - obj's shouldn't have more than quads anyway
163                for ( size_t i = 2; i < count; ++i )
164                {
165                        result++;
166                        m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ] );
167                        m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ] );
168                        m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ] );
169                }
170                return result;
171        }
172        std::vector< obj_vertex_vt > m_data;
[239]173        virtual size_t raw_size() const { return m_data.size() * sizeof( obj_vertex_vt ); }
174        virtual const uint8* raw_pointer() const { return (const uint8*)m_data.data(); }
[238]175};
176
177struct mesh_data_reader_vtn : public obj_reader
178{
179        mesh_data_reader_vtn()  {}
180        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
181        {
182                if ( count < 3 ) return 0; // TODO : report error?
183                // TODO : support if normals not present;
184                std::size_t result = 0;
185                // Simple triangulation - obj's shouldn't have more than quads anyway
186                for ( size_t i = 2; i < count; ++i )
187                {
188                        result++;
189                        m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
190                        m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
191                        m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
192                }
193                return result;
194        }
195        std::vector< obj_vertex_vtn > m_data;
[239]196        virtual size_t raw_size() const { return m_data.size() * sizeof( obj_vertex_vtn ); }
197        virtual const uint8* raw_pointer() const { return (const uint8*)m_data.data(); }
[238]198};
199
200struct mesh_data_reader_vtnt : public obj_reader
201{
202        mesh_data_reader_vtnt()  {}
203        virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
204        {
205                if ( count < 3 ) return 0; // TODO : report error?
206                // TODO : support if normals not present;
207                std::size_t result = 0;
208                // Simple triangulation - obj's shouldn't have more than quads anyway
209                for ( size_t i = 2; i < count; ++i )
210                {
211                        result++;
212                        m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
213                        m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
214                        m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
215                }
216                return result;
217        }
218        std::vector< obj_vertex_vtnt > m_data;
[239]219        virtual size_t raw_size() const { return m_data.size() * sizeof( obj_vertex_vtnt ); }
220        virtual const uint8* raw_pointer() const { return (const uint8*)m_data.data(); }
[238]221
222        // based on http://www.terathon.com/code/tangent.html
223        void calculate_tangents()
224        {
225                //              const std::vector< vec3 >& vp = m_mesh->get_positions();
226                //              const std::vector< vec2 >& vt = m_mesh->get_texcoords();
227                //              const std::vector< vec3 >& vn = m_mesh->get_normals();
228                //              std::vector< vec3 >& tg = m_mesh->get_tangents();
229
230                size_t count  = m_data.size();
231                size_t tcount = count / 3;
232
233                std::vector< vec3 > tan1( count );
234                std::vector< vec3 > tan2( count );
235
236                for (size_t a = 0; a < tcount; ++a )
237                {
238                        size_t i1 = a * 3;
239                        size_t i2 = a * 3 + 1;
240                        size_t i3 = a * 3 + 2;
241                        obj_vertex_vtnt& vtx1 = m_data[ i1 ];
242                        obj_vertex_vtnt& vtx2 = m_data[ i2 ];
243                        obj_vertex_vtnt& vtx3 = m_data[ i3 ];
244
245                        // TODO: simplify
246                        vec3 xyz1 = vtx2.position - vtx1.position;
247                        vec3 xyz2 = vtx3.position - vtx1.position;
248                        //vec2 st1  = w2 - w1;
249                        //vec2 st2  = w3 - w1;
250
251                        float s1 = vtx2.texcoord.x - vtx1.texcoord.x;
252                        float t1 = vtx2.texcoord.y - vtx1.texcoord.y;
253                        float s2 = vtx3.texcoord.x - vtx1.texcoord.x;
254                        float t2 = vtx3.texcoord.y - vtx1.texcoord.y;
255
256                        float stst = s1 * t2 - s2 * t1;
257                        float r = 0.0f;
258                        if (stst > 0.0f || stst < 0.0f) r = 1.0f / stst;
259
260                        vec3 sdir = ( t2 * xyz1 - t1 * xyz2 ) * r;
261                        vec3 tdir = ( s1 * xyz2 - s2 * xyz1 ) * r;
262
263                        // the += below obviously doesn't make sense in this case, but I'll
264                        // leave it here for when I move to indices
265                        tan1[i1] += sdir;
266                        tan1[i2] += sdir;
267                        tan1[i3] += sdir;
268
269                        // tan2 not needed anymore??
270                        tan2[i1] += tdir;
271                        tan2[i2] += tdir;
272                        tan2[i3] += tdir;
273                }
274
275                for (std::size_t a = 0; a < count; ++a )
276                {
277                        const vec3& n = m_data[a].normal;
278                        const vec3& t = tan1[a];
279                        if ( ! (t.x == 0.0f && t.y == 0.0f && t.z == 0.0f) )
280                        {
281                                m_data[a].tangent    = vec4( glm::normalize(t - n * glm::dot( n, t )), 0.0f );
282                                m_data[a].tangent[3] = (glm::dot(glm::cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
283                        }
284                }
285
286        }
287
288
289};
290
[239]291nv::obj_loader::obj_loader( bool normals /*= true*/, bool tangents /*= false */ )
[238]292        : m_normals( normals ), m_tangents( tangents ), m_mesh( nullptr )
293{
294        if ( normals )
295        {
296                if ( tangents )
297                        m_descriptor.initialize<obj_vertex_vtnt>();
298                else
299                        m_descriptor.initialize<obj_vertex_vtn>();
300        }
301        else
302                m_descriptor.initialize<obj_vertex_vt>();
303}
304
[239]305bool nv::obj_loader::load( stream& source )
[238]306{
307        if ( m_mesh ) delete m_mesh;
308       
309        obj_reader* reader = nullptr;
310        if ( m_normals )
311        {
312                if ( m_tangents )
313                        reader = new mesh_data_reader_vtnt();
314                else
315                        reader = new mesh_data_reader_vtn();
316        }
317        else
318                reader = new mesh_data_reader_vt();
319        std_stream sstream( &source );
320        reader->read_stream( sstream );
321
322        if ( m_tangents )
323        {
324                reader->calculate_tangents();
325        }
326       
327
328        mesh_raw_channel* channel = new mesh_raw_channel();
329        nv::uint8* data = nullptr;
[239]330
[238]331        if ( reader->raw_size() > 0 )
332        {
333                data = new uint8[ reader->raw_size() ];
334                std::copy_n( reader->raw_pointer(), reader->raw_size(), data );
335        }
336        channel->data  = data;
337        channel->desc  = m_descriptor;
338        channel->count = reader->size * 3;
339        channel->size  = reader->raw_size();
340        delete reader;
341
342        m_mesh = new mesh_data();
343        m_mesh->add_channel( channel );
344        return true;
345
346}
347
[239]348mesh_data* nv::obj_loader::release_mesh_data()
[238]349{
350        mesh_data* result = m_mesh;
351        m_mesh = nullptr;
352        return result;
353}
354
[239]355nv::obj_loader::~obj_loader()
[238]356{
357        if ( m_mesh ) delete m_mesh;
358}
Note: See TracBrowser for help on using the repository browser.