первый

This commit is contained in:
2024-11-01 12:23:13 +05:00
parent 801d9d33fa
commit 0688c46a7e
226 changed files with 162921 additions and 0 deletions

579
lib/object3d.cpp Normal file
View File

@ -0,0 +1,579 @@
//#pragma hdrstop
#include <GL/glew.h> //Должен стоять до "wx/gl...h"
#include <GL/glu.h>
#include <wx/glcanvas.h>
#include <wx/string.h>
#include "object3d.h"
#include "texture.h"
#include "mathTools.h"
//#include "tools/debug.h"
//---------------------------------------------------------------------------
TTriMat::TTriMat()
{
textureid=0;
ColorAmbientRGB.r=0.2f; // 0.2 по умолчанию в OpenGL
ColorAmbientRGB.g=0.2f;
ColorAmbientRGB.b=0.2f;
ColorAmbientRGB.a=1.0f;
ColorDiffuseRGB.r=0.8f; // 0.8 по умолчанию в OpenGL
ColorDiffuseRGB.g=0.8f;
ColorDiffuseRGB.b=0.8f;
ColorDiffuseRGB.a=1.0f;
ColorSpecularRGB.r=0.0f; // 0.0 по умолчанию в OpenGL
ColorSpecularRGB.g=0.0f;
ColorSpecularRGB.b=0.0f;
ColorSpecularRGB.a=1.0f;
USCALE=1.0f;
VSCALE=1.0f;
}
//---------------------------------------------------------------------------
void TTriMat::Release()
{
//прозрачность
glDisable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float*)&ColorAmbientRGB);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float*)&ColorDiffuseRGB);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float*)&ColorSpecularRGB);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0f); //яркость источника цвета от 0 до 128
//если текстурированна
if(textureid!=0)
{
glBindTexture(GL_TEXTURE_2D, textureid);
glEnable(GL_TEXTURE_2D);
//чтобы цвет не воздействовал на текстуру если она есть
glEnable(GL_COLOR_MATERIAL);
glColor4f(1,1,1,ColorDiffuseRGB.a);
glDisable(GL_COLOR_MATERIAL);
} else glDisable(GL_TEXTURE_2D);
}
//---------------------------------------------------------------------------
TTriangles::TTriangles()
{
del=false;
bSmooth=false;
CountVertex=0;
CountFaces=0;
//countMapPoint=0;
Vertices=NULL;
SmoothNormals=NULL;
faces=NULL;
TexVertices=NULL;
SmoothG=NULL;
bInit=false;
//разбивка по материалам
countMF=0;
MatFaces=NULL;
//материал (после разбивки)
TriMat=NULL;
}
//------------------------------------------------------------------------------
TTriangles::~TTriangles()
{
if (Vertices!=NULL) delete[] Vertices;
Vertices=NULL;
if (SmoothNormals!=NULL) delete[] SmoothNormals;
SmoothNormals=NULL;
if (faces!=NULL) delete[] faces;
faces=NULL;
if (TexVertices!=NULL) delete[] TexVertices;
TexVertices=NULL;
if (SmoothG!=NULL) delete[] SmoothG;
SmoothG=NULL;
if (bInit)
{
glDeleteBuffersARB(1,&vbov);
glDeleteBuffersARB(1,&vbot);
glDeleteBuffersARB(1,&vbon);
glDeleteBuffersARB(1,&vbof);
}
for(unsigned int i=0;i<countMF;i++)
{
delete[] MatFaces[i].name;
delete[] MatFaces[i].faces;
}
if(MatFaces!=NULL) delete[] MatFaces;
MatFaces=NULL;
}
//------------------------------------------------------------------------------
//TODO предусмотреть загрузку в видео память как 1го объекта так и группы (если группой то здесь сохраняется указатель и смещение к нужному объекту)
void TTriangles::render()
{
if(TexVertices==NULL) TexVertices = new RfPointXY[CountVertex];
//if(Material!=NULL) Material.Release();
if(!bSmooth) glShadeModel(GL_FLAT); else glShadeModel(GL_SMOOTH);
//передаём указатели на массивы и рендерим
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
/*RdPointXYZ *d=new RdPointXYZ[CountVertex];
for(unsigned int i=0;i<CountVertex;i++)
{
d[i].x=Vertices[i].x;
d[i].y=Vertices[i].y;
d[i].z=Vertices[i].z;
}
glVertexPointer(3,GL_DOUBLE,0,&d[0]);*/
glVertexPointer(3,GL_FLOAT,0,&Vertices[0]);
glNormalPointer(GL_FLOAT, 0, &SmoothNormals[0]);
glTexCoordPointer(2,GL_FLOAT, 0, &TexVertices[0]);
glDrawElements(GL_TRIANGLES, CountFaces*3, GL_UNSIGNED_SHORT, &faces[0]);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//delete[] d;
/**/
/*if (!bInit)
{
//огромные переделки:
//однотипные обьекты должны храниться в одном vbov значит рендеринг должен быть преобразован в другой вид а именно
//с начала идет цикл по всем созданным VBO он биндиться потом цикл по моделям(смещениям) в этом VBO здесь применяеться механизм отсечения потом
//потом применяеться подходящяя текстура потом собственно glDrawElements
glGenBuffersARB(1,&vbov);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, vbov );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, VertexCount*sizeof(RfPointXYZ), &fPointXYZ[0], GL_STATIC_DRAW_ARB);
glGenBuffersARB(1,&vbot);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, vbot );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, VertexCount*sizeof(RfPointXY), &fMapPointXY[0], GL_STATIC_DRAW_ARB );
glGenBuffersARB(1,&vbon);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, vbon );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, VertexCount*sizeof(RfPointXYZ), &fNormalXYZ[0], GL_STATIC_DRAW_ARB );
glGenBuffersARB(1,&vbof);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vbof);
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, p_object->faces*sizeof(RsFacesABC), &face[0], GL_STATIC_DRAW_ARB);
bInit=true;
}
// vbov,vbon,vbot,vbof :TGLuint; //вершины,нормали,текстура,рёбра
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState( GL_NORMAL_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbov);
glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL );
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbot);
glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL );
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbon);
glNormalPointer( GL_FLOAT, 0, (char *) NULL );
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vbof);
glDrawElements(GL_TRIANGLES, p_object->faces*3 ,GL_UNSIGNED_SHORT, NULL);
//биндим нулевой буфер
glBindBufferARB(GL_ARRAY_BUFFER_ARB,0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
/**/
}
//------------------------------------------------------------------------------
void TTriangles::CalcMinMaxPoint()
{
if(CountVertex>0)
{
MaxPointXYZ.x=Vertices[0].x;
MaxPointXYZ.y=Vertices[0].y;
MaxPointXYZ.z=Vertices[0].z;
MinPointXYZ.x=Vertices[0].x;
MinPointXYZ.y=Vertices[0].y;
MinPointXYZ.z=Vertices[0].z;
}
for(unsigned int i=1;i<CountVertex;i++)
{
if(MaxPointXYZ.x<Vertices[i].x) MaxPointXYZ.x=Vertices[i].x;
if(MaxPointXYZ.y<Vertices[i].y) MaxPointXYZ.y=Vertices[i].y;
if(MaxPointXYZ.z<Vertices[i].z) MaxPointXYZ.z=Vertices[i].z;
if(MinPointXYZ.x>Vertices[i].x) MinPointXYZ.x=Vertices[i].x;
if(MinPointXYZ.y>Vertices[i].y) MinPointXYZ.y=Vertices[i].y;
if(MinPointXYZ.z>Vertices[i].z) MinPointXYZ.z=Vertices[i].z;
}
PointXYZCenter.x=(MinPointXYZ.x+MaxPointXYZ.x)/2.0f;
PointXYZCenter.y=(MinPointXYZ.y+MaxPointXYZ.y)/2.0f;
PointXYZCenter.z=(MinPointXYZ.z+MaxPointXYZ.z)/2.0f;
/* //DOTO есть такие параметры как сдвиг вращения масштаб я здесь учитываю только масштаб и сдвиг
MaxPointXYZ.x=(MaxPointXYZ.x*ScaleXYZ.x)+ShiftXYZ.x;
MaxPointXYZ.y=(MaxPointXYZ.y*ScaleXYZ.y)+ShiftXYZ.y;
MaxPointXYZ.z=(MaxPointXYZ.z*ScaleXYZ.z)+ShiftXYZ.z;
MinPointXYZ.x=(MinPointXYZ.x*ScaleXYZ.x)+ShiftXYZ.x;
MinPointXYZ.y=(MinPointXYZ.y*ScaleXYZ.y)+ShiftXYZ.y;
MinPointXYZ.z=(MinPointXYZ.z*ScaleXYZ.z)+ShiftXYZ.z;
//так как выделяем обьект по попаданию точки в нутырь куба для плоских обьектов немного расширем пространиство
MaxPointXYZ.x=MaxPointXYZ.x+0.001;
MaxPointXYZ.y=MaxPointXYZ.y+0.001;
MaxPointXYZ.z=MaxPointXYZ.z+0.001;
MinPointXYZ.x=MinPointXYZ.x-0.001;
MinPointXYZ.y=MinPointXYZ.y-0.001;
MinPointXYZ.z=MinPointXYZ.z-0.001;*/
}
//------------------------------------------------------------------------------
//просчитать сглаживающие нормали к поверхности
void TTriangles::CalcSmoothNormals()
{
unsigned int j;
if(SmoothNormals==NULL) SmoothNormals=new RfPointXYZ[CountVertex]; //память под нормали к точке
//если отключенно сглаживание то учитываеться только последний вектор к грани
if(!bSmooth)
{
for(j=0;j<CountVertex;j++)
{
SmoothNormals[j].x=0;
SmoothNormals[j].y=0;
SmoothNormals[j].z=0;
}
for(j=0;j<CountFaces;j++)
{
CalcNormals(Vertices[faces[j].a].x,Vertices[faces[j].a].y,Vertices[faces[j].a].z,Vertices[faces[j].b].x,Vertices[faces[j].b].y,Vertices[faces[j].b].z,Vertices[faces[j].c].x,Vertices[faces[j].c].y,Vertices[faces[j].c].z,SmoothNormals[faces[j].c].x,SmoothNormals[faces[j].c].y,SmoothNormals[faces[j].c].z);
}
}else
{
for(j=0;j<CountVertex;j++)
{
SmoothNormals[j].x=0;
SmoothNormals[j].y=0;
SmoothNormals[j].z=0;
}
unsigned int *mas=new unsigned int[CountVertex];
for(j=0;j<CountVertex;j++) mas[j]=0; //для усреднения
//подсчитываем нормали для каждой точки
RfPointXYZ point;
for(j=0;j<CountFaces;j++)
{
CalcNormals(Vertices[faces[j].a].x,Vertices[faces[j].a].y,Vertices[faces[j].a].z,Vertices[faces[j].b].x,Vertices[faces[j].b].y,Vertices[faces[j].b].z,Vertices[faces[j].c].x,Vertices[faces[j].c].y,Vertices[faces[j].c].z,point.x,point.y,point.z);
SmoothNormals[faces[j].a].x=SmoothNormals[faces[j].a].x+point.x;
SmoothNormals[faces[j].a].y=SmoothNormals[faces[j].a].y+point.y;
SmoothNormals[faces[j].a].z=SmoothNormals[faces[j].a].z+point.z;
mas[faces[j].a]++;
SmoothNormals[faces[j].b].x=SmoothNormals[faces[j].b].x+point.x;
SmoothNormals[faces[j].b].y=SmoothNormals[faces[j].b].y+point.y;
SmoothNormals[faces[j].b].z=SmoothNormals[faces[j].b].z+point.z;
mas[faces[j].b]++;
SmoothNormals[faces[j].c].x=SmoothNormals[faces[j].c].x+point.x;
SmoothNormals[faces[j].c].y=SmoothNormals[faces[j].c].y+point.y;
SmoothNormals[faces[j].c].z=SmoothNormals[faces[j].c].z+point.z;
mas[faces[j].c]++;
}
//нормализуем нормали
for(j=0;j<CountVertex;j++)
{
SmoothNormals[j].x=SmoothNormals[j].x/mas[j];
SmoothNormals[j].y=SmoothNormals[j].y/mas[j];
SmoothNormals[j].z=SmoothNormals[j].z/mas[j];
normalized(SmoothNormals[j]); //приведу к еденичной длине
}
delete[] mas;
}
}
//------------------------------------------------------------------------------
void TTriangles::cutOnSmoothGroup()
{
/*if(SmoothG==NULL)
{*/
//груп сглаживания нет то каждая грань имеет единоличные точки
RfPointXYZ *vbuf=new RfPointXYZ[CountFaces*3];
RfPointXY *tbuf=new RfPointXY[CountFaces*3];
unsigned short count=0;
for(unsigned int i=0;i<CountFaces;i++)
{
vbuf[count]=Vertices[faces[i].a];
if(TexVertices!=NULL) tbuf[count]=TexVertices[faces[i].a];
faces[i].a=count; //сохраняем новый номер точки
count++;
vbuf[count]=Vertices[faces[i].b];
if(TexVertices!=NULL) tbuf[count]=TexVertices[faces[i].b];
faces[i].b=count; //сохраняем новый номер точки
count++;
vbuf[count]=Vertices[faces[i].c];
if(TexVertices!=NULL) tbuf[count]=TexVertices[faces[i].c];
faces[i].c=count; //сохраняем новый номер точки
count++;
}
delete[] Vertices;
Vertices=vbuf;
CountVertex=CountFaces*3;
if(TexVertices!=NULL) delete[] TexVertices;
TexVertices=tbuf;
/*}else
{
}*/
CalcSmoothNormals(); //просчитываем сглаживающие нормали к точкам
delete[] SmoothG;
SmoothG=NULL;
}
//------------------------------------------------------------------------------
//добавить материал
//название материала,колво элементов, список фейсов с этим материалом
void TTriangles::AddMaterial(char *name,unsigned short count,unsigned short *faces)
{
RMatFaces *buf=new RMatFaces[countMF+1];
for(unsigned int i=0;i<countMF;i++) buf[i]=MatFaces[i];
buf[countMF].name=name;
buf[countMF].count=count;
buf[countMF].faces=faces;
if(MatFaces!=NULL) delete[] MatFaces;
MatFaces=buf;
countMF++;
}
//******************************************************************************
TTrianglesList::TTrianglesList()
{
count=0;
List=NULL;
countMat=0; //количество материалов
ListMat=NULL; //массив материалов
}
//------------------------------------------------------------------------------
TTrianglesList::~TTrianglesList()
{
for(unsigned int i=0;i<count;i++) delete List[i];
if(List!=NULL) delete[] List;
count=4294967295; //на всяк.
for(unsigned int i=0;i<countMat;i++) delete ListMat[i];
if(ListMat!=NULL) delete[] ListMat;
countMat=4294967295; //на всяк.
}
//------------------------------------------------------------------------------
TTriangles* TTrianglesList::Add()
{
TTriangles **buf = new TTriangles*[count+1];
for(unsigned int i=0;i<count;i++) buf[i]=List[i];
buf[count]=new TTriangles();
if(List!=NULL) delete[] List;
List=buf;
return List[count++];
}
//------------------------------------------------------------------------------
//удалить обьект из массива
void TTrianglesList::DelTri(TTriangles* tri)
{
for(unsigned int i=0;i<count;i++)
{
if(List[i]==tri)
{
TTriangles **buf = new TTriangles*[count-1];
unsigned int pos=0;
for(unsigned int j=0;j<count;j++)
{
if(i!=j){buf[pos]=List[j]; pos++;}
}
delete[] List;
delete tri;
List=buf;
count--;
break;
}
}
}
//------------------------------------------------------------------------------
//добавить новый метериал
TTriMat* TTrianglesList::AddMat()
{
TTriMat **buf = new TTriMat*[countMat+1];
for(unsigned int i=0;i<countMat;i++) buf[i]=ListMat[i];
buf[countMat]=new TTriMat();
if(ListMat!=NULL) delete[] ListMat;
ListMat=buf;
return ListMat[countMat++];
}
//------------------------------------------------------------------------------
void TTrianglesList::render()
{
for(unsigned int i=0;i<count;i++)
{
TTriangles *tri=List[i];
if(tri->TriMat!=NULL) tri->TriMat->Release();
tri->render();
}
}
//------------------------------------------------------------------------------
void TTrianglesList::CalcMinMaxPoint()
{
for(unsigned int i=0;i<count;i++)
{
TTriangles *tri=List[i];
tri->CalcMinMaxPoint();
if(i==0) MinPointXYZ=tri->MinPointXYZ; else
{
if(MinPointXYZ.x>tri->MinPointXYZ.x) MinPointXYZ.x=tri->MinPointXYZ.x;
if(MinPointXYZ.y>tri->MinPointXYZ.y) MinPointXYZ.y=tri->MinPointXYZ.y;
if(MinPointXYZ.z>tri->MinPointXYZ.z) MinPointXYZ.z=tri->MinPointXYZ.z;
}
if(i==0) MaxPointXYZ=tri->MaxPointXYZ; else
{
if(MaxPointXYZ.x<tri->MaxPointXYZ.x) MaxPointXYZ.x=tri->MaxPointXYZ.x;
if(MaxPointXYZ.y<tri->MaxPointXYZ.y) MaxPointXYZ.y=tri->MaxPointXYZ.y;
if(MaxPointXYZ.z<tri->MaxPointXYZ.z) MaxPointXYZ.z=tri->MaxPointXYZ.z;
}
}
CenterPointXYZ.x=(MaxPointXYZ.x+MinPointXYZ.x)/2.0f;
CenterPointXYZ.y=(MaxPointXYZ.y+MinPointXYZ.y)/2.0f;
CenterPointXYZ.z=(MaxPointXYZ.z+MinPointXYZ.z)/2.0f;
}
//------------------------------------------------------------------------------
void TTrianglesList::CalcSmoothNormals()
{
for(unsigned int i=0;i<count;i++)
{
TTriangles *tri=List[i];
tri->CalcSmoothNormals();
}
}
//------------------------------------------------------------------------------
void TTrianglesList::cutOnSmoothGroup()
{
for(unsigned int i=0;i<count;i++)
{
TTriangles *tri=List[i];
tri->cutOnSmoothGroup();
}
}
//------------------------------------------------------------------------------
TTriMat *TTrianglesList::FindMatOnName(char *name)
{
for(unsigned int i=0;i<countMat;i++)
{
if(wxString::FromAscii(ListMat[i]->name).Lower()==wxString::FromAscii(name).Lower()) return ListMat[i];
}
return NULL;
}
//------------------------------------------------------------------------------
//разбить обьект по списку материалов (сколько материалов столшько и объектов будет)
//применять после разбения по группам сглаживания и просчёта нормалей а то на границе между разными материалами не будет сглаживания
void TTrianglesList::cutOnMaterials()
{
for(unsigned int i=0;i<count;i++)
{
TTriangles *tri=List[i];
if(tri->countMF==1)
{
tri->TriMat=FindMatOnName(tri->MatFaces[0].name);
}else
if(tri->countMF>1) //если несколько материалов то размножаем объекты
{
//в списке фейсов для этого материала раздные точки
//составляем список уникальных точек затем копируем их
//память выделяем под самый плохой вариант
unsigned short *buf=new unsigned short[tri->CountVertex*2]; //уникальные номера точек
for(unsigned int j=0;j<tri->countMF;j++)
{
//создаём новый объект и копируем туда точки в соответствии с материалом
unsigned int cnt=0;
for(unsigned int k=0;k<tri->MatFaces[j].count;k++)
{
if(tri->MatFaces[j].faces[k]>=tri->CountFaces)
return;
bool b=false;
for(unsigned int q=0;q<cnt;q++)
if(buf[q]==tri->faces[tri->MatFaces[j].faces[k]].a)
{
b=true;
break;
}
if(!b){buf[cnt]=tri->faces[tri->MatFaces[j].faces[k]].a; cnt++;}
b=false;
for(unsigned int q=0;q<cnt;q++)
if(buf[q]==tri->faces[tri->MatFaces[j].faces[k]].b)
{
b=true;
break;
}
if(!b){buf[cnt]=tri->faces[tri->MatFaces[j].faces[k]].b; cnt++;}
b=false;
for(unsigned int q=0;q<cnt;q++)
if(buf[q]==tri->faces[tri->MatFaces[j].faces[k]].c)
{
b=true;
break;
}
if(!b){buf[cnt]=tri->faces[tri->MatFaces[j].faces[k]].c; cnt++;}
}
//выбрали список точек нужных для нового обьекта теперь копируем
TTriangles* newTri=Add();
newTri->TriMat=FindMatOnName(tri->MatFaces[j].name);
newTri->CountVertex=cnt;
newTri->Vertices=new RfPointXYZ[newTri->CountVertex];
newTri->TexVertices=new RfPointXY[newTri->CountVertex];
newTri->SmoothNormals=new RfPointXYZ[newTri->CountVertex];
newTri->CountFaces=tri->MatFaces[j].count;
newTri->faces=new RsFacesABC[newTri->CountFaces];
for(unsigned int k=0;k<cnt;k++) //копируем точки
{
newTri->Vertices[k]=tri->Vertices[buf[k]];
newTri->TexVertices[k]=tri->TexVertices[buf[k]];
newTri->SmoothNormals[k]=tri->SmoothNormals[buf[k]];
}
for(unsigned int k=0;k<tri->MatFaces[j].count;k++)
{
newTri->faces[k]=tri->faces[tri->MatFaces[j].faces[k]];
//ищем номер точки
bool b=true;
for(unsigned int q=0;q<cnt;q++){ if(newTri->faces[k].a==buf[q]) {newTri->faces[k].a=q; b=false; break;} }
if(b||newTri->faces[k].a>=cnt)
return;
b=true;
for(unsigned int q=0;q<cnt;q++){ if(newTri->faces[k].b==buf[q]) {newTri->faces[k].b=q; b=false; break;} }
if(b||newTri->faces[k].b>=cnt)
return;
b=true;
for(unsigned int q=0;q<cnt;q++){ if(newTri->faces[k].c==buf[q]) {newTri->faces[k].c=q; b=false; break;} }
if(b||newTri->faces[k].c>=cnt)
return;
}
}
tri->del=true;
delete[] buf;
}
}
unsigned int i=0;
while(i<count)
{
TTriangles *tri=List[i];
//tri->CountVertex;
if(tri->del)
DelTri(tri);
else i++;
}
//домножаем текущие координаты материала на USCALE и VSCALE текущего материала
for(unsigned int i=0;i<count;i++)
{
TTriangles *tri=List[i];
if(tri->TriMat!=NULL)
for(unsigned int j=0;j<tri->CountVertex;j++)
{
tri->TexVertices[j].x*=tri->TriMat->USCALE;
tri->TexVertices[j].y*=tri->TriMat->VSCALE;
}
}
}
//------------------------------------------------------------------------------