add IQM format support into lib/picomodel 184/head
authorMarco Hladik <marco@icculus.org>
Thu, 29 Jul 2021 15:44:53 +0000 (17:44 +0200)
committerThomas Debesse <dev@illwieckz.net>
Tue, 10 Aug 2021 04:29:59 +0000 (06:29 +0200)
This is a combination of 2 commits.

- add IQM format support into lib/picomodel
  https://github.com/TTimo/GtkRadiant/commit/3408871d79d51b9bf27f0098cd3bc4edd92b105d
- Merge illwieckz their portability fix
  https://github.com/TTimo/GtkRadiant/commit/be993ad4222fc9fc28080a8ba8f962da82c7b1f4

See https://github.com/TTimo/GtkRadiant/pull/668

libs/picomodel/CMakeLists.txt
libs/picomodel/picointernal.h
libs/picomodel/picomodules.c
libs/picomodel/pm_fm.h
libs/picomodel/pm_iqm.c [new file with mode: 0644]

index 4800fb46af957ba1704a71bdae0ae60990c6c3b4..2e0b96779cff2b47297158596bb7c5b312680fc9 100644 (file)
@@ -16,6 +16,7 @@ add_library(picomodel STATIC
         pm_3ds.c
         pm_ase.c
         pm_fm.c pm_fm.h
         pm_3ds.c
         pm_ase.c
         pm_fm.c pm_fm.h
+        pm_iqm.c
         pm_lwo.c
         pm_md2.c
         pm_md3.c
         pm_lwo.c
         pm_md2.c
         pm_md3.c
index 1ec806ad6190255cef2667270101003e01f46316..98b6cf8f1cd0ff1c7f20766a576c2215dd0b9c67 100644 (file)
@@ -80,6 +80,10 @@ extern "C"
 extern const picoColor_t picoColor_white;
 
 /* types */
 extern const picoColor_t picoColor_white;
 
 /* types */
+#ifndef byte
+       typedef unsigned char byte;
+#endif
+
 typedef struct picoParser_s
 {
        const char *buffer;
 typedef struct picoParser_s
 {
        const char *buffer;
index 77bb4918b22af6a9385cc71af558c912dc347d56..406ee16f9ba1ef81d26cd98660defacd87d23aee 100644 (file)
@@ -48,6 +48,7 @@ extern const picoModule_t picoModuleMD2;
 extern const picoModule_t picoModuleFM;
 extern const picoModule_t picoModuleLWO;
 extern const picoModule_t picoModuleTerrain;
 extern const picoModule_t picoModuleFM;
 extern const picoModule_t picoModuleLWO;
 extern const picoModule_t picoModuleTerrain;
+extern const picoModule_t picoModuleIQM;
 
 
 
 
 
 
@@ -64,6 +65,7 @@ const picoModule_t *picoModules[] =
        &picoModuleLWO,     /* lightwave object */
        &picoModuleTerrain, /* picoterrain object */
        &picoModuleOBJ,     /* wavefront object */
        &picoModuleLWO,     /* lightwave object */
        &picoModuleTerrain, /* picoterrain object */
        &picoModuleOBJ,     /* wavefront object */
+       &picoModuleIQM,     /* interquake model */
        NULL                /* arnold */
 };
 
        NULL                /* arnold */
 };
 
index 6fa317ca8714b42f18e1cc20495f739c7a68a030..8ffcc405c923bf3bbd1a0df2270e7d38fdd3f3fd 100644 (file)
 #define INFO_HEIGHT 5
 #define INFO_Y ( SKINPAGE_HEIGHT - INFO_HEIGHT )
 
 #define INFO_HEIGHT 5
 #define INFO_Y ( SKINPAGE_HEIGHT - INFO_HEIGHT )
 
-#ifndef byte
-       #define byte unsigned char
-#endif
-
 
 //
 //     Generic header on every chunk
 
 //
 //     Generic header on every chunk
diff --git a/libs/picomodel/pm_iqm.c b/libs/picomodel/pm_iqm.c
new file mode 100644 (file)
index 0000000..739cbef
--- /dev/null
@@ -0,0 +1,359 @@
+/* -----------------------------------------------------------------------------
+
+   InterQuake Model - PicoModel Library
+
+   Copyright (c) 2018-2021, FTE Team <fteqw.org>
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without modification,
+   are permitted provided that the following conditions are met:
+
+   Redistributions of source code must retain the above copyright notice, this list
+   of conditions and the following disclaimer.
+
+   Redistributions in binary form must reproduce the above copyright notice, this
+   list of conditions and the following disclaimer in the documentation and/or
+   other materials provided with the distribution.
+
+   Neither the names of the copyright holders nor the names of its contributors may
+   be used to endorse or promote products derived from this software without
+   specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+   ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   ----------------------------------------------------------------------------- */
+
+/* dependencies */
+#include "picointernal.h"
+
+extern const picoModule_t picoModuleIQM;
+
+#define IQM_MAGIC "INTERQUAKEMODEL"    //15+null
+
+/*
+   ========================================================================
+
+   .IQM triangle model file format
+
+   ========================================================================
+ */
+
+enum
+{
+       IQM_POSITION = 0,
+       IQM_TEXCOORD = 1,
+       IQM_NORMAL = 2,
+       IQM_TANGENT = 3,
+       IQM_BLENDINDEXES = 4,
+       IQM_BLENDWEIGHTS = 5,
+       IQM_COLOR = 6,
+       IQM_CUSTOM = 0x10
+};
+
+enum
+{
+       IQM_BYTE = 0,
+       IQM_UBYTE = 1,
+       IQM_SHORT = 2,
+       IQM_USHORT = 3,
+       IQM_INT = 4,
+       IQM_UINT = 5,
+       IQM_HALF = 6,
+       IQM_FLOAT = 7,
+       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;
+
+typedef struct iqmmesh_s {
+       unsigned int name;
+       unsigned int material;
+       unsigned int first_vertex;
+       unsigned int num_vertexes;
+       unsigned int first_triangle;
+       unsigned int num_triangles;
+} iqmmesh_t;
+
+typedef struct iqmvertexarray_s {
+       unsigned int type;
+       unsigned int flags;
+       unsigned int format;
+       unsigned int size;
+       unsigned int offset;
+} iqmvertexarray_t;
+
+//is anyone actually going to run this on a big-endian cpu?
+static iqmHeader_t SwapHeader(const iqmHeader_t *h)
+{
+       iqmHeader_t r = *h;
+       r.version = _pico_little_long(h->version);
+       r.filesize = _pico_little_long(h->filesize);
+       r.flags = _pico_little_long(h->flags);
+       r.num_text = _pico_little_long(h->num_text);
+       r.ofs_text = _pico_little_long(h->ofs_text);
+       r.num_meshes = _pico_little_long(h->num_meshes);
+       r.ofs_meshes = _pico_little_long(h->ofs_meshes);
+       r.num_vertexarrays = _pico_little_long(h->num_vertexarrays);
+       r.num_vertexes = _pico_little_long(h->num_vertexes);
+       r.ofs_vertexarrays = _pico_little_long(h->ofs_vertexarrays);
+       r.num_triangles = _pico_little_long(h->num_triangles);
+       r.ofs_triangles = _pico_little_long(h->ofs_triangles);
+       r.ofs_neighbors = _pico_little_long(h->ofs_neighbors);
+       r.num_joints = _pico_little_long(h->num_joints);
+       r.ofs_joints = _pico_little_long(h->ofs_joints);
+       r.num_poses = _pico_little_long(h->num_poses);
+       r.ofs_poses = _pico_little_long(h->ofs_poses);
+       r.num_anims = _pico_little_long(h->num_anims);
+       r.ofs_anims = _pico_little_long(h->ofs_anims);
+       r.num_frames = _pico_little_long(h->num_frames);
+       r.num_framechannels = _pico_little_long(h->num_framechannels);
+       r.ofs_frames = _pico_little_long(h->ofs_frames);
+       r.ofs_bounds = _pico_little_long(h->ofs_bounds);
+       r.num_comment = _pico_little_long(h->num_comment);
+       r.ofs_comment = _pico_little_long(h->ofs_comment);
+       r.num_extensions = _pico_little_long(h->num_extensions);
+       r.ofs_extensions = _pico_little_long(h->ofs_extensions);
+       return r;
+}
+
+// _iqm_canload()
+static int _iqm_canload( PM_PARAMS_CANLOAD ){
+       iqmHeader_t h;
+
+       //make sure there's enough data for the header...
+       if ((size_t)bufSize < sizeof(h))
+               return PICO_PMV_ERROR_SIZE;
+       h = SwapHeader(buffer);
+
+       //make sure its actually an iqm
+       if (memcmp(h.id, IQM_MAGIC, sizeof(h.id)))
+               return PICO_PMV_ERROR_IDENT;
+       //v1 is flawed, we don't know about anything higher either.
+       if (h.version != 2)
+               return PICO_PMV_ERROR_VERSION;
+       //make sure its not truncated
+       if ((size_t)h.filesize != (size_t)bufSize)
+               return PICO_PMV_ERROR_SIZE;
+
+       //looks like we can probably use it.
+       return PICO_PMV_OK;
+}
+
+// _iqm_load() loads an interquake model file.
+static picoModel_t *_iqm_load( PM_PARAMS_LOAD ){
+       picoModel_t     *picoModel;
+       picoSurface_t   *picoSurface;
+       picoShader_t    *picoShader;
+       const float *inf;
+       const byte *inb;
+       picoVec3_t xyz, normal;
+       picoVec2_t st;
+       picoColor_t color;
+
+       iqmHeader_t h;
+       iqmmesh_t m;
+       iqmvertexarray_t a;
+       size_t s, t, j, i;
+       const char *stringtable;
+       char skinname[512];
+       const unsigned int *tri;
+
+       //just in case
+       if (_iqm_canload(fileName, buffer, bufSize) != PICO_PMV_OK)
+       {
+               _pico_printf( PICO_ERROR, "%s is not an IQM File!", fileName );
+               return NULL;
+       }
+       h = SwapHeader(buffer);
+       stringtable = (const char*)buffer + h.ofs_text;
+
+       // do frame check
+       if ( h.num_anims != 0 ) {
+               _pico_printf( PICO_WARNING, "%s has animations! Using base pose only.", fileName );
+       }
+
+       /* create new pico model */
+       picoModel = PicoNewModel();
+       if ( picoModel == NULL ) {
+               _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
+               return NULL;
+       }
+
+       /* do model setup */
+       PicoSetModelFrameNum( picoModel, frameNum );
+       PicoSetModelNumFrames( picoModel, 1 ); /* sea */
+       PicoSetModelName( picoModel, fileName );
+       PicoSetModelFileName( picoModel, fileName );
+
+       for (s = 0; s < h.num_meshes; s++)
+       {
+               m = ((const iqmmesh_t*)((const char*)buffer + h.ofs_meshes))[s];
+               m.first_triangle = _pico_little_long(m.first_triangle);
+               m.first_vertex = _pico_little_long(m.first_vertex);
+               m.material = _pico_little_long(m.material);
+               m.name = _pico_little_long(m.name);
+               m.num_triangles = _pico_little_long(m.num_triangles);
+               m.num_vertexes = _pico_little_long(m.num_vertexes);
+
+               // allocate new pico surface
+               picoSurface = PicoNewSurface( picoModel );
+               if ( picoSurface == NULL ) {
+                       _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
+                       PicoFreeModel( picoModel );
+                       return NULL;
+               }
+
+               // detox Skin name
+               memcpy(skinname, stringtable+m.material, sizeof(skinname));
+               _pico_setfext( skinname, "" );
+               _pico_unixify( skinname );
+
+               PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
+               PicoSetSurfaceName( picoSurface, stringtable+m.name );
+               picoShader = PicoNewShader( picoModel );
+               if ( picoShader == NULL ) {
+                       _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
+                       PicoFreeModel( picoModel );
+                       return NULL;
+               }
+
+               PicoSetShaderName( picoShader, skinname );
+
+               // associate current surface with newly created shader
+               PicoSetSurfaceShader( picoSurface, picoShader );
+
+
+               // spew the surface's indexes
+               tri = (const unsigned int *)((const char *)buffer+h.ofs_triangles) + m.first_triangle*3;
+               for (t = 0; t < m.num_triangles*3; t++)
+                       PicoSetSurfaceIndex( picoSurface, t, _pico_little_long(*tri++) - m.first_vertex );
+
+               for ( j = 0; j < h.num_vertexarrays; j++)
+               {
+                       a = ((const iqmvertexarray_t*)((const char*)buffer + h.ofs_vertexarrays))[j];
+                       a.flags = _pico_little_long(a.flags);
+                       a.format = _pico_little_long(a.format);
+                       a.offset = _pico_little_long(a.offset);
+                       a.size = _pico_little_long(a.size);
+                       a.type = _pico_little_long(a.type);
+
+                       switch(a.type)
+                       {
+                       case IQM_POSITION:
+                               if (a.format == IQM_FLOAT && a.size >= 3)
+                               {
+                                       inf = (const float*)((const char *)buffer + a.offset) + m.first_vertex*a.size;
+                                       for ( i = 0; i < m.num_vertexes; i++, inf += a.size )
+                                       {
+                                               xyz[0] = _pico_little_float(inf[0]);
+                                               xyz[1] = _pico_little_float(inf[1]);
+                                               xyz[2] = _pico_little_float(inf[2]);
+                                               PicoSetSurfaceXYZ( picoSurface, i, xyz );
+                                       }
+                               }
+                               break;
+                       case IQM_TEXCOORD:
+                               if (a.format == IQM_FLOAT && a.size >= 2)
+                               {
+                                       inf = (const float*)((const char *)buffer + a.offset) + m.first_vertex*a.size;
+                                       for ( i = 0; i < m.num_vertexes; i++, inf += a.size )
+                                       {
+                                               st[0] = _pico_little_float(inf[0]);
+                                               st[1] = _pico_little_float(inf[1]);
+                                               PicoSetSurfaceST( picoSurface, 0, i, st );
+                                       }
+                               }
+                               break;
+                       case IQM_NORMAL:
+                               if (a.format == IQM_FLOAT && a.size >= 3)
+                               {
+                                       inf = (const float*)((const char *)buffer + a.offset) + m.first_vertex*a.size;
+                                       for ( i = 0; i < m.num_vertexes; i++, inf += a.size )
+                                       {
+                                               normal[0] = _pico_little_float(inf[0]);
+                                               normal[1] = _pico_little_float(inf[1]);
+                                               normal[2] = _pico_little_float(inf[2]);
+                                               PicoSetSurfaceNormal( picoSurface, i, normal );
+                                       }
+                               }
+                               break;
+                       case IQM_COLOR:
+                               if (a.format == IQM_UBYTE && a.size >= 3)
+                               {
+                                       inb = (const byte*)((const char *)buffer + a.offset) + m.first_vertex*a.size;
+                                       for ( i = 0; i < m.num_vertexes; i++, inb += a.size )
+                                       {
+                                               color[0] = inb[0];
+                                               color[1] = inb[1];
+                                               color[2] = inb[2];
+                                               color[3] = (a.size>=4)?inb[3]:255;
+                                               PicoSetSurfaceColor( picoSurface, 0, i, color );
+                                       }
+                               }
+                               else if (a.format == IQM_FLOAT && a.size >= 3)
+                               {
+                                       inf = (const float*)((const char *)buffer + a.offset) + m.first_vertex*a.size;
+                                       for ( i = 0; i < m.num_vertexes; i++, inf += a.size )
+                                       {
+                                               color[0] = inf[0]*255;
+                                               color[1] = inf[1]*255;
+                                               color[2] = inf[2]*255;
+                                               color[3] = (a.size>=4)?inf[3]*255:255;
+                                               PicoSetSurfaceColor( picoSurface, 0, i, color );
+                                       }
+                               }
+                               break;
+                       case IQM_TANGENT:
+                       case IQM_BLENDINDEXES:
+                       case IQM_BLENDWEIGHTS:
+                       case IQM_CUSTOM:
+                               break;  // these attributes are not relevant.
+                       }
+               }
+       }
+
+       return picoModel;
+}
+
+/* pico file format module definition */
+const picoModule_t picoModuleIQM =
+{
+       "0.1",                          /* module version string */
+       "InterQuake Model",             /* module display name */
+       "Spoike",                       /* author's name */
+       "2018-2021 FTE Team",           /* module copyright */
+       {
+               "iqm", NULL, NULL, NULL /* default extensions to use */
+       },
+       _iqm_canload,                   /* validation routine */
+       _iqm_load,                      /* load routine */
+       NULL,                           /* save validation routine */
+       NULL                            /* save routine */
+};