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

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