//读入顶点索引 void ReadVertexIndices(t3DObject pObject, tChunk pPreviousChunk) { int index = 0; //读入该对象中面的数目 pPreviousChunk.bytesRead += fread(ref pObject.numOfFaces, 2, m_FilePointer); //分配所有的储存空间,并初始化结构 pObject.pFaces = new tFace[pObject.numOfFaces]; //遍历对象中所有的面 for (int i = 0; i < pObject.numOfFaces; i++) { pObject.pFaces[i] = new tFace(); for (int j = 0; j < 4; j++) { //读入当前对象的第一个点 pPreviousChunk.bytesRead += fread(ref index, 2, m_FilePointer); if (j < 3) { pObject.pFaces[i].vertIndex[j] = index; } } } }
//读入对象的材质名称 void ReadObjectMaterial(t3DModel pModel, t3DObject pObject, tChunk pPreviousChunk) { String strMaterial = ""; //用来保存对象的材质名称 int[] buffer = new int[50000]; //用来读入不需要的数据 //读入赋予当前对象的材质名称 pPreviousChunk.bytesRead += getStr(ref strMaterial); //遍历所有的纹理 for (int i = 0; i < pModel.numOfMaterials; i++) { //如果读入的纹理与当前纹理名称匹配 if (strMaterial.Equals(pModel.pMaterials[i].strName)) { //设置材质ID pObject.materialID = i; //判断是否是纹理映射,如果strFile是一个长度大于1的字符串,则是纹理 if (pModel.pMaterials[i].strFile.Length > 0) { //设置对象的纹理映射标志 pObject.bHasTexture = true; } break; } else { //如果该对象没有材质,则设置ID为-1 pObject.materialID = -1; } } pPreviousChunk.bytesRead += fread(ref buffer, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer); }
//读入对象的UV坐标 void ReadUVCoordinates(t3DObject pObject, tChunk pPreviousChunk) { //为了读入对象的UV坐标,首先需要读入数量,再读入具体的数据 //读入UV坐标的数量 pPreviousChunk.bytesRead += fread(ref pObject.numTexVertex, 2, m_FilePointer); //初始化保存UV坐标的数组 pObject.pTexVerts = new CVector2[pObject.numTexVertex]; //读入纹理坐标 pPreviousChunk.bytesRead += fread(ref pObject.pTexVerts, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer); }
//处理所有的文件中的对象信息 void ProcessNextObjectChunk(t3DModel pModel, t3DObject pObject, tChunk pPreviousChunk) { m_CurrentChunk = new tChunk(); //继续读入块的内容直至本子块结束 while (pPreviousChunk.bytesRead < pPreviousChunk.length) { ReadChunk(m_CurrentChunk); if (m_CurrentChunk.ID == FileHead.OBJECT_MESH)//正读入的是一个新块 { //使用递归函数调用,处理该新块 ProcessNextObjectChunk(pModel, pObject, m_CurrentChunk); } else if (m_CurrentChunk.ID == FileHead.OBJECT_VERTICES)//读入的是对象顶点 { ReadVertices(pObject, m_CurrentChunk); } else if (m_CurrentChunk.ID == FileHead.OBJECT_FACES)//读入的是对象的面 { ReadVertexIndices(pObject, m_CurrentChunk); } else if (m_CurrentChunk.ID == FileHead.OBJECT_MATERIAL)//读入的是对象的材质名称 { //该块保存了对象材质的名称,可能是一个颜色,也可能是一个纹理映射。 //同时在该块中也保存了纹理对象所赋予的面 //下面读入对象的材质名称 ReadObjectMaterial(pModel, pObject, m_CurrentChunk); } else if (m_CurrentChunk.ID == FileHead.OBJECT_UV)//读入对象的UV纹理坐标 { ReadUVCoordinates(pObject, m_CurrentChunk); } else { //掠过不需要读入的块 while (m_CurrentChunk.bytesRead != m_CurrentChunk.length) { int[] b = new int[1]; m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer); } } //添加从最后块中读入的字节数 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead; } //当前快设置为前面的块 m_CurrentChunk = pPreviousChunk; }
//读入对象的顶点 void ReadVertices(t3DObject pObject, tChunk pPreviousChunk) { //在读入实际的顶点之前,首先必须确定需要读入多少个顶点。 //读入顶点的数目 pPreviousChunk.bytesRead += fread(ref pObject.numOfVerts, 2, m_FilePointer); //分配顶点的储存空间,然后初始化结构体 pObject.pVerts = new CVector3[pObject.numOfVerts]; //读入顶点序列 pPreviousChunk.bytesRead += fread(ref pObject.pVerts, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer); //因为3DMax的模型Z轴是指向上的,将y轴和z轴翻转——y轴和z轴交换,再把z轴反向 //遍历所有的顶点 //for (int i = 0; i < pObject.numOfVerts; i++) //{ // float fTempY = pObject.pVerts[i].y; // pObject.pVerts[i].y = pObject.pVerts[i].z; // pObject.pVerts[i].z = -1 * fTempY; //} }
//模型单位归一化 protected void LoadBox() { boxMax = new CVector3(); boxMin = new CVector3(); boxMax.x = float.MinValue; boxMax.y = float.MinValue; boxMax.z = float.MinValue; boxMin.x = float.MaxValue; boxMin.y = float.MaxValue; boxMin.z = float.MaxValue; //获取模型的边界值 for (int i = 0; i < model.numOfObjects; i++) { t3DObject pObject = model.pObject[i]; for (int j = 0; j < pObject.numOfVerts; j++) { float x = pObject.pVerts[j].x / 1000; float y = pObject.pVerts[j].y / 1000; float z = pObject.pVerts[j].z / 1000; if (boxMin.x > x) { boxMin.x = x; } if (boxMin.y > y) { boxMin.y = y; } if (boxMin.z > z) { boxMin.z = z; } if (boxMax.x < x) { boxMax.x = x; } if (boxMax.y < y) { boxMax.y = y; } if (boxMax.z < z) { boxMax.z = z; } } } //计算模型的宽度、高度和深度 w = (boxMax.x - boxMin.x); // OutputAbs(boxMax.x) + OutputAbs(boxMin.x); h = (boxMax.y - boxMin.y); // OutputAbs(boxMax.y) + OutputAbs(boxMin.y); d = (boxMax.z - boxMin.z); // OutputAbs(boxMax.z) + OutputAbs(boxMin.z); //计算模型的中心 cx = (boxMax.x + boxMin.x) / 2.0; cy = (boxMax.y + boxMin.y) / 2.0; cz = (boxMax.z + boxMin.z) / 2.0; //计算归一化所需的缩放比例 scale = OutputMax(OutputMax(w, h), d); scale = 4.0f / scale;//根据模型实际来决定 //导入模型的单位为mm,将单位转换为m for (int i = 0; i < model.numOfObjects; i++) { t3DObject pObject = model.pObject[i]; for (int j = 0; j < pObject.numOfVerts; j++) { pObject.pVerts[j].x /= 1000; pObject.pVerts[j].y /= 1000; pObject.pVerts[j].z /= 1000; //pObject.pVerts[j].x -= (float)cx; //pObject.pVerts[j].y -= (float)cy; //pObject.pVerts[j].z -= (float)cz; } } }
//下面的这些函数主要用来计算顶点的法向量,顶点的法向量主要用来计算光照 //计算对象的法向量 private void ComputeNormals(t3DModel pModel) { CVector3 vVector1, vVector2, vNormal; CVector3[] vPoly; vPoly = new CVector3[3]; //如果模型中没有对象,则返回 if (pModel.numOfObjects <= 0) { return; } //遍历模型中所有的对象 for (int index = 0; index < pModel.numOfObjects; index++) { //获得当前对象 t3DObject pObject = pModel.pObject[index]; //分配需要的空间 CVector3[] pNormals = new CVector3[pObject.numOfFaces]; CVector3[] pTempNormals = new CVector3[pObject.numOfFaces]; pObject.pNormals = new CVector3[pObject.numOfVerts]; //遍历对象所有面 for (int i = 0; i < pObject.numOfFaces; i++) { vPoly[0] = pObject.pVerts[pObject.pFaces[i].vertIndex[0]]; vPoly[1] = pObject.pVerts[pObject.pFaces[i].vertIndex[1]]; vPoly[2] = pObject.pVerts[pObject.pFaces[i].vertIndex[2]]; //计算面的法向量 vVector1 = Vector(vPoly[0], vPoly[2]); vVector2 = Vector(vPoly[2], vPoly[1]); vNormal = Cross(vVector1, vVector2); pTempNormals[i] = vNormal; vNormal = Normalize(vNormal); pNormals[i] = vNormal; } //下面求顶点的法向量:顶点法向量是以该点为顶点的所有三角形法向量之和 CVector3 vSum = new CVector3(); vSum.x = 0; vSum.y = 0; vSum.z = 0; int shared = 0; //遍历所有的顶点 for (int i = 0; i < pObject.numOfVerts; i++) { for (int j = 0; j < pObject.numOfFaces; j++) { if (pObject.pFaces[j].vertIndex[0] == i || pObject.pFaces[j].vertIndex[1] == i || pObject.pFaces[j].vertIndex[2] == i) { vSum = AddVector(vSum, pTempNormals[j]); shared++; } } pObject.pNormals[i] = DivideVectorByScaler(vSum, (float)(-1 * shared)); //规范化最后的顶点法向量 pObject.pNormals[i] = Normalize(pObject.pNormals[i]); vSum.x = 0; vSum.y = 0; vSum.z = 0; shared = 0; } } }
//读出3ds文件的主要部分 void ProcessNextChunk(t3DModel pModel, tChunk pPreviousChunk) { t3DObject newObject = new t3DObject(); int version = 0; m_CurrentChunk = new tChunk(); // 下面每读一个新块,都要判断一下块的ID,如果该块是需要的读入的,则继续进行 // 如果是不需要读入的块,则略过 // 继续读入子块,直到达到预定的长度 while (pPreviousChunk.bytesRead < pPreviousChunk.length) { //读入下一个块 ReadChunk(m_CurrentChunk); //判断ID号 if (m_CurrentChunk.ID == FileHead.VERSION) { m_CurrentChunk.bytesRead += fread(ref version, m_CurrentChunk.length - m_CurrentChunk.bytesRead, m_FilePointer); // 如果文件版本号大于3,给出一个警告信息 if (version > 3) { Debug.WriteLine("Warning: This 3DS file is over version 3 so it may load incorrectly"); } } else if (m_CurrentChunk.ID == FileHead.OBJECTINFO) { //读入下一个块 ReadChunk(m_TempChunk); //获得网络的版本号 m_TempChunk.bytesRead += fread(ref version, m_TempChunk.length - m_TempChunk.bytesRead, m_FilePointer); //增加读入的字节数 m_CurrentChunk.bytesRead += m_TempChunk.bytesRead; //进入下一个块 ProcessNextChunk(pModel, m_CurrentChunk); } else if (m_CurrentChunk.ID == FileHead.MATERIAL)//材质信息 { //材质的数目递增 pModel.numOfMaterials++; //在纹理链表中添加一个空白纹理结构 pModel.pMaterials.Add(new tMaterialInfo()); //进入材质装入函数 ProcessNextMaterialChunk(pModel, m_CurrentChunk); } else if (m_CurrentChunk.ID == FileHead.OBJECT)//对象的名称 { //对象数目递增 pModel.numOfObjects++; //添加一个新的tObject节点到对象的链表中 pModel.pObject.Add(new t3DObject()); //获得并保存对象的名称,然后增加读入的字节数 m_CurrentChunk.bytesRead += getStr(ref pModel.pObject[pModel.numOfObjects - 1].strName); //进入其余对象信息的读入 ProcessNextObjectChunk(pModel, pModel.pObject[pModel.numOfObjects - 1], m_CurrentChunk); } else { // 跳过关键帧块的读入,增加需要读入的字节数 EDITKEYFRAME // 跳过所有忽略的块的内容的读入,增加需要读入的字节数 while (m_CurrentChunk.bytesRead != m_CurrentChunk.length) { int[] b = new int[1]; m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer); } } //添加从最后块中读入的字节数 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead; } //当前快设置为前面的块 m_CurrentChunk = pPreviousChunk; }
public void DrawModel() //画出模型 { GL.glPushMatrix(); GL.glRotatef(270.0f, 1.0f, 0.0f, 0.0f); GL.glEnable(GL.GL_BLEND); GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); GL.glDepthMask((byte)Traparent); //画太阳 //画读入的模型 #region for (int i = 0; i < this.model.numOfObjects; i++) { if (this.model.pObject.Count <= 0) { break; } t3DObject pObject = this.model.pObject[i]; // if (!floors.Contains(pObject.strName.Substring(1, 1))) continue; if (pObject.bHasTexture) { GL.glEnable(GL.GL_TEXTURE_2D); GL.glColor4ub(255, 255, 255, (byte)Transparency); GL.glBindTexture(GL.GL_TEXTURE_2D, this.g_Texture[pObject.materialID]); } else { GL.glDisable(GL.GL_TEXTURE_2D); GL.glColor3ub(255, 255, 255); } GL.glBegin(GL.GL_TRIANGLES); for (int j = 0; j < pObject.numOfFaces; j++) { for (int whichVertex = 0; whichVertex < 3; whichVertex++) { int index = pObject.pFaces[j].vertIndex[whichVertex]; GL.glNormal3f(pObject.pNormals[index].x, pObject.pNormals[index].y, pObject.pNormals[index].z); if (pObject.bHasTexture) { if (pObject.pTexVerts != null) { GL.glTexCoord2f(pObject.pTexVerts[index].x, pObject.pTexVerts[index].y); } } else { if (this.model.pMaterials.Count != 0 && pObject.materialID >= 0) { int[] color = this.model.pMaterials[pObject.materialID].color; GL.glColor3ub((byte)color[0], (byte)color[1], (byte)color[2]); } } GL.glVertex3f(pObject.pVerts[index].x * (float)scale, pObject.pVerts[index].y * (float)scale, pObject.pVerts[index].z * (float)scale); } } GL.glEnd(); GL.glDisable(GL.GL_TEXTURE_2D); } GL.glDisable(GL.GL_BLEND); GL.glPopMatrix(); #endregion }