C++ -- STL文件解析
1、 STL文件格式
STL文件是一种用许多空间小三角形面片逼近三维实体表面的3D模型。STL模型给出了组成三角形法向量的3个分量(用于确定三角面片的正反方向)及三角形的3个顶点坐标。一个完整的STL文件记录了组成实体模型的所有三角形面片的法向量数据和顶点坐标数据信息。STL文件格式包括二进制文件(BINARY)和文本文件(ASCII)两种。
1.1、STL的二进制格式
二进制STL文件用固定的字节数给出三角面片的几何信息。文件起始的80个字节是文件头,用于存储文件名;紧接着用4个字节的整数来描述模型的三角面片个数,后面逐个给出每个三角面片的几何信息。每个三角面片占用固定的50个字节,依次是3个4字节浮点数(面片的法向量),3个4字节浮点数(第1个顶点的坐标),3个4字节浮点数(第2个顶点的坐标),3个4字节浮点数(第3个顶点的坐标),最后2个字节用来描述三角面片的属性信息。一个完整二进制STL文件的大小为三角形面片数乘以50再加上84个字节,总共134个字节。
1.2、STL的ASCII文件格式
ASCII码格式的STL文件逐行给出三角面片的几何信息,每一行以1个或2个关键字开头。在STL文件中的三角面片的信息单元facet 是一个带矢量方向的三角面片,STL三维模型就是由一系列这样的三角面片构成。整个STL文件的首行给出了文件路径或文件名。在一个STL文件中,每一个 facet 由7行数据组成,facet normal 是三角面片指向实体外部的法矢量坐标,outer loop说明随后的3行数据分别是三角面片的3个顶点坐标,3顶点沿指向实体外部的法矢量方向逆时针排列。
ASCII格式的STL文件结构如下:
通过对STL两种文件格式的分析可知,二进制格式文件较小(通常是ASCII码格式的1/5),节省文件存储空间,而ASCII码格式的文件可读性更强,更容易进行进一步的数据处理。
2、代码处理
读取和保存需要的数据结构定义如下,代码文件Mesh.h
:
#pragma once
#include<iostream>
#include<math.h>
#include<vector>
#include<string>
#include<fstream>
#include<sstream>
#include<opencv2/opencv.hpp>using namespace std;// 数据结构----顶点、法向、三角形
typedef cv::Point3f Vertex;
typedef cv::Point3f Normal;
struct Tri
{int v1;int v2;int v3;
};struct VtxIdxSortItem {int i;Vertex value;
};class Mesh
{public:std::vector<Vertex> vtx; /// 顶点std::vector<Tri> tris; /// 三角面片std::vector<Normal> vtxNrm; /// 顶点法向std::vector<Normal> faceNrm; /// 面法向cv::Point3f center; /// 模型中心float radius; /// 模型半径std::vector<Vertex> mkh_joints;std::vector<Vertex> scan_joints;std::string modelname;std::string scan_joint_path;public:Mesh();~Mesh();// 解析STL模型bool readSTL_ASCII(std::string cfilename);bool saveSTL_ASCII(const char *filename, std::vector<Vertex>&vtx, std::vector<Tri> &tris, std::vector<Normal> &nor);void UnifyDuplicatedVertices(vector<Vertex> &vtx, vector<Tri> &tris);bool readSTL_Binary(std::string cfilename);bool Mesh::saveSTL_Binary(const char *filename, std::vector<Vertex>&vtx, std::vector<Tri> &tris, std::vector<Normal> &nor);
};
下面代码是读取和保存Binary
格式的代码,代码文件Mesh.cpp
:
bool Mesh::readSTL_Binary(std::string fileName)
{ifstream ifs(fileName.c_str(), ios::binary);if (!ifs){ifs.close();cout << "read stl error" << endl;return false;}vtx.clear();tris.clear();int intSize = sizeof(int);int floatSize = sizeof(float);ifs.ignore(80);// 面的个数int num_tris;ifs.read((char*)(&num_tris), intSize);cout << "面片数量:" << num_tris << endl;float tn0, tn1, tn2;float v0, v1, v2;float cx = 0.0, cy = 0.0, cz = 0.0;for (int i = 0; i < num_tris; i++){ifs.read((char*)(&tn0), floatSize);ifs.read((char*)(&tn1), floatSize);ifs.read((char*)(&tn2), floatSize);//如果模型进行坐标变换,需要重新计算法向量// faceNrm.push_back(Normal(tn0, tn1, -tn2)); // 01-STL modelifs.read((char*)(&v0), floatSize);ifs.read((char*)(&v1), floatSize);ifs.read((char*)(&v2), floatSize);vtx.push_back(Vertex(v0, v1, v2));cx += v0; cy += v1; cz += v2;ifs.read((char*)(&v0), floatSize);ifs.read((char*)(&v1), floatSize);ifs.read((char*)(&v2), floatSize);vtx.push_back(Vertex(v0, v1, v2));cx += v0; cy += v1; cz += v2;ifs.read((char*)(&v0), floatSize);ifs.read((char*)(&v1), floatSize);ifs.read((char*)(&v2), floatSize);vtx.push_back(Vertex(v0, v1, v2));cx += v0; cy += v1; cz += v2;// 建立面片索引,确定顶点顺序Tri tri;tri.v1 = i * 3 + 0;tri.v2 = i * 3 + 1;tri.v3 = i * 3 + 2;tris.push_back(tri);ifs.ignore(2);}ifs.close();// 重新计算面片法向量if (0 == vtxNrm.size()){for (int i = 0; i < tris.size(); i++){Vertex v12, v23;v12 = vtx[tris[i].v2] - vtx[tris[i].v1];v23 = vtx[tris[i].v3] - vtx[tris[i].v2];Normal faceN;faceN = v12.cross(v23);faceN /= sqrt(faceN.dot(faceN));faceNrm.push_back(faceN);}}else{for (int i = 0; i < tris.size(); i++){Normal faceN;faceN = vtxNrm[tris[i].v1] + vtxNrm[tris[i].v2] + vtxNrm[tris[i].v3];faceN /= sqrt(faceN.dot(faceN));faceNrm.push_back(faceN);}}// 计算中心位置center.x = cx / (num_tris * 3);center.y = cy / (num_tris * 3);center.z = cz / (num_tris * 3);//计算半径radius = 0;for (int i = 0; i < vtx.size(); i++){vtx[i] = vtx[i] - center;float lens;lens = sqrt((vtx[i]).dot(vtx[i]));if (lens > radius){radius = lens;}}center.x = 0;center.y = 0;center.z = 0;return true;
}
bool Mesh::saveSTL_Binary(const char *filename, std::vector<Vertex>&vtx, std::vector<Tri> &tris, std::vector<Normal> &nor)
{ofstream fs(filename, ios::binary);if (!fs) { fs.close(); return false; }int intSize = sizeof(int);int floatSize = sizeof(float);// 文件头char* fileHead = " ";fs.write(fileHead, sizeof(char) * 3);// 附加信息char fileInfo[77];for (int i = 0; i<77; i++) fileInfo[i] = ' ';fs.write(fileInfo, sizeof(char) * 77);// 面的个数int num_tris = int(tris.size());fs.write((char*)(&num_tris), intSize);// 点列表,面列表char a[2];streamsize a_size = sizeof(char) * 2;Normal tn;for (int i = 0; i<num_tris; i++){int PIndex0 = tris[i].v1;int PIndex1 = tris[i].v2;int PIndex2 = tris[i].v3;Vertex P0 = vtx[PIndex0];Vertex P1 = vtx[PIndex1];Vertex P2 = vtx[PIndex2];Normal N0 = nor[i];//Normal N1 = faceNrm[i].y;//Normal N2 = faceNrm[i].z;//tn = N0 + N1 + N2;fs.write((char*)(&(N0.x)), floatSize);fs.write((char*)(&(N0.y)), floatSize);fs.write((char*)(&(N0.z)), floatSize);// 保存顶点fs.write((char*)(&(P0.x)), floatSize);fs.write((char*)(&(P0.y)), floatSize);fs.write((char*)(&(P0.z)), floatSize);fs.write((char*)(&(P1.x)), floatSize);fs.write((char*)(&(P1.y)), floatSize);fs.write((char*)(&(P1.z)), floatSize);fs.write((char*)(&(P2.x)), floatSize);fs.write((char*)(&(P2.y)), floatSize);fs.write((char*)(&(P2.z)), floatSize);fs.write(a, a_size);}fs.close();return true;
}
下面代码是读取和保存ASCII
格式的代码,代码文件Mesh.cpp
:
bool Mesh::readSTL_ASCII(std::string fileName)
{ifstream ifs(fileName.c_str(), ios::binary);if (!ifs){ifs.close();cout << "read stl error" << endl;return false;}vtx.clear();tris.clear();int intSize = sizeof(int);int floatSize = sizeof(float);float tn0, tn1, tn2;float v0, v1, v2;float cx = 0.0, cy = 0.0, cz = 0.0;string name1, name2;ifs >> name1 >> name2;//cout << "name: " << name1 << " " << name2 << endl;int j = 3, t = 0;while (!ifs.eof()) // ifs.good(){string temp2;ifs >> temp2;//cout << "temp2: " << temp2 << endl;if (temp2 == "facet"){string temp_normal;ifs >> temp_normal;ifs >> tn0 >> tn1 >> tn2;//cout << "normal: " << tn0 << " " << tn1 << " " << tn2 << endl;Normal faceN;faceN.x = tn0;faceN.y = tn1;faceN.z = tn2;faceNrm.push_back(faceN);ifs.ignore(11);string temp;ifs >> temp;//cout << "SSS:" << temp << endl;while (temp == "vertex"){//cout << "=========================" << endl;ifs >> v0 >> v1 >> v2;//cout << "vertex: " << v0 << " " << v1 << " " << v2 << endl;vtx.push_back(Vertex(v0, v1, v2));cx += v0;cy += v1;cz += v2;ifs >> temp;//cout << "temp: " << temp << endl;}//cout << "end1: " << temp << endl;ifs >> temp;//cout << "end2: " << temp << endl;{Tri tri;tri.v1 = t * 3 + 0;tri.v2 = t * 3 + 1;tri.v3 = t * 3 + 2;tris.push_back(tri);t = t + 1;}}cout << "end string " << temp2 << endl;}ifs.close();// 计算模型的中心位置center.x = cx / (tris.size() * 3);center.y = cy / (tris.size() * 3);center.z = cz / (tris.size() * 3);//计算模型的半径radius = 0;for (int i = 0; i < vtx.size(); i++){vtx[i] = vtx[i] - center;float lens;lens = sqrt((vtx[i]).dot(vtx[i]));if (lens > radius){radius = lens;}}center.x = 0;center.y = 0;center.z = 0;return true;
}
bool Mesh::saveSTL_ASCII(const char *filename, std::vector<Vertex>&vtx, std::vector<Tri> &tris, std::vector<Normal> &nor)
{// vtx:模型顶点// tris:三角面片// nor:法线ofstream fs(filename);if (!fs) { fs.close(); return false; }int i=0;int intSize = sizeof(int);int floatSize = sizeof(float);int num_tris = tris.size();cout << nor.size() << endl;cout << num_tris << endl;fs << "solid WRAP" << endl;for (i = 0; i<num_tris; i++){fs << "facet normal " << nor[i].x << " " << nor[i].y << " " << nor[i].z << endl;fs << "outer loop" << endl;fs << "vertex " << vtx[tris[i].v1].x << " " << vtx[tris[i].v1].y << " " << vtx[tris[i].v1].z << endl;fs << "vertex " << vtx[tris[i].v2].x << " " << vtx[tris[i].v2].y << " " << vtx[tris[i].v2].z << endl;fs << "vertex " << vtx[tris[i].v3].x << " " << vtx[tris[i].v3].y << " " << vtx[tris[i].v3].z << endl;fs << "end loop" << endl;fs << "end facet" << endl;}fs << "endsolid WRAP";fs.close();return true;
}
C++ -- STL文件解析相关推荐
- 如何读取STL文件?
STL文件有两种文件格式,分别是二进制的stl和Ascii格式的stl.下面将针对这文件的两个格式,用C语言分别写出一段简单的示例代码. 一.读取二进制的STL文件 (1)二进制stl文件的结构 文件 ...
- 通过Node.js解析stl文件
通过Node.js解析stl文件 在此之前请确认已安装Node.js,已配置环境变量.如未安装与配置,请查看Node.js安装及环境配置(Windows) 一.下载Node项目文件 Node项目文件下 ...
- Qt/C++ + opengl 解析stl文件(二进制和Ascii两种格式)
前言: 3D 的stl 模型文件分为两种 二进制和Ascii 明码的Ascii 内容清晰可以打开看,但是文件比较大 二进制的文件 看不到内容 但是占用空间小 我是用 qt + opengl 加载 st ...
- MATLAB---读取STL文件并解析
function VerMat=ReadSTLFile()%读取STL文件代码 filename='model1or.stl'; %指定要打开的文件名 fid=fopen(filename,'r'); ...
- Json文件解析(下
Json文件解析(下) 代码地址:https://github.com/nlohmann/json 从STL容器转换 任何序列容器(std::array,std::vector,std::deque, ...
- Json文件解析(上)
Json文件解析(上) 代码地址:https://github.com/nlohmann/json 自述文件 alt=GitHub赞助商 data-canonical-src="https: ...
- stl文件转stp (二次开发)
前几天有个美女问我stl文件怎么处理,很尴尬我也处理不了,为了避免再次发生这种情况,所以我做了这个小软件.其下载链接将在文末给出.源码我已经上传GitHub,需要的可自取:https://github ...
- stl文件html预览,基于SpringMVC对stl文件的3D可视化
收集实现代码 首先,需要一个显示stl的前端实现.因为是前端代码,并且是显示比较主流的3D文件,所以肯定是已经有相应的实现,所以我们要做的就是找到相应的实现,然后在上面做相应的修改即可.笔者参考的是以 ...
- MATLAB快速读取STL文件
MATLAB快速读取STL文件 一.STL文件格式 binary格式 ascii格式stl 二.开源代码 安装方法 使用方法 三.快速读取 binary格式stl ascii格式stl 四.效果对比 ...
- python读取.stl文件(以及转换为obj方法)
目录 .1 文本方式读取 1.2 stl解析 1.3 stl创建 .2 把点转换为.stl .3 stl组装 .4 stl转obj(带f法向量的obj) .5 ply生成 .1 文本方式读取 代码如下 ...
最新文章
- oracl 、mysql在线查看文档
- DRV8711的使用,堵转stall检测的使用及衰减模式
- 北京工商大学计算机考研818真题,2017年北京工商大学计算机与信息工程学院818数据结构考研仿真模拟题...
- App项目内存优化计划
- PX4原生固件SPI驱动动编写与IMU传感器替换
- 图Graph--最小生成树
- 支持批任务的Coscheduling/Gang scheduling
- 强化学习《基于策略价值 - Pathwise Derivative Policy Grident》
- Cobbler安装指南
- java-多线程安全问题
- 9. HTML DOM getElementsByName() 方法
- 卡诺图化简 之 和之积形式 与 积之和形式
- linux 下查看硬盘容量
- Word如何让样式库中的样式不添加到自动生成的目录中
- 如何让自己的博客主动被搜索引擎收录
- ln火线零线_ln线哪个是火线零线
- 从航天到原始递归函数的四个定理及其证明——哥德尔读后之十二
- Trips and Users
- [乱七八糟][转]这不是你想象中的软件产业
- 谈谈C++的三大特性之一:封装性 (转载)
热门文章
- java word 水印_Java 添加水印(文本水印、图片水印)到Word文档
- 华为荣耀畅享7的计算机在哪,华为畅享7有什么新功能_华为畅享7有哪些新功能-太平洋IT百科...
- 华为畅享二十Android是,华为畅享20 pro开箱小晒
- C语言:输入一堆字符统计不同字符的数量
- 未来富豪,将出自这12大颠覆性领域
- Elasticsearch:深入理解 Dissect ingest processor
- Linux 中设置计划任务(定时任务)
- ubuntu 网卡流量_Ubuntu下使用nload查看网卡实时流量
- 腾讯云服务器漏洞怎么修复,腾讯云安全中心监测到微软披露的99个漏洞,win系统云服务器用户尽快修复...
- 基于java+ssm+mysql的医院管理系统