Skip to content

Commit d4ab9cf

Browse files
authored
Merge pull request #186 from CMU-Graphics/spring2025
Fixed uv and normal import from mesh in manager.cpp
2 parents 0452b57 + b75143b commit d4ab9cf

File tree

8 files changed

+2857
-12
lines changed

8 files changed

+2857
-12
lines changed

src/geometry/halfedge-local - old.cpp

Lines changed: 1647 additions & 0 deletions
Large diffs are not rendered by default.

src/geometry/halfedge-utility.cpp

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,13 @@ struct hash< std::pair< A, B > > {
730730
};
731731
}
732732

733-
Halfedge_Mesh Halfedge_Mesh::from_indexed_faces(std::vector< Vec3 > const &vertices_, std::vector< std::vector< Index > > const &faces_) {
733+
Halfedge_Mesh Halfedge_Mesh::from_indexed_faces(std::vector< Vec3 > const &vertices_,
734+
std::vector< std::vector< Index > > const &faces_,
735+
std::vector< std::vector< Index > > const &corner_normal_idxs,
736+
std::vector< std::vector< Index > > const &corner_uv_idxs,
737+
std::vector<Vec3> const &corner_normals_,
738+
std::vector<Vec2> const &corner_uvs_)
739+
{
734740

735741
Halfedge_Mesh mesh;
736742

@@ -744,8 +750,13 @@ Halfedge_Mesh Halfedge_Mesh::from_indexed_faces(std::vector< Vec3 > const &verti
744750

745751
std::unordered_map< std::pair< Index, Index >, HalfedgeRef > halfedges; //for quick lookup of halfedges by from/to vertex index
746752

753+
uint32_t num_faces = static_cast<uint32_t>(faces_.size());
754+
const bool add_corner_normals = corner_normal_idxs.size() >= num_faces;
755+
const bool add_corner_uvs = corner_uv_idxs.size() >= num_faces;
747756
//helper to add a face (and, later, boundary):
748-
auto add_loop = [&](std::vector< Index > const &loop, bool boundary) {
757+
auto add_loop = [&](std::vector< Index > const &loop, bool boundary,
758+
std::vector< Index> const &n_loop = std::vector<Index>{},
759+
std::vector< Index> const &uv_loop = std::vector<Index>{}) {
749760
assert(loop.size() >= 3);
750761

751762
for (uint32_t j = 0; j < loop.size(); ++j) {
@@ -791,13 +802,19 @@ Halfedge_Mesh Halfedge_Mesh::from_indexed_faces(std::vector< Vec3 > const &verti
791802

792803
if (i != 0) prev->next = halfedge; //set previous halfedge's next pointer
793804
prev = halfedge;
805+
806+
if(add_corner_normals && i < n_loop.size()) halfedge->corner_normal = corner_normals_[n_loop[i]];
807+
if(add_corner_uvs && i < uv_loop.size()) halfedge->corner_uv = corner_uvs_[uv_loop[i]];
794808
}
809+
795810
prev->next = face->halfedge; //set next pointer for last halfedge to first edge
796811
};
797812

798813
//add all faces:
799-
for (auto const &loop : faces_) {
800-
add_loop(loop, false);
814+
for (uint32_t i = 0; i < num_faces; i++) {
815+
if(add_corner_normals && add_corner_uvs) add_loop(faces_[i], false, corner_normal_idxs[i], corner_uv_idxs[i]);
816+
else if(add_corner_normals) add_loop(faces_[i], false, corner_normal_idxs[i]);
817+
else add_loop(faces_[i], false);
801818
}
802819

803820
// All halfedges created so far have valid next pointers, but some may be missing twins because they are at a boundary.
@@ -849,6 +866,49 @@ Halfedge_Mesh Halfedge_Mesh::from_indexed_faces(std::vector< Vec3 > const &verti
849866
return mesh;
850867
}
851868

869+
Halfedge_Mesh Halfedge_Mesh::from_indexed_mesh_with_corner_data(
870+
const Indexed_Mesh& indexed_mesh,
871+
std::vector<Index> const &corner_normal_idxs_,
872+
std::vector<Index> const &corner_uv_idxs_,
873+
std::vector<Vec3> const &normals_,
874+
std::vector<Vec2> const &uvs_)
875+
{
876+
877+
//extract vertex positions and face indices from indexed_mesh:
878+
std::vector< Vec3 > indexed_vertices;
879+
indexed_vertices.reserve(indexed_mesh.vertices().size());
880+
for (auto const &v : indexed_mesh.vertices()) {
881+
indexed_vertices.emplace_back(v.pos);
882+
}
883+
884+
std::vector< std::vector< Index > > indexed_faces;
885+
assert(indexed_mesh.indices().size() % 3 == 0);
886+
indexed_faces.reserve(indexed_mesh.indices().size() / 3);
887+
for (uint32_t i = 0; i < indexed_mesh.indices().size(); i += 3) {
888+
indexed_faces.emplace_back(indexed_mesh.indices().begin() + i, indexed_mesh.indices().begin() + i + 3);
889+
}
890+
891+
std::vector< std::vector < Index > > indexed_normals;
892+
assert(corner_normal_idxs_.size() % 3 == 0);
893+
indexed_normals.reserve(corner_normal_idxs_.size() / 3);
894+
for (uint32_t i = 0; i < corner_normal_idxs_.size(); i += 3) {
895+
indexed_normals.emplace_back(corner_normal_idxs_.begin() + i, corner_normal_idxs_.begin() + i + 3);
896+
}
897+
898+
std::vector< std::vector < Index > > indexed_uvs;
899+
assert(corner_uv_idxs_.size() % 3 == 0);
900+
indexed_uvs.reserve(corner_uv_idxs_.size() / 3);
901+
for (uint32_t i = 0; i < corner_uv_idxs_.size(); i += 3) {
902+
indexed_uvs.emplace_back(corner_uv_idxs_.begin() + i, corner_uv_idxs_.begin() + i + 3);
903+
}
904+
905+
//build halfedge mesh with the extracted vertex/face data:
906+
Halfedge_Mesh mesh = Halfedge_Mesh::from_indexed_faces(indexed_vertices,
907+
indexed_faces, indexed_normals, indexed_uvs, normals_, uvs_);
908+
909+
return mesh;
910+
}
911+
852912
Halfedge_Mesh Halfedge_Mesh::from_indexed_mesh(Indexed_Mesh const &indexed_mesh) {
853913

854914
//extract vertex positions and face indices from indexed_mesh:

src/geometry/halfedge.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,19 @@ class Halfedge_Mesh {
424424
// - mesh must correspond to a valid halfedge mesh (see 'validate()')
425425
// - vertices and faces will match the order they appear in vertices[] and faces[], respectively
426426
// - halfedges will match the order they are mentioned in faces[]
427-
static Halfedge_Mesh from_indexed_faces(std::vector< Vec3 > const &vertices, std::vector< std::vector< Index > > const &faces);
427+
//static Halfedge_Mesh from_indexed_faces(std::vector< Vec3 > const &vertices, std::vector< std::vector< Index > > const &faces);
428+
static Halfedge_Mesh from_indexed_faces(std::vector< Vec3 > const &vertices,
429+
std::vector< std::vector< Index > > const &faces,
430+
std::vector< std::vector< Index > > const &corner_normal_idxs = std::vector< std::vector<Index>>{},
431+
std::vector< std::vector< Index > > const &corner_uv_idxs = std::vector< std::vector<Index>>{},
432+
std::vector<Vec3> const &normals = std::vector<Vec3>{},
433+
std::vector<Vec2> const &uvs = std::vector<Vec2>{});
434+
static Halfedge_Mesh from_indexed_mesh_with_corner_data(
435+
const Indexed_Mesh& mesh,
436+
std::vector<Index> const &corner_normal_idxs = std::vector<Index>{},
437+
std::vector<Index> const &corner_uv_idxs = std::vector<Index>{},
438+
std::vector<Vec3> const &normals = std::vector<Vec3>{},
439+
std::vector<Vec2> const &uvs = std::vector<Vec2>{});
428440
static Halfedge_Mesh from_indexed_mesh(const Indexed_Mesh& mesh);
429441

430442
//generate a cube:

src/gui/manager.cpp

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,10 @@ void Manager::to_s3d() {
280280

281281
std::vector<Indexed_Mesh::Vert> verts;
282282
std::vector<Indexed_Mesh::Index> idxs;
283+
std::vector<Indexed_Mesh::Index> n_idxs;
284+
std::vector<Indexed_Mesh::Index> uv_idxs;
283285
std::vector<Vec3> normals;
286+
std::vector<Vec2> uvs = std::vector<Vec2>{};
284287

285288
// read in lines from file, assign verts and idxs
286289
char *old_model_path = nullptr;
@@ -307,9 +310,20 @@ void Manager::to_s3d() {
307310
data >> x >> y >> z;
308311
normals.emplace_back(Vec3(x, y, z));
309312
}
313+
else if (type == "vt") {
314+
float u, v;
315+
data >> u >> v;
316+
uvs.emplace_back(Vec2 {u, v});
317+
}
310318
else if (type == "f") {
311319
std::string token;
320+
312321
std::vector<uint32_t> f_idxs;
322+
std::vector<uint32_t> f_uv_idxs = std::vector<uint32_t>{};
323+
std::vector<uint32_t> f_n_idxs;
324+
325+
bool contains_uvs = false;
326+
bool warned_once = false;
313327
while (data >> token) { // v/vt/vn pair
314328
std::vector<std::string> v_data;
315329
std::stringstream v_data_ss(token);
@@ -319,27 +333,53 @@ void Manager::to_s3d() {
319333
v_data.push_back(substr);
320334
}
321335
uint32_t vi = static_cast<uint32_t>(std::stoi(v_data[0]));
322-
// int vt = 0;
323-
// try {
324-
// vt = std::stoi(v_data[1]);
325-
// } catch (std::exception e) {
326-
// vt = 0;
327-
// }
336+
uint32_t vt = 0;
337+
try {
338+
vt = std::stoi(v_data[1]);
339+
f_uv_idxs.emplace_back(vt - 1);
340+
contains_uvs = true;
341+
} catch (std::exception e) {
342+
if(!warned_once) {
343+
warn("Error loading imported obj uvs : %s", e.what());
344+
}
345+
warned_once = true;
346+
}
347+
328348
uint32_t vn = static_cast<uint32_t>(std::stoi(v_data[2]));
349+
f_n_idxs.emplace_back(vn - 1);
329350
verts[vi - 1].norm = normals[vn - 1];
351+
if(contains_uvs && vt + 1 < uvs.size()) {
352+
verts[vi - 1].uv = uvs[vt - 1];
353+
}
354+
330355
f_idxs.emplace_back(vi - 1);
331356
}
357+
332358
for (size_t i = 1; i + 1 < f_idxs.size(); i++) {
333359
idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_idxs[0]));
334360
idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_idxs[i]));
335361
idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_idxs[i + 1]));
362+
n_idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_n_idxs[0]));
363+
n_idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_n_idxs[i]));
364+
n_idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_n_idxs[i + 1]));
365+
if(contains_uvs) {
366+
uv_idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_uv_idxs[0]));
367+
uv_idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_uv_idxs[i]));
368+
uv_idxs.emplace_back(static_cast<Indexed_Mesh::Index>(f_uv_idxs[i + 1]));
369+
}
336370
}
337371
}
338372
}
339373

340374
// verts and idxs -> index mesh -> halfedge mesh
341375
Indexed_Mesh idx_mesh(std::move(verts), std::move(idxs));
342-
Halfedge_Mesh he_mesh = Halfedge_Mesh::from_indexed_mesh(idx_mesh);
376+
//if(uv_idxs.size() >= idxs.size()) {
377+
Halfedge_Mesh he_mesh = Halfedge_Mesh::from_indexed_mesh_with_corner_data(idx_mesh,
378+
std::move(n_idxs), std::move(uv_idxs), std::move(normals),
379+
std::move(uvs));
380+
//} else {
381+
// he_mesh = Halfedge_Mesh::from_indexed_mesh(idx_mesh);
382+
//}
343383
Halfedge_Mesh skinned_mesh_data = Halfedge_Mesh::from_indexed_mesh(idx_mesh);
344384
Skinned_Mesh skinned_mesh;
345385
skinned_mesh.mesh = std::move(skinned_mesh_data);

src/rasterizer/framebufferBase.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "framebuffer.h"
2+
#include "../util/hdr_image.h"
3+
#include "sample_pattern.h"
4+
5+
Framebuffer::Framebuffer(uint32_t width_, uint32_t height_, SamplePattern const& sample_pattern_)
6+
: width(width_), height(height_), sample_pattern(sample_pattern_) {
7+
8+
// check that framebuffer isn't larger than allowed:
9+
if (width > MaxWidth || height > MaxHeight) {
10+
throw std::runtime_error("Framebuffer size (" + std::to_string(width) + "x" +
11+
std::to_string(height) + ") exceeds maximum allowed (" +
12+
std::to_string(MaxWidth) + "x" + std::to_string(MaxHeight) + ").");
13+
}
14+
// check that framebuffer size is even:
15+
if (width % 2 != 0 || height % 2 != 0) {
16+
throw std::runtime_error("Framebuffer size (" + std::to_string(width) + "x" +
17+
std::to_string(height) + ") is not even.");
18+
}
19+
20+
uint32_t samples =
21+
width * height * static_cast<uint32_t>(sample_pattern.centers_and_weights.size());
22+
23+
// allocate storage for color and depth samples:
24+
colors.assign(samples, Spectrum{0.0f, 0.0f, 0.0f});
25+
depths.assign(samples, 1.0f);
26+
}
27+
28+
HDR_Image Framebuffer::resolve_colors() const {
29+
#ifdef STUDENT_CODE_A1T7
30+
// A1T7: resolve_colors
31+
// TODO: update to support sample patterns with more than one sample.
32+
33+
HDR_Image image(width, height);
34+
35+
for (uint32_t y = 0; y < height; ++y) {
36+
for (uint32_t x = 0; x < width; ++x) {
37+
image.at(x, y) = color_at(x, y, 0);
38+
}
39+
}
40+
41+
return image;
42+
#else
43+
// A1T7: resolve_colors -- reference code!
44+
45+
HDR_Image image(width, height);
46+
std::vector<Vec3> const& cw = sample_pattern.centers_and_weights;
47+
48+
for (uint32_t y = 0; y < height; ++y) {
49+
for (uint32_t x = 0; x < width; ++x) {
50+
Spectrum acc = cw[0].z * color_at(x, y, 0);
51+
for (uint32_t s = 1; s < cw.size(); ++s) {
52+
acc += cw[s].z * color_at(x, y, s);
53+
}
54+
image.at(x, y) = acc;
55+
}
56+
}
57+
58+
return image;
59+
#endif
60+
}

src/rasterizer/framebufferBase.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#pragma once
2+
3+
#include <vector>
4+
5+
#include "../lib/spectrum.h"
6+
7+
class HDR_Image;
8+
struct SamplePattern;
9+
10+
struct Framebuffer {
11+
static constexpr uint32_t MaxWidth = 4096;
12+
static constexpr uint32_t MaxHeight = 4096;
13+
14+
// Construct a new framebuffer:
15+
// width must be <= MaxWidth
16+
// height must be <= MaxHeight
17+
// both width and height must be even
18+
// these restrictions exist because:
19+
// - having a limited size makes it easier to write rasterization functions with fixed-point
20+
// math (if you want)
21+
// - having an even size avoids some corner cases if you choose to rasterize with quadfrags
22+
Framebuffer(uint32_t width, uint32_t height, SamplePattern const& sample_pattern);
23+
24+
const uint32_t width, height;
25+
SamplePattern const& sample_pattern;
26+
27+
// storage for color and depth samples:
28+
std::vector<Spectrum> colors;
29+
std::vector<float> depths;
30+
31+
// return storage index for sample s of pixel (x,y):
32+
uint32_t index(uint32_t x, uint32_t y, uint32_t s) const {
33+
#ifdef STUDENT_CODE_A1T7
34+
// A1T7: index
35+
// TODO: update to provide different storage locations for different samples
36+
return y * width + x;
37+
#else
38+
// A1T7: index -- reference code
39+
return (s * height + y) * width + x;
40+
#endif
41+
}
42+
43+
// helpers that look up colors and depths for sample s of pixel (x,y):
44+
Spectrum& color_at(uint32_t x, uint32_t y, uint32_t s) {
45+
return colors[index(x, y, s)];
46+
}
47+
Spectrum const& color_at(uint32_t x, uint32_t y, uint32_t s) const {
48+
return colors[index(x, y, s)];
49+
}
50+
float& depth_at(uint32_t x, uint32_t y, uint32_t s) {
51+
return depths[index(x, y, s)];
52+
}
53+
float const& depth_at(uint32_t x, uint32_t y, uint32_t s) const {
54+
return depths[index(x, y, s)];
55+
}
56+
57+
// resolve_colors creates a weighted average of the color samples:
58+
HDR_Image resolve_colors() const;
59+
};

0 commit comments

Comments
 (0)