/* 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); } const int IQM_POSITION = 0; const int IQM_TEXCOORD = 1; const int IQM_NORMAL = 2; const int IQM_TANGENT = 3; const int IQM_BLENDINDEXES = 4; const int IQM_BLENDWEIGHTS = 5; const int IQM_COLOR = 6; const int IQM_CUSTOM = 0x10; const int IQM_BYTE = 0; const int IQM_UBYTE = 1; const int IQM_SHORT = 2; const int IQM_USHORT = 3; const int IQM_INT = 4; const int IQM_UINT = 5; const int IQM_HALF = 6; const int IQM_FLOAT = 7; const int IQM_DOUBLE = 8; // animflags const int 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 (unsigned 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 (unsigned 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); }