• 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/

  • 相关阅读:
    多线程
    事务的概念与四大特性(ACID)
    Error和Exception的区别
    运算符的使用与注意事项(二)
    运算符的使用与注意事项(一)
    GOJS使用--前端拓扑图
    python算法实现分治法demo
    MySQL主从复制配置
    MySQL锁
    show profile查看SQL执行生命周期
  • 原文地址:https://www.cnblogs.com/hackpig/p/5846901.html
Copyright © 2020-2023  润新知