mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
2284 lines
94 KiB
C++
2284 lines
94 KiB
C++
//---------------------------------------------------------------------------
|
|
/*
|
|
MaSzyna EU07 locomotive simulator
|
|
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
|
|
|
|
*/
|
|
|
|
#include "system.hpp"
|
|
#include "classes.hpp"
|
|
#pragma hdrstop
|
|
|
|
#include "Model3d.h"
|
|
#include "Usefull.h"
|
|
#include "Texture.h"
|
|
#include "logs.h"
|
|
#include "Globals.h"
|
|
#include "Timer.h"
|
|
#include "mtable.hpp"
|
|
//---------------------------------------------------------------------------
|
|
#pragma package(smart_init)
|
|
|
|
double TSubModel::fSquareDist = 0;
|
|
int TSubModel::iInstance; // numer renderowanego egzemplarza obiektu
|
|
GLuint *TSubModel::ReplacableSkinId = NULL;
|
|
int TSubModel::iAlpha = 0x30300030; // maska do testowania flag tekstur wymiennych
|
|
TModel3d *TSubModel::pRoot; // Ra: tymczasowo wskaŸnik na model widoczny z submodelu
|
|
AnsiString *TSubModel::pasText;
|
|
// przyk³ady dla TSubModel::iAlpha:
|
|
// 0x30300030 - wszystkie bez kana³u alfa
|
|
// 0x31310031 - tekstura -1 u¿ywana w danym cyklu, pozosta³e nie
|
|
// 0x32320032 - tekstura -2 u¿ywana w danym cyklu, pozosta³e nie
|
|
// 0x34340034 - tekstura -3 u¿ywana w danym cyklu, pozosta³e nie
|
|
// 0x38380038 - tekstura -4 u¿ywana w danym cyklu, pozosta³e nie
|
|
// 0x3F3F003F - wszystkie wymienne tekstury u¿ywane w danym cyklu
|
|
// Ale w TModel3d okerœla przezroczystoœæ tekstur wymiennych!
|
|
|
|
int TSubModelInfo::iTotalTransforms = 0; // iloœæ transformów
|
|
int TSubModelInfo::iTotalNames = 0; // d³ugoœæ obszaru nazw
|
|
int TSubModelInfo::iTotalTextures = 0; // d³ugoœæ obszaru tekstur
|
|
int TSubModelInfo::iCurrent = 0; // aktualny obiekt
|
|
TSubModelInfo *TSubModelInfo::pTable = NULL; // tabele obiektów pomocniczych
|
|
|
|
char *TStringPack::String(int n)
|
|
{ // zwraca wskaŸnik do ³añcucha o podanym numerze
|
|
if (index ? n < (index[1] >> 2) - 2 : false)
|
|
return data + 8 + index[n + 2]; // indeks upraszcza kwestiê wyszukiwania
|
|
// jak nie ma indeksu, to trzeba szukaæ
|
|
int max = *((int *)(data + 4)); // d³ugoœæ obszaru ³añcuchów
|
|
char *ptr = data + 8; // pocz¹ek obszaru ³añcuchów
|
|
for (int i = 0; i < n; ++i)
|
|
{ // wyszukiwanie ³añcuchów nie jest zbyt optymalne, ale nie musi byæ
|
|
while (*ptr)
|
|
++ptr; // wyszukiwanie zera
|
|
++ptr; // pominiêcie zera
|
|
if (ptr > data + max)
|
|
return NULL; // zbyt wysoki numer
|
|
}
|
|
return ptr;
|
|
};
|
|
|
|
__fastcall TSubModel::TSubModel()
|
|
{
|
|
ZeroMemory(this, sizeof(TSubModel)); // istotne przy zapisywaniu wersji binarnej
|
|
FirstInit();
|
|
};
|
|
|
|
void __fastcall TSubModel::FirstInit()
|
|
{
|
|
eType = TP_ROTATOR;
|
|
Vertices = NULL;
|
|
uiDisplayList = 0;
|
|
iNumVerts = -1; // do sprawdzenia
|
|
iVboPtr = -1;
|
|
fLight = -1.0; //œwietcenie wy³¹czone
|
|
v_RotateAxis = float3(0, 0, 0);
|
|
v_TransVector = float3(0, 0, 0);
|
|
f_Angle = 0;
|
|
b_Anim = at_None;
|
|
b_aAnim = at_None;
|
|
fVisible = 0.0; // zawsze widoczne
|
|
iVisible = 1;
|
|
fMatrix = NULL; // to samo co iMatrix=0;
|
|
Next = NULL;
|
|
Child = NULL;
|
|
TextureID = 0;
|
|
// TexAlpha=false;
|
|
iFlags = 0x0200; // bit 9=1: submodel zosta³ utworzony a nie ustawiony na wczytany plik
|
|
// TexHash=false;
|
|
// Hits=NULL;
|
|
// CollisionPts=NULL;
|
|
// CollisionPtsCount=0;
|
|
Opacity = 1.0; // przy wczytywaniu modeli by³o dzielone przez 100...
|
|
bWire = false;
|
|
fWireSize = 0;
|
|
fNearAttenStart = 40;
|
|
fNearAttenEnd = 80;
|
|
bUseNearAtten = false;
|
|
iFarAttenDecay = 0;
|
|
fFarDecayRadius = 100;
|
|
fCosFalloffAngle = 0.5; // 120°?
|
|
fCosHotspotAngle = 0.3; // 145°?
|
|
fCosViewAngle = 0;
|
|
fSquareMaxDist = 10000 * 10000; // 10km
|
|
fSquareMinDist = 0;
|
|
iName = -1; // brak nazwy
|
|
iTexture = 0; // brak tekstury
|
|
// asName="";
|
|
// asTexture="";
|
|
pName = pTexture = NULL;
|
|
f4Ambient[0] = f4Ambient[1] = f4Ambient[2] = f4Ambient[3] = 1.0; //{1,1,1,1};
|
|
f4Diffuse[0] = f4Diffuse[1] = f4Diffuse[2] = f4Diffuse[3] = 1.0; //{1,1,1,1};
|
|
f4Specular[0] = f4Specular[1] = f4Specular[2] = 0.0;
|
|
f4Specular[3] = 1.0; //{0,0,0,1};
|
|
f4Emision[0] = f4Emision[1] = f4Emision[2] = f4Emision[3] = 1.0;
|
|
smLetter = NULL; // u¿ywany tylko roboczo dla TP_TEXT, do przyspieszenia wyœwietlania
|
|
};
|
|
|
|
__fastcall TSubModel::~TSubModel()
|
|
{
|
|
if (uiDisplayList)
|
|
glDeleteLists(uiDisplayList, 1);
|
|
if (iFlags & 0x0200)
|
|
{ // wczytany z pliku tekstowego musi sam posprz¹taæ
|
|
// SafeDeleteArray(Indices);
|
|
SafeDelete(Next);
|
|
SafeDelete(Child);
|
|
delete fMatrix; // w³asny transform trzeba usun¹æ (zawsze jeden)
|
|
delete[] Vertices;
|
|
delete[] pTexture;
|
|
delete[] pName;
|
|
}
|
|
/*
|
|
else
|
|
{//wczytano z pliku binarnego (nie jest w³aœcicielem tablic)
|
|
}
|
|
*/
|
|
delete[] smLetter; // u¿ywany tylko roboczo dla TP_TEXT, do przyspieszenia wyœwietlania
|
|
};
|
|
|
|
void __fastcall TSubModel::TextureNameSet(const char *n)
|
|
{ // ustawienie nazwy submodelu, o ile nie jest wczytany z E3D
|
|
if (iFlags & 0x0200)
|
|
{ // tylko je¿eli submodel zosta utworzony przez new
|
|
delete[] pTexture; // usuniêcie poprzedniej
|
|
int i = strlen(n);
|
|
if (i)
|
|
{ // utworzenie nowej
|
|
pTexture = new char[i + 1];
|
|
strcpy(pTexture, n);
|
|
}
|
|
else
|
|
pTexture = NULL;
|
|
}
|
|
};
|
|
|
|
void __fastcall TSubModel::NameSet(const char *n)
|
|
{ // ustawienie nazwy submodelu, o ile nie jest wczytany z E3D
|
|
if (iFlags & 0x0200)
|
|
{ // tylko je¿eli submodel zosta utworzony przez new
|
|
delete[] pName; // usuniêcie poprzedniej
|
|
int i = strlen(n);
|
|
if (i)
|
|
{ // utworzenie nowej
|
|
pName = new char[i + 1];
|
|
strcpy(pName, n);
|
|
}
|
|
else
|
|
pName = NULL;
|
|
}
|
|
};
|
|
|
|
// int __fastcall TSubModel::SeekFaceNormal(DWORD *Masks, int f,DWORD dwMask,vector3 *pt,GLVERTEX
|
|
// *Vertices)
|
|
int __fastcall TSubModel::SeekFaceNormal(DWORD *Masks, int f, DWORD dwMask, float3 *pt,
|
|
float8 *Vertices)
|
|
{ // szukanie punktu stycznego do (pt), zwraca numer wierzcho³ka, a nie trójk¹ta
|
|
int iNumFaces = iNumVerts / 3; // bo maska powierzchni jest jedna na trójk¹t
|
|
// GLVERTEX *p; //roboczy wskaŸnik
|
|
float8 *p; // roboczy wskaŸnik
|
|
for (int i = f; i < iNumFaces; ++i) // pêtla po trójk¹tach, od trójk¹ta (f)
|
|
if (Masks[i] & dwMask) // jeœli wspólna maska powierzchni
|
|
{
|
|
p = Vertices + 3 * i;
|
|
if (p->Point == *pt)
|
|
return 3 * i;
|
|
if ((++p)->Point == *pt)
|
|
return 3 * i + 1;
|
|
if ((++p)->Point == *pt)
|
|
return 3 * i + 2;
|
|
}
|
|
return -1; // nie znaleziono stycznego wierzcho³ka
|
|
}
|
|
|
|
float emm1[] = {1, 1, 1, 0};
|
|
float emm2[] = {0, 0, 0, 1};
|
|
|
|
inline double readIntAsDouble(cParser &parser, int base = 255)
|
|
{
|
|
int value;
|
|
parser.getToken(value);
|
|
return double(value) / base;
|
|
};
|
|
|
|
template <typename ColorT> inline void readColor(cParser &parser, ColorT *color)
|
|
{
|
|
parser.ignoreToken();
|
|
color[0] = readIntAsDouble(parser);
|
|
color[1] = readIntAsDouble(parser);
|
|
color[2] = readIntAsDouble(parser);
|
|
};
|
|
|
|
inline void readColor(cParser &parser, int &color)
|
|
{
|
|
int r, g, b;
|
|
parser.ignoreToken();
|
|
parser.getToken(r);
|
|
parser.getToken(g);
|
|
parser.getToken(b);
|
|
color = r + (g << 8) + (b << 16);
|
|
};
|
|
/*
|
|
inline void readMatrix(cParser& parser,matrix4x4& matrix)
|
|
{//Ra: wczytanie transforma
|
|
for (int x=0;x<=3;x++) //wiersze
|
|
for (int y=0;y<=3;y++) //kolumny
|
|
parser.getToken(matrix(x)[y]);
|
|
};
|
|
*/
|
|
inline void readMatrix(cParser &parser, float4x4 &matrix)
|
|
{ // Ra: wczytanie transforma
|
|
for (int x = 0; x <= 3; x++) // wiersze
|
|
for (int y = 0; y <= 3; y++) // kolumny
|
|
parser.getToken(matrix(x)[y]);
|
|
};
|
|
|
|
int __fastcall TSubModel::Load(cParser &parser, TModel3d *Model, int Pos, bool dynamic)
|
|
{ // Ra: VBO tworzone na poziomie modelu, a nie submodeli
|
|
iNumVerts = 0;
|
|
iVboPtr = Pos; // pozycja w VBO
|
|
// TMaterialColorf Ambient,Diffuse,Specular;
|
|
// GLuint TextureID;
|
|
// char *extName;
|
|
if (!parser.expectToken("type:"))
|
|
Error("Model type parse failure!");
|
|
{
|
|
std::string type;
|
|
parser.getToken(type);
|
|
if (type == "mesh")
|
|
eType = GL_TRIANGLES; // submodel - trójkaty
|
|
else if (type == "point")
|
|
eType = GL_POINTS; // co to niby jest?
|
|
else if (type == "freespotlight")
|
|
eType = TP_FREESPOTLIGHT; //œwiate³ko
|
|
else if (type == "text")
|
|
eType = TP_TEXT; // wyœwietlacz tekstowy (generator napisów)
|
|
else if (type == "stars")
|
|
eType = TP_STARS; // wiele punktów œwietlnych
|
|
};
|
|
parser.ignoreToken();
|
|
std::string token;
|
|
// parser.getToken(token1); //ze zmian¹ na ma³e!
|
|
parser.getTokens(1, false); // nazwa submodelu bez zmieny na ma³e
|
|
parser >> token;
|
|
NameSet(token.c_str());
|
|
if (dynamic)
|
|
{ // dla pojazdu, blokujemy za³¹czone submodele, które mog¹ byæ nieobs³ugiwane
|
|
if (token.find("_on") + 3 == token.length()) // jeœli nazwa koñczy siê na "_on"
|
|
iVisible = 0; // to domyœlnie wy³¹czyæ, ¿eby siê nie nak³ada³o z obiektem "_off"
|
|
}
|
|
else // dla pozosta³ych modeli blokujemy zapalone œwiat³a, które mog¹ byæ nieobs³ugiwane
|
|
if (token.find("Light_On") == 0) // jeœli nazwa zaczyna siê od "Light_On"
|
|
iVisible = 0; // to domyœlnie wy³¹czyæ, ¿eby siê nie nak³ada³o z obiektem "Light_Off"
|
|
|
|
if (parser.expectToken("anim:")) // Ra: ta informacja by siê przyda³a!
|
|
{ // rodzaj animacji
|
|
std::string type;
|
|
parser.getToken(type);
|
|
if (type != "false")
|
|
{
|
|
iFlags |= 0x4000; // jak animacja, to trzeba przechowywaæ macierz zawsze
|
|
if (type == "seconds_jump")
|
|
b_Anim = b_aAnim = at_SecondsJump; // sekundy z przeskokiem
|
|
else if (type == "minutes_jump")
|
|
b_Anim = b_aAnim = at_MinutesJump; // minuty z przeskokiem
|
|
else if (type == "hours_jump")
|
|
b_Anim = b_aAnim = at_HoursJump; // godziny z przeskokiem
|
|
else if (type == "hours24_jump")
|
|
b_Anim = b_aAnim = at_Hours24Jump; // godziny z przeskokiem
|
|
else if (type == "seconds")
|
|
b_Anim = b_aAnim = at_Seconds; // minuty p³ynnie
|
|
else if (type == "minutes")
|
|
b_Anim = b_aAnim = at_Minutes; // minuty p³ynnie
|
|
else if (type == "hours")
|
|
b_Anim = b_aAnim = at_Hours; // godziny p³ynnie
|
|
else if (type == "hours24")
|
|
b_Anim = b_aAnim = at_Hours24; // godziny p³ynnie
|
|
else if (type == "billboard")
|
|
b_Anim = b_aAnim = at_Billboard; // obrót w pionie do kamery
|
|
else if (type == "wind")
|
|
b_Anim = b_aAnim = at_Wind; // ruch pod wp³ywem wiatru
|
|
else if (type == "sky")
|
|
b_Anim = b_aAnim = at_Sky; // aniamacja nieba
|
|
else if (type == "ik")
|
|
b_Anim = b_aAnim = at_IK; // IK: zadaj¹cy
|
|
else if (type == "ik11")
|
|
b_Anim = b_aAnim = at_IK11; // IK: kierunkowany
|
|
else if (type == "ik21")
|
|
b_Anim = b_aAnim = at_IK21; // IK: kierunkowany
|
|
else if (type == "ik22")
|
|
b_Anim = b_aAnim = at_IK22; // IK: kierunkowany
|
|
else if (type == "digital")
|
|
b_Anim = b_aAnim = at_Digital; // licznik mechaniczny
|
|
else if (type == "digiclk")
|
|
b_Anim = b_aAnim = at_DigiClk; // zegar cyfrowy
|
|
else
|
|
b_Anim = b_aAnim = at_Undefined; // nieznana forma animacji
|
|
}
|
|
}
|
|
if (eType < TP_ROTATOR)
|
|
readColor(parser, f4Ambient); // ignoruje token przed
|
|
readColor(parser, f4Diffuse);
|
|
if (eType < TP_ROTATOR)
|
|
readColor(parser, f4Specular);
|
|
parser.ignoreTokens(1); // zignorowanie nazwy "SelfIllum:"
|
|
{
|
|
std::string light;
|
|
parser.getToken(light);
|
|
if (light == "true")
|
|
fLight = 2.0; // zawsze œwieci
|
|
else if (light == "false")
|
|
fLight = -1.0; // zawsze ciemy
|
|
else
|
|
fLight = atof(light.c_str());
|
|
};
|
|
if (eType == TP_FREESPOTLIGHT)
|
|
{
|
|
if (!parser.expectToken("nearattenstart:"))
|
|
Error("Model light parse failure!");
|
|
parser.getToken(fNearAttenStart);
|
|
parser.ignoreToken();
|
|
parser.getToken(fNearAttenEnd);
|
|
parser.ignoreToken();
|
|
bUseNearAtten = parser.expectToken("true");
|
|
parser.ignoreToken();
|
|
parser.getToken(iFarAttenDecay);
|
|
parser.ignoreToken();
|
|
parser.getToken(fFarDecayRadius);
|
|
parser.ignoreToken();
|
|
parser.getToken(fCosFalloffAngle); // k¹t liczony dla œrednicy, a nie promienia
|
|
fCosFalloffAngle = cos(DegToRad(0.5 * fCosFalloffAngle));
|
|
parser.ignoreToken();
|
|
parser.getToken(fCosHotspotAngle); // k¹t liczony dla œrednicy, a nie promienia
|
|
fCosHotspotAngle = cos(DegToRad(0.5 * fCosHotspotAngle));
|
|
iNumVerts = 1;
|
|
iFlags |= 0x4010; // rysowane w cyklu nieprzezroczystych, macierz musi zostaæ bez zmiany
|
|
}
|
|
else if (eType < TP_ROTATOR)
|
|
{
|
|
parser.ignoreToken();
|
|
bWire = parser.expectToken("true");
|
|
parser.ignoreToken();
|
|
parser.getToken(fWireSize);
|
|
parser.ignoreToken();
|
|
Opacity = readIntAsDouble(parser,
|
|
100.0f); // wymagane jest 0 dla szyb, 100 idzie w nieprzezroczyste
|
|
if (Opacity > 1.0)
|
|
Opacity *= 0.01; // w 2013 by³ b³¹d i aby go obejœæ, trzeba by³o wpisaæ 10000.0
|
|
if ((Global::iConvertModels & 1) == 0) // dla zgodnoœci wstecz
|
|
Opacity = 0.0; // wszystko idzie w przezroczyste albo zale¿nie od tekstury
|
|
if (!parser.expectToken("map:"))
|
|
Error("Model map parse failure!");
|
|
std::string texture;
|
|
parser.getToken(texture);
|
|
if (texture == "none")
|
|
{ // rysowanie podanym kolorem
|
|
TextureID = 0;
|
|
iFlags |= 0x10; // rysowane w cyklu nieprzezroczystych
|
|
}
|
|
else if (texture.find("replacableskin") != texture.npos)
|
|
{ // McZapkie-060702: zmienialne skory modelu
|
|
TextureID = -1;
|
|
iFlags |= (Opacity < 1.0) ? 1 : 0x10; // zmienna tekstura 1
|
|
}
|
|
else if (texture == "-1")
|
|
{
|
|
TextureID = -1;
|
|
iFlags |= (Opacity < 1.0) ? 1 : 0x10; // zmienna tekstura 1
|
|
}
|
|
else if (texture == "-2")
|
|
{
|
|
TextureID = -2;
|
|
iFlags |= (Opacity < 1.0) ? 2 : 0x10; // zmienna tekstura 2
|
|
}
|
|
else if (texture == "-3")
|
|
{
|
|
TextureID = -3;
|
|
iFlags |= (Opacity < 1.0) ? 4 : 0x10; // zmienna tekstura 3
|
|
}
|
|
else if (texture == "-4")
|
|
{
|
|
TextureID = -4;
|
|
iFlags |= (Opacity < 1.0) ? 8 : 0x10; // zmienna tekstura 4
|
|
}
|
|
else
|
|
{ // jeœli tylko nazwa pliku, to dawaæ bie¿¹c¹ œcie¿kê do tekstur
|
|
// asTexture=AnsiString(texture.c_str()); //zapamiêtanie nazwy tekstury
|
|
TextureNameSet(texture.c_str());
|
|
if (texture.find_first_of("/\\") == texture.npos)
|
|
texture.insert(0, Global::asCurrentTexturePath.c_str());
|
|
TextureID = TTexturesManager::GetTextureID(
|
|
szTexturePath, Global::asCurrentTexturePath.c_str(), texture);
|
|
// TexAlpha=TTexturesManager::GetAlpha(TextureID);
|
|
// iFlags|=TexAlpha?0x20:0x10; //0x10-nieprzezroczysta, 0x20-przezroczysta
|
|
if (Opacity < 1.0) // przezroczystoϾ z tekstury brana tylko dla Opacity 0!
|
|
iFlags |= TTexturesManager::GetAlpha(TextureID) ?
|
|
0x20 :
|
|
0x10; // 0x10-nieprzezroczysta, 0x20-przezroczysta
|
|
else
|
|
iFlags |= 0x10; // normalnie nieprzezroczyste
|
|
// renderowanie w cyklu przezroczystych tylko jeœli:
|
|
// 1. Opacity=0 (przejœciowo <1, czy tam <100) oraz
|
|
// 2. tekstura ma przezroczystoϾ
|
|
};
|
|
}
|
|
else
|
|
iFlags |= 0x10;
|
|
parser.ignoreToken();
|
|
parser.getToken(fSquareMaxDist);
|
|
if (fSquareMaxDist >= 0.0)
|
|
fSquareMaxDist *= fSquareMaxDist;
|
|
else
|
|
fSquareMaxDist = 15000 * 15000; // 15km to wiêcej, ni¿ siê obecnie wyœwietla
|
|
parser.ignoreToken();
|
|
parser.getToken(fSquareMinDist);
|
|
fSquareMinDist *= fSquareMinDist;
|
|
parser.ignoreToken();
|
|
fMatrix = new float4x4();
|
|
readMatrix(parser, *fMatrix); // wczytanie transform
|
|
if (!fMatrix->IdentityIs())
|
|
iFlags |= 0x8000; // transform niejedynkowy - trzeba go przechowaæ
|
|
int iNumFaces; // iloœæ trójk¹tów
|
|
DWORD *sg; // maski przynale¿noœci trójk¹tów do powierzchni
|
|
if (eType < TP_ROTATOR)
|
|
{ // wczytywanie wierzcho³ków
|
|
parser.ignoreToken();
|
|
// Ra 15-01: to wczytaæ jako tekst - jeœli pierwszy znak zawiera "*", to dalej bêdzie nazwa
|
|
// wczeœniejszego submodelu, z którego nale¿y wzi¹æ wierzcho³ki
|
|
// zapewni to jak¹œ zgodnoœæ wstecz, bo zamiast liczby bêdzie ci¹g, którego wartoœæ powinna
|
|
// byæ uznana jako zerowa
|
|
// parser.getToken(iNumVerts);
|
|
parser.getToken(token);
|
|
if (token[0] == '*')
|
|
{ // jeœli pierwszy znak jest gwiazdk¹, poszukaæ submodelu o nazwie bez tej gwiazdki i wzi¹æ
|
|
// z niego wierzcho³ki
|
|
Error("Verticles reference not yet supported!");
|
|
}
|
|
else
|
|
{ // normalna lista wierzcho³ków
|
|
iNumVerts = atoi(token.c_str());
|
|
if (iNumVerts % 3)
|
|
{
|
|
iNumVerts = 0;
|
|
Error("Mesh error, (iNumVertices=" + AnsiString(iNumVerts) + ")%3<>0");
|
|
return 0;
|
|
}
|
|
// Vertices=new GLVERTEX[iNumVerts];
|
|
if (iNumVerts)
|
|
{
|
|
Vertices = new float8[iNumVerts];
|
|
iNumFaces = iNumVerts / 3;
|
|
sg = new DWORD[iNumFaces]; // maski powierzchni: 0 oznacza brak u¿redniania wektorów
|
|
// normalnych
|
|
int *wsp = new int[iNumVerts]; // z którego wierzcho³ka kopiowaæ wektor normalny
|
|
int maska = 0;
|
|
for (int i = 0; i < iNumVerts; i++)
|
|
{ // Ra: z konwersj¹ na uk³ad scenerii - bêdzie wydajniejsze wyœwietlanie
|
|
wsp[i] = -1; // wektory normalne nie s¹ policzone dla tego wierzcho³ka
|
|
if ((i % 3) == 0)
|
|
{ // jeœli bêdzie maska -1, to dalej bêd¹ wierzcho³ki z wektorami normalnymi,
|
|
// podanymi jawnie
|
|
parser.getToken(maska); // maska powierzchni trójk¹ta
|
|
sg[i / 3] = (maska == -1) ? 0 : maska; // dla maski -1 bêdzie 0, czyli nie
|
|
// ma wspólnych wektorów normalnych
|
|
}
|
|
parser.getToken(Vertices[i].Point.x);
|
|
parser.getToken(Vertices[i].Point.y);
|
|
parser.getToken(Vertices[i].Point.z);
|
|
if (maska == -1)
|
|
{ // jeœli wektory normalne podane jawnie
|
|
parser.getToken(Vertices[i].Normal.x);
|
|
parser.getToken(Vertices[i].Normal.y);
|
|
parser.getToken(Vertices[i].Normal.z);
|
|
wsp[i] = i; // wektory normalne "s¹ ju¿ policzone"
|
|
}
|
|
parser.getToken(Vertices[i].tu);
|
|
parser.getToken(Vertices[i].tv);
|
|
if (i % 3 == 2) // je¿eli wczytano 3 punkty
|
|
{
|
|
if (Vertices[i].Point == Vertices[i - 1].Point ||
|
|
Vertices[i - 1].Point == Vertices[i - 2].Point ||
|
|
Vertices[i - 2].Point == Vertices[i].Point)
|
|
{ // je¿eli punkty siê nak³adaj¹ na siebie
|
|
--iNumFaces; // o jeden trójk¹t mniej
|
|
iNumVerts -= 3; // czyli o 3 wierzcho³ki
|
|
i -= 3; // wczytanie kolejnego w to miejsce
|
|
WriteLog(AnsiString("Degenerated triangle ignored in: \"") +
|
|
AnsiString(pName) + "\", verticle " + AnsiString(i));
|
|
}
|
|
if (i > 0) // jeœli pierwszy trójk¹t bêdzie zdegenerowany, to zostanie
|
|
// usuniêty i nie ma co sprawdzaæ
|
|
if (((Vertices[i].Point - Vertices[i - 1].Point).Length() > 1000.0) ||
|
|
((Vertices[i - 1].Point - Vertices[i - 2].Point).Length() >
|
|
1000.0) ||
|
|
((Vertices[i - 2].Point - Vertices[i].Point).Length() > 1000.0))
|
|
{ // je¿eli s¹ dalej ni¿ 2km od siebie //Ra 15-01: obiekt wstawiany nie
|
|
// powinien byæ wiêkszy ni¿ 300m (trójk¹ty terenu w E3D mog¹ mieæ
|
|
// 1.5km)
|
|
--iNumFaces; // o jeden trójk¹t mniej
|
|
iNumVerts -= 3; // czyli o 3 wierzcho³ki
|
|
i -= 3; // wczytanie kolejnego w to miejsce
|
|
WriteLog(AnsiString("Too large triangle ignored in: \"") +
|
|
AnsiString(pName) + "\"");
|
|
}
|
|
}
|
|
}
|
|
int i; // indeks dla trójk¹tów
|
|
float3 *n = new float3[iNumFaces]; // tablica wektorów normalnych dla trójk¹tów
|
|
for (i = 0; i < iNumFaces; i++) // pêtla po trójk¹tach - bêdzie szybciej, jak
|
|
// wstêpnie przeliczymy normalne trójk¹tów
|
|
n[i] = SafeNormalize(
|
|
CrossProduct(Vertices[i * 3].Point - Vertices[i * 3 + 1].Point,
|
|
Vertices[i * 3].Point - Vertices[i * 3 + 2].Point));
|
|
int v; // indeks dla wierzcho³ków
|
|
int f; // numer trójk¹ta stycznego
|
|
float3 norm; // roboczy wektor normalny
|
|
for (v = 0; v < iNumVerts; v++)
|
|
{ // pêtla po wierzcho³kach trójk¹tów
|
|
if (wsp[v] >=
|
|
0) // jeœli ju¿ by³ liczony wektor normalny z u¿yciem tego wierzcho³ka
|
|
Vertices[v].Normal =
|
|
Vertices[wsp[v]].Normal; // to wystarczy skopiowaæ policzony wczeœniej
|
|
else
|
|
{ // inaczej musimy dopiero policzyæ
|
|
i = v / 3; // numer trójk¹ta
|
|
norm = float3(0, 0, 0); // liczenie zaczynamy od zera
|
|
f = v; // zaczynamy dodawanie wektorów normalnych od w³asnego
|
|
while (f >= 0)
|
|
{ // sumowanie z wektorem normalnym s¹siada (w³¹cznie ze sob¹)
|
|
wsp[f] = v; // informacja, ¿e w tym wierzcho³ku jest ju¿ policzony
|
|
// wektor normalny
|
|
norm += n[f / 3];
|
|
f = SeekFaceNormal(sg, f / 3 + 1, sg[i], &Vertices[v].Point,
|
|
Vertices); // i szukanie od kolejnego trójk¹ta
|
|
}
|
|
// Ra 15-01: nale¿a³o by jeszcze uwzglêdniæ skalowanie wprowadzane przez
|
|
// transformy, aby normalne po przeskalowaniu by³y jednostkowe
|
|
Vertices[v].Normal =
|
|
SafeNormalize(norm); // przepisanie do wierzcho³ka trójk¹ta
|
|
}
|
|
}
|
|
delete[] wsp;
|
|
delete[] n;
|
|
delete[] sg;
|
|
}
|
|
else // gdy brak wierzcho³ków
|
|
{
|
|
eType = TP_ROTATOR; // submodel pomocniczy, ma tylko macierz przekszta³cenia
|
|
iVboPtr = iNumVerts = 0; // dla formalnoœci
|
|
}
|
|
} // obs³uga submodelu z w³asn¹ list¹ wierzcho³ków
|
|
}
|
|
else if (eType == TP_STARS)
|
|
{ // punkty œwiec¹ce dookólnie - sk³adnia jak dla smt_Mesh
|
|
parser.ignoreToken();
|
|
parser.getToken(iNumVerts);
|
|
// Vertices=new GLVERTEX[iNumVerts];
|
|
Vertices = new float8[iNumVerts];
|
|
int i, j;
|
|
for (i = 0; i < iNumVerts; i++)
|
|
{
|
|
if (i % 3 == 0)
|
|
parser.ignoreToken(); // maska powierzchni trójk¹ta
|
|
parser.getToken(Vertices[i].Point.x);
|
|
parser.getToken(Vertices[i].Point.y);
|
|
parser.getToken(Vertices[i].Point.z);
|
|
parser.getToken(j); // zakodowany kolor
|
|
parser.ignoreToken();
|
|
Vertices[i].Normal.x = ((j)&0xFF) / 255.0; // R
|
|
Vertices[i].Normal.y = ((j >> 8) & 0xFF) / 255.0; // G
|
|
Vertices[i].Normal.z = ((j >> 16) & 0xFF) / 255.0; // B
|
|
}
|
|
}
|
|
// Visible=true; //siê potem wy³¹czy w razie potrzeby
|
|
// iFlags|=0x0200; //wczytano z pliku tekstowego (jest w³aœcicielem tablic)
|
|
if (iNumVerts < 1)
|
|
iFlags &= ~0x3F; // cykl renderowania uzale¿niony od potomnych
|
|
return iNumVerts; // do okreœlenia wielkoœci VBO
|
|
};
|
|
|
|
int __fastcall TSubModel::TriangleAdd(TModel3d *m, int tex, int tri)
|
|
{ // dodanie trójk¹tów do submodelu, u¿ywane przy tworzeniu E3D terenu
|
|
TSubModel *s = this;
|
|
while (s ? (s->TextureID != tex) : false)
|
|
{ // szukanie submodelu o danej teksturze
|
|
if (s == this)
|
|
s = Child;
|
|
else
|
|
s = s->Next;
|
|
}
|
|
if (!s)
|
|
{
|
|
if (TextureID <= 0)
|
|
s = this; // u¿ycie g³ównego
|
|
else
|
|
{ // dodanie nowego submodelu do listy potomnych
|
|
s = new TSubModel();
|
|
m->AddTo(this, s);
|
|
}
|
|
// s->asTexture=AnsiString(TTexturesManager::GetName(tex).c_str());
|
|
s->TextureNameSet(TTexturesManager::GetName(tex).c_str());
|
|
s->TextureID = tex;
|
|
s->eType = GL_TRIANGLES;
|
|
// iAnimOwner=0; //roboczy wskaŸnik na wierzcho³ek
|
|
}
|
|
if (s->iNumVerts < 0)
|
|
s->iNumVerts = tri; // bo na pocz¹tku jest -1, czyli ¿e nie wiadomo
|
|
else
|
|
s->iNumVerts += tri; // aktualizacja iloœci wierzcho³ków
|
|
return s->iNumVerts - tri; // zwraca pozycjê tych trójk¹tów w submodelu
|
|
};
|
|
|
|
float8 *__fastcall TSubModel::TrianglePtr(int tex, int pos, int *la, int *ld, int *ls)
|
|
{ // zwraca wskaŸnik do wype³nienia tabeli wierzcho³ków, u¿ywane przy tworzeniu E3D terenu
|
|
TSubModel *s = this;
|
|
while (s ? s->TextureID != tex : false)
|
|
{ // szukanie submodelu o danej teksturze
|
|
if (s == this)
|
|
s = Child;
|
|
else
|
|
s = s->Next;
|
|
}
|
|
if (!s)
|
|
return NULL; // coœ nie tak posz³o
|
|
if (!s->Vertices)
|
|
{ // utworznie tabeli trójk¹tów
|
|
s->Vertices = new float8[s->iNumVerts];
|
|
// iVboPtr=pos; //pozycja submodelu w tabeli wierzcho³ków
|
|
// pos+=iNumVerts; //rezerwacja miejsca w tabeli
|
|
s->iVboPtr = iInstance; // pozycja submodelu w tabeli wierzcho³ków
|
|
iInstance += s->iNumVerts; // pozycja dla nastêpnego
|
|
}
|
|
s->ColorsSet(la, ld, ls); // ustawienie kolorów œwiate³
|
|
return s->Vertices + pos; // wskaŸnik na wolne miejsce w tabeli wierzcho³ków
|
|
};
|
|
|
|
void __fastcall TSubModel::DisplayLists()
|
|
{ // utworznie po jednej skompilowanej liœcie dla ka¿dego submodelu
|
|
if (Global::bUseVBO)
|
|
return; // Ra: przy VBO to siê nie przyda
|
|
// iFlags|=0x4000; //wy³¹czenie przeliczania wierzcho³ków, bo nie s¹ zachowane
|
|
if (eType < TP_ROTATOR)
|
|
{
|
|
if (iNumVerts > 0)
|
|
{
|
|
uiDisplayList = glGenLists(1);
|
|
glNewList(uiDisplayList, GL_COMPILE);
|
|
glColor3fv(f4Diffuse); // McZapkie-240702: zamiast ub
|
|
#ifdef USE_VERTEX_ARRAYS
|
|
// ShaXbee-121209: przekazywanie wierzcholkow hurtem
|
|
glVertexPointer(3, GL_DOUBLE, sizeof(GLVERTEX), &Vertices[0].Point.x);
|
|
glNormalPointer(GL_DOUBLE, sizeof(GLVERTEX), &Vertices[0].Normal.x);
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(GLVERTEX), &Vertices[0].tu);
|
|
glDrawArrays(eType, 0, iNumVerts);
|
|
#else
|
|
glBegin(eType);
|
|
for (int i = 0; i < iNumVerts; i++)
|
|
{
|
|
/*
|
|
glNormal3dv(&Vertices[i].Normal.x);
|
|
glTexCoord2f(Vertices[i].tu,Vertices[i].tv);
|
|
glVertex3dv(&Vertices[i].Point.x);
|
|
*/
|
|
glNormal3fv(&Vertices[i].Normal.x);
|
|
glTexCoord2f(Vertices[i].tu, Vertices[i].tv);
|
|
glVertex3fv(&Vertices[i].Point.x);
|
|
};
|
|
glEnd();
|
|
#endif
|
|
glEndList();
|
|
}
|
|
}
|
|
else if (eType == TP_FREESPOTLIGHT)
|
|
{
|
|
uiDisplayList = glGenLists(1);
|
|
glNewList(uiDisplayList, GL_COMPILE);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
// if (eType==smt_FreeSpotLight)
|
|
// {
|
|
// if (iFarAttenDecay==0)
|
|
// glColor3f(Diffuse[0],Diffuse[1],Diffuse[2]);
|
|
// }
|
|
// else
|
|
// TODO: poprawic zeby dzialalo
|
|
// glColor3f(f4Diffuse[0],f4Diffuse[1],f4Diffuse[2]);
|
|
glColorMaterial(GL_FRONT, GL_EMISSION);
|
|
glDisable(GL_LIGHTING); // Tolaris-030603: bo mu punkty swiecace sie blendowaly
|
|
glBegin(GL_POINTS);
|
|
glVertex3f(0, 0, 0);
|
|
glEnd();
|
|
glEnable(GL_LIGHTING);
|
|
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2);
|
|
glEndList();
|
|
}
|
|
else if (eType == TP_STARS)
|
|
{ // punkty œwiec¹ce dookólnie
|
|
uiDisplayList = glGenLists(1);
|
|
glNewList(uiDisplayList, GL_COMPILE);
|
|
glBindTexture(GL_TEXTURE_2D, 0); // tekstury nie ma
|
|
glColorMaterial(GL_FRONT, GL_EMISSION);
|
|
glDisable(GL_LIGHTING); // Tolaris-030603: bo mu punkty swiecace sie blendowaly
|
|
glBegin(GL_POINTS);
|
|
for (int i = 0; i < iNumVerts; i++)
|
|
{
|
|
glColor3f(Vertices[i].Normal.x, Vertices[i].Normal.y, Vertices[i].Normal.z);
|
|
// glVertex3dv(&Vertices[i].Point.x);
|
|
glVertex3fv(&Vertices[i].Point.x);
|
|
};
|
|
glEnd();
|
|
glEnable(GL_LIGHTING);
|
|
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2);
|
|
glEndList();
|
|
}
|
|
// SafeDeleteArray(Vertices); //przy VBO musz¹ zostaæ do za³adowania ca³ego modelu
|
|
if (Child)
|
|
Child->DisplayLists();
|
|
if (Next)
|
|
Next->DisplayLists();
|
|
};
|
|
|
|
void __fastcall TSubModel::InitialRotate(bool doit)
|
|
{ // konwersja uk³adu wspó³rzêdnych na zgodny ze sceneri¹
|
|
if (iFlags & 0xC000) // jeœli jest animacja albo niejednostkowy transform
|
|
{ // niejednostkowy transform jest mno¿ony i wystarczy zabawy
|
|
if (doit)
|
|
{ // obrót lewostronny
|
|
if (!fMatrix) // macierzy mo¿e nie byæ w dodanym "bananie"
|
|
{
|
|
fMatrix = new float4x4(); // tworzy macierz o przypadkowej zawartoœci
|
|
fMatrix->Identity(); // a zaczynamy obracanie od jednostkowej
|
|
}
|
|
iFlags |= 0x8000; // po obróceniu bêdzie raczej niejedynkowy matrix
|
|
fMatrix->InitialRotate(); // zmiana znaku X oraz zamiana Y i Z
|
|
if (fMatrix->IdentityIs())
|
|
iFlags &= ~0x8000; // jednak jednostkowa po obróceniu
|
|
}
|
|
if (Child)
|
|
Child->InitialRotate(
|
|
false); // potomnych nie obracamy ju¿, tylko ewentualnie optymalizujemy
|
|
else if (Global::iConvertModels & 2) // optymalizacja jest opcjonalna
|
|
if ((iFlags & 0xC000) == 0x8000) // o ile nie ma animacji
|
|
{ // jak nie ma potomnych, mo¿na wymno¿yæ przez transform i wyjedynkowaæ go
|
|
float4x4 *mat = GetMatrix(); // transform submodelu
|
|
if (Vertices)
|
|
{
|
|
for (int i = 0; i < iNumVerts; ++i)
|
|
Vertices[i].Point = (*mat) * Vertices[i].Point;
|
|
(*mat)(3)[0] = (*mat)(3)[1] = (*mat)(3)[2] =
|
|
0.0; // zerujemy przesuniêcie przed obracaniem normalnych
|
|
if (eType != TP_STARS) // gwiazdki maj¹ kolory zamiast normalnych, to ich wtedy
|
|
// nie ruszamy
|
|
for (int i = 0; i < iNumVerts; ++i)
|
|
Vertices[i].Normal = SafeNormalize((*mat) * Vertices[i].Normal);
|
|
}
|
|
mat->Identity(); // jedynkowanie transformu po przeliczeniu wierzcho³ków
|
|
iFlags &= ~0x8000; // transform jedynkowy
|
|
}
|
|
}
|
|
else // jak jest jednostkowy i nie ma animacji
|
|
if (doit)
|
|
{ // jeœli jest jednostkowy transform, to przeliczamy wierzcho³ki, a mno¿enie podajemy dalej
|
|
double t;
|
|
if (Vertices)
|
|
for (int i = 0; i < iNumVerts; ++i)
|
|
{
|
|
Vertices[i].Point.x = -Vertices[i].Point.x; // zmiana znaku X
|
|
t = Vertices[i].Point.y; // zamiana Y i Z
|
|
Vertices[i].Point.y = Vertices[i].Point.z;
|
|
Vertices[i].Point.z = t;
|
|
// wektory normalne równie¿ trzeba przekszta³ciæ, bo siê Ÿle oœwietlaj¹
|
|
Vertices[i].Normal.x = -Vertices[i].Normal.x; // zmiana znaku X
|
|
t = Vertices[i].Normal.y; // zamiana Y i Z
|
|
Vertices[i].Normal.y = Vertices[i].Normal.z;
|
|
Vertices[i].Normal.z = t;
|
|
}
|
|
if (Child)
|
|
Child->InitialRotate(doit); // potomne ewentualnie obrócimy
|
|
}
|
|
if (Next)
|
|
Next->InitialRotate(doit);
|
|
};
|
|
|
|
void __fastcall TSubModel::ChildAdd(TSubModel *SubModel)
|
|
{ // dodanie submodelu potemnego (uzale¿nionego)
|
|
// Ra: zmiana kolejnoœci, ¿eby kolejne móc renderowaæ po aktualnym (by³o przed)
|
|
if (SubModel)
|
|
SubModel->NextAdd(Child); // Ra: zmiana kolejnoœci renderowania
|
|
Child = SubModel;
|
|
};
|
|
|
|
void __fastcall TSubModel::NextAdd(TSubModel *SubModel)
|
|
{ // dodanie submodelu kolejnego (wspólny przodek)
|
|
if (Next)
|
|
Next->NextAdd(SubModel);
|
|
else
|
|
Next = SubModel;
|
|
};
|
|
|
|
int __fastcall TSubModel::FlagsCheck()
|
|
{ // analiza koniecznych zmian pomiêdzy submodelami
|
|
// samo pomijanie glBindTexture() nie poprawi wydajnoœci
|
|
// ale mo¿na sprawdziæ, czy mo¿na w ogóle pomin¹æ kod do tekstur (sprawdzanie replaceskin)
|
|
int i;
|
|
if (Child)
|
|
{ // Child jest renderowany po danym submodelu
|
|
if (Child->TextureID) // o ile ma teksturê
|
|
if (Child->TextureID != TextureID) // i jest ona inna ni¿ rodzica
|
|
Child->iFlags |= 0x80; // to trzeba sprawdzaæ, jak z teksturami jest
|
|
i = Child->FlagsCheck();
|
|
iFlags |= 0x00FF0000 & ((i << 16) | (i) | (i >> 8)); // potomny, rodzeñstwo i dzieci
|
|
if (eType == TP_TEXT)
|
|
{ // wy³¹czenie renderowania Next dla znaków wyœwietlacza tekstowego
|
|
TSubModel *p = Child;
|
|
while (p)
|
|
{
|
|
p->iFlags &= 0xC0FFFFFF;
|
|
p = p->Next;
|
|
}
|
|
}
|
|
}
|
|
if (Next)
|
|
{ // Next jest renderowany po danym submodelu (kolejnoœæ odwrócona po wczytaniu T3D)
|
|
if (TextureID) // o ile dany ma teksturê
|
|
if ((TextureID != Next->TextureID) ||
|
|
(i & 0x00800000)) // a ma inn¹ albo dzieci zmieniaj¹
|
|
iFlags |= 0x80; // to dany submodel musi sobie j¹ ustawiaæ
|
|
i = Next->FlagsCheck();
|
|
iFlags |= 0xFF000000 & ((i << 24) | (i << 8) | (i)); // nastêpny, kolejne i ich dzieci
|
|
// tekstury nie ustawiamy tylko wtedy, gdy jest taka sama jak Next i jego dzieci nie
|
|
// zmieniaj¹
|
|
}
|
|
return iFlags;
|
|
};
|
|
|
|
void __fastcall TSubModel::SetRotate(float3 vNewRotateAxis, float fNewAngle)
|
|
{ // obrócenie submodelu wg podanej osi (np. wskazówki w kabinie)
|
|
v_RotateAxis = vNewRotateAxis;
|
|
f_Angle = fNewAngle;
|
|
if (fNewAngle != 0.0)
|
|
{
|
|
b_Anim = at_Rotate;
|
|
b_aAnim = at_Rotate;
|
|
}
|
|
iAnimOwner = iInstance; // zapamiêtanie czyja jest animacja
|
|
}
|
|
|
|
void __fastcall TSubModel::SetRotateXYZ(float3 vNewAngles)
|
|
{ // obrócenie submodelu o podane k¹ty wokó³ osi lokalnego uk³adu
|
|
v_Angles = vNewAngles;
|
|
b_Anim = at_RotateXYZ;
|
|
b_aAnim = at_RotateXYZ;
|
|
iAnimOwner = iInstance; // zapamiêtanie czyja jest animacja
|
|
}
|
|
|
|
void __fastcall TSubModel::SetRotateXYZ(vector3 vNewAngles)
|
|
{ // obrócenie submodelu o podane k¹ty wokó³ osi lokalnego uk³adu
|
|
v_Angles.x = vNewAngles.x;
|
|
v_Angles.y = vNewAngles.y;
|
|
v_Angles.z = vNewAngles.z;
|
|
b_Anim = at_RotateXYZ;
|
|
b_aAnim = at_RotateXYZ;
|
|
iAnimOwner = iInstance; // zapamiêtanie czyja jest animacja
|
|
}
|
|
|
|
void __fastcall TSubModel::SetTranslate(float3 vNewTransVector)
|
|
{ // przesuniêcie submodelu (np. w kabinie)
|
|
v_TransVector = vNewTransVector;
|
|
b_Anim = at_Translate;
|
|
b_aAnim = at_Translate;
|
|
iAnimOwner = iInstance; // zapamiêtanie czyja jest animacja
|
|
}
|
|
|
|
void __fastcall TSubModel::SetTranslate(vector3 vNewTransVector)
|
|
{ // przesuniêcie submodelu (np. w kabinie)
|
|
v_TransVector.x = vNewTransVector.x;
|
|
v_TransVector.y = vNewTransVector.y;
|
|
v_TransVector.z = vNewTransVector.z;
|
|
b_Anim = at_Translate;
|
|
b_aAnim = at_Translate;
|
|
iAnimOwner = iInstance; // zapamiêtanie czyja jest animacja
|
|
}
|
|
|
|
void __fastcall TSubModel::SetRotateIK1(float3 vNewAngles)
|
|
{ // obrócenie submodelu o podane k¹ty wokó³ osi lokalnego uk³adu
|
|
v_Angles = vNewAngles;
|
|
iAnimOwner = iInstance; // zapamiêtanie czyja jest animacja
|
|
}
|
|
|
|
struct ToLower
|
|
{
|
|
char operator()(char input) { return tolower(input); }
|
|
};
|
|
|
|
TSubModel *__fastcall TSubModel::GetFromName(AnsiString search, bool i)
|
|
{
|
|
return GetFromName(search.c_str(), i);
|
|
};
|
|
|
|
TSubModel *__fastcall TSubModel::GetFromName(char *search, bool i)
|
|
{
|
|
TSubModel *result;
|
|
// std::transform(search.begin(),search.end(),search.begin(),ToLower());
|
|
// search=search.LowerCase();
|
|
// AnsiString name=AnsiString();
|
|
if (pName && search)
|
|
if ((i ? stricmp(pName, search) : strcmp(pName, search)) == 0)
|
|
return this;
|
|
else if (pName == search)
|
|
return this; // oba NULL
|
|
if (Next)
|
|
{
|
|
result = Next->GetFromName(search);
|
|
if (result)
|
|
return result;
|
|
}
|
|
if (Child)
|
|
{
|
|
result = Child->GetFromName(search);
|
|
if (result)
|
|
return result;
|
|
}
|
|
return NULL;
|
|
};
|
|
|
|
// WORD hbIndices[18]={3,0,1,5,4,2,1,0,4,1,5,3,2,3,5,2,4,0};
|
|
|
|
void __fastcall TSubModel::RaAnimation(TAnimType a)
|
|
{ // wykonanie animacji niezale¿nie od renderowania
|
|
switch (a)
|
|
{ // korekcja po³o¿enia, jeœli submodel jest animowany
|
|
case at_Translate: // Ra: by³o "true"
|
|
if (iAnimOwner != iInstance)
|
|
break; // cudza animacja
|
|
glTranslatef(v_TransVector.x, v_TransVector.y, v_TransVector.z);
|
|
break;
|
|
case at_Rotate: // Ra: by³o "true"
|
|
if (iAnimOwner != iInstance)
|
|
break; // cudza animacja
|
|
glRotatef(f_Angle, v_RotateAxis.x, v_RotateAxis.y, v_RotateAxis.z);
|
|
break;
|
|
case at_RotateXYZ:
|
|
if (iAnimOwner != iInstance)
|
|
break; // cudza animacja
|
|
glTranslatef(v_TransVector.x, v_TransVector.y, v_TransVector.z);
|
|
glRotatef(v_Angles.x, 1.0, 0.0, 0.0);
|
|
glRotatef(v_Angles.y, 0.0, 1.0, 0.0);
|
|
glRotatef(v_Angles.z, 0.0, 0.0, 1.0);
|
|
break;
|
|
case at_SecondsJump: // sekundy z przeskokiem
|
|
glRotatef(floor(GlobalTime->mr) * 6.0, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_MinutesJump: // minuty z przeskokiem
|
|
glRotatef(GlobalTime->mm * 6.0, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_HoursJump: // godziny skokowo 12h/360°
|
|
glRotatef(GlobalTime->hh * 30.0 * 0.5, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_Hours24Jump: // godziny skokowo 24h/360°
|
|
glRotatef(GlobalTime->hh * 15.0 * 0.25, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_Seconds: // sekundy p³ynnie
|
|
glRotatef(GlobalTime->mr * 6.0, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_Minutes: // minuty p³ynnie
|
|
glRotatef(GlobalTime->mm * 6.0 + GlobalTime->mr * 0.1, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_Hours: // godziny p³ynnie 12h/360°
|
|
// glRotatef(GlobalTime->hh*30.0+GlobalTime->mm*0.5+GlobalTime->mr/120.0,0.0,1.0,0.0);
|
|
glRotatef(2.0 * Global::fTimeAngleDeg, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_Hours24: // godziny p³ynnie 24h/360°
|
|
// glRotatef(GlobalTime->hh*15.0+GlobalTime->mm*0.25+GlobalTime->mr/240.0,0.0,1.0,0.0);
|
|
glRotatef(Global::fTimeAngleDeg, 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_Billboard: // obrót w pionie do kamery
|
|
{
|
|
matrix4x4 mat; // potrzebujemy wspó³rzêdne przesuniêcia œrodka uk³adu wspó³rzêdnych
|
|
// submodelu
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, mat.getArray()); // pobranie aktualnej matrycy
|
|
float3 gdzie = float3(mat[3][0], mat[3][1],
|
|
mat[3][2]); // pocz¹tek uk³adu wspó³rzêdnych submodelu wzglêdem kamery
|
|
glLoadIdentity(); // macierz jedynkowa
|
|
glTranslatef(gdzie.x, gdzie.y, gdzie.z); // pocz¹tek uk³adu zostaje bez zmian
|
|
glRotated(atan2(gdzie.x, gdzie.z) * 180.0 / M_PI, 0.0, 1.0,
|
|
0.0); // jedynie obracamy w pionie o k¹t
|
|
}
|
|
break;
|
|
case at_Wind: // ruch pod wp³ywem wiatru (wiatr bêdziemy liczyæ potem...)
|
|
glRotated(1.5 * sin(M_PI * GlobalTime->mr / 6.0), 0.0, 1.0, 0.0);
|
|
break;
|
|
case at_Sky: // animacja nieba
|
|
glRotated(Global::fLatitudeDeg, 1.0, 0.0, 0.0); // ustawienie osi OY na pó³noc
|
|
// glRotatef(Global::fTimeAngleDeg,0.0,1.0,0.0); //obrót dobowy osi OX
|
|
glRotated(-fmod(Global::fTimeAngleDeg, 360.0), 0.0, 1.0, 0.0); // obrót dobowy osi OX
|
|
break;
|
|
case at_IK11: // ostatni element animacji szkieletowej (podudzie, stopa)
|
|
glRotatef(v_Angles.z, 0.0, 1.0, 0.0); // obrót wzglêdem osi pionowej (azymut)
|
|
glRotatef(v_Angles.x, 1.0, 0.0, 0.0); // obrót wzglêdem poziomu (deklinacja)
|
|
break;
|
|
case at_DigiClk: // animacja zegara cyfrowego
|
|
{ // ustawienie animacji w submodelach potomnych
|
|
TSubModel *sm = ChildGet();
|
|
do
|
|
{ // pêtla po submodelach potomnych i obracanie ich o k¹t zale¿y od czasu
|
|
if (sm->pName)
|
|
{ // musi mieæ niepust¹ nazwê
|
|
if ((*sm->pName) >= '0')
|
|
if ((*sm->pName) <= '5') // zegarek ma 6 cyfr maksymalnie
|
|
sm->SetRotate(float3(0, 1, 0), -Global::fClockAngleDeg[(*sm->pName) - '0']);
|
|
}
|
|
sm = sm->NextGet();
|
|
} while (sm);
|
|
}
|
|
break;
|
|
}
|
|
if (mAnimMatrix) // mo¿na by to daæ np. do at_Translate
|
|
{
|
|
glMultMatrixf(mAnimMatrix->readArray());
|
|
mAnimMatrix = NULL; // jak animator bêdzie potrzebowa³, to ustawi ponownie
|
|
}
|
|
};
|
|
|
|
void __fastcall TSubModel::RenderDL()
|
|
{ // g³ówna procedura renderowania przez DL
|
|
if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist))
|
|
{
|
|
if (iFlags & 0xC000)
|
|
{
|
|
glPushMatrix();
|
|
if (fMatrix)
|
|
glMultMatrixf(fMatrix->readArray());
|
|
if (b_Anim)
|
|
RaAnimation(b_Anim);
|
|
}
|
|
if (eType < TP_ROTATOR)
|
|
{ // renderowanie obiektów OpenGL
|
|
if (iAlpha & iFlags & 0x1F) // rysuj gdy element nieprzezroczysty
|
|
{
|
|
if (TextureID < 0) // && (ReplacableSkinId!=0))
|
|
{ // zmienialne skóry
|
|
glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]);
|
|
// TexAlpha=!(iAlpha&1); //zmiana tylko w przypadku wymienej tekstury
|
|
}
|
|
else
|
|
glBindTexture(GL_TEXTURE_2D, TextureID); // równie¿ 0
|
|
if (Global::fLuminance < fLight)
|
|
{
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo
|
|
glCallList(uiDisplayList); // tylko dla siatki
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2);
|
|
}
|
|
else
|
|
glCallList(uiDisplayList); // tylko dla siatki
|
|
}
|
|
}
|
|
else if (eType == TP_FREESPOTLIGHT)
|
|
{ // wersja DL
|
|
matrix4x4 mat; // macierz opisuje uk³ad renderowania wzglêdem kamery
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, mat.getArray());
|
|
// k¹t miêdzy kierunkiem œwiat³a a wspó³rzêdnymi kamery
|
|
vector3 gdzie = mat * vector3(0, 0, 0); // pozycja punktu œwiec¹cego wzglêdem kamery
|
|
fCosViewAngle = DotProduct(Normalize(mat * vector3(0, 0, 1) - gdzie), Normalize(gdzie));
|
|
if (fCosViewAngle > fCosFalloffAngle) // k¹t wiêkszy ni¿ maksymalny sto¿ek swiat³a
|
|
{
|
|
double Distdimm = 1.0;
|
|
if (fCosViewAngle < fCosHotspotAngle) // zmniejszona jasnoœæ miêdzy Hotspot a
|
|
// Falloff
|
|
if (fCosFalloffAngle < fCosHotspotAngle)
|
|
Distdimm = 1.0 -
|
|
(fCosHotspotAngle - fCosViewAngle) /
|
|
(fCosHotspotAngle - fCosFalloffAngle);
|
|
glColor3f(f4Diffuse[0] * Distdimm, f4Diffuse[1] * Distdimm,
|
|
f4Diffuse[2] * Distdimm);
|
|
/* TODO: poprawic to zeby dzialalo
|
|
if (iFarAttenDecay>0)
|
|
switch (iFarAttenDecay)
|
|
{
|
|
case 1:
|
|
Distdimm=fFarDecayRadius/(1+sqrt(fSquareDist)); //dorobic od
|
|
kata
|
|
break;
|
|
case 2:
|
|
Distdimm=fFarDecayRadius/(1+fSquareDist); //dorobic od kata
|
|
break;
|
|
}
|
|
if (Distdimm>1)
|
|
Distdimm=1;
|
|
glColor3f(Diffuse[0]*Distdimm,Diffuse[1]*Distdimm,Diffuse[2]*Distdimm);
|
|
*/
|
|
// glPopMatrix();
|
|
// return;
|
|
glCallList(uiDisplayList); // wyœwietlenie warunkowe
|
|
}
|
|
}
|
|
else if (eType == TP_STARS)
|
|
{
|
|
// glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly
|
|
if (Global::fLuminance < fLight)
|
|
{
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo
|
|
glCallList(uiDisplayList); // narysuj naraz wszystkie punkty z DL
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2);
|
|
}
|
|
}
|
|
if (Child != NULL)
|
|
if (iAlpha & iFlags & 0x001F0000)
|
|
Child->RenderDL();
|
|
if (iFlags & 0xC000)
|
|
glPopMatrix();
|
|
}
|
|
if (b_Anim < at_SecondsJump)
|
|
b_Anim = at_None; // wy³¹czenie animacji dla kolejnego u¿ycia subm
|
|
if (Next)
|
|
if (iAlpha & iFlags & 0x1F000000)
|
|
Next->RenderDL(); // dalsze rekurencyjnie
|
|
}; // Render
|
|
|
|
void __fastcall TSubModel::RenderAlphaDL()
|
|
{ // renderowanie przezroczystych przez DL
|
|
if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist))
|
|
{
|
|
if (iFlags & 0xC000)
|
|
{
|
|
glPushMatrix();
|
|
if (fMatrix)
|
|
glMultMatrixf(fMatrix->readArray());
|
|
if (b_aAnim)
|
|
RaAnimation(b_aAnim);
|
|
}
|
|
if (eType < TP_ROTATOR)
|
|
{ // renderowanie obiektów OpenGL
|
|
if (iAlpha & iFlags & 0x2F) // rysuj gdy element przezroczysty
|
|
{
|
|
if (TextureID < 0) // && (ReplacableSkinId!=0))
|
|
{ // zmienialne skóry
|
|
glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]);
|
|
// TexAlpha=iAlpha&1; //zmiana tylko w przypadku wymienej tekstury
|
|
}
|
|
else
|
|
glBindTexture(GL_TEXTURE_2D, TextureID); // równie¿ 0
|
|
if (Global::fLuminance < fLight)
|
|
{
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo
|
|
glCallList(uiDisplayList); // tylko dla siatki
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2);
|
|
}
|
|
else
|
|
glCallList(uiDisplayList); // tylko dla siatki
|
|
}
|
|
}
|
|
else if (eType == TP_FREESPOTLIGHT)
|
|
{
|
|
// dorobiæ aureolê!
|
|
}
|
|
if (Child != NULL)
|
|
if (eType == TP_TEXT)
|
|
{ // tekst renderujemy w specjalny sposób, zamiast submodeli z ³añcucha Child
|
|
int i, j = pasText->Length();
|
|
TSubModel *p;
|
|
char c;
|
|
if (!smLetter)
|
|
{ // jeœli nie ma tablicy, to j¹ stworzyæ; miejsce nieodpowiednie, ale tymczasowo
|
|
// mo¿e byæ
|
|
smLetter = new TSubModel *[256]; // tablica wskaŸników submodeli dla
|
|
// wyœwietlania tekstu
|
|
ZeroMemory(smLetter, 256 * sizeof(TSubModel *)); // wype³nianie zerami
|
|
p = Child;
|
|
while (p)
|
|
{
|
|
smLetter[*p->pName] = p;
|
|
p = p->Next; // kolejny znak
|
|
}
|
|
}
|
|
for (i = 1; i <= j; ++i)
|
|
{
|
|
p = smLetter[(*pasText)[i]]; // znak do wyœwietlenia
|
|
if (p)
|
|
{ // na razie tylko jako przezroczyste
|
|
p->RenderAlphaDL();
|
|
if (p->fMatrix)
|
|
glMultMatrixf(p->fMatrix->readArray()); // przesuwanie widoku
|
|
}
|
|
}
|
|
}
|
|
else if (iAlpha & iFlags & 0x002F0000)
|
|
Child->RenderAlphaDL();
|
|
if (iFlags & 0xC000)
|
|
glPopMatrix();
|
|
}
|
|
if (b_aAnim < at_SecondsJump)
|
|
b_aAnim = at_None; // wy³¹czenie animacji dla kolejnego u¿ycia submodelu
|
|
if (Next != NULL)
|
|
if (iAlpha & iFlags & 0x2F000000)
|
|
Next->RenderAlphaDL();
|
|
}; // RenderAlpha
|
|
|
|
void __fastcall TSubModel::RenderVBO()
|
|
{ // g³ówna procedura renderowania przez VBO
|
|
if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist))
|
|
{
|
|
if (iFlags & 0xC000)
|
|
{
|
|
glPushMatrix();
|
|
if (fMatrix)
|
|
glMultMatrixf(fMatrix->readArray());
|
|
if (b_Anim)
|
|
RaAnimation(b_Anim);
|
|
}
|
|
if (eType < TP_ROTATOR)
|
|
{ // renderowanie obiektów OpenGL
|
|
if (iAlpha & iFlags & 0x1F) // rysuj gdy element nieprzezroczysty
|
|
{
|
|
if (TextureID < 0) // && (ReplacableSkinId!=0))
|
|
{ // zmienialne skóry
|
|
glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]);
|
|
// TexAlpha=!(iAlpha&1); //zmiana tylko w przypadku wymienej tekstury
|
|
}
|
|
else
|
|
glBindTexture(GL_TEXTURE_2D, TextureID); // równie¿ 0
|
|
glColor3fv(f4Diffuse); // McZapkie-240702: zamiast ub
|
|
// glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,f4Diffuse); //to samo, co glColor
|
|
if (Global::fLuminance < fLight)
|
|
{
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo
|
|
glDrawArrays(eType, iVboPtr,
|
|
iNumVerts); // narysuj naraz wszystkie trójk¹ty z VBO
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2);
|
|
}
|
|
else
|
|
glDrawArrays(eType, iVboPtr,
|
|
iNumVerts); // narysuj naraz wszystkie trójk¹ty z VBO
|
|
}
|
|
}
|
|
else if (eType == TP_FREESPOTLIGHT)
|
|
{ // wersja VBO
|
|
matrix4x4 mat; // macierz opisuje uk³ad renderowania wzglêdem kamery
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, mat.getArray());
|
|
// k¹t miêdzy kierunkiem œwiat³a a wspó³rzêdnymi kamery
|
|
vector3 gdzie = mat * vector3(0, 0, 0); // pozycja punktu œwiec¹cego wzglêdem kamery
|
|
fCosViewAngle = DotProduct(Normalize(mat * vector3(0, 0, 1) - gdzie), Normalize(gdzie));
|
|
if (fCosViewAngle > fCosFalloffAngle) // k¹t wiêkszy ni¿ maksymalny sto¿ek swiat³a
|
|
{
|
|
double Distdimm = 1.0;
|
|
if (fCosViewAngle < fCosHotspotAngle) // zmniejszona jasnoœæ miêdzy Hotspot a
|
|
// Falloff
|
|
if (fCosFalloffAngle < fCosHotspotAngle)
|
|
Distdimm = 1.0 -
|
|
(fCosHotspotAngle - fCosViewAngle) /
|
|
(fCosHotspotAngle - fCosFalloffAngle);
|
|
|
|
/* TODO: poprawic to zeby dzialalo
|
|
|
|
2- Inverse (Applies inverse decay. The formula is luminance=R0/R, where R0 is
|
|
the radial source of the light if no attenuation is used, or the Near End
|
|
value of the light if Attenuation is used. R is the radial distance of the
|
|
illuminated surface from R0.)
|
|
|
|
3- Inverse Square (Applies inverse-square decay. The formula for this is (R0/R)^2.
|
|
This is actually the "real-world" decay of light, but you might find it too dim
|
|
in the world of computer graphics.)
|
|
|
|
<light>.DecayRadius -- The distance over which the decay occurs.
|
|
|
|
if (iFarAttenDecay>0)
|
|
switch (iFarAttenDecay)
|
|
{
|
|
case 1:
|
|
Distdimm=fFarDecayRadius/(1+sqrt(fSquareDist)); //dorobic od
|
|
kata
|
|
break;
|
|
case 2:
|
|
Distdimm=fFarDecayRadius/(1+fSquareDist); //dorobic od kata
|
|
break;
|
|
}
|
|
if (Distdimm>1)
|
|
Distdimm=1;
|
|
|
|
*/
|
|
glBindTexture(GL_TEXTURE_2D, 0); // nie teksturowaæ
|
|
// glColor3f(f4Diffuse[0],f4Diffuse[1],f4Diffuse[2]);
|
|
// glColorMaterial(GL_FRONT,GL_EMISSION);
|
|
float color[4] = {f4Diffuse[0] * Distdimm, f4Diffuse[1] * Distdimm,
|
|
f4Diffuse[2] * Distdimm, 0};
|
|
// glColor3f(f4Diffuse[0]*Distdimm,f4Diffuse[1]*Distdimm,f4Diffuse[2]*Distdimm);
|
|
glColorMaterial(GL_FRONT, GL_EMISSION);
|
|
glDisable(GL_LIGHTING); // Tolaris-030603: bo mu punkty swiecace sie blendowaly
|
|
glColor3fv(color); // inaczej s¹ bia³e
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, color);
|
|
glDrawArrays(GL_POINTS, iVboPtr, iNumVerts); // narysuj wierzcho³ek z VBO
|
|
glEnable(GL_LIGHTING);
|
|
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); // co ma ustawiaæ glColor
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2); // bez tego s³upy siê œwiec¹
|
|
}
|
|
}
|
|
else if (eType == TP_STARS)
|
|
{
|
|
// glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly
|
|
if (Global::fLuminance < fLight)
|
|
{ // Ra: pewnie mo¿na by to zrobiæ lepiej, bez powtarzania StartVBO()
|
|
pRoot->EndVBO(); // Ra: to te¿ nie jest zbyt ³adne
|
|
if (pRoot->StartColorVBO())
|
|
{ // wyœwietlanie kolorowych punktów zamiast trójk¹tów
|
|
glBindTexture(GL_TEXTURE_2D, 0); // tekstury nie ma
|
|
glColorMaterial(GL_FRONT, GL_EMISSION);
|
|
glDisable(GL_LIGHTING); // Tolaris-030603: bo mu punkty swiecace sie blendowaly
|
|
// glMaterialfv(GL_FRONT,GL_EMISSION,f4Diffuse); //zeby swiecilo na kolorowo
|
|
glDrawArrays(GL_POINTS, iVboPtr,
|
|
iNumVerts); // narysuj naraz wszystkie punkty z VBO
|
|
glEnable(GL_LIGHTING);
|
|
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
|
|
// glMaterialfv(GL_FRONT,GL_EMISSION,emm2);
|
|
pRoot->EndVBO();
|
|
pRoot->StartVBO();
|
|
}
|
|
}
|
|
}
|
|
/*Ra: tu coœ jest bez sensu...
|
|
else
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
// if (eType==smt_FreeSpotLight)
|
|
// {
|
|
// if (iFarAttenDecay==0)
|
|
// glColor3f(Diffuse[0],Diffuse[1],Diffuse[2]);
|
|
// }
|
|
// else
|
|
//TODO: poprawic zeby dzialalo
|
|
glColor3f(f4Diffuse[0],f4Diffuse[1],f4Diffuse[2]);
|
|
glColorMaterial(GL_FRONT,GL_EMISSION);
|
|
glDisable(GL_LIGHTING); //Tolaris-030603: bo mu punkty swiecace sie blendowaly
|
|
//glBegin(GL_POINTS);
|
|
glDrawArrays(GL_POINTS,iVboPtr,iNumVerts); //narysuj wierzcho³ek z VBO
|
|
// glVertex3f(0,0,0);
|
|
//glEnd();
|
|
glEnable(GL_LIGHTING);
|
|
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
|
|
glMaterialfv(GL_FRONT,GL_EMISSION,emm2);
|
|
//glEndList();
|
|
}
|
|
*/
|
|
if (Child != NULL)
|
|
if (iAlpha & iFlags & 0x001F0000)
|
|
Child->RenderVBO();
|
|
if (iFlags & 0xC000)
|
|
glPopMatrix();
|
|
}
|
|
if (b_Anim < at_SecondsJump)
|
|
b_Anim = at_None; // wy³¹czenie animacji dla kolejnego u¿ycia submodelu
|
|
if (Next)
|
|
if (iAlpha & iFlags & 0x1F000000)
|
|
Next->RenderVBO(); // dalsze rekurencyjnie
|
|
}; // RaRender
|
|
|
|
void __fastcall TSubModel::RenderAlphaVBO()
|
|
{ // renderowanie przezroczystych przez VBO
|
|
if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist))
|
|
{
|
|
if (iFlags & 0xC000)
|
|
{
|
|
glPushMatrix(); // zapamiêtanie matrycy
|
|
if (fMatrix)
|
|
glMultMatrixf(fMatrix->readArray());
|
|
if (b_aAnim)
|
|
RaAnimation(b_aAnim);
|
|
}
|
|
glColor3fv(f4Diffuse);
|
|
if (eType < TP_ROTATOR)
|
|
{ // renderowanie obiektów OpenGL
|
|
if (iAlpha & iFlags & 0x2F) // rysuj gdy element przezroczysty
|
|
{
|
|
if (TextureID < 0) // && (ReplacableSkinId!=0))
|
|
{ // zmienialne skory
|
|
glBindTexture(GL_TEXTURE_2D, ReplacableSkinId[-TextureID]);
|
|
// TexAlpha=iAlpha&1; //zmiana tylko w przypadku wymienej tekstury
|
|
}
|
|
else
|
|
glBindTexture(GL_TEXTURE_2D, TextureID); // równie¿ 0
|
|
if (Global::fLuminance < fLight)
|
|
{
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, f4Diffuse); // zeby swiecilo na kolorowo
|
|
glDrawArrays(eType, iVboPtr,
|
|
iNumVerts); // narysuj naraz wszystkie trójk¹ty z VBO
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emm2);
|
|
}
|
|
else
|
|
glDrawArrays(eType, iVboPtr,
|
|
iNumVerts); // narysuj naraz wszystkie trójk¹ty z VBO
|
|
}
|
|
}
|
|
else if (eType == TP_FREESPOTLIGHT)
|
|
{
|
|
// dorobiæ aureolê!
|
|
}
|
|
if (Child)
|
|
if (iAlpha & iFlags & 0x002F0000)
|
|
Child->RenderAlphaVBO();
|
|
if (iFlags & 0xC000)
|
|
glPopMatrix();
|
|
}
|
|
if (b_aAnim < at_SecondsJump)
|
|
b_aAnim = at_None; // wy³¹czenie animacji dla kolejnego u¿ycia submodelu
|
|
if (Next)
|
|
if (iAlpha & iFlags & 0x2F000000)
|
|
Next->RenderAlphaVBO();
|
|
}; // RaRenderAlpha
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void __fastcall TSubModel::RaArrayFill(CVertNormTex *Vert)
|
|
{ // wype³nianie tablic VBO
|
|
if (Child)
|
|
Child->RaArrayFill(Vert);
|
|
if ((eType < TP_ROTATOR) || (eType == TP_STARS))
|
|
for (int i = 0; i < iNumVerts; ++i)
|
|
{
|
|
Vert[iVboPtr + i].x = Vertices[i].Point.x;
|
|
Vert[iVboPtr + i].y = Vertices[i].Point.y;
|
|
Vert[iVboPtr + i].z = Vertices[i].Point.z;
|
|
Vert[iVboPtr + i].nx = Vertices[i].Normal.x;
|
|
Vert[iVboPtr + i].ny = Vertices[i].Normal.y;
|
|
Vert[iVboPtr + i].nz = Vertices[i].Normal.z;
|
|
Vert[iVboPtr + i].u = Vertices[i].tu;
|
|
Vert[iVboPtr + i].v = Vertices[i].tv;
|
|
}
|
|
else if (eType == TP_FREESPOTLIGHT)
|
|
Vert[iVboPtr].x = Vert[iVboPtr].y = Vert[iVboPtr].z = 0.0;
|
|
if (Next)
|
|
Next->RaArrayFill(Vert);
|
|
};
|
|
|
|
void __fastcall TSubModel::Info()
|
|
{ // zapisanie informacji o submodelu do obiektu pomocniczego
|
|
TSubModelInfo *info = TSubModelInfo::pTable + TSubModelInfo::iCurrent;
|
|
info->pSubModel = this;
|
|
if (fMatrix && (iFlags & 0x8000)) // ma matrycê i jest ona niejednostkowa
|
|
info->iTransform = info->iTotalTransforms++;
|
|
if (TextureID > 0)
|
|
{ // jeœli ma teksturê niewymienn¹
|
|
for (int i = 0; i < info->iCurrent; ++i)
|
|
if (TextureID == info->pTable[i].pSubModel->TextureID) // porównanie z wczeœniejszym
|
|
{
|
|
info->iTexture = info->pTable[i].iTexture; // taki jaki ju¿ by³
|
|
break; // koniec sprawdzania
|
|
}
|
|
if (info->iTexture < 0) // jeœli nie znaleziono we wczeœniejszych
|
|
{
|
|
info->iTexture = ++info->iTotalTextures; // przydzielenie numeru tekstury w pliku (od 1)
|
|
AnsiString t = AnsiString(pTexture);
|
|
if (t.SubString(t.Length() - 3, 4) == ".tga")
|
|
t.Delete(t.Length() - 3, 4);
|
|
else if (t.SubString(t.Length() - 3, 4) == ".dds")
|
|
t.Delete(t.Length() - 3, 4);
|
|
if (t != AnsiString(pTexture))
|
|
{ // jeœli siê zmieni³o
|
|
// pName=new char[token.length()+1]; //nie ma sensu skracaæ tabeli
|
|
strcpy(pTexture, t.c_str());
|
|
}
|
|
info->iTextureLen = t.Length() + 1; // przygotowanie do zapisania, z zerem na koñcu
|
|
}
|
|
}
|
|
else
|
|
info->iTexture = TextureID; // nie ma albo wymienna
|
|
// if (asName.Length())
|
|
if (pName)
|
|
{
|
|
info->iName = info->iTotalNames++; // przydzielenie numeru nazwy w pliku (od 0)
|
|
info->iNameLen = strlen(pName) + 1; // z zerem na koñcu
|
|
}
|
|
++info->iCurrent; // przejœcie do kolejnego obiektu pomocniczego
|
|
if (Child)
|
|
{
|
|
info->iChild = info->iCurrent;
|
|
Child->Info();
|
|
}
|
|
if (Next)
|
|
{
|
|
info->iNext = info->iCurrent;
|
|
Next->Info();
|
|
}
|
|
};
|
|
|
|
void __fastcall TSubModel::InfoSet(TSubModelInfo *info)
|
|
{ // ustawienie danych wg obiektu pomocniczego do zapisania w pliku
|
|
int ile = (char *)&uiDisplayList - (char *)&eType; // iloœæ bajtów pomiêdzy tymi zmiennymi
|
|
ZeroMemory(this, sizeof(TSubModel)); // zerowaie ca³oœci
|
|
CopyMemory(this, info->pSubModel, ile); // skopiowanie pamiêci 1:1
|
|
iTexture = info->iTexture; // numer nazwy tekstury, a nie numer w OpenGL
|
|
TextureID = info->iTexture; // numer tekstury w OpenGL
|
|
iName = info->iName; // numer nazwy w obszarze nazw
|
|
iMatrix = info->iTransform; // numer macierzy
|
|
Next = (TSubModel *)info->iNext; // numer nastêpnego
|
|
Child = (TSubModel *)info->iChild; // numer potomnego
|
|
iFlags &= ~0x200; // nie jest wczytany z tekstowego
|
|
// asTexture=asName="";
|
|
pTexture = pName = NULL;
|
|
};
|
|
|
|
void __fastcall TSubModel::BinInit(TSubModel *s, float4x4 *m, float8 *v, TStringPack *t,
|
|
TStringPack *n, bool dynamic)
|
|
{ // ustawienie wskaŸników w submodelu
|
|
iVisible = 1; // tymczasowo u¿ywane
|
|
Child = ((int)Child > 0) ? s + (int)Child : NULL; // zerowy nie mo¿e byæ potomnym
|
|
Next = ((int)Next > 0) ? s + (int)Next : NULL; // zerowy nie mo¿e byæ nastêpnym
|
|
fMatrix = ((iMatrix >= 0) && m) ? m + iMatrix : NULL;
|
|
// if (n&&(iName>=0)) asName=AnsiString(n->String(iName)); else asName="";
|
|
if (n && (iName >= 0))
|
|
{
|
|
pName = n->String(iName);
|
|
AnsiString s = AnsiString(pName);
|
|
if (!s.IsEmpty())
|
|
{ // jeœli dany submodel jest zgaszonym œwiat³em, to domyœlnie go ukrywamy
|
|
if (s.SubString(1, 8) == "Light_On") // jeœli jest œwiat³em numerowanym
|
|
iVisible = 0; // to domyœlnie wy³¹czyæ, ¿eby siê nie nak³ada³o z obiektem
|
|
// "Light_Off"
|
|
else if (dynamic) // inaczej wy³¹cza³o smugê w latarniach
|
|
if (s.SubString(s.Length() - 2, 3) ==
|
|
"_on") // jeœli jest kontrolk¹ w stanie zapalonym
|
|
iVisible = 0; // to domyœlnie wy³¹czyæ, ¿eby siê nie nak³ada³o z obiektem "_off"
|
|
}
|
|
}
|
|
else
|
|
pName = NULL;
|
|
if (iTexture > 0)
|
|
{ // obs³uga sta³ej tekstury
|
|
// TextureID=TTexturesManager::GetTextureID(t->String(TextureID));
|
|
// asTexture=AnsiString(t->String(iTexture));
|
|
pTexture = t->String(iTexture);
|
|
AnsiString t = AnsiString(pTexture);
|
|
if (t.LastDelimiter("/\\") == 0)
|
|
t.Insert(Global::asCurrentTexturePath, 1);
|
|
TextureID = TTexturesManager::GetTextureID(szTexturePath,
|
|
Global::asCurrentTexturePath.c_str(), t.c_str());
|
|
// TexAlpha=TTexturesManager::GetAlpha(TextureID); //zmienna robocza
|
|
// ustawienie cyklu przezroczyste/nieprzezroczyste zale¿nie od w³asnoœci sta³ej tekstury
|
|
// iFlags=(iFlags&~0x30)|(TTexturesManager::GetAlpha(TextureID)?0x20:0x10);
|
|
// //0x10-nieprzezroczysta, 0x20-przezroczysta
|
|
if (Opacity < 1.0) // przezroczystoϾ z tekstury brana tylko dla Opacity 0!
|
|
iFlags |= TTexturesManager::GetAlpha(TextureID) ?
|
|
0x20 :
|
|
0x10; // 0x10-nieprzezroczysta, 0x20-przezroczysta
|
|
else
|
|
iFlags |= 0x10; // normalnie nieprzezroczyste
|
|
}
|
|
b_aAnim = b_Anim; // skopiowanie animacji do drugiego cyklu
|
|
iFlags &= ~0x0200; // wczytano z pliku binarnego (nie jest w³aœcicielem tablic)
|
|
Vertices = v + iVboPtr;
|
|
// if (!iNumVerts) eType=-1; //tymczasowo zmiana typu, ¿eby siê nie renderowa³o na si³ê
|
|
};
|
|
void __fastcall TSubModel::AdjustDist()
|
|
{ // aktualizacja odleg³oœci faz LoD, zale¿na od rozdzielczoœci pionowej oraz multisamplingu
|
|
if (fSquareMaxDist > 0.0)
|
|
fSquareMaxDist *= Global::fDistanceFactor;
|
|
if (fSquareMinDist > 0.0)
|
|
fSquareMinDist *= Global::fDistanceFactor;
|
|
// if (fNearAttenStart>0.0) fNearAttenStart*=Global::fDistanceFactor;
|
|
// if (fNearAttenEnd>0.0) fNearAttenEnd*=Global::fDistanceFactor;
|
|
if (Child)
|
|
Child->AdjustDist();
|
|
if (Next)
|
|
Next->AdjustDist();
|
|
};
|
|
|
|
void __fastcall TSubModel::ColorsSet(int *a, int *d, int *s)
|
|
{ // ustawienie kolorów dla modelu terenu
|
|
int i;
|
|
if (a)
|
|
for (i = 0; i < 4; ++i)
|
|
f4Ambient[i] = a[i] / 255.0;
|
|
if (d)
|
|
for (i = 0; i < 4; ++i)
|
|
f4Diffuse[i] = d[i] / 255.0;
|
|
if (s)
|
|
for (i = 0; i < 4; ++i)
|
|
f4Specular[i] = s[i] / 255.0;
|
|
};
|
|
void __fastcall TSubModel::ParentMatrix(float4x4 *m)
|
|
{ // pobranie transformacji wzglêdem wstawienia modelu
|
|
// jeœli nie zosta³o wykonane Init() (tzn. zaraz po wczytaniu T3D), to dodatkowy obrót
|
|
// obrót T3D jest wymagany np. do policzenia wysokoœci pantografów
|
|
*m = float4x4(*fMatrix); // skopiowanie, bo bêdziemy mno¿yæ
|
|
// m(3)[1]=m[3][1]+0.054; //w górê o wysokoœæ œlizgu (na razie tak)
|
|
TSubModel *sm = this;
|
|
while (sm->Parent)
|
|
{ // przenieœæ tê funkcjê do modelu
|
|
if (sm->Parent->GetMatrix())
|
|
*m = *sm->Parent->GetMatrix() * *m;
|
|
sm = sm->Parent;
|
|
}
|
|
// dla ostatniego mo¿e byæ potrzebny dodatkowy obrót, jeœli wczytano z T3D, a nie obrócono
|
|
// jeszcze
|
|
};
|
|
float __fastcall TSubModel::MaxY(const float4x4 &m)
|
|
{ // obliczenie maksymalnej wysokoœci, na pocz¹tek œlizgu w pantografie
|
|
if (eType != 4)
|
|
return 0; // tylko dla trójk¹tów liczymy
|
|
if (iNumVerts < 1)
|
|
return 0;
|
|
if (!Vertices)
|
|
return 0;
|
|
float y, my = m[0][1] * Vertices[0].Point.x + m[1][1] * Vertices[0].Point.y +
|
|
m[2][1] * Vertices[0].Point.z + m[3][1];
|
|
for (int i = 1; i < iNumVerts; ++i)
|
|
{
|
|
y = m[0][1] * Vertices[i].Point.x + m[1][1] * Vertices[i].Point.y +
|
|
m[2][1] * Vertices[i].Point.z + m[3][1];
|
|
if (my < y)
|
|
my = y;
|
|
}
|
|
return my;
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
|
|
__fastcall TModel3d::TModel3d()
|
|
{
|
|
// Materials=NULL;
|
|
// MaterialsCount=0;
|
|
Root = NULL;
|
|
iFlags = 0;
|
|
iSubModelsCount = 0;
|
|
iModel = NULL; // tylko jak wczytany model binarny
|
|
iNumVerts = 0; // nie ma jeszcze wierzcho³ków
|
|
};
|
|
/*
|
|
__fastcall TModel3d::TModel3d(char *FileName)
|
|
{
|
|
// Root=NULL;
|
|
// Materials=NULL;
|
|
// MaterialsCount=0;
|
|
Root=NULL;
|
|
SubModelsCount=0;
|
|
iFlags=0;
|
|
LoadFromFile(FileName);
|
|
};
|
|
*/
|
|
__fastcall TModel3d::~TModel3d()
|
|
{
|
|
// SafeDeleteArray(Materials);
|
|
if (iFlags & 0x0200)
|
|
{ // wczytany z pliku tekstowego, submodele sprz¹taj¹ same
|
|
SafeDelete(Root); // submodele siê usun¹ rekurencyjnie
|
|
}
|
|
else
|
|
{ // wczytano z pliku binarnego (jest w³aœcicielem tablic)
|
|
m_pVNT = NULL; // nie usuwaæ tego, bo wskazuje na iModel
|
|
Root = NULL;
|
|
delete[] iModel; // usuwamy ca³y wczytany plik i to wystarczy
|
|
}
|
|
// póŸniej siê jeszcze usuwa obiekt z którego dziedziczymy tabelê VBO
|
|
};
|
|
|
|
TSubModel *__fastcall TModel3d::AddToNamed(const char *Name, TSubModel *SubModel)
|
|
{
|
|
TSubModel *sm = Name ? GetFromName(Name) : NULL;
|
|
AddTo(sm, SubModel); // szukanie nadrzêdnego
|
|
return sm; // zwracamy wskaŸnik do nadrzêdnego submodelu
|
|
};
|
|
|
|
void __fastcall TModel3d::AddTo(TSubModel *tmp, TSubModel *SubModel)
|
|
{ // jedyny poprawny sposób dodawania submodeli, inaczej mog¹ zgin¹æ przy zapisie E3D
|
|
if (tmp)
|
|
{ // jeœli znaleziony, pod³¹czamy mu jako potomny
|
|
tmp->ChildAdd(SubModel);
|
|
}
|
|
else
|
|
{ // jeœli nie znaleziony, podczepiamy do ³añcucha g³ównego
|
|
SubModel->NextAdd(Root); // Ra: zmiana kolejnoœci renderowania wymusza zmianê tu
|
|
Root = SubModel;
|
|
}
|
|
++iSubModelsCount; // teraz jest o 1 submodel wiêcej
|
|
iFlags |= 0x0200; // submodele s¹ oddzielne
|
|
};
|
|
|
|
TSubModel *__fastcall TModel3d::GetFromName(const char *sName)
|
|
{ // wyszukanie submodelu po nazwie
|
|
if (!sName)
|
|
return Root; // potrzebne do terenu z E3D
|
|
if (iFlags & 0x0200) // wczytany z pliku tekstowego, wyszukiwanie rekurencyjne
|
|
return Root ? Root->GetFromName(sName) : NULL;
|
|
else // wczytano z pliku binarnego, mo¿na wyszukaæ iteracyjnie
|
|
{
|
|
// for (int i=0;i<iSubModelsCount;++i)
|
|
return Root ? Root->GetFromName(sName) : NULL;
|
|
}
|
|
};
|
|
|
|
/*
|
|
TMaterial* __fastcall TModel3d::GetMaterialFromName(char *sName)
|
|
{
|
|
AnsiString tmp=AnsiString(sName).Trim();
|
|
for (int i=0; i<MaterialsCount; i++)
|
|
if (strcmp(sName,Materials[i].Name.c_str())==0)
|
|
// if (Trim()==Materials[i].Name.tmp)
|
|
return Materials+i;
|
|
return Materials;
|
|
}
|
|
*/
|
|
|
|
bool __fastcall TModel3d::LoadFromFile(char *FileName, bool dynamic)
|
|
{ // wczytanie modelu z pliku
|
|
AnsiString name = AnsiString(FileName).LowerCase();
|
|
int i = name.LastDelimiter(".");
|
|
if (i)
|
|
if (name.SubString(i, name.Length() - i + 1) == ".t3d")
|
|
name.Delete(i, 4);
|
|
asBinary = name + ".e3d";
|
|
if (FileExists(asBinary))
|
|
{
|
|
LoadFromBinFile(asBinary.c_str(), dynamic);
|
|
asBinary = ""; // wy³¹czenie zapisu
|
|
Init();
|
|
}
|
|
else
|
|
{
|
|
if (FileExists(name + ".t3d"))
|
|
{
|
|
LoadFromTextFile(FileName, dynamic); // wczytanie tekstowego
|
|
if (!dynamic) // pojazdy dopiero po ustawieniu animacji
|
|
Init(); // generowanie siatek i zapis E3D
|
|
}
|
|
}
|
|
return Root ? (iSubModelsCount > 0) : false; // brak pliku albo problem z wczytaniem
|
|
};
|
|
|
|
void __fastcall TModel3d::LoadFromBinFile(char *FileName, bool dynamic)
|
|
{ // wczytanie modelu z pliku binarnego
|
|
WriteLog("Loading - binary model: " + AnsiString(FileName));
|
|
int i = 0, j, k, ch, size;
|
|
TFileStream *fs = new TFileStream(AnsiString(FileName), fmOpenRead);
|
|
size = fs->Size >> 2;
|
|
iModel = new int[size]; // ten wskaŸnik musi byæ w modelu, aby zwolniæ pamiêæ
|
|
fs->Read(iModel, fs->Size); // wczytanie pliku
|
|
delete fs;
|
|
float4x4 *m = NULL; // transformy
|
|
// zestaw kromek:
|
|
while ((i << 2) < size) // w pliku mo¿e byæ kilka modeli
|
|
{
|
|
ch = iModel[i]; // nazwa kromki
|
|
j = i + (iModel[i + 1] >> 2); // pocz¹tek nastêpnej kromki
|
|
if (ch == 'E3D0') // g³ówna: 'E3D0',len,pod-kromki
|
|
{ // tylko tê kromkê znamy, mo¿e kiedyœ jeszcze DOF siê zrobi
|
|
i += 2;
|
|
while (i < j)
|
|
{ // przetwarzanie kromek wewnêtrznych
|
|
ch = iModel[i]; // nazwa kromki
|
|
k = (iModel[i + 1] >> 2); // d³ugoœæ aktualnej kromki
|
|
switch (ch)
|
|
{
|
|
case 'MDL0': // zmienne modelu: 'E3D0',len,(informacje o modelu)
|
|
break;
|
|
case 'VNT0': // wierzcho³ki: 'VNT0',len,(32 bajty na wierzcho³ek)
|
|
iNumVerts = (k - 2) >> 3;
|
|
m_nVertexCount = iNumVerts;
|
|
m_pVNT = (CVertNormTex *)(iModel + i + 2);
|
|
break;
|
|
case 'SUB0': // submodele: 'SUB0',len,(256 bajtów na submodel)
|
|
iSubModelsCount = (k - 2) / 64;
|
|
Root = (TSubModel *)(iModel + i + 2); // numery na wskaŸniki przetworzymy
|
|
// póŸniej
|
|
break;
|
|
case 'SUB1': // submodele: 'SUB1',len,(320 bajtów na submodel)
|
|
iSubModelsCount = (k - 2) / 80;
|
|
Root = (TSubModel *)(iModel + i + 2); // numery na wskaŸniki przetworzymy
|
|
// póŸniej
|
|
for (ch = 1; ch < iSubModelsCount;
|
|
++ch) // trzeba przesun¹æ bli¿ej, bo 256 wystarczy
|
|
MoveMemory(((char *)Root) + 256 * ch, ((char *)Root) + 320 * ch, 256);
|
|
break;
|
|
case 'TRA0': // transformy: 'TRA0',len,(64 bajty na transform)
|
|
m = (float4x4 *)(iModel + i + 2); // tabela transformów
|
|
break;
|
|
case 'TRA1': // transformy: 'TRA1',len,(128 bajtów na transform)
|
|
m = (float4x4 *)(iModel + i + 2); // tabela transformów
|
|
for (ch = 0; ch < ((k - 2) >> 1); ++ch)
|
|
*(((float *)m) + ch) = *(((double *)m) + ch); // przepisanie double do float
|
|
break;
|
|
case 'IDX1': // indeksy 1B: 'IDX2',len,(po bajcie na numer wierzcho³ka)
|
|
break;
|
|
case 'IDX2': // indeksy 2B: 'IDX2',len,(po 2 bajty na numer wierzcho³ka)
|
|
break;
|
|
case 'IDX4': // indeksy 4B: 'IDX4',len,(po 4 bajty na numer wierzcho³ka)
|
|
break;
|
|
case 'TEX0': // tekstury: 'TEX0',len,(³añcuchy zakoñczone zerem - pliki tekstur)
|
|
Textures.Init((char *)(iModel + i)); //³¹cznie z nag³ówkiem
|
|
break;
|
|
case 'TIX0': // indeks nazw tekstur
|
|
Textures.InitIndex((int *)(iModel + i)); //³¹cznie z nag³ówkiem
|
|
break;
|
|
case 'NAM0': // nazwy: 'NAM0',len,(³añcuchy zakoñczone zerem - nazwy submodeli)
|
|
Names.Init((char *)(iModel + i)); //³¹cznie z nag³ówkiem
|
|
break;
|
|
case 'NIX0': // indeks nazw submodeli
|
|
Names.InitIndex((int *)(iModel + i)); //³¹cznie z nag³ówkiem
|
|
break;
|
|
}
|
|
i += k; // przejœcie do kolejnej kromki
|
|
}
|
|
}
|
|
i = j;
|
|
}
|
|
for (i = 0; i < iSubModelsCount; ++i)
|
|
{ // aktualizacja wskaŸników w submodelach
|
|
Root[i].BinInit(Root, m, (float8 *)m_pVNT, &Textures, &Names, dynamic);
|
|
if (Root[i].ChildGet())
|
|
Root[i].ChildGet()->Parent = Root + i; // wpisanie wskaŸnika nadrzêdnego do potmnego
|
|
if (Root[i].NextGet())
|
|
Root[i].NextGet()->Parent =
|
|
Root[i].Parent; // skopiowanie wskaŸnika nadrzêdnego do kolejnego
|
|
}
|
|
iFlags &= ~0x0200;
|
|
return;
|
|
};
|
|
|
|
void __fastcall TModel3d::LoadFromTextFile(char *FileName, bool dynamic)
|
|
{ // wczytanie submodelu z pliku tekstowego
|
|
WriteLog("Loading - text model: " + AnsiString(FileName));
|
|
iFlags |= 0x0200; // wczytano z pliku tekstowego (w³aœcicielami tablic s¹ submodle)
|
|
cParser parser(FileName, cParser::buffer_FILE); // Ra: tu powinno byæ "models\\"...
|
|
TSubModel *SubModel;
|
|
std::string token;
|
|
parser.getToken(token);
|
|
iNumVerts = 0; // w konstruktorze to jest
|
|
while (token != "" || parser.eof())
|
|
{
|
|
std::string parent;
|
|
// parser.getToken(parent);
|
|
parser.getTokens(1, false); // nazwa submodelu nadrzêdnego bez zmieny na ma³e
|
|
parser >> parent;
|
|
if (parent == "")
|
|
break;
|
|
SubModel = new TSubModel();
|
|
iNumVerts += SubModel->Load(parser, this, iNumVerts, dynamic);
|
|
SubModel->Parent = AddToNamed(
|
|
parent.c_str(), SubModel); // bêdzie potrzebne do wyliczenia pozycji, np. pantografu
|
|
// iSubModelsCount++;
|
|
parser.getToken(token);
|
|
}
|
|
// Ra: od wersji 334 przechylany jest ca³y model, a nie tylko pierwszy submodel
|
|
// ale bujanie kabiny nadal u¿ywa bananów :( od 393 przywrócone, ale z dodatkowym warunkiem
|
|
if (Global::iConvertModels & 4)
|
|
{ // automatyczne banany czasem psu³y przechylanie kabin...
|
|
if (dynamic && Root)
|
|
{
|
|
if (Root->NextGet()) // jeœli ma jakiekolwiek kolejne
|
|
{ // dynamic musi mieæ "banana", bo tylko pierwszy obiekt jest animowany, a nastêpne nie
|
|
SubModel = new TSubModel(); // utworzenie pustego
|
|
SubModel->ChildAdd(Root);
|
|
Root = SubModel;
|
|
++iSubModelsCount;
|
|
}
|
|
Root->WillBeAnimated(); // bo z tym jest du¿o problemów
|
|
}
|
|
}
|
|
}
|
|
|
|
void __fastcall TModel3d::Init()
|
|
{ // obrócenie pocz¹tkowe uk³adu wspó³rzêdnych, dla pojazdów wykonywane po analizie animacji
|
|
if (iFlags & 0x8000)
|
|
return; // operacje zosta³y ju¿ wykonane
|
|
if (Root)
|
|
{
|
|
if (iFlags & 0x0200) // jeœli wczytano z pliku tekstowego
|
|
{ // jest jakiœ dziwny b³¹d, ¿e obkrêcany ma byæ tylko ostatni submodel g³ównego ³añcucha
|
|
// TSubModel *p=Root;
|
|
// do
|
|
//{p->InitialRotate(true); //ostatniemu nale¿y siê konwersja uk³adu wspó³rzêdnych
|
|
// p=p->NextGet();
|
|
//}
|
|
// while (p->NextGet())
|
|
// Root->InitialRotate(false); //a poprzednim tylko optymalizacja
|
|
Root->InitialRotate(true); // argumet okreœla, czy wykonaæ pierwotny obrót
|
|
}
|
|
iFlags |= Root->FlagsCheck() | 0x8000; // flagi ca³ego modelu
|
|
if (!asBinary.IsEmpty()) // jeœli jest podana nazwa
|
|
{
|
|
if (Global::iConvertModels) // i w³¹czony zapis
|
|
SaveToBinFile(asBinary.c_str()); // utworzy tablicê (m_pVNT)
|
|
asBinary = ""; // zablokowanie powtórnego zapisu
|
|
}
|
|
if (iNumVerts)
|
|
{
|
|
if (Global::fDistanceFactor !=
|
|
1.0) // trochê zaoszczêdzi czasu na modelach z wieloma submocelami
|
|
Root->AdjustDist(); // aktualizacja odleg³oœci faz LoD, zale¿nie od rozdzielczoœci
|
|
// pionowej oraz multisamplingu
|
|
if (Global::bUseVBO)
|
|
{
|
|
if (!m_pVNT) // jeœli nie ma jeszcze tablicy (wczytano z pliku tekstowego)
|
|
{ // tworzenie tymczasowej tablicy z wierzcho³kami ca³ego modelu
|
|
MakeArray(iNumVerts); // tworzenie tablic dla VBO
|
|
Root->RaArrayFill(m_pVNT); // wype³nianie tablicy
|
|
BuildVBOs(); // tworzenie VBO i usuwanie tablicy z pamiêci
|
|
}
|
|
else
|
|
BuildVBOs(false); // tworzenie VBO bez usuwania tablicy z pamiêci
|
|
}
|
|
else
|
|
{ // przygotowanie skompilowanych siatek dla DisplayLists
|
|
Root->DisplayLists(); // tworzenie skompilowanej listy dla submodelu
|
|
}
|
|
// if (Root->TextureID) //o ile ma teksturê
|
|
// Root->iFlags|=0x80; //koniecznoϾ ustawienia tekstury
|
|
}
|
|
}
|
|
};
|
|
|
|
void __fastcall TModel3d::SaveToBinFile(char *FileName)
|
|
{ // zapis modelu binarnego
|
|
WriteLog("Saving E3D binary model.");
|
|
int i, zero = 0;
|
|
TSubModelInfo *info = new TSubModelInfo[iSubModelsCount];
|
|
info->Reset();
|
|
Root->Info(); // zebranie informacji o submodelach
|
|
int len; //³¹czna d³ugoœæ pliku
|
|
int sub; // iloϾ submodeli (w bajtach)
|
|
int tra; // wielkoœæ obszaru transformów
|
|
int vnt; // wielkoœæ obszaru wierzcho³ków
|
|
int tex = 0; // wielkoϾ obszaru nazw tekstur
|
|
int nam = 0; // wielkoϾ obszaru nazw submodeli
|
|
sub = 8 + sizeof(TSubModel) * iSubModelsCount;
|
|
tra = info->iTotalTransforms ? 8 + 64 * info->iTotalTransforms : 0;
|
|
vnt = 8 + 32 * iNumVerts;
|
|
for (i = 0; i < iSubModelsCount; ++i)
|
|
{
|
|
tex += info[i].iTextureLen;
|
|
nam += info[i].iNameLen;
|
|
}
|
|
if (tex)
|
|
tex += 9; // 8 na nag³ówek i jeden ci¹g pusty (tylko znacznik koñca)
|
|
if (nam)
|
|
nam += 8;
|
|
len = 8 + sub + tra + vnt + tex + ((-tex) & 3) + nam + ((-nam) & 3);
|
|
TSubModel *roboczy = new TSubModel(); // bufor u¿ywany do zapisywania
|
|
// AnsiString *asN=&roboczy->asName,*asT=&roboczy->asTexture;
|
|
// roboczy->FirstInit(); //¿eby delete nie usuwa³o czego nie powinno
|
|
TFileStream *fs = new TFileStream(AnsiString(FileName), fmCreate);
|
|
fs->Write("E3D0", 4); // kromka g³ówna
|
|
fs->Write(&len, 4);
|
|
{
|
|
fs->Write("SUB0", 4); // dane submodeli
|
|
fs->Write(&sub, 4);
|
|
for (i = 0; i < iSubModelsCount; ++i)
|
|
{
|
|
roboczy->InfoSet(info + i);
|
|
fs->Write(roboczy, sizeof(TSubModel)); // zapis jednego submodelu
|
|
}
|
|
}
|
|
if (tra)
|
|
{ // zapis transformów
|
|
fs->Write("TRA0", 4); // transformy
|
|
fs->Write(&tra, 4);
|
|
for (i = 0; i < iSubModelsCount; ++i)
|
|
if (info[i].iTransform >= 0)
|
|
fs->Write(info[i].pSubModel->GetMatrix(), 16 * 4);
|
|
}
|
|
{ // zapis wierzcho³ków
|
|
MakeArray(iNumVerts); // tworzenie tablic dla VBO
|
|
Root->RaArrayFill(m_pVNT); // wype³nianie tablicy
|
|
fs->Write("VNT0", 4); // wierzcho³ki
|
|
fs->Write(&vnt, 4);
|
|
fs->Write(m_pVNT, 32 * iNumVerts);
|
|
}
|
|
if (tex) // mo¿e byæ jeden submodel ze zmienn¹ tekstur¹ i nazwy nie bêdzie
|
|
{ // zapis nazw tekstur
|
|
fs->Write("TEX0", 4); // nazwy tekstur
|
|
i = (tex + 3) & ~3; // zaokr¹glenie w górê
|
|
fs->Write(&i, 4);
|
|
fs->Write(&zero, 1); // ci¹g o numerze zero nie jest u¿ywany, ma tylko znacznik koñca
|
|
for (i = 0; i < iSubModelsCount; ++i)
|
|
if (info[i].iTextureLen)
|
|
fs->Write(info[i].pSubModel->pTexture, info[i].iTextureLen);
|
|
if ((-tex) & 3)
|
|
fs->Write(&zero, ((-tex) & 3)); // wyrównanie do wielokrotnoœci 4 bajtów
|
|
}
|
|
if (nam) // mo¿e byæ jeden anonimowy submodel w modelu
|
|
{ // zapis nazw submodeli
|
|
fs->Write("NAM0", 4); // nazwy submodeli
|
|
i = (nam + 3) & ~3; // zaokr¹glenie w górê
|
|
fs->Write(&i, 4);
|
|
for (i = 0; i < iSubModelsCount; ++i)
|
|
if (info[i].iNameLen)
|
|
fs->Write(info[i].pSubModel->pName, info[i].iNameLen);
|
|
if ((-nam) & 3)
|
|
fs->Write(&zero, ((-nam) & 3)); // wyrównanie do wielokrotnoœci 4 bajtów
|
|
}
|
|
delete fs;
|
|
// roboczy->FirstInit(); //¿eby delete nie usuwa³o czego nie powinno
|
|
// roboczy->iFlags=0; //¿eby delete nie usuwa³o czego nie powinno
|
|
// roboczy->asName)=asN;
|
|
//&roboczy->asTexture=asT;
|
|
delete roboczy;
|
|
delete[] info;
|
|
};
|
|
|
|
void __fastcall TModel3d::BreakHierarhy() { Error("Not implemented yet :("); };
|
|
|
|
/*
|
|
void __fastcall TModel3d::Render(vector3 pPosition,double fAngle,GLuint ReplacableSkinId,int iAlpha)
|
|
{
|
|
// glColor3f(1.0f,1.0f,1.0f);
|
|
// glColor3f(0.0f,0.0f,0.0f);
|
|
glPushMatrix();
|
|
|
|
glTranslated(pPosition.x,pPosition.y,pPosition.z);
|
|
if (fAngle!=0)
|
|
glRotatef(fAngle,0,1,0);
|
|
/*
|
|
matrix4x4 Identity;
|
|
Identity.Identity();
|
|
|
|
matrix4x4 CurrentMatrix;
|
|
glGetdoublev(GL_MODELVIEW_MATRIX,CurrentMatrix.getArray());
|
|
vector3 pos=vector3(0,0,0);
|
|
pos=CurrentMatrix*pos;
|
|
fSquareDist=SquareMagnitude(pos);
|
|
* /
|
|
fSquareDist=SquareMagnitude(pPosition-Global::GetCameraPosition());
|
|
|
|
#ifdef _DEBUG
|
|
if (Root)
|
|
Root->Render(ReplacableSkinId,iAlpha);
|
|
#else
|
|
Root->Render(ReplacableSkinId,iAlpha);
|
|
#endif
|
|
glPopMatrix();
|
|
};
|
|
*/
|
|
|
|
void __fastcall TModel3d::Render(double fSquareDistance, GLuint *ReplacableSkinId, int iAlpha)
|
|
{
|
|
iAlpha ^= 0x0F0F000F; // odwrócenie flag tekstur, aby wy³apaæ nieprzezroczyste
|
|
if (iAlpha & iFlags & 0x1F1F001F) // czy w ogóle jest co robiæ w tym cyklu?
|
|
{
|
|
TSubModel::fSquareDist = fSquareDistance; // zmienna globalna!
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha);
|
|
Root->RenderDL();
|
|
}
|
|
};
|
|
|
|
void __fastcall TModel3d::RenderAlpha(double fSquareDistance, GLuint *ReplacableSkinId, int iAlpha)
|
|
{
|
|
if (iAlpha & iFlags & 0x2F2F002F)
|
|
{
|
|
TSubModel::fSquareDist = fSquareDistance; // zmienna globalna!
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha);
|
|
Root->RenderAlphaDL();
|
|
}
|
|
};
|
|
|
|
/*
|
|
void __fastcall TModel3d::RaRender(vector3 pPosition,double fAngle,GLuint *ReplacableSkinId,int
|
|
iAlpha)
|
|
{
|
|
// glColor3f(1.0f,1.0f,1.0f);
|
|
// glColor3f(0.0f,0.0f,0.0f);
|
|
glPushMatrix(); //zapamiêtanie matrycy przekszta³cenia
|
|
glTranslated(pPosition.x,pPosition.y,pPosition.z);
|
|
if (fAngle!=0)
|
|
glRotatef(fAngle,0,1,0);
|
|
/*
|
|
matrix4x4 Identity;
|
|
Identity.Identity();
|
|
|
|
matrix4x4 CurrentMatrix;
|
|
glGetdoublev(GL_MODELVIEW_MATRIX,CurrentMatrix.getArray());
|
|
vector3 pos=vector3(0,0,0);
|
|
pos=CurrentMatrix*pos;
|
|
fSquareDist=SquareMagnitude(pos);
|
|
*/
|
|
/*
|
|
fSquareDist=SquareMagnitude(pPosition-Global::GetCameraPosition()); //zmienna globalna!
|
|
if (StartVBO())
|
|
{//odwrócenie flag, aby wy³apaæ nieprzezroczyste
|
|
Root->ReplacableSet(ReplacableSkinId,iAlpha^0x0F0F000F);
|
|
Root->RaRender();
|
|
EndVBO();
|
|
}
|
|
glPopMatrix(); //przywrócenie ustawieñ przekszta³cenia
|
|
};
|
|
*/
|
|
|
|
void __fastcall TModel3d::RaRender(double fSquareDistance, GLuint *ReplacableSkinId, int iAlpha)
|
|
{ // renderowanie specjalne, np. kabiny
|
|
iAlpha ^= 0x0F0F000F; // odwrócenie flag tekstur, aby wy³apaæ nieprzezroczyste
|
|
if (iAlpha & iFlags & 0x1F1F001F) // czy w ogóle jest co robiæ w tym cyklu?
|
|
{
|
|
TSubModel::fSquareDist = fSquareDistance; // zmienna globalna!
|
|
if (StartVBO())
|
|
{ // odwrócenie flag, aby wy³apaæ nieprzezroczyste
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha);
|
|
Root->pRoot = this;
|
|
Root->RenderVBO();
|
|
EndVBO();
|
|
}
|
|
}
|
|
};
|
|
|
|
void __fastcall TModel3d::RaRenderAlpha(double fSquareDistance, GLuint *ReplacableSkinId,
|
|
int iAlpha)
|
|
{ // renderowanie specjalne, np. kabiny
|
|
if (iAlpha & iFlags & 0x2F2F002F) // czy w ogóle jest co robiæ w tym cyklu?
|
|
{
|
|
TSubModel::fSquareDist = fSquareDistance; // zmienna globalna!
|
|
if (StartVBO())
|
|
{
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha);
|
|
Root->RenderAlphaVBO();
|
|
EndVBO();
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
void __fastcall TModel3d::RaRenderAlpha(vector3 pPosition,double fAngle,GLuint *ReplacableSkinId,int
|
|
iAlpha)
|
|
{
|
|
glPushMatrix();
|
|
glTranslatef(pPosition.x,pPosition.y,pPosition.z);
|
|
if (fAngle!=0)
|
|
glRotatef(fAngle,0,1,0);
|
|
fSquareDist=SquareMagnitude(pPosition-Global::GetCameraPosition()); //zmienna globalna!
|
|
if (StartVBO())
|
|
{Root->ReplacableSet(ReplacableSkinId,iAlpha);
|
|
Root->RaRenderAlpha();
|
|
EndVBO();
|
|
}
|
|
glPopMatrix();
|
|
};
|
|
*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 2011-03-16 cztery nowe funkcje renderowania z mo¿liwoœci¹ pochylania obiektów
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void __fastcall TModel3d::Render(vector3 *vPosition, vector3 *vAngle, GLuint *ReplacableSkinId,
|
|
int iAlpha)
|
|
{ // nieprzezroczyste, Display List
|
|
glPushMatrix();
|
|
glTranslated(vPosition->x, vPosition->y, vPosition->z);
|
|
if (vAngle->y != 0.0)
|
|
glRotated(vAngle->y, 0.0, 1.0, 0.0);
|
|
if (vAngle->x != 0.0)
|
|
glRotated(vAngle->x, 1.0, 0.0, 0.0);
|
|
if (vAngle->z != 0.0)
|
|
glRotated(vAngle->z, 0.0, 0.0, 1.0);
|
|
TSubModel::fSquareDist =
|
|
SquareMagnitude(*vPosition - Global::GetCameraPosition()); // zmienna globalna!
|
|
// odwrócenie flag, aby wy³apaæ nieprzezroczyste
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha ^ 0x0F0F000F);
|
|
Root->RenderDL();
|
|
glPopMatrix();
|
|
};
|
|
void __fastcall TModel3d::RenderAlpha(vector3 *vPosition, vector3 *vAngle, GLuint *ReplacableSkinId,
|
|
int iAlpha)
|
|
{ // przezroczyste, Display List
|
|
glPushMatrix();
|
|
glTranslated(vPosition->x, vPosition->y, vPosition->z);
|
|
if (vAngle->y != 0.0)
|
|
glRotated(vAngle->y, 0.0, 1.0, 0.0);
|
|
if (vAngle->x != 0.0)
|
|
glRotated(vAngle->x, 1.0, 0.0, 0.0);
|
|
if (vAngle->z != 0.0)
|
|
glRotated(vAngle->z, 0.0, 0.0, 1.0);
|
|
TSubModel::fSquareDist =
|
|
SquareMagnitude(*vPosition - Global::GetCameraPosition()); // zmienna globalna!
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha);
|
|
Root->RenderAlphaDL();
|
|
glPopMatrix();
|
|
};
|
|
void __fastcall TModel3d::RaRender(vector3 *vPosition, vector3 *vAngle, GLuint *ReplacableSkinId,
|
|
int iAlpha)
|
|
{ // nieprzezroczyste, VBO
|
|
glPushMatrix();
|
|
glTranslated(vPosition->x, vPosition->y, vPosition->z);
|
|
if (vAngle->y != 0.0)
|
|
glRotated(vAngle->y, 0.0, 1.0, 0.0);
|
|
if (vAngle->x != 0.0)
|
|
glRotated(vAngle->x, 1.0, 0.0, 0.0);
|
|
if (vAngle->z != 0.0)
|
|
glRotated(vAngle->z, 0.0, 0.0, 1.0);
|
|
TSubModel::fSquareDist =
|
|
SquareMagnitude(*vPosition - Global::GetCameraPosition()); // zmienna globalna!
|
|
if (StartVBO())
|
|
{ // odwrócenie flag, aby wy³apaæ nieprzezroczyste
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha ^ 0x0F0F000F);
|
|
Root->RenderVBO();
|
|
EndVBO();
|
|
}
|
|
glPopMatrix();
|
|
};
|
|
void __fastcall TModel3d::RaRenderAlpha(vector3 *vPosition, vector3 *vAngle,
|
|
GLuint *ReplacableSkinId, int iAlpha)
|
|
{ // przezroczyste, VBO
|
|
glPushMatrix();
|
|
glTranslated(vPosition->x, vPosition->y, vPosition->z);
|
|
if (vAngle->y != 0.0)
|
|
glRotated(vAngle->y, 0.0, 1.0, 0.0);
|
|
if (vAngle->x != 0.0)
|
|
glRotated(vAngle->x, 1.0, 0.0, 0.0);
|
|
if (vAngle->z != 0.0)
|
|
glRotated(vAngle->z, 0.0, 0.0, 1.0);
|
|
TSubModel::fSquareDist =
|
|
SquareMagnitude(*vPosition - Global::GetCameraPosition()); // zmienna globalna!
|
|
if (StartVBO())
|
|
{
|
|
Root->ReplacableSet(ReplacableSkinId, iAlpha);
|
|
Root->RenderAlphaVBO();
|
|
EndVBO();
|
|
}
|
|
glPopMatrix();
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 2012-02 funkcje do tworzenia terenu z E3D
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int __fastcall TModel3d::TerrainCount()
|
|
{ // zliczanie kwadratów kilometrowych (g³ówna linia po Next) do tworznia tablicy
|
|
int i = 0;
|
|
TSubModel *r = Root;
|
|
while (r)
|
|
{
|
|
r = r->NextGet();
|
|
++i;
|
|
}
|
|
return i;
|
|
};
|
|
TSubModel *__fastcall TModel3d::TerrainSquare(int n)
|
|
{ // pobieranie wskaŸnika do submodelu (n)
|
|
int i = 0;
|
|
TSubModel *r = Root;
|
|
while (i < n)
|
|
{
|
|
r = r->NextGet();
|
|
++i;
|
|
}
|
|
r->UnFlagNext(); // blokowanie wyœwietlania po Next g³ównej listy
|
|
return r;
|
|
};
|
|
void __fastcall TModel3d::TerrainRenderVBO(int n)
|
|
{ // renderowanie terenu z VBO
|
|
glPushMatrix();
|
|
// glTranslated(vPosition->x,vPosition->y,vPosition->z);
|
|
// if (vAngle->y!=0.0) glRotated(vAngle->y,0.0,1.0,0.0);
|
|
// if (vAngle->x!=0.0) glRotated(vAngle->x,1.0,0.0,0.0);
|
|
// if (vAngle->z!=0.0) glRotated(vAngle->z,0.0,0.0,1.0);
|
|
// TSubModel::fSquareDist=SquareMagnitude(*vPosition-Global::GetCameraPosition()); //zmienna
|
|
// globalna!
|
|
if (StartVBO())
|
|
{ // odwrócenie flag, aby wy³apaæ nieprzezroczyste
|
|
// Root->ReplacableSet(ReplacableSkinId,iAlpha^0x0F0F000F);
|
|
TSubModel *r = Root;
|
|
while (r)
|
|
{
|
|
if (r->iVisible == n) // tylko jeœli ma byæ widoczny w danej ramce (problem dla
|
|
// 0==false)
|
|
r->RenderVBO(); // sub kolejne (Next) siê nie wyrenderuj¹
|
|
r = r->NextGet();
|
|
}
|
|
EndVBO();
|
|
}
|
|
glPopMatrix();
|
|
};
|