首页 > 代码库 > SharpGL学习笔记(十八) 解析3ds模型并显示

SharpGL学习笔记(十八) 解析3ds模型并显示

 

笔者设想的3D仿真中的元件,是不可能都是“画”出来的。这样就玩复杂了,应该把任务分包出去,让善于制作模型的软件来制作三维模型,我们只需要解析并且显示它即可。

3dsmax制作三维模型的方便,快捷,专业,我想是没有人提反对意见的。它可以把制作好的模型导出为业界通用的3ds格式,如果你愿意的话,3ds格式也可以包含材质和uvw贴图坐标。这样的模型我们在opengl中导入后只用打光和显示,非常省事。

解析3ds格式比较复杂,不过读者可以拿来主义,直接用下面的代码就可以了。

代码已经加入了必要的注释,笔者就不罗嗦了。

 

源代码: SharpGLForm.cs

  1 using System;  2 using System.Collections.Generic;  3 using System.ComponentModel;  4 using System.Data;  5 using System.Drawing;  6 using System.Linq;  7 using System.Text;  8 using System.Windows.Forms;  9 using SharpGL; 10 using Model3D; 11 using System.IO; 12  13  14 namespace SharpGLWinformsApplication1 15 { 16     //原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/ 17     public partial class SharpGLForm : Form 18     { 19         private string configPath = AppDomain.CurrentDomain.BaseDirectory + "config"; 20         private H3DModel h3d; 21         private float rotation = 0.0f; 22         private bool isRotate = false; 23         private bool isLines = false; 24         private bool isFrontView = false; 25         private bool isLeftView = false; 26         private bool isTopView = false; 27         private bool isPerspective = true; 28         private float[] lightPos = new float[] { -1, -3, 1, 1 }; 29         private float[] lightSphereColor = new float[] { 0.2f, 0.5f, 0.8f }; 30         private IList<float[]> lightColor = new List<float[]>(); 31         private double[] lookatValue = http://www.mamicode.com/{ 1, 1, 2, 0, 0, 0, 0, 1, 0 }; 32  33         float[] no_mat = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };        // 无材质颜色 34         float[] mat_ambient = new float[] { 0.7f, 0.7f, 0.7f, 1.0f };   // 环境颜色 35         float[] mat_ambient_color = new float[] { 0.8f, 0.6f, 0.2f, 1.0f }; 36         float[] mat_diffuse = new float[] { 0.2f, 0.5f, 0.8f, 1.0f };   // 散射颜色 37         float[] no_shininess = new float[] { 0.0f };                    // 镜面反射指数为0 38         float[] mat_emission = new float[] { 0.3f, 0.2f, 0.3f, 0.0f };  // 发射光颜色 39         float[] high_shininess = new float[] { 100.0f };                // 镜面反射指数为100.0 40         float[] low_shininess = new float[] { 5.0f };                   // 镜面反射指数为5.0 41         float[] mat_specular = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };  // 镜面反射颜色 42  43         private IList<double[]> viewDefaultPos = new List<double[]>(); 44         public SharpGLForm() 45         { 46             InitializeComponent(); 47              48         } 49  50         private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e) 51         { 52             OpenGL gl = openGLControl.OpenGL; 53             gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); 54             gl.LoadIdentity(); 55             gl.Rotate(rotation, 0.0f, 1.0f, 0.0f); 56             drawGrid(gl); 57             draw3DSModel(gl); 58             if (isRotate) 59                 rotation += 3.0f; 60         } 61  62         private void draw3DSModel(OpenGL Gl) 63         { 64             Gl.PushMatrix(); 65             { 66                 //Gl.PixelStore(OpenGL.GL_UNPACK_ALIGNMENT, 4);   67                 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, mat_specular); 68                 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, mat_specular); 69                 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SPECULAR, no_mat); 70                 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, no_mat); 71                 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_EMISSION, no_mat); 72                 Gl.Scale(0.05, 0.05, 0.05); 73                 Gl.Translate(0, 0, 0); 74                 h3d.DrawModel(Gl,isLines); 75                 h3d.DrawBorder(Gl); 76             } 77             Gl.PushMatrix(); 78         } 79  80         private void setLightColor(OpenGL gl) 81         { 82             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightColor[0]); 83             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, lightColor[1]); 84             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, lightColor[2]); 85         } 86  87         private void openGLControl_OpenGLInitialized(object sender, EventArgs e) 88         { 89             OpenGL gl = openGLControl.OpenGL; 90  91             //四个视图的缺省位置 92             viewDefaultPos.Add(new double[] { 1, 1, 2, 0, 0, 0, 0, 1, 0 });     //透视 93             viewDefaultPos.Add(new double[] { 0, 0, 2, 0, 0, 0, 0, 1, 0 });     //前视  94             viewDefaultPos.Add(new double[] { 5, 0, 0, 0, 0, 0, 0, 1, 0 });     //左视 95             viewDefaultPos.Add(new double[] { 0, 13, 0, -1, 0, 0, 0, 1, 0 });   //顶视 96             lookatValue =http://www.mamicode.com/(double[])viewDefaultPos[0].Clone(); 97  98             lightColor.Add(new float[] { 1f, 1f, 1f, 1f });  //环境光(ambient light) 99             lightColor.Add(new float[] { 1f, 1f, 1f, 1f });  //漫射光(diffuse light)100             lightColor.Add(new float[] { 1f, 1f, 1f, 1f });  //镜面反射光(specular light)101 102             setLightColor(gl);103             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);104 105             gl.Enable(OpenGL.GL_LIGHTING);106             gl.Enable(OpenGL.GL_LIGHT0);107             gl.Enable(OpenGL.GL_NORMALIZE);108           109 110             gl.ClearColor(0, 0, 0, 0);111             h3d = H3DModel.FromFile(gl, "teport3.3DS");112             113 114             loadConfig();115             116         }117 118     119 120         private void openGLControl_Resized(object sender, EventArgs e)121         {122 123             OpenGL gl = openGLControl.OpenGL;124             gl.MatrixMode(OpenGL.GL_PROJECTION);125             gl.LoadIdentity();126             gl.Perspective(40.0f, (double)Width / (double)Height, 0.01, 100.0);127 128 129             gl.LookAt(lookatValue[0], lookatValue[1], lookatValue[2],130                 lookatValue[3], lookatValue[4], lookatValue[5],131                 lookatValue[6], lookatValue[7], lookatValue[8]);132             133             gl.MatrixMode(OpenGL.GL_MODELVIEW);134             updateLabInfo();135         }136 137       138         void drawGrid(OpenGL gl)139         {140              //关闭纹理和光照141             gl.Disable(OpenGL.GL_TEXTURE_2D);142             gl.Disable(OpenGL.GL_LIGHTING);143 144             //绘制过程145             gl.PushAttrib(OpenGL.GL_CURRENT_BIT);  //保存当前属性146             gl.PushMatrix();                        //压入堆栈147             gl.Translate(0f, -2f, 0f);148             gl.Color(0f, 0f, 1f);149 150             //在X,Z平面上绘制网格151             for (float i = -50; i <= 50; i += 1)152             {153                 //绘制线154                 gl.Begin(OpenGL.GL_LINES);155                 {156                     if (i == 0)157                         gl.Color(0f, 1f, 0f);158                     else159                         gl.Color(0f, 0f, 1f);160 161                     //X轴方向162                     gl.Vertex(-50f, 0f, i);163                     gl.Vertex(50f, 0f, i);164                     //Z轴方向 165                     gl.Vertex(i, 0f, -50f);166                     gl.Vertex(i, 0f, 50f);167 168                 }169                 gl.End();170             }171             gl.PopMatrix();172             gl.PopAttrib();173             gl.Enable(OpenGL.GL_LIGHTING);174         }175 176       177         void drawSphere(OpenGL gl,double radius,int segx,int segy,bool isLines)178         {179             gl.PushMatrix();180             gl.Translate(2f, 1f, 2f);181             var sphere = gl.NewQuadric();182             if (isLines)183                 gl.QuadricDrawStyle(sphere, OpenGL.GL_LINES);184             else185                 gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS);186             gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH);187             gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE);188             gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE);189             gl.Sphere(sphere, radius, segx, segy);190             gl.DeleteQuadric(sphere);191             gl.PopMatrix();192         }193 194         private void moveObject(int obj,string keyName)195         {196             //obj==0移动视图197             switch (keyName)198             {199                 case "btnQ":200                     if (obj == 0) ++lookatValue[1];   //y201                     else202                         ++lightPos[1];203                     break;204                 case "btnE":205                     if (obj == 0) --lookatValue[1];206                     else207                         --lightPos[1];208                     break;209                 case "btnW":210                     if (obj == 0) --lookatValue[2];   //z211                     else212                        --lightPos[2];213                     break;214                 case "btnS":215                     if (obj == 0)  ++lookatValue[2];216                     else217                         ++lightPos[2];218                     break;219                 case "btnA":220                     if (obj == 0) --lookatValue[0];  //X221                     else222                        --lightPos[0];223                     break;224                 case "btnD":225                     if (obj == 0)  ++lookatValue[0];226                     else227                         ++lightPos[0];228                     break;229             }230         }231 232         private void rbPerspective_CheckedChanged(object sender, EventArgs e)233         {234             switch (((RadioButton)sender).Name)235             {236                 case "rbPerspective":237                     isPerspective = !isPerspective;238                     isFrontView = false;239                     isTopView = false;240                     isLeftView = false;241                     break;242                 case "rbLeft":243                      isLeftView = !isLeftView;244                     isFrontView = false;245                     isPerspective = false;246                     isTopView = false;247                     break;248                 case "rbFront":249                      isFrontView = !isFrontView;250                     isTopView = false;251                     isPerspective = false;252                     isLeftView = false;253                     break;254                 case "rbTop":255                      isTopView = !isTopView;256                     isPerspective = false;257                     isLeftView = false;258                     isFrontView = false;259                     break;260                 default:261                     return;262             }263             setViewDefaultValue();264             openGLControl_Resized(null, null);265         }266 267         private void cbxRotate_CheckedChanged(object sender, EventArgs e)268         {269             var cbx=((CheckBox)sender);270             switch (cbx.Name)271             {272                 case "cbxRotate":273                     isRotate = cbx.Checked;274                     break;275                 case "cbxLines":276                     isLines = cbx.Checked;277                     break;278                 case "cbxLightOff":279                     if (!cbx.Checked)280                         this.openGLControl.OpenGL.Enable(OpenGL.GL_LIGHT0);281                     else282                         this.openGLControl.OpenGL.Disable(OpenGL.GL_LIGHT0);283                     break;284             }285         }286 287         private void SharpGLForm_Load(object sender, EventArgs e)288         {289             this.cbxLightType.SelectedIndex = 0;290             updateLabInfo();291         }292 293         private void loadConfig()294         {295            var ary=  File.ReadAllText(configPath).Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);296            if (ary.Length == 2)297            {298                var lightary = ary[0].Split(,).Select(s => {299                    float f1=0;300                    float.TryParse(s, out f1);301                    return f1;302                }).ToArray();303                var lookAtary = ary[1].Split(,).Select(s =>304                    {305                        double d1=0;306                        double.TryParse(s,out d1);307                        return d1;308                    }).ToArray();309                for (int i = 0; i < lightPos.Length; i++)310                    lightPos[i] = lightary[i];311                for (int i = 0; i < lookatValue.Length; i++)312                    lookatValue[i] = lookAtary[i];313            }314         }315 316         private void saveConfig()317         {318             try319             {320                 File.WriteAllText(configPath, tbLightPos.Text + Environment.NewLine + tbLookAt.Text + Environment.NewLine);321             }322             catch (Exception ex)323             {324                 MessageBox.Show(ex.Message);325             }326         }327 328         private void updateLabInfo()329         {330             tbLightPos.Text = string.Format("{0},{1},{2},{3}", lightPos[0], lightPos[1], lightPos[2], lightPos[3]);331             tbLookAt.Text = string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8}", lookatValue[0], lookatValue[1], lookatValue[2],332                 lookatValue[3], lookatValue[4], lookatValue[5], lookatValue[6], lookatValue[7], lookatValue[8]);333             btnSetPos_Click(null, null);334         }335 336         private void rbWhite_CheckedChanged(object sender, EventArgs e)337         {338             var rad = ((RadioButton)sender);339             var lightType = this.cbxLightType.SelectedIndex;340             if (rad.Checked)341             {342                 switch (rad.Name)343                 {344                     case "rbBlack":345                         lightColor[lightType][0] = 0f;346                         lightColor[lightType][1] = 0f;347                         lightColor[lightType][2] = 0f;348                         lightColor[lightType][3] = 1f;349                         break;350                     case "rbWhite":351                         lightColor[lightType][0] = 1f;352                         lightColor[lightType][1] = 1f;353                         lightColor[lightType][2] = 1f;354                         lightColor[lightType][3] = 1f;355                         break;356                     case "rbRed":357                         lightColor[lightType][0] = 1f;358                         lightColor[lightType][1] = 0f;359                         lightColor[lightType][2] = 0f;360                         lightColor[lightType][3] = 1f;361                         break;362                     case "rbGreen":363                         lightColor[lightType][0] = 0f;364                         lightColor[lightType][1] = 1f;365                         lightColor[lightType][2] = 0f;366                         lightColor[lightType][3] = 1f;367                         break;368                     case "rbBlue":369                         lightColor[lightType][0] = 0f;370                         lightColor[lightType][1] = 0f;371                         lightColor[lightType][2] = 1f;372                         lightColor[lightType][3] = 1f;373                         break;374                 }375                 setLightColor(openGLControl.OpenGL);376             }377         }378 379         private void cbxLightType_SelectedIndexChanged(object sender, EventArgs e)380         {381             var lightType = this.cbxLightType.SelectedIndex;382             if (lightType >= 0)383                 judgeColor(lightColor[lightType]);384         }385 386         private void judgeColor(float[] color)387         {388             if (color[0] == 1f && color[1] == 1f && color[2] == 1f && color[3] == 1f)389                 rbWhite.Checked = true;390             else if (color[0] == 1f && color[1] == 0f && color[2] == 0f && color[3] == 1f)391                 rbRed.Checked = true;392             else if (color[0] == 0f && color[1] == 1f && color[2] == 0f && color[3] == 1f)393                 rbGreen.Checked = true;394             else if (color[0] == 0f && color[1] == 0f && color[2] == 1f && color[3] == 1f)395                 rbBlue.Checked = true;396             else if (color[0] == 0f && color[1] == 0f && color[2] ==0f && color[3] ==1f)397                 rbBlack.Checked = true; 398         }399 400         private void btnQ_Click(object sender, EventArgs e)401         {402             moveObject(radioButton1.Checked ? 0 : 1,((Button)sender).Name);403             openGLControl_Resized(null, null);404             openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);405         }406 407         private void setViewDefaultValue()408         {409             if (isPerspective)410             {411                 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[0].Clone();412             }413             else if (isFrontView)414             {415                 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[1].Clone();416             }417             else if (isLeftView)418             {419                 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[2].Clone();420             }421             else if (isTopView)422             {423                 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[3].Clone();424             }425         }426 427         private void btnDefaultPOS_Click(object sender, EventArgs e)428         {429             if (radioButton1.Checked)430             {431                 setViewDefaultValue();432             }433             else434             {435                 lightPos = new float[] { -1, -3, 1, 1 };436                 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);437             }438             openGLControl_Resized(null, null); 439         }440 441         private void openGLControl_KeyDown(object sender, KeyEventArgs e)442         {443             string name = string.Empty;444             switch (e.KeyCode)445             {446                 case Keys.W:447                     name = "btnW";448                     break;449                 case Keys.A:450                     name = "btnA";451                     break;452                 case Keys.S:453                     name = "btnS";454                     break;455                 case Keys.D:456                     name = "btnD";457                     break;458                 case Keys.Q:459                     name = "btnQ";460                     break;461                 case Keys.E:462                     name = "btnE";463                     break;464             }465             moveObject(radioButton1.Checked ? 0 : 1, name);466             openGLControl_Resized(null, null);467         }468 469         private void btnSetPos_Click(object sender, EventArgs e)470         {471             if (radioButton1.Checked)472             {473                 double[] ary = tbLookAt.Text.Split(,).Select(s => Convert.ToDouble(s)).ToArray();474                 lookatValue =http://www.mamicode.com/ ary;475                 openGLControl_Resized(null, null); 476             }477             else478             {479                 float[] ary = tbLightPos.Text.Split(,).Select(s => Convert.ToSingle(s)).ToArray();480                 lightPos = ary;481                 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, ary);482             }483            484         }485 486         private void tbLightPos_TextChanged(object sender, EventArgs e)487         {488             saveConfig();489         }490 491        492     }493 }

 

源代码:Model3D.cs

 

  1 using System;  2 using System.Collections.Generic;  3 using System.Text;  4 using System.IO;  5 using System.Diagnostics;  6 using System.Drawing;  7 using System.Drawing.Imaging;  8 using SharpGL;  9 //原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/ 10 namespace Model3D 11 { 12     internal class FileHead 13     { 14         //基本块 15         public static UInt32 PRIMARY { get { return 0x4D4D; } set { } } 16  17         //主块 18         public static UInt32 OBJECTINFO { get { return 0x3D3D; } set { } }                // 网格对象的版本号 19         public static UInt32 VERSION { get { return 0x0002; } set { } }            // .3ds文件的版本 20         public static UInt32 EDITKEYFRAME { get { return 0xB000; } set { } }            // 所有关键帧信息的头部 21  22         //  对象的次级定义(包括对象的材质和对象) 23         public static UInt32 MATERIAL { get { return 0xAFFF; } set { } }        // 保存纹理信息 24         public static UInt32 OBJECT { get { return 0x4000; } set { } }        // 保存对象的面、顶点等信息 25  26         //  材质的次级定义 27         public static UInt32 MATNAME { get { return 0xA000; } set { } }            // 保存材质名称 28         public static UInt32 MATDIFFUSE { get { return 0xA020; } set { } }            // 对象/材质的颜色 29         public static UInt32 MATMAP { get { return 0xA200; } set { } }            // 新材质的头部 30         public static UInt32 MATMAPFILE { get { return 0xA300; } set { } }            // 保存纹理的文件名 31  32         public static UInt32 OBJECT_MESH { get { return 0x4100; } set { } }            // 新的网格对象 33  34         //  OBJECT_MESH的次级定义 35         public static UInt32 OBJECT_VERTICES { get { return 0x4110; } set { } }        // 对象顶点 36         public static UInt32 OBJECT_FACES { get { return 0x4120; } set { } }    // 对象的面 37         public static UInt32 OBJECT_MATERIAL { get { return 0x4130; } set { } }        // 对象的材质 38         public static UInt32 OBJECT_UV { get { return 0x4140; } set { } }    // 对象的UV纹理坐标 39  40         //转换字符 41         public static int byte2int(byte[] buffer) { return BitConverter.ToInt32(buffer, 0); } 42         public static float byte2float(byte[] buffer) { return BitConverter.ToSingle(buffer, 0); } 43     } 44  45     // 定义3D点的类,用于保存模型中的顶点 46     public class CVector3 47     { 48         public float x, y, z; 49     } 50     // 定义2D点类,用于保存模型的UV纹理坐标 51     public class CVector2 52     { 53         public float x, y; 54     } 55     // 面的结构定义 56     public class tFace 57     { 58         public int[] vertIndex = new int[3];     //顶点坐标 59         public int[] coordIndex = new int[3];    //纹理坐标索引 60  61     } 62     // 材质信息结构体 63     public class tMaterialInfo 64     { 65         public String strName = "";            //纹理名称 66         public String strFile = "";            //如果存在纹理映射,则表示纹理文件名称 67         public int[] color = new int[3]; //对象的RGB颜色 68         public int texureId;           //纹理ID 69         public float uTile;              //u重复 70         public float vTile;              //v重复 71         public float uOffset;            //u纹理偏移 72         public float vOffset;            //v纹理偏移 73     } 74     //对象信息结构体 75     public class t3DObject 76     { 77         public int numOfVerts;     // 模型中顶点的数目 78         public int numOfFaces;     // 模型中面的数目 79         public int numTexVertex;   // 模型中纹理坐标的数目 80         public int materialID;     // 纹理ID 81         public bool bHasTexture;   // 是否具有纹理映射 82         public String strName;     // 对象的名称 83         public CVector3[] pVerts;    // 对象的顶点 84         public CVector3[] pNormals;  // 对象的法向量 85         public CVector2[] pTexVerts; // 纹理UV坐标 86         public tFace[] pFaces;       // 对象的面信息 87     } 88     //模型信息结构体 89     public class t3DMdoel 90     { 91         public int numOfObjects;       // 模型中对象的数目 92         public int numOfMaterials;     // 模型中材质的数目 93         public List<tMaterialInfo> pMaterials = new List<tMaterialInfo>();   // 材质链表信息 94         public List<t3DObject> pObject = new List<t3DObject>();              // 模型中对象链表信息 95     } 96     public class tIndices 97     { 98         public UInt16 a, b, c, bVisible; 99     }100     // 保存块信息的结构101     public class tChunk102     {103         public UInt32 ID;          //块的ID104         public UInt32 length;      //块的长度105         public UInt32 bytesRead;   //需要读的块数据的字节数106     }107 108 109     public class CLoad3DS110     {111         private tChunk m_CurrentChunk = new tChunk();112         private tChunk m_TempChunk = new tChunk();113         private FileStream m_FilePointer;114         115 116 117         public bool Import3DS(t3DMdoel pModel, String strFileName)  // 装入3ds文件到模型结构中118         {119             if (pModel == null)120                 return false;121             pModel.numOfMaterials = 0;122             pModel.numOfObjects = 0;123             try124             {125                 this.m_FilePointer = new FileStream(strFileName, FileMode.Open);126             }127             catch (Exception ex)128             {129                 Debug.WriteLine(ex.ToString());130                 return false;131             }132             // 当文件打开之后,首先应该将文件最开始的数据块读出以判断是否是一个3ds文件133             // 如果是3ds文件的话,第一个块ID应该是PRIMARY134 135             // 将文件的第一块读出并判断是否是3ds文件136             ReadChunk(this.m_CurrentChunk); //读出块的id和块的size137             // 确保是3ds文件138             if (m_CurrentChunk.ID != FileHead.PRIMARY)139             {140                 Debug.WriteLine("Unable to load PRIMARY chuck from file: " + strFileName);141                 return false;142             }143             // 现在开始读入数据,ProcessNextChunk()是一个递归函数144 145             // 通过调用下面的递归函数,将对象读出146             ProcessNextChunk(pModel, m_CurrentChunk);147 148             // 在读完整个3ds文件之后,计算顶点的法线149             ComputeNormals(pModel);150 151             m_FilePointer.Close();152 153             return true;154         }155         //读出3ds文件的主要部分156         void ProcessNextChunk(t3DMdoel pModel, tChunk pPreviousChunk)157         {158             t3DObject newObject = new t3DObject();159             int version = 0;160 161             m_CurrentChunk = new tChunk();162 163             //  下面每读一个新块,都要判断一下块的ID,如果该块是需要的读入的,则继续进行164             //  如果是不需要读入的块,则略过165 166             // 继续读入子块,直到达到预定的长度167             while (pPreviousChunk.bytesRead < pPreviousChunk.length)168             {169                 //读入下一个块170                 ReadChunk(m_CurrentChunk);171 172                 //判断ID号173                 if (m_CurrentChunk.ID == FileHead.VERSION)174                 {175                     m_CurrentChunk.bytesRead += fread(ref version, m_CurrentChunk.length - m_CurrentChunk.bytesRead, m_FilePointer);176 177                     // 如果文件版本号大于3,给出一个警告信息178                     if (version > 3)179                         Debug.WriteLine("Warning:  This 3DS file is over version 3 so it may load incorrectly");180                 }181                 else if (m_CurrentChunk.ID == FileHead.OBJECTINFO)182                 {183                     //读入下一个块184                     ReadChunk(m_TempChunk);185 186                     //获得网络的版本号187                     m_TempChunk.bytesRead += fread(ref version, m_TempChunk.length - m_TempChunk.bytesRead, m_FilePointer);188 189                     //增加读入的字节数190                     m_CurrentChunk.bytesRead += m_TempChunk.bytesRead;191 192                     //进入下一个块193                     ProcessNextChunk(pModel, m_CurrentChunk);194                 }195                 else if (m_CurrentChunk.ID == FileHead.MATERIAL)//材质信息196                 {197                     //材质的数目递增198                     pModel.numOfMaterials++;199                     //在纹理链表中添加一个空白纹理结构200                     pModel.pMaterials.Add(new tMaterialInfo());201                     //进入材质装入函数202                     ProcessNextMaterialChunk(pModel, m_CurrentChunk);203                 }204                 else if (m_CurrentChunk.ID == FileHead.OBJECT)//对象的名称205                 {206                     //对象数目递增207                     pModel.numOfObjects++;208 209                     //添加一个新的tObject节点到对象的链表中210                     pModel.pObject.Add(new t3DObject());211 212                     //获得并保存对象的名称,然后增加读入的字节数213                     m_CurrentChunk.bytesRead += getStr(ref pModel.pObject[pModel.numOfObjects - 1].strName);214 215                     //进入其余对象信息的读入216                     ProcessNextObjectChunk(pModel, pModel.pObject[pModel.numOfObjects - 1], m_CurrentChunk);217                 }218                 else219                 {220                     // 跳过关键帧块的读入,增加需要读入的字节数 EDITKEYFRAME221                     // 跳过所有忽略的块的内容的读入,增加需要读入的字节数222                     while (m_CurrentChunk.bytesRead != m_CurrentChunk.length)223                     {224                         int[] b = new int[1];225                         m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer);226                     }227 228                 }229                 //添加从最后块中读入的字节数230                 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead;231 232             }233             //当前快设置为前面的块234             m_CurrentChunk = pPreviousChunk;235         }236         //处理所有的文件中的对象信息237         void ProcessNextObjectChunk(t3DMdoel pModel, t3DObject pObject, tChunk pPreviousChunk)238         {239             m_CurrentChunk = new tChunk();240 241             //继续读入块的内容直至本子块结束242             while (pPreviousChunk.bytesRead < pPreviousChunk.length)243             {244                 ReadChunk(m_CurrentChunk);245 246                 if (m_CurrentChunk.ID == FileHead.OBJECT_MESH)//正读入的是一个新块247                 {248                     //使用递归函数调用,处理该新块249                     ProcessNextObjectChunk(pModel, pObject, m_CurrentChunk);250 251                 }252                 else if (m_CurrentChunk.ID == FileHead.OBJECT_VERTICES)//读入的是对象顶点253                 {254                     ReadVertices(pObject, m_CurrentChunk);255                 }256                 else if (m_CurrentChunk.ID == FileHead.OBJECT_FACES)//读入的是对象的面257                 {258                     ReadVertexIndices(pObject, m_CurrentChunk);259                 }260                 else if (m_CurrentChunk.ID == FileHead.OBJECT_MATERIAL)//读入的是对象的材质名称261                 {262                     //该块保存了对象材质的名称,可能是一个颜色,也可能是一个纹理映射。263                     //同时在该块中也保存了纹理对象所赋予的面264 265                     //下面读入对象的材质名称266                     ReadObjectMaterial(pModel, pObject, m_CurrentChunk);267                 }268                 else if (m_CurrentChunk.ID == FileHead.OBJECT_UV)//读入对象的UV纹理坐标269                 {270                     ReadUVCoordinates(pObject, m_CurrentChunk);271                 }272                 else273                 {274                     //掠过不需要读入的块275                     while (m_CurrentChunk.bytesRead != m_CurrentChunk.length)276                     {277                         int[] b = new int[1];278                         m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer);279                     }280                 }281 282                 //添加从最后块中读入的字节数283                 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead;284 285             }286             //当前快设置为前面的块287             m_CurrentChunk = pPreviousChunk;288         }289         //处理所有的材质信息290         void ProcessNextMaterialChunk(t3DMdoel pModel, tChunk pPreviousChunk)291         {292             //给当前块分配存储空间293             m_CurrentChunk = new tChunk();294 295             //继续读入这些块,直到该子块结束296             while (pPreviousChunk.bytesRead < pPreviousChunk.length)297             {298                 //读入下一块299                 ReadChunk(m_CurrentChunk);300 301                 //判断读入的是什么块302                 if (m_CurrentChunk.ID == FileHead.MATNAME)//材质的名称303                 {304                     //读入材质的名称305                     m_CurrentChunk.bytesRead += fread(ref pModel.pMaterials[pModel.numOfMaterials - 1].strName, m_CurrentChunk.length - m_CurrentChunk.bytesRead, m_FilePointer);306                 }307                 else if (m_CurrentChunk.ID == FileHead.MATDIFFUSE)//对象的RGB颜色308                 {309                     ReadColorChunk(pModel.pMaterials[pModel.numOfMaterials - 1], m_CurrentChunk);310                 }311                 else if (m_CurrentChunk.ID == FileHead.MATMAP)//纹理信息头部312                 {313                     //进入下一个材质块信息314                     ProcessNextMaterialChunk(pModel, m_CurrentChunk);315                 }316                 else if (m_CurrentChunk.ID == FileHead.MATMAPFILE)317                 {318                     //读入材质文件名称319                     m_CurrentChunk.bytesRead += fread(ref pModel.pMaterials[pModel.numOfMaterials - 1].strName, m_CurrentChunk.length - m_CurrentChunk.bytesRead, m_FilePointer);320                 }321                 else322                 {323                     //掠过不需要读入的块324                     while (m_CurrentChunk.bytesRead != m_CurrentChunk.length)325                     {326                         int[] b = new int[1];327                         m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer);328                     }329                 }330                 //添加从最后块中读入的字节数331                 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead;332             }333             //当前快设置为前面的块334             m_CurrentChunk = pPreviousChunk;335         }336         //读下一个块337         private void ReadChunk(tChunk pChunk)338         {339             //pChunk.bytesRead = fread(ref pChunk.ID, 2, this.m_FilePointer);340 341             Byte[] id = new Byte[2];342             Byte[] length = new Byte[4];343             pChunk.bytesRead = (UInt32)this.m_FilePointer.Read(id, 0, 2);344             pChunk.bytesRead += (UInt32)this.m_FilePointer.Read(length, 0, 4);345             pChunk.ID = (UInt32)(id[1] * 256 + id[0]);346             pChunk.length = (UInt32)(((length[3] * 256 + length[2]) * 256 + length[1]) * 256 + length[0]);347 348         }349         //读入RGB颜色350         void ReadColorChunk(tMaterialInfo pMaterial, tChunk pChunk)351         {352             //读入颜色块信息353             ReadChunk(m_TempChunk);354 355             //读入RGB颜色356             m_TempChunk.bytesRead += fread(ref pMaterial.color, m_TempChunk.length - m_TempChunk.bytesRead, m_FilePointer);357 358             //增加读入的字节数359             pChunk.bytesRead += m_TempChunk.bytesRead;360         }361         //读入顶点索引362         void ReadVertexIndices(t3DObject pObject, tChunk pPreviousChunk)363         {364             int index = 0;365             //读入该对象中面的数目366             pPreviousChunk.bytesRead += fread(ref pObject.numOfFaces, 2, m_FilePointer);367 368             //分配所有的储存空间,并初始化结构369             pObject.pFaces = new tFace[pObject.numOfFaces];370 371             //遍历对象中所有的面372             for (int i = 0; i < pObject.numOfFaces; i++)373             {374                 pObject.pFaces[i] = new tFace();375                 for (int j = 0; j < 4; j++)376                 {377                     //读入当前对象的第一个点378                     pPreviousChunk.bytesRead += fread(ref index, 2, m_FilePointer);379 380                     if (j < 3)381                     {382                         pObject.pFaces[i].vertIndex[j] = index;383                     }384                 }385             }386         }387         //读入对象的UV坐标388         void ReadUVCoordinates(t3DObject pObject, tChunk pPreviousChunk)389         {390             //为了读入对象的UV坐标,首先需要读入数量,再读入具体的数据391 392             //读入UV坐标的数量393             pPreviousChunk.bytesRead += fread(ref pObject.numTexVertex, 2, m_FilePointer);394 395             //初始化保存UV坐标的数组396             pObject.pTexVerts = new CVector2[pObject.numTexVertex];397 398             //读入纹理坐标399             pPreviousChunk.bytesRead += fread(ref pObject.pTexVerts, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer);400         }401         //读入对象的顶点402         void ReadVertices(t3DObject pObject, tChunk pPreviousChunk)403         {404             //在读入实际的顶点之前,首先必须确定需要读入多少个顶点。405 406             //读入顶点的数目407             pPreviousChunk.bytesRead += fread(ref pObject.numOfVerts, 2, m_FilePointer);408 409             //分配顶点的储存空间,然后初始化结构体410             pObject.pVerts = new CVector3[pObject.numOfVerts];411 412             //读入顶点序列413             pPreviousChunk.bytesRead += fread(ref pObject.pVerts, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer);414 415             //因为3DMax的模型Z轴是指向上的,将y轴和z轴翻转——y轴和z轴交换,再把z轴反向416 417             //遍历所有的顶点418             for (int i = 0; i < pObject.numOfVerts; i++)419             {420                 float fTempY = pObject.pVerts[i].y;421                 pObject.pVerts[i].y = pObject.pVerts[i].z;422                 pObject.pVerts[i].z = -1 * fTempY;423             }424         }425         //读入对象的材质名称426         void ReadObjectMaterial(t3DMdoel pModel, t3DObject pObject, tChunk pPreviousChunk)427         {428             String strMaterial = "";            //用来保存对象的材质名称429             int[] buffer = new int[50000];    //用来读入不需要的数据430 431             //读入赋予当前对象的材质名称432             pPreviousChunk.bytesRead += getStr(ref strMaterial);433 434             //遍历所有的纹理435             for (int i = 0; i < pModel.numOfMaterials; i++)436             {437                 //如果读入的纹理与当前纹理名称匹配438 439                 if (true)//strMaterial.Equals(pModel.pMaterials[i].strName))440                 {441                     //设置材质ID442                     pObject.materialID = i;443                     //判断是否是纹理映射,如果strFile是一个长度大于1的字符串,则是纹理444                     if (pModel.pMaterials[i].strName.Length > 0) //if (pModel.pMaterials[i].strFile.Length > 0)445                     {446                         //设置对象的纹理映射标志447                         pObject.bHasTexture = true;448                     }449                     break;450                 }451                 else452                 {453                     //如果该对象没有材质,则设置ID为-1454                     pObject.materialID = -1;455                 }456             }457             pPreviousChunk.bytesRead += fread(ref buffer, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer);458         }459 460         //下面的这些函数主要用来计算顶点的法向量,顶点的法向量主要用来计算光照461         //计算对象的法向量462         private void ComputeNormals(t3DMdoel pModel)463         {464             CVector3 vVector1, vVector2, vNormal;465             CVector3[] vPoly;466 467             vPoly = new CVector3[3];468             //如果模型中没有对象,则返回469             if (pModel.numOfObjects <= 0)470                 return;471 472             //遍历模型中所有的对象473             for (int index = 0; index < pModel.numOfObjects; index++)474             {475                 //获得当前对象476                 t3DObject pObject = pModel.pObject[index];477 478                 //分配需要的空间479                 CVector3[] pNormals = new CVector3[pObject.numOfFaces];480                 CVector3[] pTempNormals = new CVector3[pObject.numOfFaces];481                 pObject.pNormals = new CVector3[pObject.numOfVerts];482 483                 //遍历对象所有面484                 for (int i = 0; i < pObject.numOfFaces; i++)485                 {486                     vPoly[0] = pObject.pVerts[pObject.pFaces[i].vertIndex[0]];487                     vPoly[1] = pObject.pVerts[pObject.pFaces[i].vertIndex[1]];488                     vPoly[2] = pObject.pVerts[pObject.pFaces[i].vertIndex[2]];489 490                     //计算面的法向量491                     vVector1 = Vector(vPoly[0], vPoly[2]);492                     vVector2 = Vector(vPoly[2], vPoly[1]);493 494                     vNormal = Cross(vVector1, vVector2);495                     pTempNormals[i] = vNormal;496                     vNormal = Normalize(vNormal);497                     pNormals[i] = vNormal;498                 }499 500                 //下面求顶点的法向量501                 CVector3 vSum = new CVector3();502                 vSum.x = 0; vSum.y = 0; vSum.z = 0;503                 int shared = 0;504 505                 //遍历所有的顶点506                 for (int i = 0; i < pObject.numOfVerts; i++)507                 {508                     for (int j = 0; j < pObject.numOfFaces; j++)509                     {510                         if (pObject.pFaces[j].vertIndex[0] == i ||511                             pObject.pFaces[j].vertIndex[1] == i ||512                             pObject.pFaces[j].vertIndex[2] == i)513                         {514                             vSum = AddVector(vSum, pTempNormals[j]);515                             shared++;516                         }517                     }518                     pObject.pNormals[i] = DivideVectorByScaler(vSum, (float)(-1 * shared));519 520                     //规范化最后的顶点法向量521                     pObject.pNormals[i] = Normalize(pObject.pNormals[i]);522 523                     vSum.x = 0; vSum.y = 0; vSum.z = 0;524                     shared = 0;525                 }526             }527         }528         //求两点决定的矢量529         CVector3 Vector(CVector3 p1, CVector3 p2)530         {531             CVector3 v = new CVector3();532             v.x = p1.x - p2.x;533             v.y = p1.y - p2.y;534             v.z = p1.z - p2.z;535             return v;536         }537         //返回两个矢量的和538         CVector3 AddVector(CVector3 p1, CVector3 p2)539         {540             CVector3 v = new CVector3();541             v.x = p1.x + p2.x;542             v.y = p1.y + p2.y;543             v.z = p1.z + p2.z;544             return v;545         }546         //返回矢量的缩放547         CVector3 DivideVectorByScaler(CVector3 v, float Scaler)548         {549             CVector3 vr = new CVector3();550             vr.x = v.x / Scaler;551             vr.y = v.y / Scaler;552             vr.z = v.z / Scaler;553             return vr;554         }555         //返回两个矢量的叉积556         CVector3 Cross(CVector3 p1, CVector3 p2)557         {558             CVector3 c = new CVector3();559             c.x = ((p1.y * p2.z) - (p1.z * p2.y));560             c.y = ((p1.z * p2.x) - (p1.x * p2.z));561             c.z = ((p1.x * p2.y) - (p1.y * p2.x));562             return c;563         }564         //规范化矢量565         CVector3 Normalize(CVector3 v)566         {567             CVector3 n = new CVector3();568             double mag = Mag(v);569             n.x = v.x / (float)mag;570             n.y = v.y / (float)mag;571             n.z = v.z / (float)mag;572             return n;573         }574         //矢量的模575         double Mag(CVector3 v)576         {577             return Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z);578         }579 580         //读出一个字符串581         uint getStr(ref String str)582         {583             str = "";584             char c = (char)m_FilePointer.ReadByte();585             while (c != 0)586             {587                 str += c;588                 c = (char)m_FilePointer.ReadByte();589             }590 591             return (uint)(str.Length + 1);592         }593         //读出byte数组594         public static uint fread(ref int[] buffer, uint length, FileStream f)595         {596             for (uint i = 0; i < length; i++)597             {598                 try599                 {600                     buffer[i] = f.ReadByte();601                 }602                 catch (Exception ex)603                 {604                     Debug.WriteLine(f.Name + " 读取出错");605                     Debug.WriteLine(ex.ToString());606                     return i;607                 }608             }609             return length;610         }611         //读出2个字节或4个字节的int612         public static uint fread(ref int buffer, uint length, FileStream f)613         {614             if (length == 2)615             {616                 Byte[] buf = new Byte[2];617                 uint len = (UInt32)f.Read(buf, 0, 2);618                 buffer = (buf[1] * 256 + buf[0]);619                 return len;620             }621             else if (length == 4)622             {623                 Byte[] buf = new Byte[4];624                 uint len = (UInt32)f.Read(buf, 0, 4);625                 buffer = (((buf[3] * 256 + buf[2]) * 256 + buf[1]) * 256 + buf[0]);626                 return len;627             }628             return 0;629         }630         //读出CVector3数组631         public static uint fread(ref CVector3[] buffer, uint length, FileStream f)632         {633             uint l = 0;634             try635             {636                 for (uint i = 0; i < length / 12; i++)637                 {638                     buffer[i] = new CVector3();639                     Byte[] bts = new Byte[4];640                     l += (uint)f.Read(bts, 0, 4);641                     buffer[i].x = FileHead.byte2float(bts);642                     l += (uint)f.Read(bts, 0, 4);643                     buffer[i].y = FileHead.byte2float(bts);644                     l += (uint)f.Read(bts, 0, 4);645                     buffer[i].z = FileHead.byte2float(bts);646                 }647                 return l;648             }649             catch (Exception ex)650             {651                 Debug.WriteLine(f.Name + " 读取出错");652                 Debug.WriteLine(ex.ToString());653                 return l;654             }655         }656         //读出CVector数组657         public static uint fread(ref CVector2[] buffer, uint length, FileStream f)658         {659             uint l = 0;660             try661             {662                 for (uint i = 0; i < length / 8; i++)663                 {664                     buffer[i] = new CVector2();665                     Byte[] bts = new Byte[4];666                     l += (uint)f.Read(bts, 0, 4);667                     buffer[i].x = FileHead.byte2float(bts);668                     l += (uint)f.Read(bts, 0, 4);669                     buffer[i].y = FileHead.byte2float(bts);670                 }671                 return l;672             }673             catch (Exception ex)674             {675                 Debug.WriteLine(f.Name + " 读取出错");676                 Debug.WriteLine(ex.ToString());677                 return l;678             }679         }680         //读出字符串681         public static uint fread(ref String buffer, uint length, FileStream f)682         {683             uint l = 0;684             buffer = "";685             try686             {687                 for (int i = 0; i < length; i++)688                 {689                     Byte[] b = new Byte[1];690                     l += (uint)f.Read(b, 0, 1);691                     if (i != length - 1)692                         buffer += (char)(b[0]);693                 }694 695                 return l;696             }697             catch (Exception ex)698             {699                 Debug.WriteLine(f.Name + " 读取出错");700                 Debug.WriteLine(ex.ToString());701                 return l;702             }703         }704     }705 706     public class H3DModel707     {708         public const int CHANGE = 1;709         public const int IGNORE = 2;710         public const int ADD = 3;711         t3DMdoel model = null;712         uint[] g_Texture;713         CVector3 boxMin, boxMax;714 715         public H3DModel()716         {717             this.model = new t3DMdoel();718         }719         public static H3DModel FromFile(OpenGL gl, string fileName)    //从文件中加载3D模型720         {721             H3DModel h3d = new H3DModel();722             CLoad3DS load = new CLoad3DS();723             load.Import3DS(h3d.model, fileName);724             if (!h3d.LoadTextrue(gl))725                 return null;726             h3d.LoadBox();727             return h3d;728         }729         public t3DMdoel getModelData()                      //得到3D模型数据730         {731             return this.model;732         }733 734         protected bool LoadTextrue(OpenGL gl)735         {736             this.g_Texture = new uint[100];737             for (int i = 0; i < model.numOfMaterials; i++)738             {739                 if (model.pMaterials[i].strName.Length > 0)  //if (model.pMaterials[i].strFile.Length > 0)740                     if (!CreateTexture(gl, ref this.g_Texture, model.pMaterials[i].strName, i))   // if (!CreateTexture(gl, ref this.g_Texture, model.pMaterials[i].strFile, i))741                         return false;742                 model.pMaterials[i].texureId = i;743             }744             return true;745         }746         protected void LoadBox()747         {748             boxMax = new CVector3();749             boxMin = new CVector3();750             boxMax.x = float.MinValue;751             boxMax.y = float.MinValue;752             boxMax.z = float.MinValue;753             boxMin.x = float.MaxValue;754             boxMin.y = float.MaxValue;755             boxMin.z = float.MaxValue;756             for (int i = 0; i < model.numOfObjects; i++)757             {758                 t3DObject pObject = model.pObject[i];759                 for (int j = 0; j < pObject.numOfVerts; j++)760                 {761                     float x = pObject.pVerts[j].x;762                     float y = pObject.pVerts[j].y;763                     float z = pObject.pVerts[j].z;764                     if (boxMin.x > x)765                         boxMin.x = x;766                     if (boxMin.y > y)767                         boxMin.y = y;768                     if (boxMin.z > z)769                         boxMin.z = z;770                     if (boxMax.x < x)771                         boxMax.x = x;772                     if (boxMax.y < y)773                         boxMax.y = y;774                     if (boxMax.z < z)775                         boxMax.z = z;776                 }777             }778 779         }780         protected bool CreateTexture(OpenGL GL,ref uint[] textureArray, String strFileName, int textureID)781         {782             Bitmap image = null;783             try784             {785                 image = new Bitmap(strFileName);786             }787             catch (ArgumentException)788             {789                 Debug.WriteLine("Could not load " + strFileName + " .");790                 return false;791             }792             if (image != null)793             {794                 image.RotateFlip(RotateFlipType.RotateNoneFlipY);795                 BitmapData bitmapdata;796                 Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);797                 bitmapdata =http://www.mamicode.com/ image.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);798 799                 uint[] tArray = new uint[1];800                 GL.GenTextures(1, tArray);801                 textureArray[textureID] = tArray[0];802 803                 GL.PixelStore(OpenGL.GL_UNPACK_ALIGNMENT, 1);804 805                 GL.BindTexture(OpenGL.GL_TEXTURE_2D, textureArray[textureID]);806                 GL.Build2DMipmaps(OpenGL.GL_TEXTURE_2D, 3, image.Width, image.Height, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bitmapdata.Scan0);807 808                 GL.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR_MIPMAP_NEAREST);809                 GL.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR_MIPMAP_LINEAR);810 811                 return true;812             }813             return false;814         }815 816         public void DrawModel(OpenGL GL,bool isLines)                             //画出模型817         {818             for (int i = 0; i < this.model.numOfObjects; i++)819             {820                 if (this.model.pObject.Count <= 0) break;821 822                 t3DObject pObject = this.model.pObject[i];823 824                 if (pObject.bHasTexture)825                 {826                     GL.Enable(OpenGL.GL_TEXTURE_2D);827                     GL.Color(1f,1f,1f);828                     GL.BindTexture(OpenGL.GL_TEXTURE_2D, this.g_Texture[i]); //pObject.materialID]);829 830                 }831                 else832                 {833                     GL.Disable(OpenGL.GL_TEXTURE_2D);834                     GL.Color(1f, 1f, 1f);    //GL.Color3ub(255, 255, 255);835                 }836 837                 if (isLines)838                     GL.Begin(OpenGL.GL_LINE_STRIP);839                 else840                     GL.Begin(OpenGL.GL_TRIANGLES);841 842                 for (int j = 0; j < pObject.numOfFaces; j++)843                 {844                     for (int whichVertex = 0; whichVertex < 3; whichVertex++)845                     {846                         int index = pObject.pFaces[j].vertIndex[whichVertex];847 848                         GL.Normal(-pObject.pNormals[index].x, -pObject.pNormals[index].y, -pObject.pNormals[index].z);849 850                         if (pObject.bHasTexture)851                         {852                             if (pObject.pTexVerts != null)853                             {854                                 GL.TexCoord(pObject.pTexVerts[index].x, pObject.pTexVerts[index].y);855                             }856                         }857                         else858                         {859 860                             if (this.model.pMaterials.Count != 0 && pObject.materialID >= 0)861                             {862                                 int[] color = this.model.pMaterials[pObject.materialID].color;863                                 GL.Color((byte)color[0], (byte)color[1], (byte)color[2]);864 865                             }866                         }867 868                         GL.Vertex(pObject.pVerts[index].x, pObject.pVerts[index].y, pObject.pVerts[index].z);869 870                     }871 872                 }873                 GL.End();874             }875         }876 877         public void DrawBorder(OpenGL GL)                            //画出边框878         {879             if (this.boxMax.x != float.MinValue && this.boxMin.x != float.MaxValue)880             {881                 GL.Color(1f,1f,1f);882                 float[] v = new float[6];883                 v[0] = boxMin.x;884                 v[1] = boxMin.y;885                 v[2] = boxMin.z;886                 v[3] = boxMax.x;887                 v[4] = boxMax.y;888                 v[5] = boxMax.z;889 890                 GL.Begin(OpenGL.GL_LINE_LOOP);891                 {892                     GL.Vertex(v[0], v[1], v[2]);893                     GL.Vertex(v[0], v[4], v[2]);894                     GL.Vertex(v[3], v[4], v[2]);895                     GL.Vertex(v[3], v[1], v[2]);896                 }897                 GL.End();898                 GL.Begin(OpenGL.GL_LINE_LOOP);899                 {900                     GL.Vertex(v[0], v[1], v[5]);901                     GL.Vertex(v[0], v[4], v[5]);902                     GL.Vertex(v[3], v[4], v[5]);903                     GL.Vertex(v[3], v[1], v[5]);904                 }905                 GL.End();906                 GL.Begin(OpenGL.GL_LINES);907                 {908                     GL.Vertex(v[0], v[1], v[2]);909                     GL.Vertex(v[0], v[1], v[5]);910                     GL.Vertex(v[0], v[4], v[2]);911                     GL.Vertex(v[0], v[4], v[5]);912                     GL.Vertex(v[3], v[4], v[2]);913                     GL.Vertex(v[3], v[4], v[5]);914                     GL.Vertex(v[3], v[1], v[2]);915                     GL.Vertex(v[3], v[1], v[5]);916                 }917                 GL.End();918             }919             else920             {921                 Debug.WriteLine("No Objects");922             }923 924         }925         public CVector3[] getOriginalBorder()               //得到模型边框的8个点926         {927             CVector3[] vs = new CVector3[8];928             float[] v = new float[6];929             v[0] = boxMin.x;930             v[1] = boxMin.y;931             v[2] = boxMin.z;932             v[3] = boxMax.x;933             v[4] = boxMax.y;934             v[5] = boxMax.z;935             for (int i = 0; i < 8; i++)936                 vs[i] = new CVector3();937             vs[0].x = v[0]; vs[0].y = v[1]; vs[0].z = v[2];938             vs[1].x = v[0]; vs[1].y = v[4]; vs[1].z = v[2];939             vs[2].x = v[3]; vs[2].y = v[4]; vs[2].z = v[2];940             vs[3].x = v[3]; vs[3].y = v[1]; vs[3].z = v[2];941             vs[4].x = v[0]; vs[4].y = v[1]; vs[4].z = v[5];942             vs[5].x = v[0]; vs[5].y = v[4]; vs[5].z = v[5];943             vs[6].x = v[3]; vs[6].y = v[4]; vs[6].z = v[5];944             vs[7].x = v[3]; vs[7].y = v[1]; vs[7].z = v[5];945             return vs;946         }947 948     }949 }

 

 

 

 

效果如下:

 

技术分享

 

重要的说明:

1. 第848行要注意,这里的法线方向很重要,你可以尝试一下,XYZ的法线要么全部为正,要么为负。如果法线方向搞错了会怎么样?如下图

  技术分享

 

2. 第806行Build2DMipmaps()中,把 OpenGL.GL_BGR换 OpenGL.GL_RGB这两个参数,会影响贴图的颜色,如果贴图失真偏色,就要考虑这个参数是否有问题。

比如下图中的贴图就偏蓝色。正确的颜色应该为本节代码的运行效果图。

技术分享

 

 3. 本源码没能很好的处理材质,只能处理有贴图的普通材质,如果是颜色材质或者其它材质则无法处理。读者可以尝试修改,记得把改好的代码共享给笔者参考一下哦!

 4. 另外,如果导出没有材质的模型,例如本文这样的组合的模型,笔者不知道如何单独为茶壶或者地板赋不同的材质,组合导入的3ds模型貌似是一个整体,不能打散为多个对象。

 5. 源代码目录提供了多个3ds模型,你可以试验一下区别。

 6. 如果你自己用3dsmax导出带材质的三维模型,注意把贴图和3ds文件都拷贝到bin目录。  

 

 

本节源代码下载

 

原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/

 

SharpGL学习笔记(十八) 解析3ds模型并显示