[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型

作者:u0u0 - iTyran

上一节中,我们分析了OBJ格式。
OBJ格式优点是文本形式,可读性好,缺点也很明显,计算机解析文本过程会比解析二进制文件慢很多。OBJ还有个问题是各种3D建模工具导出的布局格式还不太一样,face还有多边形(超过三边形),不利于在OpenGL ES里面加载。

.3ds文件是OBJ的二进制形式,并且多很多信息。有一个C语言写的开源库可以用来加.3ds文件,这就是lib3ds。GPL的license,直接使用在商业程序里面可能不太理想。这不妨碍我们这篇文章的分析。

教程截图:

  

demo继承于我的另一篇教程:Xcode创建的默认iOS OpenGL ES 2.0 project代码分析
的OpenGLStart,去掉了GLKit着色器代码,添加lib3ds并加载显示。

工程下载地址:http://ityran.com/thread-765-1-1.html

原理
我使用Google SketchUp建了一个简单的三角锥和长方体,分别导出为triangle.3ds和square.3ds。要把模型在OpenGL ES里面显示出来,我们需要模型的两个基本信息:顶点坐标和顶点法线。
.3ds文件保存有顶点坐标,和face序列,而顶点法线需求自行计算。
lib3ds提供了接口读取顶点坐标和face序列,并提供接口计算顶点法线。
我们要做的就是把这些数据读取出来,转换成OpenGL ES接受的数据形式。

TT3ds加载类
为了方便加载模型,我lib3ds的接口封装在一个类里面。
这个类负责加载解析.3ds,并转换为OpenGL ES接受的数据。
注意:一个.3ds里面可能包含多个模型,TT3ds只能处理一个文件一个模型。

先来看TT3ds.h文件。

#import
#import

#import "lib3ds.h"
@interface TT3ds : NSObject {
         char*name;//模型名称
       
         GLuintnvertices;//顶点数
         GLsizeiptrverticesSize;//顶点数组大小
         GLfloat*vertices;//顶点数组指针
         
         GLuintnfaces;//面数
         GLsizeiptrfacesSize;//面数据大小
         GLubyte*faces;//面数据指针
         
         //VBO相关数据定义,OpenGL ES 接受数据
         GLuintvertexArray;
         GLuintvertexArrayBuffer;
         GLuintvertexElementBuffer;
         
         GLenumerror;
} - (id)initWithFilename:(NSString *) fileName;
- (void)bindVertexArray;
- (void)draw;

@end

三个接口,initWithFilename很显然是用来初始化的,用法如下:

testObject = [[TT3ds alloc]initWithFilename:@"triangle"];

参数是triangle而不是triangle.3ds,ofType指定了只能识别3ds文件。

NSString *dsPathname = [[NSBundle mainBundle]pathForResource:fileName ofType:@"3ds"];

后面2个接口是给OpenGL ES框架的
- (void)glkView:(GLKView *)viewdrawInRect:(CGRect)rect
调用的。

lib3ds的文档相当少,只有代码里面的自带demo可以参考一下。
整个流程为参考了3ds2obj这个demo的代码。
lib3ds_file_open返回的Lib3dsFile包含一个node系列。
查找序列里面属性为LIB3DS_NODE_MESH_INSTANCE的node,
一个LIB3DS_NODE_MESH_INSTANCE node是一个模型。
一个.3ds可包含多个模型。

parseMeshNode函数把3ds数据转换为VBO数据。
VBO数据组织如下图所示:

对于的VBO代码如下:

glGenVertexArraysOES(1, &vertexArray);
glBindVertexArrayOES(vertexArray);

glGenBuffers(1, &vertexArrayBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBuffer);
glBufferData(GL_ARRAY_BUFFER, verticesSize, vertices,GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3,GL_FLOAT, GL_FALSE,
sizeof(GLfloat) * 3, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3,GL_FLOAT, GL_FALSE,
              sizeof(GLfloat)* 3, BUFFER_OFFSET(verticesSize / 2));

glGenBuffers(1, &vertexElementBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,vertexElementBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, facesSize,faces, GL_STATIC_DRAW); glBindVertexArrayOES(0);

bindVertexArray
在调用glDrawElements来显示模型前,我们需要告诉OpenGL使用那个数据里显示。
bindVertexArray是TT3ds的一个方法,在TTViewController.m中被调用,封装的目前是避免返回一个vertexArray指针给外部。

- (void)bindVertexArray {
         glBindVertexArrayOES(vertexArray);
}

draw
由于.3ds文件提供的是face序列,这里就需要用到glDrawElements来显示模型,而不是glDrawArrays。
我们已经将element信息通过VBO设置好,TT3ds的draw方法如下:

- (void)draw {
         glDrawElements(GL_TRIANGLES,facesSize, GL_UNSIGNED_BYTE, 0);
}

glDrawElements第4个参数设置0,而不是faces,这里涉及glDrawElements的两种用法:一种是直接在这里设置为faces指针;一种是在VBO里面提前设置好element信息,glDrawElements的时候把第4个参数设置为0。

TTViewController.m中的加载显示模型
第一步在setupGL中初始化模型

glEnable(GL_DEPTH_TEST);
testObject = [[TT3ds alloc]initWithFilename:@"triangle"];

然后修改draw相关代码,红色部分。

- (void)glkView:(GLKView *)viewdrawInRect:(CGRect)rect
{
   glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
    //glBindVertexArrayOES(_vertexArray);
    [testObject bindVertexArray];
   
    // Renderthe object again with ES2
   glUseProgram(_program);    glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0,
_modelViewProjectionMatrix.m);
   glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0,_normalMatrix.m);
   
    //glDrawElements(GL_TRIANGLES,sizeof(gFaces) / sizeof(GLubyte), GL_UNSIGNED_BYTE, 0);
    [testObject draw];
}

运行下工程试试吧,你将看到文章开头的截图。

结束语:
TT3ds功能并不完善,没处理多模型的情况,没优化数据,没异常处理,顶点法线的计算的正确性我不敢保证,正方体的显示颜色上有点不对。
我认为直接使用lib3ds在项目中可能不是很理想,.3ds的数据并没有优化过,太多重复点数据。最理想的方法是:居于lib3ds写一个PC端工具,转换.3ds为另一种优化好的数据格式,再把优化后的数据直接放到OpenGL里面来,这样效率高内存占用也小。

[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型的更多相关文章

  1. [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ格式分析

    [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ文件格式分析作者:yuezang - iTyran     在iOS的3D开发中常常需要导入通过3DS MAX之类 ...

  2. iphone中button按钮显示为圆形解决

    iphone中button按钮显示为圆形解决: 添加样式: -webkit-appearance:button; 如果需要为直角: border-radius:0 在源码中添加如:style=&quo ...

  3. OpenGL ES 2.0 Shader 调试新思路(二): 做一个可用的原型

    OpenGL ES 2.0 Shader 调试新思路(二): 做一个可用的原型 目录 背景介绍 请参考前文OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式 优化 ledCha ...

  4. NeHe OpenGL教程 第三十一课:加载模型

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. 3ds Max绘制一个漂亮的青花瓷碗3D模型

    这篇教程向小伙伴门介绍使用3ds Max绘制一个漂亮的青花瓷碗3D模型方法,教程很不错,很适合大家学习,推荐过来,一起来学习吧! 车削,材质贴图的应用,添加位图,渲染视图 步骤如下: 在桌面找到3DM ...

  6. C#开发BIMFACE系列37 网页集成开发1:审图系统中加载模型或图纸

    系列目录     [已更新最新开发文章,点击查看详细] 在之前的<C#开发BIMFACE系列>中主要介绍了BIMFACE平台提供的服务端API接口的封装开发与测试过程. 服务端API测试通 ...

  7. 在NVIDIA A100 GPU中使用DALI和新的硬件JPEG解码器快速加载数据

    在NVIDIA A100 GPU中使用DALI和新的硬件JPEG解码器快速加载数据 如今,最流行的拍照设备智能手机可以捕获高达4K UHD的图像(3840×2160图像),原始数据超过25 MB.即使 ...

  8. C#开发BIMFACE系列50 Web网页中使用jQuery加载模型与图纸

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] 在前一篇博客<C#开发BIMFACE系列49 Web网页集成BIMFACE应用的技术方案>中介绍了目前市场主流 ...

  9. 关于ligerui 中 grid 表格的扩展搜索功能在远程数据加载时无法使用的解决办法

    要想使用grid里的扩展搜索功能,除了要引用ligerui主要的js文件外,还必须引入下面的JS文件: 1.Source\demos\filter\ligerGrid.showFilter.js 2. ...

随机推荐

  1. PlayerPrefs

    PlayerPrefs http://blog.csdn.net/u011416077/article/details/47334963

  2. 【HttpRunner v3.x】笔记—8.运行testcase的几种方式

    在之前的demo过程中,已经运行过testcase了,那这篇就也来汇总一下,运行case相关的知识点. 一.运行testcase的几种场景 1. 运行单个case 通常单个case的话我会在编辑器里用 ...

  3. ASP.NET Core 3.x控制IHostedService启动顺序浅探

    想写好中间件,这是基础.   一.前言 今天这个内容,基于于ASP.NET Core 3.x. 从3.x开始,ASP.NET Core使用了通用主机模式.它将WebHostBuilder放到了通用的I ...

  4. 20190919-02安装Xshell和CRT远程工具 000 008

    Linux远程登录及相关工具介绍 Linux一般作为服务器使用,而服务器一般放在机房,你不可能在机房操作你的Linux服务器.这时我们就需要远程登录到Linux服务器来管理维护系统. Linux系统中 ...

  5. 【新阁教育】基于Log4Net实现日志信息双向存储(含源码)

    1.引言 在上位机开发中,日志记录是必不可少的,我们可以通过日志记录做日志分析及错误追踪.初学者会采用txt文本写入来实现日志保存,但是文本写入不是线程安全,当存在多个线程同时写入日志时,就会出现一些 ...

  6. 老生常谈SpringAop日志收集与处理做的工具包

    AopLog是基于Spring Aop 和ThreadLocal实现的一个专门对请求方法内容日志的拦截与处理的日志工具包. 场景 : 我想知道一些重要的请求方法的请求参数,响应参数,请求头,以及耗时, ...

  7. 福利来了~Linux一键部署包,环境安装不用愁!!!

    前言 昨天一哥们的弟弟突然问我有没有部署过的Linux,公司连个运维都没有,服务器都要后端部署.... 你有没有相似的遭遇呢?公司规模小,后端即是运维,一份工资干两份活,哈哈~ 为了解决这老弟的困惑, ...

  8. 云计算openstack核心组件——nova计算服务(7)

    一.nova介绍:       Nova 是 OpenStack 最核心的服务,负责维护和管理云环境的计算资源.OpenStack 作为 IaaS 的云操作系统,虚拟机生命周期管理也就是通过 Nova ...

  9. matlab中如何定义函数

    首先建立M文件或直接点击(File/New/Function)建立函数文件,其中函数文件的格式是: function [输出变量] = 函数名称(输入变量) % 注释 % 函数体 如下所示,是编写的一 ...

  10. hystrix文档翻译之metrics

     metrics和监控 动机 HystrixCommands和HystrixObservableCommands执行过程中会产生相关运行情况的metrics.这些metrics对于监控系统表现有很大的 ...