meshio.cpp
来自「RBF平台」· C++ 代码 · 共 1,634 行 · 第 1/3 页
CPP
1,634 行
COND_READ(norm_skip > 0, buf[0], norm_skip);
COND_READ(have_norm, mesh->normals[i][0], 12);
COND_READ(color_skip > 0, buf[0], color_skip);
COND_READ(have_colors, mesh->colors[i][0], 3);
COND_READ(conf_skip > 0, buf[0], conf_skip);
COND_READ(have_conf, mesh->confidences[i], 4);
check_need_swap(mesh->vertices[i], need_swap);
if (need_swap) {
swap_float(mesh->vertices[i][0]);
swap_float(mesh->vertices[i][1]);
swap_float(mesh->vertices[i][2]);
if (have_norm) {
swap_float(mesh->normals[i][0]);
swap_float(mesh->normals[i][1]);
swap_float(mesh->normals[i][2]);
}
if (have_conf)
swap_float(mesh->confidences[i]);
}
TriMesh::dprintf("\n Reading %d vertices... ", nverts);
if (vert_len == 12 && sizeof(point) == 12 && nverts > 1)
return slurp_verts_bin(f, mesh, need_swap, nverts);
while (++i < new_nverts) {
COND_READ(skip > 0, buf[0], skip);
COND_READ(true, mesh->vertices[i][0], 12);
COND_READ(norm_skip > 0, buf[0], norm_skip);
COND_READ(have_norm, mesh->normals[i][0], 12);
COND_READ(color_skip > 0, buf[0], color_skip);
COND_READ(have_colors, mesh->colors[i][0], 3);
COND_READ(conf_skip > 0, buf[0], conf_skip);
COND_READ(have_conf, mesh->confidences[i], 4);
if (need_swap) {
swap_float(mesh->vertices[i][0]);
swap_float(mesh->vertices[i][1]);
swap_float(mesh->vertices[i][2]);
if (have_norm) {
swap_float(mesh->normals[i][0]);
swap_float(mesh->normals[i][1]);
swap_float(mesh->normals[i][2]);
}
if (have_conf)
swap_float(mesh->confidences[i]);
}
}
int final_skip = skip - vert_pos;
COND_READ(final_skip > 0, buf[0], final_skip);
return true;
}
// Optimized reader for the simple case of just vertices w/o other properties
bool CMeshIO::slurp_verts_bin(FILE *f, TriMesh *mesh, bool need_swap, int nverts)
{
int first = mesh->vertices.size() - nverts + 1;
COND_READ(true, mesh->vertices[first][0], (nverts-1)*12);
if (need_swap) {
for (int i = first; i < mesh->vertices.size(); i++) {
swap_float(mesh->vertices[i][0]);
swap_float(mesh->vertices[i][1]);
swap_float(mesh->vertices[i][2]);
}
}
return true;
}
// Read a bunch of vertices from an ASCII file
bool CMeshIO::read_verts_asc(FILE *f, TriMesh *mesh,
int nverts, int vert_len, int vert_pos, int vert_norm,
int vert_color, int vert_conf)
{
if (nverts <= 0 || vert_len < 3 || vert_pos < 0)
return false;
int old_nverts = mesh->vertices.size();
int new_nverts = old_nverts + nverts;
mesh->vertices.resize(new_nverts);
if (vert_norm > 0)
mesh->normals.resize(new_nverts);
if (vert_color > 0)
mesh->colors.resize(new_nverts);
if (vert_conf > 0)
mesh->confidences.resize(new_nverts);
char buf[1024];
skip_comments(f);
TriMesh::dprintf("\n Reading %d vertices... ", nverts);
for (int i = old_nverts; i < new_nverts; i++) {
for (int j = 0; j < vert_len; j++) {
if (j == vert_pos) {
if (fscanf(f, "%f %f %f",
&mesh->vertices[i][0],
&mesh->vertices[i][1],
&mesh->vertices[i][2]) != 3)
return false;
j += 2;
} else if (j == vert_norm) {
if (fscanf(f, "%f %f %f",
&mesh->normals[i][0],
&mesh->normals[i][1],
&mesh->normals[i][2]) != 3)
return false;
j += 2;
} else if (j == vert_color) {
int r, g, b;
if (fscanf(f, "%d %d %d", &r, &g, &b) != 3)
return false;
mesh->colors[i][0] = __min(__max(r, 0), 255);
mesh->colors[i][1] = __min(__max(g, 0), 255);
mesh->colors[i][2] = __min(__max(b, 0), 255);
j += 2;
} else if (j == vert_conf) {
if (fscanf(f, "%f", &mesh->confidences[i]) != 1)
return false;
} else {
fscanf(f, " %1024s", buf);
}
}
}
return true;
}
// Read a bunch of faces from a binary file
bool CMeshIO::read_faces_bin(FILE *f, TriMesh *mesh, bool need_swap,
int nfaces, int face_len, int face_count, int face_idx)
{
if (nfaces < 0 || face_idx < 0)
return false;
if (nfaces == 0)
return true;
TriMesh::dprintf("\n Reading %d faces... ", nfaces);
int old_nfaces = mesh->faces.size();
int new_nfaces = old_nfaces + nfaces;
mesh->faces.reserve(new_nfaces);
// face_len doesn't include the indices themeselves, since that's
// potentially variable-length
int face_skip = face_len - face_idx;
char buf[1024];
vector<int> thisface;
for (int i = 0; i < nfaces; i++) {
int this_face_count = 0;
if (face_count >= 0) {
COND_READ(face_count > 0, buf[0], face_count);
if (face_idx - face_count == 4) {
COND_READ(true, this_face_count, 4);
if (need_swap)
swap_int(this_face_count);
} else { // XXX - FIXME
COND_READ(true, buf[0], 1);
this_face_count = * (unsigned char *) buf;
}
} else {
this_face_count = 3;
COND_READ(face_idx > 0, buf[0], face_idx);
}
thisface.resize(this_face_count);
COND_READ(true, thisface[0], 4*this_face_count);
if (need_swap) {
for (int j = 0; j < thisface.size(); j++)
swap_int(thisface[j]);
}
tess(mesh->vertices, thisface, mesh->faces);
COND_READ(face_skip > 0, buf[0], face_skip);
}
return true;
}
// Read a bunch of faces from an ASCII file
bool CMeshIO::read_faces_asc(FILE *f, TriMesh *mesh, int nfaces,
int face_len, int face_count, int face_idx, bool read_to_eol /* = false */)
{
if (nfaces < 0 || face_idx < 0)
return false;
if (nfaces == 0)
return true;
int old_nfaces = mesh->faces.size();
int new_nfaces = old_nfaces + nfaces;
mesh->faces.reserve(new_nfaces);
char buf[1024];
skip_comments(f);
TriMesh::dprintf("\n Reading %d faces... ", nfaces);
vector<int> thisface;
for (int i = 0; i < nfaces; i++) {
thisface.clear();
int this_face_count = 3;
for (int j = 0; j < face_len + this_face_count; j++) {
if (j >= face_idx && j < face_idx + this_face_count) {
thisface.push_back(0);
if (!fscanf(f, " %d", &(thisface.back())))
return false;
} else if (j == face_count) {
if (!fscanf(f, " %d", &this_face_count))
return false;
} else {
fscanf(f, " %s", buf);
}
}
tess(mesh->vertices, thisface, mesh->faces);
if (read_to_eol) {
while (1) {
int c = fgetc(f);
if (c == EOF)
return false;
if (c == '\n')
break;
}
}
}
return true;
}
// Read triangle strips from a binary file
bool CMeshIO::read_strips_bin(FILE *f, TriMesh *mesh, bool need_swap)
{
int striplen;
COND_READ(true, striplen, 4);
if (need_swap)
swap_int(striplen);
int old_striplen = mesh->tstrips.size();
int new_striplen = old_striplen + striplen;
mesh->tstrips.resize(new_striplen);
TriMesh::dprintf("\n Reading triangle strips... ");
COND_READ(true, mesh->tstrips[old_striplen], 4*striplen);
if (need_swap) {
for (int i = old_striplen; i < new_striplen; i++)
swap_int(mesh->tstrips[i]);
}
return true;
}
// Read triangle strips from an ASCII file
bool CMeshIO::read_strips_asc(FILE *f, TriMesh *mesh)
{
skip_comments(f);
int striplen;
if (fscanf(f, "%d", &striplen) != 1)
return false;
int old_striplen = mesh->tstrips.size();
int new_striplen = old_striplen + striplen;
mesh->tstrips.resize(new_striplen);
TriMesh::dprintf("\n Reading triangle strips... ");
skip_comments(f);
for (int i = old_striplen; i < new_striplen; i++)
if (fscanf(f, "%d", &mesh->tstrips[i]) != 1)
return false;
return true;
}
// Read range grid data from a binary file
bool CMeshIO::read_grid_bin(FILE *f, TriMesh *mesh, bool need_swap,
int range_width, int range_height)
{
TriMesh::dprintf("\n Reading range grid... ");
int ngrid = range_width*range_height;
vector<int> grid(ngrid, -1);
for (int i = 0; i < ngrid; i++) {
int n = fgetc(f);
if (n == EOF)
return false;
while (n--) {
if (!fread((void *)&(grid[i]), 4, 1, f))
return false;
if (need_swap)
swap_int(grid[i]);
}
}
triangulate_grid(mesh, range_width, range_height, grid);
return true;
}
// Read range grid data from an ASCII file
bool CMeshIO::read_grid_asc(FILE *f, TriMesh *mesh,
int range_width, int range_height)
{
TriMesh::dprintf("\n Reading range grid... ");
int ngrid = range_width*range_height;
vector<int> grid(ngrid, -1);
for (int i = 0; i < ngrid; i++) {
int n;
if (fscanf(f, "%d", &n) != 1)
return false;
while (n--) {
if (fscanf(f, "%d", &(grid[i])) != 1)
return false;
}
}
triangulate_grid(mesh, range_width, range_height, grid);
return true;
}
// Helper function - make a face with the 3 given vertices from the grid
void CMeshIO::mkface(TriMesh *mesh, vector<int> &grid, int v1, int v2, int v3)
{
mesh->faces.push_back(TriMesh::Face(grid[v1], grid[v2], grid[v3]));
}
// Triangulate a range grid
void CMeshIO::triangulate_grid(TriMesh *mesh, int range_width, int range_height,
vector<int> &grid)
{
int i, j;
// Work around broken files that have a vertex position of (0,0,0)
// but mark the vertex as valid
for (i = 0; i < range_width*range_height; i++)
if (grid[i] != -1 && mesh->vertices[grid[i]] == point(0,0,0))
grid[i] = -1;
// Count valid faces
int ntris = 0;
for (j = 0; j < range_height - 1; j++) {
for (i = 0; i < range_width - 1; i++) {
int ll = i + j * range_width;
int lr = ll + 1;
int ul = ll + range_width;
int ur = ul + 1;
int nvalid = (grid[ll] >= 0) + (grid[lr] >= 0) +
(grid[ul] >= 0) + (grid[ur] >= 0);
if (nvalid == 4)
ntris += 2;
else if (nvalid == 3)
ntris += 1;
}
}
// Actually make the faces
int old_nfaces = mesh->faces.size();
int new_nfaces = old_nfaces + ntris;
mesh->faces.reserve(new_nfaces);
for (j = 0; j < range_height - 1; j++) {
for (i = 0; i < range_width - 1; i++) {
int ll = i + j * range_width;
int lr = ll + 1;
int ul = ll + range_width;
int ur = ul + 1;
int nvalid = (grid[ll] >= 0) + (grid[lr] >= 0) +
(grid[ul] >= 0) + (grid[ur] >= 0);
if (nvalid < 3)
continue;
if (nvalid == 4) {
// Triangulate in the direction that
// gives the shorter diagonal
float ll_ur = dist2(mesh->vertices[grid[ll]],
mesh->vertices[grid[ur]]);
float lr_ul = dist2(mesh->vertices[grid[lr]],
mesh->vertices[grid[ul]]);
if (ll_ur < lr_ul) {
mkface(mesh, grid, ll, lr, ur);
mkface(mesh, grid, ll, ur, ul);
} else {
mkface(mesh, grid, ll, lr, ul);
mkface(mesh, grid, lr, ur, ul);
}
continue;
}
// nvalid == 3
if (grid[ll] < 0)
mkface(mesh, grid, lr, ur, ul);
else if (grid[lr] < 0)
mkface(mesh, grid, ll, ur, ul);
else if (grid[ul] < 0)
mkface(mesh, grid, ll, lr, ur);
else
mkface(mesh, grid, ll, lr, ul);
}
}
//TriMesh::dprintf("%zu faces.\n ", mesh->faces.size());
mesh->remove_sliver_faces(mesh); //need this function
//TriMesh::dprintf(" ");
}
// Parse a PLY property line, and figure how many bytes it represents
// Increments "len" by the number of bytes, or by 1 if !binary
bool CMeshIO::ply_property(const char *buf, int &len, bool binary)
{
if (LINE_IS("property char") ||
LINE_IS("property uchar") ||
LINE_IS("property int8") ||
LINE_IS("property uint8")) {
len += 1;
} else if (LINE_IS("property short") ||
LINE_IS("property ushort") ||
LINE_IS("property int16") ||
LINE_IS("property uint16")) {
len += (binary ? 2 : 1);
} else if (LINE_IS("property int") ||
LINE_IS("property uint") ||
LINE_IS("property float") ||
LINE_IS("property int32") ||
LINE_IS("property uint32") ||
LINE_IS("property float32")) {
len += (binary ? 4 : 1);
} else if (LINE_IS("property double") ||
LINE_IS("property float64")) {
len += (binary ? 8 : 1);
} else {
fprintf(stderr, "Unsupported vertex property: %s\n", buf);
return false;
}
return true;
}
// Figure out whether this machine is little- or big-endian
bool CMeshIO::we_are_little_endian()
{
char buf[4];
*(int *)(&buf[0]) = 1;
return (buf[0] == 1);
}
// Figure out whether the need_swap setting makes sense, or whether this
// file incorrectly declares its endianness
void CMeshIO::check_need_swap(const point &p, bool &need_swap)
{
float p0 = p[0], p1 = p[1], p2 = p[2];
if (need_swap) {
swap_float(p0);
swap_float(p1);
swap_float(p2);
}
bool makes_sense = (p0 > -BIGNUM && p0 < BIGNUM &&
p1 > -BIGNUM && p1 < BIGNUM &&
p2 > -BIGNUM && p2 < BIGNUM);
if (makes_sense)
return;
swap_float(p0);
swap_float(p1);
swap_float(p2);
bool makes_sense_swapped = (p0 > -BIGNUM && p0 < BIGNUM &&
p1 > -BIGNUM && p1 < BIGNUM &&
p2 > -BIGNUM && p2 < BIGNUM);
if (makes_sense_swapped) {
fprintf(stderr, "Compensating for bogus endianness.\n");
need_swap = !need_swap;
}
}
// Check whether the indices in the file mistakenly go
// from 1..N instead of 0..N-1
void CMeshIO::check_ind_range(TriMesh *mesh)
{
if (mesh->faces.empty())
return;
int min_ind = mesh->faces[0][0];
int max_ind = mesh->faces[0][0];
for (int i = 0; i < mesh->faces.size(); i++) {
for (int j = 0; j < 3; j++) {
min_ind = __min(min_ind, mesh->faces[i][j]);
max_ind = __max(max_ind, mesh->faces[i][j]);
}
}
int nv = mesh->vertices.size();
// All good
if (min_ind == 0 && max_ind == nv-1)
return;
// Simple fix: offset everything
if (max_ind - min_ind == nv-1) {
TriMesh::dprintf("Found indices ranging from %d through %d\n",
min_ind, max_ind);
TriMesh::dprintf("Remapping to %d through %d\n", 0, nv-1);
for (int i = 0; i < mesh->faces.size(); i++)
for (int j = 0; j < 3; j++)
mesh->faces[i][j] -= min_ind;
return;
}
// Else can't do anything...
}
// Skip comments in an ASCII file (lines beginning with #)
void CMeshIO::skip_comments(FILE *f)
{
int c;
bool in_comment = false;
while (1) {
c = fgetc(f);
if (c == EOF)
return;
if (in_comment) {
if (c == '\n')
in_comment = false;
} else if (c == '#') {
in_comment = true;
} else if (!isspace(c)) {
break;
}
}
ungetc(c, f);
}
// Write mesh to a file
void CMeshIO::write(const char *filename, TriMesh * trimesh)
{
if (!filename || *filename == '\0')
return;
if (trimesh->vertices.empty()) {
fprintf(stderr, "Empty mesh - nothing to write!\n");
return;
}
enum { PLY_ASCII, PLY_BINARY_BE, PLY_BINARY_LE,
RAY, OBJ, OFF, SM, CC } filetype;
// Set default file type to be native-endian binary ply
filetype = we_are_little_endian() ? PLY_BINARY_LE : PLY_BINARY_BE;
bool write_norm = false;
// Infer file type from file extension
const char *c = strrchr(filename, '.');
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?