//********************************************
// Mesh3d.cpp
//********************************************
// class CMesh3d
//********************************************
// alliez@usc.edu
// Created : 15/01/98
// Modified : 15/01/98
//********************************************
#include "stdafx.h"
#include <math.h>
#include "Base3d.h"
#include "Mesh3d.h"
#include "ColorRamp.h"
#include "AVLInd.h" // Gaspard Breton's stuff
#include "WmfTools.h"
//////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////
//********************************************
// Constructor
//********************************************
CMesh3d::CMesh3d()
{
m_ListDone = 0;
m_Modified = 1;
m_NormalBinding = NORMAL_PER_FACE;
m_ColorBinding = COLOR_PER_VERTEX;
m_Name = _T("Mesh");
// Texture
m_IndexTexture = -1;
m_pTextureCoordinate = NULL;
m_pTextureCoordinateIndex = NULL;
m_Show = 1;
}
//********************************************
// Destructor
//********************************************
CMesh3d::~CMesh3d()
{
Free();
}
//********************************************
// Free
//********************************************
void CMesh3d::Free()
{
//TRACE("Cleanup mesh %x\n",this);
m_ArrayVertex.Free();
m_ArrayFace.Free();
if(m_pTextureCoordinate != NULL)
delete [] m_pTextureCoordinate;
if(m_pTextureCoordinateIndex != NULL)
delete [] m_pTextureCoordinateIndex;
}
//////////////////////////////////////////////
// OPENGL
//////////////////////////////////////////////
//********************************************
// BuildList
//********************************************
int CMesh3d::glBuildList()
{
//TRACE(" Start building list ...\n");
// Check for valid mesh
if(m_ArrayVertex.GetSize() == 0 ||
m_ArrayFace.GetSize() == 0)
{
TRACE("CMesh3d::BuildList : empty mesh\n");
return 0;
}
if(!m_Modified && m_ListDone)
return 0;
// Erase last list
::glDeleteLists(m_ListOpenGL,1);
// Search for a new list
m_ListOpenGL = ::glGenLists(1);
if(m_ListOpenGL == 0)
{
TRACE("CMesh3d::BuildList : unable to build DrawList\n");
return 0;
}
// Start list
::glNewList(m_ListOpenGL,GL_COMPILE_AND_EXECUTE);
glDrawDirect();
::glEndList();
// List is done now
m_ListDone = 1;
m_Modified = 0;
return 1;
}
//*********************************
// glDrawDirect
//*********************************
int CMesh3d::glDrawDirect()
{
unsigned int NbVertex = (unsigned int)m_ArrayVertex.GetSize();
unsigned int NbFace = (unsigned int)m_ArrayFace.GetSize();
if(!NbVertex)
return 0;
if(!NbFace)
return 0;
CFace3d *pFace;
CVector3d *pVector;
CColor *pColorPrevious;
::glPushMatrix();
// Position / translation / scaling
glTranslatef(m_Transform.GetTranslation()->x(),
m_Transform.GetTranslation()->y(),
m_Transform.GetTranslation()->z());
glScalef(m_Transform.GetScale()->x(),
m_Transform.GetScale()->y(),
m_Transform.GetScale()->z());
glRotatef(m_Transform.GetValueRotation(),
m_Transform.GetRotation()->x(),
m_Transform.GetRotation()->y(),
m_Transform.GetRotation()->z());
// Init color
pFace = m_ArrayFace[0];
pColorPrevious = pFace->GetColor();
::glColor3ub(pFace->GetColor()->r(),pFace->GetColor()->g(),pFace->GetColor()->b());
// Triangles
::glBegin(GL_TRIANGLES);
for(unsigned int i=0;i<NbFace;i++)
{
{
pFace = m_ArrayFace[i];
ASSERT(pFace != NULL);
// Normal (per face)
if(m_NormalBinding == NORMAL_PER_FACE)
{
pVector = pFace->GetNormal();
::glNormal3f(pVector->x(),pVector->y(),pVector->z());
}
// Color (per face)
if(m_ColorBinding == COLOR_PER_FACE &&
pColorPrevious != pFace->GetColor())
{
::glColor3ub(pFace->GetColor()->r(),pFace->GetColor()->g(),pFace->GetColor()->b());
pColorPrevious = pFace->GetColor();
}
for(int j=0;j<3;j++)
{
// Normal
if(m_NormalBinding == NORMAL_PER_VERTEX)
{
pVector = pFace->v(j)->GetNormal();
::glNormal3f(pVector->x(),pVector->y(),pVector->z());
}
// Color (per vertex)
if(m_ColorBinding == COLOR_PER_VERTEX &&
pColorPrevious != pFace->v(j)->GetColor())
{
::glColor3ub(pFace->v(j)->GetColor()->r(),pFace->v(j)->GetColor()->g(),pFace->v(j)->GetColor()->b());
pColorPrevious = pFace->v(j)->GetColor();
}
// Texture coordinate (if needed)
if(m_IndexTexture != -1)
{
glTexCoord2f(m_pTextureCoordinate[2*m_pTextureCoordinateIndex[3*i+j]],
m_pTextureCoordinate[2*m_pTextureCoordinateIndex[3*i+j]+1]);
}
// Vertex
::glVertex3f(pFace->v(j)->x(),pFace->v(j)->y(),pFace->v(j)->z());
}
}
}
::glEnd();
::glPopMatrix();
return 1;
}
//********************************************
// Draw
//********************************************
int CMesh3d::glDraw()
{
if(!m_Show)
return 0;
// Build list at first
if(!m_ListDone || m_Modified)
glBuildList();
// Search for a new list
if(::glIsList(m_ListOpenGL)==GL_TRUE)
{
::glCallList(m_ListOpenGL);
return 1;
}
else
{
TRACE(" CMesh3d::Draw : unable to draw list %d\n",m_ListOpenGL);
return 0;
}
}
//////////////////////////////////////////////
// DATAS
//////////////////////////////////////////////
//********************************************
// Copy
//********************************************
void CMesh3d::Copy(CMesh3d *pMesh)
{
// Vertices
int NbVertex = pMesh->NbVertex();
m_ArrayVertex.SetSize(NbVertex);
for(int i=0;i<NbVertex;i++)
m_ArrayVertex.SetAt(i,new CVertex3d(pMesh->GetVertex(i)));
// Faces
int NbFace = pMesh->NbFace();
m_ArrayFace.SetSize(NbFace);
for(i=0;i<NbFace;i++)
{
CFace3d *pFace = pMesh->GetFace(i);
m_ArrayFace.SetAt(i,new CFace3d(
m_ArrayVertex[pMesh->IndexFrom(pFace->v1())],
m_ArrayVertex[pMesh->IndexFrom(pFace->v2())],
m_ArrayVertex[pMesh->IndexFrom(pFace->v3())]));
}
// Transform
m_Transform.Copy(pMesh->GetTransform());
}
//********************************************
// GetType
//********************************************
int CMesh3d::GetType()
{
return TYPE_MESH3D;
}
//********************************************
// IsValid
//********************************************
int CMesh3d::IsValid()
{
int NbFace = m_ArrayFace.GetSize();
for(int i=0;i<NbFace;i++)
if(!m_ArrayFace[i]->IsValid())
return 0;
return 1;
}
//********************************************
// DeleteVertex
//********************************************
int CMesh3d::DeleteVertex(CVertex3d *pVertex)
{
int size = m_ArrayVertex.GetSize();
for(int i=0;i<size;i++)
{
CVertex3d *pV = m_ArrayVertex[i];
if(pV == pVertex)
{
m_ArrayVertex.RemoveAt(i);
delete pVertex;
return 1;
}
}
return 0;
}
//********************************************
// DeleteVertex
//********************************************
int CMesh3d::DeleteVertex(int index)
{
if(index < m_ArrayVertex.GetSize())
{
CVertex3d *pVertex = (CVertex3d *)m_ArrayVertex[index];
m_ArrayVertex.RemoveAt(index);
delete pVertex;
return 1;
}
return 0;
}
//********************************************
// DeleteFace
//********************************************
int CMesh3d::DeleteFaceNbNeighbors(int NbNeighbor)
{
TRACE("Delete faces which has %d neighbors...",NbNeighbor);
int deleted = 0;
for(int i=0;i<m_ArrayFace.GetSize();i++)
{
CFace3d *pFace = m_ArrayFace[i];
if(pFace->NbFaceNeighbor() == NbNeighbor)
{
m_ArrayFace.RemoveAt(i);
delete pFace;
i--;
deleted++;
}
}
TRACE("%d face(s) deleted\n",deleted);
return deleted;
}
//////////////////////////////////////////////
// RANGE
//////////////////////////////////////////////
//********************************************
// Range
//********************************************
void CMesh3d::Range(int coord,
float *min,
float *max)
{
ASSERT(coord >= 0 && coord <= 2);
int NbVertex = m_ArrayVertex.GetSize();
float Min = m_ArrayVertex[0]->Get(coord);
float Max = Min;
for(int i=1;i<NbVertex;i++)
{
float value = m_ArrayVertex[i]->Get(coord);
if(value < Min)
Min = value;
if(value > Max)
Max = value;
}
*min = Min;
*max = Max;
}
//********************************************
// Range (apply)
//********************************************
void CMesh3d::Range(int coord,
float min,
float max)
{
TRACE("Range mesh...");
float Min,Max;
Range(coord,&Min,&Max);
TRACE("old : (%g,%g) -> (%g %g)",Min,Max,min,max);
Offset(coord,-Min);
Scale(coord,(max-min)/(Max-Min));
Offset(coord,min);
TRACE("ok\n");
}
//********************************************
// Scale
//********************************************
void CMesh3d::Scale(int coord,
float scale)
{
int NbVertex = m_ArrayVertex.GetSize();
for(int i=0;i<NbVertex;i++)
m_ArrayVertex[i]->Set(coord,m_ArrayVertex[i]->Get(coord) * scale);
m_Modified = 1;
}
//********************************************
// Offset
//********************************************
void CMesh3d::Offset(int coord,
float offset)
{
int NbVertex = m_ArrayVertex.GetSize();
for(int i=0;i<NbVertex;i++)
m_ArrayVertex[i]->Set(coord,m_ArrayVertex[i]->Get(coord) + offset);
m_Modified = 1;
}
//////////////////////////////////////////////
// PROCESSING
//////////////////////////////////////////////
//********************************************
// BuildAdjacency
// From VertexArray and FaceArray, build
// neighboring vertices and faces, using
// edge and vertex sharing
//********************************************
int CMesh3d::BuildAdjacency()
{
// Check for valid sizes
int NbVertex = m_ArrayVertex.GetSize();
int NbFace = m_ArrayFace.GetSize();
TRACE("Build adjacency (%d faces, %d vertices)",NbFace,NbVertex);
if(NbVertex < 3 || NbFace <= 0)
{
TRACE(" invalid array sizes\n");
return 0;
}
// At first, clear all neighbors for each face
//TRACE(" clear face neighbors\n");
TRACE(".");
for(int i=0;i<NbFace;i++)
for(int k=0;k<6;k++)
m_ArrayFace[i]->f(k,NULL);
// At first, clear all neighbors for each vertex
//TRACE(" clear vertex neighbors\n");
TRACE(".");
for(i=0;i<NbVertex;i++)
{
m_ArrayVertex[i]->RemoveAllFaceNeighbor();
m_ArrayVertex[i]->RemoveAllVertexNeighbor();
}
//*********************************************
// For each face, set face neighboring vertices
//*********************************************
//TRACE(" set face neighboring vertices\n");
TRACE(".");
for(i=0;i<NbFace;i++)
{
CFace3d *pFaceCurrent = m_ArrayFace[i];
for(int j=0;j<3;j++)
pFaceCurrent->v(j)->AddNeighbor(pFaceCurrent);
}
//*********************************************
// For each vertex, set vertex neighboring,
// just look on neighboring faces
//*********************************************
//TRACE(" set vertices neighboring vertices\n");
TRACE(".");
for(i=0;i<NbVertex;i++)
{
CVertex3d *pVertexCurrent = m_ArrayVertex[i];
int NbFaceNeighbor = pVertexCurrent->NbFaceNeighbor();
for(int j=0;j<NbFaceNeighbor;j++)
{
CFace3d *pFace = pVertexCurrent->GetFaceNeighbor(j);
for(int k=0;k<3;k++)
pVertexCurrent->AddNeighbor(pFace->v(k));
}
}
//*********************************************
// For each face, set face neighboring,
// just look on faces neighboring vertices
//*********************************************
//TRACE(" set faces neighboring faces\n");
TRACE(".");
for(i=0;i<NbFace;i++)
{
CFace3d *pFaceCurrent = m_ArrayFace[i];
// For each edge
for(int j=0;j<3;j++)
{
CVertex3d *pVertex = pFaceCurrent->v(j);
CVertex3d *pNextVertex = pFaceCurrent->v((j+1)%3);
int NbFaceNeighbor = pVertex->NbFaceNeighbor();
for(int k=0;k<NbFaceNeighbor;k++)
{
// This face contain pVertex
CFace3d *pFace = pVertex->GetFaceNeighbor(k);
if(pFace != pFaceCurrent)
if(pFaceCurrent->f(j) == NULL)
if(pFace->HasVertex(pVertex))
if(pFace->HasVertex(pNextVertex))
pFaceCurrent->f(j,pFace);
}
}
}
/*
// Check
for(i=0;i<NbFace;i++)
{
ASSERT(m_ArrayFace[i]->IsValid());
}
*/
TRACE("ok\n");
return TRUE;
}
//********************************************
// Rebuild
//********************************************
void CMesh3d::Rebuild()
{
BuildAdjacency();
CalculateNormalPerFace();
CalculateNormalPerVertex();
SetModified(1);
}
/////////////////////////////////////////////
// DEBUG
//////////////////////////////////////////////
//********************************************
// Trace
//********************************************
void CMesh3d::Trace()
{
int NbVertex = m_ArrayVertex.GetSize();
int NbFace = m_ArrayFace.GetSize();
TRACE("\n");
TRACE(" Mesh \n");
TRACE("Vertex : %d\n",NbVertex);
TRACE("Face : %d\n",NbFace);
for(int i=0;i<NbVertex;i++)
((CVertex3d *)m_ArrayVertex[i])->Trace();
for(int j=0;j<NbFace;j++)
((CFace3d *)m_ArrayFace[j])->Trace();
}
//********************************************
// IndexFrom
// Return -1 if failed
//********************************************
int CMesh3d::IndexFrom(CFace3d *pFace)
{
ASSERT(pFace != NULL);
int NbFace = m_ArrayFace.GetSize();
for(int i=0;i<NbFace;i++)
if(m_ArrayFace[i] == pFace)
return i;
return -1;
}
//********************************************
// IndexFrom
// Return -1 if failed
//********************************************
int CMesh3d::IndexFrom(CVertex3d *pVertex)
{
int NbVertex = m_ArrayVertex.GetSize();
for(int i=0;i<NbVertex;i++)
if(m_ArrayVertex[i] == pVertex)
return i;
return -1;
}
//********************************************
// Move
//********************************************
void CMesh3d::Move(float dx,float dy,float dz)
{
int NbVertex = m_ArrayVertex.GetSize();
for(int i=0;i<NbVertex;i++)
m_ArrayVertex[i]->Move(dx,dy,dz);
m_Modified = 1;
}
//********************************************
// FindVertexInFaces
//********************************************
int CMesh3d::FindVertex(CVertex3d *pVertex)
{
int find = 0;
int NbFace = m_ArrayFace.GetSize();
for(int j=0;j<NbFace;j++)
if(m_ArrayFace[j]->HasVertex(pVertex))
{
find = 1;
TRACE("Find vertex [%x] in face %d\n",pVertex,j);
}
int NbVertex = m_ArrayVertex.GetSize();
for(j=0;j<NbVertex;j++)
if(m_ArrayVertex[j] == pVertex)
{
find = 1;
TRACE("Find vertex [%x] at position %d\n",pVertex,j);
}
for(j=0;j<NbVertex;j++)
{
CVertex3d *pV = m_ArrayVertex[j];
if(pV->HasNeighbor(pVertex))
{
find = 1;
TRACE("Find vertex [%x] in neighbors of vertex %d\n",pVertex,j);
}
}
return find;
}
//********************************************
// FindFace
//********************************************
int CMesh3d::FindFace(CFace3d *pFace)
{
int find = 0;
int NbFace = m_ArrayFace.GetSize();
for(int j=0;j<NbFace;j++)
{
CFace3d *pF = m_ArrayFace[j];
if(pF == pFace)
{
find = 1;
TRACE("Find face [%x] in mesh (index : %d)\n",pFace,j);
}
for(int i=0;i<3;i++)
if(pF->f(i) == pFace)
{
find = 1;
TRACE("Find face [%x] in neighbors %d of face %d\n",pFace,i,j);
}
}
return find;
}
//////////////////////////////////////////////
// NORMALS
//////////////////////////////////////////////
//********************************************
// CalculateNormalPerVertex
//********************************************
int CMesh3d::CalculateNormalPerVertex(void)
{
int NbVertex = m_ArrayVertex.GetSize();
int NbFace = m_ArrayFace.GetSize();
TRACE("Calculate normal per vertex (%d faces, %d vertices)...",NbFace,NbVertex);
for(int i=0;i<NbVertex;i++)
{
CVertex3d *pVertex = m_ArrayVertex[i];
int NbNormal = 0;
CVector3d vector;
int NbFaceNeighbor = pVertex->NbFaceNeighbor();
for(int j=0;j<NbFaceNeighbor;j++)
{
CFace3d *pFace = pVertex->GetFaceNeighbor(j);
NbNormal += 1;
vector += pFace->GetNormal();
}
if(NbNormal>=1)
{
vector.NormalizeL2();
pVertex->SetNormal(vector);
}
}
m_Modified = 1;
TRACE("ok\n");
return 1;
}
//********************************************
// CalculateNormalPerFace
//********************************************
int CMesh3d::CalculateNormalPerFace(void)
{
int size = m_ArrayFace.GetSize();
TRACE("Calculate normal per face (%d faces)...",size);
for(int i=0;i<size;i++)
{
CFace3d *pFace = m_ArrayFace[i];
if(pFace->IsValid())
pFace->CalculateNormal();
}
m_Modified = 1;
TRACE("ok\n");
return 1;
}
//********************************************
// ColorSharpEdges
//********************************************
int CMesh3d::ColorSharpEdge(float threshold,
CColor &color)
{
int NbFace = m_ArrayFace.GetSize();
TRACE(" Start ColorSharpEdges\n");
TRACE(" Faces : %d\n",NbFace);
for(int i=0;i<NbFace;i++)
m_ArrayFace[i]->ColorSharpEdge(threshold,color);
TRACE(" End ColorSharpEdges\n");
return 1;
}
//********************************************
// SetNormalBinding
//********************************************
void CMesh3d::SetNormalBinding(int type)
{
m_NormalBinding = type;
m_Modified = 1;
}
//********************************************
// GetNormalBinding
//********************************************
int CMesh3d::GetNormalBinding(void)
{
return m_NormalBinding;
}
//********************************************
// SetColorBinding
//********************************************
void CMesh3d::SetColorBinding(int type)
{
m_ColorBinding = type;
m_Modified = 1;
}
//********************************************
// SetColor
//********************************************
void CMesh3d::SetColor(CColor &color)
{
int size = m_ArrayFace.GetSize();
for(int i=0;i<size;i++)
m_ArrayFace[i]->SetColor(color);
m_Modified = 1;
}
//********************************************
// SetColor
//********************************************
void CMesh3d::SetColor(unsigned char r,
unsigned char g,
unsigned char b)
{
for(int i=0;i<m_ArrayFace.GetSize();i++)
m_ArrayFace[i]->SetColor(r,g,b);
for(i=0;i<m_ArrayVertex.GetSize();i++)
m_ArrayVertex[i]->SetColor(r,g,b);
m_Modified = 1;
}
//********************************************
// GetColorBinding
//********************************************
int CMesh3d::GetColorBinding(void)
{
return m_ColorBinding;
}
//********************************************
// SetFlagOnFaces
//********************************************
void CMesh3d::SetFlagOnFaces(int flag)
{
int size = m_ArrayFace.GetSize();
for(int i=0;i<size;i++)
m_ArrayFace[i]->SetFlag(flag);
}
//********************************************
// GetMaxFlagOnFaces
//********************************************
int CMesh3d::GetMaxFlagOnFaces()
{
int size = m_ArrayFace.GetSize();
int max = 0;
for(int i=0;i<size;i++)
{
int tmp = m_ArrayFace[i]->GetFlag();
max = (tmp > max) ? tmp : max;
}
return max;
}
//********************************************
// SetFlagOnVertices
//********************************************
void CMesh3d::SetFlagOnVertices(int flag)
{
int size = m_ArrayVertex.GetSize();
for(int i=0;i<size;i++)
m_ArrayVertex[i]->SetFlag((char)flag);
}
//********************************************
// GetFirstVertexWithFlag
//********************************************
float CMesh3d::GetMeanLengthEdge()
{
int size = m_ArrayVertex.GetSize();
double sum = 0;
for(int i=0;i<size;i++)
sum += m_ArrayVertex[i]->GetMeanLengthEdgeAround();
if(size)
return (float)sum/size;
else
return 0.0f;
}
//////////////////////////////////////////////
// SUBDIVISION
//////////////////////////////////////////////
//********************************************
// Alpha
// From Piecewise smooth reconstruction (Hoppe)
//********************************************
float CMesh3d::Alpha(int n)
{
float tmp = 3.0f + 2.0f * (float)cos(2.0f * 3.14159265359f/(float)n);
float a = 5.0f/8.0f - (tmp*tmp)/64.0f;
return (n*(1-a)/a);
}
//********************************************
// SubdivisionLoop
// From Piecewise smooth reconstruction (Hoppe)
//********************************************
// (SIGGRAPH 94) -> Charles Loop
//********************************************
int CMesh3d::SubdivisionLoop()
{
// We assume adjacency is built
int NbVertex = m_ArrayVertex.GetSize();
SetFlagOnVertices(0); // for boundaries
// Create subdivision info (edge vertices)
int NbFace = m_ArrayFace.GetSize();
TRACE("Start loop's subdivision (%d faces, %d vertices)\n",NbFace,NbVertex);
TRACE(" subdivision info (%d vertices)\n",m_ArrayVertex.GetSize());
// For each face
for(int i=0;i<NbFace;i++)
{
CFace3d *pFace = m_ArrayFace[i];
// Check valid neighboring
if(!pFace->IsValid())
continue;
// On each edge
for(int IndexEdge=0;IndexEdge<3;IndexEdge++)
{
// Get IndexEdge on neighbor
int IndexCurrent,IndexNeighbor;
CFace3d *pFaceNeighbor = pFace->f(IndexEdge);
// No neighbor on this edge, it is a boundary edge
if(pFaceNeighbor == NULL)
{
// Two vertices involved
CVertex3d *pVertex[2];
pVertex[0] = pFace->v(IndexEdge);
pVertex[1] = pFace->v((IndexEdge+1)%3);
float x = 0.5f*(pVertex[0]->x()+pVertex[1]->x());
float y = 0.5f*(pVertex[0]->y()+pVertex[1]->y());
float z = 0.5f*(pVertex[0]->z()+pVertex[1]->z());
CVertex3d *pNewVertex = new CVertex3d(x,y,z);
m_ArrayVertex.Add(pNewVertex);
pFace->v(3+IndexEdge,pNewVertex);
// Boundary vertices
pVertex[0]->SetFlag(1);
pVertex[1]->SetFlag(1);
}
else // std case
{
VERIFY(pFace->Share2Vertex(pFaceNeighbor,&IndexCurrent,&IndexNeighbor));
ASSERT(IndexCurrent == IndexEdge);
// If neighboring face has been treated, then get vertex
// and go to next step
if(pFaceNeighbor->GetFlag())
{
CVertex3d *pVertex = pFaceNeighbor->v(3+(IndexNeighbor%3));
pFace->v(3+(IndexEdge%3),pVertex);
continue;
}
// Vertex weighting
// 0 & 1 : weight : 3, 2 & 3 : weight : 1
CVertex3d *pVertex[4];
// Weight : 3
pVertex[0] = pFace->v(IndexEdge);
pVertex[1] = pFace->v((IndexEdge+1)%3);
// Weight : 1
pVertex[2] = pFace->v((IndexEdge+2)%3);
pVertex[3] = pFaceNeighbor->v((IndexNeighbor+2)%3);
ASSERT(pVertex[0] != NULL &&
pVertex[1] != NULL &&
pVertex[2] != NULL &&
pVertex[3] != NULL);
// For each composant
float x = (3.0f * (pVertex[0]->x() + pVertex[1]->x()) +
pVertex[2]->x() + pVertex[3]->x()) / 8.0f;
float y = (3.0f * (pVertex[0]->y() + pVertex[1]->y()) +
pVertex[2]->y() + pVertex[3]->y()) / 8.0f;
float z = (3.0f * (pVertex[0]->z() + pVertex[1]->z()) +
pVertex[2]->z() + pVertex[3]->z()) / 8.0f;
// Add vertex to global mesh array, and set face's vertex
CVertex3d *pNewVertex = new CVertex3d(x,y,z);
m_ArrayVertex.Add(pNewVertex);
pFace->v(3+IndexCurrent,pNewVertex);
}
}
// Set flag
pFace->SetFlag(1);
}
//*****************************
// Create faces
//*****************************
TRACE(" creating faces (%d faces)\n",NbFace);
// For each valid face
for(i=0;i<NbFace;i++)
{
CFace3d *pFace = m_ArrayFace[i];
pFace->SetFlag(0);
// Valid face
int NbVertex = pFace->NbVertex();
CFace3d *pNewFace;
switch(NbVertex)
{
case 4:
// Create one face
// On edge 0
if(pFace->v(3) != NULL)
{
pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(1,pFace->v(3));
}
else
// On edge 1
if(pFace->v(4) != NULL)
{
pNewFace = new CFace3d(pFace->v(0),pFace->v(4),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(2,pFace->v(4));
}
else
// On edge 2
if(pFace->v(5) != NULL)
{
pNewFace = new CFace3d(pFace->v(5),pFace->v(1),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(2,pFace->v(5));
}
break;
case 5:
// Create two faces
// On edge 0 & 2
if(pFace->v(3) != NULL &&
pFace->v(5) != NULL)
{
pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(5));
m_ArrayFace.Add(pNewFace);
pNewFace = new CFace3d(pFace->v(5),pFace->v(3),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(0,pFace->v(3));
}
else
// On edge 0 & 1
if(pFace->v(3) != NULL &&
pFace->v(4) != NULL)
{
pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
m_ArrayFace.Add(pNewFace);
pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(4));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(1,pFace->v(4));
}
else
// On edge 1 & 2
if(pFace->v(4) != NULL &&
pFace->v(5) != NULL)
{
pNewFace = new CFace3d(pFace->v(1),pFace->v(4),pFace->v(5));
m_ArrayFace.Add(pNewFace);
pNewFace = new CFace3d(pFace->v(4),pFace->v(2),pFace->v(5));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(2,pFace->v(5));
}
break;
case 6:
// Create three faces
// First (v3,v1,v4)
pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
m_ArrayFace.Add(pNewFace);
// Second (v3,v4,v5)
pNewFace = new CFace3d(pFace->v(3),pFace->v(4),pFace->v(5));
m_ArrayFace.Add(pNewFace);
// Third (v5,v4,v2)
pNewFace = new CFace3d(pFace->v(5),pFace->v(4),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(1,pFace->v(3));
pFace->v(2,pFace->v(5));
break;
}
// Remove subdivision info
for(int k=3;k<6;k++)
pFace->v(k,NULL);
}
TRACE(" end creating faces (%d faces)\n",m_ArrayFace.GetSize());
//*****************************
// Move original vertices
//*****************************
// Copy
TRACE(" copy\n");
CArray3d<CVertex3d> ArrayVertex;
ArrayVertex.SetSize(NbVertex);
for(i=0;i<NbVertex;i++)
ArrayVertex.SetAt(i,new CVertex3d);
// For each vertex (at least 2 neighbors)
for(i=0;i<NbVertex;i++)
{
CVertex3d *pVertex = m_ArrayVertex[i];
int n = pVertex->NbVertexNeighbor();
// Spline on boundaries, we need two vertices
// remind that the adjacency has not been
// updated yet
// weighting: 1 6 1
if(pVertex->GetFlag())
{
CVertex3d *pv[2];
int index = 0;
for(int k=0;k<n;k++)
{
CVertex3d *pNVertex = pVertex->GetVertexNeighbor(k);
if(pNVertex->GetFlag())
pv[index++] = pNVertex;
}
ASSERT(index == 2);
ArrayVertex[i]->Set(0.125f*(6.0f*pVertex->x()+pv[0]->x()+pv[1]->x()),
0.125f*(6.0f*pVertex->y()+pv[0]->y()+pv[1]->y()),
0.125f*(6.0f*pVertex->z()+pv[0]->z()+pv[1]->z()));
}
else
{
float alpha = Alpha(n);
float tmp = alpha + (float)n;
// For each componant
for(unsigned int j=0;j<3;j++)
{
float value = alpha * pVertex->Get(j);
for(int k=0;k<n;k++)
value += pVertex->GetVertexNeighbor(k)->Get(j);
value /= tmp;
ArrayVertex[i]->Set(j,value);
}
}
}
// Restore
TRACE(" restore\n");
for(i=0;i<NbVertex;i++)
for(unsigned int j=0;j<3;j++)
m_ArrayVertex[i]->Set(j,ArrayVertex[i]->Get(j));
ArrayVertex.Free();
// Rebuild adjacency and normals
BuildAdjacency();
CalculateNormalPerFace();
CalculateNormalPerVertex();
m_Modified = 1;
TRACE("End loop's subdivision (%d faces, %d vertices)\n",
m_ArrayFace.GetSize(),m_ArrayVertex.GetSize());
return 1;
}
//********************************************
// Subdivision
// Simple : 1->4
//********************************************
int CMesh3d::Subdivision(void)
{
// We assume adjacency is built
int NbVertex = m_ArrayVertex.GetSize();
// Create subdivision info (edge vertices)
int NbFace = m_ArrayFace.GetSize();
TRACE("Start subdivision (%d faces, %d vertices)\n",NbFace,NbVertex);
TRACE(" subdivision info (%d vertices)\n",m_ArrayVertex.GetSize());
// For each face
for(int i=0;i<NbFace;i++)
{
CFace3d *pFace = m_ArrayFace[i];
// On each edge
for(int IndexEdge=0;IndexEdge<3;IndexEdge++)
{
// Get IndexEdge on neighbor
int IndexCurrent,IndexNeighbor;
CFace3d *pFaceNeighbor = pFace->f(IndexEdge);
// No neighbor on this edge, go to next
if(pFaceNeighbor != NULL)
{
VERIFY(pFace->Share2Vertex(pFaceNeighbor,&IndexCurrent,&IndexNeighbor));
ASSERT(IndexCurrent == IndexEdge);
// If neighboring face has been treated, then get vertex
// and go to next step
if(pFaceNeighbor->GetFlag())
{
CVertex3d *pVertex = pFaceNeighbor->v(3+(IndexNeighbor%3));
pFace->v(3+(IndexEdge%3),pVertex);
continue;
}
}
// Vertex weighting
// 0 & 1 : weight : 1
CVertex3d *pVertex[2];
// Weight : 1
pVertex[0] = pFace->v(IndexEdge);
pVertex[1] = pFace->v((IndexEdge+1)%3);
ASSERT(pVertex[0] != NULL && pVertex[1] != NULL);
// For each composant
float coord[3];
for(int k=0;k<3;k++)
coord[k] = (pVertex[0]->Get(k) + pVertex[1]->Get(k)) / 2.0f;
// Add vertex to global mesh array, and set face's vertex
CVertex3d *pNewVertex = new CVertex3d(coord[0],coord[1],coord[2]);
m_ArrayVertex.Add(pNewVertex);
pFace->v(3+IndexEdge,pNewVertex);
}
// Set flag
pFace->SetFlag(1);
}
//*****************************
// Create faces
//*****************************
TRACE(" creating faces (%d faces)\n",NbFace);
// For each valid face
for(i=0;i<NbFace;i++)
{
CFace3d *pFace = m_ArrayFace[i];
pFace->SetFlag(0);
// Valid face
int NbVertex = pFace->NbVertex();
CFace3d *pNewFace;
switch(NbVertex)
{
case 4:
// Create one face
// On edge 0
if(pFace->v(3) != NULL)
{
pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(1,pFace->v(3));
}
else
// On edge 1
if(pFace->v(4) != NULL)
{
pNewFace = new CFace3d(pFace->v(0),pFace->v(4),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(2,pFace->v(4));
}
else
// On edge 2
if(pFace->v(5) != NULL)
{
pNewFace = new CFace3d(pFace->v(5),pFace->v(1),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(2,pFace->v(5));
}
break;
case 5:
// Create two faces
// On edge 0 & 2
if(pFace->v(3) != NULL &&
pFace->v(5) != NULL)
{
pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(5));
m_ArrayFace.Add(pNewFace);
pNewFace = new CFace3d(pFace->v(5),pFace->v(3),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(0,pFace->v(3));
}
else
// On edge 0 & 1
if(pFace->v(3) != NULL &&
pFace->v(4) != NULL)
{
pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
m_ArrayFace.Add(pNewFace);
pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(4));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(1,pFace->v(4));
}
else
// On edge 1 & 2
if(pFace->v(4) != NULL &&
pFace->v(5) != NULL)
{
pNewFace = new CFace3d(pFace->v(1),pFace->v(4),pFace->v(5));
m_ArrayFace.Add(pNewFace);
pNewFace = new CFace3d(pFace->v(4),pFace->v(2),pFace->v(5));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(2,pFace->v(5));
}
break;
case 6:
// Create three faces
// First (v3,v1,v4)
pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
m_ArrayFace.Add(pNewFace);
// Second (v3,v4,v5)
pNewFace = new CFace3d(pFace->v(3),pFace->v(4),pFace->v(5));
m_ArrayFace.Add(pNewFace);
// Third (v5,v4,v2)
pNewFace = new CFace3d(pFace->v(5),pFace->v(4),pFace->v(2));
m_ArrayFace.Add(pNewFace);
// Move current face
pFace->v(1,pFace->v(3));
pFace->v(2,pFace->v(5));
break;
}
// Remove subdivision info
for(int k=3;k<6;k++)
pFace->v(k,NULL);
}
TRACE(" end creating faces (%d faces)\n",m_ArrayFace.GetSize());
// Rebuild adjacency and normals
BuildAdjacency();
CalculateNormalPerFace();
CalculateNormalPerVertex();
m_Modified = 1;
TRACE("End subdivision (%d faces, %d vertices)\n",
m_ArrayFace.GetSize(),m_ArrayVertex.GetSize());
return 1;
}
//********************************************
// Smooth
// 30/09/98
//********************************************
int CMesh3d::Smooth(int MoveOnBundary /* = 1 */)
{
// We assume adjacency is built
int NbVertex = m_ArrayVertex.GetSize();
// Create subdivision info (edge vertices)
int NbFace = m_ArrayFace.GetSize();
TRACE("Start smoothing (%d faces, %d vertices)",NbFace,NbVertex);
//*****************************
// Move original vertices
//*****************************
// Copy
TRACE(".");
CArray3d<CVertex3d> ArrayVertex;
ArrayVertex.SetSize(NbVertex);
for(int i=0;i<NbVertex;i++)
ArrayVertex.SetAt(i,new CVertex3d);
// For each vertex (at least 3 neighbors)
for(i=0;i<NbVertex;i++)
{
CVertex3d *pVertex = m_ArrayVertex[i];
if(!MoveOnBundary)
if(pVertex->IsOnBoundary())
{
ArrayVertex[i]->Set(pVertex);
continue;
}
int n = pVertex->NbVertexNeighbor();
float alpha = Alpha(n);
float tmp = alpha + (float)n;
// For each composant
for(unsigned int j=0;j<3;j++)
{
float value = alpha * pVertex->Get(j);
for(int k=0;k<n;k++)
value += pVertex->GetVertexNeighbor(k)->Get(j);
value /= tmp;
ArrayVertex[i]->Set(j,value);
}
}
// Restore
TRACE(".");
for(i=0;i<NbVertex;i++)
for(unsigned int j=0;j<3;j++)
m_ArrayVertex[i]->Set(j,ArrayVertex[i]->Get(j));
// Cleanup
TRACE(".");
ArrayVertex.Free();
TRACE("ok\n");
// Rebuild adjacency and normals
BuildAdjacency();
CalculateNormalPerFace();
CalculateNormalPerVertex();
m_Modified = 1;
return 1;
}
//********************************************
// ColorCurvature
// Each face is colored, function of mean curvature
//********************************************
void CMesh3d::ColorCurvature(CColorRamp *pRamp)
{
TRACE("Start coloring mesh (curvature)\n");
int NbVertex = m_ArrayVertex.GetSize();
TRACE(" %d vertices\n",NbVertex);
double *pMax = new double[NbVertex];
// Store curvatures
for(int i=0;i<NbVertex;i++)
pMax[i] = m_ArrayVertex[i]->GetMaxAngleAround();
// Process extremas
double min = MAX_DOUBLE;
double max = 0.0f;
for(i=0;i<NbVertex;i++)
{
min = (pMax[i] < min) ? pMax[i] : min;
max = (pMax[i] > max) ? pMax[i] : max;
}
min = (min < 0.0f) ? 0.0f : min;
double amplitude = max-min;//max-min;
TRACE(" min : %g\n",min);
TRACE(" max : %g\n",max);
TRACE(" amplitude : %g\n",amplitude);
for(i=0;i<NbVertex;i++)
{
unsigned char _grey = (unsigned char)((pMax[i]-min)/amplitude * 255.0f);
unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
m_ArrayVertex[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
}
SetModified();
TRACE("End coloring mesh (curvature)\n");
}
//********************************************
// ColorSpaceNormal
// Each vertex is colored, function of
// normal space (sum of angles between
// adjacent faces)
//********************************************
void CMesh3d::ColorNormalSpace(CColorRamp *pRamp)
{
TRACE("Start coloring mesh (space of normals)\n");
int NbVertex = m_ArrayVertex.GetSize();
TRACE(" %d vertices\n",NbVertex);
double *pSum = new double[NbVertex];
// Store curvatures
for(int i=0;i<NbVertex;i++)
m_ArrayVertex[i]->NormalMax(&pSum[i]);
// Process extremas
double min = MAX_DOUBLE;
double max = 0.0f;
for(i=0;i<NbVertex;i++)
{
min = (pSum[i] < min) ? pSum[i] : min;
max = (pSum[i] > max) ? pSum[i] : max;
}
min = (min < 0.0f) ? 0.0f : min;
double amplitude = max-min;//max-min;
TRACE(" min : %g\n",min);
TRACE(" max : %g\n",max);
TRACE(" amplitude : %g\n",amplitude);
for(i=0;i<NbVertex;i++)
{
unsigned char _grey = (unsigned char)((pSum[i]-min)/amplitude * 255.0f);
unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
//unsigned char grey = (unsigned char)((pCurvature[i]-min)/amplitude * 255.0f);
m_ArrayVertex[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
}
SetModified();
delete [] pSum;
TRACE("End coloring mesh (space of normals)\n");
}
//********************************************
// ColorCompacity
// Each face is colored, function of face
// compacity
//********************************************
void CMesh3d::ColorCompacity(CColorRamp *pRamp)
{
TRACE("Start coloring mesh (compacity)\n");
int NbFace = m_ArrayFace.GetSize();
TRACE(" %d face(s)\n",NbFace);
double *pCompacity = new double[NbFace];
// Store compacity
for(int i=0;i<NbFace;i++)
pCompacity[i] = m_ArrayFace[i]->Compacity();
// Process extremas
double min = MAX_DOUBLE;
double max = 0.0f;
for(i=0;i<NbFace;i++)
{
min = (pCompacity[i] < min) ? pCompacity[i] : min;
max = (pCompacity[i] > max) ? pCompacity[i] : max;
}
double amplitude = max-min;//max-min;
TRACE(" min : %g\n",min);
TRACE(" max : %g\n",max);
TRACE(" amplitude : %g\n",amplitude);
for(i=0;i<NbFace;i++)
{
unsigned char _grey = (unsigned char)((pCompacity[i]-min)/amplitude * 255.0f);
unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
//unsigned char grey = (unsigned char)((pCurvature[i]-min)/amplitude * 255.0f);
m_ArrayFace[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
}
SetModified();
delete [] pCompacity;
TRACE("End coloring mesh (compacity)\n");
}
//********************************************
// ColorHeight
// Each vertex is colored, function of height
//********************************************
void CMesh3d::ColorHeight(CColorRamp *pRamp)
{
// Color vertices
int NbVertex = m_ArrayVertex.GetSize();
double min = MAX_DOUBLE;
double max = 0.0f;
for(int i=0;i<NbVertex;i++)
{
float height = m_ArrayVertex[i]->y();
min = (height < min) ? height : min;
max = (height > max) ? height : max;
}
double amplitude = max-min;
for(i=0;i<NbVertex;i++)
{
float height = m_ArrayVertex[i]->y();
unsigned char _grey = (unsigned char)((height-min)/amplitude * 255.0f);
unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
m_ArrayVertex[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
}
// Color faces
int NbFace = m_ArrayFace.GetSize();
TRACE(" %d faces\n",NbFace);
for(i=0;i<NbFace;i++)
{
float height = (m_ArrayFace[i]->v1()->y()+
m_ArrayFace[i]->v1()->y()+
m_ArrayFace[i]->v1()->y())/3.0f;
unsigned char _grey = (unsigned char)((height-min)/amplitude * 255.0f);
unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
m_ArrayFace[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
}
SetModified();
}
//////////////////////////////////////////////
// PREDEFINED
//////////////////////////////////////////////
//********************************************
// GenerateBox
//********************************************
int CMesh3d::GenerateBox(float dx,
float dy,
float dz)
{
TRACE("Generate box...");
CVertex3d *pVertex;
pVertex = new CVertex3d(-dx/2,-dy/2,-dz/2);
m_ArrayVertex.Add(pVertex);
pVertex = new CVertex3d(-dx/2,+dy/2,-dz/2);
m_ArrayVertex.Add(pVertex);
pVertex = new CVertex3d(+dx/2,+dy/2,-dz/2);
m_ArrayVertex.Add(pVertex);
pVertex = new CVertex3d(+dx/2,-dy/2,-dz/2);
m_ArrayVertex.Add(pVertex);
pVertex = new CVertex3d(-dx/2,-dy/2,+dz/2);
m_ArrayVertex.Add(pVertex);
pVertex = new CVertex3d(-dx/2,+dy/2,+dz/2);
m_ArrayVertex.Add(pVertex);
pVertex = new CVertex3d(+dx/2,+dy/2,+dz/2);
m_ArrayVertex.Add(pVertex);
pVertex = new CVertex3d(+dx/2,-dy/2,+dz/2);
m_ArrayVertex.Add(pVertex);
CFace3d *pFace;
pFace = new CFace3d(m_ArrayVertex[0],
m_ArrayVertex[1],
m_ArrayVertex[3]);
pFace->SetNormal(0.0f,0.0f,-1.0f);
m_ArrayFace.Add(pFace);
pFace = new CFace3d(m_ArrayVertex[3],
m_ArrayVertex[1],
m_ArrayVertex[2]);
pFace->SetNormal(0.0f,0.0f,-1.0f);
m_ArrayFace.Add(pFace);
pFace = new CFace3d(m_ArrayVertex[0],
m_ArrayVertex[4],
m_ArrayVertex[1]);
pFace->SetNormal(-1.0f,0.0f,0.0f);
m_ArrayFace.Add(pFace);
pFace = new CFace3d(m_ArrayVertex[1],
m_ArrayVertex[4],
m_ArrayVertex[5]);
pFace->SetNormal(-1.0f,0.0f,0.0f);
m_ArrayFace.Add(pFace);
pFace = new CFace3d(m_ArrayVertex[3],
m_ArrayVertex[2],
m_ArrayVertex[7]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(1.0f,0.0f,0.0f);
pFace = new CFace3d(m_ArrayVertex[7],
m_ArrayVertex[2],
m_ArrayVertex[6]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(1.0f,0.0f,0.0f);
pFace = new CFace3d(m_ArrayVertex[4],
m_ArrayVertex[0],
m_ArrayVertex[3]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(0.0f,-1.0f,0.0f);
pFace = new CFace3d(m_ArrayVertex[7],
m_ArrayVertex[4],
m_ArrayVertex[3]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(0.0f,-1.0f,0.0f);
pFace = new CFace3d(m_ArrayVertex[6],
m_ArrayVertex[4],
m_ArrayVertex[7]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(0.0f,0.0f,1.0f);
pFace = new CFace3d(m_ArrayVertex[6],
m_ArrayVertex[5],
m_ArrayVertex[4]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(0.0f,0.0f,1.0f);
pFace = new CFace3d(m_ArrayVertex[1],
m_ArrayVertex[5],
m_ArrayVertex[6]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(0.0f,1.0f,0.0f);
pFace = new CFace3d(m_ArrayVertex[2],
m_ArrayVertex[1],
m_ArrayVertex[6]);
m_ArrayFace.Add(pFace);
pFace->SetNormal(0.0f,1.0f,0.0f);
TRACE("ok\n");
return 1;
}
//********************************************
// GenerateMap
//********************************************
int CMesh3d::GenerateMap(int line,
int col,
float min,
float max)
{
TRACE("Generate map...");
float x,y,z;
int i,j;
// Set vertices
for(i=0;i<col;i++)
for(j=0;j<line;j++)
{
x = min + ((float)i/(float)line)*(max-min);
z = min + ((float)j/(float)line)*(max-min);
y = (float)(cos(x)*cos(z));
m_ArrayVertex.Add(new CVertex3d(x,y,z));
}
// Set faces
for(i=0;i<col-1;i++)
for(j=0;j<line-1;j++)
{
CVertex3d *pVertex1 = m_ArrayVertex[line*i+j];
CVertex3d *pVertex2 = m_ArrayVertex[line*i+j+1];
CVertex3d *pVertex3 = m_ArrayVertex[line*(i+1)+j+1];
CVertex3d *pVertex4 = m_ArrayVertex[line*(i+1)+j];
m_ArrayFace.Add(new CFace3d(pVertex1,pVertex2,pVertex3));
m_ArrayFace.Add(new CFace3d(pVertex1,pVertex3,pVertex4));
}
TRACE("ok\n");
return 1;
}
//********************************************
// GenerateMapFromImage
//********************************************
int CMesh3d::GenerateMap(CTexture *pTexture,
int width,
int height,
int FlagColor /* = 1 */)
{
// Cleanup
Free();
int WidthImage = pTexture->GetWidth();
int HeightImage = pTexture->GetHeight();
unsigned char red,green,blue;
// Vertices
for(int j=0;j<height;j++)
for(int i=0;i<width;i++)
{
int xImage = (int)((float)i/(float)width*(float)WidthImage);
int yImage = (int)((float)j/(float)height*(float)HeightImage);
int index = m_ArrayVertex.Add(new CVertex3d((float)i,(float)pTexture->Grey(xImage,yImage),(float)j));
if(FlagColor)
{
pTexture->Color(xImage,yImage,&red,&green,&blue);
m_ArrayVertex[index]->SetColor(red,green,blue);
}
}
// Faces
for(j=0;j<height-1;j++)
for(int i=0;i<width-1;i++)
{
int index = m_ArrayFace.Add(new CFace3d(m_ArrayVertex[j*width+i+1],
m_ArrayVertex[j*width+i],
m_ArrayVertex[(j+1)*width+i+1]));
m_ArrayFace.Add(new CFace3d(m_ArrayVertex[(j+1)*width+i+1],
m_ArrayVertex[j*width+i],
m_ArrayVertex[(j+1)*width+i]));
if(FlagColor)
{
m_ArrayFace[index]->SetColor(*m_ArrayVertex[j*width+i]->GetColor());
m_ArrayFace[index+1]->SetColor(*m_ArrayVertex[j*width+i+1]->GetColor());
}
}
// Rebuild
BuildAdjacency();
CalculateNormalPerFace();
CalculateNormalPerVertex();
return 1;
}
/////////////////////////////////////////////
// INTERSECTION
/////////////////////////////////////////////
//********************************************
// NearestIntersectionWithLine
// Non-optimized
// Nearest -> distance from pV0 to pVertexResult
//********************************************
int CMesh3d::NearestIntersectionWithLine(CVertex3d *pV0,
CVertex3d *pV1,
CVertex3d *pVertexResult,
CFace3d **ppFaceResult,
int *pNbFaceVisited)
{
return ::NearestIntersectionWithLine(&m_ArrayFace,pV0,pV1,pVertexResult,ppFaceResult,pNbFaceVisited);
}
/////////////////////////////////////////////
// I/O
/////////////////////////////////////////////
//********************************************
// WriteFile
//********************************************
int CMesh3d::WriteFile(CStdioFile &file)
{
CString string;
TRY
{
// Comment
string.Format("# Mesh : %d vertices, %d faces\n",NbVertex(),NbFace());
file.WriteString(string);
// First line
file.WriteString("DEF Mesh-ROOT Transform {\n");
// Transform
string.Format(" translation %g %g %g\n",m_Transform.GetTranslation()->x(),
m_Transform.GetTranslation()->y(),
m_Transform.GetTranslation()->z());
file.WriteString(string);
string.Format(" rotation %g %g %g %g\n",m_Transform.GetRotation()->x(),
m_Transform.GetRotation()->y(),
m_Transform.GetRotation()->z(),
m_Transform.GetValueRotation()/360.0f*2*3.14159265359f);
file.WriteString(string);
string.Format(" scale %g %g %g\n",m_Transform.GetScale()->x(),
m_Transform.GetScale()->y(),
m_Transform.GetScale()->z());
file.WriteString(string);
// Material
file.WriteString(" children [\n");
file.WriteString(" Shape {\n");
file.WriteString(" appearance Appearance {\n");
file.WriteString(" material Material {\n");
file.WriteString(" diffuseColor 0 0 0\n"); // todo
file.WriteString(" }\n");
file.WriteString(" }\n");
// Geometry
file.WriteString(" geometry DEF Mesh-FACES IndexedFaceSet {\n");
file.WriteString(" ccw TRUE\n");
file.WriteString(" solid TRUE\n");
// Vertices
file.WriteString(" coord DEF Mesh-COORD Coordinate { point [\n");
int NbVertex = m_ArrayVertex.GetSize();
for(int i=0;i<NbVertex;i++)
{
string.Format(" %g %g %g",m_ArrayVertex[i]->x(),
m_ArrayVertex[i]->y(),
m_ArrayVertex[i]->z());
file.WriteString(string);
if(i!=(NbVertex-1))
file.WriteString(",\n");
else
file.WriteString("]\n");
}
file.WriteString(" }\n");
// Faces
file.WriteString(" coordIndex [\n");
int NbFace = m_ArrayFace.GetSize();
for(i=0;i<NbFace;i++)
{
string.Format(" %d, %d, %d, -1",m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v(0)),
m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v(1)),
m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v(2)));
file.WriteString(string);
if(i!=(NbFace-1))
file.WriteString(",\n");
else
file.WriteString("]\n");
}
// End
file.WriteString(" }\n");
file.WriteString(" }\n");
file.WriteString(" ]\n");
file.WriteString(" }\n\n");
}
CATCH(CFileException, e)
{
#ifdef _DEBUG
afxDump << "Error during writing transform" << e->m_cause << "\n";
#endif
AfxMessageBox("Error during writing transform");
return 0;
}
END_CATCH
return 1;
}
//********************************************
// WriteFileRaw (binary raw mode)
//********************************************
int CMesh3d::WriteFileRaw(CFile &file)
{
// A mesh :
//*******************************************
// Transform : 10 * float 32 bits
// NbVertices : UINT 32 bits
// NbFaces : UINT 32 bits
// Vertices : x y z : 3 x float 32 bits
// Faces : v1 v2 v3 : 3 x UINT 32 bits
//*******************************************
// Cost : 40 + 8 + 12*(v+f) bytes
CString string;
TRY
{
// Transform
// Translation (xyz)
float x,y,z;
x = m_Transform.GetTranslation()->x();
y = m_Transform.GetTranslation()->y();
z = m_Transform.GetTranslation()->z();
file.Write(&x,sizeof(float));
file.Write(&y,sizeof(float));
file.Write(&z,sizeof(float));
// Rotation (xyz)
x = m_Transform.GetRotation()->x();
y = m_Transform.GetRotation()->y();
z = m_Transform.GetRotation()->z();
float v = m_Transform.GetValueRotation();
file.Write(&x,sizeof(float));
file.Write(&y,sizeof(float));
file.Write(&z,sizeof(float));
file.Write(&v,sizeof(float));
// Scale (xyz)
x = m_Transform.GetScale()->x();
y = m_Transform.GetScale()->y();
z = m_Transform.GetScale()->z();
file.Write(&x,sizeof(float));
file.Write(&y,sizeof(float));
file.Write(&z,sizeof(float));
// Geometry
// NbVertices
// NbFaces
unsigned int NbVertex = m_ArrayVertex.GetSize();
unsigned int NbFace = m_ArrayFace.GetSize();
file.Write(&NbVertex,sizeof(unsigned int));
file.Write(&NbFace,sizeof(unsigned int));
// Vertices
for(unsigned int i=0;i<NbVertex;i++)
{
x = m_ArrayVertex[i]->x();
y = m_ArrayVertex[i]->y();
z = m_ArrayVertex[i]->z();
file.Write(&x,sizeof(float));
file.Write(&y,sizeof(float));
file.Write(&z,sizeof(float));
}
// Faces
unsigned int v1,v2,v3;
for(i=0;i<NbFace;i++)
{
v1 = m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v1());
v2 = m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v2());
v3 = m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v3());
file.Write(&v1,sizeof(unsigned int));
file.Write(&v2,sizeof(unsigned int));
file.Write(&v3,sizeof(unsigned int));
}
}
CATCH(CFileException, e)
{
#ifdef _DEBUG
afxDump << "Error during writing " << e->m_cause << "\n";
#endif
AfxMessageBox("Error during writing");
return 0;
}
END_CATCH
return 1;
}
//********************************************
// GetMinArea
//********************************************
double CMesh3d::GetMinArea(CFace3d **ppFace /* NULL */)
{
double min = MAX_DOUBLE;
int size = m_ArrayFace.GetSize();
for(int i=0;i<size;i++)
{
double area = m_ArrayFace[i]->Area();
if(area < min)
{
min = area;
if(ppFace != NULL)
*ppFace = m_ArrayFace[i];
}
}
return min;
}
//********************************************
// GetMeanArea
//********************************************
double CMesh3d::GetMeanArea()
{
return ::GetMeanArea(&m_ArrayFace);
}
//*********************************
// GenerateEdgeArray
//*********************************
int CMesh3d::GenerateEdgeArray(CArray3d<CEdge3d> *pArrayEdge,
BOOL FlagOnBoundary,
char flag,
BOOL *pHasBoundary)
{
ASSERT(pArrayEdge != NULL);
int NbVertex = m_ArrayVertex.GetSize();
// Set flags
SetFlagOnVertices(0);
TRACE(" begin generate edge array\n");
TRACE(" %d vertices\n",NbVertex);
TRACE(" %d faces\n",NbFace());
int NbEdgePrevious = NbVertex+NbFace();
TRACE(" %d edges previous\n",NbEdgePrevious);
pArrayEdge->SetSize(NbEdgePrevious);
int NbEdge = 0;
TRACE(" begin...");
for(int i=0;i<NbVertex;i++)
{
CVertex3d *pVertex = m_ArrayVertex[i];
int NbVertexNeighbor = pVertex->NbVertexNeighbor();
for(int j=0;j<NbVertexNeighbor;j++)
{
CVertex3d *pNeighbor = pVertex->GetVertexNeighbor(j);
ASSERT(pNeighbor != NULL);
if(pNeighbor->GetFlag() == 0)
{
// Alloc
CEdge3d *pEdge = new CEdge3d(pVertex,pNeighbor);
// Set faces (at least one)
CArray3d<CFace3d> array;
pVertex->FindFaceAroundContainVertex(pNeighbor,array);
ASSERT(array.GetSize() >= 1);
pEdge->f1(array[0]);
if(array.GetSize() > 1)
pEdge->f2(array[1]);
if(NbEdge<NbEdgePrevious)
pArrayEdge->SetAt(NbEdge,pEdge);
else
pArrayEdge->Add(pEdge);
NbEdge++;
// Flag (optional)
if(FlagOnBoundary)
if(pVertex->IsOnBoundary() && pNeighbor->IsOnBoundary())
{
*pHasBoundary = TRUE;
pEdge->SetFlag(flag);
}
}
}
pVertex->SetFlag(1); // done
//if(i%(NbVertex/30)==0) { TRACE("."); }
}
TRACE("ok\n");
pArrayEdge->SetSize(NbEdge);
TRACE(" %d edges\n",NbEdge);
return 1;
}
//********************************************
// glDrawProjectLine
// paint the current mesh via projection
// in line mode
//********************************************
void CMesh3d::glDrawProjectLine(CDC *pDC,
double *modelMatrix,
double *projMatrix,
int *viewport,
COLORREF ColorLine,
double ratio,
int height)
{
TRACE("Draw projected mesh in metafile-based device context\n");
TRACE(" line mode\n");
TRACE(" viewport : (%d;%d;%d;%d)\n",viewport[0],viewport[1],viewport[2],viewport[3]);
TRACE(" model : %g\t%g\t%g\n",modelMatrix[0],modelMatrix[1],modelMatrix[2]);
TRACE(" %g\t%g\t%g\n",modelMatrix[3],modelMatrix[4],modelMatrix[5]);
TRACE(" %g\t%g\t%g\n",modelMatrix[6],modelMatrix[7],modelMatrix[8]);
TRACE(" proj : %g\t%g\t%g\n",projMatrix[0],projMatrix[1],projMatrix[2]);
TRACE(" %g\t%g\t%g\n",projMatrix[3],projMatrix[4],projMatrix[5]);
TRACE(" %g\t%g\t%g\n",projMatrix[6],projMatrix[7],projMatrix[8]);
// Generate edge array (it saves memory)
CArray3d<CEdge3d> ArrayEdge;
GenerateEdgeArray(&ArrayEdge);
// Select pen
CPen pen(PS_SOLID,0,ColorLine);
CPen *pOldPen = pDC->SelectObject(&pen);
double x1,y1,x2,y2,z;
for(int i=0;i<ArrayEdge.GetSize();i++)
{
CEdge3d *pEdge = ArrayEdge[i];
ASSERT(pEdge != NULL);
gluProject((double)pEdge->v1()->x(),
(double)pEdge->v1()->y(),
(double)pEdge->v1()->z(),
modelMatrix,
projMatrix,
viewport,&x1,&y1,&z);
gluProject((double)pEdge->v2()->x(),
(double)pEdge->v2()->y(),
(double)pEdge->v2()->z(),
modelMatrix,
projMatrix,
viewport,&x2,&y2,&z);
// Crop to window
if(x1 < viewport[0] || y1 < viewport[1] ||
x1 > viewport[2] || y1 > viewport[3] ||
x2 < viewport[0] || y2 < viewport[1] ||
x2 > viewport[2] || y2 > viewport[3])
continue; // crop to window
else // draw
{
pDC->MoveTo((int)(ratio*x1),(int)(ratio*((float)height-y1)));
pDC->LineTo((int)(ratio*x2),(int)(ratio*((float)height-y2)));
}
}
// Cleanup
ArrayEdge.Free();
pDC->SelectObject(pOldPen);
}
//********************************************
// glDrawProjectFace
//********************************************
void CMesh3d::glDrawProjectFace(CDC *pDC,
double *modelMatrix,
double *projMatrix,
int *viewport,
COLORREF ColorLine,
COLORREF ColorFace,
double ratio,
int height, // window height
float RatioNbFace) // default -> 1.0
{
TRACE("Draw projected mesh in metafile-based device context\n");
TRACE(" face mode\n");
TRACE(" viewport : (%d;%d;%d;%d)\n",viewport[0],viewport[1],viewport[2],viewport[3]);
TRACE(" model : %g\t%g\t%g\n",modelMatrix[0],modelMatrix[1],modelMatrix[2]);
TRACE(" %g\t%g\t%g\n",modelMatrix[3],modelMatrix[4],modelMatrix[5]);
TRACE(" %g\t%g\t%g\n",modelMatrix[6],modelMatrix[7],modelMatrix[8]);
TRACE(" proj : %g\t%g\t%g\n",projMatrix[0],projMatrix[1],projMatrix[2]);
TRACE(" %g\t%g\t%g\n",projMatrix[3],projMatrix[4],projMatrix[5]);
TRACE(" %g\t%g\t%g\n",projMatrix[6],projMatrix[7],projMatrix[8]);
CWmfFace *pArray = new CWmfFace[m_ArrayFace.GetSize()];
// Many thanks to Gaspard Breton for having
// implemented the AVL fast z-sorting part.
ASSERT(pArray);
CAVL<CWmfFace,double> avl;
CWmfFace bidon;
avl.Register(&bidon,&bidon.zc,&bidon.avl); // z as key
int NbFaces = m_ArrayFace.GetSize();
TRACE(" %d faces\n",NbFaces);
int NbFacesToProcess = (int)(RatioNbFace*(float)NbFaces);
TRACE(" %d faces to process\n",NbFacesToProcess);
TRACE(" begin sort...");
int NbFaceValid = 0;
for(int i=0;i<NbFaces;i++)
{
CFace3d *pFace = m_ArrayFace[i];
// Compute barycenter as z-reference
// Sorting by a triangle average depth does not allow
// to disambiguate some cases. Handling these cases would
// require breaking up the primitives. Please mail any
// improvement about this
double xc = (pFace->v1()->x()+pFace->v2()->x()+pFace->v3()->x())/3;
double yc = (pFace->v1()->y()+pFace->v2()->y()+pFace->v3()->y())/3;
double zc = (pFace->v1()->z()+pFace->v2()->z()+pFace->v3()->z())/3;
// Project center
gluProject(xc,yc,zc,
modelMatrix,
projMatrix,
viewport,&pArray[i].xc,&pArray[i].yc,&pArray[i].zc);
// Project three vertices
gluProject((double)pFace->v1()->x(),
(double)pFace->v1()->y(),
(double)pFace->v1()->z(),
modelMatrix,
projMatrix,
viewport,&pArray[i].x1,&pArray[i].y1,&pArray[i].z1);
gluProject((double)pFace->v2()->x(),
(double)pFace->v2()->y(),
(double)pFace->v2()->z(),
modelMatrix,
projMatrix,
viewport,&pArray[i].x2,&pArray[i].y2,&pArray[i].z2);
gluProject((double)pFace->v3()->x(),
(double)pFace->v3()->y(),
(double)pFace->v3()->z(),
modelMatrix,
projMatrix,
viewport,&pArray[i].x3,&pArray[i].y3,&pArray[i].z3);
// Crop & sort
if(pArray[i].x1 < viewport[0] || pArray[i].y1 < viewport[1] ||
pArray[i].x1 > viewport[2] || pArray[i].y1 > viewport[3] ||
pArray[i].x2 < viewport[0] || pArray[i].y2 < viewport[1] ||
pArray[i].x2 > viewport[2] || pArray[i].y2 > viewport[3] ||
pArray[i].x3 < viewport[0] || pArray[i].y3 < viewport[1] ||
pArray[i].x3 > viewport[2] || pArray[i].y3 > viewport[3])
continue;
else
{
pArray[i].m_Draw = 1; // yes, insert this triangle
pArray[i].zc *= -1.0f; // back to front
avl.Insert(pArray,i); // insert via sort
NbFaceValid++;
}
}
TRACE("ok\n");
// Draw
CPen pen(PS_SOLID,0,ColorLine);
CBrush BrushFace(ColorFace);
CPen *pOldPen = pDC->SelectObject(&pen);
POINT points[3]; // triangular faces only
// Default
CBrush *pOldBrush = pDC->SelectObject(&BrushFace);
TRACE("begin draw...");
int nb = 0;
for(i=avl.GetFirst(pArray);
(AVLNULL != i) && nb < NbFacesToProcess;
i=avl.GetNext(pArray),nb++)
{
// Fill and outline the face
points[0].x = (int)(ratio*pArray[i].x1);
points[0].y = (int)(ratio*((float)height-pArray[i].y1));
points[1].x = (int)(ratio*pArray[i].x2);
points[1].y = (int)(ratio*((float)height-pArray[i].y2));
points[2].x = (int)(ratio*pArray[i].x3);
points[2].y = (int)(ratio*((float)height-pArray[i].y3));
// Fill triangle
pDC->Polygon(points,3);
// Outline triangle
pDC->MoveTo(points[0]);
pDC->LineTo(points[1]);
pDC->LineTo(points[2]);
pDC->LineTo(points[0]);
}
TRACE("ok\n");
// Restore and cleanup
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
delete [] pArray;
}
// ** EOF **