File: 3dobject.cc

package info (click to toggle)
performous 1.1%2Bgit20181118-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 11,712 kB
  • sloc: cpp: 30,008; ansic: 2,751; sh: 801; xml: 464; python: 374; makefile: 22
file content (118 lines) | stat: -rw-r--r-- 4,015 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include "3dobject.hh"

#include <boost/filesystem/fstream.hpp>
#include <stdexcept>
#include <sstream>

#include "texture.hh"

// 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;
	}
}

/// A polygon containing links to required point data
struct Face {
	std::vector<int> vertices;
	std::vector<int> texcoords;
	std::vector<int> normals;
};

/// Load a Wavefront .obj file and possibly scale it also
void Object3d::loadWavefrontObj(fs::path const& filepath, float scale) {
	int linenumber = 0;
	std::string row;
	fs::ifstream file(filepath, std::ios::binary);
	if (!file) throw std::runtime_error("Couldn't open object file "+filepath.string());
	std::vector<glmath::vec3> vertices;
	std::vector<glmath::vec3> normals;
	std::vector<glmath::vec2> texcoords;
	std::vector<Face> m_faces;
	while (getline(file, row)) {
		++linenumber;
		std::istringstream srow(row);
		float x,y,z;
		std::string tempst;
		if (row.substr(0,2) == "v ") {  // Vertices
			srow >> tempst >> x >> y >> z;
			vertices.push_back(glmath::vec3(x*scale, y*scale, z*scale));
		} else if (row.substr(0,2) == "vt") {  // Texture Coordinates
			srow >> tempst >> x >> y;
			texcoords.push_back(glmath::vec2(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("Invalid normal in "+filepath.string()+":"+std::to_string(linenumber));
			x /= sum; y /= sum; z /= sum; // Normalize components
			normals.push_back(glmath::vec3(x, y, z));
		} else if (row.substr(0,2) == "f ") {  // Faces
			Face f;
			srow >> tempst; // Eat away prefix
			// Parse face point's coordinate references
			for (std::string fpoint; srow >> fpoint; ) {
				for (size_t i = 1; i <= 3; ++i) {
					std::string st_id(getWord(fpoint,i,'/'));
					if (!st_id.empty()) {
						// Vertex indices are 1-based in the file
						int v_id = std::stoi(st_id) - 1;
						switch (i) {
							case 1: f.vertices.push_back(v_id); break;
							case 2: f.texcoords.push_back(v_id); break;
							case 3: f.normals.push_back(v_id); break;
						}
					}
				}
			}
			if (!f.vertices.empty() && f.vertices.size() != 3)
				throw std::runtime_error("Only triangle faces allowed in "+filepath.string()+":"+std::to_string(linenumber));
			// Face must have equal number of v, vt, vn or none of a kind
			if (!f.vertices.empty()
			  && (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("Invalid face in "+filepath.string()+":"+std::to_string(linenumber));
			}
		}
	}
	// Construct a vertex array
	for (std::vector<Face>::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i) {
		bool hasNormals = !i->normals.empty();
		bool hasTexCoords = !i->texcoords.empty();
		for (size_t j = 0; j < i->vertices.size(); ++j) {
			if (hasNormals) m_va.normal(normals[i->normals[j]]);
			if (hasTexCoords) m_va.texCoord(texcoords[i->texcoords[j]]);
			m_va.vertex(vertices[i->vertices[j]]);
		}
	}
}

void Object3d::load(fs::path const& filepath, fs::path const& texturepath, float scale) {
	if (!texturepath.empty()) m_texture = std::make_unique<Texture>(texturepath);
	loadWavefrontObj(filepath, scale);
}

void Object3d::draw() {
	UseShader us(getShader("3dobject"));
	if (m_texture) {
		UseTexture tex(*m_texture);
		m_va.draw(GL_TRIANGLES);
	} else {
		m_va.draw(GL_TRIANGLES);
	}
}

void Object3d::draw(float x, float y, float z, float s) {
	using namespace glmath;
	Transform trans(translate(vec3(x, y, z)) * scale(s));  // Move to position and scale
	draw();
}