自制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 ...
随机推荐
- CSS3动画效果结合JS的轮播
<style> *{;} #big{ width: 100%; height: 280px; } .carousel-wrapper{ width: 500px; height: 280p ...
- oracle 11gr2 官方文档下载
http://www.oracle.com/technetwork/database/enterprise-edition/documentation/index.html
- java四种内部类
内部类有成员内部类,局部内部类,匿名内部类,静态内部类. 1,成员内部类package innerClass; public class InnerClassTest { String s1=&quo ...
- Java LinkedList 源码剖析
LinkedList同时实现了List接口和Deque接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack).这样看来,LinkedList简直就 ...
- 用SYS_CONNECT_BY_PATH进行层级查询时的排序问题
用SYS_CONNECT_BY_PATH进行层级查询时, 对同一级别的节点进行排序,可以加order SIBLINGS by 子句实现 WITH N2 AS( SELECT n.ID, org.&qu ...
- ftp 服务器搭建和添加用户和目录
安装: yum install -y vsftpd 修改配置: vsftpd.conf 修改:anonymous_enable=YES 改为:anonymous_enable=NO 启动/停止/重启 ...
- 浅析JVM内存结构和6大区域(转)举例非常好
内存作为系统中重要的资源,对于系统稳定运行和高效运行起到了关键的作用,Java和C之类的语言不同,不需要开发人员来分配内存和回收内存,而是由JVM来管理对象内存的分配以及对象内存的回收(又称为垃圾回收 ...
- python学习粘贴
1. Python通过re模块提供对正则表达式的支持.使用re的一般步骤是先使用re.compile()函数,将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得 ...
- node的事件模块应用(译)
第一次接触Node.js时,就觉得他只不过是用javascript实现的服务端.但实际上他提供了许多浏览器端不具备的方法,比如EventEmitter类.我们在本文中来学习如何使用EventEmitt ...
- (转)详解Linux Top 命令
top 命令是最流行的性能监视工具之一,我们必需了解.它是一个优秀的交互式工具,用于监视性能.它提供系统整体性能,但报告进程信息才是 top 命令的长处.top 命令交互界面如下图所视: