Quake 1 (idtech2) model format (MDL version 6): C++11/STL parsing library

Application

Quake 1 (idtech2)

File extension

mdl

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.7

This page hosts a formal specification of Quake 1 (idtech2) model format (MDL version 6) 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.mdl", 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:
    quake_mdl_t data(&ks);
    

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

data.header() // => get header

C++11/STL source code to parse Quake 1 (idtech2) model format (MDL version 6)

quake_mdl.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

class quake_mdl_t : public kaitai::kstruct {

public:
    class mdl_vertex_t;
    class mdl_texcoord_t;
    class mdl_header_t;
    class mdl_skin_t;
    class mdl_frame_t;
    class mdl_simple_frame_t;
    class mdl_triangle_t;
    class vec3_t;

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

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

public:
    ~quake_mdl_t();

    class mdl_vertex_t : public kaitai::kstruct {

    public:

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

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

    public:
        ~mdl_vertex_t();

    private:
        std::unique_ptr<std::vector<uint8_t>> m_values;
        uint8_t m_normal_index;
        quake_mdl_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        std::vector<uint8_t>* values() const { return m_values.get(); }
        uint8_t normal_index() const { return m_normal_index; }
        quake_mdl_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    class mdl_texcoord_t : public kaitai::kstruct {

    public:

        mdl_texcoord_t(kaitai::kstream* p__io, quake_mdl_t* p__parent = nullptr, quake_mdl_t* p__root = nullptr);

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

    public:
        ~mdl_texcoord_t();

    private:
        int32_t m_on_seam;
        int32_t m_s;
        int32_t m_t;
        quake_mdl_t* m__root;
        quake_mdl_t* m__parent;

    public:
        int32_t on_seam() const { return m_on_seam; }
        int32_t s() const { return m_s; }
        int32_t t() const { return m_t; }
        quake_mdl_t* _root() const { return m__root; }
        quake_mdl_t* _parent() const { return m__parent; }
    };

    class mdl_header_t : public kaitai::kstruct {

    public:

        mdl_header_t(kaitai::kstream* p__io, quake_mdl_t* p__parent = nullptr, quake_mdl_t* p__root = nullptr);

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

    public:
        ~mdl_header_t();

    private:
        bool f_version;
        int8_t m_version;

    public:
        int8_t version();

    private:
        bool f_skin_size;
        int32_t m_skin_size;

    public:
        int32_t skin_size();

    private:
        std::string m_ident;
        std::string m_version_must_be_6;
        std::unique_ptr<vec3_t> m_scale;
        std::unique_ptr<vec3_t> m_origin;
        float m_radius;
        std::unique_ptr<vec3_t> m_eye_position;
        int32_t m_num_skins;
        int32_t m_skin_width;
        int32_t m_skin_height;
        int32_t m_num_verts;
        int32_t m_num_tris;
        int32_t m_num_frames;
        int32_t m_synctype;
        int32_t m_flags;
        float m_size;
        quake_mdl_t* m__root;
        quake_mdl_t* m__parent;

    public:
        std::string ident() const { return m_ident; }
        std::string version_must_be_6() const { return m_version_must_be_6; }
        vec3_t* scale() const { return m_scale.get(); }
        vec3_t* origin() const { return m_origin.get(); }
        float radius() const { return m_radius; }
        vec3_t* eye_position() const { return m_eye_position.get(); }
        int32_t num_skins() const { return m_num_skins; }
        int32_t skin_width() const { return m_skin_width; }
        int32_t skin_height() const { return m_skin_height; }
        int32_t num_verts() const { return m_num_verts; }
        int32_t num_tris() const { return m_num_tris; }
        int32_t num_frames() const { return m_num_frames; }
        int32_t synctype() const { return m_synctype; }
        int32_t flags() const { return m_flags; }
        float size() const { return m_size; }
        quake_mdl_t* _root() const { return m__root; }
        quake_mdl_t* _parent() const { return m__parent; }
    };

    class mdl_skin_t : public kaitai::kstruct {

    public:

        mdl_skin_t(kaitai::kstream* p__io, quake_mdl_t* p__parent = nullptr, quake_mdl_t* p__root = nullptr);

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

    public:
        ~mdl_skin_t();

    private:
        int32_t m_group;
        std::string m_single_texture_data;
        bool n_single_texture_data;

    public:
        bool _is_null_single_texture_data() { single_texture_data(); return n_single_texture_data; };

    private:
        uint32_t m_num_frames;
        bool n_num_frames;

    public:
        bool _is_null_num_frames() { num_frames(); return n_num_frames; };

    private:
        std::unique_ptr<std::vector<float>> m_frame_times;
        bool n_frame_times;

    public:
        bool _is_null_frame_times() { frame_times(); return n_frame_times; };

    private:
        std::unique_ptr<std::vector<std::string>> m_group_texture_data;
        bool n_group_texture_data;

    public:
        bool _is_null_group_texture_data() { group_texture_data(); return n_group_texture_data; };

    private:
        quake_mdl_t* m__root;
        quake_mdl_t* m__parent;

    public:
        int32_t group() const { return m_group; }
        std::string single_texture_data() const { return m_single_texture_data; }
        uint32_t num_frames() const { return m_num_frames; }
        std::vector<float>* frame_times() const { return m_frame_times.get(); }
        std::vector<std::string>* group_texture_data() const { return m_group_texture_data.get(); }
        quake_mdl_t* _root() const { return m__root; }
        quake_mdl_t* _parent() const { return m__parent; }
    };

    class mdl_frame_t : public kaitai::kstruct {

    public:

        mdl_frame_t(kaitai::kstream* p__io, quake_mdl_t* p__parent = nullptr, quake_mdl_t* p__root = nullptr);

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

    public:
        ~mdl_frame_t();

    private:
        bool f_num_simple_frames;
        int32_t m_num_simple_frames;

    public:
        int32_t num_simple_frames();

    private:
        int32_t m_type;
        std::unique_ptr<mdl_vertex_t> m_min;
        bool n_min;

    public:
        bool _is_null_min() { min(); return n_min; };

    private:
        std::unique_ptr<mdl_vertex_t> m_max;
        bool n_max;

    public:
        bool _is_null_max() { max(); return n_max; };

    private:
        std::unique_ptr<std::vector<float>> m_time;
        bool n_time;

    public:
        bool _is_null_time() { time(); return n_time; };

    private:
        std::unique_ptr<std::vector<std::unique_ptr<mdl_simple_frame_t>>> m_frames;
        quake_mdl_t* m__root;
        quake_mdl_t* m__parent;

    public:
        int32_t type() const { return m_type; }
        mdl_vertex_t* min() const { return m_min.get(); }
        mdl_vertex_t* max() const { return m_max.get(); }
        std::vector<float>* time() const { return m_time.get(); }
        std::vector<std::unique_ptr<mdl_simple_frame_t>>* frames() const { return m_frames.get(); }
        quake_mdl_t* _root() const { return m__root; }
        quake_mdl_t* _parent() const { return m__parent; }
    };

    class mdl_simple_frame_t : public kaitai::kstruct {

    public:

        mdl_simple_frame_t(kaitai::kstream* p__io, quake_mdl_t::mdl_frame_t* p__parent = nullptr, quake_mdl_t* p__root = nullptr);

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

    public:
        ~mdl_simple_frame_t();

    private:
        std::unique_ptr<mdl_vertex_t> m_bbox_min;
        std::unique_ptr<mdl_vertex_t> m_bbox_max;
        std::string m_name;
        std::unique_ptr<std::vector<std::unique_ptr<mdl_vertex_t>>> m_vertices;
        quake_mdl_t* m__root;
        quake_mdl_t::mdl_frame_t* m__parent;

    public:
        mdl_vertex_t* bbox_min() const { return m_bbox_min.get(); }
        mdl_vertex_t* bbox_max() const { return m_bbox_max.get(); }
        std::string name() const { return m_name; }
        std::vector<std::unique_ptr<mdl_vertex_t>>* vertices() const { return m_vertices.get(); }
        quake_mdl_t* _root() const { return m__root; }
        quake_mdl_t::mdl_frame_t* _parent() const { return m__parent; }
    };

    class mdl_triangle_t : public kaitai::kstruct {

    public:

        mdl_triangle_t(kaitai::kstream* p__io, quake_mdl_t* p__parent = nullptr, quake_mdl_t* p__root = nullptr);

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

    public:
        ~mdl_triangle_t();

    private:
        int32_t m_faces_front;
        std::unique_ptr<std::vector<int32_t>> m_vertices;
        quake_mdl_t* m__root;
        quake_mdl_t* m__parent;

    public:
        int32_t faces_front() const { return m_faces_front; }
        std::vector<int32_t>* vertices() const { return m_vertices.get(); }
        quake_mdl_t* _root() const { return m__root; }
        quake_mdl_t* _parent() const { return m__parent; }
    };

    class vec3_t : public kaitai::kstruct {

    public:

        vec3_t(kaitai::kstream* p__io, quake_mdl_t::mdl_header_t* p__parent = nullptr, quake_mdl_t* p__root = nullptr);

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

    public:
        ~vec3_t();

    private:
        float m_x;
        float m_y;
        float m_z;
        quake_mdl_t* m__root;
        quake_mdl_t::mdl_header_t* m__parent;

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

private:
    std::unique_ptr<mdl_header_t> m_header;
    std::unique_ptr<std::vector<std::unique_ptr<mdl_skin_t>>> m_skins;
    std::unique_ptr<std::vector<std::unique_ptr<mdl_texcoord_t>>> m_texture_coordinates;
    std::unique_ptr<std::vector<std::unique_ptr<mdl_triangle_t>>> m_triangles;
    std::unique_ptr<std::vector<std::unique_ptr<mdl_frame_t>>> m_frames;
    quake_mdl_t* m__root;
    kaitai::kstruct* m__parent;

public:
    mdl_header_t* header() const { return m_header.get(); }
    std::vector<std::unique_ptr<mdl_skin_t>>* skins() const { return m_skins.get(); }
    std::vector<std::unique_ptr<mdl_texcoord_t>>* texture_coordinates() const { return m_texture_coordinates.get(); }
    std::vector<std::unique_ptr<mdl_triangle_t>>* triangles() const { return m_triangles.get(); }
    std::vector<std::unique_ptr<mdl_frame_t>>* frames() const { return m_frames.get(); }
    quake_mdl_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

quake_mdl.cpp

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

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

quake_mdl_t::quake_mdl_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_header = nullptr;
    m_skins = nullptr;
    m_texture_coordinates = nullptr;
    m_triangles = nullptr;
    m_frames = nullptr;
    _read();
}

void quake_mdl_t::_read() {
    m_header = std::unique_ptr<mdl_header_t>(new mdl_header_t(m__io, this, m__root));
    int l_skins = header()->num_skins();
    m_skins = std::unique_ptr<std::vector<std::unique_ptr<mdl_skin_t>>>(new std::vector<std::unique_ptr<mdl_skin_t>>());
    m_skins->reserve(l_skins);
    for (int i = 0; i < l_skins; i++) {
        m_skins->push_back(std::move(std::unique_ptr<mdl_skin_t>(new mdl_skin_t(m__io, this, m__root))));
    }
    int l_texture_coordinates = header()->num_verts();
    m_texture_coordinates = std::unique_ptr<std::vector<std::unique_ptr<mdl_texcoord_t>>>(new std::vector<std::unique_ptr<mdl_texcoord_t>>());
    m_texture_coordinates->reserve(l_texture_coordinates);
    for (int i = 0; i < l_texture_coordinates; i++) {
        m_texture_coordinates->push_back(std::move(std::unique_ptr<mdl_texcoord_t>(new mdl_texcoord_t(m__io, this, m__root))));
    }
    int l_triangles = header()->num_tris();
    m_triangles = std::unique_ptr<std::vector<std::unique_ptr<mdl_triangle_t>>>(new std::vector<std::unique_ptr<mdl_triangle_t>>());
    m_triangles->reserve(l_triangles);
    for (int i = 0; i < l_triangles; i++) {
        m_triangles->push_back(std::move(std::unique_ptr<mdl_triangle_t>(new mdl_triangle_t(m__io, this, m__root))));
    }
    int l_frames = header()->num_frames();
    m_frames = std::unique_ptr<std::vector<std::unique_ptr<mdl_frame_t>>>(new std::vector<std::unique_ptr<mdl_frame_t>>());
    m_frames->reserve(l_frames);
    for (int i = 0; i < l_frames; i++) {
        m_frames->push_back(std::move(std::unique_ptr<mdl_frame_t>(new mdl_frame_t(m__io, this, m__root))));
    }
}

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

void quake_mdl_t::_clean_up() {
}

quake_mdl_t::mdl_vertex_t::mdl_vertex_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_values = nullptr;
    _read();
}

void quake_mdl_t::mdl_vertex_t::_read() {
    int l_values = 3;
    m_values = std::unique_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>());
    m_values->reserve(l_values);
    for (int i = 0; i < l_values; i++) {
        m_values->push_back(std::move(m__io->read_u1()));
    }
    m_normal_index = m__io->read_u1();
}

quake_mdl_t::mdl_vertex_t::~mdl_vertex_t() {
    _clean_up();
}

void quake_mdl_t::mdl_vertex_t::_clean_up() {
}

quake_mdl_t::mdl_texcoord_t::mdl_texcoord_t(kaitai::kstream* p__io, quake_mdl_t* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void quake_mdl_t::mdl_texcoord_t::_read() {
    m_on_seam = m__io->read_s4le();
    m_s = m__io->read_s4le();
    m_t = m__io->read_s4le();
}

quake_mdl_t::mdl_texcoord_t::~mdl_texcoord_t() {
    _clean_up();
}

void quake_mdl_t::mdl_texcoord_t::_clean_up() {
}

quake_mdl_t::mdl_header_t::mdl_header_t(kaitai::kstream* p__io, quake_mdl_t* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_scale = nullptr;
    m_origin = nullptr;
    m_eye_position = nullptr;
    f_version = false;
    f_skin_size = false;
    _read();
}

void quake_mdl_t::mdl_header_t::_read() {
    m_ident = m__io->read_bytes(4);
    if (!(ident() == std::string("\x49\x44\x50\x4F", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x49\x44\x50\x4F", 4), ident(), _io(), std::string("/types/mdl_header/seq/0"));
    }
    m_version_must_be_6 = m__io->read_bytes(4);
    if (!(version_must_be_6() == std::string("\x06\x00\x00\x00", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x06\x00\x00\x00", 4), version_must_be_6(), _io(), std::string("/types/mdl_header/seq/1"));
    }
    m_scale = std::unique_ptr<vec3_t>(new vec3_t(m__io, this, m__root));
    m_origin = std::unique_ptr<vec3_t>(new vec3_t(m__io, this, m__root));
    m_radius = m__io->read_f4le();
    m_eye_position = std::unique_ptr<vec3_t>(new vec3_t(m__io, this, m__root));
    m_num_skins = m__io->read_s4le();
    m_skin_width = m__io->read_s4le();
    m_skin_height = m__io->read_s4le();
    m_num_verts = m__io->read_s4le();
    m_num_tris = m__io->read_s4le();
    m_num_frames = m__io->read_s4le();
    m_synctype = m__io->read_s4le();
    m_flags = m__io->read_s4le();
    m_size = m__io->read_f4le();
}

quake_mdl_t::mdl_header_t::~mdl_header_t() {
    _clean_up();
}

void quake_mdl_t::mdl_header_t::_clean_up() {
}

int8_t quake_mdl_t::mdl_header_t::version() {
    if (f_version)
        return m_version;
    m_version = 6;
    f_version = true;
    return m_version;
}

int32_t quake_mdl_t::mdl_header_t::skin_size() {
    if (f_skin_size)
        return m_skin_size;
    m_skin_size = (skin_width() * skin_height());
    f_skin_size = true;
    return m_skin_size;
}

quake_mdl_t::mdl_skin_t::mdl_skin_t(kaitai::kstream* p__io, quake_mdl_t* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_frame_times = nullptr;
    m_group_texture_data = nullptr;
    _read();
}

void quake_mdl_t::mdl_skin_t::_read() {
    m_group = m__io->read_s4le();
    n_single_texture_data = true;
    if (group() == 0) {
        n_single_texture_data = false;
        m_single_texture_data = m__io->read_bytes(_root()->header()->skin_size());
    }
    n_num_frames = true;
    if (group() != 0) {
        n_num_frames = false;
        m_num_frames = m__io->read_u4le();
    }
    n_frame_times = true;
    if (group() != 0) {
        n_frame_times = false;
        int l_frame_times = num_frames();
        m_frame_times = std::unique_ptr<std::vector<float>>(new std::vector<float>());
        m_frame_times->reserve(l_frame_times);
        for (int i = 0; i < l_frame_times; i++) {
            m_frame_times->push_back(std::move(m__io->read_f4le()));
        }
    }
    n_group_texture_data = true;
    if (group() != 0) {
        n_group_texture_data = false;
        int l_group_texture_data = num_frames();
        m_group_texture_data = std::unique_ptr<std::vector<std::string>>(new std::vector<std::string>());
        m_group_texture_data->reserve(l_group_texture_data);
        for (int i = 0; i < l_group_texture_data; i++) {
            m_group_texture_data->push_back(std::move(m__io->read_bytes(_root()->header()->skin_size())));
        }
    }
}

quake_mdl_t::mdl_skin_t::~mdl_skin_t() {
    _clean_up();
}

void quake_mdl_t::mdl_skin_t::_clean_up() {
    if (!n_single_texture_data) {
    }
    if (!n_num_frames) {
    }
    if (!n_frame_times) {
    }
    if (!n_group_texture_data) {
    }
}

quake_mdl_t::mdl_frame_t::mdl_frame_t(kaitai::kstream* p__io, quake_mdl_t* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_min = nullptr;
    m_max = nullptr;
    m_time = nullptr;
    m_frames = nullptr;
    f_num_simple_frames = false;
    _read();
}

void quake_mdl_t::mdl_frame_t::_read() {
    m_type = m__io->read_s4le();
    n_min = true;
    if (type() != 0) {
        n_min = false;
        m_min = std::unique_ptr<mdl_vertex_t>(new mdl_vertex_t(m__io, this, m__root));
    }
    n_max = true;
    if (type() != 0) {
        n_max = false;
        m_max = std::unique_ptr<mdl_vertex_t>(new mdl_vertex_t(m__io, this, m__root));
    }
    n_time = true;
    if (type() != 0) {
        n_time = false;
        int l_time = type();
        m_time = std::unique_ptr<std::vector<float>>(new std::vector<float>());
        m_time->reserve(l_time);
        for (int i = 0; i < l_time; i++) {
            m_time->push_back(std::move(m__io->read_f4le()));
        }
    }
    int l_frames = num_simple_frames();
    m_frames = std::unique_ptr<std::vector<std::unique_ptr<mdl_simple_frame_t>>>(new std::vector<std::unique_ptr<mdl_simple_frame_t>>());
    m_frames->reserve(l_frames);
    for (int i = 0; i < l_frames; i++) {
        m_frames->push_back(std::move(std::unique_ptr<mdl_simple_frame_t>(new mdl_simple_frame_t(m__io, this, m__root))));
    }
}

quake_mdl_t::mdl_frame_t::~mdl_frame_t() {
    _clean_up();
}

void quake_mdl_t::mdl_frame_t::_clean_up() {
    if (!n_min) {
    }
    if (!n_max) {
    }
    if (!n_time) {
    }
}

int32_t quake_mdl_t::mdl_frame_t::num_simple_frames() {
    if (f_num_simple_frames)
        return m_num_simple_frames;
    m_num_simple_frames = ((type() == 0) ? (1) : (type()));
    f_num_simple_frames = true;
    return m_num_simple_frames;
}

quake_mdl_t::mdl_simple_frame_t::mdl_simple_frame_t(kaitai::kstream* p__io, quake_mdl_t::mdl_frame_t* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_bbox_min = nullptr;
    m_bbox_max = nullptr;
    m_vertices = nullptr;
    _read();
}

void quake_mdl_t::mdl_simple_frame_t::_read() {
    m_bbox_min = std::unique_ptr<mdl_vertex_t>(new mdl_vertex_t(m__io, this, m__root));
    m_bbox_max = std::unique_ptr<mdl_vertex_t>(new mdl_vertex_t(m__io, this, m__root));
    m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(kaitai::kstream::bytes_strip_right(m__io->read_bytes(16), 0), 0, false), std::string("ASCII"));
    int l_vertices = _root()->header()->num_verts();
    m_vertices = std::unique_ptr<std::vector<std::unique_ptr<mdl_vertex_t>>>(new std::vector<std::unique_ptr<mdl_vertex_t>>());
    m_vertices->reserve(l_vertices);
    for (int i = 0; i < l_vertices; i++) {
        m_vertices->push_back(std::move(std::unique_ptr<mdl_vertex_t>(new mdl_vertex_t(m__io, this, m__root))));
    }
}

quake_mdl_t::mdl_simple_frame_t::~mdl_simple_frame_t() {
    _clean_up();
}

void quake_mdl_t::mdl_simple_frame_t::_clean_up() {
}

quake_mdl_t::mdl_triangle_t::mdl_triangle_t(kaitai::kstream* p__io, quake_mdl_t* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_vertices = nullptr;
    _read();
}

void quake_mdl_t::mdl_triangle_t::_read() {
    m_faces_front = m__io->read_s4le();
    int l_vertices = 3;
    m_vertices = std::unique_ptr<std::vector<int32_t>>(new std::vector<int32_t>());
    m_vertices->reserve(l_vertices);
    for (int i = 0; i < l_vertices; i++) {
        m_vertices->push_back(std::move(m__io->read_s4le()));
    }
}

quake_mdl_t::mdl_triangle_t::~mdl_triangle_t() {
    _clean_up();
}

void quake_mdl_t::mdl_triangle_t::_clean_up() {
}

quake_mdl_t::vec3_t::vec3_t(kaitai::kstream* p__io, quake_mdl_t::mdl_header_t* p__parent, quake_mdl_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

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

quake_mdl_t::vec3_t::~vec3_t() {
    _clean_up();
}

void quake_mdl_t::vec3_t::_clean_up() {
}