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 samplermetadata
parent
e1054035a3
commit
9fed7118bf
|
@ -14,6 +14,11 @@ public:
|
||||||
/// Create a new, empty triangle mesh with the given triangle and vertex count
|
/// Create a new, empty triangle mesh with the given triangle and vertex count
|
||||||
TriMesh(size_t triangleCount, size_t vertexCount);
|
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
|
/// Unserialize a triangle mesh
|
||||||
TriMesh(Stream *stream, InstanceManager *manager);
|
TriMesh(Stream *stream, InstanceManager *manager);
|
||||||
|
|
||||||
|
|
|
@ -184,13 +184,17 @@ void Scene::configure() {
|
||||||
m_integrator->configure();
|
m_integrator->configure();
|
||||||
}
|
}
|
||||||
if (m_camera == NULL) {
|
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");
|
Properties props("perspective");
|
||||||
/* Create a perspective camera with 45deg. FOV, which can see the whole scene */
|
/* Create a perspective camera with 45deg. FOV, which can see the whole scene */
|
||||||
AABB aabb;
|
AABB aabb;
|
||||||
for (size_t i=0; i<m_shapes.size(); ++i)
|
for (size_t i=0; i<m_shapes.size(); ++i)
|
||||||
aabb.expandBy(m_shapes[i]->getAABB());
|
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();
|
Point center = aabb.getCenter();
|
||||||
Vector extents = aabb.getExtents();
|
Vector extents = aabb.getExtents();
|
||||||
Float maxExtents = std::max(extents.x, extents.y);
|
Float maxExtents = std::max(extents.x, extents.y);
|
||||||
|
|
|
@ -91,7 +91,7 @@ void Shape::addChild(const std::string &name, ConfigurableObject *child) {
|
||||||
Log(EError, "Shape: Invalid child node!");
|
Log(EError, "Shape: Invalid child node!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ray intersection test
|
/// Ray intersection test
|
||||||
bool Shape::rayIntersect(const Ray &ray, Intersection &its) const {
|
bool Shape::rayIntersect(const Ray &ray, Intersection &its) const {
|
||||||
Log(EError, "Not implemented!");
|
Log(EError, "Not implemented!");
|
||||||
|
|
|
@ -8,10 +8,20 @@
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
TriMesh::TriMesh(size_t triangleCount, size_t vertexCount)
|
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_triangles = new Triangle[m_triangleCount];
|
||||||
m_vertexBuffer = new Vertex[m_vertexCount];
|
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)
|
TriMesh::TriMesh(const Properties &props)
|
||||||
|
|
|
@ -117,10 +117,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void setParent(ConfigurableObject *parent) {
|
void setParent(ConfigurableObject *parent) {
|
||||||
ConfigurableObject::setParent(parent);
|
|
||||||
|
|
||||||
if (parent->getClass()->derivesFrom(Shape::m_theClass)) {
|
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();
|
parent->configure();
|
||||||
m_surfaceArea = m_shape->getSurfaceArea();
|
m_surfaceArea = m_shape->getSurfaceArea();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -39,9 +39,9 @@ public:
|
||||||
inline Scene *getScene() { return m_scene; }
|
inline Scene *getScene() { return m_scene; }
|
||||||
|
|
||||||
inline std::string transcode(const XMLCh * const xmlName) const {
|
inline std::string transcode(const XMLCh * const xmlName) const {
|
||||||
const char *value = XMLString::transcode(xmlName);
|
char *value = XMLString::transcode(xmlName);
|
||||||
std::string result(value);
|
std::string result(value);
|
||||||
delete[] value;
|
XMLString::release(&value);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
|
||||||
QDomElement newSampler, sampler = findUniqueChild(camera, "sampler");
|
QDomElement newSampler, sampler = findUniqueChild(camera, "sampler");
|
||||||
if (sampler.isNull()) {
|
if (sampler.isNull()) {
|
||||||
newSampler = doc.createElement("sampler");
|
newSampler = doc.createElement("sampler");
|
||||||
camera.appendChild(sampler);
|
camera.appendChild(newSampler);
|
||||||
} else {
|
} else {
|
||||||
newSampler = doc.createElement("sampler");
|
newSampler = doc.createElement("sampler");
|
||||||
camera.insertAfter(newSampler, sampler);
|
camera.insertAfter(newSampler, sampler);
|
||||||
|
@ -224,16 +224,13 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
|
|
||||||
QDomElement oldIntegratorNode = findUniqueChild(root, "integrator");
|
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");
|
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();
|
const Integrator *integrator = ctx->scene->getIntegrator();
|
||||||
setProperties(doc, newIntegratorNode, integrator->getProperties());
|
setProperties(doc, newIntegratorNode, integrator->getProperties());
|
||||||
|
|
|
@ -29,9 +29,10 @@ public:
|
||||||
std::vector<Normal> normals;
|
std::vector<Normal> normals;
|
||||||
std::vector<Point2> texcoords;
|
std::vector<Point2> texcoords;
|
||||||
std::vector<OBJTriangle> triangles;
|
std::vector<OBJTriangle> triangles;
|
||||||
|
bool hasNormals = false, hasTexcoords = false;
|
||||||
|
bool firstVertex = true;
|
||||||
|
|
||||||
bool hasNormals = false;
|
std::string name = m_name;
|
||||||
bool hasTexCoords = false;
|
|
||||||
|
|
||||||
while (is >> buf) {
|
while (is >> buf) {
|
||||||
if (buf == "v") {
|
if (buf == "v") {
|
||||||
|
@ -39,6 +40,18 @@ public:
|
||||||
Point p;
|
Point p;
|
||||||
is >> p.x >> p.y >> p.z;
|
is >> p.x >> p.y >> p.z;
|
||||||
vertices.push_back(m_objectToWorld(p));
|
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") {
|
} else if (buf == "vn") {
|
||||||
Normal n;
|
Normal n;
|
||||||
is >> n.x >> n.y >> n.z;
|
is >> n.x >> n.y >> n.z;
|
||||||
|
@ -47,18 +60,23 @@ public:
|
||||||
else
|
else
|
||||||
normals.push_back(n);
|
normals.push_back(n);
|
||||||
hasNormals = true;
|
hasNormals = true;
|
||||||
|
} else if (buf == "g") {
|
||||||
|
std::string line;
|
||||||
|
std::getline(is, line);
|
||||||
|
name = line.substr(1, line.length()-2);
|
||||||
} else if (buf == "vt") {
|
} else if (buf == "vt") {
|
||||||
std::string line;
|
std::string line;
|
||||||
Float u, v, w;
|
Float u, v, w;
|
||||||
std::getline(is, line);
|
std::getline(is, line);
|
||||||
std::istringstream iss(line);
|
std::istringstream iss(line);
|
||||||
iss >> u >> v >> w;
|
iss >> u >> v >> w;
|
||||||
hasTexCoords = true;
|
|
||||||
texcoords.push_back(Point2(u, v));
|
texcoords.push_back(Point2(u, v));
|
||||||
|
hasTexcoords = true;
|
||||||
} else if (buf == "f") {
|
} else if (buf == "f") {
|
||||||
std::string line, tmp;
|
std::string line, tmp;
|
||||||
std::getline(is, line);
|
std::getline(is, line);
|
||||||
std::istringstream iss(line);
|
std::istringstream iss(line);
|
||||||
|
firstVertex = true;
|
||||||
OBJTriangle t;
|
OBJTriangle t;
|
||||||
iss >> tmp; parse(t, 0, tmp);
|
iss >> tmp; parse(t, 0, tmp);
|
||||||
iss >> tmp; parse(t, 1, tmp);
|
iss >> tmp; parse(t, 1, tmp);
|
||||||
|
@ -74,66 +92,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vertex> vertexBuffer(vertices.size());
|
generateGeometry(name, vertices, normals, texcoords,
|
||||||
std::vector<bool> touched(vertices.size());
|
triangles, hasNormals, hasTexcoords);
|
||||||
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) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse(OBJTriangle &t, int i, const std::string &str) {
|
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()
|
MTS_DECLARE_CLASS()
|
||||||
|
private:
|
||||||
|
std::vector<TriMesh *> m_meshes;
|
||||||
};
|
};
|
||||||
|
|
||||||
MTS_IMPLEMENT_CLASS_S(WavefrontOBJ, false, TriMesh)
|
MTS_IMPLEMENT_CLASS_S(WavefrontOBJ, false, TriMesh)
|
||||||
|
|
Loading…
Reference in New Issue