// Copyright 2011 Fotios Sioutis (sfotis@gmail.com)
//
//This file is part of pythonOCC.
//
//pythonOCC is free software: you can redistribute it and/or modify
//it under the terms of the GNU Lesser General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//pythonOCC is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public License
//along with pythonOCC. If not, see .
//---------------------------------------------------------------------------
#include "Tesselator.h"
//---------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//---------------------------------------------------------------------------
Tesselator::Tesselator(TopoDS_Shape aShape,
theTextureMappingRule aTxtMapType,
Standard_Real anAutoScaleSizeOnU,
Standard_Real anAutoScaleSizeOnV,
Standard_Real aDeviation,
Standard_Real aUOrigin,
Standard_Real aVOrigin,
Standard_Real aURepeat,
Standard_Real aVRepeat,
Standard_Real aScaleU,
Standard_Real aScaleV,
Standard_Real aRotationAngle) :
//set local variables
myDeviation(aDeviation),
myUOrigin(aUOrigin),
myVOrigin(aVOrigin),
myURepeat(aURepeat),
myVRepeat(aVRepeat),
myScaleU(aScaleU),
myScaleV(aScaleV),
myAutoScaleSizeOnU(anAutoScaleSizeOnU),
myAutoScaleSizeOnV(anAutoScaleSizeOnV),
myTxtMapType(aTxtMapType),
myShape(aShape),
myRotationAngle(aRotationAngle)
{
//prepare bbox tex coords
if (myTxtMapType == atCube) {
//create a compound for bbox
//prepare bbox tex coords
PrepareBoxTextureCoordinates(myShape);
}
locVertexcoord = NULL;
locNormalcoord = NULL;
locTexcoord = NULL;
// compute default deviation
ComputeDefaultDeviation();
//Tesselate();
TesselateWithUVCoords();
}
Tesselator::Tesselator(TopoDS_Shape aShape) :
//set local variables
myDeviation(1.0),
myUOrigin(0.),
myVOrigin(0.),
myURepeat(1.),
myVRepeat(1.),
myScaleU(1.),
myScaleV(1.),
myAutoScaleSizeOnU(1.),
myAutoScaleSizeOnV(1.),
myTxtMapType(atNormal),
myShape(aShape),
myRotationAngle(0.)
{
locVertexcoord = NULL;
locNormalcoord = NULL;
locTexcoord = NULL;
// compute default deviation
ComputeDefaultDeviation();
TesselateWithUVCoords();
}
//---------------------------------------------------------------------------
Tesselator::~Tesselator()
{
if (locVertexcoord)
delete [] locVertexcoord;
if (locNormalcoord)
delete [] locNormalcoord;
if (locTexcoord)
delete [] locTexcoord;
}
//---------------------------------------------------------------------------
void Tesselator::SetDeviation(Standard_Real aDeviation)
{
myDeviation = aDeviation;
}
//---------------------------------------------------------------------------
void Tesselator::Tesselate()
{
Standard_Real Umin;
Standard_Real Umax;
Standard_Real Vmin;
Standard_Real Vmax;
Standard_Real dUmax;
Standard_Real dVmax;
TopExp_Explorer ExpFace;
StdPrs_ToolShadedShape SST;
gp_Dir d;
gp_Pnt p;
gp_Vec2d theCoord_p;
gp_Pnt2d d_coord;
//Triangulate
BRepMesh::Mesh(myShape, myDeviation);
for (ExpFace.Init(myShape, TopAbs_FACE); ExpFace.More(); ExpFace.Next()) {
const TopoDS_Face& myFace = TopoDS::Face(ExpFace.Current());
TopLoc_Location aLocation;
Handle(Poly_Triangulation) myT = BRep_Tool::Triangulation(myFace, aLocation);
if (!myT.IsNull()) {
Poly_Connect pc(myT);
aface *this_face = new aface;
//write vertex buffer
const TColgp_Array1OfPnt& Nodes = myT->Nodes();
this_face->vertex_coord = new float[Nodes.Length() * 3];
this_face->number_of_coords = Nodes.Length();
for (int i = Nodes.Lower(); i <= Nodes.Upper(); i++) {
p = Nodes(i).Transformed(aLocation.Transformation());
this_face->vertex_coord[((i-1) * 3)+ 0] = p.X();
this_face->vertex_coord[((i-1) * 3)+ 1] = p.Y();
this_face->vertex_coord[((i-1) * 3)+ 2] = p.Z();
}
//write normal buffer
TColgp_Array1OfDir myNormal(Nodes.Lower(), Nodes.Upper());
SST.Normal(myFace, pc, myNormal);
this_face->normal_coord = new float[myNormal.Length() * 3];
this_face->number_of_normals = myNormal.Length();
for (int i = myNormal.Lower(); i <= myNormal.Upper(); i++) {
d = myNormal(i).Transformed(aLocation.Transformation());
this_face->normal_coord[((i-1) * 3)+ 0] = d.X();
this_face->normal_coord[((i-1) * 3)+ 1] = d.Y();
this_face->normal_coord[((i-1) * 3)+ 2] = d.Z();
}
// set uvcoords buffers to NULL
// necessary for JoinPrimitive to be performed
this_face->tex_coord = NULL;
this_face->number_of_texcoords = 0;
//write triangle buffer
Standard_Integer validFaceTriCount = 0;
Standard_Integer n1 , n2 , n3;
TopAbs_Orientation orient = myFace.Orientation();
const Poly_Array1OfTriangle& triangles = myT->Triangles();
this_face->tri_indexes = new int [triangles.Length()* 3];
for (int nt = 1; nt <= myT->NbTriangles(); nt++) {
triangles(nt).Get(n1,n2,n3);
if (orient != TopAbs_FORWARD) {
Standard_Integer tmp=n1;
n1 = n2;
n2 = tmp;
}
if (TriangleIsValid(Nodes(n1),Nodes(n2),Nodes(n3))) {
this_face->tri_indexes[(validFaceTriCount * 3)+ 0] = n1;
this_face->tri_indexes[(validFaceTriCount * 3)+ 1] = n2;
this_face->tri_indexes[(validFaceTriCount * 3)+ 2] = n3;
validFaceTriCount++;
}
}
this_face->number_of_triangles = validFaceTriCount;
facelist.push_back(this_face);
}
}
JoinPrimitives();
}
//---------------------------------------------------------------------------
void Tesselator::TesselateWithUVCoords()
{
Standard_Real Umin;
Standard_Real Umax;
Standard_Real Vmin;
Standard_Real Vmax;
Standard_Real dUmax;
Standard_Real dVmax;
TopExp_Explorer ExpFace;
StdPrs_ToolShadedShape SST;
gp_Dir d;
gp_Pnt p;
gp_Vec2d theCoord_p;
gp_Pnt2d d_coord;
cout << "Tesselate with UV coords\n";
//Triangulate
BRepMesh::Mesh(myShape, myDeviation);
for (ExpFace.Init(myShape, TopAbs_FACE); ExpFace.More(); ExpFace.Next()) {
const TopoDS_Face& myFace = TopoDS::Face(ExpFace.Current());
TopLoc_Location aLocation;
Handle(Poly_Triangulation) myT = BRep_Tool::Triangulation(myFace, aLocation);
if (!myT.IsNull()) {
Poly_Connect pc(myT);
aface *this_face = new aface;
//write vertex buffer
const TColgp_Array1OfPnt& Nodes = myT->Nodes();
this_face->vertex_coord = new float[Nodes.Length() * 3];
this_face->number_of_coords = Nodes.Length();
for (int i = Nodes.Lower(); i <= Nodes.Upper(); i++) {
p = Nodes(i).Transformed(aLocation.Transformation());
this_face->vertex_coord[((i-1) * 3)+ 0] = p.X();
this_face->vertex_coord[((i-1) * 3)+ 1] = p.Y();
this_face->vertex_coord[((i-1) * 3)+ 2] = p.Z();
}
//write normal buffer
TColgp_Array1OfDir myNormal(Nodes.Lower(), Nodes.Upper());
SST.Normal(myFace, pc, myNormal);
this_face->normal_coord = new float[myNormal.Length() * 3];
this_face->number_of_normals = myNormal.Length();
for (int i = myNormal.Lower(); i <= myNormal.Upper(); i++) {
d = myNormal(i).Transformed(aLocation.Transformation());
this_face->normal_coord[((i-1) * 3)+ 0] = d.X();
this_face->normal_coord[((i-1) * 3)+ 1] = d.Y();
this_face->normal_coord[((i-1) * 3)+ 2] = d.Z();
}
//write uvcoord buffer
int id1 = 0, id2 = 2, idNull = 1;
if (myTxtMapType == atNormal || myTxtMapType == atNormalAutoScale) {
BRepTools::UVBounds(myFace,Umin, Umax, Vmin, Vmax);
dUmax = (Umax - Umin);
dVmax = (Vmax - Vmin);
if (myTxtMapType == atNormalAutoScale) {
myScaleU = myAutoScaleSizeOnU/dUmax;
myScaleV = myAutoScaleSizeOnV/dVmax;
}
}
const TColgp_Array1OfPnt2d& UVNodes = myT->UVNodes();
this_face->tex_coord = new float[UVNodes.Length() * 3];
this_face->number_of_texcoords = UVNodes.Length();
for (int i = UVNodes.Lower(); i <= UVNodes.Upper();i++) {
if (myTxtMapType == atCube) {
GetBoxTextureCoordinate( Nodes(i).Transformed(aLocation.Transformation()),
myNormal(i).Transformed(aLocation.Transformation()),
theCoord_p);
d_coord.SetX((-myUOrigin+(myURepeat*theCoord_p.X())/aBndBoxSz)/myScaleU);
d_coord.SetY((-myVOrigin+(myVRepeat*theCoord_p.Y())/aBndBoxSz)/myScaleV);
}
else {
d_coord = UVNodes(i);
d_coord.SetX((-myUOrigin+(myURepeat*(d_coord.X()-Umin))/dUmax)/myScaleU);
d_coord.SetY((-myVOrigin+(myVRepeat*(d_coord.Y()-Vmin))/dVmax)/myScaleV);
}
d_coord.Rotate(gp::Origin2d(), myRotationAngle);
this_face->tex_coord[((i-1) * 3)+ id1] = d_coord.X();
this_face->tex_coord[((i-1) * 3)+ id2] = d_coord.Y();
this_face->tex_coord[((i-1) * 3)+ idNull] = 0;
}
//write triangle buffer
Standard_Integer validFaceTriCount = 0;
Standard_Integer n1 , n2 , n3;
TopAbs_Orientation orient = myFace.Orientation();
const Poly_Array1OfTriangle& triangles = myT->Triangles();
this_face->tri_indexes = new int [triangles.Length()* 3];
for (int nt = 1; nt <= myT->NbTriangles(); nt++) {
triangles(nt).Get(n1,n2,n3);
if (orient != TopAbs_FORWARD) {
Standard_Integer tmp=n1;
n1 = n2;
n2 = tmp;
}
if (TriangleIsValid(Nodes(n1),Nodes(n2),Nodes(n3))) {
this_face->tri_indexes[(validFaceTriCount * 3)+ 0] = n1;
this_face->tri_indexes[(validFaceTriCount * 3)+ 1] = n2;
this_face->tri_indexes[(validFaceTriCount * 3)+ 2] = n3;
validFaceTriCount++;
}
}
this_face->number_of_triangles = validFaceTriCount;
facelist.push_back(this_face);
}
}
JoinPrimitivesWithUVCoords();
}
//---------------------------INTERFACE---------------------------------------
void Tesselator::ComputeDefaultDeviation()
{
// This method automatically computes precision from the bounding box of the shape
Bnd_Box aBox;
Standard_Real aXmin,aYmin ,aZmin ,aXmax ,aYmax ,aZmax;
//calculate the bounding box
BRepBndLib::Add(myShape, aBox);
aBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
Standard_Real xDim = abs((long)aXmax - (long)aXmin);
Standard_Real yDim = abs((long)aYmax - (long)aYmin);
Standard_Real zDim = abs((long)aZmax - (long)aZmin);
Standard_Real adeviation = std::max(aXmax-aXmin, std::max(aYmax-aYmin, aZmax-aZmin)) * 1e-2 ;
myDeviation = adeviation;
}
void Tesselator::ExportShapeToX3D(char * filename, int diffR, int diffG, int diffB)
{
ofstream X3Dfile;
int *vertices_idx = new int[3];
int *texcoords_idx = new int[3];
int *normals_idx = new int[3];
X3Dfile.open (filename);
// write header
X3Dfile << "" ;
X3Dfile << "";
X3Dfile << "";
X3Dfile << "";
X3Dfile << "";
X3Dfile << "";
X3Dfile << "";
// export faces indices
X3Dfile << "";
// write points coordinates
X3Dfile << "";
// write normals
X3Dfile << "";
// close all markups
X3Dfile << "\n";
X3Dfile.close();
delete [] vertices_idx;
delete [] texcoords_idx;
delete [] normals_idx;
}
void Tesselator::ExportShapeToJSON(char * filename)
{
ofstream JSONObject;
int *vertices_idx = new int[3];
int *texcoords_idx = new int[3];
int *normals_idx = new int[3];
JSONObject.open (filename);
// write header
JSONObject << "var Shape = function () {\n";
JSONObject << "var scope = this;\n";
JSONObject << "THREE.Geometry.call( this );\n";
// first write vertices coords
for (int i=0;i::iterator anIterator = facelist.begin();
while (anIterator != facelist.end()) {
aface* myface = *anIterator;
total_poly_count = total_poly_count + myface->number_of_triangles;
total_vertex_count = total_vertex_count + myface->number_of_coords;
total_normal_count = total_normal_count + myface->number_of_normals;
anIterator++;
}
loc_tri_indexes= new int[total_poly_count * 3 ];
locVertexcoord = new float[total_vertex_count * 3 ];
locNormalcoord = new float[total_normal_count * 3 ];
tot_triangle_count = total_poly_count;
tot_vertex_count = total_vertex_count;
tot_normal_count = total_normal_count;
anIterator = facelist.begin();
while (anIterator != facelist.end()) {
aface* myface = *anIterator;
for (int x = 0; x < myface->number_of_coords; x++) {
locVertexcoord[(obP * 3) + 0] = myface->vertex_coord[(x * 3) + 0];
locVertexcoord[(obP * 3) + 1] = myface->vertex_coord[(x * 3) + 1];
locVertexcoord[(obP * 3) + 2] = myface->vertex_coord[(x * 3) + 2];
obP++;
}
for (int x = 0; x < myface->number_of_normals; x++) {
locNormalcoord[(obN * 3) + 0] = myface->normal_coord[(x * 3) + 0];
locNormalcoord[(obN * 3) + 1] = myface->normal_coord[(x * 3) + 1];
locNormalcoord[(obN * 3) + 2] = myface->normal_coord[(x * 3) + 2];
obN++;
}
for (int x = 0; x < myface->number_of_triangles; x++) {
loc_tri_indexes[(obTR * 3) + 0] = myface->tri_indexes[(x * 3) + 0] + advance - 1;
loc_tri_indexes[(obTR * 3) + 1] = myface->tri_indexes[(x * 3) + 1] + advance - 1;
loc_tri_indexes[(obTR * 3) + 2] = myface->tri_indexes[(x * 3) + 2] + advance - 1;
obTR++;
}
advance = obP;
delete [] myface->vertex_coord;
myface->vertex_coord = NULL;
delete [] myface->normal_coord;
myface->normal_coord = NULL;
delete [] myface->tri_indexes;
myface->tri_indexes = NULL;
delete myface;
myface = NULL;
anIterator++;
}
}
void Tesselator::JoinPrimitivesWithUVCoords()
{
int obP = 0;
int obN = 0;
int obT = 0;
int obTR = 0;
int advance = 0;
int total_poly_count = 0;
int total_vertex_count = 0;
int total_normal_count = 0;
int total_texcoord_count = 0;
std::vector::iterator anIterator = facelist.begin();
while (anIterator != facelist.end()) {
aface* myface = *anIterator;
total_poly_count = total_poly_count + myface->number_of_triangles;
total_vertex_count = total_vertex_count + myface->number_of_coords;
total_normal_count = total_normal_count + myface->number_of_normals;
total_texcoord_count = total_texcoord_count + myface->number_of_texcoords;
anIterator++;
}
loc_tri_indexes= new int[total_poly_count * 3 ];
locVertexcoord = new float[total_vertex_count * 3 ];
locNormalcoord = new float[total_normal_count * 3 ];
locTexcoord = new float[total_texcoord_count * 3 ];
tot_triangle_count = total_poly_count;
tot_vertex_count = total_vertex_count;
tot_normal_count = total_normal_count;
tot_texcoord_count = total_texcoord_count;
anIterator = facelist.begin();
while (anIterator != facelist.end()) {
aface* myface = *anIterator;
for (int x = 0; x < myface->number_of_coords; x++) {
locVertexcoord[(obP * 3) + 0] = myface->vertex_coord[(x * 3) + 0];
locVertexcoord[(obP * 3) + 1] = myface->vertex_coord[(x * 3) + 1];
locVertexcoord[(obP * 3) + 2] = myface->vertex_coord[(x * 3) + 2];
obP++;
}
for (int x = 0; x < myface->number_of_normals; x++) {
locNormalcoord[(obN * 3) + 0] = myface->normal_coord[(x * 3) + 0];
locNormalcoord[(obN * 3) + 1] = myface->normal_coord[(x * 3) + 1];
locNormalcoord[(obN * 3) + 2] = myface->normal_coord[(x * 3) + 2];
obN++;
}
for (int x = 0; x < myface->number_of_texcoords; x++) {
locTexcoord[(obT * 3) + 0] = myface->tex_coord[(x * 3) + 0];
locTexcoord[(obT * 3) + 1] = myface->tex_coord[(x * 3) + 1];
locTexcoord[(obT * 3) + 2] = myface->tex_coord[(x * 3) + 2];
obT++;
}
for (int x = 0; x < myface->number_of_triangles; x++) {
loc_tri_indexes[(obTR * 3) + 0] = myface->tri_indexes[(x * 3) + 0] + advance - 1;
loc_tri_indexes[(obTR * 3) + 1] = myface->tri_indexes[(x * 3) + 1] + advance - 1;
loc_tri_indexes[(obTR * 3) + 2] = myface->tri_indexes[(x * 3) + 2] + advance - 1;
obTR++;
}
advance = obP;
delete [] myface->vertex_coord;
myface->vertex_coord = NULL;
delete [] myface->normal_coord;
myface->normal_coord = NULL;
delete [] myface->tex_coord;
myface->tex_coord = NULL;
delete [] myface->tri_indexes;
myface->tri_indexes = NULL;
delete myface;
myface = NULL;
anIterator++;
}
}
//---------------------------------------------------------------------------
Standard_Boolean Tesselator::TriangleIsValid(const gp_Pnt& P1, const gp_Pnt& P2, const gp_Pnt& P3) const
{
gp_Vec V1(P1,P2);
gp_Vec V2(P2,P3);
gp_Vec V3(P3,P1);
if ((V1.SquareMagnitude() > 1.e-10) && (V2.SquareMagnitude() > 1.e-10) && (V3.SquareMagnitude() > 1.e-10)) {
V1.Cross(V2);
if (V1.SquareMagnitude() > 1.e-10)
return Standard_True;
else
return Standard_False;
}
else
return Standard_False;
}
//---------------------------------------------------------------------------
void Tesselator::PrepareBoxTextureCoordinates(const TopoDS_Shape& aShape)
{
//declare local variables
Bnd_Box aBox;
Standard_Real aXmin,aYmin ,aZmin ,aXmax ,aYmax ,aZmax;
Standard_Real aUmaxBack, aVmaxBack;
Standard_Real aUmaxLeft, aVmaxLeft;
Standard_Real aUmaxBottom, aVmaxBottom;
//calculate the bounding box
BRepBndLib::Add(aShape, aBox);
aBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
//enlarge the bb so all edges have the size of the biggest one
Standard_Real xDim = abs((long)aXmax - (long)aXmin);
Standard_Real yDim = abs((long)aYmax - (long)aYmin);
Standard_Real zDim = abs((long)aZmax - (long)aZmin);
if ((xDim > yDim) && (xDim > zDim)) {
aYmin -= (xDim - yDim) / 2;
aYmax += (xDim - yDim) / 2;
aZmin -= (xDim - zDim) / 2;
aZmax += (xDim - zDim) / 2;
}
else if ((yDim > xDim) && (yDim > zDim)) {
aXmin -= (yDim - xDim) / 2;
aXmax += (yDim - xDim) / 2;
aZmin -= (yDim - zDim) / 2;
aZmax += (yDim - zDim) / 2;
}
else {
aXmin -= (zDim - xDim) / 2;
aXmax += (zDim - xDim) / 2;
aYmin -= (zDim - yDim) / 2;
aYmax += (zDim - yDim) / 2;
}
aBndBoxSz = aXmax-aXmin;
}
//---------------------------------------------------------------------------
void Tesselator::GetBoxTextureCoordinate(const gp_Pnt& p, const gp_Dir& N1, gp_Vec2d& theCoord_p)
{
Standard_Real x = abs(N1.X());
Standard_Real y = abs(N1.Y());
Standard_Real z = abs(N1.Z());
if (x >= y && x >= z) {
if (N1.X() > 0) { //right
theCoord_p.SetX(p.Y() - aYmin);
theCoord_p.SetY(p.Z() - aZmin);
theCoord_p.Rotate(M_PI/2.);
}
else { //left
theCoord_p.SetX(p.Z() - aZmin);
theCoord_p.SetY(p.Y() - aYmin);
}
}
else if ((y >= z) && (y >= x)) {
if (N1.Y() > 0) { //top
theCoord_p.SetX(p.X() - aXmin);
theCoord_p.SetY(-(p.Z() - aZmin));
}
else { //bottom
theCoord_p.SetY(p.Z() - aZmin);
theCoord_p.SetX(p.X() - aXmin);
theCoord_p.Rotate( M_PI);
}
}
else {
if (N1.Z() > 0) { //front
theCoord_p.SetX(p.X() - aXmin);
theCoord_p.SetY(p.Y() - aYmin);
}
else { //back
theCoord_p.SetX(p.Y() - aYmin);
theCoord_p.SetY(p.X() - aXmin);
theCoord_p.Rotate(M_PI/2.);
}
}
}