Quake II player model (version 8): C++11/STL parsing library

The MD2 format is used for 3D animated models in id Sofware's Quake II.

A model consists of named frames, each with the same number of vertices (vertices_per_frame). Each such vertex has a position and normal in model space. Each vertex has the same topological "meaning" across frames, in terms of triangle and texture info; it just varies in position and normal for animation purposes.

How the vertices form triangles is defined via disjoint triangles or via gl_cmds (which allows strip and fan topology). Each triangle contains three vertex_indices into frame vertices, and three tex_point_indices into global tex_coords. Each texture point has pixel coords s_px and t_px ranging from 0 to skin_{width,height}_px respectively, along with {s,t}_normalized ranging from 0 to 1 for your convenience.

A GL command has a primitive type (TRIANGLE_FAN or TRIANGLE_STRIP) along with some vertices. Each GL vertex contains tex_coords_normalized from 0 to 1, and a vertex_index into frame vertices.

A model may also contain skins, which are just file paths to PCX images. However, this is empty for many models, in which case it is up to the client (e.g. Q2PRO) to offer skins some other way (e.g. by similar filename in the current directory).

There are 198 frames in total, partitioned into a fixed set of ranges used for different animations. Each frame has a standard name for humans, but the client just uses their index and the name can be arbitrary. The name, start frame index and frame count of each animation can be looked up in the arrays anim_names, anim_start_indices, and anim_num_frames respectively. This information is summarized in the following table:

|   INDEX  |    NAME | SUFFIX | NOTES                                                  |
|:--------:|--------:|:-------|:-------------------------------------------------------|
|    0-39  |   stand | 01-40  | Idle animation                                         |
|   40-45  |     run | 1-6    | Full run cycle                                         |
|   46-53  |  attack | 1-8    | Shoot, reload; some weapons just repeat 1st few frames |
|   54-57  |   pain1 | 01-04  | Q2Pro also uses this for switching weapons             |
|   58-61  |   pain2 | 01-04  |                                                        |
|   62-65  |   pain3 | 01-04  |                                                        |
|   66-71  |    jump | 1-6    | Starts at height and lands on feet                     |
|   72-83  |    flip | 01-12  | Flipoff, i.e. middle finger                            |
|   84-94  |  salute | 01-11  |                                                        |
|   95-111 |   taunt | 01-17  |                                                        |
|  112-122 |    wave | 01-11  | Q2Pro plays this backwards for a handgrenade toss      |
|  123-134 |   point | 01-12  |                                                        |
|  135-153 |  crstnd | 01-19  | Idle while crouching                                   |
|  154-159 |  crwalk | 1-6    |                                                        |
|  160-168 | crattak | 1-9    |                                                        |
|  169-172 |  crpain | 1-4    |                                                        |
|  173-177 | crdeath | 1-5    |                                                        |
|  178-183 |  death1 | 01-06  |                                                        |
|  184-189 |  death2 | 01-06  |                                                        |
|  190-197 |  death3 | 01-08  |                                                        |

The above are filled in for player models; for the separate weapon models, the final frame is 173 "g_view" (unknown purpose) since weapons aren't shown during death animations. a_grenades.md2, the handgrenade weapon model, is the same except that the wave frames are blank (according to the default female model files). This is likely due to its dual use as a grenade throw animation where this model must leave the player's model.

Application

Quake II

File extension

md2

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of Quake II player model (version 8) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.md2", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    quake2_md2_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.magic() // => get magic

C++11/STL source code to parse Quake II player model (version 8)

quake2_md2.h

#pragma once

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * The MD2 format is used for 3D animated models in id Sofware's Quake II.
 * 
 * A model consists of named `frames`, each with the same number of `vertices`
 * (`vertices_per_frame`). Each such vertex has a `position` and `normal` in
 * model space. Each vertex has the same topological "meaning" across frames, in
 * terms of triangle and texture info; it just varies in position and normal for
 * animation purposes.
 * 
 * How the vertices form triangles is defined via disjoint `triangles` or via
 * `gl_cmds` (which allows strip and fan topology). Each triangle contains three
 * `vertex_indices` into frame vertices, and three `tex_point_indices` into
 * global `tex_coords`. Each texture point has pixel coords `s_px` and `t_px`
 * ranging from 0 to `skin_{width,height}_px` respectively, along with
 * `{s,t}_normalized` ranging from 0 to 1 for your convenience.
 * 
 * A GL command has a `primitive` type (`TRIANGLE_FAN` or `TRIANGLE_STRIP`) along
 * with some `vertices`. Each GL vertex contains `tex_coords_normalized` from 0
 * to 1, and a `vertex_index` into frame vertices.
 * 
 * A model may also contain `skins`, which are just file paths to PCX images.
 * However, this is empty for many models, in which case it is up to the client
 * (e.g. Q2PRO) to offer skins some other way (e.g. by similar filename in the
 * current directory).
 * 
 * There are 198 `frames` in total, partitioned into a fixed set of ranges used
 * for different animations. Each frame has a standard `name` for humans, but the
 * client just uses their index and the name can be arbitrary. The name, start
 * frame index and frame count of each animation can be looked up in the arrays
 * `anim_names`, `anim_start_indices`, and `anim_num_frames` respectively. This
 * information is summarized in the following table:
 * 
 * ```
 * |   INDEX  |    NAME | SUFFIX | NOTES                                                  |
 * |:--------:|--------:|:-------|:-------------------------------------------------------|
 * |    0-39  |   stand | 01-40  | Idle animation                                         |
 * |   40-45  |     run | 1-6    | Full run cycle                                         |
 * |   46-53  |  attack | 1-8    | Shoot, reload; some weapons just repeat 1st few frames |
 * |   54-57  |   pain1 | 01-04  | Q2Pro also uses this for switching weapons             |
 * |   58-61  |   pain2 | 01-04  |                                                        |
 * |   62-65  |   pain3 | 01-04  |                                                        |
 * |   66-71  |    jump | 1-6    | Starts at height and lands on feet                     |
 * |   72-83  |    flip | 01-12  | Flipoff, i.e. middle finger                            |
 * |   84-94  |  salute | 01-11  |                                                        |
 * |   95-111 |   taunt | 01-17  |                                                        |
 * |  112-122 |    wave | 01-11  | Q2Pro plays this backwards for a handgrenade toss      |
 * |  123-134 |   point | 01-12  |                                                        |
 * |  135-153 |  crstnd | 01-19  | Idle while crouching                                   |
 * |  154-159 |  crwalk | 1-6    |                                                        |
 * |  160-168 | crattak | 1-9    |                                                        |
 * |  169-172 |  crpain | 1-4    |                                                        |
 * |  173-177 | crdeath | 1-5    |                                                        |
 * |  178-183 |  death1 | 01-06  |                                                        |
 * |  184-189 |  death2 | 01-06  |                                                        |
 * |  190-197 |  death3 | 01-08  |                                                        |
 * ```
 * 
 * The above are filled in for player models; for the separate weapon models,
 * the final frame is 173 "g_view" (unknown purpose) since weapons aren't shown
 * during death animations. `a_grenades.md2`, the handgrenade weapon model, is
 * the same except that the `wave` frames are blank (according to the default
 * female model files). This is likely due to its dual use as a grenade throw
 * animation where this model must leave the player's model.
 * \sa https://icculus.org/~phaethon/q3a/formats/md2-schoenblum.html Source
 * \sa http://tfc.duke.free.fr/coding/md2-specs-en.html Source
 * \sa http://tastyspleen.net/~panjoo/downloads/quake2_model_frames.html Source
 * \sa http://wiki.polycount.com/wiki/OldSiteResourcesQuake2FramesList Source
 */

class quake2_md2_t : public kaitai::kstruct {

public:
    class vertex_t;
    class compressed_vec_t;
    class triangle_t;
    class frame_t;
    class gl_cmds_list_t;
    class tex_point_t;
    class vec3f_t;
    class gl_vertex_t;
    class gl_cmd_t;

    enum gl_primitive_t {
        GL_PRIMITIVE_TRIANGLE_STRIP = 0,
        GL_PRIMITIVE_TRIANGLE_FAN = 1
    };

    quake2_md2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

private:
    void _read();
    void _clean_up();

public:
    ~quake2_md2_t();

    class vertex_t : public kaitai::kstruct {

    public:

        vertex_t(kaitai::kstream* p__io, quake2_md2_t::frame_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~vertex_t();

    private:
        bool f_normal;
        std::vector<double>* m_normal;

    public:
        std::vector<double>* normal();

    private:
        std::unique_ptr<compressed_vec_t> m_position;
        uint8_t m_normal_index;
        quake2_md2_t* m__root;
        quake2_md2_t::frame_t* m__parent;

    public:
        compressed_vec_t* position() const { return m_position.get(); }
        uint8_t normal_index() const { return m_normal_index; }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t::frame_t* _parent() const { return m__parent; }
    };

    class compressed_vec_t : public kaitai::kstruct {

    public:

        compressed_vec_t(kaitai::kstream* p__io, quake2_md2_t::vertex_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~compressed_vec_t();

    private:
        bool f_x;
        double m_x;

    public:
        double x();

    private:
        bool f_y;
        double m_y;

    public:
        double y();

    private:
        bool f_z;
        double m_z;

    public:
        double z();

    private:
        uint8_t m_x_compressed;
        uint8_t m_y_compressed;
        uint8_t m_z_compressed;
        quake2_md2_t* m__root;
        quake2_md2_t::vertex_t* m__parent;

    public:
        uint8_t x_compressed() const { return m_x_compressed; }
        uint8_t y_compressed() const { return m_y_compressed; }
        uint8_t z_compressed() const { return m_z_compressed; }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t::vertex_t* _parent() const { return m__parent; }
    };

    class triangle_t : public kaitai::kstruct {

    public:

        triangle_t(kaitai::kstream* p__io, quake2_md2_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~triangle_t();

    private:
        std::unique_ptr<std::vector<uint16_t>> m_vertex_indices;
        std::unique_ptr<std::vector<uint16_t>> m_tex_point_indices;
        quake2_md2_t* m__root;
        quake2_md2_t* m__parent;

    public:

        /**
         * indices to `_root.frames[i].vertices` (for each frame with index `i`)
         */
        std::vector<uint16_t>* vertex_indices() const { return m_vertex_indices.get(); }

        /**
         * indices to `_root.tex_coords`
         */
        std::vector<uint16_t>* tex_point_indices() const { return m_tex_point_indices.get(); }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t* _parent() const { return m__parent; }
    };

    class frame_t : public kaitai::kstruct {

    public:

        frame_t(kaitai::kstream* p__io, quake2_md2_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~frame_t();

    private:
        std::unique_ptr<vec3f_t> m_scale;
        std::unique_ptr<vec3f_t> m_translate;
        std::string m_name;
        std::unique_ptr<std::vector<std::unique_ptr<vertex_t>>> m_vertices;
        quake2_md2_t* m__root;
        quake2_md2_t* m__parent;

    public:
        vec3f_t* scale() const { return m_scale.get(); }
        vec3f_t* translate() const { return m_translate.get(); }
        std::string name() const { return m_name; }
        std::vector<std::unique_ptr<vertex_t>>* vertices() const { return m_vertices.get(); }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t* _parent() const { return m__parent; }
    };

    class gl_cmds_list_t : public kaitai::kstruct {

    public:

        gl_cmds_list_t(kaitai::kstream* p__io, quake2_md2_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~gl_cmds_list_t();

    private:
        std::unique_ptr<std::vector<std::unique_ptr<gl_cmd_t>>> m_items;
        bool n_items;

    public:
        bool _is_null_items() { items(); return n_items; };

    private:
        quake2_md2_t* m__root;
        quake2_md2_t* m__parent;

    public:
        std::vector<std::unique_ptr<gl_cmd_t>>* items() const { return m_items.get(); }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t* _parent() const { return m__parent; }
    };

    class tex_point_t : public kaitai::kstruct {

    public:

        tex_point_t(kaitai::kstream* p__io, quake2_md2_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~tex_point_t();

    private:
        bool f_s_normalized;
        double m_s_normalized;

    public:
        double s_normalized();

    private:
        bool f_t_normalized;
        double m_t_normalized;

    public:
        double t_normalized();

    private:
        uint16_t m_s_px;
        uint16_t m_t_px;
        quake2_md2_t* m__root;
        quake2_md2_t* m__parent;

    public:
        uint16_t s_px() const { return m_s_px; }
        uint16_t t_px() const { return m_t_px; }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t* _parent() const { return m__parent; }
    };

    class vec3f_t : public kaitai::kstruct {

    public:

        vec3f_t(kaitai::kstream* p__io, quake2_md2_t::frame_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~vec3f_t();

    private:
        float m_x;
        float m_y;
        float m_z;
        quake2_md2_t* m__root;
        quake2_md2_t::frame_t* m__parent;

    public:
        float x() const { return m_x; }
        float y() const { return m_y; }
        float z() const { return m_z; }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t::frame_t* _parent() const { return m__parent; }
    };

    class gl_vertex_t : public kaitai::kstruct {

    public:

        gl_vertex_t(kaitai::kstream* p__io, quake2_md2_t::gl_cmd_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~gl_vertex_t();

    private:
        std::unique_ptr<std::vector<float>> m_tex_coords_normalized;
        uint32_t m_vertex_index;
        quake2_md2_t* m__root;
        quake2_md2_t::gl_cmd_t* m__parent;

    public:
        std::vector<float>* tex_coords_normalized() const { return m_tex_coords_normalized.get(); }

        /**
         * index to `_root.frames[i].vertices` (for each frame with index `i`)
         */
        uint32_t vertex_index() const { return m_vertex_index; }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t::gl_cmd_t* _parent() const { return m__parent; }
    };

    class gl_cmd_t : public kaitai::kstruct {

    public:

        gl_cmd_t(kaitai::kstream* p__io, quake2_md2_t::gl_cmds_list_t* p__parent = nullptr, quake2_md2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~gl_cmd_t();

    private:
        bool f_num_vertices;
        int32_t m_num_vertices;

    public:
        int32_t num_vertices();

    private:
        bool f_primitive;
        gl_primitive_t m_primitive;

    public:
        gl_primitive_t primitive();

    private:
        int32_t m_cmd_num_vertices;
        std::unique_ptr<std::vector<std::unique_ptr<gl_vertex_t>>> m_vertices;
        quake2_md2_t* m__root;
        quake2_md2_t::gl_cmds_list_t* m__parent;

    public:
        int32_t cmd_num_vertices() const { return m_cmd_num_vertices; }
        std::vector<std::unique_ptr<gl_vertex_t>>* vertices() const { return m_vertices.get(); }
        quake2_md2_t* _root() const { return m__root; }
        quake2_md2_t::gl_cmds_list_t* _parent() const { return m__parent; }
    };

private:
    bool f_anim_num_frames;
    std::string m_anim_num_frames;

public:
    std::string anim_num_frames();

private:
    bool f_anorms_table;
    std::unique_ptr<std::vector<std::unique_ptr<std::vector<double>>>> m_anorms_table;

public:

    /**
     * \sa https://github.com/skullernet/q2pro/blob/f4faabd/src/common/math.c#L80
     * from Quake anorms.h
     */
    std::vector<std::unique_ptr<std::vector<double>>>* anorms_table();

private:
    bool f_tex_coords;
    std::unique_ptr<std::vector<std::unique_ptr<tex_point_t>>> m_tex_coords;

public:
    std::vector<std::unique_ptr<tex_point_t>>* tex_coords();

private:
    bool f_triangles;
    std::unique_ptr<std::vector<std::unique_ptr<triangle_t>>> m_triangles;

public:
    std::vector<std::unique_ptr<triangle_t>>* triangles();

private:
    bool f_frames;
    std::unique_ptr<std::vector<std::unique_ptr<frame_t>>> m_frames;

public:
    std::vector<std::unique_ptr<frame_t>>* frames();

private:
    bool f_anim_names;
    std::unique_ptr<std::vector<std::string>> m_anim_names;

public:
    std::vector<std::string>* anim_names();

private:
    bool f_gl_cmds;
    std::unique_ptr<gl_cmds_list_t> m_gl_cmds;

public:
    gl_cmds_list_t* gl_cmds();

private:
    bool f_skins;
    std::unique_ptr<std::vector<std::string>> m_skins;

public:
    std::vector<std::string>* skins();

private:
    bool f_anim_start_indices;
    std::string m_anim_start_indices;

public:
    std::string anim_start_indices();

private:
    std::string m_magic;
    uint32_t m_version;
    uint32_t m_skin_width_px;
    uint32_t m_skin_height_px;
    uint32_t m_bytes_per_frame;
    uint32_t m_num_skins;
    uint32_t m_vertices_per_frame;
    uint32_t m_num_tex_coords;
    uint32_t m_num_triangles;
    uint32_t m_num_gl_cmds;
    uint32_t m_num_frames;
    uint32_t m_ofs_skins;
    uint32_t m_ofs_tex_coords;
    uint32_t m_ofs_triangles;
    uint32_t m_ofs_frames;
    uint32_t m_ofs_gl_cmds;
    uint32_t m_ofs_eof;
    quake2_md2_t* m__root;
    kaitai::kstruct* m__parent;
    std::unique_ptr<std::vector<std::string>> m__raw_frames;
    std::unique_ptr<std::vector<std::unique_ptr<kaitai::kstream>>> m__io__raw_frames;
    std::string m__raw_gl_cmds;
    std::unique_ptr<kaitai::kstream> m__io__raw_gl_cmds;

public:
    std::string magic() const { return m_magic; }
    uint32_t version() const { return m_version; }
    uint32_t skin_width_px() const { return m_skin_width_px; }
    uint32_t skin_height_px() const { return m_skin_height_px; }
    uint32_t bytes_per_frame() const { return m_bytes_per_frame; }
    uint32_t num_skins() const { return m_num_skins; }
    uint32_t vertices_per_frame() const { return m_vertices_per_frame; }
    uint32_t num_tex_coords() const { return m_num_tex_coords; }
    uint32_t num_triangles() const { return m_num_triangles; }
    uint32_t num_gl_cmds() const { return m_num_gl_cmds; }
    uint32_t num_frames() const { return m_num_frames; }
    uint32_t ofs_skins() const { return m_ofs_skins; }
    uint32_t ofs_tex_coords() const { return m_ofs_tex_coords; }
    uint32_t ofs_triangles() const { return m_ofs_triangles; }
    uint32_t ofs_frames() const { return m_ofs_frames; }
    uint32_t ofs_gl_cmds() const { return m_ofs_gl_cmds; }
    uint32_t ofs_eof() const { return m_ofs_eof; }
    quake2_md2_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    std::vector<std::string>* _raw_frames() const { return m__raw_frames.get(); }
    std::vector<std::unique_ptr<kaitai::kstream>>* _io__raw_frames() const { return m__io__raw_frames.get(); }
    std::string _raw_gl_cmds() const { return m__raw_gl_cmds; }
    kaitai::kstream* _io__raw_gl_cmds() const { return m__io__raw_gl_cmds.get(); }
};

quake2_md2.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "quake2_md2.h"
#include "kaitai/exceptions.h"

quake2_md2_t::quake2_md2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_tex_coords = nullptr;
    m_triangles = nullptr;
    m_frames = nullptr;
    m__raw_frames = nullptr;
    m__io__raw_frames = nullptr;
    m_gl_cmds = nullptr;
    m__io__raw_gl_cmds = nullptr;
    m_skins = nullptr;
    f_anim_num_frames = false;
    f_anorms_table = false;
    f_tex_coords = false;
    f_triangles = false;
    f_frames = false;
    f_anim_names = false;
    f_gl_cmds = false;
    f_skins = false;
    f_anim_start_indices = false;
    _read();
}

void quake2_md2_t::_read() {
    m_magic = m__io->read_bytes(4);
    if (!(magic() == std::string("\x49\x44\x50\x32", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x49\x44\x50\x32", 4), magic(), _io(), std::string("/seq/0"));
    }
    m_version = m__io->read_u4le();
    if (!(version() == 8)) {
        throw kaitai::validation_not_equal_error<uint32_t>(8, version(), _io(), std::string("/seq/1"));
    }
    m_skin_width_px = m__io->read_u4le();
    m_skin_height_px = m__io->read_u4le();
    m_bytes_per_frame = m__io->read_u4le();
    m_num_skins = m__io->read_u4le();
    m_vertices_per_frame = m__io->read_u4le();
    m_num_tex_coords = m__io->read_u4le();
    m_num_triangles = m__io->read_u4le();
    m_num_gl_cmds = m__io->read_u4le();
    m_num_frames = m__io->read_u4le();
    m_ofs_skins = m__io->read_u4le();
    m_ofs_tex_coords = m__io->read_u4le();
    m_ofs_triangles = m__io->read_u4le();
    m_ofs_frames = m__io->read_u4le();
    m_ofs_gl_cmds = m__io->read_u4le();
    m_ofs_eof = m__io->read_u4le();
}

quake2_md2_t::~quake2_md2_t() {
    _clean_up();
}

void quake2_md2_t::_clean_up() {
    if (f_tex_coords) {
    }
    if (f_triangles) {
    }
    if (f_frames) {
    }
    if (f_gl_cmds) {
    }
    if (f_skins) {
    }
}

quake2_md2_t::vertex_t::vertex_t(kaitai::kstream* p__io, quake2_md2_t::frame_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_position = nullptr;
    f_normal = false;
    _read();
}

void quake2_md2_t::vertex_t::_read() {
    m_position = std::unique_ptr<compressed_vec_t>(new compressed_vec_t(m__io, this, m__root));
    m_normal_index = m__io->read_u1();
}

quake2_md2_t::vertex_t::~vertex_t() {
    _clean_up();
}

void quake2_md2_t::vertex_t::_clean_up() {
}

std::vector<double>* quake2_md2_t::vertex_t::normal() {
    if (f_normal)
        return m_normal;
    m_normal = _root()->anorms_table()->at(normal_index());
    f_normal = true;
    return m_normal;
}

quake2_md2_t::compressed_vec_t::compressed_vec_t(kaitai::kstream* p__io, quake2_md2_t::vertex_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_x = false;
    f_y = false;
    f_z = false;
    _read();
}

void quake2_md2_t::compressed_vec_t::_read() {
    m_x_compressed = m__io->read_u1();
    m_y_compressed = m__io->read_u1();
    m_z_compressed = m__io->read_u1();
}

quake2_md2_t::compressed_vec_t::~compressed_vec_t() {
    _clean_up();
}

void quake2_md2_t::compressed_vec_t::_clean_up() {
}

double quake2_md2_t::compressed_vec_t::x() {
    if (f_x)
        return m_x;
    m_x = ((x_compressed() * _parent()->_parent()->scale()->x()) + _parent()->_parent()->translate()->x());
    f_x = true;
    return m_x;
}

double quake2_md2_t::compressed_vec_t::y() {
    if (f_y)
        return m_y;
    m_y = ((y_compressed() * _parent()->_parent()->scale()->y()) + _parent()->_parent()->translate()->y());
    f_y = true;
    return m_y;
}

double quake2_md2_t::compressed_vec_t::z() {
    if (f_z)
        return m_z;
    m_z = ((z_compressed() * _parent()->_parent()->scale()->z()) + _parent()->_parent()->translate()->z());
    f_z = true;
    return m_z;
}

quake2_md2_t::triangle_t::triangle_t(kaitai::kstream* p__io, quake2_md2_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_vertex_indices = nullptr;
    m_tex_point_indices = nullptr;
    _read();
}

void quake2_md2_t::triangle_t::_read() {
    m_vertex_indices = std::unique_ptr<std::vector<uint16_t>>(new std::vector<uint16_t>());
    const int l_vertex_indices = 3;
    for (int i = 0; i < l_vertex_indices; i++) {
        m_vertex_indices->push_back(std::move(m__io->read_u2le()));
    }
    m_tex_point_indices = std::unique_ptr<std::vector<uint16_t>>(new std::vector<uint16_t>());
    const int l_tex_point_indices = 3;
    for (int i = 0; i < l_tex_point_indices; i++) {
        m_tex_point_indices->push_back(std::move(m__io->read_u2le()));
    }
}

quake2_md2_t::triangle_t::~triangle_t() {
    _clean_up();
}

void quake2_md2_t::triangle_t::_clean_up() {
}

quake2_md2_t::frame_t::frame_t(kaitai::kstream* p__io, quake2_md2_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_scale = nullptr;
    m_translate = nullptr;
    m_vertices = nullptr;
    _read();
}

void quake2_md2_t::frame_t::_read() {
    m_scale = std::unique_ptr<vec3f_t>(new vec3f_t(m__io, this, m__root));
    m_translate = std::unique_ptr<vec3f_t>(new vec3f_t(m__io, this, m__root));
    m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(16), 0, false), std::string("ascii"));
    m_vertices = std::unique_ptr<std::vector<std::unique_ptr<vertex_t>>>(new std::vector<std::unique_ptr<vertex_t>>());
    const int l_vertices = _root()->vertices_per_frame();
    for (int i = 0; i < l_vertices; i++) {
        m_vertices->push_back(std::move(std::unique_ptr<vertex_t>(new vertex_t(m__io, this, m__root))));
    }
}

quake2_md2_t::frame_t::~frame_t() {
    _clean_up();
}

void quake2_md2_t::frame_t::_clean_up() {
}

quake2_md2_t::gl_cmds_list_t::gl_cmds_list_t(kaitai::kstream* p__io, quake2_md2_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_items = nullptr;
    _read();
}

void quake2_md2_t::gl_cmds_list_t::_read() {
    n_items = true;
    if (!(_io()->is_eof())) {
        n_items = false;
        m_items = std::unique_ptr<std::vector<std::unique_ptr<gl_cmd_t>>>(new std::vector<std::unique_ptr<gl_cmd_t>>());
        {
            int i = 0;
            gl_cmd_t* _;
            do {
                _ = new gl_cmd_t(m__io, this, m__root);
                m_items->push_back(std::move(std::unique_ptr<gl_cmd_t>(_)));
                i++;
            } while (!(_->cmd_num_vertices() == 0));
        }
    }
}

quake2_md2_t::gl_cmds_list_t::~gl_cmds_list_t() {
    _clean_up();
}

void quake2_md2_t::gl_cmds_list_t::_clean_up() {
    if (!n_items) {
    }
}

quake2_md2_t::tex_point_t::tex_point_t(kaitai::kstream* p__io, quake2_md2_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_s_normalized = false;
    f_t_normalized = false;
    _read();
}

void quake2_md2_t::tex_point_t::_read() {
    m_s_px = m__io->read_u2le();
    m_t_px = m__io->read_u2le();
}

quake2_md2_t::tex_point_t::~tex_point_t() {
    _clean_up();
}

void quake2_md2_t::tex_point_t::_clean_up() {
}

double quake2_md2_t::tex_point_t::s_normalized() {
    if (f_s_normalized)
        return m_s_normalized;
    m_s_normalized = ((s_px() + 0.0) / _root()->skin_width_px());
    f_s_normalized = true;
    return m_s_normalized;
}

double quake2_md2_t::tex_point_t::t_normalized() {
    if (f_t_normalized)
        return m_t_normalized;
    m_t_normalized = ((t_px() + 0.0) / _root()->skin_height_px());
    f_t_normalized = true;
    return m_t_normalized;
}

quake2_md2_t::vec3f_t::vec3f_t(kaitai::kstream* p__io, quake2_md2_t::frame_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void quake2_md2_t::vec3f_t::_read() {
    m_x = m__io->read_f4le();
    m_y = m__io->read_f4le();
    m_z = m__io->read_f4le();
}

quake2_md2_t::vec3f_t::~vec3f_t() {
    _clean_up();
}

void quake2_md2_t::vec3f_t::_clean_up() {
}

quake2_md2_t::gl_vertex_t::gl_vertex_t(kaitai::kstream* p__io, quake2_md2_t::gl_cmd_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_tex_coords_normalized = nullptr;
    _read();
}

void quake2_md2_t::gl_vertex_t::_read() {
    m_tex_coords_normalized = std::unique_ptr<std::vector<float>>(new std::vector<float>());
    const int l_tex_coords_normalized = 2;
    for (int i = 0; i < l_tex_coords_normalized; i++) {
        m_tex_coords_normalized->push_back(std::move(m__io->read_f4le()));
    }
    m_vertex_index = m__io->read_u4le();
}

quake2_md2_t::gl_vertex_t::~gl_vertex_t() {
    _clean_up();
}

void quake2_md2_t::gl_vertex_t::_clean_up() {
}

quake2_md2_t::gl_cmd_t::gl_cmd_t(kaitai::kstream* p__io, quake2_md2_t::gl_cmds_list_t* p__parent, quake2_md2_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_vertices = nullptr;
    f_num_vertices = false;
    f_primitive = false;
    _read();
}

void quake2_md2_t::gl_cmd_t::_read() {
    m_cmd_num_vertices = m__io->read_s4le();
    m_vertices = std::unique_ptr<std::vector<std::unique_ptr<gl_vertex_t>>>(new std::vector<std::unique_ptr<gl_vertex_t>>());
    const int l_vertices = num_vertices();
    for (int i = 0; i < l_vertices; i++) {
        m_vertices->push_back(std::move(std::unique_ptr<gl_vertex_t>(new gl_vertex_t(m__io, this, m__root))));
    }
}

quake2_md2_t::gl_cmd_t::~gl_cmd_t() {
    _clean_up();
}

void quake2_md2_t::gl_cmd_t::_clean_up() {
}

int32_t quake2_md2_t::gl_cmd_t::num_vertices() {
    if (f_num_vertices)
        return m_num_vertices;
    m_num_vertices = ((cmd_num_vertices() < 0) ? (-(cmd_num_vertices())) : (cmd_num_vertices()));
    f_num_vertices = true;
    return m_num_vertices;
}

quake2_md2_t::gl_primitive_t quake2_md2_t::gl_cmd_t::primitive() {
    if (f_primitive)
        return m_primitive;
    m_primitive = ((cmd_num_vertices() < 0) ? (quake2_md2_t::GL_PRIMITIVE_TRIANGLE_FAN) : (quake2_md2_t::GL_PRIMITIVE_TRIANGLE_STRIP));
    f_primitive = true;
    return m_primitive;
}

std::string quake2_md2_t::anim_num_frames() {
    if (f_anim_num_frames)
        return m_anim_num_frames;
    m_anim_num_frames = std::string("\x28\x06\x08\x04\x04\x04\x06\x0C\x0B\x11\x0B\x0C\x13\x06\x09\x04\x05\x06\x06\x08", 20);
    f_anim_num_frames = true;
    return m_anim_num_frames;
}

std::vector<std::unique_ptr<std::vector<double>>>* quake2_md2_t::anorms_table() {
    if (f_anorms_table)
        return m_anorms_table.get();
    m_anorms_table = std::unique_ptr<std::vector<std::unique_ptr<std::vector<double>>>>(new std::vector<std::unique_ptr<std::vector<double>>>{std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.525731, 0.000000, 0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.442863, 0.238856, 0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.295242, 0.000000, 0.955423}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.309017, 0.500000, 0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.162460, 0.262866, 0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.000000, 1.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.850651, 0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.147621, 0.716567, 0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.147621, 0.716567, 0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.525731, 0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.309017, 0.500000, 0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.525731, 0.000000, 0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.295242, 0.000000, 0.955423}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.442863, 0.238856, 0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.162460, 0.262866, 0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.681718, 0.147621, 0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.809017, 0.309017, 0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.587785, 0.425325, 0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.850651, 0.525731, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.864188, 0.442863, 0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.716567, 0.681718, 0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.688191, 0.587785, 0.425325}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.500000, 0.809017, 0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.238856, 0.864188, 0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.425325, 0.688191, 0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.716567, 0.681718, -0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.500000, 0.809017, -0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.525731, 0.850651, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.850651, -0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.238856, 0.864188, -0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.955423, -0.295242}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.262866, 0.951056, -0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 1.000000, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.955423, 0.295242}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.262866, 0.951056, 0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.238856, 0.864188, 0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.262866, 0.951056, 0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.500000, 0.809017, 0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.238856, 0.864188, -0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.262866, 0.951056, -0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.500000, 0.809017, -0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.850651, 0.525731, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.716567, 0.681718, 0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.716567, 0.681718, -0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.525731, 0.850651, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.425325, 0.688191, 0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.864188, 0.442863, 0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.688191, 0.587785, 0.425325}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.809017, 0.309017, 0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.681718, 0.147621, 0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.587785, 0.425325, 0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.955423, 0.295242, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{1.000000, 0.000000, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.951056, 0.162460, 0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.850651, -0.525731, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.955423, -0.295242, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.864188, -0.442863, 0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.951056, -0.162460, 0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.809017, -0.309017, 0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.681718, -0.147621, 0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.850651, 0.000000, 0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.864188, 0.442863, -0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.809017, 0.309017, -0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.951056, 0.162460, -0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.525731, 0.000000, -0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.681718, 0.147621, -0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.681718, -0.147621, -0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.850651, 0.000000, -0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.809017, -0.309017, -0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.864188, -0.442863, -0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.951056, -0.162460, -0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.147621, 0.716567, -0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.309017, 0.500000, -0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.425325, 0.688191, -0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.442863, 0.238856, -0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.587785, 0.425325, -0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.688191, 0.587785, -0.425325}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.147621, 0.716567, -0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.309017, 0.500000, -0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.525731, -0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.525731, 0.000000, -0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.442863, 0.238856, -0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.295242, 0.000000, -0.955423}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.162460, 0.262866, -0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, 0.000000, -1.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.295242, 0.000000, -0.955423}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.162460, 0.262866, -0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.442863, -0.238856, -0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.309017, -0.500000, -0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.162460, -0.262866, -0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, -0.850651, -0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.147621, -0.716567, -0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.147621, -0.716567, -0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, -0.525731, -0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.309017, -0.500000, -0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.442863, -0.238856, -0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.162460, -0.262866, -0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.238856, -0.864188, -0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.500000, -0.809017, -0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.425325, -0.688191, -0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.716567, -0.681718, -0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.688191, -0.587785, -0.425325}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.587785, -0.425325, -0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, -0.955423, -0.295242}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, -1.000000, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.262866, -0.951056, -0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, -0.850651, 0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, -0.955423, 0.295242}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.238856, -0.864188, 0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.262866, -0.951056, 0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.500000, -0.809017, 0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.716567, -0.681718, 0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.525731, -0.850651, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.238856, -0.864188, -0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.500000, -0.809017, -0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.262866, -0.951056, -0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.850651, -0.525731, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.716567, -0.681718, -0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.716567, -0.681718, 0.147621}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.525731, -0.850651, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.500000, -0.809017, 0.309017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.238856, -0.864188, 0.442863}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.262866, -0.951056, 0.162460}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.864188, -0.442863, 0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.809017, -0.309017, 0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.688191, -0.587785, 0.425325}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.681718, -0.147621, 0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.442863, -0.238856, 0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.587785, -0.425325, 0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.309017, -0.500000, 0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.147621, -0.716567, 0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.425325, -0.688191, 0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.162460, -0.262866, 0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.442863, -0.238856, 0.864188}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.162460, -0.262866, 0.951056}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.309017, -0.500000, 0.809017}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.147621, -0.716567, 0.681718}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.000000, -0.525731, 0.850651}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.425325, -0.688191, 0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.587785, -0.425325, 0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{0.688191, -0.587785, 0.425325}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.955423, 0.295242, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.951056, 0.162460, 0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-1.000000, 0.000000, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.850651, 0.000000, 0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.955423, -0.295242, 0.000000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.951056, -0.162460, 0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.864188, 0.442863, -0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.951056, 0.162460, -0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.809017, 0.309017, -0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.864188, -0.442863, -0.238856}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.951056, -0.162460, -0.262866}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.809017, -0.309017, -0.500000}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.681718, 0.147621, -0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.681718, -0.147621, -0.716567}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.850651, 0.000000, -0.525731}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.688191, 0.587785, -0.425325}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.587785, 0.425325, -0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.425325, 0.688191, -0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.425325, -0.688191, -0.587785}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.587785, -0.425325, -0.688191}), std::unique_ptr<std::vector<double>>(new std::vector<double>{-0.688191, -0.587785, -0.425325})});
    f_anorms_table = true;
    return m_anorms_table.get();
}

std::vector<std::unique_ptr<quake2_md2_t::tex_point_t>>* quake2_md2_t::tex_coords() {
    if (f_tex_coords)
        return m_tex_coords.get();
    std::streampos _pos = m__io->pos();
    m__io->seek(ofs_tex_coords());
    m_tex_coords = std::unique_ptr<std::vector<std::unique_ptr<tex_point_t>>>(new std::vector<std::unique_ptr<tex_point_t>>());
    const int l_tex_coords = num_tex_coords();
    for (int i = 0; i < l_tex_coords; i++) {
        m_tex_coords->push_back(std::move(std::unique_ptr<tex_point_t>(new tex_point_t(m__io, this, m__root))));
    }
    m__io->seek(_pos);
    f_tex_coords = true;
    return m_tex_coords.get();
}

std::vector<std::unique_ptr<quake2_md2_t::triangle_t>>* quake2_md2_t::triangles() {
    if (f_triangles)
        return m_triangles.get();
    std::streampos _pos = m__io->pos();
    m__io->seek(ofs_triangles());
    m_triangles = std::unique_ptr<std::vector<std::unique_ptr<triangle_t>>>(new std::vector<std::unique_ptr<triangle_t>>());
    const int l_triangles = num_triangles();
    for (int i = 0; i < l_triangles; i++) {
        m_triangles->push_back(std::move(std::unique_ptr<triangle_t>(new triangle_t(m__io, this, m__root))));
    }
    m__io->seek(_pos);
    f_triangles = true;
    return m_triangles.get();
}

std::vector<std::unique_ptr<quake2_md2_t::frame_t>>* quake2_md2_t::frames() {
    if (f_frames)
        return m_frames.get();
    std::streampos _pos = m__io->pos();
    m__io->seek(ofs_frames());
    m__raw_frames = std::unique_ptr<std::vector<std::string>>(new std::vector<std::string>());
    m__io__raw_frames = std::unique_ptr<std::vector<std::unique_ptr<kaitai::kstream>>>(new std::vector<std::unique_ptr<kaitai::kstream>>());
    m_frames = std::unique_ptr<std::vector<std::unique_ptr<frame_t>>>(new std::vector<std::unique_ptr<frame_t>>());
    const int l_frames = num_frames();
    for (int i = 0; i < l_frames; i++) {
        m__raw_frames->push_back(std::move(m__io->read_bytes(bytes_per_frame())));
        kaitai::kstream* io__raw_frames = new kaitai::kstream(m__raw_frames->at(m__raw_frames->size() - 1));
        m__io__raw_frames->emplace_back(io__raw_frames);
        m_frames->push_back(std::move(std::unique_ptr<frame_t>(new frame_t(io__raw_frames, this, m__root))));
    }
    m__io->seek(_pos);
    f_frames = true;
    return m_frames.get();
}

std::vector<std::string>* quake2_md2_t::anim_names() {
    if (f_anim_names)
        return m_anim_names.get();
    m_anim_names = std::unique_ptr<std::vector<std::string>>(new std::vector<std::string>{std::string("stand"), std::string("run"), std::string("attack"), std::string("pain1"), std::string("pain2"), std::string("pain3"), std::string("jump"), std::string("flip"), std::string("salute"), std::string("taunt"), std::string("wave"), std::string("point"), std::string("crstnd"), std::string("crwalk"), std::string("crattak"), std::string("crpain"), std::string("crdeath"), std::string("death1"), std::string("death2"), std::string("death3")});
    f_anim_names = true;
    return m_anim_names.get();
}

quake2_md2_t::gl_cmds_list_t* quake2_md2_t::gl_cmds() {
    if (f_gl_cmds)
        return m_gl_cmds.get();
    std::streampos _pos = m__io->pos();
    m__io->seek(ofs_gl_cmds());
    m__raw_gl_cmds = m__io->read_bytes((4 * num_gl_cmds()));
    m__io__raw_gl_cmds = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_gl_cmds));
    m_gl_cmds = std::unique_ptr<gl_cmds_list_t>(new gl_cmds_list_t(m__io__raw_gl_cmds.get(), this, m__root));
    m__io->seek(_pos);
    f_gl_cmds = true;
    return m_gl_cmds.get();
}

std::vector<std::string>* quake2_md2_t::skins() {
    if (f_skins)
        return m_skins.get();
    std::streampos _pos = m__io->pos();
    m__io->seek(ofs_skins());
    m_skins = std::unique_ptr<std::vector<std::string>>(new std::vector<std::string>());
    const int l_skins = num_skins();
    for (int i = 0; i < l_skins; i++) {
        m_skins->push_back(std::move(kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(64), 0, false), std::string("ascii"))));
    }
    m__io->seek(_pos);
    f_skins = true;
    return m_skins.get();
}

std::string quake2_md2_t::anim_start_indices() {
    if (f_anim_start_indices)
        return m_anim_start_indices;
    m_anim_start_indices = std::string("\x00\x28\x2E\x36\x3A\x3E\x42\x48\x54\x5F\x70\x7B\x87\x9A\xA0\xA9\xAD\xB2\xB8\xBE", 20);
    f_anim_start_indices = true;
    return m_anim_start_indices;
}