我有 .obj 文件和 .mtl 文件,但不知道如何导入。我试过这些教程,但它们不起作用。
最佳答案
从 .obj 文件加载对象的一种方法是首先从文件中获取输入流,然后遍历每一行,因为一行由一个顶点组成。然后使用 .obj 文件中的数据实际绘制它的部分是您可以轻松地在网上找到一堆教程的部分,一个示例:http://www.droidnova.com/android-3d-game-tutorial-part-i,312.html .如果您想更深入地了解 Opengl 并真正学到很多东西,您可以使用 Nehe 这个全能的在线教程,它涵盖了很多内容并且非常深入地介绍了 OpenGL 编程:http://nehe.gamedev.net/ .
无论如何,.obj 文件的加载可以像这样开始:
while ((line = reader.readLine()) != null)
{
if (line.startsWith("f"))
{
faces++;
processFLine(line);
} else if (line.startsWith("vn"))
{
normals++;
processVNLine(line);
} else if (line.startsWith("vt"))
{
UVCoords++;
processVTLine(line);
} else if (line.startsWith("v"))
{
vertices++;
processVLine(line);
}
}
除了行以 'f' 开头之外的所有内容都非常简单,您只需将两个或三个值存储在某个东西中,我更喜欢 Vector:
private void processVNLine(String line)
{
String[] tokens = line.split("[ ]+");
int c = tokens.length;
for (int i = 1; i < c; i++)
{ // add the normals to the normal vector
_vn.add(Float.valueOf(tokens[i]));
}
}
对于面部分(行以'f'开头,您最好先检查每个顶点由多少个'/'组成:
private void processFLine(String line)
{
String[] tokens = line.split("[ ]+");
int c = tokens.length;
if (tokens[1].matches("[0-9]+"))
{
caseFEqOne(tokens, c);
}
if (tokens[1].matches("[0-9]+/[0-9]+"))
{
caseFEqTwo(tokens, c);
}
if (tokens[1].matches("[0-9]+//[0-9]+"))
{
caseFEqOneAndThree(tokens, c);
}
if (tokens[1].matches("[0-9]+/[0-9]+/[0-9]+"))
{
caseFEqThree(tokens, c);
}
}
每个面由三个顶点组成,其中每个顶点索引为v/vt/vn。
如果你有 v/vt,就会发生这种情况。它将索引存储在单独的向量中。
private void caseFEqTwo(String[] tokens, int c)
{
for (int i = 1; i < c; i++)
{
Short s = Short.valueOf(tokens[i].split("/")[0]);
s--;
_vPointer.add(s);
s = Short.valueOf(tokens[i].split("/")[1]);
s--;
_vtPointer.add(s);
}
}
现在您已经通过将索引添加到单独的向量或数组中来处理这个问题。
现在您应该拥有由坐标组成的向量和由每种类型(v、vt 或 vn)的索引组成的向量。你也应该有面数。 通过知道面数,您现在可以开始一个从 0 到面数的循环。
如果您现在创建包含最终结果的新向量,则可以在此循环中将索引处的坐标从循环索引移动到结果向量中的循环索引。
这可能需要一些解释,所以这里是一个简单的例子:
private void reArrange()
{
Iterator<Short> i;
short s;
i = _vPointer.iterator();
while (i.hasNext())
{
s = (short) (i.next() * 3);
for (int k = 0; k < 3; k++)
{
_vResult.add(_v.get(s + k));
}
}
i = _vnPointer.iterator();
while (i.hasNext())
{
s = (short) (i.next() * 3);
for (int k = 0; k < 3; k++)
{
_vnResult.add(_vn.get(s + k));
}
}
i = _vtPointer.iterator();
while (i.hasNext())
{
s = (short) (i.next() * 2);
for (int k = 0; k < 2; k++)
{
_vtResult.add(1f - _vt.get(s + k));
}
}
_indices = new short[faces * 3];
for (short k = 0; k < faces * 3; k++)
{
_indices[k] = k;
}
}
现在您已经完全完成并从 .obj 文件中获得了您需要的信息。因此,通过将 thoose 向量转换为 FloatBuffers 并创建一个像 0,1,2,3,4,5 ... 一样简单的 ShortBuffer ... 等等,您可以将 thoose 与 OpenGL-ES 结合使用来绘制您的对象。
(这不是我完全写的,我使用了我在网上某处找到的一个基本概念,但现在找不到了,然后使它非常适合我想要的并稍微调整它以变得更容易明白)
本文未涵盖的是您还加载 .mtl 文件的部分,您在从程序导出对象时通常也会获得该文件。如果你也想实现它,你可以看看这个:http://people.sc.fsu.edu/~jburkardt/data/mtl/mtl.html
如果您在程序中构建一个对象并想要一个更精确的复制品,那么这将是一个很好的东西,它可以为您提供应该如何处理光线到 Material 的值。
如果您有任何问题,请随时提出。
(我知道这可能不是加载 .obj 文件的最佳方式,因为我们使用多达 9 个不同的向量,但如果您也使用序列化,这实际上可能会变得非常快。)
关于android - 如何在 Android 中加载 3d 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6088534/