Algorithm 将多个.OBJ索引缓冲区映射/折叠到OpenGL';s1索引缓冲区

Algorithm 将多个.OBJ索引缓冲区映射/折叠到OpenGL';s1索引缓冲区,algorithm,scala,opengl,.obj,index-buffer,Algorithm,Scala,Opengl,.obj,Index Buffer,我正在尝试加载一个.obj文件,并在gldrawerelements的帮助下绘制它 现在,使用glDrawArrays一切都可以完美地工作,但它当然是低效的 我现在遇到的问题是,.obj文件使用多个索引缓冲区(对于每个属性),而OpenGL可能只使用一个。因此,我需要相应地映射它们 有很多伪算法,我甚至发现了C++实现。我确实知道一些C++,但奇怪的是,它也没有帮助我在Scala中实现。 让我们看看: private def parseObj(path: String): Model = {

我正在尝试加载一个.obj文件,并在
gldrawerelements
的帮助下绘制它

现在,使用
glDrawArrays
一切都可以完美地工作,但它当然是低效的

我现在遇到的问题是,.obj文件使用多个索引缓冲区(对于每个属性),而OpenGL可能只使用一个。因此,我需要相应地映射它们

有很多伪算法,我甚至发现了C++实现。我确实知道一些C++,但奇怪的是,它也没有帮助我在Scala中实现。 让我们看看:

private def parseObj(path: String): Model =
{
    val objSource: List[String] = Source.fromFile(path).getLines.toList

    val positions: List[Vector3] = objSource.filter(_.startsWith("v ")).map(_.split(" ")).map(v => new Vector3(v(1).toFloat,v(2).toFloat,v(3).toFloat))//, 1.0f))
    val normals: List[Vector4] = objSource.filter(_.startsWith("vn ")).map(_.split(" ")).map(v => new Vector4(v(1)toFloat,v(2).toFloat, v(3).toFloat, 0.0f))
    val textureCoordinates: List[Vector2] = objSource.filter(_.startsWith("vt ")).map(_.split(" ")).map(v => new Vector2(v(1).toFloat, 1-v(2).toFloat)) // TODO 1-y because of blender
    val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))

    val vertices: List[Vertex] =  for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))

    val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
    println(f.mkString("\n"))

    val indices: List[Int] = faces.map(f => f._1-1) // Wrong!

    new Model(vertices.toArray, indices.toArray)
}
但我完全被卡住了。我尝试过不同的方法,但却找不到一个好的、干净的解决方案

例如:

val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
面仍然存在的地方:

val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))
有趣的事实我显然还不明白,因为那样我就永远不会得到一个复制品

这意味着结尾处的组合骰子只包含自然数,如:

ListBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...)
这是javascript(抱歉不是scala),但它有注释,应该很容易转换

// bow-tie
var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv -1 1 0\nv -1 -1 0\n" +
    "vt 0 .5\nvt 1 1\nvt 1 0\n" +
    "vn 0 0 1\n" +
    "f 1/1/1 2/2/1 3/3/1\nf 1/1/1 4/2/1 5/3/1";
// output indices should be [0, 1, 2, 0, 3, 4]
// parse the file
var lines = objString.split("\n");
var data = lines.map(function(line) { return line.split(" "); });
var v = [];
var t = [];
var n = [];
var f = [];
var indexMap = new Map(); // HashMap<face:string, index:integer>
var nextIndex = 0;
var vertices = [];
var indices = [];
// fill vertex, texture and normal arrays
data.filter(function(d) { return d[0] == "v"; }).forEach(function(d) { v.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d) { t.push([parseFloat(d[1]), parseFloat(d[2])]); });
data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d) { n.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
//
console.log("V", v.toString());
console.log("T", t.toString());
console.log("N", n.toString());
// create vertices and indices arrays by parsing faces
data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) { 
    var f1 = d[1].split("/").map(function(d) { return parseInt(d)-1; });
    var f2 = d[2].split("/").map(function(d) { return parseInt(d)-1; });
    var f3 = d[3].split("/").map(function(d) { return parseInt(d)-1; });
    // 1
    if(indexMap.has(d[1].toString())) {
        indices.push(indexMap.get(d[1].toString()));
    } else {
        vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
        indexMap.set(d[1].toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 2
    if(indexMap.has(d[2].toString())) {
        indices.push(indexMap.get(d[2].toString()));
    } else {
        vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
        indexMap.set(d[2].toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 3
    if(indexMap.has(d[3].toString())) {
        indices.push(indexMap.get(d[3].toString()));
    } else {
        vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
        indexMap.set(d[3].toString(), nextIndex);
        indices.push(nextIndex++);
    }
});
//
console.log("Vertices", vertices.toString());
console.log("Indices", indices.toString());
三叉戟

我所做的唯一不同的事情是使用表示面一部分的字符串作为索引映射的键(例如:“25/32/5”)

EDITjsFIDLE此版本结合了顶点、纹理和法线的重复值。这将优化OBJ文件,这些文件重复相同的值,使每个面都是唯一的

// bow-tie
var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv 0 0 0\nv -1 1 0\nv -1 -1 0\n" +
    "vt 0 .5\nvt 1 1\nvt 1 0\nvt 0 .5\nvt 1 1\nvt 1 0\n" +
    "vn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\n" +
    "f 1/1/1 2/2/2 3/3/3\nf 4/4/4 5/5/5 6/6/6";
// output indices should be [0, 1, 2, 0, 3, 4]
// parse the file
var lines = objString.split("\n");
var data = lines.map(function(line) { return line.split(" "); });
var v = [];
var t = [];
var n = [];
var f = [];
var vIndexMap = new Map(); // map to earliest index in the list
var vtIndexMap = new Map();
var vnIndexMap = new Map();
var indexMap = new Map(); // HashMap<face:string, index:integer>
var nextIndex = 0;
var vertices = [];
var indices = [];
// fill vertex, texture and normal arrays
data.filter(function(d) { return d[0] == "v"; }).forEach(function(d, i) { 
    v[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]; 
    var key = [d[1], d[2], d[3]].toString();
    if(!vIndexMap.has(key)) {
        vIndexMap.set(key, i);
    }
});
data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d, i) { 
    t[i] = [parseFloat(d[1]), parseFloat(d[2])]; 
    var key = [d[1], d[2]].toString();
    if(!vtIndexMap.has(key)) {
        vtIndexMap.set(key, i);
    }
});
data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d, i) { 
    n[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]; 
    var key = [d[1], d[2], d[3]].toString();
    if(!vnIndexMap.has(key)) {
        vnIndexMap.set(key, i);
    }
});
//
console.log("V", v.toString());
console.log("T", t.toString());
console.log("N", n.toString());
// create vertices and indices arrays by parsing faces
data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) { 
    var f1 = d[1].split("/").map(function(d, i) {
        var index = parseInt(d)-1;
        if(i == 0) index = vIndexMap.get(v[index].toString());
        else if(i == 1) index = vtIndexMap.get(t[index].toString());
        else if(i == 2) index = vnIndexMap.get(n[index].toString());
        return index;
    });
    var f2 = d[2].split("/").map(function(d, i) { 
        var index = parseInt(d)-1;
        if(i == 0) index = vIndexMap.get(v[index].toString());
        else if(i == 1) index = vtIndexMap.get(t[index].toString());
        else if(i == 2) index = vnIndexMap.get(n[index].toString());
        return index;
    });
    var f3 = d[3].split("/").map(function(d, i) { 
        var index = parseInt(d)-1;
        if(i == 0) index = vIndexMap.get(v[index].toString());
        else if(i == 1) index = vtIndexMap.get(t[index].toString());
        else if(i == 2) index = vnIndexMap.get(n[index].toString());
        return index; 
    });
    // 1
    if(indexMap.has(f1.toString())) {
        indices.push(indexMap.get(f1.toString()));
    } else {
        vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
        indexMap.set(f1.toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 2
    if(indexMap.has(f2.toString())) {
        indices.push(indexMap.get(f2.toString()));
    } else {
        vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
        indexMap.set(f2.toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 3
    if(indexMap.has(f3.toString())) {
        indices.push(indexMap.get(f3.toString()));
    } else {
        vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
        indexMap.set(f3.toString(), nextIndex);
        indices.push(nextIndex++);
    }
});
//
console.log("Vertices", vertices.toString());
console.log("Indices", indices.toString());
//领结
var objString=“v 0 0 0\nv 1 1 0\nv 1-1 0\nv 0 0\nv-1 1 0\nv-1 1 0\n”+
“vt 0.5\nvt 1 1\nvt 1 0\nvt 0.5\nvt 1 1\nvt 1 0\n”+
“vn 0 0 1\nvn 0 1\nvn 0 1\nvn 0 1\nvn 0 1\nvn 0 1\n”+
“f 1/1/12/2/23/3\nf 4/4/4 5/5/5 6/6”;
//产出指数应为[0,1,2,0,3,4]
//解析文件
var lines=objString.split(“\n”);
var data=lines.map(函数(行){returnline.split(“”;});
var v=[];
var t=[];
var n=[];
var f=[];
var vIndexMap=新映射();//映射到列表中最早的索引
var vtIndexMap=新映射();
var vnIndexMap=新映射();
var indexMap=new Map();//哈希图
var-nextIndex=0;
var顶点=[];
var指数=[];
//填充顶点、纹理和法线阵列
filter(函数(d){returnd[0]=“v”}).forEach(函数(d,i){
v[i]=[parseFloat(d[1])、parseFloat(d[2])、parseFloat(d[3]);
var key=[d[1],d[2],d[3]].toString();
如果(!vIndexMap.has(键)){
设置(键,i);
}
});
filter(函数(d){returnd[0]==“vt”}).forEach(函数(d,i){
t[i]=[parseFloat(d[1]),parseFloat(d[2]);
var key=[d[1],d[2]].toString();
如果(!vtinexmap.has(key)){
vtinexmap.set(键,i);
}
});
filter(函数(d){returnd[0]==“vn”}).forEach(函数(d,i){
n[i]=[parseFloat(d[1])、parseFloat(d[2])、parseFloat(d[3]);
var key=[d[1],d[2],d[3]].toString();
如果(!vnIndexMap.has(key)){
vnIndexMap.set(键,i);
}
});
//
log(“V”,V.toString());
log(“T”,T.toString());
log(“N”,N.toString());
//通过分析面创建顶点和索引数组
data.filter(函数(d){返回d[0]=“f”}).forEach(函数(d){
var f1=d[1]。拆分(“/”).map(函数(d,i){
var指数=parseInt(d)-1;
如果(i==0)index=vIndexMap.get(v[index].toString());
如果(i==1)index=vtinexmap.get(t[index].toString()),则为else;
如果(i==2)index=vnIndexMap.get(n[index].toString()),则为else;
收益指数;
});
var f2=d[2]。拆分(“/”).map(函数(d,i){
var指数=parseInt(d)-1;
如果(i==0)index=vIndexMap.get(v[index].toString());
如果(i==1)index=vtinexmap.get(t[index].toString()),则为else;
如果(i==2)index=vnIndexMap.get(n[index].toString()),则为else;
收益指数;
});
var f3=d[3]。拆分(“/”).map(函数(d,i){
var指数=parseInt(d)-1;
如果(i==0)index=vIndexMap.get(v[index].toString());
如果(i==1)index=vtinexmap.get(t[index].toString()),则为else;
如果(i==2)index=vnIndexMap.get(n[index].toString()),则为else;
收益指数;
});
// 1
if(indexMap.has(f1.toString())){
index.push(indexMap.get(f1.toString());
}否则{
顶点=顶点。concat(v[f1[0]])。concat(t[f1[1]])。concat(n[f1[2]]);
indexMap.set(f1.toString(),nextIndex);
push(nextIndex++);
}
// 2
if(indexMap.has(f2.toString())){
index.push(indexMap.get(f2.toString());
}否则{
顶点=顶点。concat(v[f2[0]])。concat(t[f2[1]])。concat(n[f2[2]]);
index.map.set(f2.toString(),nextIndex);
push(nextIndex++);
}
// 3
if(indexMap.has(f3.toString())){
index.push(indexMap.get(f3.toString());
}否则{
顶点=顶点.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
index.map.set(f3.toString(),nextIndex);
push(nextIndex++);
}
});
//
log(“顶点”,顶点.toString());
log(“index”,index.toString());

我对Scala一点也不了解,所以我不知道这是否是您已经在做的事情。但是您需要的关键数据结构是一个使用元组的地图,元组的索引是位置、纹理坐标和面记录中的法线,索引是OpenGL顶点的值。这是我的一个旧答案中的伪代码,如果这不是你在搜索中已经找到的帖子之一:。@RetoKoradi:那么对于每个面,我需要创建一个带有键[位置(面._1-1)、纹理坐标(面._2-1)、法线(面._2-1)]的地图条目,然后如何计算OpenGL顶点的索引值?从0开始,并在每次需要新顶点时增加该顶点(即,关键点不在贴图中)。从我链接的伪代码中应该可以很清楚地看到,问题就在于这个方法(我肯定是这样的)
// bow-tie
var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv -1 1 0\nv -1 -1 0\n" +
    "vt 0 .5\nvt 1 1\nvt 1 0\n" +
    "vn 0 0 1\n" +
    "f 1/1/1 2/2/1 3/3/1\nf 1/1/1 4/2/1 5/3/1";
// output indices should be [0, 1, 2, 0, 3, 4]
// parse the file
var lines = objString.split("\n");
var data = lines.map(function(line) { return line.split(" "); });
var v = [];
var t = [];
var n = [];
var f = [];
var indexMap = new Map(); // HashMap<face:string, index:integer>
var nextIndex = 0;
var vertices = [];
var indices = [];
// fill vertex, texture and normal arrays
data.filter(function(d) { return d[0] == "v"; }).forEach(function(d) { v.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d) { t.push([parseFloat(d[1]), parseFloat(d[2])]); });
data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d) { n.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
//
console.log("V", v.toString());
console.log("T", t.toString());
console.log("N", n.toString());
// create vertices and indices arrays by parsing faces
data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) { 
    var f1 = d[1].split("/").map(function(d) { return parseInt(d)-1; });
    var f2 = d[2].split("/").map(function(d) { return parseInt(d)-1; });
    var f3 = d[3].split("/").map(function(d) { return parseInt(d)-1; });
    // 1
    if(indexMap.has(d[1].toString())) {
        indices.push(indexMap.get(d[1].toString()));
    } else {
        vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
        indexMap.set(d[1].toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 2
    if(indexMap.has(d[2].toString())) {
        indices.push(indexMap.get(d[2].toString()));
    } else {
        vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
        indexMap.set(d[2].toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 3
    if(indexMap.has(d[3].toString())) {
        indices.push(indexMap.get(d[3].toString()));
    } else {
        vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
        indexMap.set(d[3].toString(), nextIndex);
        indices.push(nextIndex++);
    }
});
//
console.log("Vertices", vertices.toString());
console.log("Indices", indices.toString());
V 0,0,0,1,1,0,1,-1,0,-1,1,0,-1,-1,0
T 0,0.5,1,1,1,0
N 0,0,1
Vertices 0,0,0,0,0.5,0,0,1,1,1,0,1,1,0,0,1,1,-1,0,1,0,0,0,1,-1,1,0,1,1,0,0,1,-1,-1,0,1,0,0,0,1
Indices 0,1,2,0,3,4
// bow-tie
var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv 0 0 0\nv -1 1 0\nv -1 -1 0\n" +
    "vt 0 .5\nvt 1 1\nvt 1 0\nvt 0 .5\nvt 1 1\nvt 1 0\n" +
    "vn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\n" +
    "f 1/1/1 2/2/2 3/3/3\nf 4/4/4 5/5/5 6/6/6";
// output indices should be [0, 1, 2, 0, 3, 4]
// parse the file
var lines = objString.split("\n");
var data = lines.map(function(line) { return line.split(" "); });
var v = [];
var t = [];
var n = [];
var f = [];
var vIndexMap = new Map(); // map to earliest index in the list
var vtIndexMap = new Map();
var vnIndexMap = new Map();
var indexMap = new Map(); // HashMap<face:string, index:integer>
var nextIndex = 0;
var vertices = [];
var indices = [];
// fill vertex, texture and normal arrays
data.filter(function(d) { return d[0] == "v"; }).forEach(function(d, i) { 
    v[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]; 
    var key = [d[1], d[2], d[3]].toString();
    if(!vIndexMap.has(key)) {
        vIndexMap.set(key, i);
    }
});
data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d, i) { 
    t[i] = [parseFloat(d[1]), parseFloat(d[2])]; 
    var key = [d[1], d[2]].toString();
    if(!vtIndexMap.has(key)) {
        vtIndexMap.set(key, i);
    }
});
data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d, i) { 
    n[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]; 
    var key = [d[1], d[2], d[3]].toString();
    if(!vnIndexMap.has(key)) {
        vnIndexMap.set(key, i);
    }
});
//
console.log("V", v.toString());
console.log("T", t.toString());
console.log("N", n.toString());
// create vertices and indices arrays by parsing faces
data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) { 
    var f1 = d[1].split("/").map(function(d, i) {
        var index = parseInt(d)-1;
        if(i == 0) index = vIndexMap.get(v[index].toString());
        else if(i == 1) index = vtIndexMap.get(t[index].toString());
        else if(i == 2) index = vnIndexMap.get(n[index].toString());
        return index;
    });
    var f2 = d[2].split("/").map(function(d, i) { 
        var index = parseInt(d)-1;
        if(i == 0) index = vIndexMap.get(v[index].toString());
        else if(i == 1) index = vtIndexMap.get(t[index].toString());
        else if(i == 2) index = vnIndexMap.get(n[index].toString());
        return index;
    });
    var f3 = d[3].split("/").map(function(d, i) { 
        var index = parseInt(d)-1;
        if(i == 0) index = vIndexMap.get(v[index].toString());
        else if(i == 1) index = vtIndexMap.get(t[index].toString());
        else if(i == 2) index = vnIndexMap.get(n[index].toString());
        return index; 
    });
    // 1
    if(indexMap.has(f1.toString())) {
        indices.push(indexMap.get(f1.toString()));
    } else {
        vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
        indexMap.set(f1.toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 2
    if(indexMap.has(f2.toString())) {
        indices.push(indexMap.get(f2.toString()));
    } else {
        vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
        indexMap.set(f2.toString(), nextIndex);
        indices.push(nextIndex++);
    }
    // 3
    if(indexMap.has(f3.toString())) {
        indices.push(indexMap.get(f3.toString()));
    } else {
        vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
        indexMap.set(f3.toString(), nextIndex);
        indices.push(nextIndex++);
    }
});
//
console.log("Vertices", vertices.toString());
console.log("Indices", indices.toString());