Advertisement
Guest User

Untitled

a guest
May 28th, 2015
218
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 16.82 KB | None | 0 0
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  4.  
  5. using klukule.OpenGL;
  6. using System.Globalization;
  7.  
  8. namespace testing_game
  9. {
  10.     public class ObjLoader : IDisposable
  11.     {
  12.         private List<ObjObject> objects = new List<ObjObject>();
  13.         private Dictionary<string, ObjMaterial> materials = new Dictionary<string, ObjMaterial>();
  14.  
  15.         public ShaderProgram defaultProgram;
  16.  
  17.         public ObjLoader(string filename, ShaderProgram program)
  18.         {
  19.             this.defaultProgram = program;
  20.  
  21.             System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
  22.             ObjMaterial defaultMaterial = new ObjMaterial(program);
  23.  
  24.             using (StreamReader stream = new StreamReader(filename))
  25.             {
  26.                 List<string> lines = new List<string>();
  27.                 int vertexOffset = 1, vertexCount = 0;
  28.                 int uvOffset = 1, uvCount = 0;
  29.  
  30.                 // read the entire file
  31.                 while (!stream.EndOfStream)
  32.                 {
  33.                     string line = stream.ReadLine();
  34.                     if (line.Trim().Length == 0) continue;
  35.  
  36.                     if ((line[0] == 'o' || line[0] == 'g') && lines.Count != 0)
  37.                     {
  38.                         ObjObject newObject = new ObjObject(lines, materials, vertexOffset, uvOffset);
  39.                         objects.Add(newObject);
  40.  
  41.                         if (newObject.Material == null) newObject.Material = defaultMaterial;
  42.  
  43.                         lines.Clear();
  44.                         vertexOffset += vertexCount;
  45.                         uvOffset += uvCount;
  46.                         vertexCount = 0;
  47.                         uvCount = 0;
  48.                     }
  49.                     if (line[0] != '#') lines.Add(line);
  50.                     if (line[0] == 'v')
  51.                     {
  52.                         if (line[1] == ' ') vertexCount++;
  53.                         else uvCount++;
  54.                     }
  55.  
  56.                     // check if a material file is being used
  57.                     if (line[0] == 'm' && line[1] == 't') LoadMaterials(CreateFixedPath(filename, line.Split(' ')[1]));
  58.                 }
  59.             }
  60.  
  61.             watch.Stop();
  62.             Console.WriteLine("Took {0}ms", watch.ElapsedMilliseconds);
  63.         }
  64.  
  65.         private void LoadMaterials(string filename)
  66.         {
  67.             using (StreamReader stream = new StreamReader(filename))
  68.             {
  69.                 List<string> lines = new List<string>();
  70.  
  71.                 while (!stream.EndOfStream)
  72.                 {
  73.                     string line = stream.ReadLine();
  74.                     if (line.Trim().Length == 0) continue;
  75.  
  76.                     if (line[0] == 'n' && lines.Count != 0)
  77.                     {
  78.                         // if this is a new material ('newmtl name') then load it
  79.                         ObjMaterial material = new ObjMaterial(lines, defaultProgram);
  80.                         if (!materials.ContainsKey(material.Name)) materials.Add(material.Name, material);
  81.                         lines.Clear();
  82.                     }
  83.  
  84.                     if (line[0] == 'm')
  85.                     {
  86.                         // try to fix up filenames of texture maps
  87.                         string[] split = line.Split(' ');
  88.                         lines.Add(string.Format("{0} {1}", split[0], CreateFixedPath(filename, split[1])));
  89.                     }
  90.                     else if (line[0] != '#') lines.Add(line);    // ignore comments
  91.                 }
  92.             }
  93.         }
  94.  
  95.         private string CreateFixedPath(string objectPath, string filename)
  96.         {
  97.             if (File.Exists(filename)) return filename;
  98.  
  99.             DirectoryInfo directory = new FileInfo(objectPath).Directory;
  100.  
  101.             filename = filename.Replace('\\', '/');
  102.             if (filename.Contains("/")) filename = filename.Substring(filename.LastIndexOf('/') + 1);
  103.             filename = directory.FullName + "\\" + filename;
  104.  
  105.             return filename;
  106.         }
  107.  
  108.         public void Draw()
  109.         {
  110.             List<ObjObject> transparentObjects = new List<ObjObject>();
  111.  
  112.             for (int i = 0; i < objects.Count; i++)
  113.             {
  114.                 if (objects[i].Material.Transparency != 1f) transparentObjects.Add(objects[i]);
  115.                 else objects[i].Draw();
  116.             }
  117.  
  118.             for (int i = 0; i < transparentObjects.Count; i++)
  119.             {
  120.                 transparentObjects[i].Draw();
  121.             }
  122.         }
  123.  
  124.         public void Dispose()
  125.         {
  126.             for (int i = 0; i < objects.Count; i++) objects[i].Dispose();
  127.         }
  128.     }
  129.  
  130.     public class ObjMaterial : IDisposable
  131.     {
  132.         public string Name { get; private set; }
  133.         public Vector3 Ambient { get; private set; }
  134.         public Vector3 Diffuse { get; private set; }
  135.         public Vector3 Specular { get; private set; }
  136.         public float SpecularCoefficient { get; private set; }
  137.         public float Transparency { get; private set; }
  138.         public IlluminationMode Illumination { get; private set; }
  139.  
  140.         public Texture DiffuseMap { get; private set; }
  141.         public ShaderProgram Program { get; private set; }
  142.  
  143.         public enum IlluminationMode
  144.         {
  145.             ColorOnAmbientOff = 0,
  146.             ColorOnAmbientOn = 1,
  147.             HighlightOn = 2,
  148.             ReflectionOnRaytraceOn = 3,
  149.             TransparencyGlassOnReflectionRayTraceOn = 4,
  150.             ReflectionFresnelOnRayTranceOn = 5,
  151.             TransparencyRefractionOnReflectionFresnelOffRayTraceOn = 6,
  152.             TransparencyRefractionOnReflectionFresnelOnRayTranceOn = 7,
  153.             ReflectionOnRayTraceOff = 8,
  154.             TransparencyGlassOnReflectionRayTraceOff = 9,
  155.             CastsShadowsOntoInvisibleSurfaces = 10
  156.         }
  157.  
  158.         public ObjMaterial(ShaderProgram program)
  159.         {
  160.             this.Name = "opengl-default-project";
  161.             this.Transparency = 1f;
  162.             this.Ambient = Vector3.UnitScale;
  163.             this.Diffuse = Vector3.UnitScale;
  164.             this.Program = program;
  165.         }
  166.  
  167.         public ObjMaterial(List<string> lines, ShaderProgram program)
  168.         {
  169.             if (!lines[0].StartsWith("newmtl")) return;
  170.  
  171.             this.Name = lines[0].Substring(7);
  172.             this.Transparency = 1f;
  173.  
  174.             for (int i = 1; i < lines.Count; i++)
  175.             {
  176.                 string[] split = lines[i].Split(' ');
  177.  
  178.                 switch (split[0])
  179.                 {
  180.                     case "Ns": this.SpecularCoefficient = float.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat);
  181.                         break;
  182.                     case "Ka": this.Ambient = new Vector3(float.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat), float.Parse(split[2], CultureInfo.InvariantCulture.NumberFormat), float.Parse(split[3], CultureInfo.InvariantCulture.NumberFormat));
  183.                         break;
  184.                     case "Kd": this.Diffuse = new Vector3(float.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat), float.Parse(split[2], CultureInfo.InvariantCulture.NumberFormat), float.Parse(split[3], CultureInfo.InvariantCulture.NumberFormat));
  185.                         break;
  186.                     case "Ks": this.Specular = new Vector3(float.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat), float.Parse(split[2], CultureInfo.InvariantCulture.NumberFormat), float.Parse(split[3], CultureInfo.InvariantCulture.NumberFormat));
  187.                         break;
  188.                     case "d": this.Transparency = float.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat);
  189.                         break;
  190.                     case "illum": this.Illumination = (IlluminationMode)int.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat);
  191.                         break;
  192.                     case "map_Kd": if (File.Exists(split[1])) this.DiffuseMap = new Texture(split[1]);
  193.                         break;
  194.                 }
  195.             }
  196.  
  197.             this.Program = program;
  198.         }
  199.  
  200.         public void Use()
  201.         {
  202.             if (DiffuseMap != null)
  203.             {
  204.                 Gl.ActiveTexture(TextureUnit.Texture0);
  205.                 Gl.BindTexture(this.DiffuseMap);
  206.                 this.Program["useTexture"].SetValue(true);
  207.             }
  208.             else this.Program["useTexture"].SetValue(false);
  209.  
  210.             this.Program.Use();
  211.  
  212.             this.Program["diffuse"].SetValue(this.Diffuse);
  213.             this.Program["texture"].SetValue(0);
  214.             this.Program["transparency"].SetValue(this.Transparency);
  215.         }
  216.  
  217.         public void Dispose()
  218.         {
  219.             if (DiffuseMap != null) DiffuseMap.Dispose();
  220.             if (Program != null)
  221.             {
  222.                 Program.DisposeChildren = true;
  223.                 Program.Dispose();
  224.             }
  225.         }
  226.     }
  227.  
  228.     public class ObjObject : IDisposable
  229.     {
  230.         private VBO<Vector3> vertices;
  231.         private VBO<Vector3> normals;
  232.         private VBO<Vector2> uvs;
  233.         private VBO<int> triangles;
  234.  
  235.         public string Name { get; private set; }
  236.  
  237.         public ObjMaterial Material { get; set; }
  238.  
  239.         public ObjObject(List<string> lines, Dictionary<string, ObjMaterial> materials, int vertexOffset, int uvOffset)
  240.         {
  241.             // we need at least 1 line to be a valid file
  242.             if (lines.Count == 0) return;
  243.  
  244.             // the first line should contain 'o'
  245.             if (lines[0][0] != 'o' && lines[0][0] != 'g') return;
  246.             this.Name = lines[0].Substring(2);
  247.  
  248.             List<Vector3> vertexList = new List<Vector3>();
  249.             List<Vector2> uvList = new List<Vector2>();
  250.             List<int> triangleList = new List<int>();
  251.             List<Vector2> unpackedUvs = new List<Vector2>();
  252.             List<int> normalsList = new List<int>();
  253.  
  254.             // now we read the lines
  255.             for (int i = 1; i < lines.Count; i++)
  256.             {
  257.                 string[] split = lines[i].Split(' ');
  258.  
  259.                 switch (split[0])
  260.                 {
  261.                     case "v":
  262.                         vertexList.Add(new Vector3(double.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat), double.Parse(split[2], CultureInfo.InvariantCulture.NumberFormat), double.Parse(split[3], CultureInfo.InvariantCulture.NumberFormat)) * 0.025f);
  263.                         break;
  264.                     case "vt":
  265.                         uvList.Add(new Vector2(double.Parse(split[1], CultureInfo.InvariantCulture.NumberFormat), double.Parse(split[2], CultureInfo.InvariantCulture.NumberFormat)));
  266.                         break;
  267.                     case "f":
  268.                         if (split.Length == 5)  // this is a quad, so split it up
  269.                         {
  270.                             string[] split1 = new string[] { split[0], split[1], split[2], split[3] };
  271.                             UnpackFace(split1, vertexOffset, uvOffset, vertexList, uvList, triangleList, unpackedUvs, normalsList);
  272.  
  273.                             string[] split2 = new string[] { split[0], split[1], split[3], split[4] };
  274.                             UnpackFace(split2, vertexOffset, uvOffset, vertexList, uvList, triangleList, unpackedUvs, normalsList);
  275.                         }
  276.                         else UnpackFace(split, vertexOffset, uvOffset, vertexList, uvList, triangleList, unpackedUvs, normalsList);
  277.                         break;
  278.                     case "usemtl":
  279.                         if (materials.ContainsKey(split[1])) Material = materials[split[1]];
  280.                         break;
  281.                 }
  282.             }
  283.  
  284.             // calculate the normals (if they didn't exist)
  285.             Vector3[] vertexData = vertexList.ToArray();
  286.             int[] elementData = triangleList.ToArray();
  287.             Vector3[] normalData = CalculateNormals(vertexData, elementData);
  288.  
  289.             // now convert the lists over to vertex buffer objects to be rendered by OpenGL
  290.             this.vertices = new VBO<Vector3>(vertexData);
  291.             this.normals = new VBO<Vector3>(normalData);
  292.             if (unpackedUvs.Count != 0) this.uvs = new VBO<Vector2>(unpackedUvs.ToArray());
  293.             this.triangles = new VBO<int>(elementData, BufferTarget.ElementArrayBuffer);
  294.         }
  295.  
  296.         private void UnpackFace(string[] split, int vertexOffset, int uvOffset, List<Vector3> vertexList, List<Vector2> uvList, List<int> triangleList, List<Vector2> unpackedUvs, List<int> normalsList)
  297.         {
  298.             string[] indices = new string[] { split[1], split[2], split[3] };
  299.  
  300.             if (split[1].Contains("/"))
  301.             {
  302.                 indices[0] = split[1].Substring(0, split[1].IndexOf("/"));
  303.                 indices[1] = split[2].Substring(0, split[2].IndexOf("/"));
  304.                 indices[2] = split[3].Substring(0, split[3].IndexOf("/"));
  305.  
  306.                 string[] uvs = new string[3];
  307.                 uvs[0] = split[1].Substring(split[1].IndexOf("/") + 1);
  308.                 uvs[1] = split[2].Substring(split[2].IndexOf("/") + 1);
  309.                 uvs[2] = split[3].Substring(split[3].IndexOf("/") + 1);
  310.  
  311.                 int[] triangle = new int[] { int.Parse(indices[0]) - vertexOffset, int.Parse(indices[1]) - vertexOffset, int.Parse(indices[2]) - vertexOffset };
  312.  
  313.                 if (unpackedUvs.Count == 0) for (int j = 0; j < vertexList.Count; j++) unpackedUvs.Add(Vector2.Zero);
  314.                 normalsList.Add(triangle[0]);
  315.                 normalsList.Add(triangle[1]);
  316.                 normalsList.Add(triangle[2]);
  317.  
  318.                 if (unpackedUvs[triangle[0]] == Vector2.Zero) unpackedUvs[triangle[0]] = uvList[int.Parse(uvs[0]) - uvOffset];
  319.                 else
  320.                 {
  321.                     unpackedUvs.Add(uvList[int.Parse(uvs[0]) - uvOffset]);
  322.                     vertexList.Add(vertexList[triangle[0]]);
  323.                     triangle[0] = unpackedUvs.Count - 1;
  324.                 }
  325.  
  326.                 if (unpackedUvs[triangle[1]] == Vector2.Zero) unpackedUvs[triangle[1]] = uvList[int.Parse(uvs[1]) - uvOffset];
  327.                 else
  328.                 {
  329.                     unpackedUvs.Add(uvList[int.Parse(uvs[1]) - uvOffset]);
  330.                     vertexList.Add(vertexList[triangle[1]]);
  331.                     triangle[1] = unpackedUvs.Count - 1;
  332.                 }
  333.  
  334.                 if (unpackedUvs[triangle[2]] == Vector2.Zero) unpackedUvs[triangle[2]] = uvList[int.Parse(uvs[2]) - uvOffset];
  335.                 else
  336.                 {
  337.                     unpackedUvs.Add(uvList[int.Parse(uvs[2]) - uvOffset]);
  338.                     vertexList.Add(vertexList[triangle[2]]);
  339.                     triangle[2] = unpackedUvs.Count - 1;
  340.                 }
  341.  
  342.                 triangleList.Add(triangle[0]);
  343.                 triangleList.Add(triangle[1]);
  344.                 triangleList.Add(triangle[2]);
  345.             }
  346.             else
  347.             {
  348.                 triangleList.Add(int.Parse(indices[0]) - vertexOffset);
  349.                 triangleList.Add(int.Parse(indices[1]) - vertexOffset);
  350.                 triangleList.Add(int.Parse(indices[2]) - vertexOffset);
  351.             }
  352.         }
  353.  
  354.         public static Vector3[] CalculateNormals(Vector3[] vertexData, int[] elementData)
  355.         {
  356.             Vector3 b1, b2, normal;
  357.             Vector3[] normalData = new Vector3[vertexData.Length];
  358.  
  359.             for (int i = 0; i < elementData.Length / 3; i++)
  360.             {
  361.                 int cornerA = elementData[i * 3];
  362.                 int cornerB = elementData[i * 3 + 1];
  363.                 int cornerC = elementData[i * 3 + 2];
  364.  
  365.                 b1 = vertexData[cornerB] - vertexData[cornerA];
  366.                 b2 = vertexData[cornerC] - vertexData[cornerA];
  367.  
  368.                 normal = Vector3.Cross(b1, b2).Normalize();
  369.  
  370.                 normalData[cornerA] += normal;
  371.                 normalData[cornerB] += normal;
  372.                 normalData[cornerC] += normal;
  373.             }
  374.  
  375.             for (int i = 0; i < normalData.Length; i++) normalData[i] = normalData[i].Normalize();
  376.  
  377.             return normalData;
  378.         }
  379.  
  380.         public void Draw()
  381.         {
  382.             if (vertices == null || triangles == null) return;
  383.             //if (Material == null) return;
  384.  
  385.             Gl.Disable(EnableCap.CullFace);
  386.             if (Material != null) Material.Use();
  387.  
  388.             Gl.BindBufferToShaderAttribute(vertices, Material.Program, "vertexPosition");
  389.             Gl.BindBufferToShaderAttribute(normals, Material.Program, "vertexNormal");
  390.             if (uvs != null) Gl.BindBufferToShaderAttribute(uvs, Material.Program, "vertexUV");
  391.             Gl.BindBuffer(triangles);
  392.  
  393.             Gl.DrawElements(BeginMode.Triangles, triangles.Count, DrawElementsType.UnsignedInt, IntPtr.Zero);
  394.         }
  395.  
  396.         public void Dispose()
  397.         {
  398.             if (vertices != null) vertices.Dispose();
  399.             if (normals != null) normals.Dispose();
  400.             if (triangles != null) triangles.Dispose();
  401.         }
  402.     }
  403. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement