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>
49 TexturedViewer::TexturedViewer(
const std::string &title)
51 camera()->setUpVector(
vec3(0, 1, 0));
57 struct Group :
public std::vector<SurfaceMesh::Face> {
67 Model* TexturedViewer::add_model(
const std::string &file_name,
bool create_default_drawables) {
71 LOG(ERROR) <<
"file does not exist: " << file_name;
78 fastObjMesh *fom = fast_obj_read(file_name.c_str());
80 LOG(ERROR) <<
"failed reading file: " + file_name;
91 auto mesh =
new SurfaceMesh;
92 mesh->set_name(file_name);
94 SurfaceMeshBuilder builder(mesh);
95 builder.begin_surface();
99 for (std::size_t v = 1; v < fom->position_count; ++v) {
101 builder.add_vertex(
vec3(fom->positions + v * 3));
105 SurfaceMesh::HalfedgeProperty<vec2> prop_texcoords;
106 if (fom->texcoord_count > 0 && fom->texcoords)
107 prop_texcoords = mesh->add_halfedge_property<
vec2>(
"h:texcoord");
110 SurfaceMesh::FaceProperty<vec3> prop_face_color;
111 if (fom->material_count > 0 && fom->materials)
112 prop_face_color = mesh->add_face_property<
vec3>(
"f:color");
115 auto find_face_halfedge = [](SurfaceMesh *mesh, SurfaceMesh::Face face,
116 SurfaceMesh::Vertex v) -> SurfaceMesh::Halfedge {
117 for (
auto h : mesh->halfedges(face)) {
118 if (mesh->target(h) == v)
121 LOG_N_TIMES(3, ERROR) <<
"failed to find halfedge pointing to " << v <<
" in face " << face <<
". " << COUNTER;
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)
209 return Viewer::add_model(std::shared_ptr<SurfaceMesh>(mesh), create_default_drawables);
213 mesh->update_vertex_normals();
214 auto normals = mesh->get_vertex_property<
vec3>(
"v:normal");
215 auto points = mesh->get_vertex_property<
vec3>(
"v:point");
217 Tessellator tessellator;
218 for (std::size_t i = 0; i < groups.size(); ++i) {
219 const auto &group = groups[i];
225 for (
auto face : group) {
226 tessellator.begin_polygon(mesh->compute_face_normal(face));
227 tessellator.set_winding_rule(Tessellator::WINDING_NONZERO);
228 tessellator.begin_contour();
229 for (
auto h : mesh->halfedges(face)) {
230 auto v = mesh->target(h);
231 Tessellator::Vertex vtx(points[v], v.idx());
232 vtx.append(normals[v]);
234 vtx.append(prop_texcoords[h]);
236 vtx.append(prop_face_color[face]);
237 tessellator.add_vertex(vtx);
239 tessellator.end_contour();
240 tessellator.end_polygon();
243 std::vector<vec3> d_points, d_colors, d_normals;
244 std::vector<vec2> d_texcoords;
245 const std::vector<Tessellator::Vertex*>& vts = tessellator.vertices();
247 std::size_t offset = 0;
248 d_points.emplace_back(v->data() + offset);
250 d_normals.emplace_back(v->data() + offset);
252 if (prop_texcoords) {
253 d_texcoords.emplace_back(v->data() + offset);
257 d_colors.emplace_back(v->data() + offset);
260 const auto &d_indices = tessellator.elements();
262 auto drawable = mesh->renderer()->add_triangles_drawable(
"faces_" + std::to_string(i));
264 drawable->update_element_buffer(d_indices);
265 drawable->update_vertex_buffer(d_points);
266 drawable->update_normal_buffer(d_normals);
267 if (!d_colors.empty())
268 drawable->update_color_buffer(d_colors);
269 if (!d_texcoords.empty())
270 drawable->update_texcoord_buffer(d_texcoords);
272 drawable->set_smooth_shading(
false);
273 drawable->set_distinct_back_color(
false);
274 if (prop_texcoords) {
275 if (!group.tex_file.empty()) {
279 drawable->set_distinct_back_color(
false);
280 LOG(INFO) <<
"texture created from " << group.tex_file;
285 if (!drawable->texture()) {
289 drawable->set_uniform_coloring(
vec4(group.diffuse, 1.0f));
@ FACE
Property defined on faces.
Definition state.h:70
@ HALFEDGE
Property defined on halfedges.
Definition state.h:72
@ REPEAT
Repeat the texture coordinate.
Definition texture.h:47
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:54
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:1242
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:44
Vec< 4, float > vec4
A 4D point/vector of float type.
Definition types.h:46
Vec< 2, float > vec2
A 2D point/vector of float type.
Definition types.h:42