MD2 Loader

24e56334ed6cdc138ed2b6e439b8f8f4
0
VitaliBR 101 Jul 13, 2010 at 17:47

Has anyone used this tutorial / code to load md2 models?

http://www.videotutorialsrock.com/opengl_tutorial/animation/text.php

I can load the template included in the code perfectly, including its texture.
But when I try to load another model with the same code is not loaded anything on the screen.

This code / tutorial is not to load any model md2?
It was written just for the model that comes with the code?

9 Replies

Please log in or register to post a reply.

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 Jul 13, 2010 at 19:40

There’s probably some problem or difference with the exported model you have. I know the Blender MD2 exporter hasn’t been working for a long time now. You’d have to find a very old version to get it to work. There’s a place somewhere on the the Blender site where you can download old versions.

24e56334ed6cdc138ed2b6e439b8f8f4
0
VitaliBR 101 Jul 13, 2010 at 19:45

Thanks for the reply guy! :)

You know another lib that I can use to md2?

Edit:
Actually, I opened the template that came with the tutorial in MilkShape 3D and exported it to md2
And the model has not appeared on screen as it appeared before being exported by MilkShape. I tried to send an email to the writer of the tutorial, but the e-mail back

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 Jul 13, 2010 at 23:14

Milkshape really keeps up with their exporters so I’d say it’s a problem with the tutorial code. You might want to find another tutorial. I saw this one on a search:

http://tfc.duke.free.fr/old/models/md2.htm

24e56334ed6cdc138ed2b6e439b8f8f4
0
VitaliBR 101 Jul 14, 2010 at 00:47

thanks fireside,

I had already seen this tutorial, your explanation is good. But the sample code is very confusing. (I own more easily understand the subject, analyzing the code)
:/

Edit: I did the test, I downloaded the same version (blender 2.42) as the writer said in the tutorial use.
But the model is not loaded yet, only two models used in the example

24e56334ed6cdc138ed2b6e439b8f8f4
0
VitaliBR 101 Jul 14, 2010 at 13:42

md2model.h

#ifndef MD2_MODEL_H_INCLUDED
#define MD2_MODEL_H_INCLUDED

#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/GL.h>
#include <GL/Glu.h>
#endif

#include "vec3f.h"

struct MD2Vertex {
    Vec3f pos;
    Vec3f normal;
};

struct MD2Frame {
    char name[16];
    MD2Vertex* vertices;
};

struct MD2TexCoord {
    float texCoordX;
    float texCoordY;
};

struct MD2Triangle {
    int vertices[3];  //The indices of the vertices in this triangle
    int texCoords[3]; //The indices of the texture coordinates of the triangle
};

class MD2Model {
    private:
        MD2Frame* frames;
        int numFrames;
        MD2TexCoord* texCoords;
        MD2Triangle* triangles;
        int numTriangles;
        GLuint textureId;

        int startFrame; //The first frame of the current animation
        int endFrame;   //The last frame of the current animation

        /* The position in the current animation.  0 indicates the beginning of
         * the animation, which is at the starting frame, and 1 indicates the
         * end of the animation, which is right when the starting frame is
         * reached again.  It always lies between 0 and 1.
         */
        float time;

        MD2Model();
    public:
        ~MD2Model();

        //Switches to the given animation
        void setAnimation(const char* name);
        //Advances the position in the current animation.  The entire animation
        //lasts one unit of time.
        void advance(float dt);
        //Draws the current state of the animated model.
        void draw();

        //Loads an MD2Model from the specified file.  Returns NULL if there was
        //an error loading it.
        static MD2Model* load(const char* filename);
};

#endif

vec3f.h

#ifndef VEC3F_H_INCLUDED
#define VEC3F_H_INCLUDED

#include <iostream>

class Vec3f {
    private:
        float v[3];
    public:
        Vec3f();
        Vec3f(float x, float y, float z);
        
        float &operator[](int index);
        float operator[](int index) const;
        
        Vec3f operator*(float scale) const;
        Vec3f operator/(float scale) const;
        Vec3f operator+(const Vec3f &other) const;
        Vec3f operator-(const Vec3f &other) const;
        Vec3f operator-() const;
        
        const Vec3f &operator*=(float scale);
        const Vec3f &operator/=(float scale);
        const Vec3f &operator+=(const Vec3f &other);
        const Vec3f &operator-=(const Vec3f &other);
        
        float magnitude() const;
        float magnitudeSquared() const;
        Vec3f normalize() const;
        float dot(const Vec3f &other) const;
        Vec3f cross(const Vec3f &other) const;
};

Vec3f operator*(float scale, const Vec3f &v);
std::ostream &operator<<(std::ostream &output, const Vec3f &v);

#endif


[/source]

[b]imageloader.h[/b]
[source lang="cpp"]#ifndef IMAGE_LOADER_H_INCLUDED
#define IMAGE_LOADER_H_INCLUDED

//Represents an image
class Image {
    public:
        Image(char* ps, int w, int h);
        ~Image();
        
        /* An array of the form (R1, G1, B1, R2, G2, B2, ...) indicating the
         * color of each pixel in image.  Color components range from 0 to 255.
         * The array starts the bottom-left pixel, then moves right to the end
         * of the row, then moves up to the next column, and so on.  This is the
         * format in which OpenGL likes images.
         */
        char* pixels;
        int width;
        int height;
};

//Reads a bitmap image from file.
Image* loadBMP(const char* filename);

#endif

md2model.cpp

#include <fstream>
#include <string.h>
#include "imageloader.h"
#include "md2model.h"

using namespace std;

namespace {
    //Normals used in the MD2 file format
    float NORMALS[486] =
        {-0.525731f,  0.000000f,  0.850651f,
         -0.442863f,  0.238856f,  0.864188f,
         -0.295242f,  0.000000f,  0.955423f,
         -0.309017f,  0.500000f,  0.809017f,
         -0.162460f,  0.262866f,  0.951056f,
          0.000000f,  0.000000f,  1.000000f,
          0.000000f,  0.850651f,  0.525731f,
         -0.147621f,  0.716567f,  0.681718f,
          0.147621f,  0.716567f,  0.681718f,
          0.000000f,  0.525731f,  0.850651f,
          0.309017f,  0.500000f,  0.809017f,
          0.525731f,  0.000000f,  0.850651f,
          0.295242f,  0.000000f,  0.955423f,
          0.442863f,  0.238856f,  0.864188f,
          0.162460f,  0.262866f,  0.951056f,
         -0.681718f,  0.147621f,  0.716567f,
         -0.809017f,  0.309017f,  0.500000f,
         -0.587785f,  0.425325f,  0.688191f,
         -0.850651f,  0.525731f,  0.000000f,
         -0.864188f,  0.442863f,  0.238856f,
         -0.716567f,  0.681718f,  0.147621f,
         -0.688191f,  0.587785f,  0.425325f,
         -0.500000f,  0.809017f,  0.309017f,
         -0.238856f,  0.864188f,  0.442863f,
         -0.425325f,  0.688191f,  0.587785f,
         -0.716567f,  0.681718f, -0.147621f,
         -0.500000f,  0.809017f, -0.309017f,
         -0.525731f,  0.850651f,  0.000000f,
          0.000000f,  0.850651f, -0.525731f,
         -0.238856f,  0.864188f, -0.442863f,
          0.000000f,  0.955423f, -0.295242f,
         -0.262866f,  0.951056f, -0.162460f,
          0.000000f,  1.000000f,  0.000000f,
          0.000000f,  0.955423f,  0.295242f,
         -0.262866f,  0.951056f,  0.162460f,
          0.238856f,  0.864188f,  0.442863f,
          0.262866f,  0.951056f,  0.162460f,
          0.500000f,  0.809017f,  0.309017f,
          0.238856f,  0.864188f, -0.442863f,
          0.262866f,  0.951056f, -0.162460f,
          0.500000f,  0.809017f, -0.309017f,
          0.850651f,  0.525731f,  0.000000f,
          0.716567f,  0.681718f,  0.147621f,
          0.716567f,  0.681718f, -0.147621f,
          0.525731f,  0.850651f,  0.000000f,
          0.425325f,  0.688191f,  0.587785f,
          0.864188f,  0.442863f,  0.238856f,
          0.688191f,  0.587785f,  0.425325f,
          0.809017f,  0.309017f,  0.500000f,
          0.681718f,  0.147621f,  0.716567f,
          0.587785f,  0.425325f,  0.688191f,
          0.955423f,  0.295242f,  0.000000f,
          1.000000f,  0.000000f,  0.000000f,
          0.951056f,  0.162460f,  0.262866f,
          0.850651f, -0.525731f,  0.000000f,
          0.955423f, -0.295242f,  0.000000f,
          0.864188f, -0.442863f,  0.238856f,
          0.951056f, -0.162460f,  0.262866f,
          0.809017f, -0.309017f,  0.500000f,
          0.681718f, -0.147621f,  0.716567f,
          0.850651f,  0.000000f,  0.525731f,
          0.864188f,  0.442863f, -0.238856f,
          0.809017f,  0.309017f, -0.500000f,
          0.951056f,  0.162460f, -0.262866f,
          0.525731f,  0.000000f, -0.850651f,
          0.681718f,  0.147621f, -0.716567f,
          0.681718f, -0.147621f, -0.716567f,
          0.850651f,  0.000000f, -0.525731f,
          0.809017f, -0.309017f, -0.500000f,
          0.864188f, -0.442863f, -0.238856f,
          0.951056f, -0.162460f, -0.262866f,
          0.147621f,  0.716567f, -0.681718f,
          0.309017f,  0.500000f, -0.809017f,
          0.425325f,  0.688191f, -0.587785f,
          0.442863f,  0.238856f, -0.864188f,
          0.587785f,  0.425325f, -0.688191f,
          0.688191f,  0.587785f, -0.425325f,
         -0.147621f,  0.716567f, -0.681718f,
         -0.309017f,  0.500000f, -0.809017f,
          0.000000f,  0.525731f, -0.850651f,
         -0.525731f,  0.000000f, -0.850651f,
         -0.442863f,  0.238856f, -0.864188f,
         -0.295242f,  0.000000f, -0.955423f,
         -0.162460f,  0.262866f, -0.951056f,
          0.000000f,  0.000000f, -1.000000f,
          0.295242f,  0.000000f, -0.955423f,
          0.162460f,  0.262866f, -0.951056f,
         -0.442863f, -0.238856f, -0.864188f,
         -0.309017f, -0.500000f, -0.809017f,
         -0.162460f, -0.262866f, -0.951056f,
          0.000000f, -0.850651f, -0.525731f,
         -0.147621f, -0.716567f, -0.681718f,
          0.147621f, -0.716567f, -0.681718f,
          0.000000f, -0.525731f, -0.850651f,
          0.309017f, -0.500000f, -0.809017f,
          0.442863f, -0.238856f, -0.864188f,
          0.162460f, -0.262866f, -0.951056f,
          0.238856f, -0.864188f, -0.442863f,
          0.500000f, -0.809017f, -0.309017f,
          0.425325f, -0.688191f, -0.587785f,
          0.716567f, -0.681718f, -0.147621f,
          0.688191f, -0.587785f, -0.425325f,
          0.587785f, -0.425325f, -0.688191f,
          0.000000f, -0.955423f, -0.295242f,
          0.000000f, -1.000000f,  0.000000f,
          0.262866f, -0.951056f, -0.162460f,
          0.000000f, -0.850651f,  0.525731f,
          0.000000f, -0.955423f,  0.295242f,
          0.238856f, -0.864188f,  0.442863f,
          0.262866f, -0.951056f,  0.162460f,
          0.500000f, -0.809017f,  0.309017f,
          0.716567f, -0.681718f,  0.147621f,
          0.525731f, -0.850651f,  0.000000f,
         -0.238856f, -0.864188f, -0.442863f,
         -0.500000f, -0.809017f, -0.309017f,
         -0.262866f, -0.951056f, -0.162460f,
         -0.850651f, -0.525731f,  0.000000f,
         -0.716567f, -0.681718f, -0.147621f,
         -0.716567f, -0.681718f,  0.147621f,
         -0.525731f, -0.850651f,  0.000000f,
         -0.500000f, -0.809017f,  0.309017f,
         -0.238856f, -0.864188f,  0.442863f,
         -0.262866f, -0.951056f,  0.162460f,
         -0.864188f, -0.442863f,  0.238856f,
         -0.809017f, -0.309017f,  0.500000f,
         -0.688191f, -0.587785f,  0.425325f,
         -0.681718f, -0.147621f,  0.716567f,
         -0.442863f, -0.238856f,  0.864188f,
         -0.587785f, -0.425325f,  0.688191f,
         -0.309017f, -0.500000f,  0.809017f,
         -0.147621f, -0.716567f,  0.681718f,
         -0.425325f, -0.688191f,  0.587785f,
         -0.162460f, -0.262866f,  0.951056f,
          0.442863f, -0.238856f,  0.864188f,
          0.162460f, -0.262866f,  0.951056f,
          0.309017f, -0.500000f,  0.809017f,
          0.147621f, -0.716567f,  0.681718f,
          0.000000f, -0.525731f,  0.850651f,
          0.425325f, -0.688191f,  0.587785f,
          0.587785f, -0.425325f,  0.688191f,
          0.688191f, -0.587785f,  0.425325f,
         -0.955423f,  0.295242f,  0.000000f,
         -0.951056f,  0.162460f,  0.262866f,
         -1.000000f,  0.000000f,  0.000000f,
         -0.850651f,  0.000000f,  0.525731f,
         -0.955423f, -0.295242f,  0.000000f,
         -0.951056f, -0.162460f,  0.262866f,
         -0.864188f,  0.442863f, -0.238856f,
         -0.951056f,  0.162460f, -0.262866f,
         -0.809017f,  0.309017f, -0.500000f,
         -0.864188f, -0.442863f, -0.238856f,
         -0.951056f, -0.162460f, -0.262866f,
         -0.809017f, -0.309017f, -0.500000f,
         -0.681718f,  0.147621f, -0.716567f,
         -0.681718f, -0.147621f, -0.716567f,
         -0.850651f,  0.000000f, -0.525731f,
         -0.688191f,  0.587785f, -0.425325f,
         -0.587785f,  0.425325f, -0.688191f,
         -0.425325f,  0.688191f, -0.587785f,
         -0.425325f, -0.688191f, -0.587785f,
         -0.587785f, -0.425325f, -0.688191f,
         -0.688191f, -0.587785f, -0.425325f};

    //Returns whether the system is little-endian
    bool littleEndian() {
        //The short value 1 has bytes (1, 0) in little-endian and (0, 1) in
        //big-endian
        short s = 1;
        return (((char*)&s)[0]) == 1;
    }

    //Converts a four-character array to an integer, using little-endian form
    int toInt(const char* bytes) {
        return (int)(((unsigned char)bytes[3] << 24) |
                     ((unsigned char)bytes[2] << 16) |
                     ((unsigned char)bytes[1] << 8) |
                     (unsigned char)bytes[0]);
    }

    //Converts a two-character array to a short, using little-endian form
    short toShort(const char* bytes) {
        return (short)(((unsigned char)bytes[1] << 8) |
                       (unsigned char)bytes[0]);
    }

    //Converts a two-character array to an unsigned short, using little-endian
    //form
    unsigned short toUShort(const char* bytes) {
        return (unsigned short)(((unsigned char)bytes[1] << 8) |
                                (unsigned char)bytes[0]);
    }

    //Converts a four-character array to a float, using little-endian form
    float toFloat(const char* bytes) {
        float f;
        if (littleEndian()) {
            ((char*)&f)[0] = bytes[0];
            ((char*)&f)[1] = bytes[1];
            ((char*)&f)[2] = bytes[2];
            ((char*)&f)[3] = bytes[3];
        }
        else {
            ((char*)&f)[0] = bytes[3];
            ((char*)&f)[1] = bytes[2];
            ((char*)&f)[2] = bytes[1];
            ((char*)&f)[3] = bytes[0];
        }
        return f;
    }

    //Reads the next four bytes as an integer, using little-endian form
    int readInt(ifstream &input) {
        char buffer[4];
        input.read(buffer, 4);
        return toInt(buffer);
    }

    //Reads the next two bytes as a short, using little-endian form
    short readShort(ifstream &input) {
        char buffer[2];
        input.read(buffer, 2);
        return toShort(buffer);
    }

    //Reads the next two bytes as an unsigned short, using little-endian form
    unsigned short readUShort(ifstream &input) {
        char buffer[2];
        input.read(buffer, 2);
        return toUShort(buffer);
    }

    //Reads the next four bytes as a float, using little-endian form
    float readFloat(ifstream &input) {
        char buffer[4];
        input.read(buffer, 4);
        return toFloat(buffer);
    }

    //Calls readFloat three times and returns the results as a Vec3f object
    Vec3f readVec3f(ifstream &input) {
        float x = readFloat(input);
        float y = readFloat(input);
        float z = readFloat(input);
        return Vec3f(x, y, z);
    }

    //Makes the image into a texture, and returns the id of the texture
    GLuint loadTexture(Image *image) {
        GLuint textureId;
        glGenTextures(1, &textureId);
        glBindTexture(GL_TEXTURE_2D, textureId);
        glTexImage2D(GL_TEXTURE_2D,
                     0,
                     GL_RGB,
                     image->width, image->height,
                     0,
                     GL_RGB,
                     GL_UNSIGNED_BYTE,
                     image->pixels);
        return textureId;
    }
}

MD2Model::~MD2Model() {
    if (frames != NULL) {
        for(int i = 0; i < numFrames; i++) {
            delete[] frames[i].vertices;
        }
        delete[] frames;
    }

    if (texCoords != NULL) {
        delete[] texCoords;
    }
    if (triangles != NULL) {
        delete[] triangles;
    }
}

MD2Model::MD2Model() {
    frames = NULL;
    texCoords = NULL;
    triangles = NULL;
    time = 0;
}

//Loads the MD2 model
MD2Model* MD2Model::load(const char* filename) {
    ifstream input;
    input.open(filename, istream::binary);

    char buffer[64];
    input.read(buffer, 4); //Should be "IPD2", if this is an MD2 file
    if (buffer[0] != 'I' || buffer[1] != 'D' ||
        buffer[2] != 'P' || buffer[3] != '2') {
        return NULL;
    }
    if (readInt(input) != 8) { //The version number
        return NULL;
    }

    int textureWidth = readInt(input);   //The width of the textures
    int textureHeight = readInt(input);  //The height of the textures
    readInt(input);                      //The number of bytes per frame
    int numTextures = readInt(input);    //The number of textures
    if (numTextures != 1) {
        return NULL;
    }
    int numVertices = readInt(input);    //The number of vertices
    int numTexCoords = readInt(input);   //The number of texture coordinates
    int numTriangles = readInt(input);   //The number of triangles
    readInt(input);                      //The number of OpenGL commands
    int numFrames = readInt(input);      //The number of frames

    //Offsets (number of bytes after the beginning of the file to the beginning
    //of where certain data appear)
    int textureOffset = readInt(input);  //The offset to the textures
    int texCoordOffset = readInt(input); //The offset to the texture coordinates
    int triangleOffset = readInt(input); //The offset to the triangles
    int frameOffset = readInt(input);    //The offset to the frames
    readInt(input);                      //The offset to the OpenGL commands
    readInt(input);                      //The offset to the end of the file

    //Load the texture
    input.seekg(textureOffset, ios_base::beg);
    input.read(buffer, 64);
    if (strlen(buffer) < 5 ||
        strcmp(buffer + strlen(buffer) - 4, ".bmp") != 0) {
        return NULL;
    }
    Image* image = loadBMP(buffer);
    GLuint textureId = loadTexture(image);
    delete image;
    MD2Model* model = new MD2Model();
    model->textureId = textureId;

    //Load the texture coordinates
    input.seekg(texCoordOffset, ios_base::beg);
    model->texCoords = new MD2TexCoord[numTexCoords];
    for(int i = 0; i < numTexCoords; i++) {
        MD2TexCoord* texCoord = model->texCoords + i;
        texCoord->texCoordX = (float)readShort(input) / textureWidth;
        texCoord->texCoordY = 1 - (float)readShort(input) / textureHeight;
    }

    //Load the triangles
    input.seekg(triangleOffset, ios_base::beg);
    model->triangles = new MD2Triangle[numTriangles];
    model->numTriangles = numTriangles;
    for(int i = 0; i < numTriangles; i++) {
        MD2Triangle* triangle = model->triangles + i;
        for(int j = 0; j < 3; j++) {
            triangle->vertices[j] = readUShort(input);
        }
        for(int j = 0; j < 3; j++) {
            triangle->texCoords[j] = readUShort(input);
        }
    }

    //Load the frames
    input.seekg(frameOffset, ios_base::beg);
    model->frames = new MD2Frame[numFrames];
    model->numFrames = numFrames;
    for(int i = 0; i < numFrames; i++) {
        MD2Frame* frame = model->frames + i;
        frame->vertices = new MD2Vertex[numVertices];
        Vec3f scale = readVec3f(input);
        Vec3f translation = readVec3f(input);
        input.read(frame->name, 16);

        for(int j = 0; j < numVertices; j++) {
            MD2Vertex* vertex = frame->vertices + j;
            input.read(buffer, 3);
            Vec3f v((unsigned char)buffer[0],
                    (unsigned char)buffer[1],
                    (unsigned char)buffer[2]);
            vertex->pos = translation + Vec3f(scale[0] * v[0],
                                              scale[1] * v[1],
                                              scale[2] * v[2]);
            input.read(buffer, 1);
            int normalIndex = (int)((unsigned char)buffer[0]);
            vertex->normal = Vec3f(NORMALS[3 * normalIndex],
                                   NORMALS[3 * normalIndex + 1],
                                   NORMALS[3 * normalIndex + 2]);
        }
    }

    model->startFrame = 0;
    model->endFrame = numFrames - 1;
    return model;
}

void MD2Model::setAnimation(const char* name) {
    /* The names of frames normally begin with the name of the animation in
     * which they are, e.g. "run", and are followed by a non-alphabetical
     * character.  Normally, they indicate their frame number in the animation,
     * e.g. "run_1", "run_2", etc.
     */
    bool found = false;
    for(int i = 0; i < numFrames; i++) {
        MD2Frame* frame = frames + i;
        if (strlen(frame->name) > strlen(name) &&
            strncmp(frame->name, name, strlen(name)) == 0 &&
            !isalpha(frame->name[strlen(name)])) {
            if (!found) {
                found = true;
                startFrame = i;
            }
            else {
                endFrame = i;
            }
        }
        else if (found) {
            break;
        }
    }
}

void MD2Model::advance(float dt) {
    if (dt < 0) {
        return;
    }

    time += dt;
    if (time < 1000000000) {
        time -= (int)time;
    }
    else {
        time = 0;
    }
}

void MD2Model::draw() {
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    //Figure out the two frames between which we are interpolating
    int frameIndex1 = (int)(time * (endFrame - startFrame + 1)) + startFrame;
    if (frameIndex1 > endFrame) {
        frameIndex1 = startFrame;
    }

    int frameIndex2;
    if (frameIndex1 < endFrame) {
        frameIndex2 = frameIndex1 + 1;
    }
    else {
        frameIndex2 = startFrame;
    }

    MD2Frame* frame1 = frames + frameIndex1;
    MD2Frame* frame2 = frames + frameIndex2;

    //Figure out the fraction that we are between the two frames
    float frac =
        (time - (float)(frameIndex1 - startFrame) /
         (float)(endFrame - startFrame + 1)) * (endFrame - startFrame + 1);

    //Draw the model as an interpolation between the two frames
    glBegin(GL_TRIANGLES);
    for(int i = 0; i < numTriangles; i++) {
        MD2Triangle* triangle = triangles + i;
        for(int j = 0; j < 3; j++) {
            MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
            MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
            Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
            Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
            if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
                normal = Vec3f(0, 0, 1);
            }
            glNormal3f(normal[0], normal[1], normal[2]);

            MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
            glTexCoord2f(texCoord->texCoordX, texCoord->texCoordY);
            glVertex3f(pos[0], pos[1], pos[2]);
        }
    }
    glEnd();
}
24e56334ed6cdc138ed2b6e439b8f8f4
0
VitaliBR 101 Jul 14, 2010 at 13:42

vec3f.cpp

#include <math.h>

#include "vec3f.h"

using namespace std;

Vec3f::Vec3f() {
    
}

Vec3f::Vec3f(float x, float y, float z) {
    v[0] = x;
    v[1] = y;
    v[2] = z;
}

float &Vec3f::operator[](int index) {
    return v[index];
}

float Vec3f::operator[](int index) const {
    return v[index];
}

Vec3f Vec3f::operator*(float scale) const {
    return Vec3f(v[0] * scale, v[1] * scale, v[2] * scale);
}

Vec3f Vec3f::operator/(float scale) const {
    return Vec3f(v[0] / scale, v[1] / scale, v[2] / scale);
}

Vec3f Vec3f::operator+(const Vec3f &other) const {
    return Vec3f(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]);
}

Vec3f Vec3f::operator-(const Vec3f &other) const {
    return Vec3f(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]);
}

Vec3f Vec3f::operator-() const {
    return Vec3f(-v[0], -v[1], -v[2]);
}

const Vec3f &Vec3f::operator*=(float scale) {
    v[0] *= scale;
    v[1] *= scale;
    v[2] *= scale;
    return *this;
}

const Vec3f &Vec3f::operator/=(float scale) {
    v[0] /= scale;
    v[1] /= scale;
    v[2] /= scale;
    return *this;
}

const Vec3f &Vec3f::operator+=(const Vec3f &other) {
    v[0] += other.v[0];
    v[1] += other.v[1];
    v[2] += other.v[2];
    return *this;
}

const Vec3f &Vec3f::operator-=(const Vec3f &other) {
    v[0] -= other.v[0];
    v[1] -= other.v[1];
    v[2] -= other.v[2];
    return *this;
}

float Vec3f::magnitude() const {
    return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

float Vec3f::magnitudeSquared() const {
    return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}

Vec3f Vec3f::normalize() const {
    float m = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    return Vec3f(v[0] / m, v[1] / m, v[2] / m);
}

float Vec3f::dot(const Vec3f &other) const {
    return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2];
}

Vec3f Vec3f::cross(const Vec3f &other) const {
    return Vec3f(v[1] * other.v[2] - v[2] * other.v[1],
                 v[2] * other.v[0] - v[0] * other.v[2],
                 v[0] * other.v[1] - v[1] * other.v[0]);
}

Vec3f operator*(float scale, const Vec3f &v) {
    return v * scale;
}

ostream &operator<<(ostream &output, const Vec3f &v) {
    cout << '(' << v[0] << ", " << v[1] << ", " << v[2] << ')';
    return output;
}

imageloader.cpp

#include <assert.h>
#include <fstream>

#include "imageloader.h"

using namespace std;

Image::Image(char* ps, int w, int h) : pixels(ps), width(w), height(h) {
    
}

Image::~Image() {
    delete[] pixels;
}

namespace {
    //Converts a four-character array to an integer, using little-endian form
    int toInt(const char* bytes) {
        return (int)(((unsigned char)bytes[3] << 24) |
                     ((unsigned char)bytes[2] << 16) |
                     ((unsigned char)bytes[1] << 8) |
                     (unsigned char)bytes[0]);
    }
    
    //Converts a two-character array to a short, using little-endian form
    short toShort(const char* bytes) {
        return (short)(((unsigned char)bytes[1] << 8) |
                       (unsigned char)bytes[0]);
    }
    
    //Reads the next four bytes as an integer, using little-endian form
    int readInt(ifstream &input) {
        char buffer[4];
        input.read(buffer, 4);
        return toInt(buffer);
    }
    
    //Reads the next two bytes as a short, using little-endian form
    short readShort(ifstream &input) {
        char buffer[2];
        input.read(buffer, 2);
        return toShort(buffer);
    }
    
    //Just like auto_ptr, but for arrays
    template<class T>
    class auto_array {
        private:
            T* array;
            mutable bool isReleased;
        public:
            explicit auto_array(T* array_ = NULL) :
                array(array_), isReleased(false) {
            }
            
            auto_array(const auto_array<T> &aarray) {
                array = aarray.array;
                isReleased = aarray.isReleased;
                aarray.isReleased = true;
            }
            
            ~auto_array() {
                if (!isReleased && array != NULL) {
                    delete[] array;
                }
            }
            
            T* get() const {
                return array;
            }
            
            T &operator*() const {
                return *array;
            }
            
            void operator=(const auto_array<T> &aarray) {
                if (!isReleased && array != NULL) {
                    delete[] array;
                }
                array = aarray.array;
                isReleased = aarray.isReleased;
                aarray.isReleased = true;
            }
            
            T* operator->() const {
                return array;
            }
            
            T* release() {
                isReleased = true;
                return array;
            }
            
            void reset(T* array_ = NULL) {
                if (!isReleased && array != NULL) {
                    delete[] array;
                }
                array = array_;
            }
            
            T* operator+(int i) {
                return array + i;
            }
            
            T &operator[](int i) {
                return array[i];
            }
    };
}

Image* loadBMP(const char* filename) {
    ifstream input;
    input.open(filename, ifstream::binary);
    assert(!input.fail() || !"Could not find file");
    char buffer[2];
    input.read(buffer, 2);
    assert(buffer[0] == 'B' && buffer[1] == 'M' || !"Not a bitmap file");
    input.ignore(8);
    int dataOffset = readInt(input);
    
    //Read the header
    int headerSize = readInt(input);
    int width;
    int height;
    switch(headerSize) {
        case 40:
            //V3
            width = readInt(input);
            height = readInt(input);
            input.ignore(2);
            assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
            assert(readShort(input) == 0 || !"Image is compressed");
            break;
        case 12:
            //OS/2 V1
            width = readShort(input);
            height = readShort(input);
            input.ignore(2);
            assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
            break;
        case 64:
            //OS/2 V2
            assert(!"Can't load OS/2 V2 bitmaps");
            break;
        case 108:
            //Windows V4
            assert(!"Can't load Windows V4 bitmaps");
            break;
        case 124:
            //Windows V5
            assert(!"Can't load Windows V5 bitmaps");
            break;
        default:
            assert(!"Unknown bitmap format");
    }
    
    //Read the data
    int bytesPerRow = ((width * 3 + 3) / 4) * 4 - (width * 3 % 4);
    int size = bytesPerRow * height;
    auto_array<char> pixels(new char[size]);
    input.seekg(dataOffset, ios_base::beg);
    input.read(pixels.get(), size);
    
    //Get the data into the right format
    auto_array<char> pixels2(new char[width * height * 3]);
    for(int y = 0; y < height; y++) {
        for(int x = 0; x < width; x++) {
            for(int c = 0; c < 3; c++) {
                pixels2[3 * (width * y + x) + c] =
                    pixels[bytesPerRow * y + 3 * x + (2 - c)];
            }
        }
    }
    
    input.close();
    return new Image(pixels2.release(), width, height);
}

imageloader.h

#ifndef IMAGE_LOADER_H_INCLUDED
#define IMAGE_LOADER_H_INCLUDED

//Represents an image
class Image {
    public:
        Image(char* ps, int w, int h);
        ~Image();
        
        /* An array of the form (R1, G1, B1, R2, G2, B2, ...) indicating the
         * color of each pixel in image.  Color components range from 0 to 255.
         * The array starts the bottom-left pixel, then moves right to the end
         * of the row, then moves up to the next column, and so on.  This is the
         * format in which OpenGL likes images.
         */
        char* pixels;
        int width;
        int height;
};

//Reads a bitmap image from file.
Image* loadBMP(const char* filename);

#endif
A638aa42130293f319eda7fa4ba121f4
0
fireside 141 Jul 14, 2010 at 18:49

You’ll have to try to debug it yourself then. Maybe write to a text file or cout to the screen in the load and draw methods of the md2 model file and see if it is reading the information correctly. Also, possibly check some more free models that are on the internet, see if they load all right. Good luck on that.

Ae6ea940ad993cd193e59bfd956b253b
0
Coriiander 101 May 04, 2011 at 03:17

Sigh

//Returns whether the system is little-endian
    bool littleEndian() {
        //The short value 1 has bytes (1, 0) in little-endian and (0, 1) in
        //big-endian
        short s = 1;
        return (((char*)&s)[0]) == 1;
    }

This will NOT return the endianness during run-time. This will return the endianness during compile-time. When do people start to realize compilers will optimize tests like this out, to replace with a fixed value? If you want to compile stuff like this, then you should be disabling compiler optimization for the specific code. And even then, this will mostlikely result in machine instructions loading the address pointed to in a machine register, which will still output the same result if you’re not careful and endian-aware enough.

Disassembled

PUBLIC  _littleEndian
; Function compile flags: /Ogtpy
; File c:\development\dna\source\tests\testconsole\main.c
;   COMDAT _littleEndian
_TEXT   SEGMENT
_littleEndian PROC                  ; COMDAT

; 12   :  //The short value 1 has bytes (1, 0) in little-endian and (0, 1) in
; 13   :  //big-endian
; 14   :  short s = 1;
; 15   :  return (((char*)&s)[0]) == 1;

    mov eax, 1

; 16   :  }

    ret 0
_littleEndian ENDP
_TEXT   ENDS

Disassembled (optimization turned off)

;#pragma optimize( "", off )

PUBLIC  _littleEndian
; Function compile flags: /Odtp
; File c:\development\dna\source\tests\testconsole\main.c
;   COMDAT _littleEndian
_TEXT   SEGMENT
_s$ = -4                        ; size = 2
_littleEndian PROC                  ; COMDAT
; 12   :  int littleEndian() {

    push    ebp
    mov ebp, esp
    push    ecx

; 13   :  //The short value 1 has bytes (1, 0) in little-endian and (0, 1) in
; 14   :  //big-endian
; 15   :  short s = 1;

    mov eax, 1
    mov WORD PTR _s$[ebp], ax

; 16   :  return (((char*)&s)[0]) == 1;

    movsx   eax, BYTE PTR _s$[ebp]
    sub eax, 1
    neg eax
    sbb eax, eax
    add eax, 1

; 17   :  }

    mov esp, ebp
    pop ebp
    ret 0
_littleEndian ENDP
_TEXT   ENDS

#pragma optimize( "", on )

Stop wasting your time writing crappy code, and begin to take a look at your disassembled code every now and then. Debug, test, verify, iterate over all desired platform configurations, and mark done. Especially if you apparantly don’t know what you’re doing. The compiler is there to serve you, but it will need help doing so correctly. You cannot possibly assume the compiler just ‘knows’ what you’re trying to do all the time. It’s the other way around (no pun intended ;)).

And no, this has nothing to do with the reason for your stuff being flipped….. Hint: bottom-up raster data.

6eaf0e08fe36b2c23ca096562dd7a8b7
0
__________Smile_ 101 May 05, 2011 at 14:51

There is nothing wrong with compile-time endianness and optimizing out such functions is action that compiler expected to do. Run-time endianness does not make sense at all (well, theoretically it is possible to make chip with run-time endianness switch, but it is simply waste of chip area). So endianness is constant for every architecture and good compiler optimize such checking correctly then compiling for that architecture.