/// <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. CSP-S 2019 初赛游记

    Day 0 上午考了一套毒瘤的数据结构题,考的我心态爆炸SB出题人 晚上考了一套初赛模拟,只考1h,然后我91分,感觉初赛完全没问题? 回寝室后一直在忙活,整理东西什么的,居然将近12点睡? Day ...

  2. JAVA培训—线程同步--卖票问题

    线程同步方法: (1).同步代码块,格式: synchronized (同步对象){ //同步代码 } (2).同步方法,格式: 在方法前加synchronized修饰 问题: 多个人同时买票. 1. ...

  3. 《iOS开发进阶》书籍目录

    第一部分:iOS开发工具 第二部分:iOS开发实践 第10章 理解内存管理 10.1 引用计数 10.1.1 什么是引用计数,原理是什么 10.1.2 我们为什么需要引用计数 10.1.3 不要向已经 ...

  4. Html转图片 -- wkhtmltox

    关于wkhtmltox,是一个可以把HTML转换为图片和pdf的工具. 不多介绍了,详见官网 https://wkhtmltopdf.org/ PHP 扩展 https://github.com/kr ...

  5. C#中SqlDataAdapter的使用小结---转载

    C#中SqlDataAdapter的使用小结 转载 叁木-Neil 最后发布于2018-06-07 21:29:39 阅读数 8275 收藏 展开 SqlDataAdapter对象 一.特点介绍1.表 ...

  6. 报错google.protobuf.text_format.ParseError: 166:8 : Message type "object_detection.protos.RandomHorizontalFlip" has no field named "i".解决方法

    运行python train.py --logtostderr --train_dir=training/ --pipeline_config_path=training/ssd_mobilenet_ ...

  7. 基于TF-IDF的推荐

    仅作学习使用 基于TF-IDF的推荐: 将文档分词 对于每个term,计算词频TF和逆文本指数IDF,形成term的权重 计算项目文档和用户偏好文档的相似度 参考: https://blog.csdn ...

  8. STM32CubeMX+FreeRTOS 定时器os_timer的使用

    转载:https://blog.csdn.net/jacklondonjia/article/details/78497120在STM32CubeMX的FreeRTOS配置中,使能FreeRTOS的S ...

  9. django静态文件处理

    django静态文件处理   从开始接接触python这门语言已有四年了,中间陆续的学习,又不断的忘记,所以基本上是没有系统的知识体系.但是挺喜欢这门简洁,强大的动态语言.最近自己私人有个项目要做,虽 ...

  10. 40和为S的两个数字

    题目描述 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 输出描述: 对应每个测试案例,输出两个数,小的先输出. 思路 ...