Всем привет. В 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
clang -Ofast -ffast-math -flto -msse4.1 main.c -lm
/nologo /MD /O2 /Ob2 /DNDEBUG - x64! msvc v19.40 VS17.10
Выше представлено то, что получилось. Это, возможно, не лучшая реализация односвязного списка. Но на текущем подходе к скелетной анимации достаточно, теперь можно создавать структуры и заполнять.
При такой организации, конечно придётся хранить структуры рядом с перечислением.
Дополнительная информация: