图形学基础 | 实现OBJ文件的载入
1. tiny_obj_loader.h 的使用
include这个头文件需要先定义一个宏
#define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h"
1
2
2. tiny_obj_loader.h 中数据结构的介绍
2.1 attrib_t
// Vertex attributes
typedef struct {
std::vector<real_t> vertices; // 'v'
std::vector<real_t> normals; // 'vn'
std::vector<real_t> texcoords; // 'vt'
std::vector<real_t> colors; // extension: vertex colors
} attrib_t;
1
2
3
4
5
6
7
attrib_t 主要存放的就是 OBJ文件中所有的顶点数据信息. 比如:
vertices : 顶点位置信息
normals : 法线信息
texcoords : 纹理坐标信息
colors : 颜色信息
2.2 shape_t
typedef struct {
std::string name;
mesh_t mesh;
path_t path;
} shape_t;
1
2
3
4
5
shape_t 表示的是比如这个物体的一个部分.
在 OBJ文件中, 如 o xxx 表示一个部分的开始.主要存储的信息有:
name : 这部分的名称 xxx
mesh : 构成这个部分的顶点信息. 这里通过使用索引的方式来记录 . 因为所有的数据信息都放在了 attrib_t 中.
path : pairs of indices for lines 按注释的意思是 线段的索引. 可能有问题. 因为没有用到这个.
在这里重点在于 mesh_t 这个数据结构:
// Index struct to support different indices for vtx/normal/texcoord.
// -1 means not used.
typedef struct {
int vertex_index;
int normal_index;
int texcoord_index;
} index_t;
typedef struct {
std::vector<index_t> indices;
std::vector<unsigned char> num_face_vertices; // The number of vertices per
// face. 3 = polygon, 4 = quad,
// ... Up to 255.
std::vector<int> material_ids; // per-face material ID
std::vector<unsigned int> smoothing_group_ids; // per-face smoothing group
// ID(0 = off. positive value
// = group id)
std::vector<tag_t> tags; // SubD tag
} mesh_t;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
索引信息重要放在 std::vector<index_t> indices; 中.
index_t 中是哪些数据信息的索引下标呢:
vertices : 顶点位置信息
normals : 法线信息
texcoords : 纹理坐标信息
3. 通过 tiny_obj_loader.h 读取并存储数据
主要参考了 loader_example.cc 的 PrintInfo(attrib, shapes, materials) 这个函数
bool Object::make_mesh_and_material_by_obj(const char* filename, const char* basepath,bool triangulate){
std::cout << "Loading " << filename << std::endl;
tinyobj::attrib_t attrib; // 所有的数据放在这里
std::vector<tinyobj::shape_t> shapes;
// 一个shape,表示一个部分,
// 其中主要存的是索引坐标 mesh_t类,
// 放在indices中
/*
// -1 means not used.
typedef struct {
int vertex_index;
int normal_index;
int texcoord_index;
} index_t;
*/
std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename,
basepath, triangulate);
// 接下里就是从上面的属性中取值了
if (!warn.empty()) {
std::cout << "WARN: " << warn << std::endl;
}
if (!err.empty()) {
std::cerr << "ERR: " << err << std::endl;
}
if (!ret) {
printf("Failed to load/parse .obj.\n");
return false;
}
// ========================== 将读入的模型数据存入自己定义的数据结构中 ========================
std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl;
std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl;
std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2)
<< std::endl;
std::cout << "# of shapes : " << shapes.size() << std::endl;
std::cout << "# of materials : " << materials.size() << std::endl;
///1. 获取各种材质和纹理
{
for (int i = 0; i < materials.size(); i++) {
Material* m = new Material();
tinyobj::material_t tm = materials[i];
string name = tm.name;
if (name.size()) {
m->name = name;
}
m->ambient.r = tm.ambient[0];
m->ambient.g = tm.ambient[1];
m->ambient.b = tm.ambient[2];
m->diffuse.r = tm.diffuse[0];
m->diffuse.g = tm.diffuse[1];
m->diffuse.b = tm.diffuse[2];
m->specular.r = tm.specular[0];
m->specular.g = tm.specular[1];
m->specular.b = tm.specular[2];
m->transmittance.r = tm.transmittance[0];
m->transmittance.g = tm.transmittance[1];
m->transmittance.b = tm.transmittance[2];
m->emission.r = tm.emission[0];
m->emission.g = tm.emission[1];
m->emission.b = tm.emission[2];
m->shininess = tm.shininess;
m->ior = tm.ior;
m->dissolve = tm.dissolve;
m->illum = tm.illum;
m->pad0 = tm.pad0;
m->ambient_tex_id = -1;
m->diffuse_tex_id = -1;
m->specular_tex_id = -1;
m->specular_highlight_tex_id = -1;
m->bump_tex_id = -1;
m->displacement_tex_id = -1;
m->alpha_tex_id = -1;
m->ambient_texname = "";
m->diffuse_texname = "";
m->specular_texname = "";
m->specular_highlight_texname = "";
m->bump_texname = "";
m->displacement_texname = "";
m->alpha_texname = "";
if (tm.ambient_texname.size()) {
}
if (tm.diffuse_texname.size()) {
}
if (tm.specular_texname.size()) {
}
if (tm.specular_highlight_texname.size()) {
}
if (tm.bump_texname.size()) {
}
if (tm.displacement_texname.size()) {
}
if (tm.alpha_texname.size()) {
}
this->materials.push_back(m);
}
}
/// 2.顶点数据
{
// For each shape 遍历每一个部分
for (size_t i = 0; i < shapes.size(); i++) {
// 这部分的名称
printf("shape[%ld].name = %s\n", static_cast<long>(i),
shapes[i].name.c_str());
// 网格的点数
printf("Size of shape[%ld].mesh.indices: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.indices.size()));
//printf("Size of shape[%ld].path.indices: %lu\n", static_cast<long>(i),static_cast<unsigned long>(shapes[i].path.indices.size()));
//assert(shapes[i].mesh.num_face_vertices.size() == shapes[i].mesh.material_ids.size());
//assert(shapes[i].mesh.num_face_vertices.size() == shapes[i].mesh.smoothing_group_ids.size());
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
Model* model = new Model(); // 每一部分的模型数据
// 顶点数量 = face的数量x3
model->mesh_num = shapes[i].mesh.num_face_vertices.size(http://www.my516.com) * 3;
// 开辟空间
Vertex *mesh_data = new Vertex[model->mesh_num];
size_t index_offset = 0;
// For each face
for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) {
size_t fnum = shapes[i].mesh.num_face_vertices[f];
// 获得所索引下标
tinyobj::index_t idx;
int vertex_index[3];
int normal_index[3];
int texcoord_index[3];
for (size_t v = 0; v < fnum; v++) {
idx = shapes[i].mesh.indices[index_offset + v];
vertex_index[v] = idx.vertex_index;
texcoord_index[v] = idx.texcoord_index;
normal_index[v] = idx.normal_index;
}
for (size_t v = 0; v < fnum; v++) {
// v
mesh_data[index_offset + v].pos.x = attrib.vertices[(vertex_index[v]) * 3 + 0];
mesh_data[index_offset + v].pos.y = attrib.vertices[(vertex_index[v]) * 3 + 1];
mesh_data[index_offset + v].pos.z = attrib.vertices[(vertex_index[v]) * 3 + 2];
mesh_data[index_offset + v].pos.w = 1.0f;
// vt
mesh_data[index_offset + v].tc.u = attrib.texcoords[texcoord_index[v] * 2 + 0];
mesh_data[index_offset + v].tc.v = attrib.texcoords[texcoord_index[v] * 2 + 1];
// vn
mesh_data[index_offset + v].normal.x = attrib.normals[normal_index[v] * 3 + 0];
mesh_data[index_offset + v].normal.y = attrib.normals[normal_index[v] * 3 + 1];
mesh_data[index_offset + v].normal.z = attrib.normals[normal_index[v] * 3 + 2];
mesh_data[index_offset + v].normal.w = 1.0f;
// color
mesh_data[index_offset + v].color.r = 1.0f;
mesh_data[index_offset + v].color.g = 1.0f;
mesh_data[index_offset + v].color.b = 1.0f;
mesh_data[index_offset + v].color.a = 1.0f;
}
// 偏移
index_offset += fnum;
}
model->mesh = mesh_data;
models.push_back(model);
}
}
std::cout << "# Loading Complete #"<< std::endl;
//PrintInfo(attrib, shapes, materials);
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
实现结果
meshlab 观看效果.
导入光栅化渲染器的线框模型
---------------------
图形学基础 | 实现OBJ文件的载入的更多相关文章
- [计算机图形学] OpenGL读取obj文件并显示其3D效果
读取三维网格模型(Wavefront OBJ文件) 无法向立方体:cube.obj 有法向兔子模型:bunny.obj 有法向有纹理八字模型:Eight.obj OBJ文件的格式可参考:http: ...
- obj文件的连接问题以及tlib的基本用法
1.基础研究 用tcc将程序编译为.obj文件. 这里也可以使用tcc -linclude run.c来将run.c文件编译成run.obj文件. 再用tcc对下面的程序进行编译链接,发现提示错误: ...
- OBJ文件
OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模 ...
- 软件光栅器实现(四、OBJ文件加载)
本节介绍软件光栅器的OBJ和MTL文件加载,转载请注明出处. 在管线的应用程序阶段,我们需要设置光栅器所渲染的模型数据.这些模型数据包括模型顶点的坐标.纹理.法线和材质等等,可以由我们手动编写,也可以 ...
- 目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包
前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”) ...
- CSharpGL(9)解析OBJ文件并用CSharpGL渲染
CSharpGL(9)解析OBJ文件并用CSharpGL渲染 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo ...
- python基础知识---操作文件
一.打开文件 open()函数 open函数返回一个文件对象. 用法:open('文件名','模式') 打开文件的模式有: r,只读模式(默认). w,只写模式.[不可读:不存在则创建:存在则删除内 ...
- Java基础之读文件——使用通道读取混合数据2(ReadPrimesMixedData2)
控制台程序,本例读取Java基础之写文件部分(PrimesToFile2)写入的Primes.txt. 方法二:设置一个任意容量的.大小合适的字节缓冲区并且使用来自文件的字节进行填充.然后整理出缓冲区 ...
- Java基础之读文件——使用通道读取混合数据1(ReadPrimesMixedData)
控制台程序,本例读取Java基础之写文件部分(PrimesToFile2)写入的Primes.txt. 方法一:可以在第一个读操作中读取字符串的长度,然后再将字符串和二进制素数值读入到文本中.这种方式 ...
随机推荐
- CodeForces979D:Kuro and GCD and XOR and SUM(Trie树&指针&Xor)
Kuro is currently playing an educational game about numbers. The game focuses on the greatest common ...
- Sorting a Three-Valued Sequence
链接 分析:首先我们先对其进行排序,并看排序以后有多少个元素顺序是对的,接着我们看有多少个元素经过一次交换可以得到的,最后剩下的元素就是经过两次交换可以得到的了. /* PROB:sort3 ID:w ...
- Dijkstra堆优化
Dijkstra是一个非常不错的最短路算法,它使用两层循环进行枚举,通过每次更新蓝白点的方式更新最短路,时间复杂度为O(n^2),优于floyd的O(n^3),不过只能用于计算单源最短路,而且无法处理 ...
- node fs模块
Node.js的文件系统的Api //公共引用 var fs = require('fs'), path = require('path'); 1.读取文件readFile函数 //readFile( ...
- jqGrid Bootstrap
<!DOCTYPE html> <html lang="en"> <head> <!-- The jQuery library is a ...
- 一个表的两个字段具有相同的类型。如何仅用SQL语句交换这两列的数据?
--假设为A B两个字段--查询Select A As B, B As A From TableName --更新Update TableName Set A = B, B = A
- Codeforces630C【水题】
题意: 有一种数只会有7或者8构成,给一个n,问你前n位上有多少个这样的数. 思路: 对于一个 i 位,有2^i个数,然后前n项和就是2^(n+1)-2; 复习一下等比数列求和,等差数列求和吧; ①: ...
- P5162 WD与积木(多项式求逆+生成函数)
传送门 题解 比赛的时候光顾着算某一个\(n\)的答案是多少忘了考虑不同的\(n\)之间的联系了--而且我也很想知道为什么推着推着会变成一个二项式反演-- 设\(f_n\)为\(n\)块积木时的总的层 ...
- 第十七篇 .NET高级技术之XML
Xml 简介(可扩展标记语言) XML优点:容易读懂:格式标准任何语言都内置了XML分析引擎,不用单独进行文件分析引擎的编写. Xml就是用一种格式化的方式来存储数据,我们可以通过用记事本打开. .n ...
- [NOIP2018校模拟赛]T2矩阵分组 Matrix
题目链接: 矩阵分组 分析: 这道题求的是两部分极差当中大的那个的最小值.对于这种求最值的问题,我们很自然(其实并没有)地想到二分答案. 这个题有两个结论: (好像当时看出来了第一个?然后发现下面都不 ...