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
|
||||
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue