Closed Roy2xc closed 1 year ago
And here is the function I used:
auto model = dynamic_cast<easy3d::SurfaceMesh*>(current_model());
SurfaceMeshPicker picker(camera());
if (model) {
auto face = picker.pick_face(model, x, y);
}
Then I used tessellator to triangulate the mesh, but still not work...
I don't understand you mean by "the log shows that my polyhedral mesh has 0 face". Can you post your full code (and is the minimum code that can reproduce the issue)?
I just print the number of faces of my model by "model->n_faces()" and it shows i have 3000+faces, but my picker cannot pick any faces... Here is the minimum code:
First load the obj, and print number of faces: Model* ExtrudeContour::add_model(const std::string& file_name) { if (!file_system::is_file(file_name)) { LOG(ERROR) << "file does not exist: " << file_name; return nullptr; }
if (file_system::extension(file_name, true) != "obj")
return Viewer::add_model(file_name, true);
fastObjMesh* fom = fast_obj_read(file_name.c_str());
if (!fom) {
LOG(ERROR) << "failed reading file: " + file_name;
return nullptr;
}
// Attention: Valid indices in the fastObjMesh::indices array start from 1.
// A dummy position, normal and texture coordinate are added to the corresponding fastObjMesh arrays at
// element 0 and then an index of 0 is used to indicate that attribute is not present at the vertex.
// ------------------------ build the mesh ------------------------
// clear the mesh in case of existing data
auto mesh = new SurfaceMesh;
mesh->set_name(file_name);
SurfaceMeshBuilder builder(mesh);
builder.begin_surface();
// add vertices
// skip the first point
for (std::size_t v = 1; v < fom->position_count; ++v) {
// Should I create vertices later, to get rid of isolated vertices?
builder.add_vertex(vec3(fom->positions + v * 3));
}
// create texture coordinate property if texture coordinates present
SurfaceMesh::HalfedgeProperty<vec2> prop_texcoords;
if (fom->texcoord_count > 0 && fom->texcoords) // index starts from 1 and the first element is dummy
prop_texcoords = mesh->add_halfedge_property<vec2>("h:texcoord");
// create face color property if material information exists
SurfaceMesh::FaceProperty<vec3> prop_face_color;
if (fom->material_count > 0 && fom->materials) // index starts from 1 and the first element is dummy
prop_face_color = mesh->add_face_property<vec3>("f:color");
// find the face's halfedge that points to v.
auto find_face_halfedge = [](SurfaceMesh* mesh, SurfaceMesh::Face face,
SurfaceMesh::Vertex v) -> SurfaceMesh::Halfedge {
for (auto h : mesh->halfedges(face)) {
if (mesh->target(h) == v)
return h;
}
LOG_N_TIMES(3, ERROR) << "could not find a halfedge pointing to " << v << " in face " << face
<< ". " << COUNTER;
return SurfaceMesh::Halfedge();
};
// group the faces according to the material
// each group is a set of faces sharing the same material
std::vector<internal::Group> groups(fom->material_count);
// for each shape
for (std::size_t ii = 0; ii < fom->group_count; ii++) {
const fastObjGroup& grp = fom->groups[ii];
// if (grp.name)
// std::cout << "group name: " << std::string(grp.name) << std::endl;
unsigned int idx = 0;
for (unsigned int jj = 0; jj < grp.face_count; ++jj) {
// number of vertices in the face
unsigned int fv = fom->face_vertices[grp.face_offset + jj];
std::vector<SurfaceMesh::Vertex> vertices;
std::vector<unsigned int> texcoord_ids;
for (unsigned int kk = 0; kk < fv; ++kk) { // for each vertex in the face
const fastObjIndex& mi = fom->indices[grp.index_offset + idx];
if (mi.p)
vertices.emplace_back(SurfaceMesh::Vertex(static_cast<int>(mi.p - 1)));
if (mi.t)
texcoord_ids.emplace_back(mi.t);
++idx;
}
SurfaceMesh::Face face = builder.add_face(vertices);
if (face.is_valid()) {
// texture coordinates
if (prop_texcoords && texcoord_ids.size() == vertices.size()) {
auto begin = find_face_halfedge(mesh, face, builder.face_vertices()[0]);
auto cur = begin;
unsigned int vid = 0;
do {
unsigned int tid = texcoord_ids[vid++];
prop_texcoords[cur] = vec2(fom->texcoords + 2 * tid);
cur = mesh->next(cur);
} while (cur != begin);
}
// now materials
if (prop_face_color) {
unsigned int mat_id = fom->face_materials[grp.face_offset + jj];
const fastObjMaterial& mat = fom->materials[mat_id];
prop_face_color[face] = vec3(mat.Kd); // currently easy3d uses only diffuse
}
auto get_file_name = [](const char* name, const char* path) -> std::string {
std::string file_name;
if (name && file_system::is_file(std::string(name)))
file_name = std::string(name);
else if (path && file_system::is_file(std::string(path)))
file_name = std::string(path);
else if (name && path) {
const std::string test_name = std::string(path) + "/" + std::string(name);
if (file_system::is_file(test_name))
file_name = test_name;
}
return file_name;
};
if (fom->material_count > 0 && fom->materials) {
unsigned int mat_id = fom->face_materials[grp.face_offset + jj];
const fastObjMaterial& mat = fom->materials[mat_id];
auto& g = groups[mat_id];
g.push_back(face);
g.ambient = vec3(mat.Ka);
g.diffuse = vec3(mat.Kd);
g.specular = vec3(mat.Ks);
g.shininess = static_cast<float>(mat.Ns);
g.tex_file = get_file_name(mat.map_Ka.name, mat.map_Ka.path); // use ambient texture it exists
if (g.tex_file.empty())
g.tex_file = get_file_name(mat.map_Kd.name, mat.map_Kd.path); // then try diffuse texture
if (g.tex_file.empty())
g.tex_file = get_file_name(mat.map_Ks.name, mat.map_Ks.path); // then try diffuse texture
}
}
}
}
builder.end_surface();
// since the mesh has been built, skip texture if material and texcoord information don't exist
if (fom->material_count == 0 || !fom->materials) {
Viewer::add_model(mesh, true);
return mesh;
}
else
Viewer::add_model(mesh, false);
mesh->update_vertex_normals();
auto normals = mesh->get_vertex_property<vec3>("v:normal");
auto points = mesh->get_vertex_property<vec3>("v:point");
Tessellator tessellator;
for (std::size_t i = 0; i < groups.size(); ++i) {
const auto& group = groups[i];
if (group.empty())
continue;
tessellator.reset();
for (auto face : group) {
tessellator.begin_polygon(mesh->compute_face_normal(face));
tessellator.set_winding_rule(Tessellator::WINDING_NONZERO); // or POSITIVE
tessellator.begin_contour();
for (auto h : mesh->halfedges(face)) {
auto v = mesh->target(h);
Tessellator::Vertex vtx(points[v], v.idx());
vtx.append(normals[v]);
if (prop_texcoords)
vtx.append(prop_texcoords[h]);
if (prop_face_color)
vtx.append(prop_face_color[face]);
tessellator.add_vertex(vtx);
}
tessellator.end_contour();
tessellator.end_polygon();
}
std::vector<vec3> d_points, d_colors, d_normals;
std::vector<vec2> d_texcoords;
const std::vector<Tessellator::Vertex*>& vts = tessellator.vertices();
for (auto v : vts) {
std::size_t offset = 0;
d_points.emplace_back(v->data() + offset);
offset += 3;
d_normals.emplace_back(v->data() + offset);
offset += 3;
if (prop_texcoords) {
d_texcoords.emplace_back(v->data() + offset);
offset += 2;
}
if (prop_face_color)
d_colors.emplace_back(v->data() + offset);
}
const auto& d_indices = tessellator.elements();
auto drawable = mesh->renderer()->add_triangles_drawable("faces_" + std::to_string(i));
drawable->update_element_buffer(d_indices);
drawable->update_vertex_buffer(d_points);
drawable->update_normal_buffer(d_normals);
if (!d_colors.empty())
drawable->update_color_buffer(d_colors);
if (!d_texcoords.empty())
drawable->update_texcoord_buffer(d_texcoords);
drawable->set_smooth_shading(false);
if (prop_texcoords) {
if (!group.tex_file.empty()) {
Texture* tex = TextureManager::request(group.tex_file, Texture::REPEAT);
if (tex) {
drawable->set_texture_coloring(State::HALFEDGE, "h:texcoord", tex);
drawable->set_distinct_back_color(false);
LOG(INFO) << "texture created from " << group.tex_file;
}
}
}
if (!drawable->texture()) { // in case texture creation failed
if (prop_face_color)
drawable->set_property_coloring(State::Location::FACE, "f:color");
else
drawable->set_uniform_coloring(vec4(group.diffuse, 1.0f));
}
}
std::cout << "#face: " << mesh->n_faces() << std::endl;
return mesh;
}
then try to use picker:
void MyfacePicker::mouse_release_select(int x, int y) {
auto model = dynamic_cast<easy3d::SurfaceMesh*>(current_model());
SurfaceMeshPicker picker(camera());
if (model) {
auto face = picker.pick_face(model, x, y);
std::cout << "face valid or not" << face.is_valid() << std::endl;
auto i_p = picker.picked_point(model, face, x, y);
std::cout <<"the point picked on the face" << i_p.x << " " << i_p.y << " " << i_p.z << std::endl;
polygon_w.push_back(vec2(i_p.x, i_p.y));
}
}
and the result is :
I think the issue is that the drawable name is not the default one. The picker expects a drawable named "faces". Can you make the name "faces" in the following line of code:
auto drawable = mesh->renderer()->add_triangles_drawable("faces_" + std::to_string(i));
I believe this should solve the problem. If not, please create a runnable project (with CMakeLists files so I can really build an executable from the files) and post it here?
Thank you very much! Your tip works! But I have another issue that "picker.pick_face()" cannot picker face of textured model? When use no-textured mesh, it works well, but if the mesh has texture in it, picker cannot pick the face, and cannot calculate the intersection point with the face... How to make the texture invisible and then make them visible? Will the texture coordinates be taken into account when picking faces?
Any texture information is simply ignored when picking, and there is not difference compared with picking non-textured models. It works well on my machine. Can you post your runnable code here so I can investigate it?
I'm sorry, my code is very messy and lengthy, and I can't publish all of it at once. Can I learn your method? Perhaps my method is incorrect. May I take a look at your demo?
Nothing need to be changed. The code is the same as for the non-texture meshes.
Yes, I found my problem, thank you very much~!
I have trouble picking faces of polyhedral mesh, the log shows that my polyhedral mesh has 0 face, but actually it does have 1 face. I create the polyhedral mesh in this way:
which is same as tutorial 305.
It confused me a lot, please help me and forgive my poor English....