Open Cascade Data Exchange STL
Open Cascade Data Exchange STL
摘要Abstract:介绍了三维数据交换格式STL的组成,以及Open Cascade中对STL的读写。并将Open Cascade读进来的STL的三角面片在OpenSceneGraph中显示。
关键字Key Words:STL, Open Cascade, OpenSceneGraph, Data Exchange
STL(the Stereo Lithograpy)是快速原型系统所应用的标准文件类型。它的目的是将几何数据发送到可以读取和解释这些数据的机器,这种机器可将模型转换成塑料的物理模型。STL是用三角网格来表示三维模型的。STL文件格式简单,只能描述三维物体的几何信息,不支持颜色、材质等信息,是三维打印机支持的最常见的文件格式。由于STL文件的网格表示方法只能表示封闭的形状,所以要转换的形状必须是实体,或封闭的面和体。STL文件有两种:一种是明码(ASCII)格式,一种是二进制(Binary)格式。
一、STL的明码(ASCII)格式
ASCII格式的STL文件逐行给出三角面片的几何信息,每行以1个或2个关键字开头。STL文件中的三角面片的信息单元facet是一个带法向方向的三角面片,STL三维模型就是由这一系列的三角面片构成。整个STL文件的首行给出了文件路径及文件名。在一个STL文件中,每个facet由7行数据组成:facet normal是三角面片指向实体外部的单位法矢量;outer loop说明随后的3行数据分别是三角面片的3个顶点坐标,3顶点沿指向实体外部的法矢量方向逆时针排列。
ASCII格式的STL文件结构如下:
说明如下:
下面给出由Open Cascade中导出的一个长方体的STL文件:
长方体的尺寸为长200,宽150,高100,原点在一个角点上。
Figure 1.1 Box in Open Cascade
solid
facet normal -1.000000e+000 -0.000000e+000 -0.000000e+000
outer loop
vertex 0.000000e+000 1.500000e+002 1.000000e+002
vertex 0.000000e+000 1.500000e+002 0.000000e+000
vertex 0.000000e+000 0.000000e+000 1.000000e+002
endloop
endfacet
facet normal -1.000000e+000 0.000000e+000 0.000000e+000
outer loop
vertex 0.000000e+000 1.500000e+002 0.000000e+000
vertex 0.000000e+000 0.000000e+000 0.000000e+000
vertex 0.000000e+000 0.000000e+000 1.000000e+002
endloop
endfacet
facet normal 1.000000e+000 -0.000000e+000 0.000000e+000
outer loop
vertex 2.000000e+002 0.000000e+000 1.000000e+002
vertex 2.000000e+002 1.500000e+002 0.000000e+000
vertex 2.000000e+002 1.500000e+002 1.000000e+002
endloop
endfacet
facet normal 1.000000e+000 -0.000000e+000 0.000000e+000
outer loop
vertex 2.000000e+002 0.000000e+000 1.000000e+002
vertex 2.000000e+002 0.000000e+000 0.000000e+000
vertex 2.000000e+002 1.500000e+002 0.000000e+000
endloop
endfacet
facet normal 0.000000e+000 -1.000000e+000 0.000000e+000
outer loop
vertex 0.000000e+000 0.000000e+000 0.000000e+000
vertex 2.000000e+002 0.000000e+000 0.000000e+000
vertex 2.000000e+002 0.000000e+000 1.000000e+002
endloop
endfacet
facet normal 0.000000e+000 -1.000000e+000 0.000000e+000
outer loop
vertex 0.000000e+000 0.000000e+000 1.000000e+002
vertex 0.000000e+000 0.000000e+000 0.000000e+000
vertex 2.000000e+002 0.000000e+000 1.000000e+002
endloop
endfacet
facet normal 0.000000e+000 1.000000e+000 0.000000e+000
outer loop
vertex 2.000000e+002 1.500000e+002 1.000000e+002
vertex 2.000000e+002 1.500000e+002 0.000000e+000
vertex 0.000000e+000 1.500000e+002 0.000000e+000
endloop
endfacet
facet normal 0.000000e+000 1.000000e+000 -0.000000e+000
outer loop
vertex 2.000000e+002 1.500000e+002 1.000000e+002
vertex 0.000000e+000 1.500000e+002 0.000000e+000
vertex 0.000000e+000 1.500000e+002 1.000000e+002
endloop
endfacet
facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
outer loop
vertex 0.000000e+000 0.000000e+000 0.000000e+000
vertex 0.000000e+000 1.500000e+002 0.000000e+000
vertex 2.000000e+002 1.500000e+002 0.000000e+000
endloop
endfacet
facet normal 0.000000e+000 0.000000e+000 -1.000000e+000
outer loop
vertex 2.000000e+002 0.000000e+000 0.000000e+000
vertex 0.000000e+000 0.000000e+000 0.000000e+000
vertex 2.000000e+002 1.500000e+002 0.000000e+000
endloop
endfacet
facet normal 0.000000e+000 0.000000e+000 1.000000e+000
outer loop
vertex 2.000000e+002 1.500000e+002 1.000000e+002
vertex 0.000000e+000 1.500000e+002 1.000000e+002
vertex 0.000000e+000 0.000000e+000 1.000000e+002
endloop
endfacet
facet normal -0.000000e+000 0.000000e+000 1.000000e+000
outer loop
vertex 2.000000e+002 1.500000e+002 1.000000e+002
vertex 0.000000e+000 0.000000e+000 1.000000e+002
vertex 2.000000e+002 0.000000e+000 1.000000e+002
endloop
endfacet
endsolid
由上面的STL明码文件可知,上述数据将一个长方体的6个面用12个三角形来表示。在OpenSceneGraph中显示效果如下图所示,分别为此长方体的实体渲染模式和线框渲染模式:
Figure 1.2 Shaded and Wireframe box in OpenSceneGraph
二、STL的二进制(Binary)格式
二进制的STL文件用固定的字节数来给出三角面片的几何信息。文件起始80个字节是文件头,用于存贮零件名;紧接着4个字节的整数来描述模型的三角面片个数;后面逐个给出每个三角面片的几何信息。每个三角面片用固定的50个字节,依次是表示三角面片的法矢量的3个4字节浮点数;表示三角面片三个顶点的3x3个4字节浮点数;最后2个字节用来描述三角面片的属性信息。
三、OCC中STL文件的读写Read/Write STL in Open Cascade
在Open Cascade中STL文件的读写分别使用类:StlAPI_Reader/StlAPI_Writer来实现。查看源程序可知,写STL文件的步骤如下:
l 遍历一个TopoDS_Shape所有的面Face;
l 使用工具BRep_Tool::Triangulation将每个面Face三角面片化;
l 计算每个三角面片的法矢量;
l 将结果写入文件。
类RWStl对STL的读定也是有两种格式,即ASCII格式和Binary格式:
n RWStl::WriteBinary
n RWStl::WriteAscii
n RWStl::ReadBinary
n RWStl::ReadAscii
程序的具体实现可以查看Open Cascade源代码,将读写部分主要代码RWStl.cxx列出如下:
// Created on: 1994-10-13
// Created by: Marc LEGAY
// Copyright (c) 1994-1999 Matra Datavision
// Copyright (c) 1999-2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License. #include <RWStl.ixx>
#include <OSD_Protection.hxx>
#include <OSD_File.hxx>
#include <Message_ProgressSentry.hxx>
#include <TCollection_AsciiString.hxx>
#include <Standard_NoMoreObject.hxx>
#include <Standard_TypeMismatch.hxx>
#include <Precision.hxx>
#include <StlMesh_MeshExplorer.hxx>
#include <OSD.hxx>
#include <OSD_Host.hxx>
#include <gp_XYZ.hxx>
#include <gp.hxx>
#include <stdio.h>
#include <gp_Vec.hxx> // constants
static const int HEADER_SIZE = ;
static const int SIZEOF_STL_FACET = ;
static const int STL_MIN_FILE_SIZE = ;
static const int ASCII_LINES_PER_FACET = ;
static const int IND_THRESHOLD = ; // increment the indicator every 1k triangles //=======================================================================
//function : WriteInteger
//purpose : writing a Little Endian 32 bits integer
//======================================================================= inline static void WriteInteger(OSD_File& ofile,const Standard_Integer value)
{
union {
Standard_Integer i;// don't be afraid, this is just an unsigned int
char c[];
} bidargum; bidargum.i = value; Standard_Integer entier; entier = bidargum.c[] & 0xFF;
entier |= (bidargum.c[] & 0xFF) << 0x08;
entier |= (bidargum.c[] & 0xFF) << 0x10;
entier |= (bidargum.c[] & 0xFF) << 0x18; ofile.Write((char *)&entier,sizeof(bidargum.c));
} //=======================================================================
//function : WriteDouble2Float
//purpose : writing a Little Endian 32 bits float
//======================================================================= inline static void WriteDouble2Float(OSD_File& ofile,Standard_Real value)
{
union {
Standard_ShortReal f;
char c[];
} bidargum; bidargum.f = (Standard_ShortReal)value; Standard_Integer entier; entier = bidargum.c[] & 0xFF;
entier |= (bidargum.c[] & 0xFF) << 0x08;
entier |= (bidargum.c[] & 0xFF) << 0x10;
entier |= (bidargum.c[] & 0xFF) << 0x18; ofile.Write((char *)&entier,sizeof(bidargum.c));
} //=======================================================================
//function : readFloat2Double
//purpose : reading a Little Endian 32 bits float
//======================================================================= inline static Standard_Real ReadFloat2Double(OSD_File &aFile)
{
union {
Standard_Boolean i; // don't be afraid, this is just an unsigned int
Standard_ShortReal f;
}bidargum; char c[];
Standard_Address adr;
adr = (Standard_Address)c;
Standard_Integer lread;
aFile.Read(adr,,lread);
bidargum.i = c[] & 0xFF;
bidargum.i |= (c[] & 0xFF) << 0x08;
bidargum.i |= (c[] & 0xFF) << 0x10;
bidargum.i |= (c[] & 0xFF) << 0x18; return (Standard_Real)(bidargum.f);
} //=======================================================================
//function : WriteBinary
//purpose : write a binary STL file in Little Endian format
//======================================================================= Standard_Boolean RWStl::WriteBinary (const Handle(StlMesh_Mesh)& theMesh,
const OSD_Path& thePath,
const Handle(Message_ProgressIndicator)& theProgInd)
{
OSD_File aFile (thePath);
aFile.Build (OSD_WriteOnly, OSD_Protection()); Standard_Real x1, y1, z1;
Standard_Real x2, y2, z2;
Standard_Real x3, y3, z3; // writing 80 bytes of the trash?
char sval[];
aFile.Write ((Standard_Address)sval,);
WriteInteger (aFile, theMesh->NbTriangles()); int dum=;
StlMesh_MeshExplorer aMexp (theMesh); // create progress sentry for domains
Standard_Integer aNbDomains = theMesh->NbDomains();
Message_ProgressSentry aDPS (theProgInd, "Mesh domains", , aNbDomains, );
for (Standard_Integer nbd = ; nbd <= aNbDomains && aDPS.More(); nbd++, aDPS.Next())
{
// create progress sentry for triangles in domain
Message_ProgressSentry aTPS (theProgInd, "Triangles", ,
theMesh->NbTriangles (nbd), IND_THRESHOLD);
Standard_Integer aTriangleInd = ;
for (aMexp.InitTriangle (nbd); aMexp.MoreTriangle(); aMexp.NextTriangle())
{
aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3);
//pgo aMexp.TriangleOrientation (x,y,z);
gp_XYZ Vect12 ((x2-x1), (y2-y1), (z2-z1));
gp_XYZ Vect13 ((x3-x1), (y3-y1), (z3-z1));
gp_XYZ Vnorm = Vect12 ^ Vect13;
Standard_Real Vmodul = Vnorm.Modulus ();
if (Vmodul > gp::Resolution())
{
Vnorm.Divide(Vmodul);
}
else
{
// si Vnorm est quasi-nul, on le charge a 0 explicitement
Vnorm.SetCoord (., ., .);
} WriteDouble2Float (aFile, Vnorm.X());
WriteDouble2Float (aFile, Vnorm.Y());
WriteDouble2Float (aFile, Vnorm.Z()); WriteDouble2Float (aFile, x1);
WriteDouble2Float (aFile, y1);
WriteDouble2Float (aFile, z1); WriteDouble2Float (aFile, x2);
WriteDouble2Float (aFile, y2);
WriteDouble2Float (aFile, z2); WriteDouble2Float (aFile, x3);
WriteDouble2Float (aFile, y3);
WriteDouble2Float (aFile, z3); aFile.Write (&dum, ); // update progress only per 1k triangles
if (++aTriangleInd % IND_THRESHOLD == )
{
if (!aTPS.More())
break;
aTPS.Next();
}
}
}
aFile.Close();
Standard_Boolean isInterrupted = !aDPS.More();
return !isInterrupted;
}
//=======================================================================
//function : WriteAscii
//purpose : write an ASCII STL file
//======================================================================= Standard_Boolean RWStl::WriteAscii (const Handle(StlMesh_Mesh)& theMesh,
const OSD_Path& thePath,
const Handle(Message_ProgressIndicator)& theProgInd)
{
OSD_File theFile (thePath);
theFile.Build(OSD_WriteOnly,OSD_Protection());
TCollection_AsciiString buf ("solid\n");
theFile.Write (buf,buf.Length());buf.Clear(); Standard_Real x1, y1, z1;
Standard_Real x2, y2, z2;
Standard_Real x3, y3, z3;
char sval[]; // create progress sentry for domains
Standard_Integer aNbDomains = theMesh->NbDomains();
Message_ProgressSentry aDPS (theProgInd, "Mesh domains", , aNbDomains, );
StlMesh_MeshExplorer aMexp (theMesh);
for (Standard_Integer nbd = ; nbd <= aNbDomains && aDPS.More(); nbd++, aDPS.Next())
{
// create progress sentry for triangles in domain
Message_ProgressSentry aTPS (theProgInd, "Triangles", ,
theMesh->NbTriangles (nbd), IND_THRESHOLD);
Standard_Integer aTriangleInd = ;
for (aMexp.InitTriangle (nbd); aMexp.MoreTriangle(); aMexp.NextTriangle())
{
aMexp.TriangleVertices (x1,y1,z1,x2,y2,z2,x3,y3,z3); // Standard_Real x, y, z;
// aMexp.TriangleOrientation (x,y,z); gp_XYZ Vect12 ((x2-x1), (y2-y1), (z2-z1));
gp_XYZ Vect23 ((x3-x2), (y3-y2), (z3-z2));
gp_XYZ Vnorm = Vect12 ^ Vect23;
Standard_Real Vmodul = Vnorm.Modulus ();
if (Vmodul > gp::Resolution())
{
Vnorm.Divide (Vmodul);
}
else
{
// si Vnorm est quasi-nul, on le charge a 0 explicitement
Vnorm.SetCoord (., ., .);
}
Sprintf (sval,
" facet normal % 12e % 12e % 12e\n"
" outer loop\n"
" vertex % 12e % 12e % 12e\n"
" vertex % 12e % 12e % 12e\n"
" vertex % 12e % 12e % 12e\n"
" endloop\n"
" endfacet\n",
Vnorm.X(), Vnorm.Y(), Vnorm.Z(),
x1, y1, z1,
x2, y2, z2,
x3, y3, z3);
buf += sval;
theFile.Write (buf, buf.Length()); buf.Clear(); // update progress only per 1k triangles
if (++aTriangleInd % IND_THRESHOLD == )
{
if (!aTPS.More())
break;
aTPS.Next();
}
}
} buf += "endsolid\n";
theFile.Write (buf, buf.Length()); buf.Clear();
theFile.Close();
Standard_Boolean isInterrupted = !aDPS.More();
return !isInterrupted;
}
//=======================================================================
//function : ReadFile
//Design :
//Warning :
//======================================================================= Handle_StlMesh_Mesh RWStl::ReadFile (const OSD_Path& thePath,
const Handle(Message_ProgressIndicator)& theProgInd)
{
OSD_File file (thePath);
file.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD));
Standard_Boolean IsAscii;
unsigned char str[];
Standard_Integer lread,i;
Standard_Address ach;
ach = (Standard_Address)str; // we skip the header which is in Ascii for both modes
file.Read(ach,HEADER_SIZE,lread); // we read 128 characters to detect if we have a non-ascii char
file.Read(ach,sizeof(str),lread); IsAscii = Standard_True;
for (i = ; i< lread && IsAscii; ++i) {
if (str[i] > '~') {
IsAscii = Standard_False;
}
}
#ifdef DEB
cout << (IsAscii ? "ascii\n" : "binary\n");
#endif
file.Close(); return IsAscii ? RWStl::ReadAscii (thePath, theProgInd)
: RWStl::ReadBinary (thePath, theProgInd);
} //=======================================================================
//function : ReadBinary
//Design :
//Warning :
//======================================================================= Handle_StlMesh_Mesh RWStl::ReadBinary (const OSD_Path& thePath,
const Handle(Message_ProgressIndicator)& /*theProgInd*/)
{
Standard_Integer NBFACET;
Standard_Integer ifacet;
Standard_Real fx,fy,fz,fx1,fy1,fz1,fx2,fy2,fz2,fx3,fy3,fz3;
Standard_Integer i1,i2,i3,lread;
char buftest[];
Standard_Address adr;
adr = (Standard_Address)buftest; // Open the file
OSD_File theFile (thePath);
theFile.Open(OSD_ReadOnly,OSD_Protection(OSD_RWD,OSD_RWD,OSD_RWD,OSD_RWD)); // the size of the file (minus the header size)
// must be a multiple of SIZEOF_STL_FACET // compute file size
Standard_Integer filesize = theFile.Size(); if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET !=
|| (filesize < STL_MIN_FILE_SIZE)) {
Standard_NoMoreObject::Raise("RWStl::ReadBinary (wrong file size)");
} // don't trust the number of triangles which is coded in the file
// sometimes it is wrong, and with this technique we don't need to swap endians for integer
NBFACET = ((filesize - HEADER_SIZE) / SIZEOF_STL_FACET); // skip the header
theFile.Seek(HEADER_SIZE,OSD_FromBeginning); // create the StlMesh_Mesh object
Handle(StlMesh_Mesh) ReadMesh = new StlMesh_Mesh ();
ReadMesh->AddDomain (); for (ifacet=; ifacet<=NBFACET; ++ifacet) {
// read normal coordinates
fx = ReadFloat2Double(theFile);
fy = ReadFloat2Double(theFile);
fz = ReadFloat2Double(theFile); // read vertex 1
fx1 = ReadFloat2Double(theFile);
fy1 = ReadFloat2Double(theFile);
fz1 = ReadFloat2Double(theFile); // read vertex 2
fx2 = ReadFloat2Double(theFile);
fy2 = ReadFloat2Double(theFile);
fz2 = ReadFloat2Double(theFile); // read vertex 3
fx3 = ReadFloat2Double(theFile);
fy3 = ReadFloat2Double(theFile);
fz3 = ReadFloat2Double(theFile); i1 = ReadMesh->AddOnlyNewVertex (fx1,fy1,fz1);
i2 = ReadMesh->AddOnlyNewVertex (fx2,fy2,fz2);
i3 = ReadMesh->AddOnlyNewVertex (fx3,fy3,fz3);
ReadMesh->AddTriangle (i1,i2,i3,fx,fy,fz); // skip extra bytes
theFile.Read(adr,,lread);
} theFile.Close ();
return ReadMesh; }
//=======================================================================
//function : ReadAscii
//Design :
//Warning :
//======================================================================= Handle_StlMesh_Mesh RWStl::ReadAscii (const OSD_Path& thePath,
const Handle(Message_ProgressIndicator)& theProgInd)
{
TCollection_AsciiString filename;
long ipos;
Standard_Integer nbLines = ;
Standard_Integer nbTris = ;
Standard_Integer iTri;
Standard_Integer i1,i2,i3;
Handle(StlMesh_Mesh) ReadMesh; thePath.SystemName (filename); // Open the file
FILE* file = fopen(filename.ToCString(),"r"); fseek(file,0L,SEEK_END); long filesize = ftell(file); fclose(file);
file = fopen(filename.ToCString(),"r"); // count the number of lines
for (ipos = ; ipos < filesize; ++ipos) {
if (getc(file) == '\n')
nbLines++;
} // compute number of triangles
nbTris = (nbLines / ASCII_LINES_PER_FACET); // go back to the beginning of the file
// fclose(file);
// file = fopen(filename.ToCString(),"r");
rewind(file); // skip header
while (getc(file) != '\n');
#ifdef DEB
cout << "start mesh\n";
#endif
ReadMesh = new StlMesh_Mesh();
ReadMesh->AddDomain(); // main reading
Message_ProgressSentry aPS (theProgInd, "Triangles", , (nbTris - ) * 1.0 / IND_THRESHOLD, );
for (iTri = ; iTri < nbTris && aPS.More();)
{
char x[]="", y[]="", z[]=""; // reading the facet normal
if ( != fscanf(file,"%*s %*s %80s %80s %80s\n", x, y, z))
break; // error should be properly reported
gp_XYZ aN (Atof(x), Atof(y), Atof(z)); // skip the keywords "outer loop"
fscanf(file,"%*s %*s"); // reading vertex
if ( != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
break; // error should be properly reported
gp_XYZ aV1 (Atof(x), Atof(y), Atof(z));
if ( != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
break; // error should be properly reported
gp_XYZ aV2 (Atof(x), Atof(y), Atof(z));
if ( != fscanf(file,"%*s %80s %80s %80s\n", x, y, z))
break; // error should be properly reported
gp_XYZ aV3 (Atof(x), Atof(y), Atof(z)); // here the facet must be built and put in the mesh datastructure i1 = ReadMesh->AddOnlyNewVertex (aV1.X(), aV1.Y(), aV1.Z());
i2 = ReadMesh->AddOnlyNewVertex (aV2.X(), aV2.Y(), aV2.Z());
i3 = ReadMesh->AddOnlyNewVertex (aV3.X(), aV3.Y(), aV3.Z());
ReadMesh->AddTriangle (i1, i2, i3, aN.X(), aN.Y(), aN.Z()); // skip the keywords "endloop"
fscanf(file,"%*s"); // skip the keywords "endfacet"
fscanf(file,"%*s"); // update progress only per 1k triangles
if (++iTri % IND_THRESHOLD == )
aPS.Next();
}
#ifdef DEB
cout << "end mesh\n";
#endif
fclose(file);
return ReadMesh;
}
程序开始定义了一些常量:
// constants
static const int HEADER_SIZE = ;
static const int SIZEOF_STL_FACET = ;
static const int STL_MIN_FILE_SIZE = ;
static const int ASCII_LINES_PER_FACET = ;
分别对应二进制文件中相关信息,即文件头84个字节,每个三角面片50个字节,STL文件最小为284字节。ASCII的STL中每个三角面有7行。
在数据的读写过程中,对数据进行了小端转换。将double数据转换成小端表示的代码如下所示:
//=====================================================================
//function : WriteDouble2Float
//purpose : writing a Little Endian 32 bits float
//===================================================================== inline static void WriteDouble2Float(OSD_File& ofile,Standard_Real value)
{
union {
Standard_ShortReal f;
char c[];
} bidargum; bidargum.f = (Standard_ShortReal)value; Standard_Integer entier; entier = bidargum.c[] & 0xFF;
entier |= (bidargum.c[] & 0xFF) << 0x08;
entier |= (bidargum.c[] & 0xFF) << 0x10;
entier |= (bidargum.c[] & 0xFF) << 0x18; ofile.Write((char *)&entier,sizeof(bidargum.c));
}
使用联合体(union)来处理显得很优雅。关于大端、小端的相关信息请参考:
http://www.cppblog.com/tx7do/archive/2009/01/06/71276.html
四、在OpenSceneGraph中显示STL
结合OpenCascade中对STL文件读写的功能和OpenSceneGraph的显示功能,将STL读取所得数据进行显示。源程序如下所示:
// Open Cascade
#include <gp_Vec.hxx>
#include <OSD_Path.hxx>
#include <RWStl.hxx>
#include <StlMesh_Mesh.hxx>
#include <StlMesh_MeshExplorer.hxx> #pragma comment(lib, "TKernel.lib")
#pragma comment(lib, "TKMath.lib")
#pragma comment(lib, "TKSTL.lib") // OpenSceneGraph
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgGA/StateSetManipulator> #pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDbd.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib") osg::Node* readSTLFile(const std::string& fileName)
{
osg::Group* root = new osg::Group(); OSD_Path stlFile(fileName.c_str());
Handle_StlMesh_Mesh stlMesh = RWStl::ReadFile(stlFile);
Standard_Integer nDomains = stlMesh->NbDomains();
StlMesh_MeshExplorer meshExplorer(stlMesh); Standard_Real x[] = {};
Standard_Real y[] = {};
Standard_Real z[] = {};
Standard_Real n[] = {}; gp_XYZ p1;
gp_XYZ p2;
gp_XYZ p3;
gp_XYZ normal;
gp_Vec vecNormal; for (int i = ; i <= nDomains; i++)
{
for (meshExplorer.InitTriangle(i); meshExplorer.MoreTriangle(); meshExplorer.NextTriangle())
{
meshExplorer.TriangleVertices(x[], y[], z[], x[], y[], z[], x[], y[], z[]);
meshExplorer.TriangleOrientation(n[], n[], n[]); p1.SetCoord(x[], y[], z[]);
p2.SetCoord(x[], y[], z[]);
p3.SetCoord(x[], y[], z[]);
normal.SetCoord(n[], n[], n[]); //gp_Vec vec12((x[1] - x[0]), (y[1] - y[0]), (z[1] - z[0]));
//gp_Vec vec23((x[2] - x[1]), (y[2] - y[1]), (z[2] - z[1]));
//vecNormal = vec12.Crossed(vec23).Normalized(); osg::ref_ptr<osg::Geode> geode = new osg::Geode();
osg::ref_ptr<osg::Geometry> triGeom = new osg::Geometry();
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array(); vertices->push_back(osg::Vec3(x[], y[], z[]));
vertices->push_back(osg::Vec3(x[], y[], z[]));
vertices->push_back(osg::Vec3(x[], y[], z[])); normals->push_back(osg::Vec3(n[], n[], n[])); triGeom->setVertexArray(vertices.get());
triGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, , vertices->size())); triGeom->setNormalArray(normals);
triGeom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE); geode->addDrawable(triGeom); root->addChild(geode);
}
} return root;
} int main(int argc, char* argv[])
{
osgViewer::Viewer myViewer;
osg::ref_ptr<osg::Group> root = new osg::Group(); //root->addChild(readSTLFile("D:\\OpenCASCADE6.5.0\\data\\stl\\propeller.stl"));
root->addChild(readSTLFile("D:\\OpenCASCADE6.5.0\\data\\stl\\sh1.stl"));
//root->addChild(readSTLFile("D:\\OpenCASCADE6.5.0\\data\\stl\\motor.stl"));
//root->addChild(readSTLFile("D:\\box.stl")); myViewer.setSceneData(root); myViewer.addEventHandler(new osgGA::StateSetManipulator(myViewer.getCamera()->getOrCreateStateSet()));
myViewer.addEventHandler(new osgViewer::StatsHandler);
myViewer.addEventHandler(new osgViewer::WindowSizeHandler); return myViewer.run();
}
以下所示为OpenCascade提供的几个STL文件在OpenSceneGraph中显示的效果:
Figure 4.1 Shaded Piston
Figure 4.2 Wireframe Piston
Figure 4.3 Shaded Propeller
Figure 4.4 Wireframe Propeller
五、结论
通过使用OpenCascade的类RWStl来读取STL格式的文件,理解了STL文件格式;通过将读取的三角面面片数据在OpenSceneGraph中显示,对三维物体在计算机中的表示有了感性的认识。
六、参考资料
1. OpenCascade中类RWStl.cxx
2. OpenCascade中STL模型数据
3. 字节序、大端、小端:http://www.cppblog.com/tx7do/archive/2009/01/06/71276.html
PDF Version: Open Cascade DataExchange STL
Open Cascade Data Exchange STL的更多相关文章
- OpenCASCADE Extended Data Exchange - XDE
OpenCASCADE Extended Data Exchange - XDE eryar@163.com Abstract. OpenCASCADE Data Exchange allows de ...
- OpenCASCADE Data Exchange - 3D PDF
OpenCASCADE Data Exchange - 3D PDF eryar@163.com Abstract. Today most 3D engineering model data are ...
- AVEVA Model Data Exchange Exports Structure Models
AVEVA Model Data Exchange Exports Structure Modelseryar@163.com Use Model Data Exchange Addin to exp ...
- [UVA] 11995 - I Can Guess the Data Structure! [STL应用]
11995 - I Can Guess the Data Structure! Time limit: 1.000 seconds Problem I I Can Guess the Data Str ...
- UVA - 11995 - I Can Guess the Data Structure! STL 模拟
There is a bag-like data structure, supporting two operations: 1 x Throw an element x into the bag. ...
- Open Cascade DataExchange DXF
Open Cascade DataExchange DXF eryar@163.com 摘要Abstract:对DXF文本格式进行详细介绍,并介绍了如何使用开源库dxflib对DXF文件进行读写操作, ...
- Open CASCADE Technology(OCCT)概述
OCCT模块结构图 基础类: Foundation Classes module underlies all other OCCT classes; 模型数据: Modeling Data modul ...
- Open Cascade DataExchange IGES
Open Cascade DataExchange IGES eryar@163.com 摘要Abstract:本文结合OpenCascade和Initial Graphics Exchange Sp ...
- Indexing Sensor Data
In particular embodiments, a method includes, from an indexer in a sensor network, accessing a set o ...
随机推荐
- javascript变量问题
CMAScript变量包含两种不同数据类型的值: 基本类型值:简单的数据段:引用类型值:可能有多个值构成的对象. 5种基本类型:Undefined,Null,Bollean,Number,String ...
- 详细!交叉编译时 note: the mangling of 'va_list' has changed in GCC 4.4解决办法
为什么要在标题前面加了详细两个字,就是为了吸引看文章的你还有写文章的我这种小白,我是从坑里面爬出来了. 废话少说.... 问题就是这样子了,至于解决办法,在网上搜索了很久,大多数以一段英文作为解决办法 ...
- android控件
---恢复内容开始--- (1)文字大小的类型 px.dip.sp.pt.in.mm (2)TextView控件 超链接显示 属性autoLink="all" (3)EditTex ...
- iOS上简单推送通知(Push Notification)的实现
iOS上简单推送通知(Push Notification)的实现 根据这篇很好的教程(http://www.raywenderlich.com/3443/apple-push-notification ...
- linux编译php的c扩展
第一步:安装php5 第二步:打开终端[为来方便,这里使用root用户],使用CD命令进入到php5源码包的ext目录 第三步:在终端键入以下命令 ./ext_skel --extname=extes ...
- pthread——pthread_cleanup
Pthread_cleanup用于注册线程清理函数,注册的清理函数将在线程被取消或者主动调用pthread_exit时被调用: 一个简单的示例: #include <pthread.h& ...
- 用python实现,冒泡排序演示
# -*- coding:utf-8 -*- import time from Tkinter import * a=[1,9,5,6,8,1] class CanvasDemo(): #n1=70 ...
- WinObjC?这是什么鬼?
https://github.com/Microsoft/WinObjC 微软啊?!你搞个编译器也就算了?!还把iOS SDK的类库都重写了?这也太不把Apple放眼里了?你就这样拽一大帮iOS的开发 ...
- 使用WatiN进行UI自动化测试
Watin是一个UI自动化测试工具,支持ie/firefox,官方网站:http://watin.org/. 主要有以下特点: 支持主要的html元素,见:http://watin.org/docum ...
- 【C语言学习】《C Primer Plus》第13章 文件输入/输出
学习总结 1.文件函数原型1: FILE* fopen(char *filename, char *openmode); //打开文件,返回文件指针 filename:文件名,更确切地说,是包含文件 ...