better obj parsing & gui scene saving support

- do a better job at parsing OBJs with multiple objects
- correctly save XML scenes when there is no integrator or sampler
metadata
Wenzel Jakob 2010-08-18 17:28:27 +02:00
parent e1054035a3
commit 9fed7118bf
8 changed files with 193 additions and 82 deletions

View File

@ -14,6 +14,11 @@ public:
/// Create a new, empty triangle mesh with the given triangle and vertex count
TriMesh(size_t triangleCount, size_t vertexCount);
/// Create a new, empty triangle mesh with the specified data
TriMesh(const std::string &name, Transform worldToObject,
Triangle *triangles, size_t triangleCount,
Vertex *vertexBuffer, size_t vertexCount);
/// Unserialize a triangle mesh
TriMesh(Stream *stream, InstanceManager *manager);

View File

@ -184,13 +184,17 @@ void Scene::configure() {
m_integrator->configure();
}
if (m_camera == NULL) {
Log(EWarn, "No camera found -- adding a default camera");
Log(EWarn, "No camera found! Adding a default camera.");
Properties props("perspective");
/* Create a perspective camera with 45deg. FOV, which can see the whole scene */
AABB aabb;
for (size_t i=0; i<m_shapes.size(); ++i)
aabb.expandBy(m_shapes[i]->getAABB());
for (size_t i=0; i<m_media.size(); ++i)
aabb.expandBy(m_media[i]->getAABB());
if (!aabb.isValid())
Log(EError, "Unable to set up a default camera -- does the scene contain anything at all?");
Point center = aabb.getCenter();
Vector extents = aabb.getExtents();
Float maxExtents = std::max(extents.x, extents.y);

View File

@ -91,7 +91,7 @@ void Shape::addChild(const std::string &name, ConfigurableObject *child) {
Log(EError, "Shape: Invalid child node!");
}
}
/// Ray intersection test
bool Shape::rayIntersect(const Ray &ray, Intersection &its) const {
Log(EError, "Not implemented!");

View File

@ -8,10 +8,20 @@
MTS_NAMESPACE_BEGIN
TriMesh::TriMesh(size_t triangleCount, size_t vertexCount)
: Shape(Properties()), m_triangleCount(triangleCount), m_vertexCount(vertexCount) {
: Shape(Properties()), m_triangleCount(triangleCount),
m_vertexCount(vertexCount), m_flipNormals(false) {
m_triangles = new Triangle[m_triangleCount];
m_vertexBuffer = new Vertex[m_vertexCount];
m_flipNormals = false;
}
TriMesh::TriMesh(const std::string &name, Transform worldToObject, Triangle *triangles,
size_t triangleCount, Vertex *vertexBuffer, size_t vertexCount)
: Shape(Properties()), m_triangles(triangles), m_triangleCount(triangleCount),
m_vertexBuffer(vertexBuffer), m_vertexCount(vertexCount), m_flipNormals(false) {
m_name = name;
m_worldToObject = worldToObject;
m_objectToWorld.inverse();
}
TriMesh::TriMesh(const Properties &props)

View File

@ -117,10 +117,17 @@ public:
}
void setParent(ConfigurableObject *parent) {
ConfigurableObject::setParent(parent);
if (parent->getClass()->derivesFrom(Shape::m_theClass)) {
m_shape = static_cast<Shape *>(parent);
Shape *shape = static_cast<Shape *>(parent);
if (shape->isCompound())
return;
if (m_parent)
Log(EError, "An area light source cannot be parent of multiple shapes");
ConfigurableObject::setParent(shape);
m_shape = shape;
parent->configure();
m_surfaceArea = m_shape->getSurfaceArea();
} else {

View File

@ -39,9 +39,9 @@ public:
inline Scene *getScene() { return m_scene; }
inline std::string transcode(const XMLCh * const xmlName) const {
const char *value = XMLString::transcode(xmlName);
char *value = XMLString::transcode(xmlName);
std::string result(value);
delete[] value;
XMLString::release(&value);
return result;
}

View File

@ -164,7 +164,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement newSampler, sampler = findUniqueChild(camera, "sampler");
if (sampler.isNull()) {
newSampler = doc.createElement("sampler");
camera.appendChild(sampler);
camera.appendChild(newSampler);
} else {
newSampler = doc.createElement("sampler");
camera.insertAfter(newSampler, sampler);
@ -224,16 +224,13 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
// ====================================================================
QDomElement oldIntegratorNode = findUniqueChild(root, "integrator");
if (oldIntegratorNode.isNull()) {
QMessageBox::critical(parent, parent->tr("Unable to save"),
parent->tr("Unable to save changes: could not find the integrator descriptor in "
"<b>%1</b>. If you are using include files, make sure that the integrator "
"descriptor is located within the main scene file.").arg(ctx->fileName),
QMessageBox::Ok);
return;
}
QDomElement newIntegratorNode = doc.createElement("integrator");
if (oldIntegratorNode.isNull()) {
film.appendChild(oldIntegratorNode);
} else {
film.insertAfter(newIntegratorNode, oldIntegratorNode);
film.removeChild(oldIntegratorNode);
}
const Integrator *integrator = ctx->scene->getIntegrator();
setProperties(doc, newIntegratorNode, integrator->getProperties());

View File

@ -29,9 +29,10 @@ public:
std::vector<Normal> normals;
std::vector<Point2> texcoords;
std::vector<OBJTriangle> triangles;
bool hasNormals = false, hasTexcoords = false;
bool firstVertex = true;
bool hasNormals = false;
bool hasTexCoords = false;
std::string name = m_name;
while (is >> buf) {
if (buf == "v") {
@ -39,6 +40,18 @@ public:
Point p;
is >> p.x >> p.y >> p.z;
vertices.push_back(m_objectToWorld(p));
if (firstVertex) {
if (triangles.size() > 0) {
generateGeometry(name, vertices, normals, texcoords,
triangles, hasNormals, hasTexcoords);
triangles.clear();
}
hasNormals = false;
hasTexcoords = false;
firstVertex = false;
}
} else if (buf == "mtllib") {
cout << "Got mtllib" << endl;
} else if (buf == "vn") {
Normal n;
is >> n.x >> n.y >> n.z;
@ -47,18 +60,23 @@ public:
else
normals.push_back(n);
hasNormals = true;
} else if (buf == "g") {
std::string line;
std::getline(is, line);
name = line.substr(1, line.length()-2);
} else if (buf == "vt") {
std::string line;
Float u, v, w;
std::getline(is, line);
std::istringstream iss(line);
iss >> u >> v >> w;
hasTexCoords = true;
texcoords.push_back(Point2(u, v));
hasTexcoords = true;
} else if (buf == "f") {
std::string line, tmp;
std::getline(is, line);
std::istringstream iss(line);
firstVertex = true;
OBJTriangle t;
iss >> tmp; parse(t, 0, tmp);
iss >> tmp; parse(t, 1, tmp);
@ -74,66 +92,8 @@ public:
}
}
std::vector<Vertex> vertexBuffer(vertices.size());
std::vector<bool> touched(vertices.size());
for (unsigned int i=0; i<vertices.size(); i++) {
vertexBuffer[i].v = vertices[i];
touched[i] = false;
}
/* Collapse the mesh into a more usable form */
m_triangles = new Triangle[triangles.size()];
for (unsigned int i=0; i<triangles.size(); i++) {
Triangle tri;
for (unsigned int j=0; j<3; j++) {
unsigned int vertexId = triangles[i].v[j];
unsigned int normalId = triangles[i].n[j];
unsigned int uvId = triangles[i].uv[j];
if (touched[vertexId] == false) {
if (hasNormals)
vertexBuffer[vertexId].n = normals[normalId];
if (hasTexCoords)
vertexBuffer[vertexId].uv = texcoords[uvId];
touched[vertexId] = true;
tri.idx[j] = vertexId;
} else {
bool safe = true;
if (hasNormals && vertexBuffer[vertexId].n
!= normals[normalId])
safe = false;
if (hasTexCoords && vertexBuffer[vertexId].uv
!= texcoords[uvId])
safe = false;
if (!safe) {
/* This vertex was used in conjunction with a different
normal / texture coordinate. Now we have to duplicate
it. */
Vertex vertex;
vertex.v = vertices[vertexId];
if (hasNormals)
vertex.n = normals[normalId];
if (hasTexCoords)
vertex.uv = texcoords[uvId];
tri.idx[j] = vertexBuffer.size();
vertexBuffer.push_back(vertex);
} else {
tri.idx[j] = vertexId;
}
}
}
m_triangles[i] = tri;
}
m_vertexBuffer = new Vertex[vertexBuffer.size()];
for (unsigned int i=0; i<vertexBuffer.size(); i++)
m_vertexBuffer[i] = vertexBuffer[i];
m_triangleCount = triangles.size();
m_vertexCount = vertexBuffer.size();
calculateTangentSpaceBasis(hasNormals, hasTexCoords);
}
WavefrontOBJ(Stream *stream, InstanceManager *manager) : TriMesh(stream, manager) {
generateGeometry(name, vertices, normals, texcoords,
triangles, hasNormals, hasTexcoords);
}
void parse(OBJTriangle &t, int i, const std::string &str) {
@ -157,7 +117,135 @@ public:
}
}
void generateGeometry(const std::string &name,
const std::vector<Point> &vertices,
const std::vector<Normal> &normals,
const std::vector<Point2> &texcoords,
const std::vector<OBJTriangle> &triangles,
bool hasNormals, bool hasTexcoords) {
if (triangles.size() == 0)
return;
Log(EInfo, "Creating geometry \"%s\"", name.c_str());
std::vector<Vertex> vertexBuffer(vertices.size());
std::vector<bool> touched(vertices.size());
for (unsigned int i=0; i<vertices.size(); i++) {
vertexBuffer[i].v = vertices[i];
vertexBuffer[i].n = Normal(0, 0, 1);
touched[i] = false;
}
/* Collapse the mesh into a more usable form */
Triangle *triangleArray = new Triangle[triangles.size()];
for (unsigned int i=0; i<triangles.size(); i++) {
Triangle tri;
for (unsigned int j=0; j<3; j++) {
unsigned int vertexId = triangles[i].v[j];
unsigned int normalId = triangles[i].n[j];
unsigned int uvId = triangles[i].uv[j];
if (touched[vertexId] == false) {
if (hasNormals)
vertexBuffer[vertexId].n = normals.at(normalId);
if (hasTexcoords)
vertexBuffer[vertexId].uv = texcoords.at(uvId);
touched[vertexId] = true;
tri.idx[j] = vertexId;
} else {
bool safe = true;
if (hasNormals && vertexBuffer.at(vertexId).n
!= normals.at(normalId))
safe = false;
if (hasTexcoords && vertexBuffer.at(vertexId).uv
!= texcoords.at(uvId))
safe = false;
if (!safe) {
/* This vertex was used in conjunction with a different
normal / texture coordinate. Now we have to duplicate
it. */
Vertex vertex;
vertex.v = vertices.at(vertexId);
if (hasNormals)
vertex.n = normals.at(normalId);
if (hasTexcoords)
vertex.uv = texcoords.at(uvId);
tri.idx[j] = vertexBuffer.size();
vertexBuffer.push_back(vertex);
} else {
tri.idx[j] = vertexId;
}
}
}
triangleArray[i] = tri;
}
Vertex *vertexArray = new Vertex[vertexBuffer.size()];
for (unsigned int i=0; i<vertexBuffer.size(); i++)
vertexArray[i] = vertexBuffer[i];
ref<TriMesh> mesh = new TriMesh(name, m_worldToObject, triangleArray,
triangles.size(), vertexArray, vertexBuffer.size());
mesh->incRef();
mesh->calculateTangentSpaceBasis(hasNormals, hasTexcoords);
m_meshes.push_back(mesh);
}
WavefrontOBJ(Stream *stream, InstanceManager *manager) : TriMesh(stream, manager) {
}
virtual ~WavefrontOBJ() {
for (size_t i=0; i<m_meshes.size(); ++i)
m_meshes[i]->decRef();
}
void configure() {
Shape::configure();
m_aabb.reset();
for (size_t i=0; i<m_meshes.size(); ++i) {
m_meshes[i]->configure();
m_aabb.expandBy(m_meshes[i]->getAABB());
}
}
void addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass();
if (cClass->derivesFrom(BSDF::m_theClass)) {
m_bsdf = static_cast<BSDF *>(child);
for (size_t i=0; i<m_meshes.size(); ++i)
m_meshes[i]->addChild(name, child);
} else if (cClass->derivesFrom(Luminaire::m_theClass)) {
Assert(m_luminaire == NULL && m_meshes.size() == 1);
m_luminaire = static_cast<Luminaire *>(child);
for (size_t i=0; i<m_meshes.size(); ++i) {
child->setParent(m_meshes[i]);
m_meshes[i]->addChild(name, child);
}
} else if (cClass->derivesFrom(Subsurface::m_theClass)) {
Assert(m_subsurface == NULL);
m_subsurface = static_cast<Subsurface *>(child);
for (size_t i=0; i<m_meshes.size(); ++i) {
child->setParent(m_meshes[i]);
m_meshes[i]->addChild(name, child);
}
} else {
Shape::addChild(name, child);
}
}
bool isCompound() const {
return true;
}
Shape *getElement(int index) {
if (index >= (int) m_meshes.size())
return NULL;
return m_meshes[index];
}
MTS_DECLARE_CLASS()
private:
std::vector<TriMesh *> m_meshes;
};
MTS_IMPLEMENT_CLASS_S(WavefrontOBJ, false, TriMesh)