Files
maszyna/Model3d.cpp
2017-02-25 21:02:27 +01:00

2423 lines
96 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
/*
MaSzyna EU07 locomotive simulator
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
*/
#include "stdafx.h"
#include "Model3d.h"
#include "Globals.h"
#include "logs.h"
#include "mczapkie/mctools.h"
#include "Usefull.h"
#include "renderer.h"
#include "Timer.h"
#include "mtable.h"
//---------------------------------------------------------------------------
using namespace Mtable;
double TSubModel::fSquareDist = 0;
int TSubModel::iInstance; // numer renderowanego egzemplarza obiektu
texture_manager::size_type *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
std::string *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;
};
TSubModel::TSubModel()
{
ZeroMemory(this, sizeof(TSubModel)); // istotne przy zapisywaniu wersji binarnej
FirstInit();
};
void 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
};
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 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 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 TSubModel::SeekFaceNormal(DWORD *Masks, int f,DWORD dwMask,vector3
// *pt,GLVERTEX
// *Vertices)
int TSubModel::SeekFaceNormal(unsigned int *Masks, int f, unsigned int 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<int>(false);
return (static_cast<double>(value) / base);
};
template <typename ColorT> inline void readColor(cParser &parser, ColorT *color)
{
double discard;
parser.getTokens(4, false);
parser >> discard >> color[0] >> color[1] >> color[2];
color[ 0 ] /= 255.0;
color[ 1 ] /= 255.0;
color[ 2 ] /= 255.0;
};
inline void readColor(cParser &parser, int &color)
{
int r, g, b, discard;
parser.getTokens(4, false);
parser >> discard >> r >> g >> 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
parser.getTokens(16, false);
for (int x = 0; x <= 3; ++x) // wiersze
for (int y = 0; y <= 3; ++y) // kolumny
parser >> matrix(x)[y];
};
int 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<std::string>();
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.size() >= 3)
&& (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.compare(0, 8, "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<std::string>();
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<std::string>();
if (light == "true")
fLight = 2.0; // zawsze świeci
else if (light == "false")
fLight = -1.0; // zawsze ciemy
else
fLight = std::stod(light);
};
if (eType == TP_FREESPOTLIGHT)
{
if (!parser.expectToken("nearattenstart:"))
{
Error("Model light parse failure!");
}
std::string discard;
parser.getTokens(13, false);
parser >> fNearAttenStart >> discard >> fNearAttenEnd >> discard >> bUseNearAtten >>
discard >> iFarAttenDecay >> discard >> fFarDecayRadius >> discard >>
fCosFalloffAngle // kąt liczony dla średnicy, a nie promienia
>> discard >> fCosHotspotAngle; // kąt liczony dla średnicy, a nie promienia
fCosFalloffAngle = cos(DegToRad(0.5 * fCosFalloffAngle));
fCosHotspotAngle = cos(DegToRad(0.5 * fCosHotspotAngle));
iNumVerts = 1;
iFlags |= 0x4010; // rysowane w cyklu nieprzezroczystych, macierz musi
// zostać bez zmiany
}
else if (eType < TP_ROTATOR)
{
std::string discard;
parser.getTokens(5, false);
parser >> discard >> bWire >> discard >> fWireSize >> discard;
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<std::string>();
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 = GfxRenderer.GetTextureId( texture, szTexturePath );
// 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 |= GfxRenderer.Texture(TextureID).has_alpha ?
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;
std::string discard;
parser.getTokens(5, false);
parser >> discard >> fSquareMaxDist >> discard >> fSquareMinDist >> discard;
if (fSquareMaxDist >= 0.0)
{
fSquareMaxDist *= fSquareMaxDist;
}
else
{
fSquareMaxDist = 15000 * 15000;
} // 15km to więcej, niż się obecnie wyświetla
fSquareMinDist *= fSquareMinDist;
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
unsigned int *sg; // maski przynależności trójkątów do powierzchni
if (eType < TP_ROTATOR)
{ // wczytywanie wierzchołków
parser.getTokens(2, false);
parser >> discard >> token;
// 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);
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=" + std::to_string(iNumVerts) + ")%3<>0");
return 0;
}
// Vertices=new GLVERTEX[iNumVerts];
if (iNumVerts)
{
Vertices = new float8[iNumVerts];
iNumFaces = iNumVerts / 3;
sg = new unsigned int[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
maska = parser.getToken<int>(false); // 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.getTokens(3, false);
parser >> Vertices[i].Point.x >> Vertices[i].Point.y >> Vertices[i].Point.z;
if (maska == -1)
{ // jeśli wektory normalne podane jawnie
parser.getTokens(3, false);
parser >> Vertices[i].Normal.x >> Vertices[i].Normal.y >>
Vertices[i].Normal.z;
wsp[i] = i; // wektory normalne "są już policzone"
}
parser.getTokens(2, false);
parser >> Vertices[i].tu >> 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(std::string("Degenerated triangle ignored in: \"") + pName +
"\", verticle " + std::to_string(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(std::string("Too large triangle ignored in: \"") + 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
std::string discard;
parser.getTokens(2, false);
parser >> discard >> 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.getTokens(5, false);
parser >> Vertices[i].Point.x >> Vertices[i].Point.y >> Vertices[i].Point.z >>
j // zakodowany kolor
>> discard;
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 TSubModel::TriangleAdd(TModel3d *m, texture_manager::size_type 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(GfxRenderer.Texture(tex).name.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 *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 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);
GfxRenderer.Bind(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.0f, 0.0f, -0.025f ); // shift point towards the viewer, to avoid z-fighting with the light polygons
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);
GfxRenderer.Bind(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 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ą
if( eType != TP_STARS ) {
// gwiazdki mają kolory zamiast normalnych, to // ich wtedy nie ruszamy
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 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 TSubModel::NextAdd(TSubModel *SubModel)
{ // dodanie submodelu kolejnego (wspólny przodek)
if (Next)
Next->NextAdd(SubModel);
else
Next = SubModel;
};
int 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 = 0;
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 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 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 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 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 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 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 *TSubModel::GetFromName(std::string const &search, bool i)
{
return GetFromName(search.c_str(), i);
};
TSubModel *TSubModel::GetFromName(char const *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 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]) >= '0')
if ((sm->pName[0]) <= '5') // zegarek ma 6 cyfr maksymalnie
sm->SetRotate(float3(0, 1, 0),
-Global::fClockAngleDeg[(sm->pName[0]) - '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 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
GfxRenderer.Bind(ReplacableSkinId[-TextureID]);
// TexAlpha=!(iAlpha&1); //zmiana tylko w przypadku wymienej tekstury
}
else
GfxRenderer.Bind(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 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
GfxRenderer.Bind(ReplacableSkinId[-TextureID]);
// TexAlpha=iAlpha&1; //zmiana tylko w przypadku wymienej tekstury
}
else
GfxRenderer.Bind(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->size();
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 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
GfxRenderer.Bind(ReplacableSkinId[-TextureID]);
// TexAlpha=!(iAlpha&1); //zmiana tylko w przypadku wymienej tekstury
}
else
GfxRenderer.Bind(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;
*/
GfxRenderer.Bind(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
GfxRenderer.Bind(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 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
GfxRenderer.Bind(ReplacableSkinId[-TextureID]);
// TexAlpha=iAlpha&1; //zmiana tylko w przypadku wymienej tekstury
}
else
GfxRenderer.Bind(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 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 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)
std::string t(pTexture);
// trim extension
size_t kropka = t.rfind('.');
if (kropka != std::string::npos &&
(t.substr(kropka) == ".tga" || t.substr(kropka) == ".dds"))
{
t.erase(t.rfind('.'));
}
if (t != std::string(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.size() + 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 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 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);
std::string name(pName);
if (false == name.empty())
{ // jeśli dany submodel jest zgaszonym światłem, to
// domyślnie go ukrywamy
if ((name.size() >= 8) && (name.substr(0, 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 ((name.size() >= 3) && (name.substr(name.size() - 3, 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);
std::string tex = pTexture;
if (tex.find_last_of("/\\") == std::string::npos)
tex.insert(0, Global::asCurrentTexturePath);
TextureID = GfxRenderer.GetTextureId( tex, szTexturePath );
// 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 |= GfxRenderer.Texture(TextureID).has_alpha ?
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 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 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 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 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;
};
//---------------------------------------------------------------------------
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
};
/*
TModel3d::TModel3d(char *FileName)
{
// Root=NULL;
// Materials=NULL;
// MaterialsCount=0;
Root=NULL;
SubModelsCount=0;
iFlags=0;
LoadFromFile(FileName);
};
*/
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 *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 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 *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* 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 TModel3d::LoadFromFile(std::string const &FileName, bool dynamic)
{
// wczytanie modelu z pliku
std::string name = ToLower(FileName);
// trim extension if needed
if( ( name.rfind( '.' ) != std::string::npos )
&& ( name.substr( name.rfind( '.' ) ) == ".t3d" ) )
{
name.erase(name.rfind('.'));
}
asBinary = name + ".e3d";
if (FileExists(asBinary))
{
LoadFromBinFile(asBinary, 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
}
}
bool const result =
Root ? (iSubModelsCount > 0) : false; // brak pliku albo problem z wczytaniem
if (false == result)
{
ErrorLog("Failed to load 3d model \"" + FileName + "\"");
}
return result;
};
void TModel3d::LoadFromBinFile(std::string const &FileName, bool dynamic)
{ // wczytanie modelu z pliku binarnego
WriteLog("Loading - binary model: " + 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;
*/ {
std::ifstream file(FileName, std::ios::binary | std::ios::ate);
file.unsetf(std::ios::skipws);
size = file.tellg(); // ios::ate already positioned us at the end of the file
iModel = new int[size >> 2]; // ten wskaźnik musi być w modelu, aby zwolnić pamięć
file.seekg(0, std::ios::beg); // rewind the caret afterwards
file.read(reinterpret_cast<char *>(iModel), size);
}
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 == MAKE_ID4('E', '3', 'D', '0')) // 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 MAKE_ID4('M', 'D', 'L',
'0'): // zmienne modelu: 'E3D0',len,(informacje o modelu)
break;
case MAKE_ID4('V', 'N', 'T',
'0'): // wierzchołki: 'VNT0',len,(32 bajty na wierzchołek)
iNumVerts = (k - 2) >> 3;
m_nVertexCount = iNumVerts;
m_pVNT = (CVertNormTex *)(iModel + i + 2);
break;
case MAKE_ID4('S', 'U', 'B', '0'): // 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 MAKE_ID4('S', 'U', 'B', '1'): // 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 MAKE_ID4('T', 'R', 'A', '0'): // transformy: 'TRA0',len,(64 bajty na transform)
m = (float4x4 *)(iModel + i + 2); // tabela transformów
break;
case MAKE_ID4('T', 'R', 'A',
'1'): // 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 MAKE_ID4('I', 'D', 'X',
'1'): // indeksy 1B: 'IDX2',len,(po bajcie na numer wierzchołka)
break;
case MAKE_ID4('I', 'D', 'X',
'2'): // indeksy 2B: 'IDX2',len,(po 2 bajty na numer wierzchołka)
break;
case MAKE_ID4('I', 'D', 'X',
'4'): // indeksy 4B: 'IDX4',len,(po 4 bajty na numer wierzchołka)
break;
case MAKE_ID4('T', 'E', 'X',
'0'): // tekstury: 'TEX0',len,(łańcuchy zakończone zerem - pliki
// tekstur)
Textures.Init((char *)(iModel + i)); //łącznie z nagłówkiem
break;
case MAKE_ID4('T', 'I', 'X', '0'): // indeks nazw tekstur
Textures.InitIndex((int *)(iModel + i)); //łącznie z nagłówkiem
break;
case MAKE_ID4('N', 'A', 'M',
'0'): // nazwy: 'NAM0',len,(łańcuchy zakończone zerem - nazwy
// submodeli)
Names.Init((char *)(iModel + i)); //łącznie z nagłówkiem
break;
case MAKE_ID4('N', 'I', 'X', '0'): // 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 TModel3d::LoadFromTextFile(std::string const &FileName, bool dynamic)
{ // wczytanie submodelu z pliku tekstowego
WriteLog("Loading - text model: " + 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<std::string>();
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.getTokens();
parser >> 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 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 (false == asBinary.empty()) // 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 TModel3d::SaveToBinFile(char const *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);
*/ {
std::ofstream file(FileName, std::ios::binary);
file.unsetf(std::ios::skipws);
file.write("E3D0", 4); // kromka główna
file.write(reinterpret_cast<char *>(&len), 4);
file.write("SUB0", 4); // dane submodeli
file.write(reinterpret_cast<char *>(&sub), 4);
for (i = 0; i < iSubModelsCount; ++i)
{
roboczy->InfoSet(info + i);
file.write(reinterpret_cast<char *>(roboczy),
sizeof(TSubModel)); // zapis jednego submodelu
}
if (tra)
{ // zapis transformów
file.write("TRA0", 4); // transformy
file.write(reinterpret_cast<char *>(&tra), 4);
for (i = 0; i < iSubModelsCount; ++i)
if (info[i].iTransform >= 0)
file.write(reinterpret_cast<char *>(info[i].pSubModel->GetMatrix()), 16 * 4);
}
{ // zapis wierzchołków
MakeArray(iNumVerts); // tworzenie tablic dla VBO
Root->RaArrayFill(m_pVNT); // wypełnianie tablicy
file.write("VNT0", 4); // wierzchołki
file.write(reinterpret_cast<char *>(&vnt), 4);
file.write(reinterpret_cast<char *>(m_pVNT), 32 * iNumVerts);
}
if (tex) // może być jeden submodel ze zmienną teksturą i nazwy nie będzie
{ // zapis nazw tekstur
file.write("TEX0", 4); // nazwy tekstur
i = (tex + 3) & ~3; // zaokrąglenie w górę
file.write(reinterpret_cast<char *>(&i), 4);
file.write(reinterpret_cast<char *>(&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)
file.write(info[i].pSubModel->pTexture, info[i].iTextureLen);
if ((-tex) & 3)
file.write(reinterpret_cast<char *>(&zero),
((-tex) & 3)); // wyrównanie do wielokrotności 4 bajtów
}
if (nam) // może być jeden anonimowy submodel w modelu
{ // zapis nazw submodeli
file.write("NAM0", 4); // nazwy submodeli
i = (nam + 3) & ~3; // zaokrąglenie w górę
file.write(reinterpret_cast<char *>(&i), 4);
for (i = 0; i < iSubModelsCount; ++i)
if (info[i].iNameLen)
file.write(info[i].pSubModel->pName, info[i].iNameLen);
if ((-nam) & 3)
file.write(reinterpret_cast<char *>(&zero),
((-nam) & 3)); // wyrównanie do wielokrotności 4 bajtów
}
} // file autocloses on getting out of scope
// 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 TModel3d::BreakHierarhy()
{
Error("Not implemented yet :(");
};
/*
void 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 TModel3d::Render(double fSquareDistance, texture_manager::size_type *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 TModel3d::RenderAlpha(double fSquareDistance, texture_manager::size_type *ReplacableSkinId, int iAlpha)
{
if (iAlpha & iFlags & 0x2F2F002F)
{
TSubModel::fSquareDist = fSquareDistance; // zmienna globalna!
Root->ReplacableSet(ReplacableSkinId, iAlpha);
Root->RenderAlphaDL();
}
};
/*
void 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 TModel3d::RaRender( double fSquareDistance, texture_manager::size_type *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 TModel3d::RaRenderAlpha(double fSquareDistance, texture_manager::size_type *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 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 TModel3d::Render(vector3 *vPosition, vector3 *vAngle, texture_manager::size_type *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 TModel3d::RenderAlpha(vector3 *vPosition, vector3 *vAngle, texture_manager::size_type *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 TModel3d::RaRender( vector3 *vPosition, vector3 *vAngle, texture_manager::size_type *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 TModel3d::RaRenderAlpha(vector3 *vPosition, vector3 *vAngle, texture_manager::size_type *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 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 *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 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();
};