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 + -
显示快捷键?