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:
787
AnimModel.cpp
Normal file
787
AnimModel.cpp
Normal 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,"Ť¶‚‚Ü?ć‚h‚j")) //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
|
||||
}
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user