你的位置:首页 > ASP.net教程

[ASP.net教程]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 = { 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 =(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 = (double[])viewDefaultPos[0].Clone();412       }413       else if (isFrontView)414       {415         lookatValue = (double[])viewDefaultPos[1].Clone();416       }417       else if (isLeftView)418       {419         lookatValue = (double[])viewDefaultPos[2].Clone();420       }421       else if (isTopView)422       {423         lookatValue = (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 = 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 = 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/