首页 > 代码库 > SharpGL学习笔记(十八) 解析3ds模型并显示
SharpGL学习笔记(十八) 解析3ds模型并显示
笔者设想的3D仿真中的元件,是不可能都是“画”出来的。这样就玩复杂了,应该把任务分包出去,让善于制作模型的软件来制作三维模型,我们只需要解析并且显示它即可。
3dsmax制作三维模型的方便,快捷,专业,我想是没有人提反对意见的。它可以把制作好的模型导出为业界通用的3ds格式,如果你愿意的话,3ds格式也可以包含材质和uvw贴图坐标。这样的模型我们在opengl中导入后只用打光和显示,非常省事。
解析3ds格式比较复杂,不过读者可以拿来主义,直接用下面的代码就可以了。
代码已经加入了必要的注释,笔者就不罗嗦了。
源代码: SharpGLForm.cs
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using SharpGL; 10 using Model3D; 11 using System.IO; 12 13 14 namespace SharpGLWinformsApplication1 15 { 16 //原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/ 17 public partial class SharpGLForm : Form 18 { 19 private string configPath = AppDomain.CurrentDomain.BaseDirectory + "config"; 20 private H3DModel h3d; 21 private float rotation = 0.0f; 22 private bool isRotate = false; 23 private bool isLines = false; 24 private bool isFrontView = false; 25 private bool isLeftView = false; 26 private bool isTopView = false; 27 private bool isPerspective = true; 28 private float[] lightPos = new float[] { -1, -3, 1, 1 }; 29 private float[] lightSphereColor = new float[] { 0.2f, 0.5f, 0.8f }; 30 private IList<float[]> lightColor = new List<float[]>(); 31 private double[] lookatValue = http://www.mamicode.com/{ 1, 1, 2, 0, 0, 0, 0, 1, 0 }; 32 33 float[] no_mat = new float[] { 0.0f, 0.0f, 0.0f, 1.0f }; // 无材质颜色 34 float[] mat_ambient = new float[] { 0.7f, 0.7f, 0.7f, 1.0f }; // 环境颜色 35 float[] mat_ambient_color = new float[] { 0.8f, 0.6f, 0.2f, 1.0f }; 36 float[] mat_diffuse = new float[] { 0.2f, 0.5f, 0.8f, 1.0f }; // 散射颜色 37 float[] no_shininess = new float[] { 0.0f }; // 镜面反射指数为0 38 float[] mat_emission = new float[] { 0.3f, 0.2f, 0.3f, 0.0f }; // 发射光颜色 39 float[] high_shininess = new float[] { 100.0f }; // 镜面反射指数为100.0 40 float[] low_shininess = new float[] { 5.0f }; // 镜面反射指数为5.0 41 float[] mat_specular = new float[] { 1.0f, 1.0f, 1.0f, 1.0f }; // 镜面反射颜色 42 43 private IList<double[]> viewDefaultPos = new List<double[]>(); 44 public SharpGLForm() 45 { 46 InitializeComponent(); 47 48 } 49 50 private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e) 51 { 52 OpenGL gl = openGLControl.OpenGL; 53 gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); 54 gl.LoadIdentity(); 55 gl.Rotate(rotation, 0.0f, 1.0f, 0.0f); 56 drawGrid(gl); 57 draw3DSModel(gl); 58 if (isRotate) 59 rotation += 3.0f; 60 } 61 62 private void draw3DSModel(OpenGL Gl) 63 { 64 Gl.PushMatrix(); 65 { 66 //Gl.PixelStore(OpenGL.GL_UNPACK_ALIGNMENT, 4); 67 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, mat_specular); 68 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, mat_specular); 69 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SPECULAR, no_mat); 70 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, no_mat); 71 //Gl.Material(OpenGL.GL_FRONT, OpenGL.GL_EMISSION, no_mat); 72 Gl.Scale(0.05, 0.05, 0.05); 73 Gl.Translate(0, 0, 0); 74 h3d.DrawModel(Gl,isLines); 75 h3d.DrawBorder(Gl); 76 } 77 Gl.PushMatrix(); 78 } 79 80 private void setLightColor(OpenGL gl) 81 { 82 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightColor[0]); 83 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, lightColor[1]); 84 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, lightColor[2]); 85 } 86 87 private void openGLControl_OpenGLInitialized(object sender, EventArgs e) 88 { 89 OpenGL gl = openGLControl.OpenGL; 90 91 //四个视图的缺省位置 92 viewDefaultPos.Add(new double[] { 1, 1, 2, 0, 0, 0, 0, 1, 0 }); //透视 93 viewDefaultPos.Add(new double[] { 0, 0, 2, 0, 0, 0, 0, 1, 0 }); //前视 94 viewDefaultPos.Add(new double[] { 5, 0, 0, 0, 0, 0, 0, 1, 0 }); //左视 95 viewDefaultPos.Add(new double[] { 0, 13, 0, -1, 0, 0, 0, 1, 0 }); //顶视 96 lookatValue =http://www.mamicode.com/(double[])viewDefaultPos[0].Clone(); 97 98 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //环境光(ambient light) 99 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //漫射光(diffuse light)100 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //镜面反射光(specular light)101 102 setLightColor(gl);103 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);104 105 gl.Enable(OpenGL.GL_LIGHTING);106 gl.Enable(OpenGL.GL_LIGHT0);107 gl.Enable(OpenGL.GL_NORMALIZE);108 109 110 gl.ClearColor(0, 0, 0, 0);111 h3d = H3DModel.FromFile(gl, "teport3.3DS");112 113 114 loadConfig();115 116 }117 118 119 120 private void openGLControl_Resized(object sender, EventArgs e)121 {122 123 OpenGL gl = openGLControl.OpenGL;124 gl.MatrixMode(OpenGL.GL_PROJECTION);125 gl.LoadIdentity();126 gl.Perspective(40.0f, (double)Width / (double)Height, 0.01, 100.0);127 128 129 gl.LookAt(lookatValue[0], lookatValue[1], lookatValue[2],130 lookatValue[3], lookatValue[4], lookatValue[5],131 lookatValue[6], lookatValue[7], lookatValue[8]);132 133 gl.MatrixMode(OpenGL.GL_MODELVIEW);134 updateLabInfo();135 }136 137 138 void drawGrid(OpenGL gl)139 {140 //关闭纹理和光照141 gl.Disable(OpenGL.GL_TEXTURE_2D);142 gl.Disable(OpenGL.GL_LIGHTING);143 144 //绘制过程145 gl.PushAttrib(OpenGL.GL_CURRENT_BIT); //保存当前属性146 gl.PushMatrix(); //压入堆栈147 gl.Translate(0f, -2f, 0f);148 gl.Color(0f, 0f, 1f);149 150 //在X,Z平面上绘制网格151 for (float i = -50; i <= 50; i += 1)152 {153 //绘制线154 gl.Begin(OpenGL.GL_LINES);155 {156 if (i == 0)157 gl.Color(0f, 1f, 0f);158 else159 gl.Color(0f, 0f, 1f);160 161 //X轴方向162 gl.Vertex(-50f, 0f, i);163 gl.Vertex(50f, 0f, i);164 //Z轴方向 165 gl.Vertex(i, 0f, -50f);166 gl.Vertex(i, 0f, 50f);167 168 }169 gl.End();170 }171 gl.PopMatrix();172 gl.PopAttrib();173 gl.Enable(OpenGL.GL_LIGHTING);174 }175 176 177 void drawSphere(OpenGL gl,double radius,int segx,int segy,bool isLines)178 {179 gl.PushMatrix();180 gl.Translate(2f, 1f, 2f);181 var sphere = gl.NewQuadric();182 if (isLines)183 gl.QuadricDrawStyle(sphere, OpenGL.GL_LINES);184 else185 gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS);186 gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH);187 gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE);188 gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE);189 gl.Sphere(sphere, radius, segx, segy);190 gl.DeleteQuadric(sphere);191 gl.PopMatrix();192 }193 194 private void moveObject(int obj,string keyName)195 {196 //obj==0移动视图197 switch (keyName)198 {199 case "btnQ":200 if (obj == 0) ++lookatValue[1]; //y201 else202 ++lightPos[1];203 break;204 case "btnE":205 if (obj == 0) --lookatValue[1];206 else207 --lightPos[1];208 break;209 case "btnW":210 if (obj == 0) --lookatValue[2]; //z211 else212 --lightPos[2];213 break;214 case "btnS":215 if (obj == 0) ++lookatValue[2];216 else217 ++lightPos[2];218 break;219 case "btnA":220 if (obj == 0) --lookatValue[0]; //X221 else222 --lightPos[0];223 break;224 case "btnD":225 if (obj == 0) ++lookatValue[0];226 else227 ++lightPos[0];228 break;229 }230 }231 232 private void rbPerspective_CheckedChanged(object sender, EventArgs e)233 {234 switch (((RadioButton)sender).Name)235 {236 case "rbPerspective":237 isPerspective = !isPerspective;238 isFrontView = false;239 isTopView = false;240 isLeftView = false;241 break;242 case "rbLeft":243 isLeftView = !isLeftView;244 isFrontView = false;245 isPerspective = false;246 isTopView = false;247 break;248 case "rbFront":249 isFrontView = !isFrontView;250 isTopView = false;251 isPerspective = false;252 isLeftView = false;253 break;254 case "rbTop":255 isTopView = !isTopView;256 isPerspective = false;257 isLeftView = false;258 isFrontView = false;259 break;260 default:261 return;262 }263 setViewDefaultValue();264 openGLControl_Resized(null, null);265 }266 267 private void cbxRotate_CheckedChanged(object sender, EventArgs e)268 {269 var cbx=((CheckBox)sender);270 switch (cbx.Name)271 {272 case "cbxRotate":273 isRotate = cbx.Checked;274 break;275 case "cbxLines":276 isLines = cbx.Checked;277 break;278 case "cbxLightOff":279 if (!cbx.Checked)280 this.openGLControl.OpenGL.Enable(OpenGL.GL_LIGHT0);281 else282 this.openGLControl.OpenGL.Disable(OpenGL.GL_LIGHT0);283 break;284 }285 }286 287 private void SharpGLForm_Load(object sender, EventArgs e)288 {289 this.cbxLightType.SelectedIndex = 0;290 updateLabInfo();291 }292 293 private void loadConfig()294 {295 var ary= File.ReadAllText(configPath).Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);296 if (ary.Length == 2)297 {298 var lightary = ary[0].Split(‘,‘).Select(s => {299 float f1=0;300 float.TryParse(s, out f1);301 return f1;302 }).ToArray();303 var lookAtary = ary[1].Split(‘,‘).Select(s =>304 {305 double d1=0;306 double.TryParse(s,out d1);307 return d1;308 }).ToArray();309 for (int i = 0; i < lightPos.Length; i++)310 lightPos[i] = lightary[i];311 for (int i = 0; i < lookatValue.Length; i++)312 lookatValue[i] = lookAtary[i];313 }314 }315 316 private void saveConfig()317 {318 try319 {320 File.WriteAllText(configPath, tbLightPos.Text + Environment.NewLine + tbLookAt.Text + Environment.NewLine);321 }322 catch (Exception ex)323 {324 MessageBox.Show(ex.Message);325 }326 }327 328 private void updateLabInfo()329 {330 tbLightPos.Text = string.Format("{0},{1},{2},{3}", lightPos[0], lightPos[1], lightPos[2], lightPos[3]);331 tbLookAt.Text = string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8}", lookatValue[0], lookatValue[1], lookatValue[2],332 lookatValue[3], lookatValue[4], lookatValue[5], lookatValue[6], lookatValue[7], lookatValue[8]);333 btnSetPos_Click(null, null);334 }335 336 private void rbWhite_CheckedChanged(object sender, EventArgs e)337 {338 var rad = ((RadioButton)sender);339 var lightType = this.cbxLightType.SelectedIndex;340 if (rad.Checked)341 {342 switch (rad.Name)343 {344 case "rbBlack":345 lightColor[lightType][0] = 0f;346 lightColor[lightType][1] = 0f;347 lightColor[lightType][2] = 0f;348 lightColor[lightType][3] = 1f;349 break;350 case "rbWhite":351 lightColor[lightType][0] = 1f;352 lightColor[lightType][1] = 1f;353 lightColor[lightType][2] = 1f;354 lightColor[lightType][3] = 1f;355 break;356 case "rbRed":357 lightColor[lightType][0] = 1f;358 lightColor[lightType][1] = 0f;359 lightColor[lightType][2] = 0f;360 lightColor[lightType][3] = 1f;361 break;362 case "rbGreen":363 lightColor[lightType][0] = 0f;364 lightColor[lightType][1] = 1f;365 lightColor[lightType][2] = 0f;366 lightColor[lightType][3] = 1f;367 break;368 case "rbBlue":369 lightColor[lightType][0] = 0f;370 lightColor[lightType][1] = 0f;371 lightColor[lightType][2] = 1f;372 lightColor[lightType][3] = 1f;373 break;374 }375 setLightColor(openGLControl.OpenGL);376 }377 }378 379 private void cbxLightType_SelectedIndexChanged(object sender, EventArgs e)380 {381 var lightType = this.cbxLightType.SelectedIndex;382 if (lightType >= 0)383 judgeColor(lightColor[lightType]);384 }385 386 private void judgeColor(float[] color)387 {388 if (color[0] == 1f && color[1] == 1f && color[2] == 1f && color[3] == 1f)389 rbWhite.Checked = true;390 else if (color[0] == 1f && color[1] == 0f && color[2] == 0f && color[3] == 1f)391 rbRed.Checked = true;392 else if (color[0] == 0f && color[1] == 1f && color[2] == 0f && color[3] == 1f)393 rbGreen.Checked = true;394 else if (color[0] == 0f && color[1] == 0f && color[2] == 1f && color[3] == 1f)395 rbBlue.Checked = true;396 else if (color[0] == 0f && color[1] == 0f && color[2] ==0f && color[3] ==1f)397 rbBlack.Checked = true; 398 }399 400 private void btnQ_Click(object sender, EventArgs e)401 {402 moveObject(radioButton1.Checked ? 0 : 1,((Button)sender).Name);403 openGLControl_Resized(null, null);404 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);405 }406 407 private void setViewDefaultValue()408 {409 if (isPerspective)410 {411 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[0].Clone();412 }413 else if (isFrontView)414 {415 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[1].Clone();416 }417 else if (isLeftView)418 {419 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[2].Clone();420 }421 else if (isTopView)422 {423 lookatValue = http://www.mamicode.com/(double[])viewDefaultPos[3].Clone();424 }425 }426 427 private void btnDefaultPOS_Click(object sender, EventArgs e)428 {429 if (radioButton1.Checked)430 {431 setViewDefaultValue();432 }433 else434 {435 lightPos = new float[] { -1, -3, 1, 1 };436 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);437 }438 openGLControl_Resized(null, null); 439 }440 441 private void openGLControl_KeyDown(object sender, KeyEventArgs e)442 {443 string name = string.Empty;444 switch (e.KeyCode)445 {446 case Keys.W:447 name = "btnW";448 break;449 case Keys.A:450 name = "btnA";451 break;452 case Keys.S:453 name = "btnS";454 break;455 case Keys.D:456 name = "btnD";457 break;458 case Keys.Q:459 name = "btnQ";460 break;461 case Keys.E:462 name = "btnE";463 break;464 }465 moveObject(radioButton1.Checked ? 0 : 1, name);466 openGLControl_Resized(null, null);467 }468 469 private void btnSetPos_Click(object sender, EventArgs e)470 {471 if (radioButton1.Checked)472 {473 double[] ary = tbLookAt.Text.Split(‘,‘).Select(s => Convert.ToDouble(s)).ToArray();474 lookatValue =http://www.mamicode.com/ ary;475 openGLControl_Resized(null, null); 476 }477 else478 {479 float[] ary = tbLightPos.Text.Split(‘,‘).Select(s => Convert.ToSingle(s)).ToArray();480 lightPos = ary;481 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, ary);482 }483 484 }485 486 private void tbLightPos_TextChanged(object sender, EventArgs e)487 {488 saveConfig();489 }490 491 492 }493 }
源代码:Model3D.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.IO; 5 using System.Diagnostics; 6 using System.Drawing; 7 using System.Drawing.Imaging; 8 using SharpGL; 9 //原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/ 10 namespace Model3D 11 { 12 internal class FileHead 13 { 14 //基本块 15 public static UInt32 PRIMARY { get { return 0x4D4D; } set { } } 16 17 //主块 18 public static UInt32 OBJECTINFO { get { return 0x3D3D; } set { } } // 网格对象的版本号 19 public static UInt32 VERSION { get { return 0x0002; } set { } } // .3ds文件的版本 20 public static UInt32 EDITKEYFRAME { get { return 0xB000; } set { } } // 所有关键帧信息的头部 21 22 // 对象的次级定义(包括对象的材质和对象) 23 public static UInt32 MATERIAL { get { return 0xAFFF; } set { } } // 保存纹理信息 24 public static UInt32 OBJECT { get { return 0x4000; } set { } } // 保存对象的面、顶点等信息 25 26 // 材质的次级定义 27 public static UInt32 MATNAME { get { return 0xA000; } set { } } // 保存材质名称 28 public static UInt32 MATDIFFUSE { get { return 0xA020; } set { } } // 对象/材质的颜色 29 public static UInt32 MATMAP { get { return 0xA200; } set { } } // 新材质的头部 30 public static UInt32 MATMAPFILE { get { return 0xA300; } set { } } // 保存纹理的文件名 31 32 public static UInt32 OBJECT_MESH { get { return 0x4100; } set { } } // 新的网格对象 33 34 // OBJECT_MESH的次级定义 35 public static UInt32 OBJECT_VERTICES { get { return 0x4110; } set { } } // 对象顶点 36 public static UInt32 OBJECT_FACES { get { return 0x4120; } set { } } // 对象的面 37 public static UInt32 OBJECT_MATERIAL { get { return 0x4130; } set { } } // 对象的材质 38 public static UInt32 OBJECT_UV { get { return 0x4140; } set { } } // 对象的UV纹理坐标 39 40 //转换字符 41 public static int byte2int(byte[] buffer) { return BitConverter.ToInt32(buffer, 0); } 42 public static float byte2float(byte[] buffer) { return BitConverter.ToSingle(buffer, 0); } 43 } 44 45 // 定义3D点的类,用于保存模型中的顶点 46 public class CVector3 47 { 48 public float x, y, z; 49 } 50 // 定义2D点类,用于保存模型的UV纹理坐标 51 public class CVector2 52 { 53 public float x, y; 54 } 55 // 面的结构定义 56 public class tFace 57 { 58 public int[] vertIndex = new int[3]; //顶点坐标 59 public int[] coordIndex = new int[3]; //纹理坐标索引 60 61 } 62 // 材质信息结构体 63 public class tMaterialInfo 64 { 65 public String strName = ""; //纹理名称 66 public String strFile = ""; //如果存在纹理映射,则表示纹理文件名称 67 public int[] color = new int[3]; //对象的RGB颜色 68 public int texureId; //纹理ID 69 public float uTile; //u重复 70 public float vTile; //v重复 71 public float uOffset; //u纹理偏移 72 public float vOffset; //v纹理偏移 73 } 74 //对象信息结构体 75 public class t3DObject 76 { 77 public int numOfVerts; // 模型中顶点的数目 78 public int numOfFaces; // 模型中面的数目 79 public int numTexVertex; // 模型中纹理坐标的数目 80 public int materialID; // 纹理ID 81 public bool bHasTexture; // 是否具有纹理映射 82 public String strName; // 对象的名称 83 public CVector3[] pVerts; // 对象的顶点 84 public CVector3[] pNormals; // 对象的法向量 85 public CVector2[] pTexVerts; // 纹理UV坐标 86 public tFace[] pFaces; // 对象的面信息 87 } 88 //模型信息结构体 89 public class t3DMdoel 90 { 91 public int numOfObjects; // 模型中对象的数目 92 public int numOfMaterials; // 模型中材质的数目 93 public List<tMaterialInfo> pMaterials = new List<tMaterialInfo>(); // 材质链表信息 94 public List<t3DObject> pObject = new List<t3DObject>(); // 模型中对象链表信息 95 } 96 public class tIndices 97 { 98 public UInt16 a, b, c, bVisible; 99 }100 // 保存块信息的结构101 public class tChunk102 {103 public UInt32 ID; //块的ID104 public UInt32 length; //块的长度105 public UInt32 bytesRead; //需要读的块数据的字节数106 }107 108 109 public class CLoad3DS110 {111 private tChunk m_CurrentChunk = new tChunk();112 private tChunk m_TempChunk = new tChunk();113 private FileStream m_FilePointer;114 115 116 117 public bool Import3DS(t3DMdoel pModel, String strFileName) // 装入3ds文件到模型结构中118 {119 if (pModel == null)120 return false;121 pModel.numOfMaterials = 0;122 pModel.numOfObjects = 0;123 try124 {125 this.m_FilePointer = new FileStream(strFileName, FileMode.Open);126 }127 catch (Exception ex)128 {129 Debug.WriteLine(ex.ToString());130 return false;131 }132 // 当文件打开之后,首先应该将文件最开始的数据块读出以判断是否是一个3ds文件133 // 如果是3ds文件的话,第一个块ID应该是PRIMARY134 135 // 将文件的第一块读出并判断是否是3ds文件136 ReadChunk(this.m_CurrentChunk); //读出块的id和块的size137 // 确保是3ds文件138 if (m_CurrentChunk.ID != FileHead.PRIMARY)139 {140 Debug.WriteLine("Unable to load PRIMARY chuck from file: " + strFileName);141 return false;142 }143 // 现在开始读入数据,ProcessNextChunk()是一个递归函数144 145 // 通过调用下面的递归函数,将对象读出146 ProcessNextChunk(pModel, m_CurrentChunk);147 148 // 在读完整个3ds文件之后,计算顶点的法线149 ComputeNormals(pModel);150 151 m_FilePointer.Close();152 153 return true;154 }155 //读出3ds文件的主要部分156 void ProcessNextChunk(t3DMdoel pModel, tChunk pPreviousChunk)157 {158 t3DObject newObject = new t3DObject();159 int version = 0;160 161 m_CurrentChunk = new tChunk();162 163 // 下面每读一个新块,都要判断一下块的ID,如果该块是需要的读入的,则继续进行164 // 如果是不需要读入的块,则略过165 166 // 继续读入子块,直到达到预定的长度167 while (pPreviousChunk.bytesRead < pPreviousChunk.length)168 {169 //读入下一个块170 ReadChunk(m_CurrentChunk);171 172 //判断ID号173 if (m_CurrentChunk.ID == FileHead.VERSION)174 {175 m_CurrentChunk.bytesRead += fread(ref version, m_CurrentChunk.length - m_CurrentChunk.bytesRead, m_FilePointer);176 177 // 如果文件版本号大于3,给出一个警告信息178 if (version > 3)179 Debug.WriteLine("Warning: This 3DS file is over version 3 so it may load incorrectly");180 }181 else if (m_CurrentChunk.ID == FileHead.OBJECTINFO)182 {183 //读入下一个块184 ReadChunk(m_TempChunk);185 186 //获得网络的版本号187 m_TempChunk.bytesRead += fread(ref version, m_TempChunk.length - m_TempChunk.bytesRead, m_FilePointer);188 189 //增加读入的字节数190 m_CurrentChunk.bytesRead += m_TempChunk.bytesRead;191 192 //进入下一个块193 ProcessNextChunk(pModel, m_CurrentChunk);194 }195 else if (m_CurrentChunk.ID == FileHead.MATERIAL)//材质信息196 {197 //材质的数目递增198 pModel.numOfMaterials++;199 //在纹理链表中添加一个空白纹理结构200 pModel.pMaterials.Add(new tMaterialInfo());201 //进入材质装入函数202 ProcessNextMaterialChunk(pModel, m_CurrentChunk);203 }204 else if (m_CurrentChunk.ID == FileHead.OBJECT)//对象的名称205 {206 //对象数目递增207 pModel.numOfObjects++;208 209 //添加一个新的tObject节点到对象的链表中210 pModel.pObject.Add(new t3DObject());211 212 //获得并保存对象的名称,然后增加读入的字节数213 m_CurrentChunk.bytesRead += getStr(ref pModel.pObject[pModel.numOfObjects - 1].strName);214 215 //进入其余对象信息的读入216 ProcessNextObjectChunk(pModel, pModel.pObject[pModel.numOfObjects - 1], m_CurrentChunk);217 }218 else219 {220 // 跳过关键帧块的读入,增加需要读入的字节数 EDITKEYFRAME221 // 跳过所有忽略的块的内容的读入,增加需要读入的字节数222 while (m_CurrentChunk.bytesRead != m_CurrentChunk.length)223 {224 int[] b = new int[1];225 m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer);226 }227 228 }229 //添加从最后块中读入的字节数230 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead;231 232 }233 //当前快设置为前面的块234 m_CurrentChunk = pPreviousChunk;235 }236 //处理所有的文件中的对象信息237 void ProcessNextObjectChunk(t3DMdoel pModel, t3DObject pObject, tChunk pPreviousChunk)238 {239 m_CurrentChunk = new tChunk();240 241 //继续读入块的内容直至本子块结束242 while (pPreviousChunk.bytesRead < pPreviousChunk.length)243 {244 ReadChunk(m_CurrentChunk);245 246 if (m_CurrentChunk.ID == FileHead.OBJECT_MESH)//正读入的是一个新块247 {248 //使用递归函数调用,处理该新块249 ProcessNextObjectChunk(pModel, pObject, m_CurrentChunk);250 251 }252 else if (m_CurrentChunk.ID == FileHead.OBJECT_VERTICES)//读入的是对象顶点253 {254 ReadVertices(pObject, m_CurrentChunk);255 }256 else if (m_CurrentChunk.ID == FileHead.OBJECT_FACES)//读入的是对象的面257 {258 ReadVertexIndices(pObject, m_CurrentChunk);259 }260 else if (m_CurrentChunk.ID == FileHead.OBJECT_MATERIAL)//读入的是对象的材质名称261 {262 //该块保存了对象材质的名称,可能是一个颜色,也可能是一个纹理映射。263 //同时在该块中也保存了纹理对象所赋予的面264 265 //下面读入对象的材质名称266 ReadObjectMaterial(pModel, pObject, m_CurrentChunk);267 }268 else if (m_CurrentChunk.ID == FileHead.OBJECT_UV)//读入对象的UV纹理坐标269 {270 ReadUVCoordinates(pObject, m_CurrentChunk);271 }272 else273 {274 //掠过不需要读入的块275 while (m_CurrentChunk.bytesRead != m_CurrentChunk.length)276 {277 int[] b = new int[1];278 m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer);279 }280 }281 282 //添加从最后块中读入的字节数283 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead;284 285 }286 //当前快设置为前面的块287 m_CurrentChunk = pPreviousChunk;288 }289 //处理所有的材质信息290 void ProcessNextMaterialChunk(t3DMdoel pModel, tChunk pPreviousChunk)291 {292 //给当前块分配存储空间293 m_CurrentChunk = new tChunk();294 295 //继续读入这些块,直到该子块结束296 while (pPreviousChunk.bytesRead < pPreviousChunk.length)297 {298 //读入下一块299 ReadChunk(m_CurrentChunk);300 301 //判断读入的是什么块302 if (m_CurrentChunk.ID == FileHead.MATNAME)//材质的名称303 {304 //读入材质的名称305 m_CurrentChunk.bytesRead += fread(ref pModel.pMaterials[pModel.numOfMaterials - 1].strName, m_CurrentChunk.length - m_CurrentChunk.bytesRead, m_FilePointer);306 }307 else if (m_CurrentChunk.ID == FileHead.MATDIFFUSE)//对象的RGB颜色308 {309 ReadColorChunk(pModel.pMaterials[pModel.numOfMaterials - 1], m_CurrentChunk);310 }311 else if (m_CurrentChunk.ID == FileHead.MATMAP)//纹理信息头部312 {313 //进入下一个材质块信息314 ProcessNextMaterialChunk(pModel, m_CurrentChunk);315 }316 else if (m_CurrentChunk.ID == FileHead.MATMAPFILE)317 {318 //读入材质文件名称319 m_CurrentChunk.bytesRead += fread(ref pModel.pMaterials[pModel.numOfMaterials - 1].strName, m_CurrentChunk.length - m_CurrentChunk.bytesRead, m_FilePointer);320 }321 else322 {323 //掠过不需要读入的块324 while (m_CurrentChunk.bytesRead != m_CurrentChunk.length)325 {326 int[] b = new int[1];327 m_CurrentChunk.bytesRead += fread(ref b, 1, m_FilePointer);328 }329 }330 //添加从最后块中读入的字节数331 pPreviousChunk.bytesRead += m_CurrentChunk.bytesRead;332 }333 //当前快设置为前面的块334 m_CurrentChunk = pPreviousChunk;335 }336 //读下一个块337 private void ReadChunk(tChunk pChunk)338 {339 //pChunk.bytesRead = fread(ref pChunk.ID, 2, this.m_FilePointer);340 341 Byte[] id = new Byte[2];342 Byte[] length = new Byte[4];343 pChunk.bytesRead = (UInt32)this.m_FilePointer.Read(id, 0, 2);344 pChunk.bytesRead += (UInt32)this.m_FilePointer.Read(length, 0, 4);345 pChunk.ID = (UInt32)(id[1] * 256 + id[0]);346 pChunk.length = (UInt32)(((length[3] * 256 + length[2]) * 256 + length[1]) * 256 + length[0]);347 348 }349 //读入RGB颜色350 void ReadColorChunk(tMaterialInfo pMaterial, tChunk pChunk)351 {352 //读入颜色块信息353 ReadChunk(m_TempChunk);354 355 //读入RGB颜色356 m_TempChunk.bytesRead += fread(ref pMaterial.color, m_TempChunk.length - m_TempChunk.bytesRead, m_FilePointer);357 358 //增加读入的字节数359 pChunk.bytesRead += m_TempChunk.bytesRead;360 }361 //读入顶点索引362 void ReadVertexIndices(t3DObject pObject, tChunk pPreviousChunk)363 {364 int index = 0;365 //读入该对象中面的数目366 pPreviousChunk.bytesRead += fread(ref pObject.numOfFaces, 2, m_FilePointer);367 368 //分配所有的储存空间,并初始化结构369 pObject.pFaces = new tFace[pObject.numOfFaces];370 371 //遍历对象中所有的面372 for (int i = 0; i < pObject.numOfFaces; i++)373 {374 pObject.pFaces[i] = new tFace();375 for (int j = 0; j < 4; j++)376 {377 //读入当前对象的第一个点378 pPreviousChunk.bytesRead += fread(ref index, 2, m_FilePointer);379 380 if (j < 3)381 {382 pObject.pFaces[i].vertIndex[j] = index;383 }384 }385 }386 }387 //读入对象的UV坐标388 void ReadUVCoordinates(t3DObject pObject, tChunk pPreviousChunk)389 {390 //为了读入对象的UV坐标,首先需要读入数量,再读入具体的数据391 392 //读入UV坐标的数量393 pPreviousChunk.bytesRead += fread(ref pObject.numTexVertex, 2, m_FilePointer);394 395 //初始化保存UV坐标的数组396 pObject.pTexVerts = new CVector2[pObject.numTexVertex];397 398 //读入纹理坐标399 pPreviousChunk.bytesRead += fread(ref pObject.pTexVerts, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer);400 }401 //读入对象的顶点402 void ReadVertices(t3DObject pObject, tChunk pPreviousChunk)403 {404 //在读入实际的顶点之前,首先必须确定需要读入多少个顶点。405 406 //读入顶点的数目407 pPreviousChunk.bytesRead += fread(ref pObject.numOfVerts, 2, m_FilePointer);408 409 //分配顶点的储存空间,然后初始化结构体410 pObject.pVerts = new CVector3[pObject.numOfVerts];411 412 //读入顶点序列413 pPreviousChunk.bytesRead += fread(ref pObject.pVerts, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer);414 415 //因为3DMax的模型Z轴是指向上的,将y轴和z轴翻转——y轴和z轴交换,再把z轴反向416 417 //遍历所有的顶点418 for (int i = 0; i < pObject.numOfVerts; i++)419 {420 float fTempY = pObject.pVerts[i].y;421 pObject.pVerts[i].y = pObject.pVerts[i].z;422 pObject.pVerts[i].z = -1 * fTempY;423 }424 }425 //读入对象的材质名称426 void ReadObjectMaterial(t3DMdoel pModel, t3DObject pObject, tChunk pPreviousChunk)427 {428 String strMaterial = ""; //用来保存对象的材质名称429 int[] buffer = new int[50000]; //用来读入不需要的数据430 431 //读入赋予当前对象的材质名称432 pPreviousChunk.bytesRead += getStr(ref strMaterial);433 434 //遍历所有的纹理435 for (int i = 0; i < pModel.numOfMaterials; i++)436 {437 //如果读入的纹理与当前纹理名称匹配438 439 if (true)//strMaterial.Equals(pModel.pMaterials[i].strName))440 {441 //设置材质ID442 pObject.materialID = i;443 //判断是否是纹理映射,如果strFile是一个长度大于1的字符串,则是纹理444 if (pModel.pMaterials[i].strName.Length > 0) //if (pModel.pMaterials[i].strFile.Length > 0)445 {446 //设置对象的纹理映射标志447 pObject.bHasTexture = true;448 }449 break;450 }451 else452 {453 //如果该对象没有材质,则设置ID为-1454 pObject.materialID = -1;455 }456 }457 pPreviousChunk.bytesRead += fread(ref buffer, pPreviousChunk.length - pPreviousChunk.bytesRead, m_FilePointer);458 }459 460 //下面的这些函数主要用来计算顶点的法向量,顶点的法向量主要用来计算光照461 //计算对象的法向量462 private void ComputeNormals(t3DMdoel pModel)463 {464 CVector3 vVector1, vVector2, vNormal;465 CVector3[] vPoly;466 467 vPoly = new CVector3[3];468 //如果模型中没有对象,则返回469 if (pModel.numOfObjects <= 0)470 return;471 472 //遍历模型中所有的对象473 for (int index = 0; index < pModel.numOfObjects; index++)474 {475 //获得当前对象476 t3DObject pObject = pModel.pObject[index];477 478 //分配需要的空间479 CVector3[] pNormals = new CVector3[pObject.numOfFaces];480 CVector3[] pTempNormals = new CVector3[pObject.numOfFaces];481 pObject.pNormals = new CVector3[pObject.numOfVerts];482 483 //遍历对象所有面484 for (int i = 0; i < pObject.numOfFaces; i++)485 {486 vPoly[0] = pObject.pVerts[pObject.pFaces[i].vertIndex[0]];487 vPoly[1] = pObject.pVerts[pObject.pFaces[i].vertIndex[1]];488 vPoly[2] = pObject.pVerts[pObject.pFaces[i].vertIndex[2]];489 490 //计算面的法向量491 vVector1 = Vector(vPoly[0], vPoly[2]);492 vVector2 = Vector(vPoly[2], vPoly[1]);493 494 vNormal = Cross(vVector1, vVector2);495 pTempNormals[i] = vNormal;496 vNormal = Normalize(vNormal);497 pNormals[i] = vNormal;498 }499 500 //下面求顶点的法向量501 CVector3 vSum = new CVector3();502 vSum.x = 0; vSum.y = 0; vSum.z = 0;503 int shared = 0;504 505 //遍历所有的顶点506 for (int i = 0; i < pObject.numOfVerts; i++)507 {508 for (int j = 0; j < pObject.numOfFaces; j++)509 {510 if (pObject.pFaces[j].vertIndex[0] == i ||511 pObject.pFaces[j].vertIndex[1] == i ||512 pObject.pFaces[j].vertIndex[2] == i)513 {514 vSum = AddVector(vSum, pTempNormals[j]);515 shared++;516 }517 }518 pObject.pNormals[i] = DivideVectorByScaler(vSum, (float)(-1 * shared));519 520 //规范化最后的顶点法向量521 pObject.pNormals[i] = Normalize(pObject.pNormals[i]);522 523 vSum.x = 0; vSum.y = 0; vSum.z = 0;524 shared = 0;525 }526 }527 }528 //求两点决定的矢量529 CVector3 Vector(CVector3 p1, CVector3 p2)530 {531 CVector3 v = new CVector3();532 v.x = p1.x - p2.x;533 v.y = p1.y - p2.y;534 v.z = p1.z - p2.z;535 return v;536 }537 //返回两个矢量的和538 CVector3 AddVector(CVector3 p1, CVector3 p2)539 {540 CVector3 v = new CVector3();541 v.x = p1.x + p2.x;542 v.y = p1.y + p2.y;543 v.z = p1.z + p2.z;544 return v;545 }546 //返回矢量的缩放547 CVector3 DivideVectorByScaler(CVector3 v, float Scaler)548 {549 CVector3 vr = new CVector3();550 vr.x = v.x / Scaler;551 vr.y = v.y / Scaler;552 vr.z = v.z / Scaler;553 return vr;554 }555 //返回两个矢量的叉积556 CVector3 Cross(CVector3 p1, CVector3 p2)557 {558 CVector3 c = new CVector3();559 c.x = ((p1.y * p2.z) - (p1.z * p2.y));560 c.y = ((p1.z * p2.x) - (p1.x * p2.z));561 c.z = ((p1.x * p2.y) - (p1.y * p2.x));562 return c;563 }564 //规范化矢量565 CVector3 Normalize(CVector3 v)566 {567 CVector3 n = new CVector3();568 double mag = Mag(v);569 n.x = v.x / (float)mag;570 n.y = v.y / (float)mag;571 n.z = v.z / (float)mag;572 return n;573 }574 //矢量的模575 double Mag(CVector3 v)576 {577 return Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z);578 }579 580 //读出一个字符串581 uint getStr(ref String str)582 {583 str = "";584 char c = (char)m_FilePointer.ReadByte();585 while (c != 0)586 {587 str += c;588 c = (char)m_FilePointer.ReadByte();589 }590 591 return (uint)(str.Length + 1);592 }593 //读出byte数组594 public static uint fread(ref int[] buffer, uint length, FileStream f)595 {596 for (uint i = 0; i < length; i++)597 {598 try599 {600 buffer[i] = f.ReadByte();601 }602 catch (Exception ex)603 {604 Debug.WriteLine(f.Name + " 读取出错");605 Debug.WriteLine(ex.ToString());606 return i;607 }608 }609 return length;610 }611 //读出2个字节或4个字节的int612 public static uint fread(ref int buffer, uint length, FileStream f)613 {614 if (length == 2)615 {616 Byte[] buf = new Byte[2];617 uint len = (UInt32)f.Read(buf, 0, 2);618 buffer = (buf[1] * 256 + buf[0]);619 return len;620 }621 else if (length == 4)622 {623 Byte[] buf = new Byte[4];624 uint len = (UInt32)f.Read(buf, 0, 4);625 buffer = (((buf[3] * 256 + buf[2]) * 256 + buf[1]) * 256 + buf[0]);626 return len;627 }628 return 0;629 }630 //读出CVector3数组631 public static uint fread(ref CVector3[] buffer, uint length, FileStream f)632 {633 uint l = 0;634 try635 {636 for (uint i = 0; i < length / 12; i++)637 {638 buffer[i] = new CVector3();639 Byte[] bts = new Byte[4];640 l += (uint)f.Read(bts, 0, 4);641 buffer[i].x = FileHead.byte2float(bts);642 l += (uint)f.Read(bts, 0, 4);643 buffer[i].y = FileHead.byte2float(bts);644 l += (uint)f.Read(bts, 0, 4);645 buffer[i].z = FileHead.byte2float(bts);646 }647 return l;648 }649 catch (Exception ex)650 {651 Debug.WriteLine(f.Name + " 读取出错");652 Debug.WriteLine(ex.ToString());653 return l;654 }655 }656 //读出CVector数组657 public static uint fread(ref CVector2[] buffer, uint length, FileStream f)658 {659 uint l = 0;660 try661 {662 for (uint i = 0; i < length / 8; i++)663 {664 buffer[i] = new CVector2();665 Byte[] bts = new Byte[4];666 l += (uint)f.Read(bts, 0, 4);667 buffer[i].x = FileHead.byte2float(bts);668 l += (uint)f.Read(bts, 0, 4);669 buffer[i].y = FileHead.byte2float(bts);670 }671 return l;672 }673 catch (Exception ex)674 {675 Debug.WriteLine(f.Name + " 读取出错");676 Debug.WriteLine(ex.ToString());677 return l;678 }679 }680 //读出字符串681 public static uint fread(ref String buffer, uint length, FileStream f)682 {683 uint l = 0;684 buffer = "";685 try686 {687 for (int i = 0; i < length; i++)688 {689 Byte[] b = new Byte[1];690 l += (uint)f.Read(b, 0, 1);691 if (i != length - 1)692 buffer += (char)(b[0]);693 }694 695 return l;696 }697 catch (Exception ex)698 {699 Debug.WriteLine(f.Name + " 读取出错");700 Debug.WriteLine(ex.ToString());701 return l;702 }703 }704 }705 706 public class H3DModel707 {708 public const int CHANGE = 1;709 public const int IGNORE = 2;710 public const int ADD = 3;711 t3DMdoel model = null;712 uint[] g_Texture;713 CVector3 boxMin, boxMax;714 715 public H3DModel()716 {717 this.model = new t3DMdoel();718 }719 public static H3DModel FromFile(OpenGL gl, string fileName) //从文件中加载3D模型720 {721 H3DModel h3d = new H3DModel();722 CLoad3DS load = new CLoad3DS();723 load.Import3DS(h3d.model, fileName);724 if (!h3d.LoadTextrue(gl))725 return null;726 h3d.LoadBox();727 return h3d;728 }729 public t3DMdoel getModelData() //得到3D模型数据730 {731 return this.model;732 }733 734 protected bool LoadTextrue(OpenGL gl)735 {736 this.g_Texture = new uint[100];737 for (int i = 0; i < model.numOfMaterials; i++)738 {739 if (model.pMaterials[i].strName.Length > 0) //if (model.pMaterials[i].strFile.Length > 0)740 if (!CreateTexture(gl, ref this.g_Texture, model.pMaterials[i].strName, i)) // if (!CreateTexture(gl, ref this.g_Texture, model.pMaterials[i].strFile, i))741 return false;742 model.pMaterials[i].texureId = i;743 }744 return true;745 }746 protected void LoadBox()747 {748 boxMax = new CVector3();749 boxMin = new CVector3();750 boxMax.x = float.MinValue;751 boxMax.y = float.MinValue;752 boxMax.z = float.MinValue;753 boxMin.x = float.MaxValue;754 boxMin.y = float.MaxValue;755 boxMin.z = float.MaxValue;756 for (int i = 0; i < model.numOfObjects; i++)757 {758 t3DObject pObject = model.pObject[i];759 for (int j = 0; j < pObject.numOfVerts; j++)760 {761 float x = pObject.pVerts[j].x;762 float y = pObject.pVerts[j].y;763 float z = pObject.pVerts[j].z;764 if (boxMin.x > x)765 boxMin.x = x;766 if (boxMin.y > y)767 boxMin.y = y;768 if (boxMin.z > z)769 boxMin.z = z;770 if (boxMax.x < x)771 boxMax.x = x;772 if (boxMax.y < y)773 boxMax.y = y;774 if (boxMax.z < z)775 boxMax.z = z;776 }777 }778 779 }780 protected bool CreateTexture(OpenGL GL,ref uint[] textureArray, String strFileName, int textureID)781 {782 Bitmap image = null;783 try784 {785 image = new Bitmap(strFileName);786 }787 catch (ArgumentException)788 {789 Debug.WriteLine("Could not load " + strFileName + " .");790 return false;791 }792 if (image != null)793 {794 image.RotateFlip(RotateFlipType.RotateNoneFlipY);795 BitmapData bitmapdata;796 Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);797 bitmapdata =http://www.mamicode.com/ image.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);798 799 uint[] tArray = new uint[1];800 GL.GenTextures(1, tArray);801 textureArray[textureID] = tArray[0];802 803 GL.PixelStore(OpenGL.GL_UNPACK_ALIGNMENT, 1);804 805 GL.BindTexture(OpenGL.GL_TEXTURE_2D, textureArray[textureID]);806 GL.Build2DMipmaps(OpenGL.GL_TEXTURE_2D, 3, image.Width, image.Height, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bitmapdata.Scan0);807 808 GL.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR_MIPMAP_NEAREST);809 GL.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR_MIPMAP_LINEAR);810 811 return true;812 }813 return false;814 }815 816 public void DrawModel(OpenGL GL,bool isLines) //画出模型817 {818 for (int i = 0; i < this.model.numOfObjects; i++)819 {820 if (this.model.pObject.Count <= 0) break;821 822 t3DObject pObject = this.model.pObject[i];823 824 if (pObject.bHasTexture)825 {826 GL.Enable(OpenGL.GL_TEXTURE_2D);827 GL.Color(1f,1f,1f);828 GL.BindTexture(OpenGL.GL_TEXTURE_2D, this.g_Texture[i]); //pObject.materialID]);829 830 }831 else832 {833 GL.Disable(OpenGL.GL_TEXTURE_2D);834 GL.Color(1f, 1f, 1f); //GL.Color3ub(255, 255, 255);835 }836 837 if (isLines)838 GL.Begin(OpenGL.GL_LINE_STRIP);839 else840 GL.Begin(OpenGL.GL_TRIANGLES);841 842 for (int j = 0; j < pObject.numOfFaces; j++)843 {844 for (int whichVertex = 0; whichVertex < 3; whichVertex++)845 {846 int index = pObject.pFaces[j].vertIndex[whichVertex];847 848 GL.Normal(-pObject.pNormals[index].x, -pObject.pNormals[index].y, -pObject.pNormals[index].z);849 850 if (pObject.bHasTexture)851 {852 if (pObject.pTexVerts != null)853 {854 GL.TexCoord(pObject.pTexVerts[index].x, pObject.pTexVerts[index].y);855 }856 }857 else858 {859 860 if (this.model.pMaterials.Count != 0 && pObject.materialID >= 0)861 {862 int[] color = this.model.pMaterials[pObject.materialID].color;863 GL.Color((byte)color[0], (byte)color[1], (byte)color[2]);864 865 }866 }867 868 GL.Vertex(pObject.pVerts[index].x, pObject.pVerts[index].y, pObject.pVerts[index].z);869 870 }871 872 }873 GL.End();874 }875 }876 877 public void DrawBorder(OpenGL GL) //画出边框878 {879 if (this.boxMax.x != float.MinValue && this.boxMin.x != float.MaxValue)880 {881 GL.Color(1f,1f,1f);882 float[] v = new float[6];883 v[0] = boxMin.x;884 v[1] = boxMin.y;885 v[2] = boxMin.z;886 v[3] = boxMax.x;887 v[4] = boxMax.y;888 v[5] = boxMax.z;889 890 GL.Begin(OpenGL.GL_LINE_LOOP);891 {892 GL.Vertex(v[0], v[1], v[2]);893 GL.Vertex(v[0], v[4], v[2]);894 GL.Vertex(v[3], v[4], v[2]);895 GL.Vertex(v[3], v[1], v[2]);896 }897 GL.End();898 GL.Begin(OpenGL.GL_LINE_LOOP);899 {900 GL.Vertex(v[0], v[1], v[5]);901 GL.Vertex(v[0], v[4], v[5]);902 GL.Vertex(v[3], v[4], v[5]);903 GL.Vertex(v[3], v[1], v[5]);904 }905 GL.End();906 GL.Begin(OpenGL.GL_LINES);907 {908 GL.Vertex(v[0], v[1], v[2]);909 GL.Vertex(v[0], v[1], v[5]);910 GL.Vertex(v[0], v[4], v[2]);911 GL.Vertex(v[0], v[4], v[5]);912 GL.Vertex(v[3], v[4], v[2]);913 GL.Vertex(v[3], v[4], v[5]);914 GL.Vertex(v[3], v[1], v[2]);915 GL.Vertex(v[3], v[1], v[5]);916 }917 GL.End();918 }919 else920 {921 Debug.WriteLine("No Objects");922 }923 924 }925 public CVector3[] getOriginalBorder() //得到模型边框的8个点926 {927 CVector3[] vs = new CVector3[8];928 float[] v = new float[6];929 v[0] = boxMin.x;930 v[1] = boxMin.y;931 v[2] = boxMin.z;932 v[3] = boxMax.x;933 v[4] = boxMax.y;934 v[5] = boxMax.z;935 for (int i = 0; i < 8; i++)936 vs[i] = new CVector3();937 vs[0].x = v[0]; vs[0].y = v[1]; vs[0].z = v[2];938 vs[1].x = v[0]; vs[1].y = v[4]; vs[1].z = v[2];939 vs[2].x = v[3]; vs[2].y = v[4]; vs[2].z = v[2];940 vs[3].x = v[3]; vs[3].y = v[1]; vs[3].z = v[2];941 vs[4].x = v[0]; vs[4].y = v[1]; vs[4].z = v[5];942 vs[5].x = v[0]; vs[5].y = v[4]; vs[5].z = v[5];943 vs[6].x = v[3]; vs[6].y = v[4]; vs[6].z = v[5];944 vs[7].x = v[3]; vs[7].y = v[1]; vs[7].z = v[5];945 return vs;946 }947 948 }949 }
效果如下:
重要的说明:
1. 第848行要注意,这里的法线方向很重要,你可以尝试一下,XYZ的法线要么全部为正,要么为负。如果法线方向搞错了会怎么样?如下图
2. 第806行Build2DMipmaps()中,把 OpenGL.GL_BGR换 OpenGL.GL_RGB这两个参数,会影响贴图的颜色,如果贴图失真偏色,就要考虑这个参数是否有问题。
比如下图中的贴图就偏蓝色。正确的颜色应该为本节代码的运行效果图。
3. 本源码没能很好的处理材质,只能处理有贴图的普通材质,如果是颜色材质或者其它材质则无法处理。读者可以尝试修改,记得把改好的代码共享给笔者参考一下哦!
4. 另外,如果导出没有材质的模型,例如本文这样的组合的模型,笔者不知道如何单独为茶壶或者地板赋不同的材质,组合导入的3ds模型貌似是一个整体,不能打散为多个对象。
5. 源代码目录提供了多个3ds模型,你可以试验一下区别。
6. 如果你自己用3dsmax导出带材质的三维模型,注意把贴图和3ds文件都拷贝到bin目录。
本节源代码下载
原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/
SharpGL学习笔记(十八) 解析3ds模型并显示