mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
obj reader sort-of works
This commit is contained in:
parent
7a2977446a
commit
858d15d0ba
7 changed files with 233 additions and 41 deletions
190
libraries/fbx/src/OBJReader.cpp
Normal file
190
libraries/fbx/src/OBJReader.cpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QIODevice>
|
||||
|
||||
|
||||
#include "FBXReader.h"
|
||||
#include "OBJReader.h"
|
||||
|
||||
|
||||
|
||||
class OBJTokenizer {
|
||||
public:
|
||||
OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { }
|
||||
enum SpecialToken { DATUM_TOKEN = 0x100 };
|
||||
int nextToken();
|
||||
const QByteArray& getDatum() const { return _datum; }
|
||||
void skipLine() { _device->readLine(); }
|
||||
void pushBackToken(int token) { _pushedBackToken = token; }
|
||||
void ungetChar(char ch) { _device->ungetChar(ch); }
|
||||
|
||||
private:
|
||||
QIODevice* _device;
|
||||
QByteArray _datum;
|
||||
int _pushedBackToken;
|
||||
};
|
||||
|
||||
int OBJTokenizer::nextToken() {
|
||||
if (_pushedBackToken != -1) {
|
||||
int token = _pushedBackToken;
|
||||
_pushedBackToken = -1;
|
||||
return token;
|
||||
}
|
||||
|
||||
char ch;
|
||||
while (_device->getChar(&ch)) {
|
||||
if (QChar(ch).isSpace()) {
|
||||
continue; // skip whitespace
|
||||
}
|
||||
switch (ch) {
|
||||
case '#':
|
||||
_device->readLine(); // skip the comment
|
||||
break;
|
||||
|
||||
case '\"':
|
||||
_datum = "";
|
||||
while (_device->getChar(&ch)) {
|
||||
if (ch == '\"') { // end on closing quote
|
||||
break;
|
||||
}
|
||||
if (ch == '\\') { // handle escaped quotes
|
||||
if (_device->getChar(&ch) && ch != '\"') {
|
||||
_datum.append('\\');
|
||||
}
|
||||
}
|
||||
_datum.append(ch);
|
||||
}
|
||||
return DATUM_TOKEN;
|
||||
|
||||
default:
|
||||
_datum = "";
|
||||
_datum.append(ch);
|
||||
while (_device->getChar(&ch)) {
|
||||
if (QChar(ch).isSpace() || ch == '\"') {
|
||||
ungetChar(ch); // read until we encounter a special character, then replace it
|
||||
break;
|
||||
}
|
||||
_datum.append(ch);
|
||||
}
|
||||
|
||||
return DATUM_TOKEN;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool parseOBJMesh(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, int& indexStart) {
|
||||
FBXMesh mesh;
|
||||
FBXMeshPart meshPart;
|
||||
bool sawG = false;
|
||||
bool meshHasData = true;
|
||||
bool result = true;
|
||||
|
||||
try { // XXX move this out/up
|
||||
while (true) {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
QByteArray token = tokenizer.getDatum();
|
||||
if (token == "g") {
|
||||
if (sawG) {
|
||||
// we've encountered the beginning of the next group.
|
||||
tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
|
||||
break;
|
||||
}
|
||||
sawG = true;
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
QByteArray groupName = tokenizer.getDatum();
|
||||
meshPart.materialID = groupName;
|
||||
meshHasData = true;
|
||||
} else if (token == "v") {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
float x = std::stof(tokenizer.getDatum().data());
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
float y = std::stof(tokenizer.getDatum().data());
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
float z = std::stof(tokenizer.getDatum().data());
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
|
||||
// float w = 1.0;
|
||||
try{
|
||||
// w =
|
||||
std::stof(tokenizer.getDatum().data());
|
||||
}
|
||||
catch(const std::exception& e){
|
||||
// next token wasn't a number (the w field is optional), push it back
|
||||
tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
|
||||
}
|
||||
|
||||
mesh.vertices.append(glm::vec3(x, y, z));
|
||||
meshHasData = true;
|
||||
} else if (token == "f") {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
int p0 = std::stoi(tokenizer.getDatum().data());
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
int p1 = std::stoi(tokenizer.getDatum().data());
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; }
|
||||
int p2 = std::stoi(tokenizer.getDatum().data());
|
||||
|
||||
// obj indexes is 1 based and index over the entire file. mesh indexes are 0 based
|
||||
// and index within the mesh.
|
||||
meshPart.triangleIndices.append(p0 - 1 - indexStart); // obj index is 1 based
|
||||
meshPart.triangleIndices.append(p1 - 1 - indexStart);
|
||||
meshPart.triangleIndices.append(p2 - 1 - indexStart);
|
||||
meshHasData = true;
|
||||
} else {
|
||||
// something we don't (yet) care about
|
||||
qDebug() << "skipping line with" << token;
|
||||
tokenizer.skipLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const std::exception& e) {
|
||||
// something went wrong, stop here.
|
||||
qDebug() << "something went wrong";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (meshHasData) {
|
||||
mesh.parts.append(meshPart);
|
||||
geometry.meshes.append(mesh);
|
||||
}
|
||||
|
||||
indexStart += mesh.vertices.count();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FBXGeometry extractOBJGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
||||
FBXGeometry geometry;
|
||||
return geometry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) {
|
||||
QBuffer buffer(const_cast<QByteArray*>(&model));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
return readOBJ(&buffer, mapping);
|
||||
}
|
||||
|
||||
|
||||
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
|
||||
FBXGeometry geometry;
|
||||
OBJTokenizer tokenizer(device);
|
||||
int indexStart = 0;
|
||||
|
||||
while (true) {
|
||||
if (!parseOBJMesh(tokenizer, mapping, geometry, indexStart)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
6
libraries/fbx/src/OBJReader.h
Normal file
6
libraries/fbx/src/OBJReader.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
|
||||
#include "FBXReader.h"
|
||||
|
||||
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping);
|
||||
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping);
|
|
@ -2101,6 +2101,8 @@ void GeometryReader::run() {
|
|||
lightmapLevel = 3.5f;
|
||||
}
|
||||
fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel);
|
||||
} else if (_url.path().toLower().endsWith(".obj")) {
|
||||
fbxgeo = readOBJ(_reply, _mapping);
|
||||
}
|
||||
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo));
|
||||
} else {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <ResourceCache.h>
|
||||
|
||||
#include <FBXReader.h>
|
||||
#include <OBJReader.h>
|
||||
|
||||
#include <AnimationCache.h>
|
||||
|
||||
|
|
|
@ -13,11 +13,6 @@
|
|||
#include "VHACDUtil.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//Read all the meshes from provided FBX file
|
||||
bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results) {
|
||||
|
||||
|
@ -29,14 +24,31 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re
|
|||
std::cout << "Reading FBX.....\n";
|
||||
|
||||
QByteArray fbxContents = fbx.readAll();
|
||||
FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
|
||||
|
||||
|
||||
FBXGeometry geometry;
|
||||
|
||||
if (filename.toLower().endsWith(".obj")) {
|
||||
geometry = readOBJ(fbxContents, QVariantHash());
|
||||
} else if (filename.toLower().endsWith(".fbx")) {
|
||||
geometry = readFBX(fbxContents, QVariantHash());
|
||||
} else {
|
||||
qDebug() << "unknown file extension";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//results->meshCount = geometry.meshes.count();
|
||||
|
||||
qDebug() << "read in" << geometry.meshes.count() << "meshes";
|
||||
|
||||
int count = 0;
|
||||
foreach(FBXMesh mesh, geometry.meshes){
|
||||
foreach(FBXMesh mesh, geometry.meshes) {
|
||||
//get vertices for each mesh
|
||||
QVector<glm::vec3> vertices = mesh.vertices;
|
||||
|
||||
qDebug() << "vertex count is" << vertices.count();
|
||||
|
||||
//get the triangle indices for each mesh
|
||||
QVector<int> triangles;
|
||||
foreach(FBXMeshPart part, mesh.parts){
|
||||
|
@ -45,9 +57,9 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re
|
|||
}
|
||||
|
||||
//only read meshes with triangles
|
||||
if (triangles.count() <= 0){
|
||||
continue;
|
||||
}
|
||||
if (triangles.count() <= 0){
|
||||
continue;
|
||||
}
|
||||
results->perMeshVertices.append(vertices);
|
||||
results->perMeshTriangleIndices.append(triangles);
|
||||
count++;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <chrono> //c++11 feature
|
||||
#include <QFile>
|
||||
#include <FBXReader.h>
|
||||
#include <OBJReader.h>
|
||||
#include <VHACD.h>
|
||||
|
||||
namespace vhacd {
|
||||
|
|
|
@ -32,7 +32,7 @@ QString formatFloat(double n) {
|
|||
}
|
||||
|
||||
|
||||
bool writeOBJ(QString outFileName, QVector<QVector<VHACD::IVHACD::ConvexHull>>& convexHullList, bool outputOneMesh) {
|
||||
bool writeOBJ(QString outFileName, QVector<QVector<VHACD::IVHACD::ConvexHull>>& meshList, bool outputOneMesh) {
|
||||
QFile file(outFileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qDebug() << "Unable to write to " << outFileName;
|
||||
|
@ -41,52 +41,32 @@ bool writeOBJ(QString outFileName, QVector<QVector<VHACD::IVHACD::ConvexHull>>&
|
|||
|
||||
QTextStream out(&file);
|
||||
|
||||
// if (meshList.size() != 1) {
|
||||
// qDebug() << "unexpected number of meshes --" << meshList.size();
|
||||
// exit(1);
|
||||
// }
|
||||
// QVector<VHACD::IVHACD::ConvexHull> hulls = meshList[0];
|
||||
|
||||
if (convexHullList.size() != 1) {
|
||||
qDebug() << "unexpected number of meshes --" << convexHullList.size();
|
||||
exit(1);
|
||||
}
|
||||
unsigned int pointStartOffset = 0;
|
||||
|
||||
QVector<VHACD::IVHACD::ConvexHull> hulls = convexHullList[0];
|
||||
|
||||
|
||||
if (outputOneMesh) {
|
||||
foreach (QVector<VHACD::IVHACD::ConvexHull> hulls, meshList) {
|
||||
unsigned int nth = 0;
|
||||
foreach (VHACD::IVHACD::ConvexHull hull, hulls) {
|
||||
out << "g hull-" << nth++ << "\n";
|
||||
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
||||
out << "v ";
|
||||
out << formatFloat(hull.m_points[i*3]) << " ";
|
||||
out << formatFloat(hull.m_points[i*3+1]) << " ";
|
||||
out << formatFloat(hull.m_points[i*3+2]) << " 1\n";
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int pointStartOffset = 0;
|
||||
foreach (VHACD::IVHACD::ConvexHull hull, hulls) {
|
||||
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
|
||||
out << "f ";
|
||||
out << hull.m_triangles[i*3] + 1 + pointStartOffset << " ";
|
||||
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
|
||||
out << hull.m_triangles[i*3+2] + 1 + pointStartOffset << "\n";
|
||||
}
|
||||
pointStartOffset += hull.m_nPoints;
|
||||
}
|
||||
} else {
|
||||
unsigned int nth = 0;
|
||||
foreach (VHACD::IVHACD::ConvexHull hull, hulls) {
|
||||
out << "o hull-" << nth++ << "\n";
|
||||
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
||||
out << "v ";
|
||||
out << formatFloat(hull.m_points[i*3]) << " ";
|
||||
out << formatFloat(hull.m_points[i*3+1]) << " ";
|
||||
out << formatFloat(hull.m_points[i*3+2]) << " 1\n";
|
||||
}
|
||||
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
|
||||
out << "f ";
|
||||
out << hull.m_triangles[i*3] + 1 << " ";
|
||||
out << hull.m_triangles[i*3+1] + 1 << " ";
|
||||
out << hull.m_triangles[i*3+2] + 1 << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
pointStartOffset += hull.m_nPoints;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue