Skip to content

Commit ac96051

Browse files
authored
Update model.cpp
1 parent 3997986 commit ac96051

1 file changed

Lines changed: 173 additions & 1 deletion

File tree

src/world/model.cpp

Lines changed: 173 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,199 @@ cg::world::model::~model() {}
1717
void cg::world::model::load_obj(const std::filesystem::path& model_path)
1818
{
1919
// TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, and `fill_buffers` methods of `cg::world::model` class
20+
// Парсинг OBJ/MTL
21+
tinyobj::attrib_t attrib;
22+
std::vector<tinyobj::shape_t> shapes;
23+
std::vector<tinyobj::material_t> materials;
24+
std::string warn, err;
25+
26+
std::filesystem::path base = model_path.parent_path();
27+
bool ok = tinyobj::LoadObj(
28+
&attrib, &shapes, &materials,
29+
&warn, &err,
30+
model_path.string().c_str(),
31+
base.string().c_str(),
32+
/*triangulate*/ true,
33+
/*default_vcols_fallback*/ false
34+
);
35+
36+
if (!warn.empty()) {
37+
// не критично, просто информируем
38+
std::cerr << "tinyobj warning: " << warn << std::endl;
39+
}
40+
if (!ok || !err.empty()) {
41+
THROW_ERROR(std::string("tinyobj error: ") + err);
42+
}
43+
44+
// Выделение буферов на шейп
45+
allocate_buffers(shapes);
46+
47+
// Заполнение VB/IB и списка текстур
48+
fill_buffers(shapes, attrib, materials, base);
2049
}
2150

2251
void model::allocate_buffers(const std::vector<tinyobj::shape_t>& shapes)
2352
{
2453
// TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, and `fill_buffers` methods of `cg::world::model` class
54+
vertex_buffers.clear();
55+
index_buffers.clear();
56+
textures.clear();
57+
vertex_buffers.reserve(shapes.size());
58+
index_buffers.reserve(shapes.size());
59+
textures.reserve(shapes.size());
60+
61+
for (const auto& s : shapes)
62+
{
63+
// Оценить верхнюю границу количества вершин по количеству индексов
64+
// (будет один к одному после развёртки индексов).
65+
size_t index_count = s.mesh.indices.size();
66+
// Создаём ресурсы: VB размером = index_count вершин, IB = index_count индексов
67+
auto vb = std::make_shared<cg::resource<cg::vertex>>(index_count);
68+
auto ib = std::make_shared<cg::resource<unsigned int>>(index_count);
69+
70+
vertex_buffers.push_back(vb);
71+
index_buffers.push_back(ib);
72+
73+
// Плейсхолдер под путь текстуры для шейпа (если будет)
74+
textures.emplace_back(std::filesystem::path{});
75+
}
2576
}
2677

2778
float3 cg::world::model::compute_normal(const tinyobj::attrib_t& attrib, const tinyobj::mesh_t& mesh, size_t index_offset)
2879
{
2980
// TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, and `fill_buffers` methods of `cg::world::model` class
30-
return float3{};
81+
// Вычисление геометрической нормали треугольника по 3 вершинам (позициям)
82+
// index_offset указывает на начало треугольника в mesh.indices (индекс a).
83+
const tinyobj::index_t ia = mesh.indices[index_offset + 0];
84+
const tinyobj::index_t ib = mesh.indices[index_offset + 1];
85+
const tinyobj::index_t ic = mesh.indices[index_offset + 2];
86+
87+
auto fetch_pos = [&](const tinyobj::index_t& idx)->float3 {
88+
size_t vi = static_cast<size_t>(idx.vertex_index);
89+
float x = attrib.vertices[3 * vi + 0];
90+
float y = attrib.vertices[3 * vi + 1];
91+
float z = attrib.vertices[3 * vi + 2];
92+
return float3{ x, y, z };
93+
};
94+
95+
float3 pa = fetch_pos(ia);
96+
float3 pb = fetch_pos(ib);
97+
float3 pc = fetch_pos(ic);
98+
99+
float3 e1 = pb - pa;
100+
float3 e2 = pc - pa;
101+
102+
// cross и normalize из linalg
103+
float3 n = normalize(cross(e1, e2));
104+
// На случай вырожденного треугольника
105+
if (!std::isfinite(n.x) || !std::isfinite(n.y) || !std::isfinite(n.z)) {
106+
n = float3{ 0.f, 0.f, 1.f };
107+
}
108+
return n;
31109
}
32110

33111
void model::fill_vertex_data(cg::vertex& vertex, const tinyobj::attrib_t& attrib, const tinyobj::index_t idx, const float3 computed_normal, const tinyobj::material_t material)
34112
{
35113
// TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, and `fill_buffers` methods of `cg::world::model` class
114+
// Позиция
115+
{
116+
size_t vi = static_cast<size_t>(idx.vertex_index);
117+
vertex.position = float3{
118+
attrib.vertices[3 * vi + 0],
119+
attrib.vertices[3 * vi + 1],
120+
attrib.vertices[3 * vi + 2]
121+
};
122+
}
123+
124+
// Нормаль: берем из атрибутов, если есть; иначе — скомпьютенную
125+
if (idx.normal_index >= 0) {
126+
size_t ni = static_cast<size_t>(idx.normal_index);
127+
vertex.normal = float3{
128+
attrib.normals[3 * ni + 0],
129+
attrib.normals[3 * ni + 1],
130+
attrib.normals[3 * ni + 2]
131+
};
132+
} else {
133+
vertex.normal = computed_normal;
134+
}
135+
136+
// UV: если есть
137+
if (idx.texcoord_index >= 0) {
138+
size_t ti = static_cast<size_t>(idx.texcoord_index);
139+
vertex.texcoord = float2{
140+
attrib.texcoords[2 * ti + 0],
141+
attrib.texcoords[2 * ti + 1]
142+
};
143+
} else {
144+
vertex.texcoord = float2{ 0.f, 0.f };
145+
}
36146
}
37147

148+
38149
void model::fill_buffers(const std::vector<tinyobj::shape_t>& shapes, const tinyobj::attrib_t& attrib, const std::vector<tinyobj::material_t>& materials, const std::filesystem::path& base_folder)
39150
{
40151
// TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, and `fill_buffers` methods of `cg::world::model` class
152+
for (size_t s = 0; s < shapes.size(); ++s)
153+
{
154+
const auto& shape = shapes[s];
155+
const auto& mesh = shape.mesh;
156+
157+
// Привязанные ресурсы для шейпа
158+
auto vb = vertex_buffers[s];
159+
auto ib = index_buffers[s];
160+
161+
// Индексная запись линейная: на каждый tinyobj индекс — уникальная вершина в нашем VB
162+
size_t write = 0;
163+
for (size_t f = 0; f < mesh.num_face_vertices.size(); ++f)
164+
{
165+
const size_t fv = static_cast<size_t>(mesh.num_face_vertices[f]);
166+
// Ожидаем triangulate=true => fv == 3
167+
if (fv != 3) {
168+
// пропускаем не‑треугольные фейсы на всякий случай
169+
continue;
170+
}
171+
const size_t index_offset = std::accumulate(mesh.num_face_vertices.begin(), mesh.num_face_vertices.begin() + f, size_t(0));
172+
173+
// Вычислим нормаль треугольника, если нормалей нет
174+
float3 tri_normal = compute_normal(attrib, mesh, index_offset);
175+
176+
// Материал для этой грани, если есть
177+
tinyobj::material_t mtl{};
178+
int mtl_id = mesh.material_ids.size() > f ? mesh.material_ids[f] : -1;
179+
if (mtl_id >= 0 && static_cast<size_t>(mtl_id) < materials.size()) {
180+
mtl = materials[static_cast<size_t>(mtl_id)];
181+
}
182+
183+
// Три вершины
184+
for (size_t v = 0; v < fv; ++v) {
185+
const tinyobj::index_t idx = mesh.indices[index_offset + v];
186+
187+
// Записываем вершину
188+
cg::vertex vert{};
189+
fill_vertex_data(vert, attrib, idx, tri_normal, mtl);
190+
vb->item(write) = vert;
191+
192+
// Индекс указывает на только что записанную вершину
193+
ib->item(write) = static_cast<unsigned int>(write);
194+
195+
++write;
196+
}
197+
}
198+
199+
// Текстуры на шейп: если материал назначен и есть diffuse_texname, сохраним относительный путь
200+
// Берём первый валидный материал у шейпа.
201+
std::filesystem::path tex_path{};
202+
for (int mid : mesh.material_ids) {
203+
if (mid >= 0 && static_cast<size_t>(mid) < materials.size()) {
204+
const auto& mtl = materials[static_cast<size_t>(mid)];
205+
if (!mtl.diffuse_texname.empty()) {
206+
tex_path = (base_folder / mtl.diffuse_texname).lexically_normal();
207+
break;
208+
}
209+
}
210+
}
211+
textures[s] = tex_path;
212+
} //for
41213
}
42214

43215

0 commit comments

Comments
 (0)