C# OBJ模型解析的封装(网上看到的保留一份)
/// <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模型解析的封装(网上看到的保留一份)的更多相关文章
- Obj模型功能完善(物体材质,光照,法线贴图).Cg着色语言+OpenTK+F#实现.
这篇文章给大家讲Obj模型里一些基本功能的完善,包含Cg着色语言,矩阵转换,光照,多重纹理,法线贴图的运用. 在上篇中,我们用GLSL实现了基本的phong光照,这里用Cg着色语言来实现另一钟Blin ...
- OpenGL OBJ模型加载.
在我们前面绘制一个屋,我们可以看到,需要每个立方体一个一个的自己来推并且还要处理位置信息.代码量大并且要时间.现在我们通过加载模型文件的方法来生成模型文件,比较流行的3D模型文件有OBJ,FBX,da ...
- 【Android开发精要笔记】Android组件模型解析
Android组件模型解析 Android中的Mashup 将应用切分成不同类别的组件,通过统一的定位模型和接口标准将他们整合在一起,来共同完成某项任务.在Android的Mashup模式下,每个组件 ...
- ASP.NET路由模型解析
大家好,我又来吹牛逼了 ~-_-~ 转载请注明出处:来自吹牛逼之<ASP.NET路由模型解析> 背景:很多人知道Asp.Net中路由怎么用的,却不知道路由模型内部的运行原理,今天我就给大家 ...
- opengl导入obj模型
在经过查阅各种资料以及各种bug之后,终于成功的实现了导入基本的obj模型. 首相介绍一下什么是obj模型 一.什么是OBJ模型 obj文件实际上是一个文本文档,主要有以下数据,一般可以通过blend ...
- 三维引擎导入obj模型全黑总结
最近有客户试用我们的三维平台,在导入模型的时候,会出现模型全黑和不可见的情况.本文说下全黑的情况. 经过测试,发现可能有如下几种情况. obj 模型没有法线向量 如果obj模型导出的时候没有导出法线向 ...
- 三维引擎导入obj模型不可见总结
最近有客户试用我们的三维平台,在导入模型的时候,会出现模型全黑和不可见的情况.上一篇文章说了全黑的情况.此文说下不可见的情况. 经过测试,发现可能有如下两种情况. 导入的模型不在镜头视野内 导入的模型 ...
- 由于OBJ模型的读取引起的Release无问题Debug卡死问题
有些时候会遇到Release版本正常运行,但是Debug无法运行甚至崩溃,原因有很多种,这里记录一下由于模型文件读取引起的Debug问题. 项目中需要读取一个obj模型文件,30M左右,Debug模式 ...
- Caffe学习笔记(一):Caffe架构及其模型解析
Caffe学习笔记(一):Caffe架构及其模型解析 写在前面:关于caffe平台如何快速搭建以及如何在caffe上进行训练与预测,请参见前面的文章<caffe平台快速搭建:caffe+wind ...
随机推荐
- Android音频捕获(录音)(转)
原文:http://www.yiibai.com/android/android_audio_capture.html Android有一个内置的麦克风,通过它可以捕获音频和存储,或在手机进行播放.有 ...
- MyBatis6——一级缓存、二级缓存、逆向工程
查询缓存 一级缓存:同一个sqlSession对象 MyBatis默认开启一级缓存,如果用同样的sqlSession对象查询相同的数据,则会在第一次查询时向数据库发送SQL语句,并将查询的结果放入到S ...
- 中山Day10——普及
今天又是愚蠢的一天,估分230,实得110.其中T2.4不会,这里就只说题意和简要思路. 收获:scanf>>a,以及printf<<a. T1:模板题 此题相对简单,就是读入 ...
- HashSet原理
- 配置<welcome-file>为自定义路径
welcome-file是web.xml中的一个配置,其作用是配置启动项目时默认跳转的欢迎页面,一般我们会将其指定为一个静态页面. 那如果我们要将自定义的请求路径作为欢迎页面该怎么做呢? 1.配置we ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:强制元素隐藏
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- mybatis注解中写SQL语句
参考: https://blog.csdn.net/gebitan505/article/details/54929287/https://blog.csdn.net/KingBoyWorld/art ...
- 修改DUILIB任务栏中显示的图标和EXE图标
在资源中添加ICO图标,获取属性名,在主窗口文件中的函数InitWindow或OnCreate中添加如下代码: SetIcon(IDR_MAINFRAME); 修改EXE显示图标,在主窗口中加入如下代 ...
- ch13 事件(思维导图)
- 「Luogu1402」酒店之王
传送门 Luogu 解题思路 网络流板子题. 建图细节见代码,也可以参考这道差不多的题 细节注意事项 咕咕咕. 参考代码 #include <algorithm> #include < ...