mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 15:24:07 +02: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;
|
lightmapLevel = 3.5f;
|
||||||
}
|
}
|
||||||
fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel);
|
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));
|
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <ResourceCache.h>
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
#include <FBXReader.h>
|
#include <FBXReader.h>
|
||||||
|
#include <OBJReader.h>
|
||||||
|
|
||||||
#include <AnimationCache.h>
|
#include <AnimationCache.h>
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,6 @@
|
||||||
#include "VHACDUtil.h"
|
#include "VHACDUtil.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Read all the meshes from provided FBX file
|
//Read all the meshes from provided FBX file
|
||||||
bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results) {
|
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";
|
std::cout << "Reading FBX.....\n";
|
||||||
|
|
||||||
QByteArray fbxContents = fbx.readAll();
|
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();
|
//results->meshCount = geometry.meshes.count();
|
||||||
|
|
||||||
|
qDebug() << "read in" << geometry.meshes.count() << "meshes";
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
foreach(FBXMesh mesh, geometry.meshes) {
|
foreach(FBXMesh mesh, geometry.meshes) {
|
||||||
//get vertices for each mesh
|
//get vertices for each mesh
|
||||||
QVector<glm::vec3> vertices = mesh.vertices;
|
QVector<glm::vec3> vertices = mesh.vertices;
|
||||||
|
|
||||||
|
qDebug() << "vertex count is" << vertices.count();
|
||||||
|
|
||||||
//get the triangle indices for each mesh
|
//get the triangle indices for each mesh
|
||||||
QVector<int> triangles;
|
QVector<int> triangles;
|
||||||
foreach(FBXMeshPart part, mesh.parts){
|
foreach(FBXMeshPart part, mesh.parts){
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <chrono> //c++11 feature
|
#include <chrono> //c++11 feature
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <FBXReader.h>
|
#include <FBXReader.h>
|
||||||
|
#include <OBJReader.h>
|
||||||
#include <VHACD.h>
|
#include <VHACD.h>
|
||||||
|
|
||||||
namespace vhacd {
|
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);
|
QFile file(outFileName);
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
qDebug() << "Unable to write to " << outFileName;
|
qDebug() << "Unable to write to " << outFileName;
|
||||||
|
@ -41,52 +41,32 @@ bool writeOBJ(QString outFileName, QVector<QVector<VHACD::IVHACD::ConvexHull>>&
|
||||||
|
|
||||||
QTextStream out(&file);
|
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) {
|
unsigned int pointStartOffset = 0;
|
||||||
qDebug() << "unexpected number of meshes --" << convexHullList.size();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<VHACD::IVHACD::ConvexHull> hulls = convexHullList[0];
|
foreach (QVector<VHACD::IVHACD::ConvexHull> hulls, meshList) {
|
||||||
|
unsigned int nth = 0;
|
||||||
|
|
||||||
if (outputOneMesh) {
|
|
||||||
foreach (VHACD::IVHACD::ConvexHull hull, hulls) {
|
foreach (VHACD::IVHACD::ConvexHull hull, hulls) {
|
||||||
|
out << "g hull-" << nth++ << "\n";
|
||||||
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
||||||
out << "v ";
|
out << "v ";
|
||||||
out << formatFloat(hull.m_points[i*3]) << " ";
|
out << formatFloat(hull.m_points[i*3]) << " ";
|
||||||
out << formatFloat(hull.m_points[i*3+1]) << " ";
|
out << formatFloat(hull.m_points[i*3+1]) << " ";
|
||||||
out << formatFloat(hull.m_points[i*3+2]) << " 1\n";
|
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++) {
|
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
|
||||||
out << "f ";
|
out << "f ";
|
||||||
out << hull.m_triangles[i*3] + 1 + pointStartOffset << " ";
|
out << hull.m_triangles[i*3] + 1 + pointStartOffset << " ";
|
||||||
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
|
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
|
||||||
out << hull.m_triangles[i*3+2] + 1 + pointStartOffset << "\n";
|
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";
|
out << "\n";
|
||||||
|
pointStartOffset += hull.m_nPoints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue