⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 md2file.cpp

📁 游戏编程精华02-含有几十个游戏编程例子
💻 CPP
字号:
/* Copyright (C) Greg James, 2001. 
 * All rights reserved worldwide.
 *
 * This software is provided "as is" without express or implied
 * warranties. You may freely copy and compile this source into
 * applications you distribute provided that the copyright text
 * below is included in the resulting source code, for example:
 * "Portions Copyright (C) Greg James, 2001"
 */

/* md2file.c - Quake 2 MD2 model loader */

/* Courtesy Mark Kilgard */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "md2file.h"

void md2FreeModel(Md2Model *model)
{
  if (model) 
  {
    if (model->texCoords) 
	{
      free(model->texCoords);
    }
    
    if (model->triangles) 
	{
      free(model->triangles);
    }
    
    if (model->frames) 
	{
      int i;
      
      for (i = 0; i < model->header.numFrames; i++) 
	  {
        if (model->frames[i].vertices) 
		{
          free(model->frames[i].vertices);
        }
      }
      free(model->frames);
    }

    if (model->filename) 
	{
      free(model->filename);
    }
    
    free(model);
  }
}

static void
generateAutomaticNormals(Md2Model *model)
{
  int i, j;
  
  for (j = 0; j < model->header.numFrames; j++) {
    Md2Frame *f = &model->frames[j];
    Md2TriangleVertex *v = f->vertices;
    
    for (i = 0; i < model->header.numVertices; i++) {
      v[i].normal[0] = 0.0;
      v[i].normal[1] = 0.0;
      v[i].normal[2] = 0.0;
    }
    
    for (i = 0; i < model->header.numTriangles; i++) {
      Md2Triangle *t = &model->triangles[i];
      Md2TriangleVertex *v0, *v1, *v2;
      float vec0[3], vec1[3], n[3];
      float invLen;

      v0 = &v[t->vertexIndices[0]];
      v1 = &v[t->vertexIndices[1]];
      v2 = &v[t->vertexIndices[2]];
      
      /* Need 2 vectors to find cross product. */
      vec0[0] = v1->vertex[0] - v0->vertex[0];
      vec0[1] = v1->vertex[1] - v0->vertex[1];
      vec0[2] = v1->vertex[2] - v0->vertex[2];
      
      vec1[0] = v2->vertex[0] - v0->vertex[0];
      vec1[1] = v2->vertex[1] - v0->vertex[1];
      vec1[2] = v2->vertex[2] - v0->vertex[2];

      n[0] =   vec0[1] * vec1[2] - vec0[2] * vec1[1];
      n[1] = -(vec0[0] * vec1[2] - vec0[2] * vec1[0]);
      n[2] =   vec0[0] * vec1[1] - vec0[1] * vec1[0];
      invLen = -1.f / (float)sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
      n[0] *= invLen;
      n[1] *= invLen;
      n[2] *= invLen;

      v0->normal[0] += n[0];
      v0->normal[1] += n[1];
      v0->normal[2] += n[2];
      v1->normal[0] += n[0];
      v1->normal[1] += n[1];
      v1->normal[2] += n[2];
      v2->normal[0] += n[0];
      v2->normal[1] += n[1];
      v2->normal[2] += n[2];
    }

    for (i = 0; i < model->header.numVertices; i++) {
      float dot, invLen;

      dot = v[i].normal[0] * v[i].normal[0] +
            v[i].normal[1] * v[i].normal[1] +
            v[i].normal[2] * v[i].normal[2];
      invLen = 1.f / (float)sqrt(dot);
      v[i].normal[0] *= invLen;
      v[i].normal[1] *= invLen;
      v[i].normal[2] *= invLen;
    }
  }
}


/* Return 2x the area of a 2D triangle.  Return a "double" so
   there is less chance of precision issues since we are
   generally interested in the sign of the area. */
static double
area2(float *v0,
      float *v1,
      float *v2)
{
  /* An equivalent algebraic expression for twice the area of a
     2D triangle is:
     
       v0[0]*v1[1] - v1[0]*v0[1] + 
       v1[0]*v2[1] - v2[0]*v1[1] + 
       v2[0]*v0[1] - v0[0]*v2[1]

     However this is 6 multiplies and 5 add/subtracs rather
     than the area2 implementations 2 multiplies and 5
     add/subtracts.  Moreover, the chosen implementation is
     less prone to overflows. */
  return (v1[0]-v0[0])*(v2[1]-v0[1]) - (v2[0]-v0[0])*(v1[1]-v0[1]);
}

/* Some models that I have encountered appear to have flipped normals.
   The Bobafett and Grey Alien models are examples of such models.
   This heuristic tries to determine when a model has bogus normals
   that should be flipped.  The algorithm is to sample each triangle
   on a pseudo-random (modulo 17) frame and test the area of the
   triangle flattened into the Z=0 plane (in model space) with the
   summed vertex normal Z components.  If the area and the normal
   component sum have the same sign, that is considered an indication that
   the normals for the model need to be flipped.  If more triangles
   appear to need flipping than not, then flip all the normals in
   the model.  Note that if a model doesn't backface cull properly,
   this heuristic would not work, but the facingness of the polygons
   seems reliable for the MD2 models that I've encountered. */
static int
flipNormalsIfProbablyNeeds(Md2Model *model)
{
  int lovesMe = 0, lovesMeNot = 0;
  unsigned int frame = 0;
  double a;
  float nz;
  int i, j;

  /* Sample each triangle on pseudo-random frames. */
  for (i = 0; i < model->header.numTriangles; i++, frame += 17) {
    Md2Frame *f = &model->frames[frame % model->header.numFrames];
    Md2Triangle *t = &model->triangles[i % model->header.numTriangles];
    Md2TriangleVertex *v0, *v1, *v2;

    v0 = &f->vertices[t->vertexIndices[0]];
    v1 = &f->vertices[t->vertexIndices[1]];
    v2 = &f->vertices[t->vertexIndices[2]];
    /* Compute the area of the polygon (in model space) projected
       onto the Z=0 plane. */
    a = area2(v0->vertex, v1->vertex, v2->vertex);
    /* Sum the Z components of the vertex normals. */
    nz = v0->normal[2] + v1->normal[2] + v2->normal[2];
    /* Don't count cases where the polygon area or normal Z sum are zero.
       Count cases where the polygon area and normal Z sum have opposite
       signs as an indication that we don't need to flip normals, but when
       the signs are the same, count that as a sign that we may need to
       flip normals. */
    if (a > 0) {
      if (nz > 0) {
        lovesMeNot++;
      } else if (nz < 0) {
        lovesMe++;
      }
    } else if (a < 0) {
      if (nz > 0) {
        lovesMe++;
      } else if (nz < 0) {
        lovesMeNot++;
      }
    }
  }

  /* If more normals seem wrong than not wrong... */
  if (lovesMeNot > lovesMe) {
    /* Then flip all the normals. */
    for (i = 0; i < model->header.numFrames; i++) {
      for (j = 0; j < model->header.numVertices; j++) {
        model->frames[i].vertices[j].normal[0] *= -1;
        model->frames[i].vertices[j].normal[1] *= -1;
        model->frames[i].vertices[j].normal[2] *= -1;
      }
    }
    printf("md2shader: flipped normals for \"%s\"\n",
      model->filename);
  }
  return 0;
}

Md2Model *
md2ReadModel(const char *filename)
{
  FILE *file;
  Md2Model *model;
  unsigned char buffer[MD2_MAX_FRAMESIZE];
  int zeroLightNormalIndexCount = 0;
  int i;
  
  model = (Md2Model *) malloc(sizeof(Md2Model));
  if (!model) {
    return NULL;
  }
  
  file = fopen(filename, "rb");
  if (!file) {
    free(model);
    return 0;
  }
 
  /* initialize model and read header */
  memset(model, 0, sizeof(Md2Model));
  fread(&model->header, sizeof(Md2Header), 1, file);

  if (model->header.magic != (int) (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')) {
    fclose(file);
    free(model);
    return 0;
  }

  model->filename = strdup(filename);

  /* read texture coordinates */
  fseek(file, model->header.offsetTexCoords, SEEK_SET);
  if (model->header.numTexCoords > 0) {
    model->texCoords = (Md2TextureCoordinate *)
      malloc(sizeof(Md2TextureCoordinate) * model->header.numTexCoords);
    if (!model->texCoords) {
      md2FreeModel(model);
      return 0;
    }
    
    fread(model->texCoords, sizeof(Md2TextureCoordinate),
      model->header.numTexCoords, file);
  }
  
  /* Read triangles. */
  fseek(file, model->header.offsetTriangles, SEEK_SET);
  if (model->header.numTriangles > 0) {
    model->triangles = (Md2Triangle *)
      malloc(sizeof(Md2Triangle) * model->header.numTriangles);
    if (!model->triangles) {
      md2FreeModel(model);
      return 0;
    }
    
    fread(model->triangles, sizeof(Md2Triangle),
      model->header.numTriangles, file);
  }
  
  /* Read alias frames. */
  fseek(file, model->header.offsetFrames, SEEK_SET);
  if (model->header.numFrames > 0) {
    model->frames = (Md2Frame *) malloc(sizeof(Md2Frame) * model->header.numFrames);
    if (!model->frames) {
      md2FreeModel(model);
      return 0;
    }
    
    for (i = 0; i < model->header.numFrames; i++) {
      Md2AliasFrame *frame = (Md2AliasFrame *) buffer;
      int j;
      
      model->frames[i].vertices = (Md2TriangleVertex *)
        malloc(sizeof(Md2TriangleVertex) * model->header.numVertices);
      if (!model->frames[i].vertices) {
        md2FreeModel(model);
        return 0;
      }
      
      fread(frame, 1, model->header.frameSize, file);
      strcpy(model->frames[i].name, frame->name);

      for (j = 0; j < model->header.numVertices; j++) {  
        int lightNormalIndex;

        /* Why is the Z coordinate negated?  I believe that
           there is a coordinate system handedness switch
           occuring. */
        model->frames[i].vertices[j].vertex[0] = (float)
          ((int) frame->alias_vertices[j].vertex[0]) * frame->scale[0]
          + frame->translate[0];
        model->frames[i].vertices[j].vertex[1] = (float)
          ((int) frame->alias_vertices[j].vertex[2]) * frame->scale[2]
          + frame->translate[2];
        model->frames[i].vertices[j].vertex[2] = -1*
          ((float) ((int) frame->alias_vertices[j].vertex[1]) * frame->scale[1]
                        + frame->translate[1]);
        
        lightNormalIndex = frame->alias_vertices[j].lightNormalIndex;
        if (lightNormalIndex == 0) {
          zeroLightNormalIndexCount++;
        }
        model->frames[i].vertices[j].normal[0] =
          md2VertexNormals[lightNormalIndex][0];
        model->frames[i].vertices[j].normal[1] =
          md2VertexNormals[lightNormalIndex][2];
        model->frames[i].vertices[j].normal[2] =
          -md2VertexNormals[lightNormalIndex][1];
      }
    }

    if (model->header.numFrames * model->header.numVertices == zeroLightNormalIndexCount) {
      printf("md2shader: bogus normal indices detected in \"%s\"\n",
        model->filename);
      generateAutomaticNormals(model);
    }
  }

  fclose(file);

  flipNormalsIfProbablyNeeds(model);
  
  return model;
}

#if 0
Md2Model *md2ReadModel(const char *filename)
{
  FILE *file;
  Md2Model *model;
  unsigned char buffer[MD2_MAX_FRAMESIZE];
  int i;
  
  model = (Md2Model *) malloc(sizeof(Md2Model));
  if (!model) {
    return NULL;
  }
  
  file = fopen(filename, "rb");
  if (!file) {
    free(model);
    return 0;
  }
 
  /* initialize model and read header */
  memset(model, 0, sizeof(Md2Model));
  fread(&model->header, sizeof(Md2Header), 1, file);

  if (model->header.magic != (int) (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')) {
    fclose(file);
    free(model);
    return 0;
  }

  model->filename = strdup(filename);
  
  /* read texture coordinates */
  fseek(file, model->header.offsetTexCoords, SEEK_SET);
  if (model->header.numTexCoords > 0) {
    model->texCoords = (Md2TextureCoordinate *)
      malloc(sizeof(Md2TextureCoordinate) * model->header.numTexCoords);
    if (!model->texCoords) {
      md2FreeModel(model);
      return 0;
    }
    
    fread(model->texCoords, sizeof(Md2TextureCoordinate),
      model->header.numTexCoords, file);
  }
  
  /* read triangles */
  fseek(file, model->header.offsetTriangles, SEEK_SET);
  if (model->header.numTriangles > 0) {
    model->triangles = (Md2Triangle *)
      malloc(sizeof(Md2Triangle) * model->header.numTriangles);
    if (!model->triangles) {
      md2FreeModel(model);
      return 0;
    }
    
    fread(model->triangles, sizeof(Md2Triangle),
      model->header.numTriangles, file);
  }
  
  /* read alias frames */
  fseek(file, model->header.offsetFrames, SEEK_SET);
  if (model->header.numFrames > 0) {
    model->frames = (Md2Frame *) malloc(sizeof(Md2Frame) * model->header.numFrames);
    if (!model->frames) {
      md2FreeModel(model);
      return 0;
    }
    
    for (i = 0; i < model->header.numFrames; i++) {
      Md2AliasFrame *frame = (Md2AliasFrame *) buffer;
      int j;
      
      model->frames[i].vertices = (Md2TriangleVertex *)
        malloc(sizeof(Md2TriangleVertex) * model->header.numVertices);
      if (!model->frames[i].vertices) {
        md2FreeModel(model);
        return 0;
      }
      
      fread(frame, 1, model->header.frameSize, file);
      strcpy(model->frames[i].name, frame->name);
      for (j = 0; j < model->header.numVertices; j++) {
        /* Why is the Z coordinate negated?  I believe that
           there is a coordinate system handedness switch
           occuring. */
        model->frames[i].vertices[j].vertex[0] = (float)
          ((int) frame->alias_vertices[j].vertex[0]) * frame->scale[0]
          + frame->translate[0];
        model->frames[i].vertices[j].vertex[1] = (float)
          ((int) frame->alias_vertices[j].vertex[2]) * frame->scale[2]
          + frame->translate[2];
        model->frames[i].vertices[j].vertex[2] = -1*
          ((float) ((int) frame->alias_vertices[j].vertex[1]) * frame->scale[1]
                        + frame->translate[1]);
        
        model->frames[i].vertices[j].normal[0] =
          md2VertexNormals[frame->alias_vertices[j].lightNormalIndex][0];
        model->frames[i].vertices[j].normal[1] =
          md2VertexNormals[frame->alias_vertices[j].lightNormalIndex][2];
        model->frames[i].vertices[j].normal[2] =
          -md2VertexNormals[frame->alias_vertices[j].lightNormalIndex][1];
      }
    }
  }

  fclose(file);
  
  return model;
}
#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -