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                     else
159                         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             else
185                 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];   //y
201                     else
202                         ++lightPos[1];
203                     break;
204                 case "btnE":
205                     if (obj == 0) --lookatValue[1];
206                     else
207                         --lightPos[1];
208                     break;
209                 case "btnW":
210                     if (obj == 0) --lookatValue[2];   //z
211                     else
212                        --lightPos[2];
213                     break;
214                 case "btnS":
215                     if (obj == 0)  ++lookatValue[2];
216                     else
217                         ++lightPos[2];
218                     break;
219                 case "btnA":
220                     if (obj == 0) --lookatValue[0];  //X
221                     else
222                        --lightPos[0];
223                     break;
224                 case "btnD":
225                     if (obj == 0)  ++lookatValue[0];
226                     else
227                         ++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                     else
282                         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             try
319             {
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             else
434             {
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             else
478             {
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 tChunk
102     {
103         public UInt32 ID;          //块的ID
104         public UInt32 length;      //块的长度
105         public UInt32 bytesRead;   //需要读的块数据的字节数
106     }
107 
108 
109     public class CLoad3DS
110     {
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             try
124             {
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应该是PRIMARY
134 
135             // 将文件的第一块读出并判断是否是3ds文件
136             ReadChunk(this.m_CurrentChunk); //读出块的id和块的size
137             // 确保是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                 else
219                 {
220                     // 跳过关键帧块的读入,增加需要读入的字节数 EDITKEYFRAME
221                     // 跳过所有忽略的块的内容的读入,增加需要读入的字节数
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                 else
273                 {
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                 else
322                 {
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                     //设置材质ID
442                     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                 else
452                 {
453                     //如果该对象没有材质,则设置ID为-1
454                     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                 try
599                 {
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个字节的int
612         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             try
635             {
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             try
661             {
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             try
686             {
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 H3DModel
707     {
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             try
784             {
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                 else
832                 {
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                 else
840                     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                         else
858                         {
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             else
920             {
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/

 

posted @ 2016-09-06 19:10  猪悟能  阅读(7582)  评论(4编辑  收藏  举报