Курсовая работа является частью проекта по разработке графического редактора анимированной 3d-сцены.
Цель курсовой работы – разработать базу данных (БД) для представления информации об анимированном объекте.
Решаются следующие задачи:
Предметная область – это анимированная полигональная модель 3d-объекта. Анимация представляется в виде последовательности кадров.
Один кадр отображает состояние модели объекта в некоторый момент времени.
Каждому полигону может быть назначена текстура.
Полигон задается координатами своих вершин.
Для каждой вершины необходимо хранить следующие данные:
Каждый полигон входит в одну группу. Всегда существует группа с номером 1.
Каждой группе, а следовательно, и ее полигонам может быть назначена текстура. То есть поддерживается операция группового назначения текстуры.
Наличие в модели групп позволяет выполнять и прочие групповые операции, например аффинные преобразования полигонов группы. В настоящей реализации такие операции не рассматриваются.
Распределение полигонов по группам выполняется в следующем порядке: отображестся имеющаяся в БД модель объекта; выбираются подходящие полигоны, из которых формируется группа. Заметим, что при выборе полигона, входящего в группу, выбираются все полигоны группы. Описанный механизм реализован с использованием графической библиотеки OpenGL.
Получаем следующее (без учета групп полигонов) представление анимированной модели в одной ненормализованной таблице:
№ кадра | № полигона | № вершины | {x, y, z} | {xn, yn, zn} | {u, v} | Текстура |
---|---|---|---|---|---|---|
1 | 1 | 1 | ||||
1 | 1 | 2 | ||||
1 | 1 | 3 | ||||
1 | 2 | 3 | ||||
1 | 2 | 7 | ||||
1 | 2 | 2 | ||||
1 | 2 | 1 | ||||
… | … | … | … | … | … | … |
2 | 1 | 1 | ||||
2 | 1 | 2 | ||||
… | … | … | … | … | … | … |
Использование данной ненормализованной таблицы неприемлемо из-за наличия избыточности и вредных эксплуатационных издержек. Например, при изменении координат вершины 1 придется изменять значения нескольких полей таблицы (аномалия редактирования).
Выделим и опишем с указанием типа данных следующие сущности:
КАДР – хранит координаты вершины и координаты нормали этой вершины.
№ кадра, integer | № вершины, integer | x, float | y, float | z, float | xn, float | yn, float | zn, float |
---|
ГРУППА – включает некоторое подмножество полигонов модели. В текущей разработке группе (всем ее полигонам) можно единовременно назначить текстуру.
№ группы, integer | Имя текстуры, varchar(20) |
---|
ТЕКСТУРА – хранит путь и имя файла текстуры; путь задается относительно расположения БД.
Имя текстуры, varchar(20) | Путь к текстуре, varchar(100) |
---|
ВЕРШИНА – хранит флаг активности вершины (активность вершин будет использована в программе для выделения объекта).
№ вершины, integer | Флаг активности, char(1) |
---|
ПОЛИГОН – хранит для каждого полигона номер группы, которой полигон принадлежит. Содержит также флаг активности полигона.
№ группы, integer | № полигона, integer | Флаг активности, char(1) |
---|
ПОЛИГОН и ВЕРШИНЫ – указывает, какие вершины образуют полигон, и UV-координаты вершины в текущем полигоне.
№ полигона, integer | № вершины, integer | u, float | v, float |
---|
Графический редактор разрабатывается в среде Borland C++ Builder 6 с использованием OpenGL.
В качестве СУБД употребляется Interbase 7.1.
База данных, ее таблицы и индексы автоматически создаются в Interbase в результате употребления следующего кода.
create table texture
(
name varchar(20) not null, art varchar(100) not null,
primary key(name)
);
create table grope
(
cod integer not null, texture varchar(20) not null,
primary key(cod),
foreign key(texture) references texture(name) on update cascade
);
create table point
(
nom integer not null, act char(1) default 'f', primary key(nom)
);
create table poligon
(
nom integer not null, nom_grope integer not null, act char(1) default 'f',
primary key(nom),
foreign key(nom_grope) references grope(cod) on update cascade
);
create table kadr
(
nom integer not null, nom_point integer not null,
x float not null, y float not null, z float not null,
xn float not null, yn float not null, zn float not null,
primary key(nom, nom_point),
foreign key(nom_point) references point(nom) on delete cascade
);
create table poligon_point
(
nom integer not null,
nom_point integer not null,
u float not null,
v float not null,
primary key(nom, nom_point)
);
При работе с БД будут полезны следующие индексы:
CREATE INDEX "I_POLIGON_POINT_NOM" ON "POLIGON_POINT"("NOM");
CREATE INDEX "I_POLIGON_NOM_GROPE" ON "POLIGON"("NOM_GROPE");
CREATE INDEX "I_POLIGON_NOM" ON "POLIGON"("NOM");
CREATE INDEX "I_KADR_NOM_POINT" ON "KADR"("NOM_POINT");
CREATE INDEX "I_KADR_NOM" ON "KADR"("NOM");
За создание хранимых процедур и триггеров отвечает следующий код.
Процедура select_point обеспечивает выбор (выделение) вершин полигонов в кадре n, охваченных прямоугольником, заданным точками {x1, y1, z1} и {x2, y2, z2}.
SET TERM !! ;
CREATE PROCEDURE select_point(n int, x1 float, y1 float, z1 float, x2 float, y2 float, z2 float)
AS
declare variable xx float;
declare variable yy float;
declare variable zz float;
declare variable ind int;
BEGIN
ind = 0;
FOR SELECT x, y, z FROM kadr WHERE nom = :n INTO :xx,yy,zz
DO BEGIN
ind = ind + 1;
IF (x1 < xx AND xx < x2 AND y1 < yy AND yy < y2 AND z1 < zz AND zz < z2)
THEN UPDATE point SET act = 't' WHERE point.nom = :ind;
END
END !!
SET TERM ; !!
Процедура выделения полигонов по выбранным вершинам. Полигон выделяется, если выбрана хотя бы одна его вершина.
SET TERM !! ;
CREATE PROCEDURE select_poligon
AS
BEGIN
UPDATE poligon SET act = 't'
WHERE nom IN
(SELECT poligon_point.nom FROM poligon_point, point
WHERE poligon_point.nom_point = point.nom AND point.act = 't');
END !!
SET TERM ; !!
Процедура, обеспечивающая создание группы из выделенных полигонов; str – название текстуры.
SET TERM !! ;
CREATE PROCEDURE create_grope(str varchar(20))
AS
declare variable i integer;
BEGIN
SELECT count(*) FROM grope INTO:i;
i = i + 1;
INSERT INTO grope VALUES(:i, :str);
UPDATE poligon SET nom_grope = :i WHERE act = 't';
END !!
SET TERM ; !!
Удаление группы контролируется двумя триггерами. Первый срабатывает до удаления группы. Если удаляется группа с номером, большим 1, то полигоны автоматически записываются в группу 1. Группа 1 всегда имеется в модели объекта.
SET TERM !! ;
CREATE TRIGGER delete_grope FOR grope
BEFORE DELETE
AS
BEGIN
IF (old.cod <> 1)
THEN BEGIN
UPDATE poligon SET act = 'f';
UPDATE poligon SET act = 't' WHERE nom_grope = old.cod;
UPDATE poligon SET nom_grope = 1 WHERE act = 't';
UPDATE poligon SET act = 'f'
END
END !!
SET TERM ; !!
Второй триггер удаления группы срабатывает после ее удаления. Если удалена группа 1, то она создается заново, иначе сдвигаем нумерацию групп для замещения пропуска, образовавшегося вследствие удаления записи.
SET TERM !! ;
CREATE TRIGGER delete_grope_not_1 FOR grope
AFTER DELETE
AS
declare variable ind int;
BEGIN
IF (old.cod = 1)
THEN INSERT INTO grope VALUES(1, 'zero');
ELSE BEGIN
FOR SELECT cod FROM grope WHERE cod > old.cod INTO :ind
DO
UPDATE grope SET cod = :ind - 1 WHERE cod = :ind;
END
END !!
SET TERM ; !!
Очистка текстуры контролируется двумя триггерами. Первый запускается до очистки; он обеспечивает замену существующей текстуры на текстуру ZERO.
SET TERM !! ;
CREATE TRIGGER delete_texture FOR texture
BEFORE DELETE
AS
BEGIN
IF (old.name <> 'zero') THEN UPDATE grope SET texture = 'zero' where texture = old.name;
END !!
SET TERM ; !!
Второй триггер запускается после очистки текстуры. Если удалена текстура ZERO, то она будет триггером восстановлена.
SET TERM !! ;
CREATE TRIGGER delete_texture_not_zero FOR texture
AFTER DELETE
AS
BEGIN
IF (old.name = 'zero') THEN INSERT INTO texture VALUES('zero', 'textura\zero.bmp');
END !!
SET TERM ; !!
Для наполнения БД используются созданные в Maya полигональные модели 3d-объектов. В качестве тестовой выбрана модель кисти руки (рис. 1).
Рис. 1. Кисть руки
В БД загружаются данные о Maya-модели, предварительно выгруженные в текстовый файл следующего формата:
<количество вершин в объекте>
x y z u v xn yn zn
...
<количество полигонов>
<количество вершин в полигоне> <номера вершин, принадлежащих полигону>
...
Так, при выгрузке созданного в Maya куба формируется файл со следующими данными:
8
-0.500000 -0.500000 0.500000 0.000000 4.000000 -0.577350 -0.577350 0.577350
0.500000 -0.500000 0.500000 1.000000 0.000000 0.577350 -0.577350 0.577350
-0.500000 0.500000 0.500000 0.000000 1.000000 -0.577350 0.577350 0.577350
0.500000 0.500000 0.500000 1.000000 1.000000 0.577350 0.577350 0.577350
-0.500000 0.500000 -0.500000 -1.000000 1.000000 -0.577350 0.577350 -0.577350
0.500000 0.500000 -0.500000 1.000000 2.000000 0.577350 0.577350 -0.577350
-0.500000 -0.500000 -0.500000 -1.000000 0.000000 -0.577350 -0.577350 -0.577350
0.500000 -0.500000 -0.500000 1.000000 3.000000 0.577350 -0.577350 -0.577350
6
4 0 1 3 2
4 2 3 5 4
4 4 5 7 6
4 6 7 1 0
4 1 7 5 3
4 6 0 2 4
Экспорт данных из Maya обеспечивает нижеприведенный плагин:
code:
#pragma comment(lib, "foundation.lib")
#pragma comment(lib, "openmaya.lib")
#include <math.h>
#include <maya/MPxCommand.h>
#include <maya/MGlobal.h>
#include <maya/MFnPlugin.h>
#include <maya/MObject.h>
#include <maya/MStatus.h>
#include <maya/MArgList.h>
#include <maya/MDagPath.h>
#include <maya/MSelectionList.h>
#include <maya/MItMeshVertex.h>
#include <maya/MItMeshPolygon.h>
class point
{
public:
point(){x = 0; y = 0; z = 0; u = 0; z = 0; xn = 0; yn = 0; zn = 0;}
float x, y, z;
float u, v;
float xn, yn, zn;
bool test(point t)
{
return (t.x == x) && (t.y == y) && (t.z == z);
}
};
class AModelExport : public MPxCommand
{
public:
virtual MStatus doIt(const MArgList& arg);
static void *creator();
};
MStatus AModelExport::doIt(const MArgList& arg)
{
int i, j;
FILE* file = NULL;
float fuv[2]={0.0f, 0.0f};
MDagPath node; // Узел объекта в иерархии
MObject obj; // Объект
MSelectionList list; // Список выделенных объектов
MFnDagNode nodefn; // Функции для работы с узлом объекта
printf("=============================\n");
printf("Begin mexport...\n");
if (MGlobal::getActiveSelectionList(list) != MS::kSuccess)
{
printf("Error: Can't get selection list\n");
return MS::kFailure;
}
if (list.length() == 0)
printf("No choosed object\n");
else
{
if (list.length() > 1) printf("This Plug-In works only with one object! Any other will be ignored\n");
list.getDagPath(0, node, obj);
nodefn.setObject(node);
printf("Object '%s' Is Founded\n", nodefn.name().asChar());
}
MStatus * ReturnStatus = NULL;
MItMeshVertex www(node, obj);
MVector vn;
bool created = 0;
MString name = nodefn.name() + ".mdl";
file = NULL;
file = fopen("C:\\mexport_obj.txt", "w"); // Открываем файл для выгрузки данных
fprintf(file, "%d\n", www.count());
point VV[3000];
for (i = 0; i < www.count(); i++)
{
www.getUV(fuv);
VV[i].x = www.position(MSpace::kWorld).x;
VV[i].y = www.position(MSpace::kWorld).y;
VV[i].z = www.position(MSpace::kWorld).z;
VV[i].u = fuv[0];
VV[i].v = fuv[1];
VV[i].xn = vn.x;
VV[i].yn = vn.y;
VV[i].zn = vn.z;
www.getNormal(vn, MSpace::kWorld);
fprintf(file, "%f %f %f %f %f %f %f %f", www.position(MSpace::kWorld).x,
www.position(MSpace::kWorld).y, www.position(MSpace::kWorld).z,
fuv[0], fuv[1], vn.x, vn.y, vn.z);
fprintf(file, "\n");
www.next();
}
// Теперь полигоны
point PP;
MItMeshPolygon poly(node, obj);
printf("Polygons Count = %d\n", poly.count());
fprintf(file, "%d\n", poly.count());
for (i = 0; i < poly.count(); i++)
{
fprintf(file, "%d", poly.polygonVertexCount());
for (j = 0; j < poly.polygonVertexCount(); j++)
{
if (poly.getUV(j, fuv) == MS::kFailure)
{
printf("Can't Get UV Coordinates For %d Vertex Of %d Polygon", j, i);
return MS::kFailure;
}
PP.x = poly.point(j, MSpace::kWorld).x;
PP.y = poly.point(j, MSpace::kWorld).y;
PP.z = poly.point(j, MSpace::kWorld).z;
for(int i = 0; i < www.count(); i++)
if(VV[i].test(PP))
{
fprintf(file, " %d", i);
i = www.count();
}
}
}
fprintf(file, "\n");
poly.next();
}
printf("End mexport...\n");
printf("=============================\n\n");
fclose(file);
return MS::kSuccess;
}
void *AModelExport::creator()
{
return new AModelExport;
}
MStatus initializePlugin(MObject obj)
{
MFnPlugin plugin(obj, "...I...", "1.0");
MStatus stat;
stat = plugin.registerCommand("mexport", AModelExport::creator);
// Аварийное завершение registerCommand в случае неудачи
if(!stat) stat.perror("registerCommand failed");
return stat;
}
MStatus uninitializePlugin(MObject obj)
{
MFnPlugin plugin(obj);
MStatus stat;
stat = plugin.deregisterCommand("mexport");
// Аварийное завершение deregisterCommand в случае неудачи
if(!stat) stat.perror("deregisterCommand failed");
return stat;
}
Выгрузка (экспорт) данных выполняется по команде mexport. Команда доступна в Maya после загрузки плагина. Выгружаемый объект должен быть выбран в Maya. Если выбрано несколько объектов, то будут выгружены сведения только об объекте, открывающем список выбранных в Maya объектов.
Выгруженные из Maya данные переносятся в БД посредством нижеприводимого кода; в нем также имеются средства для создания групп полигонов и группового назначения текстуры (при загрузке все полигоны модели автоматически помещаютсяв в группу 1).
CODE:
class TKnot
{
public:
TVertex pozition;
TVertex normal;
float uv[2];
TKnot* ret()
{
return this;
}
};
class TGrope
{
public:
AnsiString texture;
AnsiString texture_name;
TGrope* ret()
{
return this;
}
};
class SPoligon
{
public:
vector<TKnot*>point;
TGrope* grope;
};
class TObj_poligons : public IObject
{
public:
TObj_poligons(AnsiString);
~TObj_poligons();
void create(IObject*& aObj){};
bool inputFile(FILE *);
void outputFile(FILE *);
void draw();
void del();
void allocate(const TVertex p1, const TVertex& p2);
void free();
// Выделяем путь к текстуре
AnsiString textura();
void add_texture(AnsiString, AnsiString);
void update_texture(AnsiString, AnsiString);
void delete_texture(AnsiString);
void Create_grope(AnsiString);
void delete_grope(int);
TDataSource *DataSource1;
TDataSource *DataSource5;
TDataSource *DataSource_texture;
TIBClientDataSet *IBClientDataSet_grope;
TIBClientDataSet *IBClientDataSet_draw;
TIBClientDataSet *IBClientDataSet_texture;
TIBSQL *IBSQL1;
AnsiString m_tras;
protected:
void conect();
TIBDatabase *IBDatabase1;
TIBTransaction *IBTransaction1;
int m_kadr; // Текущий номер кадра
int m_n; // Количество вершин
};
TObj_poligons::TObj_poligons(AnsiString str)
{
m_tras = "";
m_kadr = 0;
m_n = 0;
IBDatabase1 = new TIBDatabase(Application);
IBTransaction1 = new TIBTransaction(Application);
IBClientDataSet_grope = new TIBClientDataSet(Application);
IBSQL1 = new TIBSQL(Application);
DataSource1 = new TDataSource(Application);
DataSource5 = new TDataSource(Application);
IBClientDataSet_draw = new TIBClientDataSet(Application);
DataSource_texture = new TDataSource(Application);
IBClientDataSet_texture = new TIBClientDataSet(Application);
IBTransaction1->DefaultDatabase = IBDatabase1;
IBDatabase1->Params->Add("user_name=SYSDBA");
IBDatabase1->Params->Add("password=masterkey");
IBDatabase1->DefaultTransaction = IBTransaction1;
IBDatabase1->DatabaseName = str;
IBDatabase1->Connected = true;
IBTransaction1->Active = true;
IBSQL1->Database = IBDatabase1;
IBSQL1->Transaction = IBTransaction1;
IBClientDataSet_grope->DBConnection = IBDatabase1;
IBClientDataSet_grope->DBTransaction = IBTransaction1;
IBClientDataSet_grope->CommandText =
"select COD, TEXTURE, ART from GROPE, TEXTURE where GROPE.TEXTURE = TEXTURE.NAME order by COD";
IBClientDataSet_grope->Active = true;
DataSource1->DataSet = IBClientDataSet_grope;
IBClientDataSet_draw->DBConnection = IBDatabase1;
IBClientDataSet_draw->DBTransaction = IBTransaction1;
IBClientDataSet_draw->CommandText = "select NOM_GROPE, POLIGON.NOM, POLIGON_POINT.NOM_POINT, X, Y, Z, XN, YN, ZN, U, V from POLIGON, POLIGON_POINT, KADR where POLIGON_POINT.NOM = POLIGON.NOM and POLIGON_POINT.NOM_POINT = KADR.NOM_POINT order by 1";
IBClientDataSet_draw->Active = true;
DataSource5->DataSet = IBClientDataSet_draw;
IBClientDataSet_texture->DBConnection = IBDatabase1;
IBClientDataSet_texture->DBTransaction = IBTransaction1;
IBClientDataSet_texture->CommandText = "select * from TEXTURE";
IBClientDataSet_texture->Active = true;
DataSource_texture->DataSet = IBClientDataSet_texture;
conect();
}
TObj_poligons::~TObj_poligons()
{
delete IBDatabase1;
delete IBTransaction1;
delete IBSQL1;
delete IBClientDataSet_grope;
delete IBClientDataSet_texture;
delete IBClientDataSet_draw;
delete DataSource1;
delete DataSource5;
}
bool TObj_poligons::inputFile(FILE *file)
{
FILE *fin;
// Добавить try
fin = fopen("c:\qqq.txt","w");
TKnot p;
int n = 0;
fscanf(file,"%d \n",&n);
for (int i = 1; i <= n; i++)
{
fscanf(file, "%f", &p.pozition.x);
fscanf(file, "%f", &p.pozition.y);
fscanf(file, "%f", &p.pozition.z);
fscanf(file, "%f", &p.uv[0]);
fscanf(file, "%f",.uv[1]);
fscanf(file, "%f",.normal.x);
fscanf(file, "%f", &p.normal.y);
fscanf(file, "%f \n", &p.normal.z);
fprintf(fin, "INSERT INTO kadr VALUES(1, %d, %f, %f, %f, %f, %f, %f); \n",
i, p.pozition.x, p.pozition.y, p.pozition.z, p.normal.x, p.normal.y, p.normal.z);
}
for (int i = 1; i <= n; i++)
fprintf(fin,"INSERT INTO point VALUES(%d, 'f'); \n" ,i);
fprintf(fin,"INSERT INTO grope VALUES(1,'zero'); \n");
fprintf(fin,"INSERT INTO texture VALUES('zero','textura\\zero.bmp'); \n");
int kp = 0; // Количество вершин в полигоне
int nom = 0;
fscanf(file, "%d", &n);
float temp = 3.1415926535 / 2;
for (int i = 1; i <= n; i++)
{
fprintf(fin,"INSERT INTO poligon (nom, nom_grope) VALUES(%d,1); \n", i);
fscanf(file,"\n %d",&kp);
for (; kp != 0; kp--)
{
fscanf(file,"%d",&nom);
fprintf(fin,"INSERT INTO poligon_point VALUES( %d, %d, %f, %f); \n",
i, nom + 1, 0.5 * (1 + cos(i * temp)), 0.5 * (1 + sin(i * temp)));
}
}
fclose(fin);
return true;
}
void TObj_poligons::draw()
{
int i, j, k;
TKnot p;
AnsiString str = "";
IBClientDataSet_grope->First();
IBClientDataSet_draw->First();
int n = IBClientDataSet_grope->RecordCount;
for(i = 0; i < n; i++)
{
// Установка текстуры или цвета
str = m_tras + IBClientDataSet_grope->FieldByName("ART")->AsString;
LoadGLTextures(str.c_str());
while((i + 1 == IBClientDataSet_draw->FieldByName("NOM_GROPE")->AsInteger) && (!IBClientDataSet_draw->Eof))
{
j = IBClientDataSet_draw->FieldByName("NOM")->AsInteger;
glBegin(GL_POLYGON);
while((j == IBClientDataSet_draw->FieldByName("NOM")->AsInteger) && (!IBClientDataSet_draw->Eof))
{
p.uv[0] = IBClientDataSet_draw->FieldByName("U")->AsFloat;
p.uv[1] = IBClientDataSet_draw->FieldByName("V")->AsFloat;
p.pozition.x = IBClientDataSet_draw->FieldByName("X")->AsFloat;
p.pozition.y = IBClientDataSet_draw->FieldByName("Y")->AsFloat;
p.pozition.z = IBClientDataSet_draw->FieldByName("Z")->AsFloat;
p.normal.x = IBClientDataSet_draw->FieldByName("XN")->AsFloat;
p.normal.y = IBClientDataSet_draw->FieldByName("YN")->AsFloat;
p.normal.z = IBClientDataSet_draw->FieldByName("ZN")->AsFloat;
glTexCoord2f(p.uv[0], p.uv[1]);
glNormal3f(p.normal.x, p.normal.y, p.normal.z);
glVertex3f(p.pozition.x, p.pozition.y, p.pozition.z);
IBClientDataSet_draw->Next();
}
glEnd();
}
IBClientDataSet_grope->Next();
}
}
void TObj_poligons::allocate(const TVertex p1, const TVertex& p2)
{
TReplaceFlags flags;
m_kadr = 1;
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("EXECUTE PROCEDURE select_point(");
IBSQL1->SQL->Add(IntToStr(m_kadr) + ",");
IBSQL1->SQL->Add(StringReplace(FloatToStr(p1.x), ", ", ".", flags) + ",");
IBSQL1->SQL->Add(StringReplace(FloatToStr(p1.y), ", ", ".", flags) + ",");
IBSQL1->SQL->Add(StringReplace(FloatToStr(p1.z), ", ", ".", flags) + ",");
IBSQL1->SQL->Add(StringReplace(FloatToStr(p2.x), ", ", ".", flags) + ",");
IBSQL1->SQL->Add(StringReplace(FloatToStr(p2.y), ", ", ".", flags) + ",");
IBSQL1->SQL->Add(StringReplace(FloatToStr(p2.z), ", ", ".", flags) + ");");
IBSQL1->ExecQuery();
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("EXECUTE PROCEDURE select_poligon;");
IBSQL1->ExecQuery();
}
void TObj_poligons::free()
{
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("UPDATE ");
IBSQL1->ExecQuery();
}
void TObj_poligons::conect()
{
m_tras = IBDatabase1->DatabaseName;
m_tras.SetLength(LastDelimiter("\\", m_tras));
}
AnsiString TObj_poligons::textura()
{
return (m_tras + IBClientDataSet_texture->FieldByName("ART")->AsString);
}
void TObj_poligons::add_texture(AnsiString str, AnsiString str_name)
{
IBClientDataSet_texture->Active = false;
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("INSERT INTO texture VALUES('" + str_name + "','" + str + "');");
IBSQL1->ExecQuery();
IBClientDataSet_texture->Active = true;
}
void TObj_poligons::update_texture(AnsiString art_new, AnsiString name)
{
IBClientDataSet_texture->Active = false;
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("UPDATE texture SET art = '" + art_new + "' WHERE name = '" + name + "';");
IBSQL1->ExecQuery();
IBClientDataSet_texture->Active = true;
}
void TObj_poligons::delete_texture(AnsiString str)
{
IBClientDataSet_texture->Active = false;
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("DELETE FROM texture WHERE name = '" + str + "';");
IBSQL1->ExecQuery();
IBClientDataSet_texture->Active = true;
}
void TObj_poligons::Create_grope(AnsiString name)
{
IBClientDataSet_draw->Active = false;
IBClientDataSet_grope->Active = false;
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("EXECUTE PROCEDURE create_grope('" + name + "');");
IBSQL1->ExecQuery();
IBClientDataSet_draw->Active = true;
IBClientDataSet_grope->Active = true;
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("UPDATE poligon SET act = 'f';");
IBSQL1->ExecQuery();
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("UPDATE point SET act = 'f';");
IBSQL1->ExecQuery();
}
void TObj_poligons::delete_grope(int n)
{
IBClientDataSet_draw->Active = false;
IBClientDataSet_grope->Active = false;
IBSQL1->ParamCheck = false;
IBSQL1->SQL->Clear();
IBSQL1->SQL->Add("DELETE from grope where cod = "+ IntToStr(n) + ";");
IBSQL1->ExecQuery();
IBClientDataSet_draw->Active = true;
IBClientDataSet_grope->Active = true;
}
Выгруженная из Maya полигональная модель кисти руки была затем загружена в созданную БД.
Проверка выполнена посредством визуализации хранимой в БД модели кисти средствами OpenGL (рис. 2).
Рис. 2. Воспроизведение хранимой в БД полигональной модели кисти
В последующем полигоны модели кисти были объединены в две группы, и каждой группе назначена текстура. На рис. 3 приведена форма, позволяющая изменять текстуру всех полигонов указанной группы.
Рис. 3. Назначение текстуры группе полигонов