自制C#版3DS文件的解析器并用SharpGL显示3DS模型
自制C#版3DS文件的解析器并用SharpGL显示3DS模型
据说*.3ds格式的3D模型文件是很古老和过时的格式。本文参考了(http://www.spacesimulator.net/wiki/index.php?title=Tutorials:3ds_Loader)和(http://www.cnblogs.com/lookof/archive/2009/03/27/1423695.html),在此表示感谢。本文讲解如何从零开始用C#写一个3ds文件的解析器,然后用SharpGL(C#对opengl的封装)来显示3ds模型。有图有真相。


上图使用的3ds模型文件和贴图文件在此。(spaceship.zip)(spaceshiptexture.bmp)
3DS文件格式
3ds文件是二进制的。3ds格式的基本单元叫块(chunk)。我们就是读这样一块一块的信息。目录树如下,缩进风格体现了块的父子关系。可见3ds模型文件和XML文件类似,都是只有1个根结点的树状结构。
MAIN CHUNK 0x4D4D
3D EDITOR CHUNK 0x3D3D
OBJECT BLOCK 0x4000
TRIANGULAR MESH 0x4100
VERTICES LIST 0x4110
FACES DESCRIPTION 0x4120
FACES MATERIAL 0x4130
MAPPING COORDINATES LIST 0x4140
SMOOTHING GROUP LIST 0x4150
LOCAL COORDINATES SYSTEM 0x4160
LIGHT 0x4600
SPOTLIGHT 0x4610
CAMERA 0x4700
MATERIAL BLOCK 0xAFFF
MATERIAL NAME 0xA000
AMBIENT COLOR 0xA010
DIFFUSE COLOR 0xA020
SPECULAR COLOR 0xA030
TEXTURE MAP 0xA200
BUMP MAP 0xA230
REFLECTION MAP 0xA220
[SUB CHUNKS FOR EACH MAP]
MAPPING FILENAME 0xA300
MAPPING PARAMETERS 0xA351
KEYFRAMER CHUNK 0xB000
MESH INFORMATION BLOCK 0xB002
SPOT LIGHT INFORMATION BLOCK 0xB007
FRAMES (START AND END) 0xB008
OBJECT NAME 0xB010
OBJECT PIVOT POINT 0xB013
POSITION TRACK 0xB020
ROTATION TRACK 0xB021
SCALE TRACK 0xB022
HIERARCHY POSITION 0xB030
3DS块结构
实际上完整的chunk列表有上千种类型,我们只需解析其中的顶点列表、面列表和纹理UV列表就行了。
以类型标识为0x4D4D的MAIN CHUNK为例,整个3ds文件的前两个byte必须是0x4D4D,否则就说明这个文件不是3ds模型文件。然后从第3到第6个byte是一个Uint32型的数值,表示整个MAIN CHUNK的长度。由于MAIN CHUNK是整个3ds文件的根结点,它的长度也即整个3ds文件的长度。
块(Chunk)的结构
每一个“chunk”的结构如下所示:
|
偏移量 |
长度 |
|
|
0 |
2 |
块标识符 |
|
2 |
4 |
块长: 块数据 + 子块内容 |
|
6 |
n |
块数据 |
|
6+n |
m |
S子块 |
读取的思路是:首先根据偏移量和长度找到一个块的标识符,然后据此来判断它是什么块,遇到我们需要的块,就进一步读取,如果不需要,直接跳过这一块,读取下面的块。
我们的解析器需要顶点、面和贴图UV信息,根据3ds模型文件的树状结构,可以找到需要解析的Chunk如下。
|
MAIN CHUNK |
|
|
Identifier |
0x4d4d |
|
Length |
0 + sub-chunks length |
|
Chunk father |
None |
|
Sub chunks |
3D EDITOR CHUNK |
|
Data |
None |
|
3D EDITOR CHUNK |
|
|
Identifier |
0x3D3D |
|
Length |
0 + sub-chunks length |
|
Chunk father |
MAIN CHUNK |
|
Sub chunks |
OBJECT BLOCK, MATERIAL BLOCK, KEYFRAMER CHUNK |
|
Data |
None |
|
OBJECT BLOCK |
|
|
Identifier |
0x4000 |
|
Length |
Object name length + sub-chunks length |
|
Chunk father |
3D EDITOR CHUNK |
|
Sub chunks |
TRIANGULAR MESH, LIGHT, CAMERA |
|
Data |
Object name |
|
TRIANGULAR MESH |
|
|
Identifier |
0x4100 |
|
Length |
0 + sub-chunks length |
|
Chunk father |
OBJECT BLOCK |
|
Sub chunks |
VERTICES LIST, FACES DESCRIPTION, MAPPING COORDINATES LIST |
|
Data |
None |
|
VERTICES LIST(点数据在这) |
|
|
Identifier |
0x4110 |
|
Length |
varying + sub-chunks length |
|
Chunk father |
TRIANGULAR MESH |
|
Sub chunks |
None |
|
Data |
Vertices number (unsigned short) |
|
FACES DESCRIPTION(面数据在这) |
|
|
Identifier |
0x4120 |
|
Length |
varying + sub-chunks length |
|
Chunk father |
TRIANGULAR MESH |
|
Sub chunks |
FACES |
|
Data |
Polygons number (unsigned short) |
|
MAPPING COORDINATES LIST(贴图数据在这) |
|
|
Identifier |
0x4140 |
|
Length |
varying + sub-chunks length |
|
Chunk father |
TRIANGULAR MESH |
|
Sub chunks |
SMOOTHING |
|
Data |
Vertices number (unsigned short) |
据此给出Chunk的枚举类型
enum ChunkType
{
MainChunk = 0x4D4D,
_3DEditorChunk = 0x3D3D,
CVersion = 0x0002,
KeyFramerChunk = 0xB000,
MaterialBlock = 0xAFFF,
MaterialName = 0xA000,
AmbientColor = 0xA010,
DiffuseColor = 0xA020,
SpecularColor = 0xA030,
C_MATSHININESS = 0xA040,
TextureMap = 0xA200,
MappingFilename = 0xA300,
ObjectBlock = 0x4000,
TriangularMesh = 0x4100,
VerticesList = 0x4110,
FacesDescription = 0x4120,
FacesMaterial = 0x4130,
MappingCoordinatesList = 0x4140
}
ChunkType
解析结果
解析结果为一个3dsFile类型的实例,它包含若干模型(称为entity)。每个entity都含有描述三维模型的顶点、面和贴图UV信息,据此我们用SharpGL来将其显示出来。
foreach (var entity in _3dsFile.Entities)
{
gl.Enable(OpenGL.GL_TEXTURE_2D);
gl.BindTextue(OpenGL.GL_TEXTURE_2D, this.texture.TextureName);
gl.Begin(SharpGL.Enumerations.BeginMode.Triangles);
foreach (var triangle in entity.indices)
{
var point1 = entity.vertices[triangle.vertex1];
var uv1 = entity.texcoords[triangle.vertex1];
gl.TexCoord(uv1.U, uv1.V);
gl.Vertex(point1.X, point1.Y, point1.Z);
var point2 = entity.vertices[triangle.vertex2];
var uv2 = entity.texcoords[triangle.vertex2];
gl.TexCoord(uv2.U, uv2.V);
gl.Vertex(point2.X, point2.Y, point2.Z);
var point3 = entity.vertices[triangle.vertex3];
var uv3 = entity.texcoords[triangle.vertex3];
gl.TexCoord(uv3.U, uv3.V);
gl.Vertex(point3.X, point3.Y, point3.Z);
}
gl.End();
}
用SharpGL显示3DS模型
需要注意的一点是,SharpGL加载的贴图是上下反向的,所以你必须把准备好的贴图上下翻转,才能在SharpGL里正常使用。
自制C#版3DS文件的解析器并用SharpGL显示3DS模型的更多相关文章
- 自制编程语言crowbar(v0.1)构建解析器时分配内存
crowbar中第一次申请内存是在生成解析器的时候: /* interface.c */CRB_Interpreter *CRB_create_interpreter(void) { MEM_Stor ...
- Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器
承接前文Spring源码情操陶冶-自定义节点的解析,本文讲述spring通过context:component-scan节点干了什么事 ComponentScanBeanDefinitionParse ...
- CSharpGL(5)解析3DS文件并用CSharpGL渲染
CSharpGL(5)解析3DS文件并用CSharpGL渲染 我曾经写过一个简单的*.3ds文件的解析器,但是只能解析最基本的顶点.索引信息,且此解析器是仿照别人的C++代码改写的,设计的也不好,不方 ...
- springMVC:为MultipartFilte配置了上传文件解析器,报错或不能使用
一.问题描述为支持restful风格请求,并且应对可能上传文件的情况,需要在配置hiddenHttpMethodFilter过滤器之前配置MultipartFilter.目的是让MultipartFi ...
- 【swupdate文档 四】SWUpdate:使用默认解析器的语法和标记
SWUpdate:使用默认解析器的语法和标记 介绍 SWUpdate使用库"libconfig"作为镜像描述的默认解析器. 但是,可以扩展SWUpdate并添加一个自己的解析器, ...
- PE解析器的编写(一)——总体说明
之前自己学习了PE文件的格式,后来自己写了个PE文件的解析器,这段时间工作上刚好要用到它,老板需要能查看某个exe中加载的dll的一个工具,我在使用之前自己写的这个东西的时候,发现很多东西都忘记了,所 ...
- Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器
本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...
- JAVA与DOM解析器基础 学习笔记
要求 必备知识 JAVA基础知识.XML基础知识. 开发环境 MyEclipse10 资料下载 源码下载 文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的 ...
- [转载]开发 Spring 自定义视图和视图解析器
原文出处 http://www.ibm.com/developerworks/cn/java/j-lo-springview/ 概述 Spring 3.0 默认包含了多种视图和视图解析器,比如 JSP ...
随机推荐
- 手动获取酷我Mp3外链
素材→http://player.kuwo.cn/webmusic/st/getNewMuiseByRid?rid=MUSIC_随便找一首歌http://www.kuwo.cn/yinyue/1034 ...
- Android照片墙加强版,使用ViewPager实现画廊效果
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/12646775 记得关于照片墙的文章我已经写过好几篇了,有最基本的照片墙,有瀑布流模 ...
- jQuery学习-什么是jquery? Js与jquery之间的关系 Jquery选择器
1. 什么是jQuery以及学习的意义等 jQuery是一个js库 JS库是什么? 把常用的方法,进行封装,封装到一个单独的js文件当中,要用的时候直接调用. 学习jQuery主要学什么? 学习jQ ...
- iOS中“返回”操作相关
在程序中,总会设置“返回”按钮,但不可能在每一个控制器中都去设置一次“返回”按钮,那如何设置全局的“返回”按钮呢? 首先自定义一个导航控制器,在tabBarController中添加子控制器时,使用这 ...
- 深入理解c#(第三版)(文摘)
第一部分 基础知识 第1章 C#开发的进化史 1.3 1.3.1 表示未知的价格 public decimal? Price { get; private set; } new ProductWith ...
- C++中未初始化的bool值的问题
原创文件,欢迎阅读,禁止转载. 问题描述 你见过一个这样的bool值吗,判断 var 和 !var 都是成立的,今天被我遇到了,是在一个坑里遇到的.今天调试了一个程序,发送一个网络消息,结果总是得不到 ...
- 如何防止JAVA反射对单例类的攻击?
在我的上篇随笔中,我们知道了创建单例类有以下几种方式: (1).饿汉式; (2).懒汉式(.加同步锁的懒汉式.加双重校验锁的懒汉式.防止指令重排优化的懒汉式); (3).登记式单例模式; (4).静态 ...
- Parameter Passing / Request Parameters in JSF 2.0 (转)
This Blog is a compilation of various methods of passing Request Parameters in JSF (2.0 +) (1) f:vi ...
- 修改centos启动项
centos7下修改启动项在路径/etc/grub.d/文件路径下,修改完成之后需要运行命令 grub2-mkconfig --output=/boot/grub2/grub.cfg
- 解决Chrome重启后插件被禁用的问题
下载组策略模版,添加白名单 http://pan.baidu.com/s/1o88kcZo 打开组策略 win+r 打开运行,输入 gpedit.msc 添加模版 右键 ->管理模版 -&g ...