Всем привет. В 3д помимо моделек — статических, существуют анимации — анимированные модели, которые имеют набор своих данных, эти данные нужны для отображения модельки и её анимирования.
Если выбрать такой язык как С, по каким-либо причинам, рано или поздно можно столкнуться с отсутствием некоторых сущностей. Самой часто используемой сущностью является vector. Если оттолкнуться от того, что простейшая абстракция вектор, и еще чуть упростить можно придти логически к *односвязному списку. Но вот незадача, если пребывать на этом этапе по наименьшему сопротивлению, то придётся на каждую структуру писать свою реализацию это в худшем случае. В этой статье хочу показать как решил вопрос с односвязным списком.
Постановка задачи
**Скелетная анимация — есть моделька,в которой есть сетка — mesh(состоит из полигонов) и скелетная составляющая.
выглядит она например так
Скрытый текст
При работе по сбору данных из анимированной модельки в момент её инициализации, при длительном подходе, я выявил следующую задачу: нужна такая сущность, в которую можно добавлять в конец данные. Количество данных в момент первых обходов могут быть не известны. Тоесть, например, чтобы проинициализировать массивы вершин, их можно просто узнать сколько их, когда. В тоже время, иерархия костной составляющей не так тривиальна. Поэтому, на первом масштабном приближении, я пришел к односвязному списку на костную составляющую.
Так же, как опция я принял решение, что тип будет кастомный — не известен как будто, так же понадобится возможность поиска по индексу и вставка в следующий за найденым индексом операции.
И окончательное требование — соглашение на 1 сущность 1 тип данных.
Пользуюсь компилятором gcc 14.2 в среде Linux, assimp из репозитория
Задача поставлена, решил её так:
Скрытый текст
//язык С #include <stdio.h> #include <stdlib.h> #include <string.h> #pragma pack(1) //кастомная тестовая структура struct EpicLEgendaryNodes_t { char *name; int Num; float weights; }; typedef struct EpicLEgendaryNodes_t EpicLEgendaryNodes; //определение типов enum typeElements { INT, CHAR, DOUBLE, FLOAT, CHARLINE, EPICLEGENDARYNODES }; //структура списка typedef struct NodeL_t NodeL; struct NodeL_t { enum typeElements T; void* data; NodeL* Next; }; //структура хендлера typedef struct VNodeL_t VNodeL; struct VNodeL_t { int NodeSz; NodeL *head; }; //определение необходимого минимума функций, которые покроют поставленную задачу NodeL* getNode(VNodeL *vNodeL,enum typeElements T,void* d); NodeL* addNodeL(void *d,enum typeElements T,VNodeL *vNodeL); NodeL* FREENodeL(VNodeL *vNodeL); NodeL* getByIndex(VNodeL *vNodeL,int Index); NodeL* setByAfterIndex(VNodeL *vNodeL,int Index,void *d,enum typeElements T); void printList(VNodeL *vNodeL); //метод получения ноды - создание NodeL* getNode(VNodeL *vNodeL,enum typeElements T,void* d) { char* dd=NULL; NodeL* tmp=NULL; tmp=malloc(sizeof(NodeL)); switch(T) { case INT: tmp->data=malloc(sizeof(int)); memcpy(tmp->data, d, sizeof(int)); //! использую memcpy vNodeL->NodeSz++; break; case CHAR: tmp->data=malloc(sizeof(char)); memcpy(tmp->data, d, sizeof(char)); vNodeL->NodeSz++; break; case DOUBLE: tmp->data=malloc(sizeof(double)); memcpy(tmp->data, d, sizeof(double)); vNodeL->NodeSz++; break; case FLOAT: tmp->data=malloc(sizeof(float)); memcpy(tmp->data, d, sizeof(float)); vNodeL->NodeSz++; break; case CHARLINE: dd=d; size_t szS=strlen(dd)+1; tmp->data=malloc(sizeof(char)*szS); ///dd[szS]='\0'; memcpy(tmp->data, d, sizeof(char)*szS); vNodeL->NodeSz++; break; case EPICLEGENDARYNODES: tmp->data=malloc(sizeof(EpicLEgendaryNodes)); memcpy(tmp->data, d, sizeof(EpicLEgendaryNodes)); vNodeL->NodeSz++; break; } tmp->Next=NULL; return tmp; } //добавление в конец ноды NodeL* addNodeL(void *d,enum typeElements T,VNodeL *vNodeL) { NodeL* tmp=getNode(vNodeL,T,d); if( vNodeL->head == NULL ) { tmp->T=T; return tmp; } NodeL* cur = vNodeL->head; while( cur->Next != NULL )cur = cur->Next; cur->Next = tmp; return vNodeL->head; } //очищение структуры списка NodeL* FREENodeL(VNodeL *vNodeL) { NodeL* cur=vNodeL->head; while( cur != NULL ) { NodeL* temp=cur; cur = temp->Next; free(temp); } return vNodeL->head; } //получение значения по индексу NodeL* getByIndex(VNodeL *vNodeL,int Index) { if(Index>vNodeL->NodeSz-1) { printf("\n\ngetByIndex\nSearchIndex > SizeNodeLists\n"); return 0; } int tInd=0; NodeL *cur=vNodeL->head; while(cur!=NULL) { if(tInd==Index)break; tInd++; cur=cur->Next; } return cur; } //вставка в место следующее за заданным индексом NodeL* setByAfterIndex(VNodeL *vNodeL,int Index,void *d,enum typeElements T) { NodeL *setting=getNode(vNodeL,T,d); if(Index>vNodeL->NodeSz-1) { printf("SearchIndex > SizeNodeLists\n"); return 0; } int tInd=0; NodeL *cur=vNodeL->head; while(cur!=NULL) { if(tInd==Index)break; tInd++; cur=cur->Next; } NodeL *tmp1=cur->Next; cur->Next=setting; setting->Next=tmp1; return vNodeL->head; } //вывод списка void printList(VNodeL *vNodeL) { if(vNodeL->head==NULL){printf("LIST IS EMPTY\n");return ;} if(vNodeL==NULL){printf("LIST IS EMPTY\n");return ;} NodeL* cur=vNodeL->head; enum typeElements Type=vNodeL->head->T; while(cur!=NULL) { switch(Type) { case INT: printf("%d\n",*(int*)cur->data); break; case CHAR: printf("%c\n",*(char*)cur->data); break; case DOUBLE: printf("%f\n",*(double*)cur->data); break; case FLOAT: printf("%f\n",*(float*)cur->data); break; case CHARLINE: printf("%s\n",(char*)cur->data); break; case EPICLEGENDARYNODES: { EpicLEgendaryNodes *temp=(EpicLEgendaryNodes*)cur->data; printf("%s\n",temp->name); printf("%d\n",temp->Num); printf("%f\n",temp->weights); break; } } cur=cur->Next; } } //типовая прогонка списка 1 тест базовых типов 2 тест кастомной структурки int main() { //1 VNodeL *tempBuf=malloc(sizeof(VNodeL)); tempBuf->head=NULL; tempBuf->NodeSz=0; int t=4; float qq=0.1f; double qw=0.1f; char* strline=(char*)"CHARLINE"; tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++; strline="CHARLINE1"; tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++; strline="CHARLINE2"; tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++; strline="CHARLINE3"; tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++; printList(tempBuf); NodeL* search=getByIndex(tempBuf,4); printf("%d\n",tempBuf->NodeSz); tempBuf->head=setByAfterIndex(tempBuf,2,"CHARLINE10",CHARLINE); search=getByIndex(tempBuf,4); printf("\nSearched element\n%s\n\n",(char*)search->data);//см в printList tempBuf->head=FREENodeL(tempBuf); free(tempBuf); //tempBuf->head=NULL; //printList(tempBuf); //2 VNodeL *tempBuf1=malloc(sizeof(VNodeL));; tempBuf1->head=NULL; tempBuf1->NodeSz=0; EpicLEgendaryNodes temp; temp.name="CHARLINE111111"; temp.Num=t; temp.weights=qq; tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++; temp.name="CHARLINE22221"; temp.Num=t; temp.weights=qq; tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++; temp.name="CHARLINE23333"; temp.Num=t; temp.weights=qq; tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++; temp.name="CHARLINE34444"; temp.Num=t; temp.weights=qq; tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1); printList(tempBuf1); tempBuf1->head=FREENodeL(tempBuf1); free(tempBuf1); //tempBuf1->head=NULL; //printList(tempBuf1); return 0; }
Вывод:
CHARLINE CHARLINE1 CHARLINE2 CHARLINE3 getByIndex SearchIndex > SizeNodeLists 4 Searched element CHARLINE3 CHARLINE111111 8 4.100000 CHARLINE22221 9 5.100000 CHARLINE23333 10 6.100000 CHARLINE34444 11 7.100000
gcc -Ofast -ffast-math -flto -msse4.1 main.c -lm
/nologo /MD /O2 /Ob2 /DNDEBUG — x64! msvc v19.40 VS17.10
Выше представлено то, что получилось. Это, возможно, не лучшая реализация односвязного списка. Но на текущем подходе к скелетной анимации достаточно, теперь можно создавать структуры и заполнять.
При такой организации, конечно придётся хранить структуры рядом с перечислением.
Дополнительная информация:
ссылка на оригинал статьи https://habr.com/ru/articles/882918/
Добавить комментарий