fixed botched merge
This commit is contained in:
parent
d077b8981c
commit
d00599ced6
1 changed files with 165 additions and 179 deletions
344
scene/scene.cpp
344
scene/scene.cpp
|
@ -11,13 +11,13 @@
|
||||||
|
|
||||||
const std::string WHITESPACE = " \n\r\t\f\v";
|
const std::string WHITESPACE = " \n\r\t\f\v";
|
||||||
std::string ltrim(const std::string &s) {
|
std::string ltrim(const std::string &s) {
|
||||||
size_t start = s.find_first_not_of(WHITESPACE);
|
size_t start = s.find_first_not_of(WHITESPACE);
|
||||||
return (start == std::string::npos) ? "" : s.substr(start);
|
return (start == std::string::npos) ? "" : s.substr(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string rtrim(const std::string &s) {
|
std::string rtrim(const std::string &s) {
|
||||||
size_t end = s.find_last_not_of(WHITESPACE);
|
size_t end = s.find_last_not_of(WHITESPACE);
|
||||||
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string trim(const std::string &s) { return rtrim(ltrim(s)); }
|
std::string trim(const std::string &s) { return rtrim(ltrim(s)); }
|
||||||
|
@ -25,213 +25,199 @@ std::string trim(const std::string &s) { return rtrim(ltrim(s)); }
|
||||||
void Scene::add(const std::shared_ptr<Light> &light) { this->lights_.push_back(light); }
|
void Scene::add(const std::shared_ptr<Light> &light) { this->lights_.push_back(light); }
|
||||||
|
|
||||||
void Scene::add(const std::shared_ptr<Primitive> &primitive) {
|
void Scene::add(const std::shared_ptr<Primitive> &primitive) {
|
||||||
assert(primitive->shader() != nullptr);
|
assert(primitive->shader() != nullptr);
|
||||||
this->primitives_.push_back(primitive);
|
this->primitives_.push_back(primitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::addObj(char const *fileName, Vector3d const &scale, Vector3d const &translation,
|
void Scene::addObj(char const *fileName, Vector3d const &scale, Vector3d const &translation,
|
||||||
const std::shared_ptr<Shader> &shader, bool flipU, bool flipV) {
|
const std::shared_ptr<Shader> &shader, bool flipU, bool flipV) {
|
||||||
std::vector<std::shared_ptr<Primitive>> triangles = loadObj(fileName, scale, translation, shader, flipU, flipV);
|
std::vector<std::shared_ptr<Primitive>> triangles = loadObj(fileName, scale, translation, shader, flipU, flipV);
|
||||||
this->primitives_.insert(this->primitives_.end(), std::make_move_iterator(triangles.begin()),
|
this->primitives_.insert(this->primitives_.end(), std::make_move_iterator(triangles.begin()),
|
||||||
std::make_move_iterator(triangles.end()));
|
std::make_move_iterator(triangles.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Primitive>> Scene::loadObj(char const *fileName, Vector3d const &scale,
|
std::vector<std::shared_ptr<Primitive>> Scene::loadObj(char const *fileName, Vector3d const &scale,
|
||||||
Vector3d const &translation,
|
Vector3d const &translation,
|
||||||
const std::shared_ptr<Shader> &shader, bool flipU, bool flipV) {
|
const std::shared_ptr<Shader> &shader, bool flipU, bool flipV) {
|
||||||
std::vector<std::shared_ptr<Primitive>> faces;
|
std::vector<std::shared_ptr<Primitive>> faces;
|
||||||
std::vector<std::array<int, 3>> indices;
|
std::vector<std::array<int, 3>> indices;
|
||||||
|
|
||||||
// Open file from disk
|
// Open file from disk
|
||||||
std::ifstream file;
|
std::ifstream file;
|
||||||
file.open(fileName);
|
file.open(fileName);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
std::cout << "(Scene): Could not open .obj file: " << fileName << std::endl;
|
std::cout << "(Scene): Could not open .obj file: " << fileName << std::endl;
|
||||||
return std::vector<std::shared_ptr<Primitive>>();
|
return std::vector<std::shared_ptr<Primitive>>();
|
||||||
}
|
|
||||||
|
|
||||||
// Print the file name
|
|
||||||
std::cout << "(Scene): Loading \"" << fileName << "\"" << std::endl;
|
|
||||||
|
|
||||||
// Actual model data
|
|
||||||
std::vector<Vector3d> vData;
|
|
||||||
std::vector<Vector3d> tangentData;
|
|
||||||
std::vector<Vector3d> bitangentData;
|
|
||||||
std::vector<Vector3d> normalData;
|
|
||||||
std::vector<Vector3d> vnData;
|
|
||||||
std::vector<Vector2d> vtData;
|
|
||||||
|
|
||||||
// Read vertices, normals, textures, and faces from the file
|
|
||||||
std::string line;
|
|
||||||
while (getline(file, line)) {
|
|
||||||
std::stringstream lineStream(trim(line));
|
|
||||||
std::string type;
|
|
||||||
lineStream >> type;
|
|
||||||
|
|
||||||
// Vertices
|
|
||||||
if (type == "v") {
|
|
||||||
float x, y, z;
|
|
||||||
lineStream >> x >> y >> z;
|
|
||||||
vData.emplace_back(componentProduct(Vector3d(x, y, z), scale) + translation);
|
|
||||||
tangentData.emplace_back();
|
|
||||||
bitangentData.emplace_back();
|
|
||||||
normalData.emplace_back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texture coordinates
|
// Print the file name
|
||||||
if (type == "vt") {
|
std::cout << "(Scene): Loading \"" << fileName << "\"" << std::endl;
|
||||||
float u, v;
|
|
||||||
lineStream >> u >> v;
|
|
||||||
vtData.emplace_back(flipU ? 1.0f - u : u, flipV ? 1.0f - v : v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normals
|
// Actual model data
|
||||||
if (type == "vn") {
|
std::vector<Vector3d> vData;
|
||||||
float a, b, c;
|
std::vector<Vector3d> tangentData;
|
||||||
lineStream >> a >> b >> c;
|
std::vector<Vector3d> bitangentData;
|
||||||
vnData.emplace_back(normalized(componentQuotient(
|
std::vector<Vector3d> normalData;
|
||||||
Vector3d(a, b, c),
|
std::vector<Vector3d> vnData;
|
||||||
scale))); // Division needed for preventing stretched normals, normals' = (transform^-1)^T * normals
|
std::vector<Vector2d> vtData;
|
||||||
}
|
|
||||||
|
|
||||||
// Faces
|
// Read vertices, normals, textures, and faces from the file
|
||||||
if (type == "f") {
|
std::string line;
|
||||||
std::string vertex[3];
|
while (getline(file, line)) {
|
||||||
std::array<int, 3> vertInd = {-1, -1, -1};
|
std::stringstream lineStream(trim(line));
|
||||||
std::array<int, 3> texInd = {-1, -1, -1};
|
std::string type;
|
||||||
std::array<int, 3> normInd = {-1, -1, -1};
|
lineStream >> type;
|
||||||
lineStream >> vertex[0] >> vertex[1] >> vertex[2];
|
|
||||||
|
|
||||||
// triangulate polygons, like quads (which must be given in triangle fan notation)
|
// Vertices
|
||||||
while (!vertex[2].empty()) {
|
if (type == "v") {
|
||||||
auto triangle = std::make_shared<Triangle>(shader);
|
float x, y, z;
|
||||||
|
lineStream >> x >> y >> z;
|
||||||
for (int i = 0; i < 3; ++i) {
|
vData.emplace_back(componentProduct(Vector3d(x, y, z), scale) + translation);
|
||||||
std::stringstream vertexSteam(vertex[i]);
|
tangentData.emplace_back();
|
||||||
std::string reference;
|
bitangentData.emplace_back();
|
||||||
|
normalData.emplace_back();
|
||||||
// vertex index
|
|
||||||
getline(vertexSteam, reference, '/');
|
|
||||||
try {
|
|
||||||
vertInd[i] = stoi(reference) - 1;
|
|
||||||
triangle->setVertex(i, vData.at(vertInd[i]));
|
|
||||||
} catch (...) {
|
|
||||||
std::cout << "Error: vertex index invalid on line \"" << line << "\"" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// texture index
|
|
||||||
if (getline(vertexSteam, reference, '/')) {
|
|
||||||
if (!reference.empty()) {
|
|
||||||
try {
|
|
||||||
texInd[i] = stoi(reference) - 1;
|
|
||||||
triangle->setSurface(i, vtData.at(texInd[i]));
|
|
||||||
} catch (...) {
|
|
||||||
std::cout << "Error: texture coordinate index invalid on line \"" << line << "\"" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// normal index
|
|
||||||
if (getline(vertexSteam, reference, '/')) {
|
|
||||||
try {
|
|
||||||
normInd[i] = stoi(reference) - 1;
|
|
||||||
triangle->setNormal(i, vnData.at(normInd[i]));
|
|
||||||
} catch (...) {
|
|
||||||
std::cout << "Error: normal index invalid on line \"" << line << "\"" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate and accumulate tangent and bitangent vectors
|
// Texture coordinates
|
||||||
if (std::all_of(vertInd.begin(), vertInd.end(), [](int i) { return i > -1; }) &&
|
if (type == "vt") {
|
||||||
std::all_of(texInd.begin(), texInd.end(), [](int i) { return i > -1; })) {
|
float u, v;
|
||||||
for (int i = 0; i < 3; i++) {
|
lineStream >> u >> v;
|
||||||
const Vector3d deltaPos1 = vData.at(vertInd[(i + 1) % 3]) - vData.at(vertInd[i]);
|
vtData.emplace_back(flipU ? 1.0f - u : u, flipV ? 1.0f - v : v);
|
||||||
const Vector3d deltaPos2 = vData.at(vertInd[(i + 2) % 3]) - vData.at(vertInd[i]);
|
|
||||||
|
|
||||||
const Vector2d deltaUV1 = vtData.at(texInd[(i + 1) % 3]) - vtData.at(texInd[i]);
|
|
||||||
const Vector2d deltaUV2 = vtData.at(texInd[(i + 2) % 3]) - vtData.at(texInd[i]);
|
|
||||||
|
|
||||||
const float r = 1.0f / (deltaUV1.u * deltaUV2.v - deltaUV1.v * deltaUV2.u);
|
|
||||||
tangentData[vertInd[i]] += (deltaPos1 * deltaUV2.v - deltaPos2 * deltaUV1.v) * r;
|
|
||||||
bitangentData[vertInd[i]] += (deltaPos2 * deltaUV1.u - deltaPos1 * deltaUV2.u) * r;
|
|
||||||
|
|
||||||
normalData[vertInd[i]] += crossProduct(tangentData[vertInd[i]], bitangentData[vertInd[i]]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
faces.push_back(triangle);
|
// Normals
|
||||||
indices.push_back(vertInd);
|
if (type == "vn") {
|
||||||
|
float a, b, c;
|
||||||
|
lineStream >> a >> b >> c;
|
||||||
|
vnData.emplace_back(normalized(componentQuotient(
|
||||||
|
Vector3d(a, b, c),
|
||||||
|
scale))); // Division needed for preventing stretched normals, normals' = (transform^-1)^T * normals
|
||||||
|
}
|
||||||
|
|
||||||
// get the next triangle
|
// Faces
|
||||||
if (lineStream.eof())
|
if (type == "f") {
|
||||||
break;
|
std::string vertex[3];
|
||||||
|
std::array<int, 3> vertInd = {-1, -1, -1};
|
||||||
|
std::array<int, 3> texInd = {-1, -1, -1};
|
||||||
|
std::array<int, 3> normInd = {-1, -1, -1};
|
||||||
|
lineStream >> vertex[0] >> vertex[1] >> vertex[2];
|
||||||
|
|
||||||
vertex[1] = vertex[2];
|
// triangulate polygons, like quads (which must be given in triangle fan notation)
|
||||||
lineStream >> vertex[2];
|
while (!vertex[2].empty()) {
|
||||||
}
|
auto triangle = std::make_shared<Triangle>(shader);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the file
|
for (int i = 0; i < 3; ++i) {
|
||||||
file.close();
|
std::stringstream vertexSteam(vertex[i]);
|
||||||
|
std::string reference;
|
||||||
|
|
||||||
// set the normalized tangents and bitangents
|
// vertex index
|
||||||
for (int i = 0; i < faces.size(); i++) {
|
getline(vertexSteam, reference, '/');
|
||||||
for (int j = 0; j < 3; j++) {
|
try {
|
||||||
Vector3d tangent = normalized(tangentData[indices[i][j]]);
|
vertInd[i] = stoi(reference) - 1;
|
||||||
const Vector3d bitangent = normalized(bitangentData[indices[i][j]]);
|
triangle->setVertex(i, vData.at(vertInd[i]));
|
||||||
// try to use the normal from the obj file, if it doesn't exist, use the computed normal
|
} catch (...) {
|
||||||
Vector3d normal = normalized(normalData[indices[i][j]]);
|
std::cout << "Error: vertex index invalid on line \"" << line << "\"" << std::endl;
|
||||||
if (vnData.size() > 0)
|
}
|
||||||
normal = dynamic_cast<Triangle *>(faces[i].get())->getNormal(j);
|
|
||||||
|
|
||||||
// gram-schmidt orthogonalization
|
// texture index
|
||||||
tangent = normalized(tangent - normal * dotProduct(normal, tangent));
|
if (getline(vertexSteam, reference, '/')) {
|
||||||
// check handedness of coordinate system
|
if (!reference.empty()) {
|
||||||
if (dotProduct(crossProduct(normal, tangent), bitangent) < 0.0f)
|
try {
|
||||||
tangent *= -1.0f;
|
texInd[i] = stoi(reference) - 1;
|
||||||
|
triangle->setSurface(i, vtData.at(texInd[i]));
|
||||||
|
} catch (...) {
|
||||||
|
std::cout << "Error: texture coordinate index invalid on line \"" << line << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dynamic_cast<Triangle *>(faces[i].get())->setTangent(j, tangent);
|
// normal index
|
||||||
dynamic_cast<Triangle *>(faces[i].get())->setBitangent(j, bitangent);
|
if (getline(vertexSteam, reference, '/')) {
|
||||||
dynamic_cast<Triangle *>(faces[i].get())->setNormal(j, normal);
|
try {
|
||||||
}
|
normInd[i] = stoi(reference) - 1;
|
||||||
}
|
triangle->setNormal(i, vnData.at(normInd[i]));
|
||||||
|
} catch (...) {
|
||||||
|
std::cout << "Error: normal index invalid on line \"" << line << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Debug output
|
// calculate and accumulate tangent and bitangent vectors
|
||||||
std::cout << " -> " << vData.size() << " vertices parsed" << std::endl;
|
if (std::all_of(vertInd.begin(), vertInd.end(), [](int i) { return i > -1; }) &&
|
||||||
std::cout << " -> " << vnData.size() << " normals parsed" << std::endl;
|
std::all_of(texInd.begin(), texInd.end(), [](int i) { return i > -1; })) {
|
||||||
std::cout << " -> " << vtData.size() << " uv-positions parsed" << std::endl;
|
for (int i = 0; i < 3; i++) {
|
||||||
std::cout << " -> " << faces.size() << " primitives parsed" << std::endl;
|
const Vector3d deltaPos1 = vData.at(vertInd[(i + 1) % 3]) - vData.at(vertInd[i]);
|
||||||
|
const Vector3d deltaPos2 = vData.at(vertInd[(i + 2) % 3]) - vData.at(vertInd[i]);
|
||||||
|
|
||||||
faces.push_back(std::make_shared<Triangle>(vertices[x - 1] * scale + translation,
|
const Vector2d deltaUV1 = vtData.at(texInd[(i + 1) % 3]) - vtData.at(texInd[i]);
|
||||||
vertices[y - 1] * scale + translation,
|
const Vector2d deltaUV2 = vtData.at(texInd[(i + 2) % 3]) - vtData.at(texInd[i]);
|
||||||
vertices[z - 1] * scale + translation,
|
|
||||||
vertex_normals[xn - 1], vertex_normals[yn - 1],
|
const float r = 1.0f / (deltaUV1.u * deltaUV2.v - deltaUV1.v * deltaUV2.u);
|
||||||
vertex_normals[zn - 1],
|
tangentData[vertInd[i]] += (deltaPos1 * deltaUV2.v - deltaPos2 * deltaUV1.v) * r;
|
||||||
shader));
|
bitangentData[vertInd[i]] += (deltaPos2 * deltaUV1.u - deltaPos1 * deltaUV2.u) * r;
|
||||||
} else if (key == "#") {
|
|
||||||
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
normalData[vertInd[i]] += crossProduct(tangentData[vertInd[i]], bitangentData[vertInd[i]]);
|
||||||
} else {
|
}
|
||||||
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
}
|
||||||
|
|
||||||
|
faces.push_back(triangle);
|
||||||
|
indices.push_back(vertInd);
|
||||||
|
|
||||||
|
// get the next triangle
|
||||||
|
if (lineStream.eof())
|
||||||
|
break;
|
||||||
|
|
||||||
|
vertex[1] = vertex[2];
|
||||||
|
lineStream >> vertex[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// set the normalized tangents and bitangents
|
||||||
|
for (int i = 0; i < faces.size(); i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
Vector3d tangent = normalized(tangentData[indices[i][j]]);
|
||||||
|
const Vector3d bitangent = normalized(bitangentData[indices[i][j]]);
|
||||||
|
// try to use the normal from the obj file, if it doesn't exist, use the computed normal
|
||||||
|
Vector3d normal = normalized(normalData[indices[i][j]]);
|
||||||
|
if (vnData.size() > 0)
|
||||||
|
normal = dynamic_cast<Triangle *>(faces[i].get())->getNormal(j);
|
||||||
|
|
||||||
|
// gram-schmidt orthogonalization
|
||||||
|
tangent = normalized(tangent - normal * dotProduct(normal, tangent));
|
||||||
|
// check handedness of coordinate system
|
||||||
|
if (dotProduct(crossProduct(normal, tangent), bitangent) < 0.0f)
|
||||||
|
tangent *= -1.0f;
|
||||||
|
|
||||||
|
dynamic_cast<Triangle *>(faces[i].get())->setTangent(j, tangent);
|
||||||
|
dynamic_cast<Triangle *>(faces[i].get())->setBitangent(j, bitangent);
|
||||||
|
dynamic_cast<Triangle *>(faces[i].get())->setNormal(j, normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug output
|
||||||
|
std::cout << " -> " << vData.size() << " vertices parsed" << std::endl;
|
||||||
|
std::cout << " -> " << vnData.size() << " normals parsed" << std::endl;
|
||||||
|
std::cout << " -> " << vtData.size() << " uv-positions parsed" << std::endl;
|
||||||
|
std::cout << " -> " << faces.size() << " primitives parsed" << std::endl;
|
||||||
|
|
||||||
return faces;
|
return faces;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color Scene::traceRay(Ray &ray) const {
|
Color Scene::traceRay(Ray &ray) const {
|
||||||
if (this->findIntersection(ray) && ray.remainingBounces-- > 0) {
|
if (this->findIntersection(ray) && ray.remainingBounces-- > 0) {
|
||||||
// If the ray has hit an object, call the shader ...
|
// If the ray has hit an object, call the shader ...
|
||||||
return ray.primitive->shader()->shade(*this, ray);
|
return ray.primitive->shader()->shade(*this, ray);
|
||||||
} else if (this->environmentMap) {
|
} else if (this->environmentMap) {
|
||||||
// ... otherwise look up the environment map ...
|
// ... otherwise look up the environment map ...
|
||||||
float const phi = std::acos(ray.direction.y);
|
float const phi = std::acos(ray.direction.y);
|
||||||
float const rho = std::atan2(ray.direction.z, ray.direction.x) + float(PI);
|
float const rho = std::atan2(ray.direction.z, ray.direction.x) + float(PI);
|
||||||
return this->environmentMap->color(rho / (2.0f * float(PI)), phi / float(PI));
|
return this->environmentMap->color(rho / (2.0f * float(PI)), phi / float(PI));
|
||||||
} else {
|
} else {
|
||||||
// ... if all else fails, just return the background color
|
// ... if all else fails, just return the background color
|
||||||
return this->backgroundColor;
|
return this->backgroundColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue