|
From: Tapio V. <aa...@us...> - 2009-11-09 22:13:57
|
Module: performous
Branch: master
Commit: 4beb4de2a8d4a3b95c8d15f7e206601e46bbe1f4
Author: Tapio Vierros <tap...@gm...>
Date: Mon Nov 9 23:03:04 2009 +0200
3d object class and simple Wavefront Obj file format parser.
---
game/3dobject.cc | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
game/3dobject.hh | 53 +++++++++++++++++++++++
2 files changed, 174 insertions(+), 0 deletions(-)
diff --git a/game/3dobject.cc b/game/3dobject.cc
new file mode 100644
index 0000000..1bac829
--- /dev/null
+++ b/game/3dobject.cc
@@ -0,0 +1,121 @@
+#include "3dobject.hh"
+#include "surface.hh"
+#include "glutil.hh"
+
+#include <vector>
+#include <map>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <stdexcept>
+#include <string>
+#include <cmath>
+
+
+// TODO: use display lists for drawing?
+// TODO: test & fix faces that doesn't have texcoords in the file
+// TODO: group handling for loader
+
+
+namespace {
+ /// Returns a word (delimited by delim) in a string st at position pos (1-based)
+ std::string getWord(std::string& st, size_t pos, char delim) {
+ std::istringstream iss(st);
+ std::string ret;
+ for (size_t i = 1; i <= pos; i++)
+ getline(iss, ret, delim);
+ return ret;
+ }
+}
+
+
+void Object3d::loadWavefrontObj(std::string filepath, float scale) {
+ std::string row;
+ std::ifstream file(filepath.c_str());
+ if (!file.is_open()) throw std::runtime_error("Couldn't open object file "+filepath);
+ m_vertices.clear();
+ m_faces.clear();
+ m_texcoords.clear();
+ while (!file.eof()) {
+ getline(file, row);
+ std::istringstream srow(row);
+ float x,y,z;
+ std::string tempst;
+ if (row.substr(0,2) == "v ") { // Vertices
+ srow >> tempst >> x >> y >> z;
+ m_vertices.push_back(Vertex(x*scale,y*scale,z*scale));
+ } else if (row.substr(0,2) == "vt") { // Texture Coordinates
+ srow >> tempst >> x >> y;
+ m_texcoords.push_back(TexCoord(x,y));
+ } else if (row.substr(0,2) == "vn") { // Normals
+ srow >> tempst >> x >> y >> z;
+ double sum = std::abs(x)+std::abs(y)+std::abs(z);
+ if (sum == 0) throw std::runtime_error("Object "+filepath+" has invalid normal(s).");
+ x /= sum; y /= sum; z /= sum;
+ m_normals.push_back(Vertex(x,y,z));
+ } else if (row.substr(0,2) == "f ") { // Faces
+ Face f;
+ srow >> tempst;
+ int v_id;
+ // Parse face point's coordinate references
+ while (!srow.eof()) {
+ srow >> tempst;
+ for (size_t i = 1; i <= 3; i++) {
+ std::string st_id(getWord(tempst,i,'/'));
+ if (!st_id.empty()) {
+ std::istringstream conv_int(st_id);
+ conv_int >> v_id;
+ switch (i) {
+ // Vertex indices are 1-based in the file
+ case 1: f.vertices.push_back(v_id-1); break;
+ case 2: f.texcoords.push_back(v_id-1); break;
+ case 3: f.normals.push_back(v_id-1); break;
+ }
+ }
+ }
+ }
+ // Face must have equal number of v, vt, vn or none of a kind
+ if (f.vertices.size() > 0
+ && (f.texcoords.empty() || (f.texcoords.size() == f.vertices.size()))
+ && (f.normals.empty() || (f.normals.size() == f.vertices.size()))) {
+ m_faces.push_back(f);
+ } else {
+ throw std::runtime_error("Object "+filepath+" has invalid face(s).");
+ }
+ }
+ }
+}
+
+
+void Object3d::draw(float x, float y, float z) const {
+ //UseTexture tex(*m_texture);
+ std::vector<Face>::const_iterator it;
+ // Iterate through faces
+ for (it = m_faces.begin(); it != m_faces.end(); it++) {
+ // Select a suitable primitive
+ GLenum polyType = GL_POLYGON;
+ switch (it->vertices.size()) {
+ case 3: polyType = GL_TRIANGLES; break;
+ case 4: polyType = GL_QUADS; break;
+ }
+ glutil::Begin block(polyType);
+ // Iterate through face's points
+ std::vector<int>::const_iterator it2;
+ for (size_t i = 0; i < it->vertices.size(); i++) {
+ // Texture coordinates
+ if (!it->texcoords.empty())
+ glTexCoord2f(m_texcoords[it->texcoords[i]].s, m_texcoords[it->texcoords[i]].t);
+ // Normals
+ if (!it->normals.empty())
+ glNormal3f(
+ m_normals[it->normals[i]].x,
+ m_normals[it->normals[i]].y,
+ m_normals[it->normals[i]].z);
+ // Vertices
+ glVertex3f(
+ m_vertices[it->vertices[i]].x+x,
+ m_vertices[it->vertices[i]].y+y,
+ m_vertices[it->vertices[i]].z+z);
+ }
+ }
+}
diff --git a/game/3dobject.hh b/game/3dobject.hh
new file mode 100644
index 0000000..9baef99
--- /dev/null
+++ b/game/3dobject.hh
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <vector>
+#include <map>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+
+#include "surface.hh"
+#include "glutil.hh"
+
+// TODO: Exception handling
+// TODO: Texture loading
+
+struct Vertex {
+ Vertex(float x = 0, float y = 0, float z = 0): x(x), y(y), z(z) {}
+ float x;
+ float y;
+ float z;
+};
+
+struct TexCoord {
+ TexCoord(float s = 0, float t = 0): s(s), t(t) {}
+ float s;
+ float t;
+};
+
+struct Face {
+ std::vector<int> vertices;
+ std::vector<int> texcoords;
+ std::vector<int> normals;
+};
+
+
+class Object3d {
+ private:
+ std::vector<Vertex> m_vertices;
+ std::vector<TexCoord> m_texcoords;
+ std::vector<Vertex> m_normals;
+ std::vector<Face> m_faces;
+ //boost::scoped_ptr<Texture> m_texture;
+ /// load a Wavefront .obj 3d object file
+ void loadWavefrontObj(std::string filepath, float scale = 1.0);
+ public:
+ /// constructors
+ Object3d();
+ Object3d(std::string filepath, float scale = 1.0) {
+ //m_texture.reset(new Texture("button.svg"));
+ loadWavefrontObj(filepath, scale);
+ }
+ /// draws the object
+ void draw(float x = 0, float y = 0, float z = 0) const;
+};
|