setp5 将OBJ文件导入Processing
既然我们知道obj实际上就是一个文本本件,我们何不索性将其改成txt?这样,我们用Processing的loadStrings()函数就能轻易读取obj的内容到我们程序的内存中,供我们解码后运用。
String dateFileName;
...
String[] lines = loadStrings(dateFileName);
好第五步轻松解决。
setp6 在Processing中解码OBJ文件
结合上一节的知识,我们知道要依靠obj文件来替我们画图,关键是在f(面)的文本行描述上。...
f 2/2/17 8/11/18 4/4/19
f 4/4/19 8/11/18 6/12/20
...
而这些信息都是用顶点属性索引来描述的。所以我们需要用数组来装载属性,使其拥有索引,以供将来绘图函数按照f(面)的索引序列来调用属性数据。
1.新建OBJ读取类
class OBJLoader {
void init(String dateFileName) {
// 载入obj文件,obj文件需要被转换成txt文件
// 以行为单位装载文本
String[] lines = loadStrings(dateFileName);
}
}
2.为各属性计数
// 在类中新建变量用于个属性计数
int vCount, vtCount, fCount; // 顶点位置、UV、面 计数
// 各属性计数实现方法
void attrArrayCount(String[] lines) {
for(int i = 0; i < lines.length; i++) { // 轮询每行文本
// 查找关键字,给属性计数,这里我们只关心v、vt、f
if(lines[i].startsWith("v ")){ // 顶点位置 计数
vCount++;
}else if(lines[i].startsWith("vt ")) { // 顶点UV 计数
vtCount++;
}else if(lines[i].startsWith("f ")){ // 面 计数
fCount++;
}
}
}
3.新建并初始化属性数组容器
// vertices
PVector[] v, vt; // 顶点位置、UV,这里我们先忽略顶点法向量。
// face(*三角面)- fx v/vt/vn v/vt/vn v/vt/vn
int[] fv1, fv2, fv3; // 顶点位置 索引数组
int[] fvt1, fvt2, fvt3; // 顶点UV 索引数组
// 初始化属性索引数组
void initAttrArray() {
// 初始化顶点位置、UV数组
v = new PVector[vCount + 1]; // 注意配合obj文件的语法
vt = new PVector[vtCount + 1]; // 起始数从1开始,这里实际上是让0元素空置了。
// 初始化面数组
fv1 = new int[fCount];
fv2 = new int[fCount];
fv3 = new int[fCount];
fvt1 = new int[fCount];
fvt2 = new int[fCount];
fvt3 = new int[fCount];
}
4.填入属性数据
void assiAttrArray(String[] lines) {
int vIndex = 1; // 因为F的引用是从1开始,所以v的起始号是1
int vtIndex = 1; // 同上
int fIndex = 0; // f本身没有描述索引号,所以我们可以按传统安排索引
for(int i = 0; i < lines.length; i++) {
// 记录顶点位置
if(lines[i].startsWith("v ")){
assiV(lines[i], vIndex);
vIndex++;
// 记录顶点UV纹理坐标
}else if(lines[i].startsWith("vt ")) {
assiVt(lines[i], vtIndex);
vtIndex++;
// 记录面构成
}else if(lines[i].startsWith("f ")){
assiF(lines[i], fIndex);
fIndex++;
}
}
}
接着我们逐步来实现其功能
// 处理顶点位置
void assiV(String line, int vIndex) {
// 把此行信息分解成单个的词语,并提取浮点信息
float[] date = float(splitTokens(line));
v[vIndex] = new PVector(date[1] * UNIT, date[2] * UNIT, date[3] * UNIT);
}
// 处理顶点UV
void assiVt(String line, int vtIndex) {
// 把此行信息分解成单个的词语,并提取浮点信息
float[] date = float(splitTokens(line));
vt[vtIndex] = new PVector(date[1], date[2]);
}
// 处理面
void assiF(String line, int fIndex) {
// 把此行信息分解成单个的词语 f1 | 1/1/1 | 2/3/1 | 3/2/2
String[] date = splitTokens(line);
// 处理面的顶点信息
PVector v1 = fvertex(date[1], fIndex);
PVector v2 = fvertex(date[2], fIndex);
PVector v3 = fvertex(date[3], fIndex);
fv1[fIndex] = int(v1.x);
fv2[fIndex] = int(v2.x);
fv3[fIndex] = int(v3.x);
fvt1[fIndex] = int(v1.y);
fvt2[fIndex] = int(v2.y);
fvt3[fIndex] = int(v3.y);
}
5.处理面的顶点信息
// 处理面的顶点的信息
PVector fvertex(String date, int fIndex) {
PVector value = new PVector();
// 因为不清楚数字的位数:123/21/21 | 25452/...
int last_n = 0;
for(int n = 0; n < 100; n++) {
String s = date.substring(n, n + 1); // 获取从参数开始到参数结束的字符串,这里是逐个审核字符
if( s.equals("/")) { // 如果字符为“/”
value.x = int(date.substring(0, n)); // 截取v属性索引
last_n = n + 1;
break;
}
}
for(int n = last_n; n < 100; n++) {
String s = date.substring(n, n + 1);
if( s.equals("/")) {
value.y = int(date.substring(last_n, n)); // 截取vt属性索引
break;
}
}
return value;
}
这样我们就解码并在程序内存中组织好了所有的网格数据,等待绘制。
6.载入obj数据 - 小结
// obj文件需要被转换成txt文件
// 本类暂时只处理v、vt、f数据
class OBJLoader {
// 顶点属性
PVector[] v, vt; // 位置、UV
// 面-属性索引
// (*三角面)- fx v/vt/vn v/vt/vn v/vt/vn
int[] fv1, fv2, fv3;
int[] fvt1, fvt2, fvt3;
// 属性计数
int vCount, vtCount, fCount;
void init(String dateFileName) {
// 以行为单位装载文本
String[] lines = loadStrings(dateFileName);
// 属性计数
attrArrayCount(lines);
// 属性数组初始化
initAttrArray();
// 属性数组赋值
assiAttrArray(lines);
}
// 各属性计数实现方法
void attrArrayCount(String[] lines) {
for(int i = 0; i < lines.length; i++) { // 轮询每行文本
// 查找关键字,给属性计数
if(lines[i].startsWith("v ")){ // 顶点位置 计数
vCount++;
}else if(lines[i].startsWith("vt ")) { // 顶点UV 计数
vtCount++;
}else if(lines[i].startsWith("f ")){ // 面 计数
fCount++;
}
}
}
// 初始化属性数组
void initAttrArray() {
// 初始化顶点位置、UV数组
v = new PVector[vCount + 1]; // 注意配合obj文件的语法
vt = new PVector[vtCount + 1]; // 起始数从1开始,这里实际上是让0元素空置了。
// 初始化面数组
fv1 = new int[fCount];
fv2 = new int[fCount];
fv3 = new int[fCount];
fvt1 = new int[fCount];
fvt2 = new int[fCount];
fvt3 = new int[fCount];
}
void assiAttrArray(String[] lines) {
int vIndex = 1; // 因为F的引用是从1开始,所以v的起始号是1
int vtIndex = 1; // 同上
int fIndex = 0; // f本身没有描述索引号,所以我们可以按传统安排索引
for(int i = 0; i < lines.length; i++) {
// 记录顶点位置
if(lines[i].startsWith("v ")){
assiV(lines[i], vIndex);
vIndex++;
// 记录顶点UV纹理坐标
}else if(lines[i].startsWith("vt ")) {
assiVt(lines[i], vtIndex);
vtIndex++;
// 记录面构成
}else if(lines[i].startsWith("f ")){
assiF(lines[i], fIndex);
fIndex++;
}
}
}
// 处理顶点位置
void assiV(String line, int vIndex) {
// 把此行信息分解成单个的词语,并提取浮点信息
float[] date = float(splitTokens(line));
v[vIndex] = new PVector(date[1] * UNIT, date[2] * UNIT, date[3] * UNIT);
}
// 处理顶点UV
void assiVt(String line, int vtIndex) {
// 把此行信息分解成单个的词语,并提取浮点信息
float[] date = float(splitTokens(line));
vt[vtIndex] = new PVector(date[1], date[2]);
}
// 处理面
void assiF(String line, int fIndex) {
// 把此行信息分解成单个的词语 f1 | 1/1/1 | 2/3/1 | 3/2/2
String[] date = splitTokens(line);
// 处理面的顶点信息
PVector v1 = fvertex(date[1], fIndex);
PVector v2 = fvertex(date[2], fIndex);
PVector v3 = fvertex(date[3], fIndex);
fv1[fIndex] = int(v1.x);
fv2[fIndex] = int(v2.x);
fv3[fIndex] = int(v3.x);
fvt1[fIndex] = int(v1.y);
fvt2[fIndex] = int(v2.y);
fvt3[fIndex] = int(v3.y);
}
// 处理面的顶点的信息
PVector fvertex(String date, int fIndex) {
PVector value = new PVector();
// 因为不清楚数字的位数:123/21/21 | 25452/...
int last_n = 0;
for(int n = 0; n < 100; n++) {
String s = date.substring(n, n + 1); // 获取从参数开始到参数结束的字符串,这里是逐个审核字符
if( s.equals("/")) { // 如果字符为“/”
value.x = int(date.substring(0, n)); // 截取v属性索引
last_n = n + 1;
break;
}
}
for(int n = last_n; n < 100; n++) {
String s = date.substring(n, n + 1);
if( s.equals("/")) {
value.y = int(date.substring(last_n, n)); // 截取vt属性索引
break;
}
}
return value;
}
}
利用obj数据绘制模型再开一个坑好了。一篇帖子太长了就不容易编辑,老是要审核审核的。
下篇帖子继续。