自制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 ...
随机推荐
- Servlet知识
1.Servlet概述 2.编写Servlet的开发步骤a.建立标准的JavaWeb应用目录FirstAppWEB-INFclasseslibweb.xmlb.编写一个类,实现javax.servle ...
- 1075 PAT Judge (25)
排序题 #include <stdio.h> #include <string.h> #include <iostream> #include <algori ...
- 由React学习到Yeoman安装以及遇到的问题
离职闲下来之后想着学一些新知识,本来是想从react入手,结果延伸出去的内容就像一棵树的树枝,不断增加. 学习计划是从这里开始的(6周学习计划,攻克javascript难关 https://zhuan ...
- jsp:软件包 javax.servlet 不存在
jdk里面有javax.servlet和javax.servlet.http这两个包吗? JavaServlet程序运行还需要安装JDK以外的包吗...? why?我运行JavaServlet程序的时 ...
- STL的迭代器和类型萃取
今天就可以把STL库中迭代器的实现,和类型萃取好好整理一下了 迭代器的设计思维是STL的关键所在,在STL的实际运用和泛型思维,迭代器都扮演着十分重要的角色,STL力求把数据容器和算法的概念分开来,于 ...
- hdu 5795
A Simple Nim Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Tota ...
- pragma
在所有的预处理指令中,#pragma指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个 编译器给出了一个方法,在保持与C和C++语言完全兼容的 ...
- set命令
set命令简介 set命令是shell中初学者比较少接触,但是却很有用的一个命令(这里我们说的shell指的是bash).set命令是shell解释器的一个内置命令,用来设置shell解释器的属性,从 ...
- python+selenium+Robot
准备工作: 1.下载python2.7 http://python.org/getit/ 2.下载下载setuptools [python 的基础包工具] 可以帮助我们轻松的下载,构建,安装,升级,卸 ...
- u3d avatar部件的理解
u3d中带动画的fbx文件导入的时候,就会显示一个avatar组件,这个到底干嘛的一直没能很好的理解,翻看网上的介绍,基本都是告诉你,设置humanoid类型动画时,拖拉过去之类,但是这玩意到底存储了 ...