谈谈Processing 3D世界 六 (续)

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数据绘制模型再开一个坑好了。一篇帖子太长了就不容易编辑,老是要审核审核的。

下篇帖子继续。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值