/* Copyright (C) 2001-2006, William Joseph. Copyright (C) 2010-2014 COR Entertainment, LLC. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "iqm.h" #include "ifilesystem.h" #include "imodel.h" #include "imagelib.h" #include "bytestreamutils.h" #include "../md3model/model.h" typedef unsigned char byte; /* ======================================================================== .IQM triangle model file format ======================================================================== */ typedef struct { float s; float t; } iqmSt_t; void istream_read_iqmSt(PointerInputStream& inputStream, iqmSt_t& st) { st.s = istream_read_float32_le(inputStream); st.t = istream_read_float32_le(inputStream); } typedef struct { unsigned int indices[3]; } iqmTriangle_t; void istream_read_iqmTriangle(PointerInputStream& inputStream, iqmTriangle_t& triangle) { triangle.indices[0] = istream_read_int32_le(inputStream); triangle.indices[1] = istream_read_int32_le(inputStream); triangle.indices[2] = istream_read_int32_le(inputStream); } typedef struct { float v[3]; } iqmPos_t; void istream_read_iqmPos(PointerInputStream& inputStream, iqmPos_t& iqmPos) { iqmPos.v[0] = istream_read_float32_le(inputStream); iqmPos.v[1] = istream_read_float32_le(inputStream); iqmPos.v[2] = istream_read_float32_le(inputStream); } #define IQM_POSITION 0 #define IQM_TEXCOORD 1 #define IQM_NORMAL 2 #define IQM_TANGENT 3 #define IQM_BLENDINDEXES 4 #define IQM_BLENDWEIGHTS 5 #define IQM_COLOR 6 #define IQM_CUSTOM 0x10 #define IQM_BYTE 0 #define IQM_UBYTE 1 #define IQM_SHORT 2 #define IQM_USHORT 3 #define IQM_INT 4 #define IQM_UINT 5 #define IQM_HALF 6 #define IQM_FLOAT 7 #define IQM_DOUBLE 8 // animflags #define IQM_LOOP 1 typedef struct iqmHeader_s { byte id[16]; unsigned int version; unsigned int filesize; unsigned int flags; unsigned int num_text, ofs_text; unsigned int num_meshes, ofs_meshes; unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; unsigned int num_triangles, ofs_triangles, ofs_neighbors; unsigned int num_joints, ofs_joints; unsigned int num_poses, ofs_poses; unsigned int num_anims, ofs_anims; unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; unsigned int num_comment, ofs_comment; unsigned int num_extensions, ofs_extensions; } iqmHeader_t; void istream_read_iqmHeader(PointerInputStream& inputStream, iqmHeader_t& header) { inputStream.read(header.id, 16); #define READINT(x) header.x = istream_read_int32_le(inputStream); READINT (version) READINT (filesize) READINT (flags) READINT (num_text) READINT (ofs_text) READINT (num_meshes) READINT (ofs_meshes) READINT (num_vertexarrays) READINT (num_vertexes) READINT (ofs_vertexarrays) READINT (num_triangles) READINT (ofs_triangles) READINT (ofs_neighbors) READINT (num_joints) READINT (ofs_joints) READINT (num_frames) READINT (num_framechannels) READINT (ofs_frames) READINT (ofs_bounds) READINT (num_comment) READINT (ofs_comment) READINT (num_extensions) READINT (ofs_extensions) #undef READINT } typedef struct iqmvertexarray_s { unsigned int type; unsigned int flags; unsigned int format; unsigned int size; unsigned int offset; } iqmvertexarray_t; void istream_read_iqmVertexarray(PointerInputStream& inputStream, iqmvertexarray_t& out) { #define READINT(x) out.x = istream_read_int32_le(inputStream); READINT (type) READINT (flags) READINT (format) READINT (size) READINT (offset) #undef READINT } ArbitraryMeshVertex IQMVertex_construct(const iqmPos_t* pos, const iqmPos_t* norm, const iqmSt_t* st) { return ArbitraryMeshVertex( Vertex3f (pos->v[0], pos->v[1], pos->v[2]), Normal3f (norm->v[0], norm->v[1], norm->v[2]), TexCoord2f (st->s, st->t) ); } void IQMSurface_read(Model& model, const byte* buffer, ArchiveFile& file) { Surface& surface = model.newSurface(); iqmHeader_t header; { PointerInputStream inputStream(buffer); istream_read_iqmHeader(inputStream, header); } { UniqueVertexBuffer inserter(surface.vertices()); inserter.reserve(header.num_vertexes); int ofs_position = -1, ofs_st = -1, ofs_normal = -1; PointerInputStream vaStream (buffer + header.ofs_vertexarrays); for (int i = 0; i < header.num_vertexarrays; i++) { iqmvertexarray_t va; istream_read_iqmVertexarray (vaStream, va); switch (va.type) { case IQM_POSITION: if (va.format == IQM_FLOAT && va.size == 3) ofs_position = va.offset; break; case IQM_TEXCOORD: if (va.format == IQM_FLOAT && va.size == 2) ofs_st = va.offset; break; case IQM_NORMAL: if (va.format == IQM_FLOAT && va.size == 3) ofs_normal = va.offset; break; } } surface.indices().reserve(header.num_vertexes); PointerInputStream posStream(buffer + ofs_position); Array iqmPos(header.num_vertexes); for(Array::iterator i = iqmPos.begin(); i != iqmPos.end(); ++i) { istream_read_iqmPos(posStream, *i); } PointerInputStream normStream(buffer + ofs_normal); Array iqmNorm(header.num_vertexes); for(Array::iterator i = iqmNorm.begin(); i != iqmNorm.end(); ++i) { istream_read_iqmPos(normStream, *i); } Array iqmSt(header.num_vertexes); PointerInputStream stStream(buffer + ofs_st); for(Array::iterator i = iqmSt.begin(); i != iqmSt.end(); ++i) { istream_read_iqmSt(stStream, *i); } PointerInputStream triangleStream(buffer + header.ofs_triangles); for(int i = 0; i < header.num_triangles; ++i) { iqmTriangle_t triangle; istream_read_iqmTriangle(triangleStream, triangle); for (int j = 0; j < 3; j++) surface.indices().insert(inserter.insert(IQMVertex_construct( &iqmPos[triangle.indices[j]], &iqmNorm[triangle.indices[j]], &iqmSt[triangle.indices[j]]))); } } surface.setShader(""); surface.updateAABB(); } void IQMModel_read(Model& model, const byte* buffer, ArchiveFile& file) { IQMSurface_read(model, buffer, file); model.updateAABB(); } scene::Node& IQMModel_new(const byte* buffer, ArchiveFile& file) { ModelNode* modelNode = new ModelNode(); IQMModel_read(modelNode->model(), buffer, file); return modelNode->node(); } scene::Node& IQMModel_default() { ModelNode* modelNode = new ModelNode(); Model_constructNull(modelNode->model()); return modelNode->node(); } scene::Node& IQMModel_fromBuffer(unsigned char* buffer, ArchiveFile& file) { if (memcmp(buffer, "INTERQUAKEMODEL", 16)) { globalErrorStream() << "IQM read error: incorrect ident\n"; return IQMModel_default(); } else { return IQMModel_new(buffer, file); } } scene::Node& loadIQMModel(ArchiveFile& file) { ScopedArchiveBuffer buffer(file); return IQMModel_fromBuffer(buffer.buffer, file); }