我创建了一个Mesh Loader来将.obj
文件转换为二进制文件,这样我就可以直接将它们加载到我的VertexBuffer
中,而无需先解析它们。
我的代码适用于我用于测试的特定模型。但是在测试了我从互联网上下载的一些模型后,我注意到那些模型没有完全转换。
这就是我如何转换.obj文件(这是直接来自微软在一个例子中提供的obj2vbo.cpp文件,稍作修改):
ifstream objFile(this->fileName.toStdString().c_str(), ifstream::in);
if (!objFile.is_open()) {
ui.statusBar->showMessage("Could not open obj file!");
return false;
}
vector<XMFLOAT3> positions;
vector<XMFLOAT3> normals;
vector<XMFLOAT2> texcoords;
vector<vector<IndexTriplet>> faces;
unsigned int lineNum = 1;
while (objFile.good()) {
string line;
getline(objFile, line);
istringstream lineStream(line);
if (lineStream.peek() != '#') {
string tag;
lineStream >> tag;
if (
tag.compare("mtllib") == 0 ||
tag.compare("o") == 0 ||
tag.compare("g") == 0 ||
tag.compare("usemtl") == 0 ||
tag.compare("s") == 0
) {
cout << "(" << lineNum << "): Warning: Does not support tag \"" << tag << "\"" << endl;
}
else if (tag.compare("v") == 0) {
XMFLOAT3 pos;
lineStream >> pos.x >> pos.y >> pos.z;
positions.push_back(pos);
}
else if (tag.compare("vn") == 0) {
XMFLOAT3 normal;
lineStream >> normal.x >> normal.y >> normal.z;
normals.push_back(normal);
}
else if (tag.compare("vt") == 0) {
XMFLOAT2 texcoord;
lineStream >> texcoord.x >> texcoord.y;
texcoords.push_back(texcoord);
}
else if (tag.compare("f") == 0) {
vector<IndexTriplet> face;
while (lineStream.good()) {
string tripletString;
lineStream >> tripletString;
if (tripletString.size() > 0) {
istringstream tripletStream(tripletString);
IndexTriplet triplet;
triplet.pos = 0;
triplet.norm = 0;
triplet.tex = 0;
tripletStream >> triplet.pos;
if (tripletStream.get() == '/') {
if (tripletStream.peek() != '/') {
tripletStream >> triplet.tex;
}
if (tripletStream.get() == '/') {
tripletStream >> triplet.norm;
}
}
face.push_back(triplet);
}
}
faces.push_back(face);
}
else if (tag.size() > 0) {
// unsupported tag
}
}
lineNum++;
}
objFile.close();
if (positions.size() == 0 || faces.size() == 0) {
ui.statusBar->showMessage("Error: File contains no geometry!");
return false;
}
// Validate Mesh
for (auto face = faces.begin(); face != faces.end(); face++) {
if (face->size() < 3) {
ui.statusBar->showMessage("Error: Face size < 3 invalid!");
return false;
}
for (auto triplet = face->begin(); triplet != face->end(); triplet++) {
if (triplet->pos > positions.size() || triplet->pos < 1) {
ui.statusBar->showMessage("Error: Position index out of range");
return false;
}
if (triplet->norm > normals.size() || triplet->norm < 1) {
ui.statusBar->showMessage("Error: Normal index out of range");
return false;
}
if (triplet->tex > texcoords.size() || triplet->tex < 1) {
ui.statusBar->showMessage("Error: Texcoord index out of range");
return false;
}
}
}
XMFLOAT3 boxMin = positions[faces[0][0].pos - 1];
XMFLOAT3 boxMax = boxMin;
for (auto face = faces.begin(); face != faces.end(); face++)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
XMFLOAT3 pos = positions[triplet->pos - 1];
boxMin.x = min(boxMin.x, pos.x);
boxMin.y = min(boxMin.y, pos.y);
boxMin.z = min(boxMin.z, pos.z);
boxMax.x = max(boxMax.x, pos.x);
boxMax.y = max(boxMax.y, pos.y);
boxMax.z = max(boxMax.z, pos.z);
}
}
XMFLOAT3 boxCenter;
XMStoreFloat3(&boxCenter, ((XMLoadFloat3(&boxMax) + XMLoadFloat3(&boxMin)) / 2.0f));
// If specified in the arguments, normalize geometry to fit within a unit cube
if (true) // Bool variable later...
{
float maxAxis = max(max(boxMax.x - boxMin.x, boxMax.y - boxMin.y), boxMax.z - boxMin.z);
for (auto pos = positions.begin(); pos != positions.end(); pos++)
{
XMStoreFloat3(&(*pos), ((XMLoadFloat3(&(*pos)) - XMLoadFloat3(&boxCenter)) / maxAxis));
}
}
// Generate missing normals
for (auto face = faces.begin(); face != faces.end(); face++)
{
XMFLOAT3 normal(0, 0, 0);
bool normalGenerated = false;
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
if (!normalGenerated && triplet->norm == 0)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
XMFLOAT3 posThis = positions[triplet->pos - 1];
XMFLOAT3 posPrev = positions[(triplet == face->begin() ? (face->end() - 1)->pos : (triplet - 1)->pos) - 1];
XMFLOAT3 posNext = positions[(triplet == face->end() - 1 ? (face->begin())->pos : (triplet + 1)->pos) - 1];
XMStoreFloat3(
&normal,
XMVectorAdd(
XMLoadFloat3(&normal),
XMVector3Cross(XMVectorSubtract(XMLoadFloat3(&posNext), XMLoadFloat3(&posThis)), XMVectorSubtract(XMLoadFloat3(&posPrev), XMLoadFloat3(&posThis)))
)
);
triplet->norm = normals.size() + 1;
}
normals.push_back(normal);
normalGenerated = true;
}
}
}
// Fill in missing texture coordinates with (0, 0)
bool missingTexcoordCreated = false;
unsigned int missingTexcoordIndex = 0;
for (auto face = faces.begin(); face != faces.end(); face++)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
if (triplet->tex == 0)
{
if (!missingTexcoordCreated)
{
texcoords.push_back(XMFLOAT2(0.0f, 0.0f));
missingTexcoordIndex = texcoords.size();
missingTexcoordCreated = true;
}
triplet->tex = missingTexcoordIndex;
}
}
}
// Generate unique vertices and convert counter-clockwise faces to clockwise triangles
vector<Vertex> vertices;
vector<unsigned short> indices;
map<IndexTriplet, unsigned short> tripletIndices;
for (auto face = faces.begin(); face != faces.end(); face++)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
if (tripletIndices.find(*triplet) == tripletIndices.end())
{
tripletIndices[*triplet] = static_cast<unsigned short>(vertices.size());
Vertex vertex;
vertex.position = positions[triplet->pos - 1];
vertex.normals = normals[triplet->norm - 1];
vertex.uv = texcoords[triplet->tex - 1];
vertices.push_back(vertex);
}
if (triplet >= face->begin() + 2)
{
indices.push_back(tripletIndices[*face->begin()]);
indices.push_back(tripletIndices[*triplet]);
indices.push_back(tripletIndices[*(triplet - 1)]);
}
}
}
// Dump vertex and index data to the output VBO file
ofstream vboFile("binaryMesh.bin", ofstream::out | ofstream::binary);
if (!vboFile.is_open())
{
ui.statusBar->showMessage("Could not open file for writing!");
return false;
}
unsigned int numVertices = vertices.size();
unsigned int numIndices = indices.size();
vboFile.write(reinterpret_cast<char*>(&numVertices), sizeof(unsigned int));
vboFile.write(reinterpret_cast<char*>(&numIndices), sizeof(unsigned int));
vboFile.write(reinterpret_cast<char*>(&vertices[0]), sizeof(Vertex)* vertices.size());
vboFile.write(reinterpret_cast<char*>(&indices[0]), sizeof(unsigned short)* indices.size());
vboFile.close();
这就是我再次阅读它的方式:
void createMeshData(_In_ byte* meshData, _Out_ VertexBuffer** vertexBuffer, _Out_ uint32* vertexCount)
{
*vertexCount = *reinterpret_cast<uint32*>(meshData);
BasicVertex* vertices = reinterpret_cast<BasicVertex*>(meshData + sizeof(uint32)* 2);
*vertexBuffer = this->m_renderer->createVertexBuffer(vertices, sizeof(BasicVertex) * (*vertexCount), false);
}
我已经确定结构BasicVertex
和Vertex
是相同的。这是一个示例模型(Hulk)以及它的外观呈现方式:
似乎不仅顶点错了,而且纹理坐标也是如此,但这是另一个问题。 如果您需要更多信息,请告诉我。 谢谢你的提示