Loading triangles from obj file using CGAL

本文介绍了一个使用 CGAL 库构建三维多面体的代码示例。该程序从 OBJ 文件加载顶点坐标和三角形索引,创建 CGAL::Polyhedron_3 对象,并将其保存为 OFF 文件格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

I've found myself redoing this code relatively frequently, so I thought I would post it.  Nothing special, just a snippet that loads the file input.obj, builds a CGAL::Polyhedron_3 from it, and writes it out as dump.off. The code also includes a rudimentary Wavefront OBJ loader.


#include<fstream>
#include<vector>
#include<string>
#include<algorithm>

#include<CGAL/Simple_cartesian.h>
#include<CGAL/Polyhedron_incremental_builder_3.h>
#include<CGAL/Polyhedron_3.h>
#include<CGAL/IO/Polyhedron_iostream.h>

typedef CGAL::Simple_cartesian<double>     Kernel;
typedef CGAL::Polyhedron_3<Kernel>         Polyhedron;
typedef Polyhedron::HalfedgeDS             HalfedgeDS;

// A modifier creating a triangle with the incremental builder.
template<class HDS>
class polyhedron_builder : public CGAL::Modifier_base<HDS> {
public:
 std::vector<double> &coords;
 std::vector<int>    &tris;
    polyhedron_builder( std::vector<double> &_coords, std::vector<int> &_tris ) : coords(_coords), tris(_tris) {}
    void operator()( HDS& hds) {
  typedef typename HDS::Vertex   Vertex;
        typedef typename Vertex::Point Point;

  // create a cgal incremental builder
        CGAL::Polyhedron_incremental_builder_3<HDS> B( hds, true);
        B.begin_surface( coords.size()/3, tris.size()/3 );
  
  // add the polyhedron vertices
  for( int i=0; i<(int)coords.size(); i+=3 ){
   B.add_vertex( Point( coords[i+0], coords[i+1], coords[i+2] ) );
  }
  
  // add the polyhedron triangles
  for( int i=0; i<(int)tris.size(); i+=3 ){
   B.begin_facet();
   B.add_vertex_to_facet( tris[i+0] );
   B.add_vertex_to_facet( tris[i+1] );
   B.add_vertex_to_facet( tris[i+2] );
   B.end_facet();
  }
  
  // finish up the surface
        B.end_surface();
    }
};

// reads the first integer from a string in the form
// "334/455/234" by stripping forward slashes and
// scanning the result
int get_first_integer( const char *v ){
 int ival;
 std::string s( v );
 std::replace( s.begin(), s.end(), '/', ' ' );
 sscanf( s.c_str(), "%d", &ival );
 return ival;
}

// barebones .OFF file reader, throws away texture coordinates, normals, etc.
// stores results in input coords array, packed [x0,y0,z0,x1,y1,z1,...] and
// tris array packed [T0a,T0b,T0c,T1a,T1b,T1c,...]
void load_obj( const char *filename, std::vector<double> &coords, std::vector<int> &tris ){
 double x, y, z;
 char line[1024], v0[1024], v1[1024], v2[1024];
 
 // open the file, return if open fails
 FILE *fp = fopen(filename, "r" );
 if( !fp ) return;
 
 // read lines from the file, if the first character of the
 // line is 'v', we are reading a vertex, otherwise, if the
 // first character is a 'f' we are reading a facet
 while( fgets( line, 1024, fp ) ){
  if( line[0] == 'v' ){
   sscanf( line, "%*s%lf%lf%lf", &x, &y, &z );
   coords.push_back( x );
   coords.push_back( y );
   coords.push_back( z );
  } else if( line[0] == 'f' ){
   sscanf( line, "%*s%s%s%s", v0, v1, v2 );
   tris.push_back( get_first_integer( v0 )-1 );
   tris.push_back( get_first_integer( v1 )-1 );
   tris.push_back( get_first_integer( v2 )-1 );
  }
 }
 fclose(fp); 
}

int main() {
 // two vectors to hold point coordinates and
 // triangle vertex indices
 std::vector<double> coords;
 std::vector<int>    tris;
 
 // load the input file
 load_obj( "input.obj", coords, tris );
 if( coords.size() == 0 )
  return 1;
 
 // build a polyhedron from the loaded arrays
 Polyhedron P;
 polyhedron_builder<HalfedgeDS> builder( coords, tris );
 P.delegate( builder );
 
 // write the polyhedron out as a .OFF file
 std::ofstream os("dump.off");
 os << P;
 os.close();
 
    return 0;
}

From:http://jamesgregson.blogspot.com/2012/05/example-code-for-building.html
### 加载并渲染 OBJ 文件到 OpenGL 加载和渲染 `.obj` 文件是一个常见的需求,在 OpenGL 中可以通过解析 `.obj` 文件的内容来实现这一目标。以下是详细的说明: #### 解析 `.obj` 文件 `.obj` 文件是一种简单的三维模型文件格式,通常包含顶点坐标 (`v`)、纹理坐标 (`vt`) 和法线向量 (`vn`) 的定义以及面片索引 (`f`)。为了在 OpenGL 中使用这些数据,需要先将其读取并转换为适合 GPU 渄染的数据结构。 可以手动编写代码来解析 `.obj` 文件,或者使用第三方库简化此过程。如果选择手动方式,则需逐行读取文件内容,并提取 `v`, `vt`, `vn`, 和 `f` 数据[^1]。 ```cpp #include <iostream> #include <fstream> #include <vector> #include <string> struct Vertex { float position[3]; float normal[3]; float texCoord[2]; }; std::vector<Vertex> vertices; std::vector<unsigned int> indices; void parseObj(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { throw std::runtime_error("Could not open file"); } std::vector<float> positions; std::vector<float> normals; std::vector<float> texCoords; std::string line; while (getline(file, line)) { if (line.substr(0, 2) == "v ") { // Parse vertex coordinates sscanf(line.c_str(), "v %f %f %f", &positions.back()[0], &positions.back()[1], &positions.back()[2]); } else if (line.substr(0, 2) == "vt") { // Parse texture coordinates sscanf(line.c_str(), "vt %f %f", &texCoords.back()[0], &texCoords.back()[1]); } else if (line.substr(0, 2) == "vn") { // Parse normal vectors sscanf(line.c_str(), "vn %f %f %f", &normals.back()[0], &normals.back()[1], &normals.back()[2]); } else if (line.substr(0, 2) == "f ") { // Parse face definitions static size_t v_idx = 0; char separator; unsigned int idx[3][3]; sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &idx[0][0], &idx[0][1], &idx[0][2], &idx[1][0], &idx[1][1], &idx[1][2], &idx[2][0], &idx[2][1], &idx[2][2]); for (int i = 0; i < 3; ++i) { Vertex vert; vert.position[0] = positions[(idx[i][0]-1)*3]; vert.position[1] = positions[(idx[i][0]-1)*3 + 1]; vert.position[2] = positions[(idx[i][0]-1)*3 + 2]; vert.normal[0] = normals[(idx[i][2]-1)*3]; vert.normal[1] = normals[(idx[i][2]-1)*3 + 1]; vert.normal[2] = normals[(idx[i][2]-1)*3 + 2]; vert.texCoord[0] = texCoords[(idx[i][1]-1)*2]; vert.texCoord[1] = texCoords[(idx[i][1]-1)*2 + 1]; vertices.push_back(vert); indices.push_back(v_idx++); } } } } ``` #### 使用 VBO 和 IBO 进行渲染 一旦成功解析了 `.obj` 文件中的几何数据,就可以通过创建 **Vertex Buffer Object (VBO)** 来存储顶点属性,并利用 **Index Buffer Object (IBO)** 存储索引数组[^4]。 下面展示如何设置缓冲区对象并将它们绑定至着色器程序中: ```cpp GLuint vbo, ibo, vao; glGenBuffers(1, &vbo); glGenBuffers(1, &ibo); glGenVertexArrays(1, &vao); // Bind VAO first before binding buffers. glBindVertexArray(vao); // Upload vertex data into a VBO. glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW); // Set up attribute pointers within the currently bound VAO. GLsizei stride = sizeof(Vertex); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, nullptr); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(offsetof(Vertex, normal))); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(offsetof(Vertex, texCoord))); // Upload index data into an EBO/IBO. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indices.size(), indices.data(), GL_STATIC_DRAW); // Unbind everything after setup is done. glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(0); ``` 最后一步是在绘制调用期间激活相应的资源并提交绘图命令给硬件管线执行: ```cpp glUseProgram(shaderProgramID); glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, nullptr); ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值