plugins/iqmmodel: apply textures on surfaces
[xonotic/netradiant.git] / plugins / iqmmodel / iqm.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    Copyright (C) 2010-2014 COR Entertainment, LLC.
4    All Rights Reserved.
5
6    This file is part of GtkRadiant.
7
8    GtkRadiant is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    GtkRadiant is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with GtkRadiant; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #include "iqm.h"
24
25 #include "ifilesystem.h"
26 #include "imodel.h"
27
28 #include "imagelib.h"
29 #include "bytestreamutils.h"
30
31 #include "../md3model/model.h"
32
33 typedef unsigned char byte;
34
35 /*
36    ========================================================================
37
38    .IQM triangle model file format
39
40    ========================================================================
41  */
42
43 typedef struct {
44         float s;
45         float t;
46 } iqmSt_t;
47
48 void istream_read_iqmSt( PointerInputStream &inputStream, iqmSt_t &st ){
49         st.s = istream_read_float32_le( inputStream );
50         st.t = istream_read_float32_le( inputStream );
51 }
52
53 typedef struct {
54         unsigned int indices[3];
55 } iqmTriangle_t;
56
57 void istream_read_iqmTriangle( PointerInputStream &inputStream, iqmTriangle_t &triangle ){
58         triangle.indices[0] = istream_read_int32_le( inputStream );
59         triangle.indices[1] = istream_read_int32_le( inputStream );
60         triangle.indices[2] = istream_read_int32_le( inputStream );
61 }
62
63 typedef struct {
64         float v[3];
65 } iqmPos_t;
66
67 void istream_read_iqmPos( PointerInputStream &inputStream, iqmPos_t &iqmPos ){
68         iqmPos.v[0] = istream_read_float32_le( inputStream );
69         iqmPos.v[1] = istream_read_float32_le( inputStream );
70         iqmPos.v[2] = istream_read_float32_le( inputStream );
71 }
72
73 const int IQM_POSITION = 0;
74 const int IQM_TEXCOORD = 1;
75 const int IQM_NORMAL = 2;
76 const int IQM_TANGENT = 3;
77 const int IQM_BLENDINDEXES = 4;
78 const int IQM_BLENDWEIGHTS = 5;
79 const int IQM_COLOR = 6;
80 const int IQM_CUSTOM = 0x10;
81
82 const int IQM_BYTE = 0;
83 const int IQM_UBYTE = 1;
84 const int IQM_SHORT = 2;
85 const int IQM_USHORT = 3;
86 const int IQM_INT = 4;
87 const int IQM_UINT = 5;
88 const int IQM_HALF = 6;
89 const int IQM_FLOAT = 7;
90 const int IQM_DOUBLE = 8;
91
92 // animflags
93 const int IQM_LOOP = 1;
94
95 typedef struct iqmHeader_s {
96         byte id[16];
97         unsigned int version;
98         unsigned int filesize;
99         unsigned int flags;
100         unsigned int num_text, ofs_text;
101         unsigned int num_meshes, ofs_meshes;
102         unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
103         unsigned int num_triangles, ofs_triangles, ofs_neighbors;
104         unsigned int num_joints, ofs_joints;
105         unsigned int num_poses, ofs_poses;
106         unsigned int num_anims, ofs_anims;
107         unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
108         unsigned int num_comment, ofs_comment;
109         unsigned int num_extensions, ofs_extensions;
110 } iqmHeader_t;
111
112 void istream_read_iqmHeader( PointerInputStream &inputStream, iqmHeader_t &header ){
113         inputStream.read( header.id, 16 );
114 #define READINT( x ) header.x = istream_read_int32_le( inputStream );
115         READINT( version )
116         READINT( filesize )
117         READINT( flags )
118         READINT( num_text )
119         READINT( ofs_text )
120         READINT( num_meshes )
121         READINT( ofs_meshes )
122         READINT( num_vertexarrays )
123         READINT( num_vertexes )
124         READINT( ofs_vertexarrays )
125         READINT( num_triangles )
126         READINT( ofs_triangles )
127         READINT( ofs_neighbors )
128         READINT( num_joints )
129         READINT( ofs_joints )
130         READINT( num_frames )
131         READINT( num_framechannels )
132         READINT( ofs_frames )
133         READINT( ofs_bounds )
134         READINT( num_comment )
135         READINT( ofs_comment )
136         READINT( num_extensions )
137         READINT( ofs_extensions )
138 #undef READINT
139 }
140
141 typedef struct iqmmesh_s {
142         unsigned int name;
143         unsigned int material;
144         unsigned int first_vertex;
145         unsigned int num_vertexes;
146         unsigned int first_triangle;
147         unsigned int num_triangles;
148 } iqmmesh_t;
149
150 void istream_read_iqmMesh( PointerInputStream &inputStream, iqmmesh_t &iqmmesh ){
151 #define READUINT( x ) iqmmesh.x = istream_read_uint32_le( inputStream );
152         READUINT( name )
153         READUINT( material )
154         READUINT( first_vertex )
155         READUINT( num_vertexes )
156         READUINT( first_triangle )
157         READUINT( num_triangles )
158 #undef READUINT
159 }
160
161 typedef struct iqmvertexarray_s {
162         unsigned int type;
163         unsigned int flags;
164         unsigned int format;
165         unsigned int size;
166         unsigned int offset;
167 } iqmvertexarray_t;
168
169 void istream_read_iqmVertexarray( PointerInputStream &inputStream, iqmvertexarray_t &vertexarray ){
170 #define READINT( x ) vertexarray.x = istream_read_int32_le( inputStream );
171         READINT( type )
172         READINT( flags )
173         READINT( format )
174         READINT( size )
175         READINT( offset )
176 #undef READINT
177 }
178
179 typedef struct iqmvertex_s {
180         float position[3];
181         float texcoord[2];
182         float normal[3];
183         float tangent[4];
184         unsigned char blendindices[4];
185         unsigned char blendweights[4];
186         unsigned char color[4];
187 } iqmvertex_t;
188
189 ArbitraryMeshVertex IQMVertex_construct( const iqmPos_t *pos, const iqmPos_t *norm, const iqmSt_t *st ){
190         return ArbitraryMeshVertex(
191                 Vertex3f( pos->v[0], pos->v[1], pos->v[2] ),
192                 Normal3f( norm->v[0], norm->v[1], norm->v[2] ),
193                 TexCoord2f( st->s, st->t )
194                 );
195 }
196
197 void IQMSurface_read( Model &model, const byte *buffer, ArchiveFile &file ){
198         iqmHeader_t header;
199         {
200                 PointerInputStream inputStream( buffer );
201                 istream_read_iqmHeader( inputStream, header );
202         }
203
204         printf( "num meshes: %d\n", header.num_meshes );
205
206         int ofs_position = -1, ofs_st = -1, ofs_normal = -1;
207         PointerInputStream vaStream( buffer + header.ofs_vertexarrays );
208         for ( unsigned int i = 0; i < header.num_vertexarrays; i++ ) {
209                 iqmvertexarray_t va;
210                 istream_read_iqmVertexarray( vaStream, va );
211
212                 switch ( va.type ) {
213                 case IQM_POSITION:
214                         if ( va.format == IQM_FLOAT && va.size == 3 ) {
215                                 ofs_position = va.offset;
216                         }
217                         break;
218                 case IQM_TEXCOORD:
219                         if ( va.format == IQM_FLOAT && va.size == 2 ) {
220                                 ofs_st = va.offset;
221                         }
222                         break;
223                 case IQM_NORMAL:
224                         if ( va.format == IQM_FLOAT && va.size == 3 ) {
225                                 ofs_normal = va.offset;
226                         }
227                         break;
228                 }
229         }
230
231         PointerInputStream posStream( buffer + ofs_position );
232         Array<iqmPos_t> iqmPos( header.num_vertexes );
233         for ( Array<iqmPos_t>::iterator i = iqmPos.begin(); i != iqmPos.end(); ++i ) {
234                 istream_read_iqmPos( posStream, *i );
235         }
236
237         PointerInputStream normStream( buffer + ofs_normal );
238         Array<iqmPos_t> iqmNorm( header.num_vertexes );
239         for ( Array<iqmPos_t>::iterator i = iqmNorm.begin(); i != iqmNorm.end(); ++i ) {
240                 istream_read_iqmPos( normStream, *i );
241         }
242
243         Array<iqmSt_t> iqmSt( header.num_vertexes );
244         PointerInputStream stStream( buffer + ofs_st );
245         for ( Array<iqmSt_t>::iterator i = iqmSt.begin(); i != iqmSt.end(); ++i ) {
246                 istream_read_iqmSt( stStream, *i );
247         }
248
249         PointerInputStream iqmMesh( buffer + header.ofs_meshes );
250         for ( unsigned int m = 0; m < header.num_meshes; m++ ) {
251                 Surface &surface = model.newSurface();
252
253                 iqmmesh_t iqmmesh;
254                 istream_read_iqmMesh( iqmMesh, iqmmesh );
255
256                 // if not malformed data neither missing string
257                 if ( iqmmesh.name <= header.num_text && iqmmesh.name > 0 ) {
258                         char *name;
259                         name = (char*) buffer + header.ofs_text + iqmmesh.name;
260                         printf( "mesh: %d, name: %s\n", m, name );
261                 }
262
263                 bool material_found = false;
264                 // if not malformed data neither missing string
265                 if ( iqmmesh.material <= header.num_text && iqmmesh.material > 0 ) {
266                         char *material;
267                         material = (char*) buffer + header.ofs_text + iqmmesh.material;
268
269                         printf( "mesh: %d, texture: %s\n", m, material );
270
271                         if ( material[0] != '\0' ) {
272                                 surface.setShader( material );
273                                 material_found = true;
274                         }
275                 }
276
277                 if ( !material_found ) {
278                         // empty string will trigger "textures/shader/notex" on display
279                         surface.setShader( "" );
280                 }
281
282                 printf( "mesh: %d, num vertexes: %d\n", m, iqmmesh.num_vertexes );
283                 printf( "mesh: %d, num triangles: %d\n", m, iqmmesh.num_triangles );
284
285                 UniqueVertexBuffer<ArbitraryMeshVertex> inserter( surface.vertices() );
286                 inserter.reserve( iqmmesh.num_vertexes );
287
288                 surface.indices().reserve( iqmmesh.num_vertexes );
289
290                 unsigned int triangle_offset = header.ofs_triangles + iqmmesh.first_triangle * sizeof( iqmTriangle_t );
291                 PointerInputStream triangleStream( buffer + triangle_offset );
292                 for ( unsigned int i = 0; i < iqmmesh.num_triangles; ++i ) {
293                         iqmTriangle_t triangle;
294                         istream_read_iqmTriangle( triangleStream, triangle );
295                         for ( int j = 0; j < 3; j++ ) {
296                                 surface.indices().insert( inserter.insert( IQMVertex_construct(
297                                                                                                                            &iqmPos[triangle.indices[j]],
298                                                                                                                            &iqmNorm[triangle.indices[j]],
299                                                                                                                            &iqmSt[triangle.indices[j]] ) ) );
300                         }
301                 }
302
303                 surface.updateAABB();
304         }
305 }
306
307 void IQMModel_read( Model &model, const byte *buffer, ArchiveFile &file ){
308         IQMSurface_read( model, buffer, file );
309         model.updateAABB();
310 }
311
312 scene::Node &IQMModel_new( const byte *buffer, ArchiveFile &file ){
313         ModelNode *modelNode = new ModelNode();
314         IQMModel_read( modelNode->model(), buffer, file );
315         return modelNode->node();
316 }
317
318 scene::Node &IQMModel_default(){
319         ModelNode *modelNode = new ModelNode();
320         Model_constructNull( modelNode->model() );
321         return modelNode->node();
322 }
323
324 scene::Node &IQMModel_fromBuffer( unsigned char *buffer, ArchiveFile &file ){
325         if ( memcmp( buffer, "INTERQUAKEMODEL", 16 ) ) {
326                 globalErrorStream() << "IQM read error: incorrect ident\n";
327                 return IQMModel_default();
328         }
329         else {
330                 return IQMModel_new( buffer, file );
331         }
332 }
333
334 scene::Node &loadIQMModel( ArchiveFile &file ){
335         ScopedArchiveBuffer buffer( file );
336         return IQMModel_fromBuffer( buffer.buffer, file );
337 }