📄 3dsload.cpp
字号:
///////////////////////////////////////////////////////////////////////////////////////////////// 3dsload.cpp 2001.12.13 Edited by JiangZibin
// 3D Studio to OpenGL Converter // 3D Studio object reader///////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <setjmp.h>#include <assert.h>
#include <math.h>
#include "3dsload.h"
#ifndef PI
#define PI 3.1415926
#endif
jmp_buf EnvState;FILE *InFile=NULL;H3dsScene *Scene=NULL;
M3DSObject *Obj3DS=NULL;
int doublesided=0;
int alphablending=0;int defaultmaterialwarning=0;
//该模块用到的全局变量
/* Module Globals */
unsigned int NumVertex=0;
OUTGL_VERTEX *VertexList; //顶点集(顶点及法向量列表)
unsigned int NumPolyLists=0;
//多边形列表,其中的顶点将用下标表示,OpenGL将这个数组视为一个顶点数组(Vertec array).这里存储的
//法向量是每一个顶点的法向量,而不是多边形的法向量。
POLYGONLIST *PolyList;
TRIANGLENODE **CommonTriangleList; //相关三角形列表
//查找
static int FindMaterialIndex(char *);
//计算面的法向量
static int CalculateSurfaceNormal(OUTGL_SURFACE *);
//计算2个面的夹角的余弦
static GLfloat cos_angle(OUTGL_SURFACE *, OUTGL_SURFACE *);
//计算一个三角形在某个顶点处的夹角
static GLfloat angle(OUTGL_SURFACE *, GLuint);
//计算2个三维点之间的距离
static double distance(OUTGL_VERTEX *, OUTGL_VERTEX *);
//建立每个顶点的相关三角形列表
static void LinkVerticesToPolygons();
//计算每个顶点的法向量
static void CalculateVertexNormals();
//建立三角形列表,每个三角形中记载其顶点在顶点列表中的下标
static void MakeTriangleIndexList();
#define JOB_DONE 0 //表明是否完成
#define DEGENERATE_SURFACE 1 //非退化三角形
//读取3DS文件到自定义的图形数据结构中static GLfloat OutputAbs(GLfloat f);
static GLfloat OutputMax(GLfloat a,GLfloat b);
/********************************************************/
/* Finds the index of the material in question */
//查找材质的下标
int FindMaterialIndex(char *name) {
unsigned int n;
for (n=0; n<NumPolyLists; n++) {
if (strcmp(name, PolyList[n].name) == 0) {
return n;
}
}
return -1;
}
//往顶点列表中加入一个顶点Edited by JiangZibin
/* Adds a vertex to the vertex list */
void OutGLAddVertex(OUTGL_VERTEX v) {
void *mem;
if ((mem = realloc(VertexList, sizeof(OUTGL_VERTEX) * (NumVertex+1))) == 0) {
exit(-1);
}
VertexList = (OUTGL_VERTEX *)mem;
VertexList[NumVertex] = v;
/*
* Add a slot into the array we're building to keep track of which
* triangles are part of which vertices, which will be used to build
* vertex normals.
*/
/*向正在创建的数组中加入一个“槽”,使之能够记载哪些三角形拥有共同
的某个顶点,加入这项内容的目的是用来创建顶点的法向量*/
if ((mem = realloc(CommonTriangleList, sizeof(TRIANGLENODE **) * (NumVertex+1))) == 0) {
exit(-1);
}
CommonTriangleList = (TRIANGLENODE **)mem;
CommonTriangleList[NumVertex] = 0;
NumVertex++;
}
//加入一个多边形的材质
/* Add a polygon of the material */
void OutGLAddPolygon(OUTGL_TRIANGLE tri, char *MaterialName) {
void *mem;
int MatNum;
POLYGONLIST *list;
OUTGL_SURFACE TempSurface;
TempSurface.p0 = tri.p0;
TempSurface.p1 = tri.p1;
TempSurface.p2 = tri.p2;
//忽略退化多边形
/* Ignore degenerate polygons */
if (CalculateSurfaceNormal(&TempSurface) == DEGENERATE_SURFACE)
return;
//查找材质在多边形列表中的下标
/* Find the polygon list index we belong to */
MatNum = FindMaterialIndex(MaterialName);
list = &PolyList[MatNum];
//将一个新三角形列表中
/* Attach a new triangle to the polygon list */
if ((mem = realloc(list->TriangleIndexList,
sizeof(OUTGL_SURFACE) * (list->NumTriangles+1))) == 0) {
exit(-1);
}
list->TriangleIndexList = (OUTGL_SURFACE *)mem;
list->TriangleIndexList[list->NumTriangles].p0 = tri.p0;
list->TriangleIndexList[list->NumTriangles].p1 = tri.p1;
list->TriangleIndexList[list->NumTriangles].p2 = tri.p2;
CalculateSurfaceNormal(&list->TriangleIndexList[list->NumTriangles]);
list->NumTriangles++;
if (PolyList[MatNum].doublesided) {
//将一个新的三角形加到多边形列表中
/* Attach a new triangle to the polygon list */
if ((mem = realloc(list->TriangleIndexList,
sizeof(OUTGL_SURFACE) * (list->NumTriangles+1))) == 0) {
exit(-1);
}
list->TriangleIndexList = (OUTGL_SURFACE *)mem;
list->TriangleIndexList[list->NumTriangles].p0 = tri.p0;
list->TriangleIndexList[list->NumTriangles].p1 = tri.p2;
list->TriangleIndexList[list->NumTriangles].p2 = tri.p1;
CalculateSurfaceNormal(&list->TriangleIndexList[list->NumTriangles]);
list->NumTriangles++;
}
}
//给模型加入材质
/* Add a material to the model */
void OutGLAddMaterial(OUTGL_RGB ambient, OUTGL_RGB diffuse, OUTGL_RGB specular, char *name, char *texname, int doublesided) {
void *mem;
if ((mem =realloc(PolyList, sizeof(POLYGONLIST) * (NumPolyLists+1))) == 0) {
exit(-1);
}
PolyList = (POLYGONLIST *)mem;
strcpy(PolyList[NumPolyLists].name, name);
strcpy(PolyList[NumPolyLists].texturename, texname);
PolyList[NumPolyLists].ambient = ambient;
PolyList[NumPolyLists].diffuse = diffuse;
PolyList[NumPolyLists].specular = specular;
PolyList[NumPolyLists].doublesided = doublesided;
PolyList[NumPolyLists].NumTriangles = 0;
PolyList[NumPolyLists].TriangleIndexList = 0;
NumPolyLists++;
}
/*********************************************************************************************/
/*
* Performs some model clean-up that can only be done once we have the entire
* model. This includes finding the vertex normals (instead of the polygon
* normals we already calculated), centering the object around 0,0,0, crunching
* the vertex data into vertex arrays for OpenGL, and compiling the display list.
* Note that this code finds the vertex normals by first calculating the angle
* that each polygon shares with that vertex. It then averages all the normals
* together, each one weighted on how much of an angle they contribute to the
* vertex. Also, if the user specified a threshold for normals so that surface
* edges are preserved, we have to add a new vertex to the list with the same
* X,Y,Z coordinates but different normal coordinates. Thus, edge preservation
* may look good, but it's expensive.
********************************************************************************************/
/*OutGLFinishModel函数在读入整个模型之后对模型的数据进行一些整理工作,其内容
包括:查找顶点的法向量(而不是前面求出的法向量),将模型居中(即定位于(0,0,0)位
置),顶点数据放入数组中,并且编译显示列表。注意,在求顶点法向量时,所用的方
法是:先求出该顶点周围的所有面的法向量,在根据每个面对顶点法向量贡献的大小
加权平均得到。*/
void OutGLFinishModel()
{
LinkVerticesToPolygons();
CalculateVertexNormals();
MakeTriangleIndexList();
}
//Build links from the vertices back to the polygon lists
//建立每个顶点的相关三角形列表,使用的方法是:将三角形加入
//其每一个顶点的三角形列表中,处理完所有的三角形后,就建立
//了所有顶点的三角形列表
static void LinkVerticesToPolygons() {
unsigned int i,j;
OUTGL_SURFACE *ThisTriangle;
OUTGL_TRIANGLENODE *TempPtr;
for (i=0; i<NumPolyLists; i++) {
for (j=0; j<(unsigned int)PolyList[i].NumTriangles; j++) {
/* For each triangle, insert a pointer to the triangle */
/* into a linked list hanging off of each of the three */
/* vertices it touches. */
//将每个三角形加入其三个顶点的相关三角形列表
ThisTriangle = &PolyList[i].TriangleIndexList[j];
/* Point One */
TempPtr = (OUTGL_TRIANGLENODE *) malloc(sizeof(OUTGL_TRIANGLENODE));
TempPtr->triangle = ThisTriangle;
TempPtr->next = CommonTriangleList[ThisTriangle->p0];
CommonTriangleList[ThisTriangle->p0] = TempPtr;
/* Point Two */
TempPtr = (OUTGL_TRIANGLENODE *) malloc(sizeof(OUTGL_TRIANGLENODE));
TempPtr->triangle = ThisTriangle;
TempPtr->next = CommonTriangleList[ThisTriangle->p1];
CommonTriangleList[ThisTriangle->p1] = TempPtr;
/* Point Three */
TempPtr = (OUTGL_TRIANGLENODE *) malloc(sizeof(OUTGL_TRIANGLENODE));
TempPtr->triangle = ThisTriangle;
TempPtr->next = CommonTriangleList[ThisTriangle->p2];
CommonTriangleList[ThisTriangle->p2] = TempPtr;
}
}
}
/*
* Generate the per-vertex normals. Make new vertices if two surfaces are
* more than a threshold angle apart. Thus, a cube would have six surface
* normals but if the threshold was 90 degrees, it would have 24
* vertex/normal pairs.
*/
//计算每个顶点的法向量
//计算每个顶点的法向量有多种方法,可以简单地将周围三角形的法向量直接相加
//得到,也可以由其周围三角形加权平均得到。权值的取法也有多种,可以是每个
//三角形的面积,也可以是每个三角形在该点处的夹角。本例就是用每个三角形在
//该点处的夹角作为权值的方法来求顶点得方向量
static void CalculateVertexNormals()
{
unsigned int i;
GLfloat VertexAngle;
GLfloat TotalAngles;
OUTGL_TRIANGLENODE *ThisSurface;
GLfloat sum, magnitude;
ThisSurface=NULL;
//计算每个顶点的法向量
/* Calculate a normal for every vertex */
for (i=0; i<NumVertex; i++) {
VertexList[i].nx = VertexList[i].ny = VertexList[i].nz = 0;
ThisSurface = CommonTriangleList[i];
TotalAngles = 0;
/* Traverse the linked list of triangles that touch this vertex */
//遍历该顶点的所有相关三角形
while (ThisSurface) {
/*
* Since it's less then the threshold angle, calculate the angle that this
* polygon shares with the vertex
*/ VertexAngle = angle(ThisSurface->triangle, i);
VertexList[i].nx += ThisSurface->triangle->n0 * VertexAngle;
VertexList[i].ny += ThisSurface->triangle->n1 * VertexAngle;
VertexList[i].nz += ThisSurface->triangle->n2 * VertexAngle;
TotalAngles += VertexAngle;
ThisSurface = ThisSurface->next;
}
/* Calculate the normal for this vertex */
VertexList[i].nx /= TotalAngles;
VertexList[i].ny /= TotalAngles;
VertexList[i].nz /= TotalAngles;
/* Make it a unit vector again */
sum = ((VertexList[i].nx * VertexList[i].nx) +
(VertexList[i].ny * VertexList[i].ny) +
(VertexList[i].nz * VertexList[i].nz));
magnitude = 1/(GLfloat)sqrt(sum);
VertexList[i].nx *= magnitude;
VertexList[i].ny *= magnitude;
VertexList[i].nz *= magnitude;
}
}
/*********************************************************************************************
* Make a list of triangle indexes for OpenGL's vertex arrays
* This does not produce the more optimized TRIANGLE_STRIPS
*********************************************************************************************/
//建立三角形列表,对于每个三角形记载其每个顶点在顶点列表中的下标
static void MakeTriangleIndexList() {
unsigned int i,j;
for (i=0; i<NumPolyLists; i++) {
PolyList[i].CrunchedTriangleIndexList =(OUTGL_TRIANGLE*)malloc(sizeof(OUTGL_TRIANGLE)\
* (PolyList[i].NumTriangles+1));
for (j=0; j<(unsigned int)PolyList[i].NumTriangles; j++) {
PolyList[i].CrunchedTriangleIndexList[j].p0 = \
PolyList[i].TriangleIndexList[j].p0;
PolyList[i].CrunchedTriangleIndexList[j].p1 =\
PolyList[i].TriangleIndexList[j].p1;
PolyList[i].CrunchedTriangleIndexList[j].p2 = \
PolyList[i].TriangleIndexList[j].p2;
}
}
}
//计算三角形法向量函数
/**********************************************************************************************
* Returns the normal for the triangle (not a vertex normal,
* but a surface normal)
**********************************************************************************************/
//计算面的法向量:顺序地从三角形中找出两条有向边,计算其叉积
//得到的结果就是三角形的法向量,一般情况下还要对结果进行单位化
int CalculateSurfaceNormal(OUTGL_SURFACE *ThisTriangle)
{
OUTGL_NORMAL ThisNormal;
GLfloat Ai, Aj, Ak;
GLfloat Bi, Bj, Bk;
GLfloat sum, magnitude;
/*
Calculate the polygon's normal using a cross product.
A,B are vectors; i,j,k are their xyz components
*/
//求出三角形的2条有向边向量A,B,i,j,k分别是其3个坐标分量
Ai = VertexList[ThisTriangle->p1].x - VertexList[ThisTriangle->p0].x;
Aj = VertexList[ThisTriangle->p1].y - VertexList[ThisTriangle->p0].y;
Ak = VertexList[ThisTriangle->p1].z - VertexList[ThisTriangle->p0].z;
Bi = VertexList[ThisTriangle->p2].x - VertexList[ThisTriangle->p1].x;
Bj = VertexList[ThisTriangle->p2].y - VertexList[ThisTriangle->p1].y;
Bk = VertexList[ThisTriangle->p2].z - VertexList[ThisTriangle->p1].z;
//用叉乘来计算多边形的法向量
/* Find the cross product */
ThisNormal.n0 = Aj*Bk - Ak*Bj;
ThisNormal.n1 = Ak*Bi - Ai*Bk;
ThisNormal.n2 = Ai*Bj - Aj*Bi;
//计算法向量模的平方
/* Make the normal a unit vector */
sum = (ThisNormal.n0 * ThisNormal.n0) +
(ThisNormal.n1 * ThisNormal.n1) +
(ThisNormal.n2 * ThisNormal.n2);
/*
* This will be zero only if the cross product's
* normal has all zero components. That won't happen
* as long as the triangle has three separate vertices.
* Otherwise, the next step would be to divide 1 by the
* square root of zero... not a pretty site.
*/
//只要三角形3个顶点不全部在一点上,法向量的模就不等于0。
if (sum == 0) //否则为退化三角形
return DEGENERATE_SURFACE;
magnitude = 1/(GLfloat)sqrt(sum);
//向量单位化
ThisNormal.n0 *= magnitude;
ThisNormal.n1 *= magnitude;
ThisNormal.n2 *= magnitude;
//将结果记入三角形中
ThisTriangle->n0 = ThisNormal.n0;
ThisTriangle->n1 = ThisNormal.n1;
ThisTriangle->n2 = ThisNormal.n2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -