Project::OSiRiON - Git repositories
Project::OSiRiON
News . About . Screenshots . Downloads . Forum . Wiki . Tracker . Git
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/model/Makefile.am2
-rw-r--r--src/model/model.cc6
-rw-r--r--src/model/objfile.cc353
-rw-r--r--src/model/objfile.h113
4 files changed, 474 insertions, 0 deletions
diff --git a/src/model/Makefile.am b/src/model/Makefile.am
index 3552392..dfd462e 100644
--- a/src/model/Makefile.am
+++ b/src/model/Makefile.am
@@ -12,6 +12,7 @@ noinst_HEADERS = \
mapfile.h \
material.h \
model.h \
+ objfile.h \
primitives.h \
quad.h \
tags.h \
@@ -28,6 +29,7 @@ libmodel_la_SOURCES = \
mapfile.cc \
material.cc \
model.cc \
+ objfile.cc \
primitives.cc \
quad.cc \
tags.cc \
diff --git a/src/model/model.cc b/src/model/model.cc
index ab89dae..776eba7 100644
--- a/src/model/model.cc
+++ b/src/model/model.cc
@@ -8,6 +8,7 @@
#include "model/model.h"
#include "model/asefile.h"
#include "model/mapfile.h"
+#include "model/objfile.h"
#include "model/vertexarray.h"
namespace model
@@ -138,6 +139,11 @@ Model *Model::load(const std::string & name)
// if it can't be found, try the ase model
model = ASEFile::load(name);
}
+
+ if (!model) {
+ // if it can't be found, try the obj model
+ model = OBJFile::load(name);
+ }
if (!model) {
con_warn << "Could not open model " << name << std::endl;
diff --git a/src/model/objfile.cc b/src/model/objfile.cc
new file mode 100644
index 0000000..aa6b6cf
--- /dev/null
+++ b/src/model/objfile.cc
@@ -0,0 +1,353 @@
+/*
+ model/objfile.cc
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
+*/
+
+#include "sys/sys.h"
+#include "model/objfile.h"
+
+#include <sstream>
+#include <cstring>
+
+/* NOTE: Does NOT support reverse indexes. Cannot find an example
+ of this in use, or a program that will export using them.
+*/
+
+namespace model
+{
+
+// max geometry bounds
+const float MAX_BOUNDS = 16384.0f;
+
+OBJFile::OBJFile(std::string const &name)
+{
+ objfile_name.append(name);
+ objfile_name.append(".obj");
+ objfile_ifs.open(objfile_name);
+
+ obj_box.assign(MAX_BOUNDS, - MAX_BOUNDS);
+
+ // a single fragmentgroup will contain all the model polygons
+ obj_fragmentgroup = new FragmentGroup();
+ obj_fragmentgroup->set_type(FragmentGroup::None);
+
+ // reset counters
+ obj_normalcount = 0;
+}
+
+OBJFile::~OBJFile()
+{
+ for (VertexList::iterator it = obj_vertexlist.begin(); it != obj_vertexlist.end(); it++) {
+ delete(*it).second;
+ }
+ obj_vertexlist.clear();
+
+ for (NormalList::iterator it = obj_normallist.begin(); it != obj_normallist.end(); it++) {
+ delete(*it).second;
+ }
+ obj_normallist.clear();
+
+ for (UVList::iterator it = obj_uvlist.begin(); it != obj_uvlist.end(); it++) {
+ delete(*it).second;
+ }
+ obj_uvlist.clear();
+
+ for (TriList::iterator it = obj_trilist.begin(); it != obj_trilist.end(); it++) {
+ delete(*it).second;
+ }
+ obj_trilist.clear();
+
+ for (QuadList::iterator it = obj_quadlist.begin(); it != obj_quadlist.end(); it++) {
+ delete(*it).second;
+ }
+ obj_quadlist.clear();
+
+ if (objfile_ifs.is_open())
+ objfile_ifs.close();
+}
+
+bool OBJFile::read()
+{
+ char data[1024];
+ memset(data, 0, sizeof(data));
+
+ math::Vector3f zero;
+
+ size_t current_m = 0;
+
+ Fragment *fragment = 0;
+ Material *material = 0;
+
+ while (objfile_ifs.getline(data, sizeof(data) - 1)) {
+ std::istringstream line(data);
+
+ std::string word;
+ line >> word;
+
+ if( word.compare("usemtl") == 0) { /* material definition */
+
+ std::string materialname;
+ material = 0;
+
+ if(line >> materialname) {
+ for(MaterialList::iterator it = obj_materiallist.begin(); it != obj_materiallist.end(); ++it) {
+ if( (*it).second->name() == materialname) {
+ material = (*it).second;
+ current_m = (*it).first;
+ break;
+ }
+ }
+
+ if(!material) {
+ material = Material::find(materialname);
+
+ if(material)
+ obj_materiallist[obj_materiallist.size()] = material;
+ else {
+ material = new Material(materialname);
+ Material::add(material);
+ material->set_texture(materialname);
+
+ obj_materiallist[obj_materiallist.size()] = material;
+ }
+ current_m = obj_materiallist.size()-1;
+ }
+ }
+ else
+ con_warn << "Invalid material definition" << std::endl;
+
+
+ } else if( word.compare("v") == 0) { /* New wertex */
+ float x, y, z;
+ line >> x >> y >> z;
+
+ math::Vector3f *v = new math::Vector3f(x, y, z);
+ obj_vertexlist[obj_vertexlist.size()] = v;
+ obj_box.expand(*v * SCALE);
+
+ } else if( word.compare("vt") == 0) { /* wertex texture coordinate */
+ float u, v;
+ line >> u >> v;
+
+ math::Vector2f *uv = new math::Vector2f(u, v);
+ obj_uvlist[obj_uvlist.size()] = uv;
+
+ } else if( word.compare("vn") == 0) { /* wertex wormal */
+ float x, y, z;
+ line >> x >> y >> z;
+
+ math::Vector3f *nm = new math::Vector3f(x, y, z);
+ nm->normalize();
+ obj_normallist[obj_normalcount] = nm;
+ obj_normalcount++;
+
+ } else if( word.compare("f") == 0) { /* Face/Polygon */
+ size_t v[4];
+ size_t uv[4];
+ size_t vn[4];
+
+ bool have_vn[4], have_uv[4];
+ size_t nslash;
+
+ std::string strbuf(line.str());
+ strbuf.erase(0,2); //remove "f "
+
+ std::stringstream tmp(strbuf);
+
+ std::vector<std::string> points;
+ int i=0;
+ while(!tmp.eof()) {
+ nslash = 0;
+
+ have_vn[i] = 0;
+ have_uv[i] = 0;
+
+ std::string point;
+ tmp >> point;
+
+ if(point.compare(" ") == 0) //bordercase for space at end of face definition
+ continue;
+
+ for(size_t c=0; c<point.length(); c++) {
+ if(point[c] == '/') {
+ nslash++;
+ have_uv[i]=true;
+ point[c] = ' ';
+ }
+ if(point[c] == ' ' && point[c+1] == '/') {
+ have_uv[i]=false;
+ have_vn[i]=true;
+ nslash=2;
+ point.erase(c+1,1);
+ }
+ }
+
+ if(nslash==2 && have_vn[i] == false) {
+ have_vn[i] = true;
+ have_uv[i] = true;
+ }
+ points.push_back(point);
+
+ std::stringstream tmp2(point);
+ tmp2 >> v[i];
+ if(have_uv[i]) tmp2 >> uv[i];
+ if(have_vn[i]) tmp2 >> vn[i];
+ i++;
+ }
+
+ if(points.size() != 4 && points.size() != 3) {
+ con_warn << "Warning! " << objfile_name << ": Faces without 3 or 4 vertices will be ignored!" << std::endl;
+ points.clear();
+ continue;
+ }
+
+ math::Vector3f *v0 = obj_vertexlist.find(v[0]-1)->second;
+ math::Vector3f *v1 = obj_vertexlist.find(v[1]-1)->second;
+ math::Vector3f *v2 = obj_vertexlist.find(v[2]-1)->second;
+ math::Vector3f *v3 = obj_vertexlist.find(v[3]-1)->second;
+
+ math::Vector3f *n0 = obj_normallist[vn[0]-1];
+ math::Vector3f *n1 = obj_normallist[vn[1]-1];
+ math::Vector3f *n2 = obj_normallist[vn[2]-1];
+ math::Vector3f *n3 = obj_normallist[vn[3]-1];
+
+ math::Vector2f *t0 = obj_uvlist.find(uv[0]-1)->second;
+ math::Vector2f *t1 = obj_uvlist.find(uv[1]-1)->second;
+ math::Vector2f *t2 = obj_uvlist.find(uv[2]-1)->second;
+ math::Vector2f *t3 = obj_uvlist.find(uv[3]-1)->second;
+
+ if(points.size() == 3) {
+ if(!v0 || !v1 || !v2) {
+ con_warn << "Warning! " << objfile_name << ": Invalid vertex!" << std::endl;
+ continue;
+ }
+
+ Triangle *triangle = new Triangle(*v0, *v1, *v2);
+
+ if(!n0) n0 = new math::Vector3f(normal(triangle->v0(), triangle->v1(), triangle->v2()));
+ if(!n1) n1 = new math::Vector3f(normal(triangle->v1(), triangle->v2(), triangle->v0()));
+ if(!n2) n2 = new math::Vector3f(normal(triangle->v2(), triangle->v0(), triangle->v1()));
+
+ triangle->n0().assign(n0->x(), n0->y(), n0->z());
+ triangle->n1().assign(n1->x(), n1->y(), n1->z());
+ triangle->n2().assign(n2->x(), n2->y(), n2->z());
+
+ if(t0) triangle->t0().assign(t0->x(), 1.0 - t0->y());
+ if(t1) triangle->t1().assign(t1->x(), 1.0 - t1->y());
+ if(t2) triangle->t2().assign(t2->x(), 1.0 - t2->y());
+
+ std::pair<size_t, Triangle *> poly(current_m, triangle);
+ obj_trilist.push_back(poly);
+
+ } else if(points.size() == 4) {
+
+ if(!v0 || !v1 || !v2 || !v3) {
+ con_warn << "Warning! " << objfile_name << ": Invalid vertex!" << std::endl;
+ continue;
+ }
+
+ Quad *quad = new Quad(*v0, *v1, *v2, *v3, zero);
+
+ if(!n0) n0 = new math::Vector3f(normal(quad->v0(), quad->v1(), quad->v3()));
+ if(!n1) n1 = new math::Vector3f(normal(quad->v1(), quad->v2(), quad->v0()));
+ if(!n2) n2 = new math::Vector3f(normal(quad->v2(), quad->v3(), quad->v1()));
+ if(!n3) n3 = new math::Vector3f(normal(quad->v3(), quad->v0(), quad->v2()));
+
+ quad->n0().assign(n0->x(), n0->y(), n0->z());
+ quad->n1().assign(n1->x(), n1->y(), n1->z());
+ quad->n2().assign(n2->x(), n2->y(), n2->z());
+ quad->n3().assign(n3->x(), n3->y(), n3->z());
+
+ if(t0) quad->t0().assign(t0->x(), 1.0 - t0->y());
+ if(t1) quad->t1().assign(t1->x(), 1.0 - t1->y());
+ if(t2) quad->t2().assign(t2->x(), 1.0 - t2->y());
+ if(t3) quad->t3().assign(t3->x(), 1.0 - t3->y());
+
+ std::pair<size_t, Quad *> poly(current_m, quad);
+ obj_quadlist.push_back(poly);
+ }
+ }
+ }
+
+ //Go through tri and quad lists and create fragments for each material
+ size_t total_m = obj_materiallist.size();
+ for(size_t i = 0; i <= total_m; i++) {
+ material = obj_materiallist[i];
+ fragment = new Fragment(Fragment::Triangles, material);
+
+ for(TriList::iterator it = obj_trilist.begin(); it != obj_trilist.end(); ++it) {
+ if((*it).first == i) {
+ fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false);
+ fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false);
+ fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false);
+ }
+ }
+
+ if( fragment->structural_size() + fragment->detail_size() > 0 )
+ obj_fragmentgroup->add_fragment(fragment);
+
+ fragment = new Fragment(Fragment::Quads, material);
+
+ for(QuadList::iterator it = obj_quadlist.begin(); it != obj_quadlist.end(); ++it) {
+ if((*it).first == i) {
+ fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false);
+ fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false);
+ fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false);
+ fragment->add_vertex(((*it).second->v3() * SCALE) , (*it).second->n3(), (*it).second->t3(), false);
+ }
+ }
+
+ if( fragment->structural_size() + fragment->detail_size() > 0 )
+ obj_fragmentgroup->add_fragment(fragment);
+ }
+
+ return true;
+}
+
+Model *OBJFile::load(const std::string &name)
+{
+ OBJFile objfile(name);
+
+ if (!objfile.is_open()) {
+ return 0;
+ }
+
+ if (!objfile.read()) {
+ return 0;
+ }
+
+ if (!objfile.fragmentgroup()->size())
+ return 0;
+
+ // create a new model
+ Model *model = new Model(name);
+
+ // center model around (0,0,0) and set the bounding box
+ math::Vector3f obj_center((objfile.box().min() + objfile.box().max()) * 0.5f);
+ model->model_box.assign(
+ objfile.box().min() - obj_center,
+ objfile.box().max() - obj_center
+ );
+ model->set_radius(model->box().max().length());
+ model->set_origin(obj_center * -1.0f);
+
+ objfile.fragmentgroup()->set_location(obj_center * -1.0f);
+
+ for (FragmentGroup::Fragments::const_iterator fit = objfile.fragmentgroup()->fragments().begin(); fit != objfile.fragmentgroup()->fragments().end(); fit++) {
+ const Fragment *fragment = (*fit);
+
+ if(fragment->type() == Fragment::Triangles)
+ model->model_tris_count += (fragment->structural_size() + fragment->detail_size()) / 3;
+ else
+ model->model_quad_count += (fragment->structural_size() + fragment->detail_size()) / 4;
+ }
+
+ model->add_group(objfile.fragmentgroup());
+
+ con_debug << " " << objfile.name() << " " << objfile.vertexcount() << " vertices " << model->model_tris_count << " triangles " << model->model_quad_count << " quads" << std::endl;
+
+ return model;
+}
+
+} // namespace model
diff --git a/src/model/objfile.h b/src/model/objfile.h
new file mode 100644
index 0000000..4e7f0c7
--- /dev/null
+++ b/src/model/objfile.h
@@ -0,0 +1,113 @@
+/*
+ model/objfile.h
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
+*/
+
+/* http://www.royriggs.com/obj.html
+ http://en.wikipedia.org/wiki/Wavefront_.obj_file */
+
+#ifndef __INCLUDED_MODEL_OBJFILE_H__
+#define __INCLUDED_MODEL_OBJFILE_H__
+
+#include "math/vector3f.h"
+#include "model/model.h"
+#include "model/material.h"
+#include "model/fragment.h"
+#include "model/triangle.h"
+#include "model/quad.h"
+#include "filesystem/filestream.h"
+
+#include <string>
+#include <map>
+#include <vector>
+#include <utility>
+
+namespace model
+{
+
+/// class to parse the .obj file structure and load geometry data into a model
+class OBJFile
+{
+public:
+ /**
+ * @brief load a .obj file into a Model
+ * @param name name of the model to be loaded, without .obj extension or models/ prefix
+ * If the file can not be read, load() returns the NULL-pointer
+ */
+ static Model *load(std::string const &name);
+
+private:
+
+ /**
+ * @brief type definition for a list of materials in the ASE file
+ */
+ typedef std::map<size_t, Material *> MaterialList;
+
+ /**
+ * @brief type definition for a list of vertices
+ */
+ typedef std::map<size_t, math::Vector3f *> VertexList;
+ typedef std::map<size_t, math::Vector3f *> NormalList;
+ typedef std::map<size_t, math::Vector2f *> UVList;
+
+ /**
+ * @brief type definition for a list of faces
+ */
+ typedef std::list<std::pair<size_t, Triangle *> > TriList;
+ typedef std::list<std::pair<size_t, Quad *> > QuadList;
+
+ OBJFile(std::string const &name);
+ ~OBJFile();
+
+ /**
+ * @brief read the .obj file
+ */
+ bool read();
+
+ /**
+ * @brief parse a line in the .obj file.
+ */
+ void readline(std::string const &line);
+
+ inline const std::string &name() const {
+ return objfile_name;
+ }
+
+ inline bool is_open() {
+ return objfile_ifs.is_open();
+ }
+
+ inline size_t vertexcount() const {
+ return obj_vertexlist.size();
+ }
+
+ inline FragmentGroup *fragmentgroup() {
+ return obj_fragmentgroup;
+ }
+
+ inline const math::BoundingBox3f & box() const {
+ return obj_box;
+ }
+
+ std::string objfile_name;
+
+ filesystem::IFileStream objfile_ifs;
+
+ MaterialList obj_materiallist;
+ VertexList obj_vertexlist;
+ UVList obj_uvlist;
+ NormalList obj_normallist;
+ TriList obj_trilist;
+ QuadList obj_quadlist;
+
+ math::BoundingBox3f obj_box;
+
+ FragmentGroup *obj_fragmentgroup;
+
+ size_t obj_normalcount;
+};
+
+} // namespace model
+
+#endif // __INCLUDED_MODEL_OBJFILE_H__