16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 05:29:18 +02:00

Set dp branch as trunk

This commit is contained in:
ShaXbee
2015-04-03 13:34:06 +00:00
commit 45f4c0d98a
132 changed files with 94789 additions and 0 deletions

787
AnimModel.cpp Normal file
View File

@@ -0,0 +1,787 @@
//---------------------------------------------------------------------------
/*
MaSzyna EU07 locomotive simulator
Copyright (C) 2001-2004 Marcin Wozniak and others
*/
#include "system.hpp"
#include "classes.hpp"
#pragma hdrstop
#include "AnimModel.h"
#include "usefull.h"
#include "Timer.h"
#include "MdlMngr.h"
//McZapkie:
#include "Texture.h"
#include "Globals.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
__fastcall TAnimAdvanced::TAnimAdvanced()
{
};
__fastcall TAnimAdvanced::~TAnimAdvanced()
{
//delete[] pVocaloidMotionData; //plik został zmodyfikowany
};
int __fastcall TAnimAdvanced::SortByBone()
{//sortowanie pliku animacji w celu optymalniejszego wykonania
//rekordy zostają ułożone wg kolejnych ramek dla każdej kości
//ułożenie kości alfabetycznie nie jest niezbędne, ale upraszcza sortowanie bąbelkowe
TAnimVocaloidFrame buf; //bufor roboczy (przydało by się pascalowe Swap()
int i,j,k,swaps=0,last=iMovements-1,e;
for (i=0;i<iMovements;++i)
for (j=0;j<15;++j)
if (pMovementData[i].cBone[j]=='\0') //jeśli znacznik końca
for (++j;j<15;++j)
pMovementData[i].cBone[j]='\0'; //zerowanie bajtów za znacznikiem końca
for (i=0;i<last;++i) //do przedostatniego
{//dopóki nie posortowane
j=i; //z którym porównywać
k=i; //z którym zamienić (nie trzeba zamieniać, jeśli ==i)
while (++j<iMovements)
{e=strcmp(pMovementData[k].cBone,pMovementData[j].cBone); //numery trzeba porównywać inaczej
if (e>0)
k=j; //trzeba zamienić - ten pod j jest mniejszy
else if (!e)
if (pMovementData[k].iFrame>pMovementData[j].iFrame)
k=j; //numer klatki pod j jest mniejszy
}
if (k>i)
{//jeśli trzeba przestawić
//buf=pMovementData[i];
//pMovementData[i]=pMovementData[k];
//pMovementData[k]=buf;
memcpy(&buf,pMovementData+i,sizeof(TAnimVocaloidFrame));
memcpy(pMovementData+i,pMovementData+k,sizeof(TAnimVocaloidFrame));
memcpy(pMovementData+k,&buf,sizeof(TAnimVocaloidFrame));
++swaps;
}
}
return swaps;
};
__fastcall TAnimContainer::TAnimContainer()
{
pNext=NULL;
vRotateAngles=vector3(0.0f,0.0f,0.0f); //aktualne kąty obrotu
vDesiredAngles=vector3(0.0f,0.0f,0.0f); //docelowe kąty obrotu
fRotateSpeed=0.0;
vTranslation=vector3(0.0f,0.0f,0.0f); //aktualne przesunięcie
vTranslateTo=vector3(0.0f,0.0f,0.0f); //docelowe przesunięcie
fTranslateSpeed=0.0;
fAngleSpeed=0.0;
pSubModel=NULL;
iAnim=0; //położenie początkowe
pMovementData=NULL; //nie ma zaawansowanej animacji
mAnim=NULL; //nie ma macierzy obrotu dla submodelu
evDone=NULL; //powiadamianie o zakończeniu animacji
acAnimNext=NULL; //na razie jest poza listą
}
__fastcall TAnimContainer::~TAnimContainer()
{
SafeDelete(pNext);
delete mAnim; //AnimContainer jest właścicielem takich macierzy
}
bool __fastcall TAnimContainer::Init(TSubModel *pNewSubModel)
{
fRotateSpeed=0.0f;
pSubModel=pNewSubModel;
return (pSubModel!=NULL);
}
void __fastcall TAnimContainer::SetRotateAnim(vector3 vNewRotateAngles, double fNewRotateSpeed)
{
if (!this) return; //wywoływane z eventu, gdy brak modelu
vDesiredAngles=vNewRotateAngles;
fRotateSpeed=fNewRotateSpeed;
iAnim|=1;
/* //Ra 2014-07: jeśli model nie jest renderowany, to obliczyć czas animacji i dodać event wewnętrzny
//można by też ustawić czas początku animacji zamiast pobierać czas ramki i liczyć różnicę
*/
if (evDone)
{//dołączyć model do listy aniomowania, żeby animacje były przeliczane również bez wyświetlania
if (iAnim>=0)
{//jeśli nie jest jeszcze na liście animacyjnej
acAnimNext=TAnimModel::acAnimList; //pozostałe doklić sobie jako ogon
TAnimModel::acAnimList=this; //a wstawić się na początek
iAnim|=0x80000000; //dodany do listy
}
}
}
void __fastcall TAnimContainer::SetTranslateAnim(vector3 vNewTranslate, double fNewSpeed)
{
if (!this) return; //wywoływane z eventu, gdy brak modelu
vTranslateTo=vNewTranslate;
fTranslateSpeed=fNewSpeed;
iAnim|=2;
/* //Ra 2014-07: jeśli model nie jest renderowany, to obliczyć czas animacji i dodać event wewnętrzny
//można by też ustawić czas początku animacji zamiast pobierać czas ramki i liczyć różnicę
*/
if (evDone)
{//dołączyć model do listy aniomowania, żeby animacje były przeliczane również bez wyświetlania
if (iAnim>=0)
{//jeśli nie jest jeszcze na liście animacyjnej
acAnimNext=TAnimModel::acAnimList; //pozostałe doklić sobie jako ogon
TAnimModel::acAnimList=this; //a wstawić się na początek
iAnim|=0x80000000; //dodany do listy
}
}
}
void __fastcall TAnimContainer::AnimSetVMD(double fNewSpeed)
{
if (!this) return; //wywoływane z eventu, gdy brak modelu
//skala do ustalenia, "cal" japoński (sun) to nieco ponad 3cm
//X-w lewo, Y-w górę, Z-do tyłu
//minimalna wysokość to -7.66, a nadal musi być ponad podłogą
//if (pMovementData->iFrame>0) return; //tylko pierwsza ramka
vTranslateTo=vector3(0.1*pMovementData->f3Vector.x,0.1*pMovementData->f3Vector.z,0.1*pMovementData->f3Vector.y);
if (LengthSquared3(vTranslateTo)>0.0?true:LengthSquared3(vTranslation)>0.0)
{//jeśli ma być przesunięte albo jest przesunięcie
iAnim|=2; //wyłączy się samo
if (fNewSpeed>0.0)
fTranslateSpeed=fNewSpeed; //prędkość jest mnożnikiem, nie podlega skalowaniu
else //za późno na animacje, trzeba przestawić
vTranslation=vTranslateTo;
}
//if ((qCurrent.w<1.0)||(pMovementData->qAngle.w<1.0))
{//jeśli jest jakiś obrót
if (!mAnim)
{mAnim=new float4x4(); //będzie potrzebna macierz animacji
mAnim->Identity(); //jedynkowanie na początek
}
iAnim|=4; //animacja kwaternionowa
qStart=qCurrent; //potrzebna początkowa do interpolacji
//---+ - też niby dobrze, ale nie tak trąca włosy na początku (macha w dół)
//-+-+ - dłoń ma w górze zamiast na pasie w pozycji początkowej
//+--+ - głowa do tyłu (broda w górę) w pozycji początkowej
//--++ - pozycja początkowa dobra, trąca u góry, ale z rękami jakoś nie tak, kółko w przeciwną stronę
//++++ - kładzie się brzuchem do góry
//-+++ - ręce w górze na początku, zamiast w dół, łokieć jakby w przeciwną stronę
//+-++ - nie podnosi ręki do głowy
//++-+ - dłoń ma w górze zamiast na pasie
qDesired=Normalize(float4(-pMovementData->qAngle.x,-pMovementData->qAngle.z,-pMovementData->qAngle.y,pMovementData->qAngle.w)); //tu trzeba będzie osie zamienić
if (fNewSpeed>0.0)
{fAngleSpeed=fNewSpeed; //wtedy animować za pomocą interpolacji
fAngleCurrent=0.0; //początek interpolacji
}
else
{//za późno na animację, można tylko przestawić w docelowe miejsce
fAngleSpeed=0.0;
fAngleCurrent=1.0; //interpolacja zakończona
qCurrent=qDesired;
}
}
//if (!strcmp(pSubModel->pName,"?Z?“?^?[")) //jak główna kość
//if (!strcmp(pSubModel->pName,"Ť¶‚‚Ü?ćhj")) //IK lewej stopy
// WriteLog(AnsiString(pMovementData->iFrame)+": "+AnsiString(pMovementData->f3Vector.x)+" "+AnsiString(pMovementData->f3Vector.y)+" "+AnsiString(pMovementData->f3Vector.z));
}
void __fastcall TAnimContainer::UpdateModel()
{//przeliczanie animacji wykonać tylko raz na model
if (pSubModel) //pozbyć się tego - sprawdzać wcześniej
{
if (fTranslateSpeed!=0.0)
{
vector3 dif=vTranslateTo-vTranslation; //wektor w kierunku docelowym
double l=LengthSquared3(dif); //długość wektora potrzebnego przemieszczenia
if (l>=0.0001)
{//jeśli do przemieszczenia jest ponad 1cm
vector3 s=SafeNormalize(dif); //jednostkowy wektor kierunku
s=s*(fTranslateSpeed*Timer::GetDeltaTime()); //przemieszczenie w podanym czasie z daną prędkością
if (LengthSquared3(s)<l) //żeby nie jechało na drugą stronę
vTranslation+=s;
else
vTranslation=vTranslateTo; //koniec animacji, "koniec animowania" uruchomi się w następnej klatce
}
else
{//koniec animowania
vTranslation=vTranslateTo;
fTranslateSpeed=0.0; //wyłączenie przeliczania wektora
if (LengthSquared3(vTranslation)<=0.0001) //jeśli jest w punkcie początkowym
iAnim&=~2; //wyłączyć zmianę pozycji submodelu
if (evDone) Global::AddToQuery(evDone,NULL); //wykonanie eventu informującego o zakończeniu
}
}
if (fRotateSpeed!=0)
{
/*
double dif= fDesiredAngle-fAngle;
double s= fRotateSpeed*sign(dif)*Timer::GetDeltaTime();
if ((abs(s)-abs(dif))>0)
fAngle= fDesiredAngle;
else
fAngle+= s;
while (fAngle>360) fAngle-= 360;
while (fAngle<-360) fAngle+= 360;
pSubModel->SetRotate(vRotateAxis,fAngle);
*/
bool anim=false;
vector3 dif=vDesiredAngles-vRotateAngles;
double s;
s=fRotateSpeed*sign(dif.x)*Timer::GetDeltaTime();
if (fabs(s)>=fabs(dif.x))
vRotateAngles.x=vDesiredAngles.x;
else
{vRotateAngles.x+=s; anim=true;}
s=fRotateSpeed*sign(dif.y)*Timer::GetDeltaTime();
if (fabs(s)>=fabs(dif.y))
vRotateAngles.y=vDesiredAngles.y;
else
{vRotateAngles.y+=s; anim=true;}
s=fRotateSpeed*sign(dif.z)*Timer::GetDeltaTime();
if (fabs(s)>=fabs(dif.z))
vRotateAngles.z=vDesiredAngles.z;
else
{vRotateAngles.z+=s; anim=true;}
while (vRotateAngles.x>= 360) vRotateAngles.x-=360;
while (vRotateAngles.x<=-360) vRotateAngles.x+=360;
while (vRotateAngles.y>= 360) vRotateAngles.y-=360;
while (vRotateAngles.y<=-360) vRotateAngles.y+=360;
while (vRotateAngles.z>= 360) vRotateAngles.z-=360;
while (vRotateAngles.z<=-360) vRotateAngles.z+=360;
if (vRotateAngles.x==0.0)
if (vRotateAngles.y==0.0)
if (vRotateAngles.z==0.0)
iAnim&=~1; //kąty są zerowe
if (!anim)
{//nie potrzeba przeliczać już
fRotateSpeed=0.0;
if (evDone) Global::AddToQuery(evDone,NULL); //wykonanie eventu informującego o zakończeniu
}
}
if (fAngleSpeed!=0.0)
{//obrót kwaternionu (interpolacja)
}
}
};
void __fastcall TAnimContainer::PrepareModel()
{//tutaj zostawić tylko ustawienie submodelu, przeliczanie ma być w UpdateModel()
if (pSubModel) //pozbyć się tego - sprawdzać wcześniej
{
//nanoszenie animacji na wzorzec
if (iAnim&1) //zmieniona pozycja względem początkowej
pSubModel->SetRotateXYZ(vRotateAngles); //ustawia typ animacji
if (iAnim&2) //zmieniona pozycja względem początkowej
pSubModel->SetTranslate(vTranslation);
if (iAnim&4) //zmieniona pozycja względem początkowej
{
if (fAngleSpeed>0.0f)
{fAngleCurrent+=fAngleSpeed*Timer::GetDeltaTime(); //aktualny parametr interpolacji
if (fAngleCurrent>=1.0f)
{//interpolacja zakończona, ustawienie na pozycję końcową
qCurrent=qDesired;
fAngleSpeed=0.0; //wyłączenie przeliczania wektora
if (evDone) Global::AddToQuery(evDone,NULL); //wykonanie eventu informującego o zakończeniu
}
else
{//obliczanie pozycji pośredniej
//normalizacja jest wymagana do interpolacji w następnej animacji
qCurrent=Normalize(Slerp(qStart,qDesired,fAngleCurrent)); //interpolacja sferyczna kąta
//qCurrent=Slerp(qStart,qDesired,fAngleCurrent); //interpolacja sferyczna kąta
if (qCurrent.w==1.0) //rozpoznać brak obrotu i wyłączyć w iAnim w takim przypadku
iAnim&=~4; //kąty są zerowe
}
}
mAnim->Quaternion(&qCurrent); //wypełnienie macierzy (wymaga normalizacji?)
pSubModel->mAnimMatrix=mAnim; //użyczenie do submodelu (na czas renderowania!)
}
}
//if (!strcmp(pSubModel->pName,"?Z?“?^?[")) //jak główna kość
// WriteLog(AnsiString(pMovementData->iFrame)+": "+AnsiString(iAnim)+" "+AnsiString(vTranslation.x)+" "+AnsiString(vTranslation.y)+" "+AnsiString(vTranslation.z));
}
void __fastcall TAnimContainer::UpdateModelIK()
{//odwrotna kinematyka wyliczana dopiero po ustawieniu macierzy w submodelach
if (pSubModel) //pozbyć się tego - sprawdzać wcześniej
{
if (pSubModel->b_Anim&at_IK)
{//odwrotna kinematyka
float3 d,k;
TSubModel *ch=pSubModel->ChildGet();
switch (pSubModel->b_Anim)
{
case at_IK11: //stopa: ustawić w kierunku czubka (pierwszy potomny)
d=ch->Translation1Get(); //wektor względem aktualnego układu (nie uwzględnia obrotu)
k=float3(RadToDeg(atan2(d.z,hypot(d.x,d.y))),0.0,-RadToDeg(atan2(d.y,d.x))); //proste skierowanie na punkt
pSubModel->SetRotateIK1(k);
//if (!strcmp(pSubModel->pName,"?Z?“?^?[")) //jak główna kość
// WriteLog("--> "+AnsiString(k.x)+" "+AnsiString(k.y)+" "+AnsiString(k.z));
//Ra: to już jest dobrze, może być inna ćwiartka i znak
break;
case at_IK22: //udo: ustawić w kierunku pierwszej potomnej pierwszej potomnej (kostki)
//pozycję kostki należy określić względem kości centralnej (+biodro może być pochylone)
//potem wyliczyć ewentualne odchylenie w tej i następnej
//w sumie to proste, jak wyznaczenie kątów w trójkącie o znanej długości boków...
d=ch->Translation2Get(); //wektor względem aktualnego układu (nie uwzględnia obrotu)
//if ()
{//kość IK jest dalej niż pozycja spoczynkowa
k=float3(RadToDeg(atan2(d.z,hypot(d.x,d.y))),0.0,-RadToDeg(atan2(d.y,d.x))); //proste skierowanie na punkt
pSubModel->SetRotateIK1(k);
}
break;
}
}
}
}
bool __fastcall TAnimContainer::InMovement()
{//czy trwa animacja - informacja dla obrotnicy
return (fRotateSpeed!=0.0)||(fTranslateSpeed!=0.0);
}
void __fastcall TAnimContainer::EventAssign(TEvent *ev)
{//przypisanie eventu wykonywanego po zakończeniu animacji
evDone=ev;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
__fastcall TAnimModel::TAnimModel()
{
pRoot=NULL;
pModel=NULL;
iNumLights=0;
fBlinkTimer=0;
ReplacableSkinId[0]=0;
ReplacableSkinId[1]=0;
ReplacableSkinId[2]=0;
ReplacableSkinId[3]=0;
ReplacableSkinId[4]=0;
for (int i=0;i<iMaxNumLights;i++)
{
LightsOn[i]=LightsOff[i]=NULL; //normalnie nie ma
lsLights[i]=ls_Off; //a jeśli są, to wyłączone
}
vAngle.x=vAngle.y=vAngle.z=0.0; //zerowanie obrotów egzemplarza
pAdvanced=NULL; //nie ma zaawansowanej animacji
fDark=0.25; //standardowy próg zaplania
fOnTime=0.66;
fOffTime=fOnTime+0.66;
}
__fastcall TAnimModel::~TAnimModel()
{
delete pAdvanced; //nie ma zaawansowanej animacji
SafeDelete(pRoot);
}
bool __fastcall TAnimModel::Init(TModel3d *pNewModel)
{
fBlinkTimer=double(random(1000*fOffTime))/(1000*fOffTime);;
pModel=pNewModel;
return (pModel!=NULL);
}
bool __fastcall TAnimModel::Init(AnsiString asName, AnsiString asReplacableTexture)
{
if (asReplacableTexture.SubString(1,1)=="*") //od gwiazdki zaczynają się teksty na wyświetlaczach
asText=asReplacableTexture.SubString(2,asReplacableTexture.Length()-1); //zapamiętanie tekstu
else if (asReplacableTexture!="none")
ReplacableSkinId[1]=TTexturesManager::GetTextureID(NULL,NULL,asReplacableTexture.c_str());
if (TTexturesManager::GetAlpha(ReplacableSkinId[1]))
iTexAlpha=0x31310031; //tekstura z kanałem alfa - nie renderować w cyklu nieprzezroczystych
else
iTexAlpha=0x30300030; //tekstura nieprzezroczysta - nie renderować w cyklu przezroczystych
return (Init(TModelsManager::GetModel(asName.c_str())));
}
bool __fastcall TAnimModel::Load(cParser *parser, bool ter)
{//rozpoznanie wpisu modelu i ustawienie świateł
AnsiString str;
std::string token;
parser->getTokens(); //nazwa modelu
*parser >> token;
str=AnsiString(token.c_str());
parser->getTokens(1,false); //tekstura (zmienia na małe)
*parser >> token;
if (!Init(str,AnsiString(token.c_str())))
{if (str!="notload")
{//gdy brak modelu
if (ter) //jeśli teren
{if (str.SubString(str.Length()-3,4)==".t3d")
str[str.Length()-2]='e';
Global::asTerrainModel=str;
WriteLog(AnsiString("Terrain model \""+str+"\" will be created."));
}
else
ErrorLog(AnsiString("Missed file: "+str));
}
}
else
{//wiązanie świateł, o ile model wczytany
LightsOn[0]=pModel->GetFromName("Light_On00");
LightsOn[1]=pModel->GetFromName("Light_On01");
LightsOn[2]=pModel->GetFromName("Light_On02");
LightsOn[3]=pModel->GetFromName("Light_On03");
LightsOn[4]=pModel->GetFromName("Light_On04");
LightsOn[5]=pModel->GetFromName("Light_On05");
LightsOn[6]=pModel->GetFromName("Light_On06");
LightsOn[7]=pModel->GetFromName("Light_On07");
LightsOff[0]=pModel->GetFromName("Light_Off00");
LightsOff[1]=pModel->GetFromName("Light_Off01");
LightsOff[2]=pModel->GetFromName("Light_Off02");
LightsOff[3]=pModel->GetFromName("Light_Off03");
LightsOff[4]=pModel->GetFromName("Light_Off04");
LightsOff[5]=pModel->GetFromName("Light_Off05");
LightsOff[6]=pModel->GetFromName("Light_Off06");
LightsOff[7]=pModel->GetFromName("Light_Off07");
}
for (int i=0;i<iMaxNumLights;++i)
if (LightsOn[i]||LightsOff[i]) //Ra: zlikwidowałem wymóg istnienia obu
iNumLights=i+1;
int i=0;
int ti;
parser->getTokens();
*parser >> token;
if (token.compare( "lights" ) == 0)
{
parser->getTokens();
*parser >> token;
str=AnsiString(token.c_str());
do
{
ti=str.ToDouble(); //stan światła jest liczbą z ułamkiem
LightSet(i,ti);
i++;
parser->getTokens();
*parser >> token;
str=AnsiString(token.c_str());
} while (str!="endmodel");
}
return true;
}
TAnimContainer* __fastcall TAnimModel::AddContainer(char *pName)
{//dodanie sterowania submodelem dla egzemplarza
if (!pModel) return NULL;
TSubModel *tsb=pModel->GetFromName(pName);
if (tsb)
{
TAnimContainer *tmp=new TAnimContainer();
tmp->Init(tsb);
tmp->pNext=pRoot;
pRoot=tmp;
return tmp;
}
return NULL;
}
TAnimContainer* __fastcall TAnimModel::GetContainer(char *pName)
{//szukanie/dodanie sterowania submodelem dla egzemplarza
if (!pName) return pRoot; //pobranie pierwszego (dla obrotnicy)
TAnimContainer *pCurrent;
for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext)
//if (pCurrent->GetName()==pName)
if (stricmp(pCurrent->NameGet(),pName)==0)
return pCurrent;
return AddContainer(pName);
}
void __fastcall TAnimModel::RaAnimate()
{//przeliczenie animacji - jednorazowo na klatkę
//Ra 2F1I: to by można pomijać dla modeli bez animacji, których jest większość
TAnimContainer *pCurrent;
for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext)
if (!pCurrent->evDone) //jeśli jest bez eventu
pCurrent->UpdateModel(); //przeliczenie animacji każdego submodelu
//if () //tylko dla modeli z IK !!!!
for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext) //albo osobny łańcuch
pCurrent->UpdateModelIK(); //przeliczenie odwrotnej kinematyki
};
void __fastcall TAnimModel::RaPrepare()
{//ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza
fBlinkTimer-=Timer::GetDeltaTime();
if (fBlinkTimer<=0) fBlinkTimer=fOffTime;
bool state; //stan światła
for (int i=0;i<iNumLights;i++)
{switch (lsLights[i])
{case ls_Blink: //migotanie
state=fBlinkTimer<fOnTime; break;
case ls_Dark: //zapalone, gdy ciemno
state=Global::fLuminance<=fDark; break;
default: //zapalony albo zgaszony
state=(lsLights[i]==ls_On);
}
if (LightsOn[i]) LightsOn[i]->iVisible=state;
if (LightsOff[i]) LightsOff[i]->iVisible=!state;
}
TSubModel::iInstance=(int)this; //żeby nie robić cudzych animacji
TSubModel::pasText=&asText; //przekazanie tekstu do wyświetlacza (!!!! do przemyślenia)
if (pAdvanced) //jeśli jest zaawansowana animacja
Advanced(); //wykonać co tam trzeba
TAnimContainer *pCurrent;
for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext)
pCurrent->PrepareModel(); //ustawienie animacji egzemplarza dla każdego submodelu
//if () //tylko dla modeli z IK !!!!
// for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext) //albo osobny łańcuch
// pCurrent->UpdateModelIK(); //przeliczenie odwrotnej kinematyki
}
void __fastcall TAnimModel::RenderVBO(vector3 pPosition,double fAngle)
{//sprawdza światła i rekurencyjnie renderuje TModel3d
RaAnimate(); //jednorazowe przeliczenie animacji
RaPrepare();
if (pModel) //renderowanie rekurencyjne submodeli
pModel->RaRender(pPosition,fAngle,ReplacableSkinId,iTexAlpha);
}
void __fastcall TAnimModel::RenderAlphaVBO(vector3 pPosition,double fAngle)
{
RaPrepare();
if (pModel) //renderowanie rekurencyjne submodeli
pModel->RaRenderAlpha(pPosition,fAngle,ReplacableSkinId,iTexAlpha);
};
void __fastcall TAnimModel::RenderDL(vector3 pPosition,double fAngle)
{
RaAnimate(); //jednorazowe przeliczenie animacji
RaPrepare();
if (pModel) //renderowanie rekurencyjne submodeli
pModel->Render(pPosition,fAngle,ReplacableSkinId,iTexAlpha);
}
void __fastcall TAnimModel::RenderAlphaDL(vector3 pPosition,double fAngle)
{
RaPrepare();
if (pModel)
pModel->RenderAlpha(pPosition,fAngle,ReplacableSkinId,iTexAlpha);
};
int __fastcall TAnimModel::Flags()
{//informacja dla TGround, czy ma być w Render, RenderAlpha, czy RenderMixed
int i=pModel?pModel->Flags():0; //pobranie flag całego modelu
if (ReplacableSkinId[1]>0) //jeśli ma wymienną teksturę 0
i|=(i&0x01010001)*((iTexAlpha&1)?0x20:0x10);
//if (ReplacableSkinId[2]>0) //jeśli ma wymienną teksturę 1
// i|=(i&0x02020002)*((iTexAlpha&1)?0x10:0x08);
//if (ReplacableSkinId[3]>0) //jeśli ma wymienną teksturę 2
// i|=(i&0x04040004)*((iTexAlpha&1)?0x08:0x04);
//if (ReplacableSkinId[4]>0) //jeśli ma wymienną teksturę 3
// i|=(i&0x08080008)*((iTexAlpha&1)?0x04:0x02);
return i;
};
//-----------------------------------------------------------------------------
//2011-03-16 cztery nowe funkcje renderowania z możliwością pochylania obiektów
//-----------------------------------------------------------------------------
void __fastcall TAnimModel::RenderDL(vector3* vPosition)
{
RaAnimate(); //jednorazowe przeliczenie animacji
RaPrepare();
if (pModel) //renderowanie rekurencyjne submodeli
pModel->Render(vPosition,&vAngle,ReplacableSkinId,iTexAlpha);
};
void __fastcall TAnimModel::RenderAlphaDL(vector3* vPosition)
{
RaPrepare();
if (pModel) //renderowanie rekurencyjne submodeli
pModel->RenderAlpha(vPosition,&vAngle,ReplacableSkinId,iTexAlpha);
};
void __fastcall TAnimModel::RenderVBO(vector3* vPosition)
{
RaAnimate(); //jednorazowe przeliczenie animacji
RaPrepare();
if (pModel) //renderowanie rekurencyjne submodeli
pModel->RaRender(vPosition,&vAngle,ReplacableSkinId,iTexAlpha);
};
void __fastcall TAnimModel::RenderAlphaVBO(vector3* vPosition)
{
RaPrepare();
if (pModel) //renderowanie rekurencyjne submodeli
pModel->RaRenderAlpha(vPosition,&vAngle,ReplacableSkinId,iTexAlpha);
};
//---------------------------------------------------------------------------
bool __fastcall TAnimModel::TerrainLoaded()
{//zliczanie kwadratów kilometrowych (główna linia po Next) do tworznia tablicy
return (this?pModel!=NULL:false);
};
int __fastcall TAnimModel::TerrainCount()
{//zliczanie kwadratów kilometrowych (główna linia po Next) do tworznia tablicy
return pModel?pModel->TerrainCount():0;
};
TSubModel* __fastcall TAnimModel::TerrainSquare(int n)
{//pobieranie wskaźników do pierwszego submodelu
return pModel?pModel->TerrainSquare(n):0;
};
void __fastcall TAnimModel::TerrainRenderVBO(int n)
{//renderowanie terenu z VBO
if (pModel) pModel->TerrainRenderVBO(n);
};
//---------------------------------------------------------------------------
void __fastcall TAnimModel::Advanced()
{//wykonanie zaawansowanych animacji na submodelach
pAdvanced->fCurrent+=pAdvanced->fFrequency*Timer::GetDeltaTime(); //aktualna ramka zmiennoprzecinkowo
int frame=floor(pAdvanced->fCurrent); //numer klatki jako int
TAnimContainer *pCurrent;
if (pAdvanced->fCurrent>=pAdvanced->fLast)
{//animacja została zakończona
delete pAdvanced;
pAdvanced=NULL; //dalej już nic
for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext)
if (pCurrent->pMovementData) //jeśli obsługiwany tabelką animacji
pCurrent->pMovementData=NULL; //usuwanie wskaźników
}
else
{//coś trzeba poanimować - wszystkie animowane submodele są w tym łańcuchu
for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext)
if (pCurrent->pMovementData) //jeśli obsługiwany tabelką animacji
if (frame>=pCurrent->pMovementData->iFrame) //koniec czekania
if (!strcmp(pCurrent->pMovementData->cBone,(pCurrent->pMovementData+1)->cBone))
{//jak kolejna ramka dotyczy tego samego submodelu, ustawić animację do kolejnej ramki
++pCurrent->pMovementData; //kolejna klatka
pCurrent->AnimSetVMD(pAdvanced->fFrequency/(double(pCurrent->pMovementData->iFrame)-pAdvanced->fCurrent));
}
else
pCurrent->pMovementData=NULL; //inna nazwa, animowanie zakończone w aktualnym położeniu
}
};
void __fastcall TAnimModel::AnimationVND(void* pData, double a, double b, double c, double d)
{//rozpoczęcie wykonywania animacji z podanego pliku
//tabela w pliku musi być posortowana wg klatek dla kolejnych kości!
//skrócone nagranie ma 3:42 = 222 sekundy, animacja kończy się na klatce 6518
//daje to 29.36 (~=30) klatek na sekundę
//w opisach jest podawane 24 albo 36 jako standard => powiedzmy, parametr (d) to FPS animacji
delete pAdvanced; //usunięcie ewentualnego poprzedniego
pAdvanced=NULL; //gdyby się nie udało rozpoznać pliku
if (AnsiString((char*)pData)=="Vocaloid Motion Data 0002")
{
pAdvanced=new TAnimAdvanced();
pAdvanced->pVocaloidMotionData=(char*)pData; //podczepienie pliku danych
pAdvanced->iMovements=*((int*)(((char*)pData)+50)); //numer ostatniej klatki
pAdvanced->pMovementData=(TAnimVocaloidFrame*)(((char*)pData)+54); //rekordy animacji
//WriteLog(sizeof(TAnimVocaloidFrame));
pAdvanced->fFrequency=d;
pAdvanced->fCurrent=0.0; //aktualna ramka
pAdvanced->fLast=0.0; //ostatnia ramka
/*
if (0) //jeśli włączone sortowanie plików VMD (trochę się przeciąga)
if (pAdvanced->SortByBone()) //próba posortowania
{//zapisać posortowany plik, jeśli dokonano zmian
TFileStream *fs=new TFileStream("models\\1.vmd",fmCreate);
fs->Write(pData,2198342); //2948728);
delete fs;
}
*/
int i,j,k,idx;
AnsiString name;
TAnimContainer *pSub;
for (i=0;i<pAdvanced->iMovements;++i)
{if (strcmp(pAdvanced->pMovementData[i].cBone,name.c_str()))
{//jeśli pozycja w tabelce nie była wyszukiwana w submodelach
pSub=GetContainer(pAdvanced->pMovementData[i].cBone); //szukanie
if (pSub) //znaleziony
{pSub->pMovementData=pAdvanced->pMovementData+i; //gotów do animowania
pSub->AnimSetVMD(0.0); //usuawienie pozycji początkowej (powinna być zerowa, inaczej będzie skok)
}
name=AnsiString(pAdvanced->pMovementData[i].cBone); //nowa nazwa do pomijania
}
if (pAdvanced->fLast<pAdvanced->pMovementData[i].iFrame)
pAdvanced->fLast=pAdvanced->pMovementData[i].iFrame;
}
/*
for (i=0;i<pAdvanced->iMovements;++i)
if (AnsiString(pAdvanced->pMovementData[i+1].cBone)!=AnsiString(pAdvanced->pMovementData[i].cBone))
{//generowane dla ostatniej klatki danej kości
name="";
for (j=0;j<15;j++)
name+=IntToHex((unsigned char)pAdvanced->pMovementData[i].cBone[j],2);
WriteLog(name+","
+AnsiString(pAdvanced->pMovementData[i].cBone)+","
+AnsiString(idx)+"," //indeks
+AnsiString(i+1-idx)+"," //ile pozycji animacji
+AnsiString(k)+"," //pierwsza klatka
+AnsiString(pAdvanced->pMovementData[i].iFrame)+"," //ostatnia klatka
+AnsiString(pAdvanced->pMovementData[i].f3Vector.x)+","
+AnsiString(pAdvanced->pMovementData[i].f3Vector.y)+","
+AnsiString(pAdvanced->pMovementData[i].f3Vector.z)+","
+AnsiString(pAdvanced->pMovementData[i].fAngle[0])+","
+AnsiString(pAdvanced->pMovementData[i].fAngle[1])+","
+AnsiString(pAdvanced->pMovementData[i].fAngle[2])+","
+AnsiString(pAdvanced->pMovementData[i].fAngle[3])
);
idx=i+1;
k=pAdvanced->pMovementData[i+1].iFrame; //pierwsza klatka następnego
}
else
if (pAdvanced->pMovementData[i].iFrame>0)
if ((k>pAdvanced->pMovementData[i].iFrame)||(k==0))
k=pAdvanced->pMovementData[i].iFrame; //pierwsza niezerowa ramka
*/
/*
for (i=0;i<pAdvanced->iMovements;++i)
if (AnsiString(pAdvanced->pMovementData[i].cBone)=="\x89\x45\x90\x65\x8E\x77\x82\x4F")
{name="";
for (j=0;j<15;j++)
name+=IntToHex((unsigned char)pAdvanced->pMovementData[i].cBone[j],2);
WriteLog(name+","
+AnsiString(i)+"," //pozycja w tabeli
+AnsiString(pAdvanced->pMovementData[i].iFrame)+"," //pierwsza klatka
);
}
*/
}
};
//---------------------------------------------------------------------------
void __fastcall TAnimModel::LightSet(int n,float v)
{//ustawienie światła (n) na wartość (v)
if (n>=iMaxNumLights) return; //przekroczony zakres
lsLights[n]=TLightState(int(v));
switch (lsLights[n])
{//interpretacja ułamka zależnie od typu
case 0: //ustalenie czasu migotania, t<1s (f>1Hz), np. 0.1 => t=0.1 (f=10Hz)
break;
case 1: //ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu
break;
case 2: //ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s)
break;
case 3: //zapalenie świateł zależne od oświetlenia scenerii
if (v>3.0)
fDark=v-3.0; //ustawienie indywidualnego progu zapalania
else
fDark=0.25; //standardowy próg zaplania
break;
}
};
//---------------------------------------------------------------------------
void __fastcall TAnimModel::AnimUpdate(double dt)
{//wykonanie zakolejkowanych animacji, nawet gdy modele nie są aktualnie wyświetlane
TAnimContainer *p=TAnimModel::acAnimList;
while (p)
{//jeśli w ogóle jest co animować
//if ((*p)->fTranslateSpeed==0.0)
// if ((*p)->fRotateSpeed==0.0)
// {//jak się naanimował, to usunąć z listy
// *p=(*p)->ListRemove(); //zwraca wskaźnik do kolejnego z listy
// }
p->UpdateModel();
p=p->acAnimNext; //na razie bez usuwania z listy, bo głównie obrotnica na nią wchodzi
}
};
//---------------------------------------------------------------------------