/// <author>Lukas Eibensteiner</author>
/// <date>19.02.2013</date>
/// <summary>Example of a Wavefront OBJ 3D model importer</summary> using SlimDX;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; namespace Games
{
/// <summary>
/// Class for reading a 3D mesh in the Wavefront OBJ format from a stream.
/// </summary>
public class WavefrontReader
{
/// <summary>
/// Enum for describing the semantic meaning of a line in an OBJ file.
/// </summary>
private enum DataType
{
/// <summary>
/// The line contains nothing or has no or an undefined keyword.
/// </summary>
Empty, /// <summary>
/// The line contains a comment.
/// </summary>
Comment, /// <summary>
/// The line contains a group definition.
/// </summary>
Group, /// <summary>
/// The line contains a smoothing group definitio.
/// </summary>
SmoothingGroup, /// <summary>
/// The line contains a position vector definition.
/// </summary>
Position, /// <summary>
/// The line contains a normal vector definition.
/// </summary>
Normal, /// <summary>
/// The line contains a texture coordinate definition.
/// </summary>
TexCoord, /// <summary>
/// The line contains a face definition.
/// </summary>
Face,
} // Dictionary mapping the DataType enumeration to the corresponding keyword.
private static Dictionary<DataType, string> Keywords
{
get
{
return new Dictionary<DataType, string>()
{
{ DataType.Comment, "#" },
{ DataType.Group, "g" },
{ DataType.SmoothingGroup, "s" },
{ DataType.Position, "v" },
{ DataType.TexCoord, "vt" },
{ DataType.Normal, "vn" },
{ DataType.Face, "f" },
};
}
} /// <summary>
/// Reads a WavefrontObject instance from the stream.
/// </summary>
/// <param name="stream">
/// Stream containing the OBJ file content.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="stream"/> is <c>null</c>.
/// </exception>
/// <exception cref="IOException">
/// Error while reading from the stream.
/// </exception>
public WavefrontObject Read(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream"); // Create the stream reader for the file
var reader = new StreamReader(stream); // Store the lines here
var lines = new List<string>(); // Store the current line here
var current = string.Empty; // Read the file line by line and normalize them
while ((current = reader.ReadLine()) != null)
lines.Add(NormalizeLine(current)); // Create empty mesh instance
var obj = new WavefrontObject(); // Iterate over all lines
foreach (string line in lines)
{
// Get line type and content
DataType type = GetType(line);
string content = GetContent(line, type); // Line is a position
if (type == DataType.Position)
obj.Positions.Add(ParseVector3(content)); // Line is a texture coordinate
if (type == DataType.TexCoord)
obj.Texcoords.Add(ParseVector2(content)); // Line is a normal vector
if (type == DataType.Normal)
obj.Normals.Add(ParseVector3(content)); // Line is a mesh sub group
if (type == DataType.Group)
obj.Groups.Add(new WavefrontFaceGroup() { Name = content }); // Line is a polygon
if (type == DataType.Face)
{
// Create the default group for all faces outside a group
if (obj.Groups.Count == 0)
obj.Groups.Add(new WavefrontFaceGroup()); // Add the face to the last group added
obj.Groups.Last().Faces.Add(ParseFace(content));
}
} return obj;
} // Trim beginning and end and collapse all whitespace in a string to single space.
private string NormalizeLine(string line)
{
return System.Text.RegularExpressions.Regex.Replace(line.Trim(), @"\s+", " ");
} // Get the type of data stored in the specified line.
private DataType GetType(string line)
{
// Iterate over the keywords
foreach (var item in Keywords)
{
var type = item.Key;
var keyword = item.Value; // Line starts with current keyword
if (line.ToLower().StartsWith(keyword.ToLower() + " "))
{
// Return current type
return type;
}
} // No type
return DataType.Empty;
} // Remove the keyword from the start of the line and return the result.
// Returns an empty string if the specified type was DataType.Empty.
private string GetContent(string line, DataType type)
{
// If empty return empty string,
// else remove the keyword from the start
return type == DataType.Empty
? string.Empty
: line.Substring(Keywords[type].Length).Trim();
} // Create an array of floats of arbitary length from a string representation,
// where the floats are spearated by whitespace.
private static float[] ParseFloatArray(string str, int count)
{
var floats = new float[count]; var segments = str.Split(' '); for (int i = 0; i < count; i++)
{
if (i < segments.Length)
{
try
{
floats[i] = float.Parse(segments[i], System.Globalization.CultureInfo.InvariantCulture);
}
catch
{
floats[i] = 0;
}
}
} return floats;
} // Parse a 3D vector from a string definition in the form of: 2.0 3.0 1.0
private Vector2 ParseVector2(string str)
{
var components = ParseFloatArray(str, 3); var vec = new Vector2(components[0], components[1]); return components[2] == 0
? vec
: vec / components[2];
} // Parse a 3D vector from a string definition in the form of: 1.0 2.0 3.0 1.0
private Vector3 ParseVector3(string str)
{
var components = ParseFloatArray(str, 4); var vec = new Vector3(components[0], components[1], components[2]); return components[3] == 0
? vec
: vec / components[3];
} // Parse a OBJ face from a string definition.
private WavefrontFace ParseFace(string str)
{
// Split the face definition at whitespace
var segments = str.Split(new Char[0], StringSplitOptions.RemoveEmptyEntries); var vertices = new List<WavefrontVertex>(); // Iterate over the segments
foreach (string segment in segments)
{
// Parse and add the vertex
vertices.Add(ParseVertex(segment));
} // Create and return the face
return new WavefrontFace()
{
Vertices = vertices,
};
} // Parse an OBJ vertex from a string definition in the forms of:
// 1/2/3
// 1//3
// 1/2
// 1
private WavefrontVertex ParseVertex(string str)
{
// Split the string definition at the slash separator
var segments = str.Split('/'); // Store the vertex indices here
var indices = new int[3]; // Iterate 3 times
for (int i = 0; i < 3; i++)
{
// If no segment exists at the location or the segment can not be passed to an integer
// Set the index to zero
if (segments.Length <= i || !int.TryParse(segments[i], out indices[i]))
indices[i] = 0;
} // Create the new vertex
return new WavefrontVertex()
{
Position = indices[0],
Texcoord = indices[1],
Normal = indices[2],
};
}
} /// <summary>
/// Class representing a Wavefront OBJ 3D mesh.
/// </summary>
public class WavefrontObject
{
public WavefrontObject()
{
Groups = new List<WavefrontFaceGroup>();
Positions = new List<Vector3>();
Texcoords = new List<Vector2>();
Normals = new List<Vector3>();
} // Lists containing the vertex components
public List<Vector3> Positions { get; private set; }
public List<Vector2> Texcoords { get; private set; }
public List<Vector3> Normals { get; private set; } // List of sub meshes
public List<WavefrontFaceGroup> Groups { get; private set; }
} /// <summary>
/// Struct representing an Wavefront OBJ face group.
/// </summary>
/// <remarks>
/// Groups contain faces and subdivide a geometry into smaller objects.
/// </remarks>
public class WavefrontFaceGroup
{
public WavefrontFaceGroup()
{
Faces = new List<WavefrontFace>();
} // Name of the sub mesh
public string Name { get; set; } // A list of faces
public List<WavefrontFace> Faces { get; set; } // Get the total number of triangles
public int TriangleCount
{
get
{
var count = 0; foreach (var face in Faces)
count += face.TriangleCount; return count;
}
}
} /// <summary>
/// A struct representing a Wavefront OBJ geometry face.
/// </summary>
/// <remarks>
/// A face is described through a list of OBJ vertices.
/// It can consist of three or more vertices an can therefore be split up
/// into one or more triangles.
/// </remarks>
public struct WavefrontFace
{
public List<WavefrontVertex> Vertices { get; set; } // Number of triangles the face (polygon) consists of
public int TriangleCount
{
get
{
return Vertices.Count - 2;
}
} // Number of vertices
public int VertexCount
{
get { return Vertices.Count; }
}
} /// <summary>
/// A struct representing an Wavefront OBJ vertex.
/// </summary>
/// <remarks>
/// OBJ vertices are indexed vertices so instead of vectors
/// it has an index for the position, texture coordinate and normal.
/// Each of those indices points to a location in a list of vectors.
/// </remarks>
public struct WavefrontVertex
{
public WavefrontVertex(int position, int texcoord, int normal)
: this()
{
Position = position;
Texcoord = texcoord;
Normal = normal;
} // Inidices of the vertex components
public int Position { get; set; }
public int Normal { get; set; }
public int Texcoord { get; set; }
}
}

  

C# OBJ模型解析的封装(网上看到的保留一份)的更多相关文章

  1. Obj模型功能完善(物体材质,光照,法线贴图).Cg着色语言+OpenTK+F#实现.

    这篇文章给大家讲Obj模型里一些基本功能的完善,包含Cg着色语言,矩阵转换,光照,多重纹理,法线贴图的运用. 在上篇中,我们用GLSL实现了基本的phong光照,这里用Cg着色语言来实现另一钟Blin ...

  2. OpenGL OBJ模型加载.

    在我们前面绘制一个屋,我们可以看到,需要每个立方体一个一个的自己来推并且还要处理位置信息.代码量大并且要时间.现在我们通过加载模型文件的方法来生成模型文件,比较流行的3D模型文件有OBJ,FBX,da ...

  3. 【Android开发精要笔记】Android组件模型解析

    Android组件模型解析 Android中的Mashup 将应用切分成不同类别的组件,通过统一的定位模型和接口标准将他们整合在一起,来共同完成某项任务.在Android的Mashup模式下,每个组件 ...

  4. ASP.NET路由模型解析

    大家好,我又来吹牛逼了 ~-_-~ 转载请注明出处:来自吹牛逼之<ASP.NET路由模型解析> 背景:很多人知道Asp.Net中路由怎么用的,却不知道路由模型内部的运行原理,今天我就给大家 ...

  5. opengl导入obj模型

    在经过查阅各种资料以及各种bug之后,终于成功的实现了导入基本的obj模型. 首相介绍一下什么是obj模型 一.什么是OBJ模型 obj文件实际上是一个文本文档,主要有以下数据,一般可以通过blend ...

  6. 三维引擎导入obj模型全黑总结

    最近有客户试用我们的三维平台,在导入模型的时候,会出现模型全黑和不可见的情况.本文说下全黑的情况. 经过测试,发现可能有如下几种情况. obj 模型没有法线向量 如果obj模型导出的时候没有导出法线向 ...

  7. 三维引擎导入obj模型不可见总结

    最近有客户试用我们的三维平台,在导入模型的时候,会出现模型全黑和不可见的情况.上一篇文章说了全黑的情况.此文说下不可见的情况. 经过测试,发现可能有如下两种情况. 导入的模型不在镜头视野内 导入的模型 ...

  8. 由于OBJ模型的读取引起的Release无问题Debug卡死问题

    有些时候会遇到Release版本正常运行,但是Debug无法运行甚至崩溃,原因有很多种,这里记录一下由于模型文件读取引起的Debug问题. 项目中需要读取一个obj模型文件,30M左右,Debug模式 ...

  9. Caffe学习笔记(一):Caffe架构及其模型解析

    Caffe学习笔记(一):Caffe架构及其模型解析 写在前面:关于caffe平台如何快速搭建以及如何在caffe上进行训练与预测,请参见前面的文章<caffe平台快速搭建:caffe+wind ...

随机推荐

  1. nodejs中this详解

    最近在用Nodejs进行APP运维服务管理系统开发时发现,nodejs中的this经常会变,查了下资料后发现this在不同的代码位置中代表不同的涵义,在实际运用过程中可以用var self = thi ...

  2. web前端面试第一次[定时器]

    BOM中定时器--计时器 定时器参数两个:(函数,时间(单位ms(1000ms=1s))) 时间设置1s,每过1s执行一次函数 //设置定时器 setInterval(funtion(){ alert ...

  3. 「CH6801」棋盘覆盖

    「CH6801」棋盘覆盖 传送门 考虑将棋盘黑白染色,两个都无障碍的相邻的点之间连边,边的容量都为1,然后就求一次最大匹配即可 参考代码: #include <cstring> #incl ...

  4. 五、Centos7安装mysql:第一步查看系统有无mysql,然后删除之

    CentOS下MySQL的彻底卸载 原创 2015年10月12日 00:16:02 标签: 21149 编辑 删除 #################CentOS7下MySQL的卸载######### ...

  5. SVN提交失败:Changing file 'XXX' is forbidden by the server;Access to 'XXX' forbidden

    解决方案:https://blog.csdn.net/m0_38084243/article/details/81503638 个人分析主要是后者,在SVN服务器上添加上我对本项目的读写权限即可: 添 ...

  6. 最新获取SkyDrive音乐外链mp3地址方法20131003

    最新获取SkyDrive音乐外链方法20131003在文章底部更新,欢迎使用! 这已经是第三次写获取SkyDrive音乐外链mp3地址方法的文章了,因为第一次.第二次都失效了.三篇文章都有个共同点,都 ...

  7. 编写跨平台Java程序注意事项

    使用Java语言编写应用程序最大的优点在于“一次编译,处处运行”,然而这并不是说所有的Java程序都具有跨平台的特性,事实上,相当一部分的Java程序是不能在别的操作系统上正确运行的,那么如何才能编写 ...

  8. “嘭、嘭、嘭”---C/S架构下的心跳机制

    本人想使用AU3开发多客户端.一服务端.需要使用到心跳机制,即 在线状态实时更新以及掉线自动重连. 搜索网络发现没有人用AU3写心跳机制. 下面是一篇转帖(原文地址:http://www.cnblog ...

  9. 子组件props接受父组件传递的值 能修改吗?

    vue2.0 子组件props接受父组件传递的值,能不能修改的问题整理 父组件代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...

  10. QQ企业通--客户端登陆模块设计---知识点2

    Maximizable 属性               获取一个值,该值指定窗口是否可以最大化. fromBorderstyle 成员名称    说明 None            无边框. Fi ...