Sign in to follow this  
Chozo

MD5 model loader

Recommended Posts

Chozo    176
I've been playing with MD5 models for the last couple days and finally have them somewhat working (minus animations). I'm having a problem with texturing though. I'm only using the _d.tga texture (working with the Doom3 imp model for testing) and it's coming out very odd. Things like the eyes and the teeth ending up on the model's arms. This is the code I've got for loading the model: MD5Model.h
/*
====================
File: MD5Model.h
Author: Shane Lillie
Description: MD5 mesh module header.
====================
*/

#if !defined __MD5MODEL_H__
#define __MD5MODEL_H__

#if _MSC_VER >= 1000
#pragma once
#endif


#include <string>

#include "EnVector.h"
#include "EnQuaternion.h"


/*
struct MD5Joint
*/
struct MD5Joint
{
    std::string name;
    MD5Joint* parent;
    EnVector3<GLdouble> position;
    EnQuaternion<GLdouble> orientation;

    MD5Joint(const std::string& n, MD5Joint* parent, const EnVector3<GLdouble>& pv, const EnVector3<GLdouble>& ov);
};


/*
struct MD5Weight
*/
struct MD5Weight
{
    MD5Joint* joint;
    GLdouble weight;
    EnVector3<GLdouble> position;

    MD5Weight(MD5Joint* j, GLdouble w, const EnVector3<GLdouble>& pv);
};


/*
struct MD5Vertex
*/
struct MD5Vertex
{
    GLdouble texture_u, texture_v;
    int weight_start, weight_count;
    EnVector3<GLdouble> position;

    MD5Vertex(GLdouble u, GLdouble v, int ws, int wc);
};


/*
struct MD5Triangle
*/
struct MD5Triangle
{
    MD5Vertex *v1, *v2, *v3;

    MD5Triangle(MD5Vertex* a, MD5Vertex* b, MD5Vertex* c);
};


/*
class MD5Mesh
*/
class MD5Mesh
{
public:
    MD5Mesh();
    ~MD5Mesh();

public:
    void render() const;
    void calculate_vertex_positions();

//private:
public:
    std::string m_shader;

    size_t m_vcount;
    MD5Vertex** m_vertices;

    size_t m_tcount;
    MD5Triangle** m_triangles;

    size_t m_wcount;
    MD5Weight** m_weights;
};


/*
class MD5Model
*/
class MD5Model
{
private:
    enum Token
    {
        // keywords
        VERSION,
        COMMANDLINE,
        NUM_JOINTS,
        NUM_MESHES,
        JOINTS,
        MESH,
        SHADER,
        NUM_VERTS,
        VERT,
        NUM_TRIS,
        TRI,
        NUM_WEIGHTS,
        WEIGHT,

        // delimiters
        OPEN_PAREN,
        CLOSE_PAREN,
        OPEN_BRACE,
        CLOSE_BRACE,

        // end of file
        END,

        // scan error
        ERROR
    };

public:
    MD5Model();
    ~MD5Model();

public:
    bool load(const std::string& filename);
    void unload();
    void render() const;

public:
    bool loaded() const
    {
        return !m_filename.empty();
    }

    int version() const
    {
        return m_version;
    }

    const std::string& commandline() const
    {
        return m_commandline;
    }

    const std::string& filename() const
    {
        return m_filename;
    }

private:
    char advance(char** ptr) const;
    Token lex(char** ptr) const;
    void skip_whitespace(char** ptr) const;
    void skip_single_comment(char** ptr) const;
    bool number_literal(char** ptr, double& value) const;
    Token keyword(char** ptr) const;
    bool string_literal(char** ptr, std::string& value) const;

    bool scan_version(char** ptr);
    bool scan_commandline(char** ptr);
    int scan_num_joints(char** ptr);
    int scan_num_meshes(char** ptr);
    bool scan_joints(char** ptr, int count);
    bool scan_meshes(char** ptr, int count);

private:
    int m_version;
    std::string m_commandline;

    size_t m_jcount;
    MD5Joint** m_joints;

    size_t m_mcount;
    MD5Mesh** m_meshes;

    std::string m_filename;

private:
    MD5Model(const MD5Model& md5model);
    MD5Model& operator=(const MD5Model& rhs);
};


#endif

MD5Model.cc
/*
====================
File: MD5Model.cc
Author: Shane Lillie
Description: MD5 mesh module source.
====================
*/

#include "EnUtilities.h"

#include <cassert>
#include <cstdlib>
#include <memory>
#include <fstream>
#include <iostream>

#include <GL/gl.h>

#include "EnAutoArray.h"

#include "MD5Model.h"


/*
Format description from:

http://www.doom3world.org/phpbb2/viewtopic.php?t=2884
http://www.trevorwilkin.com/gamesprogramming/files/direct3d/MD5%20Spec.txt
*/


/*
 *  MD5Joint methods
 *
 */


MD5Joint::MD5Joint(const std::string& n, MD5Joint* p, const EnVector3<GLdouble>& pv, const EnVector3<GLdouble>& ov)
    : name(n), parent(p), position(pv), orientation(ov)
{
}


/*
 *  MD5Weight methods
 *
 */


MD5Weight::MD5Weight(MD5Joint* j, GLdouble w, const EnVector3<GLdouble>& pv)
    : joint(j), weight(w), position(pv)
{
}


/*
 *  MD5Vertex methods
 *
 */


MD5Vertex::MD5Vertex(GLdouble u, GLdouble v, int ws, int wc)
    : texture_u(u), texture_v(v), weight_start(ws), weight_count(wc)
{
}


/*
 *  MD5Triangle methods
 *
 */


MD5Triangle::MD5Triangle(MD5Vertex* a, MD5Vertex* b, MD5Vertex* c)
    : v1(a), v2(b), v3(c)
{
}


/*
 *  MD5Model methods
 *
 */


MD5Mesh::MD5Mesh()
    : m_vcount(0), m_vertices(NULL),
        m_tcount(0), m_triangles(NULL),
        m_wcount(0), m_weights(NULL)
{
}


void MD5Mesh::render() const
{
    MD5Triangle* triangle;
    MD5Vertex* vertex;
    glBegin(GL_TRIANGLES);
        for(size_t i=0; i<m_tcount; ++i) {
            triangle = *(m_triangles + i);

            vertex = triangle->v1;
            glTexCoord2f(vertex->texture_u, vertex->texture_v);
            glVertex3f(vertex->position.x(), vertex->position.y(), vertex->position.z());

            vertex = triangle->v2;
            glTexCoord2f(vertex->texture_u, vertex->texture_v);
            glVertex3f(vertex->position.x(), vertex->position.y(), vertex->position.z());

            vertex = triangle->v3;
            glTexCoord2f(vertex->texture_u, vertex->texture_v);
            glVertex3f(vertex->position.x(), vertex->position.y(), vertex->position.z());
        }
    glEnd();
}


void MD5Mesh::calculate_vertex_positions()
{
    EnVector3<GLdouble> position;
    MD5Vertex* vertex;
    MD5Weight* weight;
    MD5Joint* joint;

    for(size_t i=0; i<m_vcount; ++i) {
        vertex = *(m_vertices + i);

        vertex->position.clear();

        for(int i=0; i<vertex->weight_count; ++i) {
            weight = m_weights[vertex->weight_start + i];
            joint = weight->joint;

            position = joint->orientation.rotate(weight->position);
            vertex->position += (position + joint->position) * weight->weight;
        }
    }
}


MD5Mesh::~MD5Mesh()
{
    if(m_vertices) {
        for(size_t i=0; i<m_vcount; ++i)
            if(m_vertices[i]) delete m_vertices[i];
        delete[] m_vertices;
    }

    if(m_triangles) {
        for(size_t i=0; i<m_tcount; ++i)
            if(m_triangles[i]) delete m_triangles[i];
        delete[] m_triangles;
    }

    if(m_weights) {
        for(size_t i=0; i<m_wcount; ++i)
            if(m_weights[i]) delete m_weights[i];
        delete[] m_weights;
    }
}


/*
 *  MD5Model methods
 *
 */


MD5Model::MD5Model()
    : m_version(0), m_jcount(0), m_joints(NULL),
        m_mcount(0), m_meshes(NULL)
{
}


MD5Model::~MD5Model()
{
    unload();
}


bool MD5Model::load(const std::string& filename)
{
    unload();

    // open the file
    std::ifstream infile(filename.c_str());
    if(!infile) return false;

    // get the file length
    infile.seekg(0, std::ios::end);
    size_t filelen = infile.tellg();
    infile.seekg(0, std::ios::beg);

    // read in the file data
    char *buffer = new char[filelen+1], *ptr = buffer;
    infile.read(buffer, filelen);
    buffer[filelen] = '\0';
    infile.close();

    // put the buffer into an auto array
    EnAutoArray<char> abuffer(buffer);

    // version first
    if(!scan_version(&ptr))
        return false;

    // then commandline
    if(!scan_commandline(&ptr))
        return false;

    // then num joints
    m_jcount = scan_num_joints(&ptr);
    if(m_jcount < 0) return false;
    m_joints = new MD5Joint*[m_jcount];
    ZeroMemory(m_joints, m_jcount * sizeof(MD5Joint*));

    // then num meshes
    m_mcount = scan_num_meshes(&ptr);
    if(m_mcount < 0) return false;
    m_meshes = new MD5Mesh*[m_mcount];
    ZeroMemory(m_meshes, m_mcount * sizeof(MD5Mesh*));

    // then joints
    if(!scan_joints(&ptr, m_jcount))
        return false;

    // finally, the meshes
    if(!scan_meshes(&ptr, m_mcount))
        return false;

    m_filename = filename;
    return true;
}


void MD5Model::unload()
{
    m_version = 0;
    m_commandline.erase();

    if(m_joints) {
        for(size_t i=0; i<m_jcount; ++i)
            if(m_joints[i]) delete m_joints[i];
        delete[] m_joints;
    }
    m_joints = NULL;
    m_jcount = 0;

    if(m_meshes) {
        for(size_t i=0; i<m_mcount; ++i)
            if(m_meshes[i]) delete m_meshes[i];
        delete[] m_meshes;
    }
    m_meshes = NULL;
    m_mcount = 0;

    m_filename.erase();
}


void MD5Model::render() const
{
    if(!loaded())
        return;

#if 0
    // draw the skeleton
    MD5Joint* joint;
    glBegin(GL_LINES);
        for(size_t i=0; i<m_jcount; ++i) {
            joint = *(m_joints + i);

            // need a parent
            if(!joint->parent)
                continue;

            glVertex3f(joint->parent->position.x(), joint->parent->position.y(), joint->parent->position.z());
            glVertex3f(joint->position.x(), joint->position.y(), joint->position.z());
        }
    glEnd();
#endif

#if 1
    // draw the meshes
    MD5Mesh* mesh;
    for(size_t i=0; i<m_mcount; ++i) {
        mesh = *(m_meshes + i);
        mesh->render();
    }
#endif
}


char MD5Model::advance(char** ptr) const
{
    char ret = **ptr;
    ++(*ptr);
    return ret;
}


MD5Model::Token MD5Model::lex(char** ptr) const
{
    skip_whitespace(ptr);

    char ch = advance(ptr);
    switch(ch)
    {
    case '\0': return END;
    case '(': return OPEN_PAREN;
    case ')': return CLOSE_PAREN;
    case '{': return OPEN_BRACE;
    case '}': return CLOSE_BRACE;
    default:
        if(isalpha(ch)) {
            --(*ptr);
            return keyword(ptr);
        }
    }

    return ERROR;
}


void MD5Model::skip_whitespace(char** ptr) const
{
    // skip whitespace
    char ch = advance(ptr);
    while(ch && isspace(ch))
        ch = advance(ptr);

    // skip comments
    if(ch == '/') {
        ch = advance(ptr);
        if(ch == '/') {
            skip_single_comment(ptr);

            // could be more whitespace
            return skip_whitespace(ptr);
        }
        --(*ptr);
    }
    --(*ptr);
}


void MD5Model::skip_single_comment(char** ptr) const
{
    // skip to newline
    // this leaves the newline
    // as the current pointer
    char ch = advance(ptr);
    while(ch && ch != '\n')
        ch = advance(ptr);
}


bool MD5Model::number_literal(char** ptr, double& value) const
{
    skip_whitespace(ptr);

    std::string scratch;
    char ch = advance(ptr);
    while(ch && (isdigit(ch) || ch == '-' || ch == '.')) {
        scratch += ch;
        ch = advance(ptr);
    }
    --(*ptr);

    char* end;
    value = strtod(scratch.c_str(), &end);
    return !(*end);
}


MD5Model::Token MD5Model::keyword(char** ptr) const
{
    std::string scratch;

    char ch = advance(ptr);
    while(ch && isalpha(ch) || isdigit(ch)) {
        scratch += ch;
        ch = advance(ptr);
    }
    --(*ptr);

    if(scratch == "MD5Version")
        return VERSION;
    else if(scratch == "commandline")
        return COMMANDLINE;
    else if(scratch == "numJoints")
        return NUM_JOINTS;
    else if(scratch == "numMeshes")
        return NUM_MESHES;
    else if(scratch == "joints")
        return JOINTS;
    else if(scratch == "mesh")
        return MESH;
    else if(scratch == "shader")
        return SHADER;
    else if(scratch == "numverts")
        return NUM_VERTS;
    else if(scratch == "vert")
        return VERT;
    else if(scratch == "numtris")
        return NUM_TRIS;
    else if(scratch == "tri")
        return TRI;
    else if(scratch == "numweights")
        return NUM_WEIGHTS;
    else if(scratch == "weight")
        return WEIGHT;
    return ERROR;
}


bool MD5Model::string_literal(char** ptr, std::string& value) const
{
    skip_whitespace(ptr);

    char ch = advance(ptr);
    if(ch != '"') return false;

    value.erase();

    ch = advance(ptr);
    while(ch && ch != '"' && ch != '\n') {
        value += ch;
        ch = advance(ptr);
    }
    return ch && ch != '\n';
}


bool MD5Model::scan_version(char** ptr)
{
    Token token = lex(ptr);
    if(token != VERSION)
        return false;

    double dvalue;
    if(!number_literal(ptr, dvalue))
        return false;

    // only version 10 is supported
    m_version = static_cast<int>(dvalue);
    return m_version == 10;
}


bool MD5Model::scan_commandline(char** ptr)
{
    Token token = lex(ptr);
    if(token != COMMANDLINE)
        return false;

    return string_literal(ptr, m_commandline);
}


int MD5Model::scan_num_joints(char** ptr)
{
    Token token = lex(ptr);
    if(token != NUM_JOINTS)
        return -1;

    double dvalue;
    if(!number_literal(ptr, dvalue))
        return -1;
    return static_cast<int>(dvalue);
}


int MD5Model::scan_num_meshes(char** ptr)
{
    Token token = lex(ptr);
    if(token != NUM_MESHES)
        return -1;

    double dvalue;
    if(!number_literal(ptr, dvalue))
        return -1;
    return static_cast<int>(dvalue);
}


bool MD5Model::scan_joints(char** ptr, int count)
{
    Token token = lex(ptr);
    if(token != JOINTS)
        return false;

    token = lex(ptr);
    if(token != OPEN_BRACE)
        return false;

    std::string name;
    int parent;
    GLdouble dvalue;
    EnVector3<GLdouble> position, orientation;

    for(int i=0; i<count; ++i) {
        // joint name
        if(!string_literal(ptr, name))
            return false;

        // joint parent
        if(!number_literal(ptr, dvalue))
            return false;
        parent = static_cast<int>(dvalue);

        token = lex(ptr);
        if(token != OPEN_PAREN)
            return false;

        // position.x
        if(!number_literal(ptr, dvalue))
            return false;
        position.x(dvalue);

        // position.y
        if(!number_literal(ptr, dvalue))
            return false;
        position.y(dvalue);

        // position.z
        if(!number_literal(ptr, dvalue))
            return false;
        position.z(dvalue);

        token = lex(ptr);
        if(token != CLOSE_PAREN)
            return false;

        token = lex(ptr);
        if(token != OPEN_PAREN)
            return false;

        // orientation.x
        if(!number_literal(ptr, dvalue))
            return false;
        orientation.x(dvalue);

        // orientation.y
        if(!number_literal(ptr, dvalue))
            return false;
        orientation.y(dvalue);

        // orientation.z
        if(!number_literal(ptr, dvalue))
            return false;
        orientation.z(dvalue);

        token = lex(ptr);
        if(token != CLOSE_PAREN)
            return false;

        // add the joint
        m_joints[i] = new MD5Joint(name, parent < 0 ? 0 : *(m_joints + parent), position, orientation);
    }

    token = lex(ptr);
    return token == CLOSE_BRACE;
}


bool MD5Model::scan_meshes(char** ptr, int count)
{
    MD5Mesh* mesh;
    std::string shader;
    size_t vcount, tcount, wcount;
    MD5Vertex** vertices;
    MD5Triangle** triangles;
    MD5Weight** weights;
    double dvalue;

    EnVector3<GLdouble> pv;
    GLdouble u, v, w;
    int ws, wc, a, b, c, ji;
    int index;

    for(int i=0; i<count; ++i) {
        Token token = lex(ptr);
        if(token != MESH)
            return false;

        token = lex(ptr);
        if(token != OPEN_BRACE)
            return false;

        token = lex(ptr);
        if(token != SHADER)
            return false;

        if(!string_literal(ptr, shader))
            return false;

        token = lex(ptr);
        if(token != NUM_VERTS)
            return false;

        if(!number_literal(ptr, dvalue))
            return false;
        vcount = static_cast<int>(dvalue);
        vertices = new MD5Vertex*[vcount];
        ZeroMemory(vertices, vcount * sizeof(MD5Vertex*));

        // read the vertices
        for(size_t j=0; j<vcount; ++j) {
            token = lex(ptr);
            if(token != VERT)
                return false;

            // vertex index
            if(!number_literal(ptr, dvalue))
                return false;
            index = static_cast<int>(dvalue);

            token = lex(ptr);
            if(token != OPEN_PAREN)
                return false;

            // texture u
            if(!number_literal(ptr, u))
                return false;

            // texture v
            if(!number_literal(ptr, v))
                return false;

            token = lex(ptr);
            if(token != CLOSE_PAREN)
                return false;

            // start weight
            if(!number_literal(ptr, dvalue))
                return false;
            ws = static_cast<int>(dvalue);

            // weight count
            if(!number_literal(ptr, dvalue))
                return false;
            wc = static_cast<int>(dvalue);

            // add the vertex
            vertices[index] = new MD5Vertex(u, v, ws, wc);
        }

        token = lex(ptr);
        if(token != NUM_TRIS)
            return false;

        if(!number_literal(ptr, dvalue))
            return false;
        tcount = static_cast<int>(dvalue);
        triangles = new MD5Triangle*[tcount];
        ZeroMemory(triangles, tcount * sizeof(MD5Triangle*));

        // read the triangles
        for(size_t j=0; j<tcount; ++j) {
            token = lex(ptr);
            if(token != TRI)
                return false;

            // triangle index
            if(!number_literal(ptr, dvalue))
                return false;
            index = static_cast<int>(dvalue);

            // vertex index a
            if(!number_literal(ptr, dvalue))
                return false;
            a = static_cast<int>(dvalue);

            // vertex index b
            if(!number_literal(ptr, dvalue))
                return false;
            b = static_cast<int>(dvalue);

            // vertex index c
            if(!number_literal(ptr, dvalue))
                return false;
            c = static_cast<int>(dvalue);

            triangles[index] = new MD5Triangle(*(vertices + a), *(vertices + b), *(vertices + c));
        }

        token = lex(ptr);
        if(token != NUM_WEIGHTS)
            return false;

        if(!number_literal(ptr, dvalue))
            return false;
        wcount = static_cast<int>(dvalue);
        weights = new MD5Weight*[wcount];
        ZeroMemory(weights, wcount * sizeof(MD5Weight*));

        // read the weights
        for(size_t j=0; j<wcount; ++j) {
            token = lex(ptr);
            if(token != WEIGHT)
                return false;

            // weight index
            if(!number_literal(ptr, dvalue))
                return false;
            index = static_cast<int>(dvalue);

            // joint index
            if(!number_literal(ptr, dvalue))
                return false;
            ji = static_cast<int>(dvalue);

            // weight
            if(!number_literal(ptr, w))
                return false;

            token = lex(ptr);
            if(token != OPEN_PAREN)
                return false;

            // position.x
            if(!number_literal(ptr, dvalue))
                return false;
            pv.x(dvalue);

            // position.y
            if(!number_literal(ptr, dvalue))
                return false;
            pv.y(dvalue);

            // position.z
            if(!number_literal(ptr, dvalue))
                return false;
            pv.z(dvalue);

            token = lex(ptr);
            if(token != CLOSE_PAREN)
                return false;

            weights[index] = new MD5Weight(*(m_joints + ji), w, pv);
        }

        token = lex(ptr);
        if(token != CLOSE_BRACE)
            return false;

        // add the mesh
        mesh = new MD5Mesh;
        mesh->m_shader = shader;
        mesh->m_vcount = vcount;
        mesh->m_vertices = vertices;
        mesh->m_tcount = tcount;
        mesh->m_triangles = triangles;
        mesh->m_wcount = wcount;
        mesh->m_weights = weights;
        mesh->calculate_vertex_positions();
        m_meshes[i] = mesh;
    }
    return true;
}

I know it's very unomptimized and ugly, I just want to get it working right now before I got and make a real mess. I'm pretty sure my .tga loader is working because if I skin a quad I get exactly the same image as if I opened it up in gimp. I'm sure it's something with how I'm loading the models or rendering them. I'm new to loading models and such, so I'm not sure where to start looking. Thanks in advance for any suggestions.

Share this post


Link to post
Share on other sites
Sunray    188
I don't flip the v texture coordinate in my MD5 loader. Does your TGA loader take any respect to the origin bit?

When you say a quad looks fine, maybe your texcoords is flipped when you draw the quad? (0,0) is the lower-left corner of the image, (1,1) is the upper-right corner.

Share this post


Link to post
Share on other sites
Chozo    176
This is my Targa loader:

Tara.cc

/*
====================
File: EnTarga.cc
Author: Shane Lillie
Description: Targa loading module source.

(c) 2004 Energon Software

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
====================
*/


#include "EnUtilities.h"

#include "EnTarga.h"


/*
* EnTarga methods
*
*/



EnTarga::EnTarga()
: m_width(0), m_height(0),
m_bpp(0), m_size(0), m_data(NULL)
{
}


EnTarga::~EnTarga()
{
unload();
}


bool EnTarga::load(const std::string& filename)
{
unload();

/* TODO: switch this to C++ */
/* NOTE: the gotos are okay here */

FILE* file = fopen(filename.c_str(), "rb");
if(!file) return false;

EnByte length;
fread(&length, sizeof(EnByte), 1, file);

fseek(file, 1, SEEK_CUR);

EnByte type;
fread(&type, sizeof(EnByte), 1, file);

fseek(file, 9, SEEK_CUR);

fread(&m_width, sizeof(EnWord), 1, file);
fread(&m_height, sizeof(EnWord), 1, file);
fread(&m_bpp, sizeof(EnWord), 1, file);

fseek(file, length + 1, SEEK_CUR);

if(type != TGA_RLE) {
if(m_bpp == 24 || m_bpp == 32) {
int stride = Bpp() * m_width;
m_data = new EnByte[stride * m_height];

for(int y=0; y<m_height; ++y) {
EnByte* line = &m_data[stride * y];

fread(line, stride, 1, file);

#if 0
// convert bgr to rgb
for(int i=0; i<stride; i+=Bpp()) {
int temp = line[i];
line[i] = line[i + 2];
line[i + 2] = temp;
}
#else
// convert grb to rgb
for(int i=0; i<stride; i+=Bpp()) {
EnByte temp = line[i];
line[i] = line[i + 1];
line[i + 1] = temp;
}
#endif
}
} else if(m_bpp == 16) {
EnWord pixels;
int r, g, b;

m_bpp = 24;
int stride = Bpp() * m_width;
m_data = new EnByte[stride * m_height];

for(int i=0; i<m_width * m_height; ++i) {
fread(&pixels, sizeof(EnWord), 1, file);

b = (pixels & 0x1f) << 3;
g = ((pixels >> 5) & 0x1f) << 3;
r = ((pixels >> 10) & 0x1f) << 3;

m_data[i * 3] = r;
m_data[i * 3 + 1] = g;
m_data[i * 3 + 2] = b;
}
} else {
fclose(file);
return false;
}
} else {
#if 0
EnByte rleid;
int colors_read = 0;
int stride = Bpp() * m_width;

m_data = new EnByte[stride * m_height];
EnByte* colors = new EnByte[Bpp()];

int i= 0;
while(i < m_width * m_height) {
fread(&rleid, sizeof(EnByte), 1, file);

if(rleid < 128) {
rleid++;

while(rleid) {
fread(colors, sizeof(EnByte) * Bpp(), 1, file);

m_data[colors_read] = colors[2];
m_data[colors_read + 1] = colors[1];
m_data[colors_read + 2] = colors[0];
if(m_bpp == 32) m_data[colors_read + 3] = colors[3];

i++;
rleid--;
colors_read += Bpp();
}
} else {
rleid -= 127;

while(rleid) {
fread(colors, sizeof(EnByte) * Bpp(), 1, file);

m_data[colors_read] = colors[2];
m_data[colors_read + 1] = colors[1];
m_data[colors_read + 2] = colors[0];
if(m_bpp == 32) m_data[colors_read + 3] = colors[3];

i++;
rleid--;
colors_read += Bpp();
}
}
}
delete[] colors;
#endif
fclose(file);
return false;
}

m_filename = filename;

// cleanup
fclose(file);
return true;
}


void EnTarga::unload()
{
m_width = m_height = 0;
m_bpp = m_size = 0;

if(m_data) {
delete[] m_data;
m_data = NULL;
}

m_filename.erase();
}



It's pretty much straight out of http://www.gametutorials.com/Tutorials/opengl/OpenGL_Pg2.htm Image Tutorial 4. I did notice that I have to swap from grb to rgb instead of bgr to rgb, but I haven't figured out exactly why just yet. This is how I draw the quad:


glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-50.0f, -50.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(50.0f, -50.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(50.0f, 50.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-50.0f, 50.0f, 0.0f);
glEnd();



That looks like it puts (0,0) in the bottom left and (1,1) in the top right. I'm guessing there's a problem with the targa loader, but I think the model problems are something to do with how I'm constructing the model.

Share this post


Link to post
Share on other sites
Chozo    176
Crazy. I just switched from reading positions as (x, y, z) to reading them as (z, x, y), the orientation as (z, x, y), the triangle as (c, b, a) instead of (a, b, c) and the v as -v and now it works. Crazy that it's done up that way but everything seems to work perfectly now.

Share this post


Link to post
Share on other sites
Chozo    176
Out of curiosity, is there any info out there about how to render the models with their textures? I'm only really interested in getting the bumpmap and specular map with the textures. This is my code so far (and it doesn't really works):


bool MD5Mesh::load_shader()
{
static EnTarga targa;

// load the texture
std::cout << "Loading texture 'imp_d.tga'..." << std::endl;
if(!targa.load("imp_d.tga")) return false;
if(targa.Bpp() != 3) return false;

glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);

// setup the texture
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, targa.width(), targa.height(), GL_RGB, GL_UNSIGNED_BYTE, targa.data());

targa.unload();

// load the bump map
std::cout << "Loading bump map 'imp_bmp.tga'..." << std::endl;
if(!targa.load("imp_bmp.tga")) return false;
if(targa.Bpp() != 3) return false;

glGenTextures(1, &m_bump_map);
glBindTexture(GL_TEXTURE_2D, m_bump_map);

// setup the bump map
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, targa.width(), targa.height(), GL_RGB, GL_UNSIGNED_BYTE, targa.data());

targa.unload();

// load the specular map
std::cout << "Loading specular map 'imp_s.tga'..." << std::endl;
if(!targa.load("imp_s.tga")) return false;
if(targa.Bpp() != 3) return false;

glGenTextures(1, &m_specular_map);
glBindTexture(GL_TEXTURE_2D, m_specular_map);

// setup the bump map
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, targa.width(), targa.height(), GL_RGB, GL_UNSIGNED_BYTE, targa.data());

targa.unload();

return true;
}

void MD5Mesh::render() const
{
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_texture);

glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_bump_map);

/*glActiveTextureARB(GL_TEXTURE2_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_specular_map);*/


do_render();

glActiveTextureARB(GL_TEXTURE0_ARB);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE2_ARB);
glDisable(GL_TEXTURE_2D);
}

void MD5Mesh::do_render() const
{
MD5Triangle* triangle;
MD5Vertex* vertex;
glBegin(GL_TRIANGLES);
for(size_t i=0; i<m_tcount; ++i) {
triangle = *(m_triangles + i);

glNormal3f(triangle->m_normal.x(), triangle->m_normal.y(), triangle->m_normal.z());

vertex = triangle->m_v1;
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, vertex->texture_u, vertex->texture_v);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, vertex->texture_u, vertex->texture_v);
glMultiTexCoord2fARB(GL_TEXTURE2_ARB, vertex->texture_u, vertex->texture_v);
glVertex3f(vertex->position.x(), vertex->position.y(), vertex->position.z());

vertex = triangle->m_v2;
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, vertex->texture_u, vertex->texture_v);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, vertex->texture_u, vertex->texture_v);
glMultiTexCoord2fARB(GL_TEXTURE2_ARB, vertex->texture_u, vertex->texture_v);
glVertex3f(vertex->position.x(), vertex->position.y(), vertex->position.z());

vertex = triangle->m_v3;
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, vertex->texture_u, vertex->texture_v);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, vertex->texture_u, vertex->texture_v);
glMultiTexCoord2fARB(GL_TEXTURE2_ARB, vertex->texture_u, vertex->texture_v);
glVertex3f(vertex->position.x(), vertex->position.y(), vertex->position.z());
}
glEnd();
}



The bumpmap seems to apply decently, but I can't figure out the parameters for the specular map. Does it look like I'm doing it all completely wrong? I'm using 3 texture units for now just to try and get it working.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this