29#include <unordered_map>
31#include <easy3d/core/surface_mesh.h>
32#include <easy3d/core/surface_mesh_builder.h>
33#include <easy3d/renderer/texture_manager.h>
34#include <easy3d/renderer/camera.h>
35#include <easy3d/renderer/drawable_triangles.h>
36#include <easy3d/renderer/renderer.h>
37#include <easy3d/algo/tessellator.h>
38#include <easy3d/util/file_system.h>
39#include <easy3d/util/logging.h>
41#define FAST_OBJ_IMPLEMENTATION
42#include <3rd_party/fastobj/fast_obj.h>
48 TexturedViewer::TexturedViewer(
const std::string &title)
50 camera()->setUpVector(
vec3(0, 1, 0));
56 struct Group :
public std::vector<SurfaceMesh::Face> {
66 Model *TexturedViewer::add_model(
const std::string &file_name,
bool create_default_drawables) {
70 LOG(ERROR) <<
"file does not exist: " << file_name;
77 fastObjMesh *fom = fast_obj_read(file_name.c_str());
79 LOG(ERROR) <<
"failed reading file: " + file_name;
90 auto mesh =
new SurfaceMesh;
91 mesh->set_name(file_name);
93 SurfaceMeshBuilder builder(mesh);
94 builder.begin_surface();
98 for (std::size_t v = 1; v < fom->position_count; ++v) {
100 builder.add_vertex(
vec3(fom->positions + v * 3));
104 SurfaceMesh::HalfedgeProperty<vec2> prop_texcoords;
105 if (fom->texcoord_count > 0 && fom->texcoords)
106 prop_texcoords = mesh->add_halfedge_property<
vec2>(
"h:texcoord");
109 SurfaceMesh::FaceProperty<vec3> prop_face_color;
110 if (fom->material_count > 0 && fom->materials)
111 prop_face_color = mesh->add_face_property<
vec3>(
"f:color");
114 auto find_face_halfedge = [](SurfaceMesh *mesh, SurfaceMesh::Face face,
115 SurfaceMesh::Vertex v) -> SurfaceMesh::Halfedge {
116 for (
auto h : mesh->halfedges(face)) {
117 if (mesh->target(h) == v)
120 LOG_N_TIMES(3, ERROR) <<
"could not find a halfedge pointing to " << v <<
" in face " << face
122 return SurfaceMesh::Halfedge();
127 std::vector<internal::Group> groups(fom->material_count);
130 for (std::size_t ii = 0; ii < fom->group_count; ii++) {
131 const fastObjGroup &grp = fom->groups[ii];
136 unsigned int idx = 0;
137 for (
unsigned int jj = 0; jj < grp.face_count; ++jj) {
139 unsigned int fv = fom->face_vertices[grp.face_offset + jj];
140 std::vector<SurfaceMesh::Vertex> vertices;
141 std::vector<unsigned int> texcoord_ids;
142 for (
unsigned int kk = 0; kk < fv; ++kk) {
143 const fastObjIndex &mi = fom->indices[grp.index_offset + idx];
145 vertices.emplace_back(SurfaceMesh::Vertex(
static_cast<int>(mi.p - 1)));
147 texcoord_ids.emplace_back(mi.t);
151 SurfaceMesh::Face face = builder.add_face(vertices);
152 if (face.is_valid()) {
154 if (prop_texcoords && texcoord_ids.size() == vertices.size()) {
155 auto begin = find_face_halfedge(mesh, face, builder.face_vertices()[0]);
157 unsigned int vid = 0;
159 unsigned int tid = texcoord_ids[vid++];
160 prop_texcoords[cur] =
vec2(fom->texcoords + 2 * tid);
161 cur = mesh->next(cur);
162 }
while (cur != begin);
166 if (prop_face_color) {
167 unsigned int mat_id = fom->face_materials[grp.face_offset + jj];
168 const fastObjMaterial &mat = fom->materials[mat_id];
169 prop_face_color[face] =
vec3(mat.Kd);
172 auto get_file_name = [](
const char *name,
const char* path) -> std::string {
173 std::string file_name;
175 file_name = std::string(name);
177 file_name = std::string(path);
178 else if (name && path){
179 const std::string test_name = std::string(path) +
"/" + std::string(name);
181 file_name = test_name;
186 if (fom->material_count > 0 && fom->materials) {
187 unsigned int mat_id = fom->face_materials[grp.face_offset + jj];
188 const fastObjMaterial &mat = fom->materials[mat_id];
189 auto &g = groups[mat_id];
191 g.ambient =
vec3(mat.Ka);
192 g.diffuse =
vec3(mat.Kd);
193 g.specular =
vec3(mat.Ks);
194 g.shininess =
static_cast<float>(mat.Ns);
195 g.tex_file = get_file_name(mat.map_Ka.name, mat.map_Ka.path);
196 if (g.tex_file.empty())
197 g.tex_file = get_file_name(mat.map_Kd.name, mat.map_Kd.path);
198 if (g.tex_file.empty())
199 g.tex_file = get_file_name(mat.map_Ks.name, mat.map_Ks.path);
205 builder.end_surface();
208 if (fom->material_count == 0 || !fom->materials) {
215 mesh->update_vertex_normals();
216 auto normals = mesh->get_vertex_property<
vec3>(
"v:normal");
217 auto points = mesh->get_vertex_property<
vec3>(
"v:point");
219 Tessellator tessellator;
220 for (std::size_t i = 0; i < groups.size(); ++i) {
221 const auto &group = groups[i];
227 for (
auto face : group) {
228 tessellator.begin_polygon(mesh->compute_face_normal(face));
229 tessellator.set_winding_rule(Tessellator::WINDING_NONZERO);
230 tessellator.begin_contour();
231 for (
auto h : mesh->halfedges(face)) {
232 auto v = mesh->target(h);
233 Tessellator::Vertex vtx(points[v], v.idx());
234 vtx.append(normals[v]);
236 vtx.append(prop_texcoords[h]);
238 vtx.append(prop_face_color[face]);
239 tessellator.add_vertex(vtx);
241 tessellator.end_contour();
242 tessellator.end_polygon();
245 std::vector<vec3> d_points, d_colors, d_normals;
246 std::vector<vec2> d_texcoords;
247 const std::vector<Tessellator::Vertex*>& vts = tessellator.vertices();
249 std::size_t offset = 0;
250 d_points.emplace_back(v->data() + offset);
252 d_normals.emplace_back(v->data() + offset);
254 if (prop_texcoords) {
255 d_texcoords.emplace_back(v->data() + offset);
259 d_colors.emplace_back(v->data() + offset);
262 const auto &d_indices = tessellator.elements();
264 auto drawable = mesh->renderer()->add_triangles_drawable(
"faces_" + std::to_string(i));
266 drawable->update_element_buffer(d_indices);
267 drawable->update_vertex_buffer(d_points);
268 drawable->update_normal_buffer(d_normals);
269 if (!d_colors.empty())
270 drawable->update_color_buffer(d_colors);
271 if (!d_texcoords.empty())
272 drawable->update_texcoord_buffer(d_texcoords);
274 drawable->set_smooth_shading(
false);
275 drawable->set_distinct_back_color(
false);
276 if (prop_texcoords) {
277 if (!group.tex_file.empty()) {
280 drawable->set_texture_coloring(State::HALFEDGE,
"h:texcoord", tex);
281 drawable->set_distinct_back_color(
false);
282 LOG(INFO) <<
"texture created from " << group.tex_file;
287 if (!drawable->texture()) {
289 drawable->set_property_coloring(State::Location::FACE,
"f:color");
291 drawable->set_uniform_coloring(
vec4(group.diffuse, 1.0f));
static Texture * request(const std::string &image_file, Texture::WrapMode wrap=Texture::CLAMP_TO_EDGE, Texture::FilterMode filter=Texture::LINEAR)
Request a texture from the image file.
Definition: texture_manager.cpp:40
virtual Model * add_model(const std::string &file_name, bool create_default_drawables=true)
Add a model from a file to the viewer to be visualized. On success, the viewer will be in charge of t...
Definition: viewer.cpp:1204
bool is_file(const std::string &path)
Tests if 'path' is an existing file.
std::string extension(const std::string &path, bool lower=true)
Query the file extension without dot (e.g., /a/b/c.Ext => Ext).
Vec< 3, float > vec3
A 3D point/vector of float type.
Definition: types.h:45
Vec< 4, float > vec4
A 4D point/vector of float type.
Definition: types.h:47
Vec< 2, float > vec2
A 2D point/vector of float type.
Definition: types.h:43