Files
maszyna/Ground.cpp

5225 lines
235 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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 and others
*/
#include "system.hpp"
#include "classes.hpp"
#include "opengl/glew.h"
#include "opengl/glut.h"
#pragma hdrstop
#include "Timer.h"
#include "Texture.h"
#include "Ground.h"
#include "Globals.h"
#include "Event.h"
#include "EvLaunch.h"
#include "TractionPower.h"
#include "Traction.h"
#include "Track.h"
#include "RealSound.h"
#include "AnimModel.h"
#include "MemCell.h"
#include "mtable.hpp"
#include "DynObj.h"
#include "Data.h"
#include "parser.h" //Tolaris-010603
#include "Driver.h"
#include "Console.h"
#include "Names.h"
#define _PROBLEND 1
//---------------------------------------------------------------------------
#pragma package(smart_init)
bool bCondition; // McZapkie: do testowania warunku na event multiple
AnsiString LogComment;
//---------------------------------------------------------------------------
// Obiekt renderuj¹cy siatkê jest sztucznie tworzonym obiektem pomocniczym,
// grupuj¹cym siatki obiektów dla danej tekstury. Obiektami sk³adowymi mog¹
// byc trójk¹ty terenu, szyny, podsypki, a tak¿e proste modele np. s³upy.
// Obiekty sk³adowe dodane s¹ do listy TSubRect::nMeshed z list¹ zrobion¹ na
// TGroundNode::nNext3, gdzie s¹ posortowane wg tekstury. Obiekty renderuj¹ce
// s¹ wpisane na listê TSubRect::nRootMesh (TGroundNode::nNext2) oraz na
// odpowiednie listy renderowania, gdzie zastêpuj¹ obiekty sk³adowe (nNext3).
// Problematyczne s¹ tory/drogi/rzeki, gdzie u¿ywane sa 2 tekstury. Dlatego
// tory s¹ zdublowane jako TP_TRACK oraz TP_DUMMYTRACK. Jeœli tekstura jest
// tylko jedna (np. zwrotnice), nie jest u¿ywany TP_DUMMYTRACK.
//---------------------------------------------------------------------------
TGroundNode::TGroundNode()
{ // nowy obiekt terenu - pusty
iType = GL_POINTS;
Vertices = NULL;
nNext = nNext2 = NULL;
pCenter = vector3(0, 0, 0);
iCount = 0; // wierzcho³ków w trójk¹cie
// iNumPts=0; //punktów w linii
TextureID = 0;
iFlags = 0; // tryb przezroczystoœci nie zbadany
DisplayListID = 0;
Pointer = NULL; // zerowanie wskaŸnika kontekstowego
bVisible = false; // czy widoczny
fSquareRadius = 10000 * 10000;
fSquareMinRadius = 0;
asName = "";
// Color= TMaterialColor(1);
// fAngle=0; //obrót dla modelu
// fLineThickness=1.0; //mm dla linii
for (int i = 0; i < 3; i++)
{
Ambient[i] = Global::whiteLight[i] * 255;
Diffuse[i] = Global::whiteLight[i] * 255;
Specular[i] = Global::noLight[i] * 255;
}
nNext3 = NULL; // nie wyœwietla innych
iVboPtr = -1; // indeks w VBO sektora (-1: nie u¿ywa VBO)
iVersion = 0; // wersja siatki
}
TGroundNode::~TGroundNode()
{
// if (iFlags&0x200) //czy obiekt zosta³ utworzony?
switch (iType)
{
case TP_MEMCELL:
SafeDelete(MemCell);
break;
case TP_EVLAUNCH:
SafeDelete(EvLaunch);
break;
case TP_TRACTION:
SafeDelete(hvTraction);
break;
case TP_TRACTIONPOWERSOURCE:
SafeDelete(psTractionPowerSource);
break;
case TP_TRACK:
SafeDelete(pTrack);
break;
case TP_DYNAMIC:
SafeDelete(DynamicObject);
break;
case TP_MODEL:
if (iFlags & 0x200) // czy model zosta³ utworzony?
delete Model;
Model = NULL;
break;
case TP_TERRAIN:
{ // pierwsze nNode zawiera model E3D, reszta to trójk¹ty
for (int i = 1; i < iCount; ++i)
nNode->Vertices =
NULL; // zerowanie wskaŸników w kolejnych elementach, bo nie s¹ do usuwania
delete[] nNode; // usuniêcie tablicy i pierwszego elementu
}
case TP_SUBMODEL: // dla formalnoœci, nie wymaga usuwania
break;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
SafeDeleteArray(Points);
break;
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_TRIANGLES:
SafeDeleteArray(Vertices);
break;
}
}
void TGroundNode::Init(int n)
{ // utworzenie tablicy wierzcho³ków
bVisible = false;
iNumVerts = n;
Vertices = new TGroundVertex[iNumVerts];
}
TGroundNode::TGroundNode(TGroundNodeType t, int n)
{ // utworzenie obiektu
TGroundNode(); // domyœlne ustawienia
iNumVerts = n;
if (iNumVerts)
Vertices = new TGroundVertex[iNumVerts];
iType = t;
switch (iType)
{ // zale¿nie od typu
case TP_TRACK:
pTrack = new TTrack(this);
break;
}
}
void TGroundNode::InitCenter()
{ // obliczenie œrodka ciê¿koœci obiektu
for (int i = 0; i < iNumVerts; i++)
pCenter += Vertices[i].Point;
pCenter /= iNumVerts;
}
void TGroundNode::InitNormals()
{ // obliczenie wektorów normalnych
vector3 v1, v2, v3, v4, v5, n1, n2, n3, n4;
int i;
float tu, tv;
switch (iType)
{
case GL_TRIANGLE_STRIP:
v1 = Vertices[0].Point - Vertices[1].Point;
v2 = Vertices[1].Point - Vertices[2].Point;
n1 = SafeNormalize(CrossProduct(v1, v2));
if (Vertices[0].Normal == vector3(0, 0, 0))
Vertices[0].Normal = n1;
v3 = Vertices[2].Point - Vertices[3].Point;
n2 = SafeNormalize(CrossProduct(v3, v2));
if (Vertices[1].Normal == vector3(0, 0, 0))
Vertices[1].Normal = (n1 + n2) * 0.5;
for (i = 2; i < iNumVerts - 2; i += 2)
{
v4 = Vertices[i - 1].Point - Vertices[i].Point;
v5 = Vertices[i].Point - Vertices[i + 1].Point;
n3 = SafeNormalize(CrossProduct(v3, v4));
n4 = SafeNormalize(CrossProduct(v5, v4));
if (Vertices[i].Normal == vector3(0, 0, 0))
Vertices[i].Normal = (n1 + n2 + n3) / 3;
if (Vertices[i + 1].Normal == vector3(0, 0, 0))
Vertices[i + 1].Normal = (n2 + n3 + n4) / 3;
n1 = n3;
n2 = n4;
v3 = v5;
}
if (Vertices[i].Normal == vector3(0, 0, 0))
Vertices[i].Normal = (n1 + n2) / 2;
if (Vertices[i + 1].Normal == vector3(0, 0, 0))
Vertices[i + 1].Normal = n2;
break;
case GL_TRIANGLE_FAN:
break;
case GL_TRIANGLES:
for (i = 0; i < iNumVerts; i += 3)
{
v1 = Vertices[i + 0].Point - Vertices[i + 1].Point;
v2 = Vertices[i + 1].Point - Vertices[i + 2].Point;
n1 = SafeNormalize(CrossProduct(v1, v2));
if (Vertices[i + 0].Normal == vector3(0, 0, 0))
Vertices[i + 0].Normal = (n1);
if (Vertices[i + 1].Normal == vector3(0, 0, 0))
Vertices[i + 1].Normal = (n1);
if (Vertices[i + 2].Normal == vector3(0, 0, 0))
Vertices[i + 2].Normal = (n1);
tu = floor(Vertices[i + 0].tu);
tv = floor(Vertices[i + 0].tv);
Vertices[i + 1].tv -= tv;
Vertices[i + 2].tv -= tv;
Vertices[i + 0].tv -= tv;
Vertices[i + 1].tu -= tu;
Vertices[i + 2].tu -= tu;
Vertices[i + 0].tu -= tu;
}
break;
}
}
void TGroundNode::MoveMe(vector3 pPosition)
{ // przesuwanie obiektów scenerii o wektor w celu redukcji trzêsienia
pCenter += pPosition;
switch (iType)
{
case TP_TRACTION:
hvTraction->pPoint1 += pPosition;
hvTraction->pPoint2 += pPosition;
hvTraction->pPoint3 += pPosition;
hvTraction->pPoint4 += pPosition;
hvTraction->Optimize();
break;
case TP_MODEL:
case TP_DYNAMIC:
case TP_MEMCELL:
case TP_EVLAUNCH:
break;
case TP_TRACK:
pTrack->MoveMe(pPosition);
break;
case TP_SOUND: // McZapkie - dzwiek zapetlony w zaleznosci od odleglosci
tsStaticSound->vSoundPosition += pPosition;
break;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
for (int i = 0; i < iNumPts; i++)
Points[i] += pPosition;
ResourceManager::Unregister(this);
break;
default:
for (int i = 0; i < iNumVerts; i++)
Vertices[i].Point += pPosition;
ResourceManager::Unregister(this);
}
}
void TGroundNode::RaRenderVBO()
{ // renderowanie z domyslnego bufora VBO
glColor3ub(Diffuse[0], Diffuse[1], Diffuse[2]);
if (TextureID)
glBindTexture(GL_TEXTURE_2D, TextureID); // Ustaw aktywn¹ teksturê
glDrawArrays(iType, iVboPtr, iNumVerts); // Narysuj naraz wszystkie trójk¹ty
}
void TGroundNode::RenderVBO()
{ // renderowanie obiektu z VBO - faza nieprzezroczystych
double mgn = SquareMagnitude(pCenter - Global::pCameraPosition);
if ((mgn > fSquareRadius || (mgn < fSquareMinRadius)) &&
(iType != TP_EVLAUNCH)) // McZapkie-070602: nie rysuj odleglych obiektow ale sprawdzaj
// wyzwalacz zdarzen
return;
int i, a;
switch (iType)
{
case TP_TRACTION:
return;
case TP_TRACK:
if (iNumVerts)
pTrack->RaRenderVBO(iVboPtr);
return;
case TP_MODEL:
Model->RenderVBO(&pCenter);
return;
// case TP_SOUND: //McZapkie - dzwiek zapetlony w zaleznosci od odleglosci
// if ((pStaticSound->GetStatus()&DSBSTATUS_PLAYING)==DSBPLAY_LOOPING)
// {
// pStaticSound->Play(1,DSBPLAY_LOOPING,true,pStaticSound->vSoundPosition);
// pStaticSound->AdjFreq(1.0,Timer::GetDeltaTime());
// }
// return; //Ra: TODO sprawdziæ, czy dŸwiêki nie s¹ tylko w RenderHidden
case TP_MEMCELL:
return;
case TP_EVLAUNCH:
if (EvLaunch->Render())
if ((EvLaunch->dRadius < 0) || (mgn < EvLaunch->dRadius))
{
if (Console::Pressed(VK_SHIFT) && EvLaunch->Event2 != NULL)
Global::AddToQuery(EvLaunch->Event2, NULL);
else if (EvLaunch->Event1 != NULL)
Global::AddToQuery(EvLaunch->Event1, NULL);
}
return;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
if (iNumPts)
{
float linealpha = 255000 * fLineThickness / (mgn + 1.0);
if (linealpha > 255)
linealpha = 255;
float r, g, b;
r = floor(Diffuse[0] * Global::ambientDayLight[0]); // w zaleznosci od koloru swiatla
g = floor(Diffuse[1] * Global::ambientDayLight[1]);
b = floor(Diffuse[2] * Global::ambientDayLight[2]);
glColor4ub(r, g, b, linealpha); // przezroczystosc dalekiej linii
// glDisable(GL_LIGHTING); //nie powinny œwieciæ
glDrawArrays(iType, iVboPtr, iNumPts); // rysowanie linii
// glEnable(GL_LIGHTING);
}
return;
default:
if (iVboPtr >= 0)
RaRenderVBO();
};
return;
};
void TGroundNode::RenderAlphaVBO()
{ // renderowanie obiektu z VBO - faza przezroczystych
double mgn = SquareMagnitude(pCenter - Global::pCameraPosition);
float r, g, b;
if (mgn < fSquareMinRadius)
return;
if (mgn > fSquareRadius)
return;
int i, a;
#ifdef _PROBLEND
if ((PROBLEND)) // sprawdza, czy w nazwie nie ma @ //Q: 13122011 - Szociu: 27012012
{
glDisable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.45); // im mniejsza wartoœæ, tym wiêksza ramka, domyœlnie 0.1f
};
#endif
switch (iType)
{
case TP_TRACTION:
if (bVisible)
{
#ifdef _PROBLEND
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
hvTraction->RenderVBO(mgn, iVboPtr);
}
return;
case TP_MODEL:
#ifdef _PROBLEND
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
Model->RenderAlphaVBO(&pCenter);
return;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
if (iNumPts)
{
float linealpha = 255000 * fLineThickness / (mgn + 1.0);
if (linealpha > 255)
linealpha = 255;
r = Diffuse[0] * Global::ambientDayLight[0]; // w zaleznosci od koloru swiatla
g = Diffuse[1] * Global::ambientDayLight[1];
b = Diffuse[2] * Global::ambientDayLight[2];
glColor4ub(r, g, b, linealpha); // przezroczystosc dalekiej linii
// glDisable(GL_LIGHTING); //nie powinny œwieciæ
glDrawArrays(iType, iVboPtr, iNumPts); // rysowanie linii
// glEnable(GL_LIGHTING);
#ifdef _PROBLEND
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
}
#ifdef _PROBLEND
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
return;
default:
if (iVboPtr >= 0)
{
RaRenderVBO();
#ifdef _PROBLEND
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
return;
}
}
#ifdef _PROBLEND
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif
return;
}
void TGroundNode::Compile(bool many)
{ // tworzenie skompilowanej listy w wyœwietlaniu DL
if (!many)
{ // obs³uga pojedynczej listy
if (DisplayListID)
Release();
if (Global::bManageNodes)
{
DisplayListID = glGenLists(1);
glNewList(DisplayListID, GL_COMPILE);
iVersion = Global::iReCompile; // aktualna wersja siatek (do WireFrame)
}
}
if ((iType == GL_LINES) || (iType == GL_LINE_STRIP) || (iType == GL_LINE_LOOP))
{
#ifdef USE_VERTEX_ARRAYS
glVertexPointer(3, GL_DOUBLE, sizeof(vector3), &Points[0].x);
#endif
glBindTexture(GL_TEXTURE_2D, 0);
#ifdef USE_VERTEX_ARRAYS
glDrawArrays(iType, 0, iNumPts);
#else
glBegin(iType);
for (int i = 0; i < iNumPts; i++)
glVertex3dv(&Points[i].x);
glEnd();
#endif
}
else if (iType == GL_TRIANGLE_STRIP || iType == GL_TRIANGLE_FAN || iType == GL_TRIANGLES)
{ // jak nie linie, to trójk¹ty
TGroundNode *tri = this;
do
{ // pêtla po obiektach w grupie w celu po³¹czenia siatek
#ifdef USE_VERTEX_ARRAYS
glVertexPointer(3, GL_DOUBLE, sizeof(TGroundVertex), &tri->Vertices[0].Point.x);
glNormalPointer(GL_DOUBLE, sizeof(TGroundVertex), &tri->Vertices[0].Normal.x);
glTexCoordPointer(2, GL_FLOAT, sizeof(TGroundVertex), &tri->Vertices[0].tu);
#endif
glColor3ub(tri->Diffuse[0], tri->Diffuse[1], tri->Diffuse[2]);
glBindTexture(GL_TEXTURE_2D, Global::bWireFrame ? 0 : tri->TextureID);
#ifdef USE_VERTEX_ARRAYS
glDrawArrays(Global::bWireFrame ? GL_LINE_LOOP : tri->iType, 0, tri->iNumVerts);
#else
glBegin(Global::bWireFrame ? GL_LINE_LOOP : tri->iType);
for (int i = 0; i < tri->iNumVerts; i++)
{
glNormal3d(tri->Vertices[i].Normal.x, tri->Vertices[i].Normal.y,
tri->Vertices[i].Normal.z);
glTexCoord2f(tri->Vertices[i].tu, tri->Vertices[i].tv);
glVertex3dv(&tri->Vertices[i].Point.x);
};
glEnd();
#endif
/*
if (tri->pTriGroup) //jeœli z grupy
{tri=tri->pNext2; //nastêpny w sektorze
while (tri?!tri->pTriGroup:false) tri=tri->pNext2; //szukamy kolejnego nale¿¹cego do
grupy
}
else
*/
tri = NULL; // a jak nie, to koniec
} while (tri);
}
else if (iType == TP_MESH)
{ // grupa ze wspóln¹ tekstur¹ - wrzucanie do wspólnego Display List
if (TextureID)
glBindTexture(GL_TEXTURE_2D, TextureID); // Ustaw aktywn¹ teksturê
TGroundNode *n = nNode;
while (n ? n->TextureID == TextureID : false)
{ // wszystkie obiekty o tej samej testurze
switch (n->iType)
{ // poszczególne typy ró¿nie siê tworzy
case TP_TRACK:
case TP_DUMMYTRACK:
n->pTrack->Compile(TextureID); // dodanie trójk¹tów dla podanej tekstury
break;
}
n = n->nNext3; // nastêpny z listy
}
}
if (!many)
if (Global::bManageNodes)
glEndList();
};
void TGroundNode::Release()
{
if (DisplayListID)
glDeleteLists(DisplayListID, 1);
DisplayListID = 0;
};
void TGroundNode::RenderHidden()
{ // renderowanie obiektów niewidocznych
double mgn = SquareMagnitude(pCenter - Global::pCameraPosition);
switch (iType)
{
case TP_SOUND: // McZapkie - dzwiek zapetlony w zaleznosci od odleglosci
if ((tsStaticSound->GetStatus() & DSBSTATUS_PLAYING) == DSBPLAY_LOOPING)
{
tsStaticSound->Play(1, DSBPLAY_LOOPING, true, tsStaticSound->vSoundPosition);
tsStaticSound->AdjFreq(1.0, Timer::GetDeltaTime());
}
return;
case TP_EVLAUNCH:
if (EvLaunch->Render())
if ((EvLaunch->dRadius < 0) || (mgn < EvLaunch->dRadius))
{
WriteLog("Eventlauncher " + asName);
if (Console::Pressed(VK_SHIFT) && (EvLaunch->Event2))
Global::AddToQuery(EvLaunch->Event2, NULL);
else if (EvLaunch->Event1)
Global::AddToQuery(EvLaunch->Event1, NULL);
}
return;
}
};
void TGroundNode::RenderDL()
{ // wyœwietlanie obiektu przez Display List
switch (iType)
{ // obiekty renderowane niezale¿nie od odleg³oœci
case TP_SUBMODEL:
TSubModel::fSquareDist = 0;
return smTerrain->RenderDL();
}
// if (pTriGroup) if (pTriGroup!=this) return; //wyœwietla go inny obiekt
double mgn = SquareMagnitude(pCenter - Global::pCameraPosition);
if ((mgn > fSquareRadius) || (mgn < fSquareMinRadius)) // McZapkie-070602: nie rysuj odleglych
// obiektow ale sprawdzaj wyzwalacz
// zdarzen
return;
int i, a;
switch (iType)
{
case TP_TRACK:
return pTrack->Render();
case TP_MODEL:
return Model->RenderDL(&pCenter);
}
// TODO: sprawdzic czy jest potrzebny warunek fLineThickness < 0
// if ((iNumVerts&&(iFlags&0x10))||(iNumPts&&(fLineThickness<0)))
if ((iFlags & 0x10) || (fLineThickness < 0))
{
if (!DisplayListID || (iVersion != Global::iReCompile)) // Ra: wymuszenie rekompilacji
{
Compile();
if (Global::bManageNodes)
ResourceManager::Register(this);
};
if ((iType == GL_LINES) || (iType == GL_LINE_STRIP) || (iType == GL_LINE_LOOP))
// if (iNumPts)
{ // wszelkie linie s¹ rysowane na samym koñcu
float r, g, b;
r = Diffuse[0] * Global::ambientDayLight[0]; // w zaleznosci od koloru swiatla
g = Diffuse[1] * Global::ambientDayLight[1];
b = Diffuse[2] * Global::ambientDayLight[2];
glColor4ub(r, g, b, 1.0);
glCallList(DisplayListID);
// glColor4fv(Diffuse); //przywrócenie koloru
// glColor3ub(Diffuse[0],Diffuse[1],Diffuse[2]);
}
// GL_TRIANGLE etc
else
glCallList(DisplayListID);
SetLastUsage(Timer::GetSimulationTime());
};
};
void TGroundNode::RenderAlphaDL()
{
// SPOSOB NA POZBYCIE SIE RAMKI DOOKOLA TEXTURY ALPHA DLA OBIEKTOW ZAGNIEZDZONYCH W SCN JAKO
// NODE
// W GROUND.H dajemy do klasy TGroundNode zmienna bool PROBLEND to samo robimy w klasie TGround
// nastepnie podczas wczytywania textury dla TRIANGLES w TGround::AddGroundNode
// sprawdzamy czy w nazwie jest @ i wg tego
// ustawiamy PROBLEND na true dla wlasnie wczytywanego trojkata (kazdy trojkat jest osobnym
// nodem)
// nastepnie podczas renderowania w bool TGroundNode::RenderAlpha()
// na poczatku ustawiamy standardowe GL_GREATER = 0.04
// pozniej sprawdzamy czy jest wlaczony PROBLEND dla aktualnie renderowanego noda TRIANGLE,
// wlasciwie dla kazdego node'a
// i jezeli tak to odpowiedni GL_GREATER w przeciwnym wypadku standardowy 0.04
// if (pTriGroup) if (pTriGroup!=this) return; //wyœwietla go inny obiekt
double mgn = SquareMagnitude(pCenter - Global::pCameraPosition);
float r, g, b;
if (mgn < fSquareMinRadius)
return;
if (mgn > fSquareRadius)
return;
int i, a;
switch (iType)
{
case TP_TRACTION:
if (bVisible)
hvTraction->RenderDL(mgn);
return;
case TP_MODEL:
Model->RenderAlphaDL(&pCenter);
return;
case TP_TRACK:
// pTrack->RenderAlpha();
return;
};
// TODO: sprawdzic czy jest potrzebny warunek fLineThickness < 0
if ((iNumVerts && (iFlags & 0x20)) || (iNumPts && (fLineThickness > 0)))
{
#ifdef _PROBLEND
if ((PROBLEND)) // sprawdza, czy w nazwie nie ma @ //Q: 13122011 - Szociu: 27012012
{
glDisable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.45); // im mniejsza wartoœæ, tym wiêksza ramka, domyœlnie 0.1f
};
#endif
if (!DisplayListID) //||Global::bReCompile) //Ra: wymuszenie rekompilacji
{
Compile();
if (Global::bManageNodes)
ResourceManager::Register(this);
};
// GL_LINE, GL_LINE_STRIP, GL_LINE_LOOP
if (iNumPts)
{
float linealpha = 255000 * fLineThickness / (mgn + 1.0);
if (linealpha > 255)
linealpha = 255;
r = Diffuse[0] * Global::ambientDayLight[0]; // w zaleznosci od koloru swiatla
g = Diffuse[1] * Global::ambientDayLight[1];
b = Diffuse[2] * Global::ambientDayLight[2];
glColor4ub(r, g, b, linealpha); // przezroczystosc dalekiej linii
glCallList(DisplayListID);
}
// GL_TRIANGLE etc
else
glCallList(DisplayListID);
SetLastUsage(Timer::GetSimulationTime());
};
#ifdef _PROBLEND
if ((PROBLEND)) // sprawdza, czy w nazwie nie ma @ //Q: 13122011 - Szociu: 27012012
{
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
};
#endif
}
//------------------------------------------------------------------------------
//------------------ Podstawowy pojemnik terenu - sektor -----------------------
//------------------------------------------------------------------------------
TSubRect::TSubRect()
{
nRootNode = NULL; // lista wszystkich obiektów jest pusta
nRenderHidden = nRenderRect = nRenderRectAlpha = nRender = nRenderMixed = nRenderAlpha =
nRenderWires = NULL;
tTrackAnim = NULL; // nic nie animujemy
tTracks = NULL; // nie ma jeszcze torów
nRootMesh = nMeshed = NULL; // te listy te¿ s¹ puste
iNodeCount = 0; // licznik obiektów
iTracks = 0; // licznik torów
}
TSubRect::~TSubRect()
{
if (Global::bManageNodes) // Ra: tu siê coœ sypie
ResourceManager::Unregister(this); // wyrejestrowanie ze sprz¹tacza
// TODO: usun¹æ obiekty z listy (nRootMesh), bo s¹ one tworzone dla sektora
}
void TSubRect::NodeAdd(TGroundNode *Node)
{ // przyczepienie obiektu do sektora, wstêpna kwalifikacja na listy renderowania
if (!this)
return; // zabezpiecznie przed obiektami przekraczaj¹cymi obszar roboczy
// Ra: sortowanie obiektów na listy renderowania:
// nRenderHidden - lista obiektów niewidocznych, "renderowanych" równie¿ z ty³u
// nRenderRect - lista grup renderowanych z sektora
// nRenderRectAlpha - lista grup renderowanych z sektora z przezroczystoœci¹
// nRender - lista grup renderowanych z w³asnych VBO albo DL
// nRenderAlpha - lista grup renderowanych z w³asnych VBO albo DL z przezroczystoœci¹
// nRenderWires - lista grup renderowanych z w³asnych VBO albo DL - druty i linie
// nMeshed - obiekty do pogrupowania wg tekstur
GLuint t; // pomocniczy kod tekstury
switch (Node->iType)
{
case TP_SOUND: // te obiekty s¹ sprawdzanie niezale¿nie od kierunku patrzenia
case TP_EVLAUNCH:
Node->nNext3 = nRenderHidden;
nRenderHidden = Node; // do listy koniecznych
break;
case TP_TRACK: // TODO: tory z cieniem (tunel, canyon) te¿ daæ bez ³¹czenia?
++iTracks; // jeden tor wiêcej
Node->pTrack->RaOwnerSet(this); // do którego sektora ma zg³aszaæ animacjê
// if (Global::bUseVBO?false:!Node->pTrack->IsGroupable())
if (Global::bUseVBO ? true :
!Node->pTrack->IsGroupable()) // TODO: tymczasowo dla VBO wy³¹czone
RaNodeAdd(
Node); // tory ruchome nie s¹ grupowane przy Display Lists (wymagaj¹ odœwie¿ania DL)
else
{ // tory nieruchome mog¹ byæ pogrupowane wg tekstury, przy VBO wszystkie
Node->TextureID = Node->pTrack->TextureGet(0); // pobranie tekstury do sortowania
t = Node->pTrack->TextureGet(1);
if (Node->TextureID) // je¿eli jest pierwsza
{
if (t && (Node->TextureID != t))
{ // jeœli s¹ dwie ró¿ne tekstury, dodajemy drugi obiekt dla danego toru
TGroundNode *n = new TGroundNode();
n->iType = TP_DUMMYTRACK; // obiekt renderuj¹cy siatki dla tekstury
n->TextureID = t;
n->pTrack = Node->pTrack; // wskazuje na ten sam tor
n->pCenter = Node->pCenter;
n->fSquareRadius = Node->fSquareRadius;
n->fSquareMinRadius = Node->fSquareMinRadius;
n->iFlags = Node->iFlags;
n->nNext2 = nRootMesh;
nRootMesh = n; // podczepienie do listy, ¿eby usun¹æ na koñcu
n->nNext3 = nMeshed;
nMeshed = n;
}
}
else
Node->TextureID = t; // jest tylko druga tekstura
if (Node->TextureID)
{
Node->nNext3 = nMeshed;
nMeshed = Node;
} // do podzielenia potem
}
break;
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_TRIANGLES:
// Node->nNext3=nMeshed; nMeshed=Node; //do podzielenia potem
if (Node->iFlags & 0x20) // czy jest przezroczyste?
{
Node->nNext3 = nRenderRectAlpha;
nRenderRectAlpha = Node;
} // DL: do przezroczystych z sektora
else if (Global::bUseVBO)
{
Node->nNext3 = nRenderRect;
nRenderRect = Node;
} // VBO: do nieprzezroczystych z sektora
else
{
Node->nNext3 = nRender;
nRender = Node;
} // DL: do nieprzezroczystych wszelakich
/*
//Ra: na razie wy³¹czone do testów VBO
//if
((Node->iType==GL_TRIANGLE_STRIP)||(Node->iType==GL_TRIANGLE_FAN)||(Node->iType==GL_TRIANGLES))
if (Node->fSquareMinRadius==0.0) //znikaj¹ce z bliska nie mog¹ byæ optymalizowane
if (Node->fSquareRadius>=160000.0) //tak od 400m to ju¿ normalne trójk¹ty musz¹ byæ
//if (Node->iFlags&0x10) //i nieprzezroczysty
{if (pTriGroup) //je¿eli by³ ju¿ jakiœ grupuj¹cy
{if (pTriGroup->fSquareRadius>Node->fSquareRadius) //i mia³ wiêkszy zasiêg
Node->fSquareRadius=pTriGroup->fSquareRadius; //zwiêkszenie zakresu widocznoœci
grupuj¹cego
pTriGroup->pTriGroup=Node; //poprzedniemu doczepiamy nowy
}
Node->pTriGroup=Node; //nowy lider ma siê sam wyœwietlaæ - wskaŸnik na siebie
pTriGroup=Node; //zapamiêtanie lidera
}
*/
break;
case TP_TRACTION:
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP: // te renderowane na koñcu, ¿eby nie ³apa³y koloru nieba
Node->nNext3 = nRenderWires;
nRenderWires = Node; // lista drutów
break;
case TP_MODEL: // modele zawsze wyœwietlane z w³asnego VBO
// jeœli model jest prosty, mo¿na próbowaæ zrobiæ wspóln¹ siatkê (s³upy)
if ((Node->iFlags & 0x20200020) == 0) // czy brak przezroczystoœci?
{
Node->nNext3 = nRender;
nRender = Node;
} // do nieprzezroczystych
else if ((Node->iFlags & 0x10100010) == 0) // czy brak nieprzezroczystoœci?
{
Node->nNext3 = nRenderAlpha;
nRenderAlpha = Node;
} // do przezroczystych
else // jak i take i takie, to bêdzie dwa razy renderowane...
{
Node->nNext3 = nRenderMixed;
nRenderMixed = Node;
} // do mieszanych
// Node->nNext3=nMeshed; //dopisanie do listy sortowania
// nMeshed=Node;
break;
case TP_MEMCELL:
case TP_TRACTIONPOWERSOURCE: // a te w ogóle pomijamy
// case TP_ISOLATED: //lista torów w obwodzie izolowanym - na razie ignorowana
break;
case TP_DYNAMIC:
return; // tych nie dopisujemy wcale
}
Node->nNext2 = nRootNode; // dopisanie do ogólnej listy
nRootNode = Node;
++iNodeCount; // licznik obiektów
}
void TSubRect::RaNodeAdd(TGroundNode *Node)
{ // finalna kwalifikacja na listy renderowania, jeœli nie obs³ugiwane grupowo
switch (Node->iType)
{
case TP_TRACK:
if (Global::bUseVBO)
{
Node->nNext3 = nRenderRect;
nRenderRect = Node;
} // VBO: do nieprzezroczystych z sektora
else
{
Node->nNext3 = nRender;
nRender = Node;
} // DL: do nieprzezroczystych
break;
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_TRIANGLES:
if (Node->iFlags & 0x20) // czy jest przezroczyste?
{
Node->nNext3 = nRenderRectAlpha;
nRenderRectAlpha = Node;
} // DL: do przezroczystych z sektora
else if (Global::bUseVBO)
{
Node->nNext3 = nRenderRect;
nRenderRect = Node;
} // VBO: do nieprzezroczystych z sektora
else
{
Node->nNext3 = nRender;
nRender = Node;
} // DL: do nieprzezroczystych wszelakich
break;
case TP_MODEL: // modele zawsze wyœwietlane z w³asnego VBO
if ((Node->iFlags & 0x20200020) == 0) // czy brak przezroczystoœci?
{
Node->nNext3 = nRender;
nRender = Node;
} // do nieprzezroczystych
else if ((Node->iFlags & 0x10100010) == 0) // czy brak nieprzezroczystoœci?
{
Node->nNext3 = nRenderAlpha;
nRenderAlpha = Node;
} // do przezroczystych
else // jak i take i takie, to bêdzie dwa razy renderowane...
{
Node->nNext3 = nRenderMixed;
nRenderMixed = Node;
} // do mieszanych
break;
case TP_MESH: // grupa ze wspóln¹ tekstur¹
//{Node->nNext3=nRenderRect; nRenderRect=Node;} //do nieprzezroczystych z sektora
{
Node->nNext3 = nRender;
nRender = Node;
} // do nieprzezroczystych
break;
case TP_SUBMODEL: // submodele terenu w kwadracie kilometrowym id¹ do nRootMesh
// WriteLog("nRootMesh was "+AnsiString(nRootMesh?"not null ":"null
// ")+IntToHex(int(this),8));
Node->nNext3 = nRootMesh; // przy VBO musi byæ inaczej
nRootMesh = Node;
break;
}
}
void TSubRect::Sort()
{ // przygotowanie sektora do renderowania
TGroundNode **n0, *n1, *n2; // wskaŸniki robocze
delete[] tTracks; // usuniêcie listy
tTracks =
iTracks ? new TTrack *[iTracks] : NULL; // tworzenie tabeli torów do renderowania pojazdów
if (tTracks)
{ // wype³nianie tabeli torów
int i = 0;
for (n1 = nRootNode; n1; n1 = n1->nNext2) // kolejne obiekty z sektora
if (n1->iType == TP_TRACK)
tTracks[i++] = n1->pTrack;
}
// sortowanie obiektów w sektorze na listy renderowania
if (!nMeshed)
return; // nie ma nic do sortowania
bool sorted = false;
while (!sorted)
{ // sortowanie b¹belkowe obiektów wg tekstury
sorted = true; // zak³adamy posortowanie
n0 = &nMeshed; // wskaŸnik niezbêdny do zamieniania obiektów
n1 = nMeshed; // lista obiektów przetwarzanych na statyczne siatki
while (n1)
{ // sprawdzanie stanu posortowania obiektów i ewentualne zamiany
n2 = n1->nNext3; // kolejny z tej listy
if (n2) // jeœli istnieje
if (n1->TextureID > n2->TextureID)
{ // zamiana elementów miejscami
*n0 = n2; // drugi bêdzie na pocz¹tku
n1->nNext3 = n2->nNext3; // ten zza drugiego bêdzie za pierwszym
n2->nNext3 = n1; // a za drugim bêdzie pierwszy
sorted = false; // potrzebny kolejny przebieg
}
n0 = &(n1->nNext3);
n1 = n2;
};
}
// wyrzucenie z listy obiektów pojedynczych (nie ma z czym ich grupowaæ)
// nawet jak s¹ pojedyncze, to i tak lepiej, aby by³y w jednym Display List
/*
else
{//dodanie do zwyk³ej listy renderowania i usuniêcie z grupowego
*n0=n2; //drugi bêdzie na pocz¹tku
RaNodeAdd(n1); //nie ma go z czym zgrupowaæ; (n1->nNext3) zostanie nadpisane
n1=n2; //potrzebne do ustawienia (n0)
}
*/
//...
// przegl¹danie listy i tworzenie obiektów renderuj¹cych dla danej tekstury
GLuint t = 0; // pomocniczy kod tekstury
n1 = nMeshed; // lista obiektów przetwarzanych na statyczne siatki
while (n1)
{ // dla ka¿dej tekstury powinny istnieæ co najmniej dwa obiekty, ale dla DL nie ma to znaczenia
if (t < n1->TextureID) // jeœli (n1) ma inn¹ teksturê ni¿ poprzednie
{ // mo¿na zrobiæ obiekt renderuj¹cy
t = n1->TextureID;
n2 = new TGroundNode();
n2->nNext2 = nRootMesh;
nRootMesh = n2; // podczepienie na pocz¹tku listy
nRootMesh->iType = TP_MESH; // obiekt renderuj¹cy siatki dla tekstury
nRootMesh->TextureID = t;
nRootMesh->nNode = n1; // pierwszy element z listy
nRootMesh->pCenter = n1->pCenter;
nRootMesh->fSquareRadius = 1e8; // widaæ bez ograniczeñ
nRootMesh->fSquareMinRadius = 0.0;
nRootMesh->iFlags = 0x10;
RaNodeAdd(nRootMesh); // dodanie do odpowiedniej listy renderowania
}
n1 = n1->nNext3; // kolejny z tej listy
};
}
TTrack * TSubRect::FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude)
{ // szukanie toru, którego koniec jest najbli¿szy (*Point)
TTrack *Track;
for (int i = 0; i < iTracks; ++i)
if (tTracks[i] != Exclude) // mo¿na u¿yæ tabelê torów, bo jest mniejsza
{
iConnection = tTracks[i]->TestPoint(Point);
if (iConnection >= 0)
return tTracks[i]; // szukanie TGroundNode nie jest potrzebne
}
/*
TGroundNode *Current;
for (Current=nRootNode;Current;Current=Current->Next)
if ((Current->iType==TP_TRACK)&&(Current->pTrack!=Exclude)) //mo¿na u¿yæ tabelê torów
{
iConnection=Current->pTrack->TestPoint(Point);
if (iConnection>=0) return Current;
}
*/
return NULL;
};
bool TSubRect::RaTrackAnimAdd(TTrack *t)
{ // aktywacja animacji torów w VBO (zwrotnica, obrotnica)
if (m_nVertexCount < 0)
return true; // nie ma animacji, gdy nie widaæ
if (tTrackAnim)
tTrackAnim->RaAnimListAdd(t);
else
tTrackAnim = t;
return false; // bêdzie animowane...
}
void TSubRect::RaAnimate()
{ // wykonanie animacji
if (!tTrackAnim)
return; // nie ma nic do animowania
if (Global::bUseVBO)
{ // odœwie¿enie VBO sektora
if (Global::bOpenGL_1_5) // modyfikacje VBO s¹ dostêpne od OpenGL 1.5
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_nVBOVertices);
else // dla OpenGL 1.4 z GL_ARB_vertex_buffer_object odœwie¿enie ca³ego sektora
Release(); // opró¿nienie VBO sektora, aby siê odœwie¿y³ z nowymi ustawieniami
}
tTrackAnim = tTrackAnim->RaAnimate(); // przeliczenie animacji kolejnego
};
TTraction * TSubRect::FindTraction(vector3 *Point, int &iConnection, TTraction *Exclude)
{ // szukanie przês³a w sektorze, którego koniec jest najbli¿szy (*Point)
TGroundNode *Current;
for (Current = nRenderWires; Current; Current = Current->nNext3)
if ((Current->iType == TP_TRACTION) && (Current->hvTraction != Exclude))
{
iConnection = Current->hvTraction->TestPoint(Point);
if (iConnection >= 0)
return Current->hvTraction;
}
return NULL;
};
void TSubRect::LoadNodes()
{ // utworzenie siatek VBO dla wszystkich node w sektorze
if (m_nVertexCount >= 0)
return; // obiekty by³y ju¿ sprawdzone
m_nVertexCount = 0; //-1 oznacza, ¿e nie sprawdzono listy obiektów
if (!nRootNode)
return;
TGroundNode *n = nRootNode;
while (n)
{
switch (n->iType)
{
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_TRIANGLES:
n->iVboPtr = m_nVertexCount; // nowy pocz¹tek
m_nVertexCount += n->iNumVerts;
break;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
n->iVboPtr = m_nVertexCount; // nowy pocz¹tek
m_nVertexCount +=
n->iNumPts; // miejsce w tablicach normalnych i teksturowania siê zmarnuje...
break;
case TP_TRACK:
n->iVboPtr = m_nVertexCount; // nowy pocz¹tek
n->iNumVerts = n->pTrack->RaArrayPrepare(); // zliczenie wierzcho³ków
m_nVertexCount += n->iNumVerts;
break;
case TP_TRACTION:
n->iVboPtr = m_nVertexCount; // nowy pocz¹tek
n->iNumVerts = n->hvTraction->RaArrayPrepare(); // zliczenie wierzcho³ków
m_nVertexCount += n->iNumVerts;
break;
}
n = n->nNext2; // nastêpny z sektora
}
if (!m_nVertexCount)
return; // jeœli nie ma obiektów do wyœwietlenia z VBO, to koniec
if (Global::bUseVBO)
{ // tylko liczenie wierzcho³ów, gdy nie ma VBO
MakeArray(m_nVertexCount);
n = nRootNode;
int i;
while (n)
{
if (n->iVboPtr >= 0)
switch (n->iType)
{
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_TRIANGLES:
for (i = 0; i < n->iNumVerts; ++i)
{ // Ra: trójk¹ty mo¿na od razu wczytywaæ do takich tablic... to mo¿e poczekaæ
m_pVNT[n->iVboPtr + i].x = n->Vertices[i].Point.x;
m_pVNT[n->iVboPtr + i].y = n->Vertices[i].Point.y;
m_pVNT[n->iVboPtr + i].z = n->Vertices[i].Point.z;
m_pVNT[n->iVboPtr + i].nx = n->Vertices[i].Normal.x;
m_pVNT[n->iVboPtr + i].ny = n->Vertices[i].Normal.y;
m_pVNT[n->iVboPtr + i].nz = n->Vertices[i].Normal.z;
m_pVNT[n->iVboPtr + i].u = n->Vertices[i].tu;
m_pVNT[n->iVboPtr + i].v = n->Vertices[i].tv;
}
break;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
for (i = 0; i < n->iNumPts; ++i)
{
m_pVNT[n->iVboPtr + i].x = n->Points[i].x;
m_pVNT[n->iVboPtr + i].y = n->Points[i].y;
m_pVNT[n->iVboPtr + i].z = n->Points[i].z;
// miejsce w tablicach normalnych i teksturowania siê marnuje...
}
break;
case TP_TRACK:
if (n->iNumVerts) // bo tory zabezpieczaj¹ce s¹ niewidoczne
n->pTrack->RaArrayFill(m_pVNT + n->iVboPtr, m_pVNT);
break;
case TP_TRACTION:
if (n->iNumVerts) // druty mog¹ byæ niewidoczne...?
n->hvTraction->RaArrayFill(m_pVNT + n->iVboPtr);
break;
}
n = n->nNext2; // nastêpny z sektora
}
BuildVBOs();
}
if (Global::bManageNodes)
ResourceManager::Register(this); // dodanie do automatu zwalniaj¹cego pamiêæ
}
bool TSubRect::StartVBO()
{ // pocz¹tek rysowania elementów z VBO w sektorze
SetLastUsage(Timer::GetSimulationTime()); // te z ty³u bêd¹ niepotrzebnie zwalniane
return CMesh::StartVBO();
};
void TSubRect::Release()
{ // wirtualne zwolnienie zasobów przez sprz¹tacz albo destruktor
if (Global::bUseVBO)
CMesh::Clear(); // usuwanie buforów
};
void TSubRect::RenderDL()
{ // renderowanie nieprzezroczystych (DL)
TGroundNode *node;
RaAnimate(); // przeliczenia animacji torów w sektorze
for (node = nRender; node; node = node->nNext3)
node->RenderDL(); // nieprzezroczyste obiekty (oprócz pojazdów)
for (node = nRenderMixed; node; node = node->nNext3)
node->RenderDL(); // nieprzezroczyste z mieszanych modeli
for (int j = 0; j < iTracks; ++j)
tTracks[j]->RenderDyn(); // nieprzezroczyste fragmenty pojazdów na torach
};
void TSubRect::RenderAlphaDL()
{ // renderowanie przezroczystych modeli oraz pojazdów (DL)
TGroundNode *node;
for (node = nRenderMixed; node; node = node->nNext3)
node->RenderAlphaDL(); // przezroczyste z mieszanych modeli
for (node = nRenderAlpha; node; node = node->nNext3)
node->RenderAlphaDL(); // przezroczyste modele
// for (node=tmp->nRender;node;node=node->nNext3)
// if (node->iType==TP_TRACK)
// node->pTrack->RenderAlpha(); //przezroczyste fragmenty pojazdów na torach
for (int j = 0; j < iTracks; ++j)
tTracks[j]->RenderDynAlpha(); // przezroczyste fragmenty pojazdów na torach
};
void TSubRect::RenderVBO()
{ // renderowanie nieprzezroczystych (VBO)
TGroundNode *node;
RaAnimate(); // przeliczenia animacji torów w sektorze
LoadNodes(); // czemu tutaj?
if (StartVBO())
{
for (node = nRenderRect; node; node = node->nNext3)
if (node->iVboPtr >= 0)
node->RenderVBO(); // nieprzezroczyste obiekty terenu
EndVBO();
}
for (node = nRender; node; node = node->nNext3)
node->RenderVBO(); // nieprzezroczyste obiekty (oprócz pojazdów)
for (node = nRenderMixed; node; node = node->nNext3)
node->RenderVBO(); // nieprzezroczyste z mieszanych modeli
for (int j = 0; j < iTracks; ++j)
tTracks[j]->RenderDyn(); // nieprzezroczyste fragmenty pojazdów na torach
};
void TSubRect::RenderAlphaVBO()
{ // renderowanie przezroczystych modeli oraz pojazdów (VBO)
TGroundNode *node;
for (node = nRenderMixed; node; node = node->nNext3)
node->RenderAlphaVBO(); // przezroczyste z mieszanych modeli
for (node = nRenderAlpha; node; node = node->nNext3)
node->RenderAlphaVBO(); // przezroczyste modele
// for (node=tmp->nRender;node;node=node->nNext3)
// if (node->iType==TP_TRACK)
// node->pTrack->RenderAlpha(); //przezroczyste fragmenty pojazdów na torach
for (int j = 0; j < iTracks; ++j)
tTracks[j]->RenderDynAlpha(); // przezroczyste fragmenty pojazdów na torach
};
void TSubRect::RenderSounds()
{ // aktualizacja dŸwiêków w pojazdach sektora (sektor mo¿e nie byæ wyœwietlany)
for (int j = 0; j < iTracks; ++j)
tTracks[j]->RenderDynSounds(); // dŸwiêki pojazdów id¹ niezale¿nie od wyœwietlania
};
//---------------------------------------------------------------------------
//------------------ Kwadrat kilometrowy ------------------------------------
//---------------------------------------------------------------------------
int TGroundRect::iFrameNumber = 0; // licznik wyœwietlanych klatek
TGroundRect::TGroundRect()
{
pSubRects = NULL;
nTerrain = NULL;
};
TGroundRect::~TGroundRect()
{
SafeDeleteArray(pSubRects);
};
void TGroundRect::RenderDL()
{ // renderowanie kwadratu kilometrowego (DL), jeœli jeszcze nie zrobione
if (iLastDisplay != iFrameNumber)
{ // tylko jezeli dany kwadrat nie by³ jeszcze renderowany
// for (TGroundNode* node=pRender;node;node=node->pNext3)
// node->Render(); //nieprzezroczyste trójk¹ty kwadratu kilometrowego
if (nRender)
{ //³¹czenie trójk¹tów w jedn¹ listê - trochê wioska
if (!nRender->DisplayListID || (nRender->iVersion != Global::iReCompile))
{ // je¿eli nie skompilowany, kompilujemy wszystkie trójk¹ty w jeden
nRender->fSquareRadius = 5000.0 * 5000.0; // aby agregat nigdy nie znika³
nRender->DisplayListID = glGenLists(1);
glNewList(nRender->DisplayListID, GL_COMPILE);
nRender->iVersion = Global::iReCompile; // aktualna wersja siatek
for (TGroundNode *node = nRender; node; node = node->nNext3) // nastêpny tej grupy
node->Compile(true);
glEndList();
}
nRender->RenderDL(); // nieprzezroczyste trójk¹ty kwadratu kilometrowego
}
if (nRootMesh)
nRootMesh->RenderDL();
iLastDisplay = iFrameNumber; // drugi raz nie potrzeba
}
};
void TGroundRect::RenderVBO()
{ // renderowanie kwadratu kilometrowego (VBO), jeœli jeszcze nie zrobione
if (iLastDisplay != iFrameNumber)
{ // tylko jezeli dany kwadrat nie by³ jeszcze renderowany
LoadNodes(); // ewentualne tworzenie siatek
if (StartVBO())
{
for (TGroundNode *node = nRenderRect; node; node = node->nNext3) // nastêpny tej grupy
node->RaRenderVBO(); // nieprzezroczyste trójk¹ty kwadratu kilometrowego
EndVBO();
iLastDisplay = iFrameNumber;
}
if (nTerrain)
nTerrain->smTerrain->iVisible = iFrameNumber; // ma siê wyœwietliæ w tej ramce
}
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TGround::MoveGroundNode(vector3 pPosition)
{ // Ra: to wymaga gruntownej reformy
/*
TGroundNode *Current;
for (Current=RootNode;Current!=NULL;Current=Current->Next)
Current->MoveMe(pPosition);
TGroundRect *Rectx=new TGroundRect; //kwadrat kilometrowy
for(int i=0;i<iNumRects;i++)
for(int j=0;j<iNumRects;j++)
Rects[i][j]=*Rectx; //kopiowanie zawartoœci do ka¿dego kwadratu
delete Rectx;
for (Current=RootNode;Current!=NULL;Current=Current->Next)
{//roz³o¿enie obiektów na mapie
if (Current->iType!=TP_DYNAMIC)
{//pojazdów to w ogóle nie dotyczy
if ((Current->iType!=GL_TRIANGLES)&&(Current->iType!=GL_TRIANGLE_STRIP)?true //~czy trójk¹t?
:(Current->iFlags&0x20)?true //~czy teksturê ma nieprzezroczyst¹?
//:(Current->iNumVerts!=3)?true //~czy tylko jeden trójk¹t?
:(Current->fSquareMinRadius!=0.0)?true //~czy widoczny z bliska?
:(Current->fSquareRadius<=90000.0)) //~czy widoczny z daleka?
GetSubRect(Current->pCenter.x,Current->pCenter.z)->AddNode(Current);
else //dodajemy do kwadratu kilometrowego
GetRect(Current->pCenter.x,Current->pCenter.z)->AddNode(Current);
}
}
for (Current=RootDynamic;Current!=NULL;Current=Current->Next)
{
Current->pCenter+=pPosition;
Current->DynamicObject->UpdatePos();
}
for (Current=RootDynamic;Current!=NULL;Current=Current->Next)
Current->DynamicObject->MoverParameters->Physic_ReActivation();
*/
}
TGround::TGround()
{
// RootNode=NULL;
nRootDynamic = NULL;
QueryRootEvent = NULL;
tmpEvent = NULL;
tmp2Event = NULL;
OldQRE = NULL;
RootEvent = NULL;
iNumNodes = 0;
// pTrain=NULL;
Global::pGround = this;
bInitDone = false; // Ra: ¿eby nie robi³o dwa razy FirstInit
for (int i = 0; i < TP_LAST; i++)
nRootOfType[i] = NULL; // zerowanie tablic wyszukiwania
bDynamicRemove = false; // na razie nic do usuniêcia
sTracks = new TNames(); // nazwy torów - na razie tak
}
TGround::~TGround()
{
Free();
}
void TGround::Free()
{
TEvent *tmp;
for (TEvent *Current = RootEvent; Current;)
{
tmp = Current;
Current = Current->evNext2;
delete tmp;
}
TGroundNode *tmpn;
for (int i = 0; i < TP_LAST; ++i)
{
for (TGroundNode *Current = nRootOfType[i]; Current;)
{
tmpn = Current;
Current = Current->nNext;
delete tmpn;
}
nRootOfType[i] = NULL;
}
for (TGroundNode *Current = nRootDynamic; Current;)
{
tmpn = Current;
Current = Current->nNext;
delete tmpn;
}
iNumNodes = 0;
// RootNode=NULL;
nRootDynamic = NULL;
delete sTracks;
}
TGroundNode * TGround::DynamicFindAny(AnsiString asNameToFind)
{ // wyszukanie pojazdu o podanej nazwie, szukanie po wszystkich (u¿yæ drzewa!)
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
if ((Current->asName == asNameToFind))
return Current;
return NULL;
};
TGroundNode * TGround::DynamicFind(AnsiString asNameToFind)
{ // wyszukanie pojazdu z obsad¹ o podanej nazwie (u¿yæ drzewa!)
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
if (Current->DynamicObject->Mechanik)
if ((Current->asName == asNameToFind))
return Current;
return NULL;
};
void TGround::DynamicList(bool all)
{ // odes³anie nazw pojazdów dostêpnych na scenerii (nazwy, szczególnie wagonów, mog¹ siê
// powtarzaæ!)
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
if (all || Current->DynamicObject->Mechanik)
WyslijString(Current->asName, 6); // same nazwy pojazdów
WyslijString("none", 6); // informacja o koñcu listy
};
TGroundNode * TGround::FindGroundNode(AnsiString asNameToFind, TGroundNodeType iNodeType)
{ // wyszukiwanie obiektu o podanej nazwie i konkretnym typie
if ((iNodeType == TP_TRACK) || (iNodeType == TP_MEMCELL) || (iNodeType == TP_MODEL))
{ // wyszukiwanie w drzewie binarnym
return (TGroundNode *)sTracks->Find(iNodeType, asNameToFind.c_str());
}
// standardowe wyszukiwanie liniowe
TGroundNode *Current;
for (Current = nRootOfType[iNodeType]; Current; Current = Current->nNext)
if (Current->asName == asNameToFind)
return Current;
return NULL;
}
double fTrainSetVel = 0;
double fTrainSetDir = 0;
double fTrainSetDist = 0; // odleg³oœæ sk³adu od punktu 1 w stronê punktu 2
AnsiString asTrainSetTrack = "";
int iTrainSetConnection = 0;
bool bTrainSet = false;
AnsiString asTrainName = "";
int iTrainSetWehicleNumber = 0;
TGroundNode *nTrainSetNode = NULL; // poprzedni pojazd do ³¹czenia
TGroundNode *nTrainSetDriver = NULL; // pojazd, któremu zostanie wys³any rozk³ad
TGroundVertex TempVerts[10000]; // tu wczytywane s¹ trójk¹ty
Byte TempConnectionType[200]; // Ra: sprzêgi w sk³adzie; ujemne, gdy odwrotnie
void TGround::RaTriangleDivider(TGroundNode *node)
{ // tworzy dodatkowe trójk¹ty i zmiejsza podany
// to jest wywo³ywane przy wczytywaniu trójk¹tów
// dodatkowe trójk¹ty s¹ dodawane do g³ównej listy trójk¹tów
// podzia³ trójk¹tów na sektory i kwadraty jest dokonywany póŸniej w FirstInit
if (node->iType != GL_TRIANGLES)
return; // tylko pojedyncze trójk¹ty
if (node->iNumVerts != 3)
return; // tylko gdy jeden trójk¹t
double x0 = 1000.0 * floor(0.001 * node->pCenter.x) - 200.0;
double x1 = x0 + 1400.0;
double z0 = 1000.0 * floor(0.001 * node->pCenter.z) - 200.0;
double z1 = z0 + 1400.0;
if ((node->Vertices[0].Point.x >= x0) && (node->Vertices[0].Point.x <= x1) &&
(node->Vertices[0].Point.z >= z0) && (node->Vertices[0].Point.z <= z1) &&
(node->Vertices[1].Point.x >= x0) && (node->Vertices[1].Point.x <= x1) &&
(node->Vertices[1].Point.z >= z0) && (node->Vertices[1].Point.z <= z1) &&
(node->Vertices[2].Point.x >= x0) && (node->Vertices[2].Point.x <= x1) &&
(node->Vertices[2].Point.z >= z0) && (node->Vertices[2].Point.z <= z1))
return; // trójk¹t wystaj¹cy mniej ni¿ 200m z kw. kilometrowego jest do przyjêcia
// Ra: przerobiæ na dzielenie na 2 trójk¹ty, podzia³ w przeciêciu z siatk¹ kilometrow¹
// Ra: i z rekurencj¹ bêdzie dzieliæ trzy trójk¹ty, jeœli bêdzie taka potrzeba
int divide = -1; // bok do podzielenia: 0=AB, 1=BC, 2=CA; +4=podzia³ po OZ; +8 na x1/z1
double min = 0, mul; // jeœli przechodzi przez oœ, iloczyn bêdzie ujemny
x0 += 200.0;
x1 -= 200.0; // przestawienie na siatkê
z0 += 200.0;
z1 -= 200.0;
mul = (node->Vertices[0].Point.x - x0) * (node->Vertices[1].Point.x - x0); // AB na wschodzie
if (mul < min)
min = mul, divide = 0;
mul = (node->Vertices[1].Point.x - x0) * (node->Vertices[2].Point.x - x0); // BC na wschodzie
if (mul < min)
min = mul, divide = 1;
mul = (node->Vertices[2].Point.x - x0) * (node->Vertices[0].Point.x - x0); // CA na wschodzie
if (mul < min)
min = mul, divide = 2;
mul = (node->Vertices[0].Point.x - x1) * (node->Vertices[1].Point.x - x1); // AB na zachodzie
if (mul < min)
min = mul, divide = 8;
mul = (node->Vertices[1].Point.x - x1) * (node->Vertices[2].Point.x - x1); // BC na zachodzie
if (mul < min)
min = mul, divide = 9;
mul = (node->Vertices[2].Point.x - x1) * (node->Vertices[0].Point.x - x1); // CA na zachodzie
if (mul < min)
min = mul, divide = 10;
mul = (node->Vertices[0].Point.z - z0) * (node->Vertices[1].Point.z - z0); // AB na po³udniu
if (mul < min)
min = mul, divide = 4;
mul = (node->Vertices[1].Point.z - z0) * (node->Vertices[2].Point.z - z0); // BC na po³udniu
if (mul < min)
min = mul, divide = 5;
mul = (node->Vertices[2].Point.z - z0) * (node->Vertices[0].Point.z - z0); // CA na po³udniu
if (mul < min)
min = mul, divide = 6;
mul = (node->Vertices[0].Point.z - z1) * (node->Vertices[1].Point.z - z1); // AB na pó³nocy
if (mul < min)
min = mul, divide = 12;
mul = (node->Vertices[1].Point.z - z1) * (node->Vertices[2].Point.z - z1); // BC na pó³nocy
if (mul < min)
min = mul, divide = 13;
mul = (node->Vertices[2].Point.z - z1) * (node->Vertices[0].Point.z - z1); // CA na pó³nocy
if (mul < min)
divide = 14;
// tworzymy jeden dodatkowy trójk¹t, dziel¹c jeden bok na przeciêciu siatki kilometrowej
TGroundNode *ntri; // wskaŸnik na nowy trójk¹t
ntri = new TGroundNode(); // a ten jest nowy
ntri->iType = GL_TRIANGLES; // kopiowanie parametrów, przyda³by siê konstruktor kopiuj¹cy
ntri->Init(3);
ntri->TextureID = node->TextureID;
ntri->iFlags = node->iFlags;
for (int j = 0; j < 4; ++j)
{
ntri->Ambient[j] = node->Ambient[j];
ntri->Diffuse[j] = node->Diffuse[j];
ntri->Specular[j] = node->Specular[j];
}
ntri->asName = node->asName;
ntri->fSquareRadius = node->fSquareRadius;
ntri->fSquareMinRadius = node->fSquareMinRadius;
ntri->bVisible = node->bVisible; // a s¹ jakieœ niewidoczne?
ntri->nNext = nRootOfType[GL_TRIANGLES];
nRootOfType[GL_TRIANGLES] = ntri; // dopisanie z przodu do listy
iNumNodes++;
switch (divide & 3)
{ // podzielenie jednego z boków, powstaje wierzcho³ek D
case 0: // podzia³ AB (0-1) -> ADC i DBC
ntri->Vertices[2] = node->Vertices[2]; // wierzcho³ek C jest wspólny
ntri->Vertices[1] = node->Vertices[1]; // wierzcho³ek B przechodzi do nowego
// node->Vertices[1].HalfSet(node->Vertices[0],node->Vertices[1]); //na razie D tak
if (divide & 4)
node->Vertices[1].SetByZ(node->Vertices[0], node->Vertices[1], divide & 8 ? z1 : z0);
else
node->Vertices[1].SetByX(node->Vertices[0], node->Vertices[1], divide & 8 ? x1 : x0);
ntri->Vertices[0] = node->Vertices[1]; // wierzcho³ek D jest wspólny
break;
case 1: // podzia³ BC (1-2) -> ABD i ADC
ntri->Vertices[0] = node->Vertices[0]; // wierzcho³ek A jest wspólny
ntri->Vertices[2] = node->Vertices[2]; // wierzcho³ek C przechodzi do nowego
// node->Vertices[2].HalfSet(node->Vertices[1],node->Vertices[2]); //na razie D tak
if (divide & 4)
node->Vertices[2].SetByZ(node->Vertices[1], node->Vertices[2], divide & 8 ? z1 : z0);
else
node->Vertices[2].SetByX(node->Vertices[1], node->Vertices[2], divide & 8 ? x1 : x0);
ntri->Vertices[1] = node->Vertices[2]; // wierzcho³ek D jest wspólny
break;
case 2: // podzia³ CA (2-0) -> ABD i DBC
ntri->Vertices[1] = node->Vertices[1]; // wierzcho³ek B jest wspólny
ntri->Vertices[2] = node->Vertices[2]; // wierzcho³ek C przechodzi do nowego
// node->Vertices[2].HalfSet(node->Vertices[2],node->Vertices[0]); //na razie D tak
if (divide & 4)
node->Vertices[2].SetByZ(node->Vertices[2], node->Vertices[0], divide & 8 ? z1 : z0);
else
node->Vertices[2].SetByX(node->Vertices[2], node->Vertices[0], divide & 8 ? x1 : x0);
ntri->Vertices[0] = node->Vertices[2]; // wierzcho³ek D jest wspólny
break;
}
// przeliczenie œrodków ciê¿koœci obu
node->pCenter =
(node->Vertices[0].Point + node->Vertices[1].Point + node->Vertices[2].Point) / 3.0;
ntri->pCenter =
(ntri->Vertices[0].Point + ntri->Vertices[1].Point + ntri->Vertices[2].Point) / 3.0;
RaTriangleDivider(node); // rekurencja, bo nawet na TD raz nie wystarczy
RaTriangleDivider(ntri);
};
TGroundNode * TGround::AddGroundNode(cParser *parser)
{ // wczytanie wpisu typu "node"
// parser->LoadTraction=Global::bLoadTraction; //Ra: tu nie potrzeba powtarzaæ
AnsiString str, str1, str2, str3, str4, Skin, DriverType, asNodeName;
int nv, ti, i, n;
double tf, r, rmin, tf1, tf2, tf3, tf4, l, dist, mgn;
int int1, int2;
bool bError = false, curve;
vector3 pt, front, up, left, pos, tv;
matrix4x4 mat2, mat1, mat;
GLuint TexID;
TGroundNode *tmp1;
TTrack *Track;
TTextSound *tmpsound;
std::string token;
parser->getTokens(2);
*parser >> r >> rmin;
parser->getTokens();
*parser >> token;
asNodeName = AnsiString(token.c_str());
parser->getTokens();
*parser >> token;
str = AnsiString(token.c_str());
TGroundNode *tmp, *tmp2;
tmp = new TGroundNode();
tmp->asName = (asNodeName == AnsiString("none") ? AnsiString("") : asNodeName);
if (r >= 0)
tmp->fSquareRadius = r * r;
tmp->fSquareMinRadius = rmin * rmin;
if (str == "triangles")
tmp->iType = GL_TRIANGLES;
else if (str == "triangle_strip")
tmp->iType = GL_TRIANGLE_STRIP;
else if (str == "triangle_fan")
tmp->iType = GL_TRIANGLE_FAN;
else if (str == "lines")
tmp->iType = GL_LINES;
else if (str == "line_strip")
tmp->iType = GL_LINE_STRIP;
else if (str == "line_loop")
tmp->iType = GL_LINE_LOOP;
else if (str == "model")
tmp->iType = TP_MODEL;
// else if (str=="terrain") tmp->iType=TP_TERRAIN; //tymczasowo do odwo³ania
else if (str == "dynamic")
tmp->iType = TP_DYNAMIC;
else if (str == "sound")
tmp->iType = TP_SOUND;
else if (str == "track")
tmp->iType = TP_TRACK;
else if (str == "memcell")
tmp->iType = TP_MEMCELL;
else if (str == "eventlauncher")
tmp->iType = TP_EVLAUNCH;
else if (str == "traction")
tmp->iType = TP_TRACTION;
else if (str == "tractionpowersource")
tmp->iType = TP_TRACTIONPOWERSOURCE;
// else if (str=="isolated") tmp->iType=TP_ISOLATED;
else
bError = true;
// WriteLog("-> node "+str+" "+tmp->asName);
if (bError)
{
Error(AnsiString("Scene parse error near " + str).c_str());
for (int i = 0; i < 60; ++i)
{ // Ra: skopiowanie dalszej czêœci do logu - taka prowizorka, lepsza ni¿ nic
parser->getTokens(); // pobranie linijki tekstu nie dzia³a
*parser >> token;
WriteLog(token.c_str());
}
// if (tmp==RootNode) RootNode=NULL;
delete tmp;
return NULL;
}
switch (tmp->iType)
{
case TP_TRACTION:
tmp->hvTraction = new TTraction();
parser->getTokens();
*parser >> token;
tmp->hvTraction->asPowerSupplyName = AnsiString(token.c_str()); // nazwa zasilacza
parser->getTokens(3);
*parser >> tmp->hvTraction->NominalVoltage >> tmp->hvTraction->MaxCurrent >>
tmp->hvTraction->fResistivity;
if (tmp->hvTraction->fResistivity == 0.01) // tyle jest w sceneriach [om/km]
tmp->hvTraction->fResistivity = 0.075; // taka sensowniejsza wartoϾ za
// http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf
tmp->hvTraction->fResistivity *= 0.001; // teraz [om/m]
parser->getTokens();
*parser >> token;
// Ra 2014-02: a tutaj damy symbol sieci i jej budowê, np.:
// SKB70-C, CuCd70-2C, KB95-2C, C95-C, C95-2C, YC95-2C, YpC95-2C, YC120-2C
// YpC120-2C, YzC120-2C, YwsC120-2C, YC150-C150, YC150-2C150, C150-C150
// C120-2C, 2C120-2C, 2C120-2C-1, 2C120-2C-2, 2C120-2C-3, 2C120-2C-4
if (token.compare("none") == 0)
tmp->hvTraction->Material = 0;
else if (token.compare("al") == 0)
tmp->hvTraction->Material = 2; // 1=aluminiowa, rysuje siê na czarno
else
tmp->hvTraction->Material = 1; // 1=miedziana, rysuje siê na zielono albo czerwono
parser->getTokens();
*parser >> tmp->hvTraction->WireThickness;
parser->getTokens();
*parser >> tmp->hvTraction->DamageFlag;
parser->getTokens(3);
*parser >> tmp->hvTraction->pPoint1.x >> tmp->hvTraction->pPoint1.y >>
tmp->hvTraction->pPoint1.z;
tmp->hvTraction->pPoint1 += pOrigin;
parser->getTokens(3);
*parser >> tmp->hvTraction->pPoint2.x >> tmp->hvTraction->pPoint2.y >>
tmp->hvTraction->pPoint2.z;
tmp->hvTraction->pPoint2 += pOrigin;
parser->getTokens(3);
*parser >> tmp->hvTraction->pPoint3.x >> tmp->hvTraction->pPoint3.y >>
tmp->hvTraction->pPoint3.z;
tmp->hvTraction->pPoint3 += pOrigin;
parser->getTokens(3);
*parser >> tmp->hvTraction->pPoint4.x >> tmp->hvTraction->pPoint4.y >>
tmp->hvTraction->pPoint4.z;
tmp->hvTraction->pPoint4 += pOrigin;
parser->getTokens();
*parser >> tf1;
tmp->hvTraction->fHeightDifference =
(tmp->hvTraction->pPoint3.y - tmp->hvTraction->pPoint1.y + tmp->hvTraction->pPoint4.y -
tmp->hvTraction->pPoint2.y) *
0.5f -
tf1;
parser->getTokens();
*parser >> tf1;
if (tf1 > 0)
tmp->hvTraction->iNumSections =
(tmp->hvTraction->pPoint1 - tmp->hvTraction->pPoint2).Length() / tf1;
else
tmp->hvTraction->iNumSections = 0;
parser->getTokens();
*parser >> tmp->hvTraction->Wires;
parser->getTokens();
*parser >> tmp->hvTraction->WireOffset;
parser->getTokens();
*parser >> token;
tmp->bVisible = (token.compare("vis") == 0);
parser->getTokens();
*parser >> token;
if (token.compare("parallel") == 0)
{ // jawne wskazanie innego przês³a, na które mo¿e przestawiæ siê pantograf
parser->getTokens();
*parser >> token; // wypada³o by to zapamiêtaæ...
tmp->hvTraction->asParallel = AnsiString(token.c_str());
parser->getTokens();
*parser >> token; // a tu ju¿ powinien byæ koniec
}
if (token.compare("endtraction") != 0)
Error("ENDTRACTION delimiter missing! " + str2 + " found instead.");
tmp->hvTraction->Init(); // przeliczenie parametrów
// if (Global::bLoadTraction)
// tmp->hvTraction->Optimize(); //generowanie DL dla wszystkiego przy wczytywaniu?
tmp->pCenter = (tmp->hvTraction->pPoint2 + tmp->hvTraction->pPoint1) * 0.5f;
// if (!Global::bLoadTraction) SafeDelete(tmp); //Ra: tak byæ nie mo¿e, bo NULL to b³¹d
break;
case TP_TRACTIONPOWERSOURCE:
parser->getTokens(3);
*parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z;
tmp->pCenter += pOrigin;
tmp->psTractionPowerSource = new TTractionPowerSource(tmp);
tmp->psTractionPowerSource->Load(parser);
break;
case TP_MEMCELL:
parser->getTokens(3);
*parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z;
tmp->pCenter.RotateY(aRotate.y / 180.0 * M_PI); // Ra 2014-11: uwzglêdnienie rotacji
tmp->pCenter += pOrigin;
tmp->MemCell = new TMemCell(&tmp->pCenter);
tmp->MemCell->Load(parser);
if (!tmp->asName.IsEmpty()) // jest pusta gdy "none"
{ // dodanie do wyszukiwarki
if (sTracks->Update(TP_MEMCELL, tmp->asName.c_str(),
tmp)) // najpierw sprawdziæ, czy ju¿ jest
{ // przy zdublowaniu wskaŸnik zostanie podmieniony w drzewku na póŸniejszy (zgodnoœæ
// wsteczna)
ErrorLog("Duplicated memcell: " + tmp->asName); // to zg³aszaæ duplikat
}
else
sTracks->Add(TP_MEMCELL, tmp->asName.c_str(), tmp); // nazwa jest unikalna
}
break;
case TP_EVLAUNCH:
parser->getTokens(3);
*parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z;
tmp->pCenter.RotateY(aRotate.y / 180.0 * M_PI); // Ra 2014-11: uwzglêdnienie rotacji
tmp->pCenter += pOrigin;
tmp->EvLaunch = new TEventLauncher();
tmp->EvLaunch->Load(parser);
break;
case TP_TRACK:
tmp->pTrack = new TTrack(tmp);
if (Global::iWriteLogEnabled & 4)
if (!tmp->asName.IsEmpty())
WriteLog(tmp->asName.c_str());
tmp->pTrack->Load(parser, pOrigin,
tmp->asName); // w nazwie mo¿e byæ nazwa odcinka izolowanego
if (!tmp->asName.IsEmpty()) // jest pusta gdy "none"
{ // dodanie do wyszukiwarki
if (sTracks->Update(TP_TRACK, tmp->asName.c_str(),
tmp)) // najpierw sprawdziæ, czy ju¿ jest
{ // przy zdublowaniu wskaŸnik zostanie podmieniony w drzewku na póŸniejszy (zgodnoœæ
// wsteczna)
if (tmp->pTrack->iCategoryFlag & 1) // jeœli jest zdublowany tor kolejowy
ErrorLog("Duplicated track: " + tmp->asName); // to zg³aszaæ duplikat
}
else
sTracks->Add(TP_TRACK, tmp->asName.c_str(), tmp); // nazwa jest unikalna
}
tmp->pCenter = (tmp->pTrack->CurrentSegment()->FastGetPoint_0() +
tmp->pTrack->CurrentSegment()->FastGetPoint(0.5) +
tmp->pTrack->CurrentSegment()->FastGetPoint_1()) /
3.0;
break;
case TP_SOUND:
tmp->tsStaticSound = new TTextSound;
parser->getTokens(3);
*parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z;
tmp->pCenter.RotateY(aRotate.y / 180.0 * M_PI); // Ra 2014-11: uwzglêdnienie rotacji
tmp->pCenter += pOrigin;
parser->getTokens();
*parser >> token;
str = AnsiString(token.c_str());
tmp->tsStaticSound->Init(str.c_str(), sqrt(tmp->fSquareRadius), tmp->pCenter.x,
tmp->pCenter.y, tmp->pCenter.z, false, rmin);
if (rmin < 0.0)
rmin =
0.0; // przywrócenie poprawnej wartoœci, jeœli s³u¿y³a do wy³¹czenia efektu Dopplera
// tmp->pDirectSoundBuffer=TSoundsManager::GetFromName(str.c_str());
// tmp->iState=(Parser->GetNextSymbol().LowerCase()=="loop"?DSBPLAY_LOOPING:0);
parser->getTokens();
*parser >> token;
break;
case TP_DYNAMIC:
tmp->DynamicObject = new TDynamicObject();
// tmp->DynamicObject->Load(Parser);
parser->getTokens();
*parser >> token;
str1 = AnsiString(token.c_str()); // katalog
// McZapkie: doszedl parametr ze zmienialna skora
parser->getTokens();
*parser >> token;
Skin = AnsiString(token.c_str()); // tekstura wymienna
parser->getTokens();
*parser >> token;
str3 = AnsiString(token.c_str()); // McZapkie-131102: model w MMD
if (bTrainSet)
{ // jeœli pojazd jest umieszczony w sk³adzie
str = asTrainSetTrack;
parser->getTokens();
*parser >> tf1; // Ra: -1 oznacza odwrotne wstawienie, normalnie w sk³adzie 0
parser->getTokens();
*parser >> token;
DriverType = AnsiString(token.c_str()); // McZapkie:010303 - w przyszlosci rozne
// konfiguracje mechanik/pomocnik itp
tf3 = fTrainSetVel; // prêdkoœæ
parser->getTokens();
*parser >> token;
str4 = AnsiString(token.c_str());
int2 = str4.Pos("."); // yB: wykorzystuje tutaj zmienna, ktora potem bedzie ladunkiem
if (int2 > 0) // yB: jesli znalazl kropke, to ja przetwarza jako parametry
{
int dlugosc = str4.Length();
int1 = str4.SubString(1, int2 - 1).ToInt(); // niech sprzegiem bedzie do kropki cos
str4 = str4.SubString(int2 + 1, dlugosc - int2);
}
else
{
int1 = str4.ToInt();
str4 = "";
}
int2 = 0; // zeruje po wykorzystaniu
// *parser >> int1; //yB: nastawy i takie tam TUTAJ!!!!!
if (int1 < 0)
int1 = (-int1) |
ctrain_depot; // sprzêg zablokowany (pojazdy nieroz³¹czalne przy manewrach)
if (tf1 != -1.0)
if (fabs(tf1) > 0.5) // maksymalna odleg³oœæ miêdzy sprzêgami - do przemyœlenia
int1 = 0; // likwidacja sprzêgu, jeœli odleg³oœæ zbyt du¿a - to powinno byæ
// uwzglêdniane w fizyce sprzêgów...
TempConnectionType[iTrainSetWehicleNumber] = int1; // wartoϾ dodatnia
}
else
{ // pojazd wstawiony luzem
fTrainSetDist = 0; // zerowanie dodatkowego przesuniêcia
asTrainName = ""; // puste oznacza jazdê pojedynczego bez rozk³adu, "none" jest dla
// sk³adu (trainset)
parser->getTokens();
*parser >> token;
str = AnsiString(token.c_str()); // track
parser->getTokens();
*parser >> tf1; // Ra: -1 oznacza odwrotne wstawienie
parser->getTokens();
*parser >> token;
DriverType = AnsiString(token.c_str()); // McZapkie:010303: obsada
parser->getTokens();
*parser >>
tf3; // prêdkoœæ, niektórzy wpisuj¹ tu "3" jako sprzêg, ¿eby nie by³o tabliczki
iTrainSetWehicleNumber = 0;
TempConnectionType[iTrainSetWehicleNumber] = 3; // likwidacja tabliczki na koñcu?
}
parser->getTokens();
*parser >> int2; // iloœæ ³adunku
if (int2 > 0)
{ // je¿eli ³adunku jest wiêcej ni¿ 0, to rozpoznajemy jego typ
parser->getTokens();
*parser >> token;
str2 = AnsiString(token.c_str()); // LoadType
if (str2 == AnsiString("enddynamic")) // idiotoodpornoœæ: ³adunek bez podanego typu
{
str2 = "";
int2 = 0; // iloœæ bez typu siê nie liczy jako ³adunek
}
}
else
str2 = ""; // brak ladunku
tmp1 = FindGroundNode(str, TP_TRACK); // poszukiwanie toru
if (tmp1 ? tmp1->pTrack != NULL : false)
{ // jeœli tor znaleziony
Track = tmp1->pTrack;
if (!iTrainSetWehicleNumber) // jeœli pierwszy pojazd
if (Track->evEvent0) // jeœli tor ma Event0
if (fabs(fTrainSetVel) <= 1.0) // a sk³ad stoi
if (fTrainSetDist >= 0.0) // ale mo¿e nie siêgaæ na owy tor
if (fTrainSetDist < 8.0) // i raczej nie siêga
fTrainSetDist =
8.0; // przesuwamy oko³o pó³ EU07 dla wstecznej zgodnoœci
// WriteLog("Dynamic shift: "+AnsiString(fTrainSetDist));
/* //Ra: to jednak robi du¿e problemy - przesuniêcie w dynamic jest przesuniêciem do
ty³u, odwrotnie ni¿ w trainset
if (!iTrainSetWehicleNumber) //dla pierwszego jest to przesuniêcie (ujemne = do
ty³u)
if (tf1!=-1.0) //-1 wyj¹tkowo oznacza odwrócenie
tf1=-tf1; //a dla kolejnych odleg³oœæ miêdzy sprzêgami (ujemne = wbite)
*/
tf3 = tmp->DynamicObject->Init(asNodeName, str1, Skin, str3, Track,
(tf1 == -1.0 ? fTrainSetDist : fTrainSetDist - tf1),
DriverType, tf3, asTrainName, int2, str2, (tf1 == -1.0),
str4);
if (tf3 != 0.0) // zero oznacza b³¹d
{
fTrainSetDist -=
tf3; // przesuniêcie dla kolejnego, minus bo idziemy w stronê punktu 1
tmp->pCenter = tmp->DynamicObject->GetPosition();
if (TempConnectionType[iTrainSetWehicleNumber]) // jeœli jest sprzêg
if (tmp->DynamicObject->MoverParameters->Couplers[tf1 == -1.0 ? 0 : 1]
.AllowedFlag &
ctrain_depot) // jesli zablokowany
TempConnectionType[iTrainSetWehicleNumber] |= ctrain_depot; // bêdzie
// blokada
iTrainSetWehicleNumber++;
}
else
{ // LastNode=NULL;
delete tmp;
tmp = NULL; // nie mo¿e byæ tu return, bo trzeba pomin¹æ jeszcze enddynamic
}
}
else
{ // gdy tor nie znaleziony
ErrorLog("Missed track: dynamic placed on \"" + tmp->DynamicObject->asTrack + "\"");
delete tmp;
tmp = NULL; // nie mo¿e byæ tu return, bo trzeba pomin¹æ jeszcze enddynamic
}
parser->getTokens();
*parser >> token;
if (token.compare("destination") == 0)
{ // dok¹d wagon ma jechaæ, uwzglêdniane przy manewrach
parser->getTokens();
*parser >> token;
if (tmp)
tmp->DynamicObject->asDestination = AnsiString(token.c_str());
*parser >> token;
}
if (token.compare("enddynamic") != 0)
Error("enddynamic statement missing");
break;
case TP_MODEL:
if (rmin < 0)
{
tmp->iType = TP_TERRAIN;
tmp->fSquareMinRadius = 0; // to w ogóle potrzebne?
}
parser->getTokens(3);
*parser >> tmp->pCenter.x >> tmp->pCenter.y >> tmp->pCenter.z;
parser->getTokens();
*parser >> tf1;
// OlO_EU&KAKISH-030103: obracanie punktow zaczepien w modelu
tmp->pCenter.RotateY(aRotate.y / 180.0 * M_PI);
// McZapkie-260402: model tez ma wspolrzedne wzgledne
tmp->pCenter += pOrigin;
// tmp->fAngle+=aRotate.y; // /180*M_PI
/*
if (tmp->iType==TP_MODEL)
{//jeœli standardowy model
*/
tmp->Model = new TAnimModel();
tmp->Model->RaAnglesSet(aRotate.x, tf1 + aRotate.y,
aRotate.z); // dostosowanie do pochylania linii
if (tmp->Model->Load(
parser, tmp->iType == TP_TERRAIN)) // wczytanie modelu, tekstury i stanu œwiate³...
tmp->iFlags =
tmp->Model->Flags() | 0x200; // ustalenie, czy przezroczysty; flaga usuwania
else if (tmp->iType != TP_TERRAIN)
{ // model nie wczyta³ siê - ignorowanie node
delete tmp;
tmp = NULL; // nie mo¿e byæ tu return
break; // nie mo¿e byæ tu return?
}
/*
}
else if (tmp->iType==TP_TERRAIN)
{//nie potrzeba nak³adki animuj¹cej submodele
*parser >> token;
tmp->pModel3D=TModelsManager::GetModel(token.c_str(),false);
do //Ra: z tym to trochê bez sensu jest
{parser->getTokens();
*parser >> token;
str=AnsiString(token.c_str());
} while (str!="endterrains");
}
*/
if (tmp->iType == TP_TERRAIN)
{ // jeœli model jest terenem, trzeba utworzyæ dodatkowe obiekty
// po wczytaniu model ma ju¿ utworzone DL albo VBO
Global::pTerrainCompact = tmp->Model; // istnieje co najmniej jeden obiekt terenu
tmp->iCount = Global::pTerrainCompact->TerrainCount() + 1; // zliczenie submodeli
tmp->nNode = new TGroundNode[tmp->iCount]; // sztuczne node dla kwadratów
tmp->nNode[0].iType = TP_MODEL; // pierwszy zawiera model (dla delete)
tmp->nNode[0].Model = Global::pTerrainCompact;
tmp->nNode[0].iFlags = 0x200; // nie wyœwietlany, ale usuwany
for (i = 1; i < tmp->iCount; ++i)
{ // a reszta to submodele
tmp->nNode[i].iType = TP_SUBMODEL; //
tmp->nNode[i].smTerrain = Global::pTerrainCompact->TerrainSquare(i - 1);
tmp->nNode[i].iFlags = 0x10; // nieprzezroczyste; nie usuwany
tmp->nNode[i].bVisible = true;
tmp->nNode[i].pCenter = tmp->pCenter; // nie przesuwamy w inne miejsce
// tmp->nNode[i].asName=
}
}
else if (!tmp->asName.IsEmpty()) // jest pusta gdy "none"
{ // dodanie do wyszukiwarki
if (sTracks->Update(TP_MODEL, tmp->asName.c_str(),
tmp)) // najpierw sprawdziæ, czy ju¿ jest
{ // przy zdublowaniu wskaŸnik zostanie podmieniony w drzewku na póŸniejszy (zgodnoœæ
// wsteczna)
ErrorLog("Duplicated model: " + tmp->asName); // to zg³aszaæ duplikat
}
else
sTracks->Add(TP_MODEL, tmp->asName.c_str(), tmp); // nazwa jest unikalna
}
// str=Parser->GetNextSymbol().LowerCase();
break;
// case TP_GEOMETRY :
case GL_TRIANGLES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
parser->getTokens();
*parser >> token;
// McZapkie-050702: opcjonalne wczytywanie parametrow materialu (ambient,diffuse,specular)
if (token.compare("material") == 0)
{
parser->getTokens();
*parser >> token;
while (token.compare("endmaterial") != 0)
{
if (token.compare("ambient:") == 0)
{
parser->getTokens();
*parser >> tmp->Ambient[0];
parser->getTokens();
*parser >> tmp->Ambient[1];
parser->getTokens();
*parser >> tmp->Ambient[2];
}
else if (token.compare("diffuse:") == 0)
{ // Ra: coœ jest nie tak, bo w jednej linijce nie dzia³a
parser->getTokens();
*parser >> tmp->Diffuse[0];
parser->getTokens();
*parser >> tmp->Diffuse[1];
parser->getTokens();
*parser >> tmp->Diffuse[2];
}
else if (token.compare("specular:") == 0)
{
parser->getTokens();
*parser >> tmp->Specular[0];
parser->getTokens();
*parser >> tmp->Specular[1];
parser->getTokens();
*parser >> tmp->Specular[2];
}
else
Error("Scene material failure!");
parser->getTokens();
*parser >> token;
}
}
if (token.compare("endmaterial") == 0)
{
parser->getTokens();
*parser >> token;
}
str = AnsiString(token.c_str());
#ifdef _PROBLEND
// PROBLEND Q: 13122011 - Szociu: 27012012
PROBLEND = true; // domyslnie uruchomione nowe wyœwietlanie
tmp->PROBLEND = true; // odwolanie do tgroundnode, bo rendering jest w tej klasie
if (str.Pos("@") > 0) // sprawdza, czy w nazwie tekstury jest znak "@"
{
PROBLEND = false; // jeœli jest, wyswietla po staremu
tmp->PROBLEND = false;
}
#endif
tmp->TextureID = TTexturesManager::GetTextureID(szTexturePath, szSceneryPath, str.c_str());
tmp->iFlags = TTexturesManager::GetAlpha(tmp->TextureID) ? 0x220 : 0x210; // z usuwaniem
if (((tmp->iType == GL_TRIANGLES) && (tmp->iFlags & 0x10)) ?
Global::pTerrainCompact->TerrainLoaded() :
false)
{ // jeœli jest tekstura nieprzezroczysta, a teren za³adowany, to pomijamy trójk¹ty
do
{ // pomijanie trójk¹tów
parser->getTokens();
*parser >> token;
} while (token.compare("endtri") != 0);
// delete tmp; //nie ma co tego trzymaæ
// tmp=NULL; //to jest b³¹d
}
else
{
i = 0;
do
{
if (i < 9999) // 3333 trójk¹ty
{ // liczba wierzcho³ków nie jest nieograniczona
parser->getTokens(3);
*parser >> TempVerts[i].Point.x >> TempVerts[i].Point.y >> TempVerts[i].Point.z;
parser->getTokens(3);
*parser >> TempVerts[i].Normal.x >> TempVerts[i].Normal.y >>
TempVerts[i].Normal.z;
/*
str=Parser->GetNextSymbol().LowerCase();
if (str==AnsiString("x"))
TempVerts[i].tu=(TempVerts[i].Point.x+Parser->GetNextSymbol().ToDouble())/Parser->GetNextSymbol().ToDouble();
else
if (str==AnsiString("y"))
TempVerts[i].tu=(TempVerts[i].Point.y+Parser->GetNextSymbol().ToDouble())/Parser->GetNextSymbol().ToDouble();
else
if (str==AnsiString("z"))
TempVerts[i].tu=(TempVerts[i].Point.z+Parser->GetNextSymbol().ToDouble())/Parser->GetNextSymbol().ToDouble();
else
TempVerts[i].tu=str.ToDouble();;
str=Parser->GetNextSymbol().LowerCase();
if (str==AnsiString("x"))
TempVerts[i].tv=(TempVerts[i].Point.x+Parser->GetNextSymbol().ToDouble())/Parser->GetNextSymbol().ToDouble();
else
if (str==AnsiString("y"))
TempVerts[i].tv=(TempVerts[i].Point.y+Parser->GetNextSymbol().ToDouble())/Parser->GetNextSymbol().ToDouble();
else
if (str==AnsiString("z"))
TempVerts[i].tv=(TempVerts[i].Point.z+Parser->GetNextSymbol().ToDouble())/Parser->GetNextSymbol().ToDouble();
else
TempVerts[i].tv=str.ToDouble();;
*/
parser->getTokens(2);
*parser >> TempVerts[i].tu >> TempVerts[i].tv;
// tf=Parser->GetNextSymbol().ToDouble();
// TempVerts[i].tu=tf;
// tf=Parser->GetNextSymbol().ToDouble();
// TempVerts[i].tv=tf;
TempVerts[i].Point.RotateZ(aRotate.z / 180 * M_PI);
TempVerts[i].Point.RotateX(aRotate.x / 180 * M_PI);
TempVerts[i].Point.RotateY(aRotate.y / 180 * M_PI);
TempVerts[i].Normal.RotateZ(aRotate.z / 180 * M_PI);
TempVerts[i].Normal.RotateX(aRotate.x / 180 * M_PI);
TempVerts[i].Normal.RotateY(aRotate.y / 180 * M_PI);
TempVerts[i].Point += pOrigin;
tmp->pCenter += TempVerts[i].Point;
}
else if (i == 9999)
ErrorLog("Bad triangles: too many verices");
i++;
parser->getTokens();
*parser >> token;
// }
} while (token.compare("endtri") != 0);
nv = i;
tmp->Init(nv); // utworzenie tablicy wierzcho³ków
tmp->pCenter /= (nv > 0 ? nv : 1);
// memcpy(tmp->Vertices,TempVerts,nv*sizeof(TGroundVertex));
r = 0;
for (int i = 0; i < nv; i++)
{
tmp->Vertices[i] = TempVerts[i];
tf = SquareMagnitude(tmp->Vertices[i].Point - tmp->pCenter);
if (tf > r)
r = tf;
}
// tmp->fSquareRadius=2000*2000+r;
tmp->fSquareRadius += r;
RaTriangleDivider(tmp); // Ra: dzielenie trójk¹tów jest teraz ca³kiem wydajne
} // koniec wczytywania trójk¹tów
break;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
parser->getTokens(3);
*parser >> tmp->Diffuse[0] >> tmp->Diffuse[1] >> tmp->Diffuse[2];
// tmp->Diffuse[0]=Parser->GetNextSymbol().ToDouble()/255;
// tmp->Diffuse[1]=Parser->GetNextSymbol().ToDouble()/255;
// tmp->Diffuse[2]=Parser->GetNextSymbol().ToDouble()/255;
parser->getTokens();
*parser >> tmp->fLineThickness;
i = 0;
parser->getTokens();
*parser >> token;
do
{
str = AnsiString(token.c_str());
TempVerts[i].Point.x = str.ToDouble();
parser->getTokens(2);
*parser >> TempVerts[i].Point.y >> TempVerts[i].Point.z;
TempVerts[i].Point.RotateZ(aRotate.z / 180 * M_PI);
TempVerts[i].Point.RotateX(aRotate.x / 180 * M_PI);
TempVerts[i].Point.RotateY(aRotate.y / 180 * M_PI);
TempVerts[i].Point += pOrigin;
tmp->pCenter += TempVerts[i].Point;
i++;
parser->getTokens();
*parser >> token;
} while (token.compare("endline") != 0);
nv = i;
// tmp->Init(nv);
tmp->Points = new vector3[nv];
tmp->iNumPts = nv;
tmp->pCenter /= (nv > 0 ? nv : 1);
for (int i = 0; i < nv; i++)
tmp->Points[i] = TempVerts[i].Point;
break;
}
return tmp;
}
TSubRect * TGround::FastGetSubRect(int iCol, int iRow)
{
int br, bc, sr, sc;
br = iRow / iNumSubRects;
bc = iCol / iNumSubRects;
sr = iRow - br * iNumSubRects;
sc = iCol - bc * iNumSubRects;
if ((br < 0) || (bc < 0) || (br >= iNumRects) || (bc >= iNumRects))
return NULL;
return (Rects[br][bc].FastGetRect(sc, sr));
}
TSubRect * TGround::GetSubRect(int iCol, int iRow)
{ // znalezienie ma³ego kwadratu mapy
int br, bc, sr, sc;
br = iRow / iNumSubRects; // wspó³rzêdne kwadratu kilometrowego
bc = iCol / iNumSubRects;
sr = iRow - br * iNumSubRects; // wspó³rzêdne wzglêne ma³ego kwadratu
sc = iCol - bc * iNumSubRects;
if ((br < 0) || (bc < 0) || (br >= iNumRects) || (bc >= iNumRects))
return NULL; // jeœli poza map¹
return (Rects[br][bc].SafeGetRect(sc, sr)); // pobranie ma³ego kwadratu
}
TEvent * TGround::FindEvent(const AnsiString &asEventName)
{
return (TEvent *)sTracks->Find(0, asEventName.c_str()); // wyszukiwanie w drzewie
/* //powolna wyszukiwarka
for (TEvent *Current=RootEvent;Current;Current=Current->Next2)
{
if (Current->asName==asEventName)
return Current;
}
return NULL;
*/
}
TEvent * TGround::FindEventScan(const AnsiString &asEventName)
{ // wyszukanie eventu z opcj¹ utworzenia niejawnego dla komórek skanowanych
TEvent *e = (TEvent *)sTracks->Find(0, asEventName.c_str()); // wyszukiwanie w drzewie eventów
if (e)
return e; // jak istnieje, to w porz¹dku
if (asEventName.SubString(asEventName.Length() - 4, 5) ==
":scan") // jeszcze mo¿e byæ event niejawny
{ // no to szukamy komórki pamiêci o nazwie zawartej w evencie
AnsiString n = asEventName.SubString(1, asEventName.Length() - 5); // do dwukropka
if (sTracks->Find(TP_MEMCELL, n.c_str())) // jeœli jest takowa komórka pamiêci
e = new TEvent(n); // utworzenie niejawnego eventu jej odczytu
}
return e; // utworzony albo siê nie uda³o
}
void TGround::FirstInit()
{ // ustalanie zale¿noœci na scenerii przed wczytaniem pojazdów
if (bInitDone)
return; // Ra: ¿eby nie robi³o siê dwa razy
bInitDone = true;
WriteLog("InitNormals");
int i, j;
for (i = 0; i < TP_LAST; ++i)
{
for (TGroundNode *Current = nRootOfType[i]; Current; Current = Current->nNext)
{
Current->InitNormals();
if (Current->iType != TP_DYNAMIC)
{ // pojazdów w ogóle nie dotyczy dodawanie do mapy
if (i == TP_EVLAUNCH ? Current->EvLaunch->IsGlobal() : false)
srGlobal.NodeAdd(Current); // dodanie do globalnego obiektu
else if (i == TP_TERRAIN)
{ // specjalne przetwarzanie terenu wczytanego z pliku E3D
AnsiString xxxzzz; // nazwa kwadratu
TGroundRect *gr;
for (j = 1; j < Current->iCount; ++j)
{ // od 1 do koñca s¹ zestawy trójk¹tów
xxxzzz = AnsiString(Current->nNode[j].smTerrain->pName); // pobranie nazwy
gr = GetRect(1000 * (xxxzzz.SubString(1, 3).ToIntDef(0) - 500),
1000 * (xxxzzz.SubString(4, 3).ToIntDef(0) - 500));
if (Global::bUseVBO)
gr->nTerrain = Current->nNode + j; // zapamiêtanie
else
gr->RaNodeAdd(&Current->nNode[j]);
}
}
// else if
// ((Current->iType!=GL_TRIANGLES)&&(Current->iType!=GL_TRIANGLE_STRIP)?true
// //~czy trójk¹t?
else if ((Current->iType != GL_TRIANGLES) ?
true //~czy trójk¹t?
:
(Current->iFlags & 0x20) ?
true //~czy teksturê ma nieprzezroczyst¹?
:
(Current->fSquareMinRadius != 0.0) ?
true //~czy widoczny z bliska?
:
(Current->fSquareRadius <= 90000.0)) //~czy widoczny z daleka?
GetSubRect(Current->pCenter.x, Current->pCenter.z)->NodeAdd(Current);
else // dodajemy do kwadratu kilometrowego
GetRect(Current->pCenter.x, Current->pCenter.z)->NodeAdd(Current);
}
// if (Current->iType!=TP_DYNAMIC)
// GetSubRect(Current->pCenter.x,Current->pCenter.z)->AddNode(Current);
}
}
for (i = 0; i < iNumRects; ++i)
for (j = 0; j < iNumRects; ++j)
Rects[i][j].Optimize(); // optymalizacja obiektów w sektorach
WriteLog("InitNormals OK");
WriteLog("InitTracks");
InitTracks(); //³¹czenie odcinków ze sob¹ i przyklejanie eventów
WriteLog("InitTracks OK");
WriteLog("InitTraction");
InitTraction(); //³¹czenie drutów ze sob¹
WriteLog("InitTraction OK");
WriteLog("InitEvents");
InitEvents();
WriteLog("InitEvents OK");
WriteLog("InitLaunchers");
InitLaunchers();
WriteLog("InitLaunchers OK");
WriteLog("InitGlobalTime");
// ABu 160205: juz nie TODO :)
GlobalTime = new TMTableTime(
hh, mm, srh, srm, ssh,
ssm); // McZapkie-300302: inicjacja czasu rozkladowego - TODO: czytac z trasy!
WriteLog("InitGlobalTime OK");
// jeszcze ustawienie pogody, gdyby nie by³o w scenerii wpisów
glClearColor(Global::AtmoColor[0], Global::AtmoColor[1], Global::AtmoColor[2],
0.0); // Background Color
if (Global::fFogEnd > 0)
{
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogfv(GL_FOG_COLOR, Global::FogColor); // set fog color
glFogf(GL_FOG_START, Global::fFogStart); // fog start depth
glFogf(GL_FOG_END, Global::fFogEnd); // fog end depth
glEnable(GL_FOG);
}
else
glDisable(GL_FOG);
glDisable(GL_LIGHTING);
glLightfv(GL_LIGHT0, GL_POSITION, Global::lightPos); // daylight position
glLightfv(GL_LIGHT0, GL_AMBIENT, Global::ambientDayLight); // kolor wszechobceny
glLightfv(GL_LIGHT0, GL_DIFFUSE, Global::diffuseDayLight); // kolor padaj¹cy
glLightfv(GL_LIGHT0, GL_SPECULAR, Global::specularDayLight); // kolor odbity
// musi byæ tutaj, bo wczeœniej nie mieliœmy wartoœci œwiat³a
if (Global::fMoveLight >= 0.0) // albo tak, albo niech ustala minimum ciemnoœci w nocy
{
Global::fLuminance = // obliczenie luminacji "œwiat³a w ciemnoœci"
+0.150 * Global::ambientDayLight[0] // R
+ 0.295 * Global::ambientDayLight[1] // G
+ 0.055 * Global::ambientDayLight[2]; // B
if (Global::fLuminance > 0.1) // jeœli mia³o by byæ za jasno
for (int i = 0; i < 3; i++)
Global::ambientDayLight[i] *=
0.1 / Global::fLuminance; // ograniczenie jasnoœci w nocy
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Global::ambientDayLight);
}
else if (Global::bDoubleAmbient) // Ra: wczeœniej by³o ambient dawane na obydwa œwiat³a
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Global::ambientDayLight);
glEnable(GL_LIGHTING);
WriteLog("FirstInit is done");
};
bool TGround::Init(AnsiString asFile, HDC hDC)
{ // g³ówne wczytywanie scenerii
if (asFile.LowerCase().SubString(1, 7) == "scenery")
asFile.Delete(1, 8); // Ra: usuniêcie niepotrzebnych znaków - zgodnoœæ wstecz z 2003
WriteLog("Loading scenery from " + asFile);
Global::pGround = this;
// pTrain=NULL;
pOrigin = aRotate = vector3(0, 0, 0); // zerowanie przesuniêcia i obrotu
AnsiString str = "";
// TFileStream *fs;
// int size;
std::string subpath = Global::asCurrentSceneryPath.c_str(); // "scenery/";
cParser parser(asFile.c_str(), cParser::buffer_FILE, subpath, Global::bLoadTraction);
std::string token;
/*
TFileStream *fs;
fs=new TFileStream(asFile , fmOpenRead | fmShareCompat );
AnsiString str="";
int size=fs->Size;
str.SetLength(size);
fs->Read(str.c_str(),size);
str+="";
delete fs;
TQueryParserComp *Parser;
Parser=new TQueryParserComp(NULL);
Parser->TextToParse=str;
// Parser->LoadStringToParse(asFile);
Parser->First();
AnsiString Token,asFileName;
*/
const int OriginStackMaxDepth = 100; // rozmiar stosu dla zagnie¿d¿enia origin
int OriginStackTop = 0;
vector3 OriginStack[OriginStackMaxDepth]; // stos zagnie¿d¿enia origin
double tf;
int ParamCount, ParamPos;
// ABu: Jezeli nie ma definicji w scenerii to ustawiane ponizsze wartosci:
hh = 10; // godzina startu
mm = 30; // minuty startu
srh = 6; // godzina wschodu slonca
srm = 0; // minuty wschodu slonca
ssh = 20; // godzina zachodu slonca
ssm = 0; // minuty zachodu slonca
TGroundNode *LastNode = NULL; // do u¿ycia w trainset
iNumNodes = 0;
token = "";
parser.getTokens();
parser >> token;
int refresh = 0;
while (token != "") //(!Parser->EndOfFile)
{
if (refresh == 50)
{ // SwapBuffers(hDC); //Ra: bez ogranicznika za bardzo spowalnia :( a u niektórych miga
refresh = 0;
Global::DoEvents();
}
else
++refresh;
str = AnsiString(token.c_str());
if (str == AnsiString("node"))
{
LastNode = AddGroundNode(&parser); // rozpoznanie wêz³a
if (LastNode)
{ // je¿eli przetworzony poprawnie
if (LastNode->iType == GL_TRIANGLES)
{
if (!LastNode->Vertices)
SafeDelete(LastNode); // usuwamy nieprzezroczyste trójk¹ty terenu
}
else if (Global::bLoadTraction ? false : LastNode->iType == TP_TRACTION)
SafeDelete(LastNode); // usuwamy druty, jeœli wy³¹czone
if (LastNode) // dopiero na koniec dopisujemy do tablic
if (LastNode->iType != TP_DYNAMIC)
{ // jeœli nie jest pojazdem
LastNode->nNext = nRootOfType[LastNode->iType]; // ostatni dodany do³¹czamy
// na koñcu nowego
nRootOfType[LastNode->iType] =
LastNode; // ustawienie nowego na pocz¹tku listy
iNumNodes++;
}
else
{ // jeœli jest pojazdem
// if (!bInitDone) FirstInit(); //jeœli nie by³o w scenerii
if (LastNode->DynamicObject->Mechanik) // ale mo¿e byæ pasa¿er
if (LastNode->DynamicObject->Mechanik
->Primary()) // jeœli jest g³ównym (pasa¿er nie jest)
nTrainSetDriver =
LastNode; // pojazd, któremu zostanie wys³any rozk³ad
LastNode->nNext = nRootDynamic;
nRootDynamic = LastNode; // dopisanie z przodu do listy
// if (bTrainSet && (LastNode?(LastNode->iType==TP_DYNAMIC):false))
if (nTrainSetNode) // je¿eli istnieje wczeœniejszy TP_DYNAMIC
nTrainSetNode->DynamicObject->AttachPrev(
LastNode->DynamicObject,
TempConnectionType[iTrainSetWehicleNumber - 2]);
nTrainSetNode = LastNode; // ostatnio wczytany
if (TempConnectionType[iTrainSetWehicleNumber - 1] ==
0) // jeœli sprzêg jest zerowy, to wys³aæ rozk³ad do sk³adu
{ // powinien te¿ tu wchodziæ, gdy pojazd bez trainset
if (nTrainSetDriver) // pojazd, któremu zostanie wys³any rozk³ad
{ // wys³anie komendy "Timetable" ustawia odpowiedni tryb jazdy
nTrainSetDriver->DynamicObject->Mechanik->DirectionInitial();
nTrainSetDriver->DynamicObject->Mechanik->PutCommand(
"Timetable:" + asTrainName, fTrainSetVel, 0, NULL);
nTrainSetDriver =
NULL; // a przy "endtrainset" ju¿ wtedy nie potrzeba
}
}
}
}
else
{
Error("Scene parse error near " + AnsiString(token.c_str()));
// break;
}
}
else if (str == AnsiString("trainset"))
{
iTrainSetWehicleNumber = 0;
nTrainSetNode = NULL;
nTrainSetDriver = NULL; // pojazd, któremu zostanie wys³any rozk³ad
bTrainSet = true;
parser.getTokens();
parser >> token;
asTrainName = AnsiString(token.c_str()); // McZapkie: rodzaj+nazwa pociagu w SRJP
parser.getTokens();
parser >> token;
asTrainSetTrack = AnsiString(token.c_str()); //œcie¿ka startowa
parser.getTokens(2);
parser >> fTrainSetDist >> fTrainSetVel; // przesuniêcie i prêdkoœæ
}
else if (str == AnsiString("endtrainset"))
{ // McZapkie-110103: sygnaly konca pociagu ale tylko dla pociagow rozkladowych
if (nTrainSetNode) // trainset bez dynamic siê sypa³
{ // powinien te¿ tu wchodziæ, gdy pojazd bez trainset
if (nTrainSetDriver) // pojazd, któremu zostanie wys³any rozk³ad
{ // wys³anie komendy "Timetable" ustawia odpowiedni tryb jazdy
nTrainSetDriver->DynamicObject->Mechanik->DirectionInitial();
nTrainSetDriver->DynamicObject->Mechanik->PutCommand("Timetable:" + asTrainName,
fTrainSetVel, 0, NULL);
}
}
if (LastNode) // ostatni wczytany obiekt
if (LastNode->iType ==
TP_DYNAMIC) // o ile jest pojazdem (na ogó³ jest, ale kto wie...)
if (iTrainSetWehicleNumber ? !TempConnectionType[iTrainSetWehicleNumber - 1] :
false) // jeœli ostatni pojazd ma sprzêg 0
LastNode->DynamicObject->RaLightsSet(-1, 2 + 32 + 64); // to za³o¿ymy mu
// koñcówki blaszane
// (jak AI siê
// odpali, to sobie
// poprawi)
bTrainSet = false;
fTrainSetVel = 0;
// iTrainSetConnection=0;
nTrainSetNode = nTrainSetDriver = NULL;
iTrainSetWehicleNumber = 0;
}
else if (str == AnsiString("event"))
{
TEvent *tmp = new TEvent();
tmp->Load(&parser, &pOrigin);
if (tmp->Type == tp_Unknown)
delete tmp;
else
{ // najpierw sprawdzamy, czy nie ma, a potem dopisujemy
TEvent *found = FindEvent(tmp->asName);
if (found)
{ // jeœli znaleziony duplikat
int i = tmp->asName.Length();
if (tmp->asName[1] == '#') // zawsze jeden znak co najmniej jest
{
delete tmp;
tmp = NULL;
} // utylizacja duplikatu z krzy¿ykiem
else if (i > 8 ? tmp->asName.SubString(1, 9) == "lineinfo:" :
false) // tymczasowo wyj¹tki
{
delete tmp;
tmp = NULL;
} // tymczasowa utylizacja duplikatów W5
else if (i > 8 ? tmp->asName.SubString(i - 7, 8) == "_warning" :
false) // tymczasowo wyj¹tki
{
delete tmp;
tmp = NULL;
} // tymczasowa utylizacja duplikatu z tr¹bieniem
else if (i > 4 ? tmp->asName.SubString(i - 3, 4) == "_shp" :
false) // nie podlegaj¹ logowaniu
{
delete tmp;
tmp = NULL;
} // tymczasowa utylizacja duplikatu SHP
if (tmp) // jeœli nie zosta³ zutylizowany
if (Global::bJoinEvents)
found->Append(tmp); // doczepka (taki wirtualny multiple bez warunków)
else
{
ErrorLog("Duplicated event: " + tmp->asName);
found->Append(tmp); // doczepka (taki wirtualny multiple bez warunków)
found->Type = tp_Ignored; // dezaktywacja pierwotnego - taka proteza na
// wsteczn¹ zgodnoœæ
// SafeDelete(tmp); //bezlitoœnie usuwamy wszelkie duplikaty, ¿eby nie
// zaœmiecaæ drzewka
}
}
if (tmp)
{ // jeœli nie duplikat
tmp->evNext2 = RootEvent; // lista wszystkich eventów (m.in. do InitEvents)
RootEvent = tmp;
if (!found)
{ // jeœli nazwa wyst¹pi³a, to do kolejki i wyszukiwarki dodawany jest tylko
// pierwszy
if (RootEvent->Type != tp_Ignored)
if (RootEvent->asName.Pos(
"onstart")) // event uruchamiany automatycznie po starcie
AddToQuery(RootEvent, NULL); // dodanie do kolejki
sTracks->Add(0, tmp->asName.c_str(), tmp); // dodanie do wyszukiwarki
}
}
}
}
// else
// if (str==AnsiString("include")) //Tolaris to zrobil wewnatrz parsera
// {
// Include(Parser);
// }
else if (str == AnsiString("rotate"))
{
// parser.getTokens(3);
// parser >> aRotate.x >> aRotate.y >> aRotate.z; //Ra: to potrafi dawaæ b³êdne
// rezultaty
parser.getTokens();
parser >> aRotate.x;
parser.getTokens();
parser >> aRotate.y;
parser.getTokens();
parser >> aRotate.z;
// WriteLog("*** rotate "+AnsiString(aRotate.x)+" "+AnsiString(aRotate.y)+"
// "+AnsiString(aRotate.z));
}
else if (str == AnsiString("origin"))
{
// str=Parser->GetNextSymbol().LowerCase();
// if (str=="begin")
{
if (OriginStackTop >= OriginStackMaxDepth - 1)
{
MessageBox(0, AnsiString("Origin stack overflow ").c_str(), "Error", MB_OK);
break;
}
parser.getTokens(3);
parser >> OriginStack[OriginStackTop].x >> OriginStack[OriginStackTop].y >>
OriginStack[OriginStackTop].z;
pOrigin += OriginStack[OriginStackTop]; // sumowanie ca³kowitego przesuniêcia
OriginStackTop++; // zwiêkszenie wskaŸnika stosu
}
}
else if (str == AnsiString("endorigin"))
{
// else
// if (str=="end")
{
if (OriginStackTop <= 0)
{
MessageBox(0, AnsiString("Origin stack underflow ").c_str(), "Error", MB_OK);
break;
}
OriginStackTop--; // zmniejszenie wskaŸnika stosu
pOrigin -= OriginStack[OriginStackTop];
}
}
else if (str == AnsiString("atmo")) // TODO: uporzadkowac gdzie maja byc parametry mgly!
{ // Ra: ustawienie parametrów OpenGL przeniesione do FirstInit
WriteLog("Scenery atmo definition");
parser.getTokens(3);
parser >> Global::AtmoColor[0] >> Global::AtmoColor[1] >> Global::AtmoColor[2];
parser.getTokens(2);
parser >> Global::fFogStart >> Global::fFogEnd;
if (Global::fFogEnd > 0.0)
{ // ostatnie 3 parametry s¹ opcjonalne
parser.getTokens(3);
parser >> Global::FogColor[0] >> Global::FogColor[1] >> Global::FogColor[2];
}
parser.getTokens();
parser >> token;
while (token.compare("endatmo") != 0)
{ // a kolejne parametry s¹ pomijane
parser.getTokens();
parser >> token;
}
}
else if (str == AnsiString("time"))
{
WriteLog("Scenery time definition");
char temp_in[9];
char temp_out[9];
int i, j;
parser.getTokens();
parser >> temp_in;
for (j = 0; j <= 8; j++)
temp_out[j] = ' ';
for (i = 0; temp_in[i] != ':'; i++)
temp_out[i] = temp_in[i];
hh = atoi(temp_out);
for (j = 0; j <= 8; j++)
temp_out[j] = ' ';
for (j = i + 1; j <= 8; j++)
temp_out[j - (i + 1)] = temp_in[j];
mm = atoi(temp_out);
parser.getTokens();
parser >> temp_in;
for (j = 0; j <= 8; j++)
temp_out[j] = ' ';
for (i = 0; temp_in[i] != ':'; i++)
temp_out[i] = temp_in[i];
srh = atoi(temp_out);
for (j = 0; j <= 8; j++)
temp_out[j] = ' ';
for (j = i + 1; j <= 8; j++)
temp_out[j - (i + 1)] = temp_in[j];
srm = atoi(temp_out);
parser.getTokens();
parser >> temp_in;
for (j = 0; j <= 8; j++)
temp_out[j] = ' ';
for (i = 0; temp_in[i] != ':'; i++)
temp_out[i] = temp_in[i];
ssh = atoi(temp_out);
for (j = 0; j <= 8; j++)
temp_out[j] = ' ';
for (j = i + 1; j <= 8; j++)
temp_out[j - (i + 1)] = temp_in[j];
ssm = atoi(temp_out);
while (token.compare("endtime") != 0)
{
parser.getTokens();
parser >> token;
}
}
else if (str == AnsiString("light"))
{ // Ra: ustawianie œwiat³a przeniesione do FirstInit
WriteLog("Scenery light definition");
vector3 lp;
parser.getTokens();
parser >> lp.x;
parser.getTokens();
parser >> lp.y;
parser.getTokens();
parser >> lp.z;
lp = Normalize(lp); // kierunek padania
Global::lightPos[0] = lp.x; // daylight position
Global::lightPos[1] = lp.y;
Global::lightPos[2] = lp.z;
parser.getTokens();
parser >> Global::ambientDayLight[0]; // kolor wszechobceny
parser.getTokens();
parser >> Global::ambientDayLight[1];
parser.getTokens();
parser >> Global::ambientDayLight[2];
parser.getTokens();
parser >> Global::diffuseDayLight[0]; // kolor padaj¹cy
parser.getTokens();
parser >> Global::diffuseDayLight[1];
parser.getTokens();
parser >> Global::diffuseDayLight[2];
parser.getTokens();
parser >> Global::specularDayLight[0]; // kolor odbity
parser.getTokens();
parser >> Global::specularDayLight[1];
parser.getTokens();
parser >> Global::specularDayLight[2];
do
{
parser.getTokens();
parser >> token;
} while (token.compare("endlight") != 0);
}
else if (str == AnsiString("camera"))
{
vector3 xyz, abc;
xyz = abc = vector3(0, 0, 0); // wartoœci domyœlne, bo nie wszystie musz¹ byæ
int i = -1, into = -1; // do której definicji kamery wstawiæ
WriteLog("Scenery camera definition");
do
{ // opcjonalna siódma liczba okreœla numer kamery, a kiedyœ by³y tylko 3
parser.getTokens();
parser >> token;
switch (++i)
{ // kiedyœ camera mia³o tylko 3 wspó³rzêdne
case 0:
xyz.x = atof(token.c_str());
break;
case 1:
xyz.y = atof(token.c_str());
break;
case 2:
xyz.z = atof(token.c_str());
break;
case 3:
abc.x = atof(token.c_str());
break;
case 4:
abc.y = atof(token.c_str());
break;
case 5:
abc.z = atof(token.c_str());
break;
case 6:
into = atoi(token.c_str()); // takie sobie, bo mo¿na wpisaæ -1
}
} while (token.compare("endcamera") != 0);
if (into < 0)
into = ++Global::iCameraLast;
if ((into >= 0) && (into < 10))
{ // przepisanie do odpowiedniego miejsca w tabelce
Global::pFreeCameraInit[into] = xyz;
abc.x = DegToRad(abc.x);
abc.y = DegToRad(abc.y);
abc.z = DegToRad(abc.z);
Global::pFreeCameraInitAngle[into] = abc;
Global::iCameraLast = into; // numer ostatniej
}
}
else if (str == AnsiString("sky"))
{ // youBy - niebo z pliku
WriteLog("Scenery sky definition");
parser.getTokens();
parser >> token;
AnsiString SkyTemp;
SkyTemp = AnsiString(token.c_str());
if (Global::asSky == "1")
Global::asSky = SkyTemp;
do
{ // po¿arcie dodatkowych parametrów
parser.getTokens();
parser >> token;
} while (token.compare("endsky") != 0);
WriteLog(Global::asSky.c_str());
}
else if (str == AnsiString("firstinit"))
FirstInit();
else if (str == AnsiString("description"))
{
do
{
parser.getTokens();
parser >> token;
} while (token.compare("enddescription") != 0);
}
else if (str == AnsiString("test"))
{ // wypisywanie treœci po przetworzeniu
WriteLog("---> Parser test:");
do
{
parser.getTokens();
parser >> token;
WriteLog(token.c_str());
} while (token.compare("endtest") != 0);
WriteLog("---> End of parser test.");
}
else if (str == AnsiString("config"))
{ // mo¿liwoœæ przedefiniowania parametrów w scenerii
Global::ConfigParse(NULL, &parser); // parsowanie dodatkowych ustawieñ
}
else if (str != AnsiString(""))
{ // pomijanie od nierozpoznanej komendy do jej zakoñczenia
if ((token.length() > 2) && (atof(token.c_str()) == 0.0))
{ // jeœli nie liczba, to spróbowaæ pomin¹æ komendê
WriteLog(AnsiString("Unrecognized command: " + str));
str = "end" + str;
do
{
parser.getTokens();
token = "";
parser >> token;
} while ((token != "") && (token.compare(str.c_str()) != 0));
}
else // jak liczba to na pewno b³¹d
Error(AnsiString("Unrecognized command: " + str));
}
else if (str == AnsiString(""))
break;
// LastNode=NULL;
token = "";
parser.getTokens();
parser >> token;
}
delete parser;
sTracks->Sort(TP_TRACK); // finalne sortowanie drzewa torów
sTracks->Sort(TP_MEMCELL); // finalne sortowanie drzewa komórek pamiêci
sTracks->Sort(TP_MODEL); // finalne sortowanie drzewa modeli
sTracks->Sort(0); // finalne sortowanie drzewa eventów
if (!bInitDone)
FirstInit(); // jeœli nie by³o w scenerii
if (Global::pTerrainCompact)
TerrainWrite(); // Ra: teraz mo¿na zapisaæ teren w jednym pliku
Global::iPause &= ~0x10; // koniec pauzy wczytywania
return true;
}
bool TGround::InitEvents()
{ //³¹czenie eventów z pozosta³ymi obiektami
TGroundNode *tmp, *trk;
char buff[255];
int i;
for (TEvent *Current = RootEvent; Current; Current = Current->evNext2)
{
switch (Current->Type)
{
case tp_AddValues: // sumowanie wartoœci
case tp_UpdateValues: // zmiana wartoœci
tmp = FindGroundNode(Current->asNodeName,
TP_MEMCELL); // nazwa komórki powi¹zanej z eventem
if (tmp)
{ // McZapkie-100302
if (Current->iFlags & (conditional_trackoccupied | conditional_trackfree))
{ // jeœli chodzi o zajetosc toru (tor mo¿e byæ inny, ni¿ wpisany w komórce)
trk = FindGroundNode(Current->asNodeName,
TP_TRACK); // nazwa toru ta sama, co nazwa komórki
if (trk)
Current->Params[9].asTrack = trk->pTrack;
if (!Current->Params[9].asTrack)
ErrorLog("Bad event: track \"" + AnsiString(Current->asNodeName) +
"\" does not exists in \"" + Current->asName + "\"");
}
Current->Params[4].nGroundNode = tmp;
Current->Params[5].asMemCell = tmp->MemCell; // komórka do aktualizacji
if (Current->iFlags & (conditional_memcompare))
Current->Params[9].asMemCell = tmp->MemCell; // komórka do badania warunku
if (!tmp->MemCell->asTrackName
.IsEmpty()) // tor powi¹zany z komórk¹ powi¹zan¹ z eventem
{ // tu potrzebujemy wskaŸnik do komórki w (tmp)
trk = FindGroundNode(tmp->MemCell->asTrackName, TP_TRACK);
if (trk)
Current->Params[6].asTrack = trk->pTrack;
else
ErrorLog("Bad memcell: track \"" + tmp->MemCell->asTrackName +
"\" not exists in memcell \"" + tmp->asName + "\"");
}
else
Current->Params[6].asTrack = NULL;
}
else
{ // nie ma komórki, to nie bêdzie dzia³a³ poprawnie
Current->Type = tp_Ignored; // deaktywacja
ErrorLog("Bad event: \"" + Current->asName + "\" cannot find memcell \"" +
Current->asNodeName + "\"");
}
break;
case tp_LogValues: // skojarzenie z memcell
if (Current->asNodeName.IsEmpty())
{ // brak skojarzenia daje logowanie wszystkich
Current->Params[9].asMemCell = NULL;
break;
}
case tp_GetValues:
case tp_WhoIs:
tmp = FindGroundNode(Current->asNodeName, TP_MEMCELL);
if (tmp)
{
Current->Params[8].nGroundNode = tmp;
Current->Params[9].asMemCell = tmp->MemCell;
if (Current->Type == tp_GetValues) // jeœli odczyt komórki
if (tmp->MemCell->IsVelocity()) // a komórka zawiera komendê SetVelocity albo
// ShuntVelocity
Current->bEnabled = false; // to event nie bêdzie dodawany do kolejki
}
else
{ // nie ma komórki, to nie bêdzie dzia³a³ poprawnie
Current->Type = tp_Ignored; // deaktywacja
ErrorLog("Bad event: \"" + Current->asName + "\" cannot find memcell \"" +
Current->asNodeName + "\"");
}
break;
case tp_CopyValues: // skopiowanie komórki do innej
tmp = FindGroundNode(Current->asNodeName, TP_MEMCELL); // komórka docelowa
if (tmp)
{
Current->Params[4].nGroundNode = tmp;
Current->Params[5].asMemCell = tmp->MemCell; // komórka docelowa
if (!tmp->MemCell->asTrackName
.IsEmpty()) // tor powi¹zany z komórk¹ powi¹zan¹ z eventem
{ // tu potrzebujemy wskaŸnik do komórki w (tmp)
trk = FindGroundNode(tmp->MemCell->asTrackName, TP_TRACK);
if (trk)
Current->Params[6].asTrack = trk->pTrack;
else
ErrorLog("Bad memcell: track \"" + tmp->MemCell->asTrackName +
"\" not exists in memcell \"" + tmp->asName + "\"");
}
else
Current->Params[6].asTrack = NULL;
}
else
ErrorLog("Bad copyvalues: event \"" + Current->asName +
"\" cannot find memcell \"" + Current->asNodeName + "\"");
strcpy(
buff,
Current->Params[9].asText); // skopiowanie nazwy drugiej komórki do bufora roboczego
SafeDeleteArray(Current->Params[9].asText); // usuniêcie nazwy komórki
tmp = FindGroundNode(buff, TP_MEMCELL); // komórka Ÿód³owa
if (tmp)
{
Current->Params[8].nGroundNode = tmp;
Current->Params[9].asMemCell = tmp->MemCell; // komórka Ÿród³owa
}
else
ErrorLog("Bad copyvalues: event \"" + Current->asName +
"\" cannot find memcell \"" + AnsiString(buff) + "\"");
break;
case tp_Animation: // animacja modelu
tmp = FindGroundNode(Current->asNodeName, TP_MODEL); // egzemplarza modelu do animowania
if (tmp)
{
strcpy(
buff,
Current->Params[9].asText); // skopiowanie nazwy submodelu do bufora roboczego
SafeDeleteArray(Current->Params[9].asText); // usuniêcie nazwy submodelu
if (Current->Params[0].asInt == 4)
Current->Params[9].asModel = tmp->Model; // model dla ca³omodelowych animacji
else
{ // standardowo przypisanie submodelu
Current->Params[9].asAnimContainer = tmp->Model->GetContainer(buff); // submodel
if (Current->Params[9].asAnimContainer)
{
Current->Params[9].asAnimContainer->WillBeAnimated(); // oflagowanie
// animacji
if (!Current->Params[9]
.asAnimContainer->Event()) // nie szukaæ, gdy znaleziony
Current->Params[9].asAnimContainer->EventAssign(
FindEvent(Current->asNodeName + "." + AnsiString(buff) + ":done"));
}
}
}
else
ErrorLog("Bad animation: event \"" + Current->asName + "\" cannot find model \"" +
Current->asNodeName + "\"");
Current->asNodeName = "";
break;
case tp_Lights: // zmiana œwiete³ modelu
tmp = FindGroundNode(Current->asNodeName, TP_MODEL);
if (tmp)
Current->Params[9].asModel = tmp->Model;
else
ErrorLog("Bad lights: event \"" + Current->asName + "\" cannot find model \"" +
Current->asNodeName + "\"");
Current->asNodeName = "";
break;
case tp_Visible: // ukrycie albo przywrócenie obiektu
tmp = FindGroundNode(Current->asNodeName, TP_MODEL); // najpierw model
if (!tmp)
tmp = FindGroundNode(Current->asNodeName, TP_TRACTION); // mo¿e druty?
if (!tmp)
tmp = FindGroundNode(Current->asNodeName, TP_TRACK); // albo tory?
if (tmp)
Current->Params[9].nGroundNode = tmp;
else
ErrorLog("Bad visibility: event \"" + Current->asName + "\" cannot find model \"" +
Current->asNodeName + "\"");
Current->asNodeName = "";
break;
case tp_Switch: // prze³o¿enie zwrotnicy albo zmiana stanu obrotnicy
tmp = FindGroundNode(Current->asNodeName, TP_TRACK);
if (tmp)
{ // dowi¹zanie toru
if (!tmp->pTrack->iAction) // jeœli nie jest zwrotnic¹ ani obrotnic¹
tmp->pTrack->iAction |= 0x100; // to bêdzie siê zmienia³ stan uszkodzenia
Current->Params[9].asTrack = tmp->pTrack;
if (!Current->Params[0].asInt) // jeœli prze³¹cza do stanu 0
if (Current->Params[2].asdouble >=
0.0) // jeœli jest zdefiniowany dodatkowy ruch iglic
Current->Params[9].asTrack->Switch(
0, Current->Params[1].asdouble,
Current->Params[2].asdouble); // przes³anie parametrów
}
else
ErrorLog("Bad switch: event \"" + Current->asName + "\" cannot find track \"" +
Current->asNodeName + "\"");
Current->asNodeName = "";
break;
case tp_Sound: // odtworzenie dŸwiêku
tmp = FindGroundNode(Current->asNodeName, TP_SOUND);
if (tmp)
Current->Params[9].tsTextSound = tmp->tsStaticSound;
else
ErrorLog("Bad sound: event \"" + Current->asName +
"\" cannot find static sound \"" + Current->asNodeName + "\"");
Current->asNodeName = "";
break;
case tp_TrackVel: // ustawienie prêdkoœci na torze
if (!Current->asNodeName.IsEmpty())
{
tmp = FindGroundNode(Current->asNodeName, TP_TRACK);
if (tmp)
{
tmp->pTrack->iAction |=
0x200; // flaga zmiany prêdkoœci toru jest istotna dla skanowania
Current->Params[9].asTrack = tmp->pTrack;
}
else
ErrorLog("Bad velocity: event \"" + Current->asName +
"\" cannot find track \"" + Current->asNodeName + "\"");
}
Current->asNodeName = "";
break;
case tp_DynVel: // komunikacja z pojazdem o konkretnej nazwie
if (Current->asNodeName == "activator")
Current->Params[9].asDynamic = NULL;
else
{
tmp = FindGroundNode(Current->asNodeName, TP_DYNAMIC);
if (tmp)
Current->Params[9].asDynamic = tmp->DynamicObject;
else
Error("Event \"" + Current->asName + "\" cannot find dynamic \"" +
Current->asNodeName + "\"");
}
Current->asNodeName = "";
break;
case tp_Multiple:
if (Current->Params[9].asText != NULL)
{ // przepisanie nazwy do bufora
strcpy(buff, Current->Params[9].asText);
SafeDeleteArray(Current->Params[9].asText);
Current->Params[9].asPointer = NULL; // zerowanie wskaŸnika, aby wykryæ brak obeiktu
}
else
buff[0] = '\0';
if (Current->iFlags & (conditional_trackoccupied | conditional_trackfree))
{ // jeœli chodzi o zajetosc toru
tmp = FindGroundNode(buff, TP_TRACK);
if (tmp)
Current->Params[9].asTrack = tmp->pTrack;
if (!Current->Params[9].asTrack)
{
ErrorLog(AnsiString("Bad event: Track \"") + AnsiString(buff) +
"\" does not exist in \"" + Current->asName + "\"");
Current->iFlags &=
~(conditional_trackoccupied | conditional_trackfree); // zerowanie flag
}
}
else if (Current->iFlags &
(conditional_memstring | conditional_memval1 | conditional_memval2))
{ // jeœli chodzi o komorke pamieciow¹
tmp = FindGroundNode(buff, TP_MEMCELL);
if (tmp)
Current->Params[9].asMemCell = tmp->MemCell;
if (!Current->Params[9].asMemCell)
{
ErrorLog(AnsiString("Bad event: MemCell \"") + AnsiString(buff) +
AnsiString("\" does not exist in \"" + Current->asName + "\""));
Current->iFlags &=
~(conditional_memstring | conditional_memval1 | conditional_memval2);
}
}
for (i = 0; i < 8; i++)
{
if (Current->Params[i].asText != NULL)
{
strcpy(buff, Current->Params[i].asText);
SafeDeleteArray(Current->Params[i].asText);
Current->Params[i].asEvent = FindEvent(buff);
if (!Current->Params[i].asEvent) // Ra: tylko w logu informacja o braku
if (AnsiString(Current->Params[i].asText).SubString(1, 5) != "none_")
{
WriteLog(AnsiString("Event \"") + AnsiString(buff) +
AnsiString("\" does not exist"));
ErrorLog("Missed event: " + AnsiString(buff) + " in multiple " +
Current->asName);
}
}
}
break;
case tp_Voltage: // zmiana napiêcia w zasilaczu (TractionPowerSource)
if (!Current->asNodeName.IsEmpty())
{
tmp = FindGroundNode(Current->asNodeName,
TP_TRACTIONPOWERSOURCE); // pod³¹czenie zasilacza
if (tmp)
Current->Params[9].psPower = tmp->psTractionPowerSource;
else
ErrorLog("Bad voltage: event \"" + Current->asName +
"\" cannot find power source \"" + Current->asNodeName + "\"");
}
Current->asNodeName = "";
break;
case tp_Message: // wyœwietlenie komunikatu
break;
}
if (Current->fDelay < 0)
AddToQuery(Current, NULL);
}
for (TGroundNode *Current = nRootOfType[TP_MEMCELL]; Current; Current = Current->nNext)
{ // Ra: eventy komórek pamiêci, wykonywane po wys³aniu komendy do zatrzymanego pojazdu
Current->MemCell->AssignEvents(FindEvent(Current->asName + ":sent"));
}
return true;
}
void TGround::InitTracks()
{ //³¹czenie torów ze sob¹ i z eventami
TGroundNode *Current, *Model;
TTrack *tmp; // znaleziony tor
TTrack *Track;
int iConnection, state;
AnsiString name;
// tracks=tracksfar=0;
for (Current = nRootOfType[TP_TRACK]; Current; Current = Current->nNext)
{
Track = Current->pTrack;
if (Global::iHiddenEvents & 1)
if (!Current->asName.IsEmpty())
{ // jeœli podana jest nazwa torów, mo¿na szukaæ eventów skojarzonych przez nazwê
if (Track->asEvent0Name.IsEmpty())
if (FindEvent(Current->asName + ":event0"))
Track->asEvent0Name = Current->asName + ":event0";
if (Track->asEvent1Name.IsEmpty())
if (FindEvent(Current->asName + ":event1"))
Track->asEvent1Name = Current->asName + ":event1";
if (Track->asEvent2Name.IsEmpty())
if (FindEvent(Current->asName + ":event2"))
Track->asEvent2Name = Current->asName + ":event2";
if (Track->asEventall0Name.IsEmpty())
if (FindEvent(Current->asName + ":eventall0"))
Track->asEventall0Name = Current->asName + ":eventall0";
if (Track->asEventall1Name.IsEmpty())
if (FindEvent(Current->asName + ":eventall1"))
Track->asEventall1Name = Current->asName + ":eventall1";
if (Track->asEventall2Name.IsEmpty())
if (FindEvent(Current->asName + ":eventall2"))
Track->asEventall2Name = Current->asName + ":eventall2";
}
Track->AssignEvents(
Track->asEvent0Name.IsEmpty() ? NULL : FindEvent(Track->asEvent0Name),
Track->asEvent1Name.IsEmpty() ? NULL : FindEventScan(Track->asEvent1Name),
Track->asEvent2Name.IsEmpty() ? NULL : FindEventScan(Track->asEvent2Name));
Track->AssignallEvents(
Track->asEventall0Name.IsEmpty() ? NULL : FindEvent(Track->asEventall0Name),
Track->asEventall1Name.IsEmpty() ? NULL : FindEvent(Track->asEventall1Name),
Track->asEventall2Name.IsEmpty() ? NULL :
FindEvent(Track->asEventall2Name)); // MC-280503
switch (Track->eType)
{
case tt_Table: // obrotnicê te¿ ³¹czymy na starcie z innymi torami
Model = FindGroundNode(Current->asName, TP_MODEL); // szukamy modelu o tej samej nazwie
// if (tmp) //mamy model, trzeba zapamiêtaæ wskaŸnik do jego animacji
{ // jak coœ pójdzie Ÿle, to robimy z tego normalny tor
// Track->ModelAssign(tmp->Model->GetContainer(NULL)); //wi¹zanie toru z modelem
// obrotnicy
Track->RaAssign(
Current, Model ? Model->Model : NULL, FindEvent(Current->asName + ":done"),
FindEvent(Current->asName + ":joined")); // wi¹zanie toru z modelem obrotnicy
// break; //jednak po³¹czê z s¹siednim, jak ma siê wysypywaæ null track
}
if (!Model) // jak nie ma modelu
break; // to pewnie jest wykolejnica, a ta jest domyœlnie zamkniêta i wykoleja
case tt_Normal: // tylko proste s¹ pod³¹czane do rozjazdów, st¹d dwa rozjazdy siê nie
// po³¹cz¹ ze sob¹
if (Track->CurrentPrev() == NULL) // tylko jeœli jeszcze nie pod³¹czony
{
tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection, Current);
switch (iConnection)
{
case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków
// if ((Track->iCategoryFlag&1)==0) //jeœli nie jest torem szynowym
// Track->ConnectPrevPrev(Track,0); //³¹czenie koñca odcinka do samego siebie
break;
case 0:
Track->ConnectPrevPrev(tmp, 0);
break;
case 1:
Track->ConnectPrevNext(tmp, 1);
break;
case 2:
Track->ConnectPrevPrev(tmp, 0); // do Point1 pierwszego
tmp->SetConnections(0); // zapamiêtanie ustawieñ w Segmencie
break;
case 3:
Track->ConnectPrevNext(tmp, 1); // do Point2 pierwszego
tmp->SetConnections(0); // zapamiêtanie ustawieñ w Segmencie
break;
case 4:
tmp->Switch(1);
Track->ConnectPrevPrev(tmp, 2); // do Point1 drugiego
tmp->SetConnections(1); // robi te¿ Switch(0)
tmp->Switch(0);
break;
case 5:
tmp->Switch(1);
Track->ConnectPrevNext(tmp, 3); // do Point2 drugiego
tmp->SetConnections(1); // robi te¿ Switch(0)
tmp->Switch(0);
break;
}
}
if (Track->CurrentNext() == NULL) // tylko jeœli jeszcze nie pod³¹czony
{
tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_1(), iConnection, Current);
switch (iConnection)
{
case -1: // Ra: pierwsza koncepcja zawijania samochodów i statków
// if ((Track->iCategoryFlag&1)==0) //jeœli nie jest torem szynowym
// Track->ConnectNextNext(Track,1); //³¹czenie koñca odcinka do samego siebie
break;
case 0:
Track->ConnectNextPrev(tmp, 0);
break;
case 1:
Track->ConnectNextNext(tmp, 1);
break;
case 2:
Track->ConnectNextPrev(tmp, 0);
tmp->SetConnections(0); // zapamiêtanie ustawieñ w Segmencie
break;
case 3:
Track->ConnectNextNext(tmp, 1);
tmp->SetConnections(0); // zapamiêtanie ustawieñ w Segmencie
break;
case 4:
tmp->Switch(1);
Track->ConnectNextPrev(tmp, 2);
tmp->SetConnections(1); // robi te¿ Switch(0)
// tmp->Switch(0);
break;
case 5:
tmp->Switch(1);
Track->ConnectNextNext(tmp, 3);
tmp->SetConnections(1); // robi te¿ Switch(0)
// tmp->Switch(0);
break;
}
}
break;
case tt_Switch: // dla rozjazdów szukamy eventów sygnalizacji rozprucia
Track->AssignForcedEvents(FindEvent(Current->asName + ":forced+"),
FindEvent(Current->asName + ":forced-"));
break;
}
name = Track->IsolatedName(); // pobranie nazwy odcinka izolowanego
if (!name.IsEmpty()) // jeœli zosta³a zwrócona nazwa
Track->IsolatedEventsAssign(FindEvent(name + ":busy"), FindEvent(name + ":free"));
if (Current->asName.SubString(1, 1) ==
"*") // mo¿liwy portal, jeœli nie pod³¹czony od striny 1
if (!Track->CurrentPrev() && Track->CurrentNext())
Track->iCategoryFlag |= 0x100; // ustawienie flagi portalu
}
// WriteLog("Total "+AnsiString(tracks)+", far "+AnsiString(tracksfar));
TIsolated *p = TIsolated::Root();
while (p)
{ // jeœli siê znajdzie, to podaæ wskaŸnik
Current = FindGroundNode(p->asName, TP_MEMCELL); // czy jest komóka o odpowiedniej nazwie
if (Current)
p->pMemCell = Current->MemCell; // przypisanie powi¹zanej komórki
else
{ // utworzenie automatycznej komórki
Current = new TGroundNode(); // to nie musi mieæ nazwy, nazwa w wyszukiwarce wystarczy
// Current->asName=p->asName; //mazwa identyczna, jak nazwa odcinka izolowanego
Current->MemCell = new TMemCell(NULL); // nowa komórka
sTracks->Add(TP_MEMCELL, p->asName.c_str(), Current); // dodanie do wyszukiwarki
Current->nNext =
nRootOfType[TP_MEMCELL]; // to nie powinno tutaj byæ, bo robi siê œmietnik
nRootOfType[TP_MEMCELL] = Current;
iNumNodes++;
p->pMemCell = Current->MemCell; // wskaŸnik komóki przekazany do odcinka izolowanego
}
p = p->Next();
}
// for (Current=nRootOfType[TP_TRACK];Current;Current=Current->nNext)
// if (Current->pTrack->eType==tt_Cross)
// Current->pTrack->ConnectionsLog(); //zalogowanie informacji o po³¹czeniach
}
void TGround::InitTraction()
{ //³¹czenie drutów ze sob¹ oraz z torami i eventami
TGroundNode *nCurrent, *nTemp;
TTraction *tmp; // znalezione przês³o
TTraction *Traction;
int iConnection;
AnsiString name;
for (nCurrent = nRootOfType[TP_TRACTION]; nCurrent; nCurrent = nCurrent->nNext)
{ // pod³¹czenie do zasilacza, ¿eby mo¿na by³o sumowaæ pr¹d kilku pojazdów
// a jednoczeœnie z jednego miejsca zmieniaæ napiêcie eventem
// wykonywane najpierw, ¿eby mo¿na by³o logowaæ pod³¹czenie 2 zasilaczy do jednego drutu
// izolator zawieszony na przêœle jest ma byæ osobnym odcinkiem drutu o d³ugoœci ok. 1m,
// pod³¹czonym do zasilacza o nazwie "*" (gwiazka); "none" nie bêdzie odpowiednie
Traction = nCurrent->hvTraction;
nTemp = FindGroundNode(Traction->asPowerSupplyName, TP_TRACTIONPOWERSOURCE);
if (nTemp) // jak zasilacz znaleziony
Traction->PowerSet(nTemp->psTractionPowerSource); // to pod³¹czyæ do przês³a
else if (Traction->asPowerSupplyName != "*") // gwiazdka dla przês³a z izolatorem
if (Traction->asPowerSupplyName != "none") // dopuszczamy na razie brak pod³¹czenia?
{ // logowanie b³êdu i utworzenie zasilacza o domyœlnej zawartoœci
ErrorLog("Missed TractionPowerSource: " + Traction->asPowerSupplyName);
nTemp = new TGroundNode();
nTemp->iType = TP_TRACTIONPOWERSOURCE;
nTemp->asName = Traction->asPowerSupplyName;
nTemp->psTractionPowerSource = new TTractionPowerSource(nTemp);
nTemp->psTractionPowerSource->Init(Traction->NominalVoltage, Traction->MaxCurrent);
nTemp->nNext = nRootOfType[nTemp->iType]; // ostatni dodany do³¹czamy na koñcu
// nowego
nRootOfType[nTemp->iType] = nTemp; // ustawienie nowego na pocz¹tku listy
iNumNodes++;
}
}
for (nCurrent = nRootOfType[TP_TRACTION]; nCurrent; nCurrent = nCurrent->nNext)
{
Traction = nCurrent->hvTraction;
if (!Traction->hvNext[0]) // tylko jeœli jeszcze nie pod³¹czony
{
tmp = FindTraction(&Traction->pPoint1, iConnection, nCurrent);
switch (iConnection)
{
case 0:
Traction->Connect(0, tmp, 0);
break;
case 1:
Traction->Connect(0, tmp, 1);
break;
}
if (Traction->hvNext[0]) // jeœli zosta³ pod³¹czony
if (Traction->psSection && tmp->psSection) // tylko przês³o z izolatorem mo¿e nie
// mieæ zasilania, bo ma 2, trzeba
// sprawdzaæ s¹siednie
if (Traction->psSection !=
tmp->psSection) // po³¹czone odcinki maj¹ ró¿ne zasilacze
{ // to mo¿e byæ albo pod³¹czenie podstacji lub kabiny sekcyjnej do sekcji, albo
// b³¹d
if (Traction->psSection->bSection && !tmp->psSection->bSection)
{ //(tmp->psSection) jest podstacj¹, a (Traction->psSection) nazw¹ sekcji
tmp->PowerSet(Traction->psSection); // zast¹pienie wskazaniem sekcji
}
else if (!Traction->psSection->bSection && tmp->psSection->bSection)
{ //(Traction->psSection) jest podstacj¹, a (tmp->psSection) nazw¹ sekcji
Traction->PowerSet(tmp->psSection); // zast¹pienie wskazaniem sekcji
}
else // jeœli obie to sekcje albo obie podstacje, to bêdzie b³¹d
ErrorLog("Bad power: at " +
FloatToStrF(Traction->pPoint1.x, ffFixed, 6, 2) + " " +
FloatToStrF(Traction->pPoint1.y, ffFixed, 6, 2) + " " +
FloatToStrF(Traction->pPoint1.z, ffFixed, 6, 2));
}
}
if (!Traction->hvNext[1]) // tylko jeœli jeszcze nie pod³¹czony
{
tmp = FindTraction(&Traction->pPoint2, iConnection, nCurrent);
switch (iConnection)
{
case 0:
Traction->Connect(1, tmp, 0);
break;
case 1:
Traction->Connect(1, tmp, 1);
break;
}
if (Traction->hvNext[1]) // jeœli zosta³ pod³¹czony
if (Traction->psSection && tmp->psSection) // tylko przês³o z izolatorem mo¿e nie
// mieæ zasilania, bo ma 2, trzeba
// sprawdzaæ s¹siednie
if (Traction->psSection != tmp->psSection)
{ // to mo¿e byæ albo pod³¹czenie podstacji lub kabiny sekcyjnej do sekcji, albo
// b³¹d
if (Traction->psSection->bSection && !tmp->psSection->bSection)
{ //(tmp->psSection) jest podstacj¹, a (Traction->psSection) nazw¹ sekcji
tmp->PowerSet(Traction->psSection); // zast¹pienie wskazaniem sekcji
}
else if (!Traction->psSection->bSection && tmp->psSection->bSection)
{ //(Traction->psSection) jest podstacj¹, a (tmp->psSection) nazw¹ sekcji
Traction->PowerSet(tmp->psSection); // zast¹pienie wskazaniem sekcji
}
else // jeœli obie to sekcje albo obie podstacje, to bêdzie b³¹d
ErrorLog("Bad power: at " +
FloatToStrF(Traction->pPoint2.x, ffFixed, 6, 2) + " " +
FloatToStrF(Traction->pPoint2.y, ffFixed, 6, 2) + " " +
FloatToStrF(Traction->pPoint2.z, ffFixed, 6, 2));
}
}
}
iConnection = 0; // teraz bêdzie licznikiem koñców
for (nCurrent = nRootOfType[TP_TRACTION]; nCurrent; nCurrent = nCurrent->nNext)
{ // operacje maj¹ce na celu wykrywanie bie¿ni wspólnych i ³¹czenie przêse³ napr¹¿ania
if (nCurrent->hvTraction->WhereIs()) // oznakowanie przedostatnich przêse³
{ // poszukiwanie bie¿ni wspólnej dla przedostatnich przêse³, równie¿ w celu po³¹czenia
// zasilania
// to siê nie sprawdza, bo po³¹czyæ siê mog¹ dwa niezasilane odcinki jako najbli¿sze
// sobie
// nCurrent->hvTraction->hvParallel=TractionNearestFind(nCurrent->pCenter,0,nCurrent);
// //szukanie najbli¿szego przês³a
// trzeba by zliczaæ koñce, a potem wpisaæ je do tablicy, aby sukcesywnie pod³¹czaæ do
// zasilaczy
nCurrent->hvTraction->iTries = 5; // oznaczanie koñcowych
++iConnection;
}
if (nCurrent->hvTraction->fResistance[0] == 0.0)
{
nCurrent->hvTraction
->ResistanceCalc(); // obliczanie przêse³ w segmencie z bezpoœrednim zasilaniem
// ErrorLog("Section "+nCurrent->hvTraction->asPowerSupplyName+" connected"); //jako
// niby b³¹d bêdzie bardziej widoczne
nCurrent->hvTraction->iTries = 0; // nie potrzeba mu szukaæ zasilania
}
// if (!Traction->hvParallel) //jeszcze utworzyæ pêtle z bie¿ni wspólnych
}
int zg = 0; // zgodnoœæ kierunku przêse³, tymczasowo iterator do tabeli koñców
TGroundNode **nEnds = new TGroundNode *[iConnection]; // koñców jest ok. 10 razy mniej ni¿
// wszystkich przêse³ (Quark: 216)
for (nCurrent = nRootOfType[TP_TRACTION]; nCurrent; nCurrent = nCurrent->nNext)
{ //³¹czenie bie¿ni wspólnych, w tym oznaczanie niepodanych jawnie
Traction = nCurrent->hvTraction;
if (!Traction->asParallel.IsEmpty()) // bêdzie wskaŸnik na inne przês³o
if ((Traction->asParallel == "none") ||
(Traction->asParallel == "*")) // jeœli nieokreœlone
Traction->iLast =
2; // jakby przedostatni - niech po prostu szuka (iLast ju¿ przeliczone)
else if (!Traction->hvParallel) // jeœli jeszcze nie zosta³ w³¹czony w kó³ko
{
nTemp = FindGroundNode(Traction->asParallel, TP_TRACTION);
if (nTemp)
{ // o ile zostanie znalezione przês³o o takiej nazwie
if (!nTemp->hvTraction
->hvParallel) // jeœli tamten jeszcze nie ma wskaŸnika bie¿ni wspólnej
Traction->hvParallel =
nTemp->hvTraction; // wpisaæ siebie i dalej daæ mu wskaŸnik zwrotny
else // a jak ma, to albo do³¹czyæ siê do kó³eczka
Traction->hvParallel =
nTemp->hvTraction->hvParallel; // przj¹æ dotychczasowy wskaŸnik od niego
nTemp->hvTraction->hvParallel =
Traction; // i na koniec ustawienie wskaŸnika zwrotnego
}
if (!Traction->hvParallel)
ErrorLog("Missed overhead: " + Traction->asParallel); // logowanie braku
}
if (Traction->iTries > 0) // jeœli zaznaczony do pod³¹czenia
// if (!nCurrent->hvTraction->psPower[0]||!nCurrent->hvTraction->psPower[1])
if (zg < iConnection) // zabezpieczenie
nEnds[zg++] = nCurrent; // wype³nianie tabeli koñców w celu szukania im po³¹czeñ
}
while (zg < iConnection)
nEnds[zg++] = NULL; // zape³nienie do koñca tablicy, jeœli by jakieœ koñce wypad³y
zg = 1; // nieefektywny przebieg koñczy ³¹czenie
while (zg)
{ // ustalenie zastêpczej rezystancji dla ka¿dego przês³a
zg = 0; // flaga pod³¹czonych przêse³ koñcowych: -1=puste wskaŸniki, 0=coœ zosta³o,
// 1=wykonano ³¹czenie
for (int i = 0; i < iConnection; ++i)
if (nEnds[i]) // za³atwione bêdziemy zerowaæ
{ // ka¿dy przebieg to próba pod³¹czenia koñca segmentu naprê¿ania do innego zasilanego
// przês³a
if (nEnds[i]->hvTraction->hvNext[0])
{ // jeœli koñcowy ma ci¹g dalszy od strony 0 (Point1), szukamy odcinka najbli¿szego
// do Point2
if (TractionNearestFind(nEnds[i]->hvTraction->pPoint2, 0,
nEnds[i])) // poszukiwanie przês³a
{
nEnds[i] = NULL;
zg = 1; // jak coœ zosta³o pod³¹czone, to mo¿e zasilanie gdzieœ dodatkowo
// dotrze
}
}
else if (nEnds[i]->hvTraction->hvNext[1])
{ // jeœli koñcowy ma ci¹g dalszy od strony 1 (Point2), szukamy odcinka najbli¿szego
// do Point1
if (TractionNearestFind(nEnds[i]->hvTraction->pPoint1, 1,
nEnds[i])) // poszukiwanie przês³a
{
nEnds[i] = NULL;
zg = 1; // jak coœ zosta³o pod³¹czone, to mo¿e zasilanie gdzieœ dodatkowo
// dotrze
}
}
else
{ // gdy koniec jest samotny, to na razie nie zostanie pod³¹czony (nie powinno
// takich byæ)
nEnds[i] = NULL;
}
}
}
delete[] nEnds; // nie potrzebne ju¿
};
void TGround::TrackJoin(TGroundNode *Current)
{ // wyszukiwanie s¹siednich torów do pod³¹czenia (wydzielone na u¿ytek obrotnicy)
TTrack *Track = Current->pTrack;
TTrack *tmp;
int iConnection;
if (!Track->CurrentPrev())
{
tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection,
Current); // Current do pominiêcia
switch (iConnection)
{
case 0:
Track->ConnectPrevPrev(tmp, 0);
break;
case 1:
Track->ConnectPrevNext(tmp, 1);
break;
}
}
if (!Track->CurrentNext())
{
tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_1(), iConnection, Current);
switch (iConnection)
{
case 0:
Track->ConnectNextPrev(tmp, 0);
break;
case 1:
Track->ConnectNextNext(tmp, 1);
break;
}
}
}
// McZapkie-070602: wyzwalacze zdarzen
bool TGround::InitLaunchers()
{
TGroundNode *Current, *tmp;
TEventLauncher *EventLauncher;
int i;
for (Current = nRootOfType[TP_EVLAUNCH]; Current; Current = Current->nNext)
{
EventLauncher = Current->EvLaunch;
if (EventLauncher->iCheckMask != 0)
if (EventLauncher->asMemCellName != AnsiString("none"))
{ // jeœli jest powi¹zana komórka pamiêci
tmp = FindGroundNode(EventLauncher->asMemCellName, TP_MEMCELL);
if (tmp)
EventLauncher->MemCell = tmp->MemCell; // jeœli znaleziona, dopisaæ
else
MessageBox(0, "Cannot find Memory Cell for Event Launcher", "Error", MB_OK);
}
else
EventLauncher->MemCell = NULL;
EventLauncher->Event1 = (EventLauncher->asEvent1Name != AnsiString("none")) ?
FindEvent(EventLauncher->asEvent1Name) :
NULL;
EventLauncher->Event2 = (EventLauncher->asEvent2Name != AnsiString("none")) ?
FindEvent(EventLauncher->asEvent2Name) :
NULL;
}
return true;
}
TTrack * TGround::FindTrack(vector3 Point, int &iConnection, TGroundNode *Exclude)
{ // wyszukiwanie innego toru koñcz¹cego siê w (Point)
TTrack *Track;
TGroundNode *Current;
TTrack *tmp;
iConnection = -1;
TSubRect *sr;
// najpierw szukamy w okolicznych segmentach
int c = GetColFromX(Point.x);
int r = GetRowFromZ(Point.z);
if ((sr = FastGetSubRect(c, r)) != NULL) // 75% torów jest w tym samym sektorze
if ((tmp = sr->FindTrack(&Point, iConnection, Exclude->pTrack)) != NULL)
return tmp;
int i, x, y;
for (i = 1; i < 9;
++i) // sektory w kolejnoœci odleg³oœci, 4 jest tu wystarczaj¹ce, 9 na wszelki wypadek
{ // niemal wszystkie pod³¹czone tory znajduj¹ siê w s¹siednich 8 sektorach
x = SectorOrder[i].x;
y = SectorOrder[i].y;
if ((sr = FastGetSubRect(c + y, r + x)) != NULL)
if ((tmp = sr->FindTrack(&Point, iConnection, Exclude->pTrack)) != NULL)
return tmp;
if (x)
if ((sr = FastGetSubRect(c + y, r - x)) != NULL)
if ((tmp = sr->FindTrack(&Point, iConnection, Exclude->pTrack)) != NULL)
return tmp;
if (y)
if ((sr = FastGetSubRect(c - y, r + x)) != NULL)
if ((tmp = sr->FindTrack(&Point, iConnection, Exclude->pTrack)) != NULL)
return tmp;
if ((sr = FastGetSubRect(c - y, r - x)) != NULL)
if ((tmp = sr->FindTrack(&Point, iConnection, Exclude->pTrack)) != NULL)
return tmp;
}
#if 0
//wyszukiwanie czo³gowe (po wszystkich jak leci) - nie ma chyba sensu
for (Current=nRootOfType[TP_TRACK];Current;Current=Current->Next)
{
if ((Current->iType==TP_TRACK) && (Current!=Exclude))
{
iConnection=Current->pTrack->TestPoint(&Point);
if (iConnection>=0) return Current->pTrack;
}
}
#endif
return NULL;
}
TTraction * TGround::FindTraction(vector3 *Point, int &iConnection, TGroundNode *Exclude)
{ // wyszukiwanie innego przês³a koñcz¹cego siê w (Point)
TTraction *Traction;
TGroundNode *Current;
TTraction *tmp;
iConnection = -1;
TSubRect *sr;
// najpierw szukamy w okolicznych segmentach
int c = GetColFromX(Point->x);
int r = GetRowFromZ(Point->z);
if ((sr = FastGetSubRect(c, r)) != NULL) // wiêkszoœæ bêdzie w tym samym sektorze
if ((tmp = sr->FindTraction(Point, iConnection, Exclude->hvTraction)) != NULL)
return tmp;
int i, x, y;
for (i = 1; i < 9;
++i) // sektory w kolejnoœci odleg³oœci, 4 jest tu wystarczaj¹ce, 9 na wszelki wypadek
{ // wszystkie przês³a powinny zostaæ znajdowaæ siê w s¹siednich 8 sektorach
x = SectorOrder[i].x;
y = SectorOrder[i].y;
if ((sr = FastGetSubRect(c + y, r + x)) != NULL)
if ((tmp = sr->FindTraction(Point, iConnection, Exclude->hvTraction)) != NULL)
return tmp;
if (x & y)
{
if ((sr = FastGetSubRect(c + y, r - x)) != NULL)
if ((tmp = sr->FindTraction(Point, iConnection, Exclude->hvTraction)) != NULL)
return tmp;
if ((sr = FastGetSubRect(c - y, r + x)) != NULL)
if ((tmp = sr->FindTraction(Point, iConnection, Exclude->hvTraction)) != NULL)
return tmp;
}
if ((sr = FastGetSubRect(c - y, r - x)) != NULL)
if ((tmp = sr->FindTraction(Point, iConnection, Exclude->hvTraction)) != NULL)
return tmp;
}
return NULL;
};
TTraction * TGround::TractionNearestFind(vector3 &p, int dir, TGroundNode *n)
{ // wyszukanie najbli¿szego do (p) przês³a o tej samej nazwie sekcji (ale innego ni¿ pod³¹czone)
// oraz zasilanego z kierunku (dir)
TGroundNode *nCurrent, *nBest = NULL;
int i, j, k, zg;
double d, dist = 200.0 * 200.0; //[m] odleg³oœæ graniczna
// najpierw szukamy w okolicznych segmentach
int c = GetColFromX(n->pCenter.x);
int r = GetRowFromZ(n->pCenter.z);
TSubRect *sr;
for (i = -1; i <= 1; ++i) // przegl¹damy 9 najbli¿szych sektorów
for (j = -1; j <= 1; ++j) //
if ((sr = FastGetSubRect(c + i, r + j)) != NULL) // o ile w ogóle sektor jest
for (nCurrent = sr->nRenderWires; nCurrent; nCurrent = nCurrent->nNext3)
if (nCurrent->iType == TP_TRACTION)
if (nCurrent->hvTraction->psSection ==
n->hvTraction->psSection) // jeœli ta sama sekcja
if (nCurrent != n) // ale nie jest tym samym
if (nCurrent->hvTraction !=
n->hvTraction
->hvNext[0]) // ale nie jest bezpoœrednio pod³¹czonym
if (nCurrent->hvTraction != n->hvTraction->hvNext[1])
if (nCurrent->hvTraction->psPower
[k = (DotProduct(
n->hvTraction->vParametric,
nCurrent->hvTraction->vParametric) >= 0 ?
dir ^ 1 :
dir)]) // ma zasilanie z odpowiedniej
// strony
if (nCurrent->hvTraction->fResistance[k] >=
0.0) //¿eby siê nie propagowa³y jakieœ ujemne
{ // znaleziony kandydat do po³¹czenia
d = SquareMagnitude(
p -
nCurrent
->pCenter); // kwadrat odleg³oœci œrodków
if (dist > d)
{ // zapamiêtanie nowego najbli¿szego
dist = d; // nowy rekord odleg³oœci
nBest = nCurrent;
zg = k; // z którego koñca braæ wskaŸnik
// zasilacza
}
}
if (nBest) // jak znalezione przês³o z zasilaniem, to pod³¹czenie "równoleg³e"
{
n->hvTraction->ResistanceCalc(dir, nBest->hvTraction->fResistance[zg],
nBest->hvTraction->psPower[zg]);
// testowo skrzywienie przês³a tak, aby pokazaæ sk¹d ma zasilanie
// if (dir) //1 gdy ci¹g dalszy jest od strony Point2
// n->hvTraction->pPoint3=0.25*(nBest->pCenter+3*(zg?nBest->hvTraction->pPoint4:nBest->hvTraction->pPoint3));
// else
// n->hvTraction->pPoint4=0.25*(nBest->pCenter+3*(zg?nBest->hvTraction->pPoint4:nBest->hvTraction->pPoint3));
}
return (nBest ? nBest->hvTraction : NULL);
};
bool TGround::AddToQuery(TEvent *Event, TDynamicObject *Node)
{
if (Event->bEnabled) // jeœli mo¿e byæ dodany do kolejki (nie u¿ywany w skanowaniu)
if (!Event->iQueued) // jeœli nie dodany jeszcze do kolejki
{ // kolejka eventów jest posortowana wzglêdem (fStartTime)
Event->Activator = Node;
if (Event->Type == tp_AddValues ? (Event->fDelay == 0.0) : false)
{ // eventy AddValues trzeba wykonywaæ natychmiastowo, inaczej kolejka mo¿e zgubiæ
// jakieœ dodawanie
// Ra: kopiowanie wykonania tu jest bez sensu, lepiej by by³o wydzieliæ funkcjê
// wykonuj¹c¹ eventy i j¹ wywo³aæ
if (EventConditon(Event))
{ // teraz mog¹ byæ warunki do tych eventów
Event->Params[5].asMemCell->UpdateValues(
Event->Params[0].asText, Event->Params[1].asdouble,
Event->Params[2].asdouble, Event->iFlags);
if (Event->Params[6].asTrack)
{ // McZapkie-100302 - updatevalues oprocz zmiany wartosci robi putcommand dla
// wszystkich 'dynamic' na danym torze
for (int i = 0; i < Event->Params[6].asTrack->iNumDynamics; ++i)
Event->Params[5].asMemCell->PutCommand(
Event->Params[6].asTrack->Dynamics[i]->Mechanik,
&Event->Params[4].nGroundNode->pCenter);
if (DebugModeFlag)
WriteLog("EVENT EXECUTED: AddValues & Track command - " +
AnsiString(Event->Params[0].asText) + " " +
AnsiString(Event->Params[1].asdouble) + " " +
AnsiString(Event->Params[2].asdouble));
}
else if (DebugModeFlag)
WriteLog("EVENT EXECUTED: AddValues - " +
AnsiString(Event->Params[0].asText) + " " +
AnsiString(Event->Params[1].asdouble) + " " +
AnsiString(Event->Params[2].asdouble));
}
Event =
Event
->evJoined; // jeœli jest kolejny o takiej samej nazwie, to idzie do kolejki
}
if (Event)
{ // standardowe dodanie do kolejki
WriteLog("EVENT ADDED TO QUEUE: " + Event->asName +
(Node ? AnsiString(" by " + Node->asName) : AnsiString("")));
Event->fStartTime =
fabs(Event->fDelay) + Timer::GetTime(); // czas od uruchomienia scenerii
if (Event->fRandomDelay > 0.0)
Event->fStartTime += Event->fRandomDelay * random(10000) *
0.0001; // doliczenie losowego czasu opóŸnienia
++Event->iQueued; // zabezpieczenie przed podwójnym dodaniem do kolejki
if (QueryRootEvent ? Event->fStartTime >= QueryRootEvent->fStartTime : false)
QueryRootEvent->AddToQuery(Event); // dodanie gdzieœ w œrodku
else
{ // dodanie z przodu: albo nic nie ma, albo ma byæ wykonany szybciej ni¿ pierwszy
Event->evNext = QueryRootEvent;
QueryRootEvent = Event;
}
}
}
return true;
}
bool TGround::EventConditon(TEvent *e)
{ // sprawdzenie spelnienia warunków dla eventu
if (e->iFlags <= update_only)
return true; // bezwarunkowo
if (e->iFlags & conditional_trackoccupied)
return (!e->Params[9].asTrack->IsEmpty());
else if (e->iFlags & conditional_trackfree)
return (e->Params[9].asTrack->IsEmpty());
else if (e->iFlags & conditional_propability)
{
double rprobability = 1.0 * rand() / RAND_MAX;
WriteLog("Random integer: " + CurrToStr(rprobability) + "/" +
CurrToStr(e->Params[10].asdouble));
return (e->Params[10].asdouble > rprobability);
}
else if (e->iFlags & conditional_memcompare)
{ // porównanie wartoœci
if (tmpEvent->Params[9].asMemCell->Compare(e->Params[10].asText, e->Params[11].asdouble,
e->Params[12].asdouble, e->iFlags))
return true;
else if (Global::iWriteLogEnabled && DebugModeFlag)
{ // nie zgadza siê, wiêc sprawdzmy, co
LogComment = e->Params[9].asMemCell->Text() + AnsiString(" ") +
FloatToStrF(e->Params[9].asMemCell->Value1(), ffFixed, 8, 2) + " " +
FloatToStrF(tmpEvent->Params[9].asMemCell->Value2(), ffFixed, 8, 2) +
" != ";
if (TestFlag(e->iFlags, conditional_memstring))
LogComment += AnsiString(tmpEvent->Params[10].asText);
else
LogComment += "*";
if (TestFlag(tmpEvent->iFlags, conditional_memval1))
LogComment += " " + FloatToStrF(tmpEvent->Params[11].asdouble, ffFixed, 8, 2);
else
LogComment += " *";
if (TestFlag(tmpEvent->iFlags, conditional_memval2))
LogComment += " " + FloatToStrF(tmpEvent->Params[12].asdouble, ffFixed, 8, 2);
else
LogComment += " *";
WriteLog(LogComment.c_str());
}
}
return false;
};
bool TGround::CheckQuery()
{ // sprawdzenie kolejki eventów oraz wykonanie tych, którym czas min¹³
TLocation loc;
int i;
/* //Ra: to w ogóle jakiœ chory kod jest; wygl¹da jak wyszukanie eventu z najlepszym czasem
Double evtime,evlowesttime; //Ra: co to za typ?
//evlowesttime=1000000;
if (QueryRootEvent)
{
OldQRE=QueryRootEvent;
tmpEvent=QueryRootEvent;
}
if (QueryRootEvent)
{
for (i=0;i<90;++i)
{
evtime=((tmpEvent->fStartTime)-(Timer::GetTime())); //pobranie wartoœci zmiennej
if (evtime<evlowesttime)
{
evlowesttime=evtime;
tmp2Event=tmpEvent;
}
if (tmpEvent->Next)
tmpEvent=tmpEvent->Next;
else
i=100;
}
if (OldQRE!=tmp2Event)
{
QueryRootEvent->AddToQuery(QueryRootEvent);
QueryRootEvent=tmp2Event;
}
}
*/
/*
if (QueryRootEvent)
{//wypisanie kolejki
tmpEvent=QueryRootEvent;
WriteLog("--> Event queue:");
while (tmpEvent)
{
WriteLog(tmpEvent->asName+" "+AnsiString(tmpEvent->fStartTime));
tmpEvent=tmpEvent->Next;
}
}
*/
while (QueryRootEvent ? QueryRootEvent->fStartTime < Timer::GetTime() : false)
{ // eventy s¹ posortowana wg czasu wykonania
tmpEvent = QueryRootEvent; // wyjêcie eventu z kolejki
if (QueryRootEvent->evJoined) // jeœli jest kolejny o takiej samej nazwie
{ // to teraz on bêdzie nastêpny do wykonania
QueryRootEvent = QueryRootEvent->evJoined; // nastêpny bêdzie ten doczepiony
QueryRootEvent->evNext = tmpEvent->evNext; // pamiêtaj¹c o nastêpnym z kolejki
QueryRootEvent->fStartTime =
tmpEvent->fStartTime; // czas musi byæ ten sam, bo nie jest aktualizowany
QueryRootEvent->Activator = tmpEvent->Activator; // pojazd aktywuj¹cy
// w sumie mo¿na by go dodaæ normalnie do kolejki, ale trzeba te po³¹czone posortowaæ wg
// czasu wykonania
}
else // a jak nazwa jest unikalna, to kolejka idzie dalej
QueryRootEvent = QueryRootEvent->evNext; // NULL w skrajnym przypadku
if (tmpEvent->bEnabled)
{ // w zasadzie te wy³¹czone s¹ skanowane i nie powinny siê nigdy w kolejce znaleŸæ
WriteLog("EVENT LAUNCHED: " + tmpEvent->asName +
(tmpEvent->Activator ? AnsiString(" by " + tmpEvent->Activator->asName) :
AnsiString("")));
switch (tmpEvent->Type)
{
case tp_CopyValues: // skopiowanie wartoœci z innej komórki
tmpEvent->Params[5].asMemCell->UpdateValues(
tmpEvent->Params[9].asMemCell->Text(), tmpEvent->Params[9].asMemCell->Value1(),
tmpEvent->Params[9].asMemCell->Value2(),
tmpEvent->iFlags // flagi okreœlaj¹, co ma byæ skopiowane
);
// break; //¿eby siê wys³a³o do torów i nie by³o potrzeby na AddValues * 0 0
case tp_AddValues: // ró¿ni siê jedn¹ flag¹ od UpdateValues
case tp_UpdateValues:
if (EventConditon(tmpEvent))
{ // teraz mog¹ byæ warunki do tych eventów
if (tmpEvent->Type != tp_CopyValues) // dla CopyValues zrobi³o siê wczeœniej
tmpEvent->Params[5].asMemCell->UpdateValues(
tmpEvent->Params[0].asText, tmpEvent->Params[1].asdouble,
tmpEvent->Params[2].asdouble, tmpEvent->iFlags);
if (tmpEvent->Params[6].asTrack)
{ // McZapkie-100302 - updatevalues oprocz zmiany wartosci robi putcommand dla
// wszystkich 'dynamic' na danym torze
for (int i = 0; i < tmpEvent->Params[6].asTrack->iNumDynamics; ++i)
tmpEvent->Params[5].asMemCell->PutCommand(
tmpEvent->Params[6].asTrack->Dynamics[i]->Mechanik,
&tmpEvent->Params[4].nGroundNode->pCenter);
if (DebugModeFlag)
WriteLog("Type: UpdateValues & Track command - " +
AnsiString(tmpEvent->Params[0].asText) + " " +
AnsiString(tmpEvent->Params[1].asdouble) + " " +
AnsiString(tmpEvent->Params[2].asdouble));
}
else if (DebugModeFlag)
WriteLog("Type: UpdateValues - " + AnsiString(tmpEvent->Params[0].asText) +
" " + AnsiString(tmpEvent->Params[1].asdouble) + " " +
AnsiString(tmpEvent->Params[2].asdouble));
}
break;
case tp_GetValues:
if (tmpEvent->Activator)
{
// loc.X= -tmpEvent->Params[8].nGroundNode->pCenter.x;
// loc.Y= tmpEvent->Params[8].nGroundNode->pCenter.z;
// loc.Z= tmpEvent->Params[8].nGroundNode->pCenter.y;
if (Global::iMultiplayer) // potwierdzenie wykonania dla serwera (odczyt
// semafora ju¿ tak nie dzia³a)
WyslijEvent(tmpEvent->asName, tmpEvent->Activator->GetName());
// tmpEvent->Params[9].asMemCell->PutCommand(tmpEvent->Activator->Mechanik,loc);
tmpEvent->Params[9].asMemCell->PutCommand(
tmpEvent->Activator->Mechanik, &tmpEvent->Params[8].nGroundNode->pCenter);
}
WriteLog("Type: GetValues");
break;
case tp_PutValues:
if (tmpEvent->Activator)
{
loc.X =
-tmpEvent->Params[3].asdouble; // zamiana, bo fizyka ma inaczej ni¿ sceneria
loc.Y = tmpEvent->Params[5].asdouble;
loc.Z = tmpEvent->Params[4].asdouble;
if (tmpEvent->Activator->Mechanik) // przekazanie rozkazu do AI
tmpEvent->Activator->Mechanik->PutCommand(
tmpEvent->Params[0].asText, tmpEvent->Params[1].asdouble,
tmpEvent->Params[2].asdouble, loc);
else
{ // przekazanie do pojazdu
tmpEvent->Activator->MoverParameters->PutCommand(
tmpEvent->Params[0].asText, tmpEvent->Params[1].asdouble,
tmpEvent->Params[2].asdouble, loc);
}
}
WriteLog("Type: PutValues");
break;
case tp_Lights:
if (tmpEvent->Params[9].asModel)
for (i = 0; i < iMaxNumLights; i++)
if (tmpEvent->Params[i].asdouble >= 0) //-1 zostawia bez zmiany
tmpEvent->Params[9].asModel->LightSet(
i, tmpEvent->Params[i].asdouble); // teraz te¿ u³amek
break;
case tp_Visible:
if (tmpEvent->Params[9].nGroundNode)
tmpEvent->Params[9].nGroundNode->bVisible = (tmpEvent->Params[i].asInt > 0);
break;
case tp_Velocity:
Error("Not implemented yet :(");
break;
case tp_Exit:
MessageBox(0, tmpEvent->asNodeName.c_str(), " THE END ", MB_OK);
Global::iTextMode = -1; // wy³¹czenie takie samo jak sekwencja F10 -> Y
return false;
case tp_Sound:
switch (tmpEvent->Params[0].asInt)
{ // trzy mo¿liwe przypadki:
case 0:
tmpEvent->Params[9].tsTextSound->Stop();
break;
case 1:
tmpEvent->Params[9].tsTextSound->Play(
1, 0, true, tmpEvent->Params[9].tsTextSound->vSoundPosition);
break;
case -1:
tmpEvent->Params[9].tsTextSound->Play(
1, DSBPLAY_LOOPING, true, tmpEvent->Params[9].tsTextSound->vSoundPosition);
break;
}
break;
case tp_Disable:
Error("Not implemented yet :(");
break;
case tp_Animation: // Marcin: dorobic translacje - Ra: dorobi³em ;-)
if (tmpEvent->Params[0].asInt == 1)
tmpEvent->Params[9].asAnimContainer->SetRotateAnim(
vector3(tmpEvent->Params[1].asdouble, tmpEvent->Params[2].asdouble,
tmpEvent->Params[3].asdouble),
tmpEvent->Params[4].asdouble);
else if (tmpEvent->Params[0].asInt == 2)
tmpEvent->Params[9].asAnimContainer->SetTranslateAnim(
vector3(tmpEvent->Params[1].asdouble, tmpEvent->Params[2].asdouble,
tmpEvent->Params[3].asdouble),
tmpEvent->Params[4].asdouble);
else if (tmpEvent->Params[0].asInt == 4)
tmpEvent->Params[9].asModel->AnimationVND(
tmpEvent->Params[8].asPointer,
tmpEvent->Params[1].asdouble, // tu mog¹ byæ dodatkowe parametry, np. od-do
tmpEvent->Params[2].asdouble, tmpEvent->Params[3].asdouble,
tmpEvent->Params[4].asdouble);
break;
case tp_Switch:
if (tmpEvent->Params[9].asTrack)
tmpEvent->Params[9].asTrack->Switch(tmpEvent->Params[0].asInt,
tmpEvent->Params[1].asdouble,
tmpEvent->Params[2].asdouble);
if (Global::iMultiplayer) // dajemy znaæ do serwera o prze³o¿eniu
WyslijEvent(tmpEvent->asName, ""); // wys³anie nazwy eventu prze³¹czajacego
// Ra: bardziej by siê przyda³a nazwa toru, ale nie ma do niej st¹d dostêpu
break;
case tp_TrackVel:
if (tmpEvent->Params[9].asTrack)
{ // prêdkoœæ na zwrotnicy mo¿e byæ ograniczona z góry we wpisie, wiêkszej siê nie
// ustawi eventem
WriteLog("type: TrackVel");
// WriteLog("Vel: ",tmpEvent->Params[0].asdouble);
tmpEvent->Params[9].asTrack->VelocitySet(tmpEvent->Params[0].asdouble);
if (DebugModeFlag) // wyœwietlana jest ta faktycznie ustawiona
WriteLog("vel: ", tmpEvent->Params[9].asTrack->VelocityGet());
}
break;
case tp_DynVel:
Error("Event \"DynVel\" is obsolete");
break;
case tp_Multiple:
{
bCondition = EventConditon(tmpEvent);
if (bCondition || (tmpEvent->iFlags &
conditional_anyelse)) // warunek spelniony albo by³o u¿yte else
{
WriteLog("Multiple passed");
for (i = 0; i < 8; ++i)
{ // dodawane do kolejki w kolejnoœci zapisania
if (tmpEvent->Params[i].asEvent)
if (bCondition != bool(tmpEvent->iFlags & (conditional_else << i)))
{
if (tmpEvent->Params[i].asEvent != tmpEvent)
AddToQuery(tmpEvent->Params[i].asEvent,
tmpEvent->Activator); // normalnie dodaæ
else // jeœli ma byæ rekurencja
if (tmpEvent->fDelay >=
5.0) // to musi mieæ sensowny okres powtarzania
if (tmpEvent->iQueued < 2)
{ // trzeba zrobiæ wyj¹tek, aby event móg³ siê sam dodaæ do
// kolejki, raz ju¿ jest, ale bêdzie usuniêty
// pêtla eventowa mo¿e byæ uruchomiona wiele razy, ale tylko
// pierwsze uruchomienie zadzia³a
tmpEvent->iQueued =
0; // tymczasowo, aby by³ ponownie dodany do kolejki
AddToQuery(tmpEvent, tmpEvent->Activator);
tmpEvent->iQueued =
2; // kolejny raz ju¿ absolutnie nie dodawaæ
}
}
}
if (Global::iMultiplayer) // dajemy znaæ do serwera o wykonaniu
if ((tmpEvent->iFlags & conditional_anyelse) ==
0) // jednoznaczne tylko, gdy nie by³o else
{
if (tmpEvent->Activator)
WyslijEvent(tmpEvent->asName, tmpEvent->Activator->GetName());
else
WyslijEvent(tmpEvent->asName, "");
}
}
}
break;
case tp_WhoIs: // pobranie nazwy poci¹gu do komórki pamiêci
if (tmpEvent->iFlags & update_load)
{ // jeœli pytanie o ³adunek
if (tmpEvent->iFlags & update_memadd) // jeœli typ pojazdu
tmpEvent->Params[9].asMemCell->UpdateValues(
tmpEvent->Activator->MoverParameters->TypeName.c_str(), // typ pojazdu
0, // na razie nic
0, // na razie nic
tmpEvent->iFlags &
(update_memstring | update_memval1 | update_memval2));
else // jeœli parametry ³adunku
tmpEvent->Params[9].asMemCell->UpdateValues(
tmpEvent->Activator->MoverParameters->LoadType != "" ?
tmpEvent->Activator->MoverParameters->LoadType.c_str() :
"none", // nazwa ³adunku
tmpEvent->Activator->MoverParameters->Load, // aktualna iloϾ
tmpEvent->Activator->MoverParameters->MaxLoad, // maksymalna iloϾ
tmpEvent->iFlags &
(update_memstring | update_memval1 | update_memval2));
}
else if (tmpEvent->iFlags & update_memadd)
{ // jeœli miejsce docelowe pojazdu
tmpEvent->Params[9].asMemCell->UpdateValues(
tmpEvent->Activator->asDestination.c_str(), // adres docelowy
tmpEvent->Activator->DirectionGet(), // kierunek pojazdu wzglêdem czo³a
// sk³adu (1=zgodny,-1=przeciwny)
tmpEvent->Activator->MoverParameters
->Power, // moc pojazdu silnikowego: 0 dla wagonu
tmpEvent->iFlags & (update_memstring | update_memval1 | update_memval2));
}
else if (tmpEvent->Activator->Mechanik)
if (tmpEvent->Activator->Mechanik->Primary())
{ // tylko jeœli ktoœ tam siedzi - nie powinno dotyczyæ pasa¿era!
tmpEvent->Params[9].asMemCell->UpdateValues(
tmpEvent->Activator->Mechanik->TrainName().c_str(),
tmpEvent->Activator->Mechanik->StationCount() -
tmpEvent->Activator->Mechanik
->StationIndex(), // ile przystanków do koñca
tmpEvent->Activator->Mechanik->IsStop() ? 1 :
0, // 1, gdy ma tu zatrzymanie
tmpEvent->iFlags);
WriteLog("Train detected: " + tmpEvent->Activator->Mechanik->TrainName());
}
break;
case tp_LogValues: // zapisanie zawartoœci komórki pamiêci do logu
if (tmpEvent->Params[9].asMemCell) // jeœli by³a podana nazwa komórki
WriteLog("Memcell \"" + tmpEvent->asNodeName + "\": " +
tmpEvent->Params[9].asMemCell->Text() + " " +
tmpEvent->Params[9].asMemCell->Value1() + " " +
tmpEvent->Params[9].asMemCell->Value2());
else // lista wszystkich
for (TGroundNode *Current = nRootOfType[TP_MEMCELL]; Current;
Current = Current->nNext)
WriteLog("Memcell \"" + Current->asName + "\": " +
Current->MemCell->Text() + " " + Current->MemCell->Value1() + " " +
Current->MemCell->Value2());
break;
case tp_Voltage: // zmiana napiêcia w zasilaczu (TractionPowerSource)
if (tmpEvent->Params[9].psPower)
{ // na razie takie chamskie ustawienie napiêcia zasilania
WriteLog("type: Voltage");
tmpEvent->Params[9].psPower->VoltageSet(tmpEvent->Params[0].asdouble);
}
case tp_Friction: // zmiana tarcia na scenerii
{ // na razie takie chamskie ustawienie napiêcia zasilania
WriteLog("type: Friction");
Global::fFriction = (tmpEvent->Params[0].asdouble);
}
break;
case tp_Message: // wyœwietlenie komunikatu
break;
} // switch (tmpEvent->Type)
} // if (tmpEvent->bEnabled)
--tmpEvent->iQueued; // teraz moze byæ ponownie dodany do kolejki
/*
if (QueryRootEvent->eJoined) //jeœli jest kolejny o takiej samej nazwie
{//to teraz jego dajemy do wykonania
QueryRootEvent->eJoined->Next=QueryRootEvent->Next; //pamiêtaj¹c o nastêpnym z kolejki
QueryRootEvent->eJoined->fStartTime=QueryRootEvent->fStartTime; //czas musi byæ ten sam,
bo nie jest aktualizowany
//QueryRootEvent->fStartTime=0;
QueryRootEvent=QueryRootEvent->eJoined; //a wykonaæ ten doczepiony
}
else
{//a jak nazwa jest unikalna, to kolejka idzie dalej
//QueryRootEvent->fStartTime=0;
QueryRootEvent=QueryRootEvent->Next; //NULL w skrajnym przypadku
}
*/
} // while
return true;
}
void TGround::OpenGLUpdate(HDC hDC)
{
SwapBuffers(hDC); // swap buffers (double buffering)
};
void TGround::UpdatePhys(double dt, int iter)
{ // aktualizacja fizyki sta³ym krokiem: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeñ
for (TGroundNode *Current = nRootOfType[TP_TRACTIONPOWERSOURCE]; Current;
Current = Current->nNext)
Current->psTractionPowerSource->Update(dt * iter); // zerowanie sumy pr¹dów
};
bool TGround::Update(double dt, int iter)
{ // aktualizacja animacji krokiem FPS: dt=krok czasu [s], dt*iter=czas od ostatnich przeliczeñ
if (dt == 0.0)
{ // jeœli za³¹czona jest pauza, to tylko obs³u¿yæ ruch w kabinie trzeba
return true;
}
// Ra: w zasadzie to trzeba by utworzyæ oddzieln¹ listê taboru do liczenia fizyki
// na któr¹ by siê zapisywa³y wszystkie pojazdy bêd¹ce w ruchu
// pojazdy stoj¹ce nie potrzebuj¹ aktualizacji, chyba ¿e np. ktoœ im zmieni nastawê hamulca
// oddzieln¹ listê mo¿na by zrobiæ na pojazdy z napêdem, najlepiej posortowan¹ wg typu napêdu
if (iter > 1) // ABu: ponizsze wykonujemy tylko jesli wiecej niz jedna iteracja
{ // pierwsza iteracja i wyznaczenie stalych:
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
{ //
Current->DynamicObject->MoverParameters->ComputeConstans();
Current->DynamicObject->CoupleDist();
Current->DynamicObject->UpdateForce(dt, dt, false);
}
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
Current->DynamicObject->FastUpdate(dt);
// pozostale iteracje
for (int i = 1; i < (iter - 1); ++i) // jeœli iter==5, to wykona siê 3 razy
{
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
Current->DynamicObject->UpdateForce(dt, dt, false);
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
Current->DynamicObject->FastUpdate(dt);
}
// ABu 200205: a to robimy tylko raz, bo nie potrzeba wiêcej
// Winger 180204 - pantografy
double dt1 = dt * iter; // ca³kowity czas
UpdatePhys(dt1, 1);
TAnimModel::AnimUpdate(dt1); // wykonanie zakolejkowanych animacji
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
{ // Ra: zmieniæ warunek na sprawdzanie pantografów w jednej zmiennej: czy pantografy i czy
// podniesione
if (Current->DynamicObject->MoverParameters->EnginePowerSource.SourceType ==
CurrentCollector)
GetTraction(Current->DynamicObject); // poszukiwanie drutu dla pantografów
Current->DynamicObject->UpdateForce(dt, dt1, true); //,true);
}
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
Current->DynamicObject->Update(dt, dt1); // Ra 2015-01: tylko tu przelicza sieæ
// trakcyjn¹
}
else
{ // jezeli jest tylko jedna iteracja
UpdatePhys(dt, 1);
TAnimModel::AnimUpdate(dt); // wykonanie zakolejkowanych animacji
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
{
if (Current->DynamicObject->MoverParameters->EnginePowerSource.SourceType ==
CurrentCollector)
GetTraction(Current->DynamicObject);
Current->DynamicObject->MoverParameters->ComputeConstans();
Current->DynamicObject->CoupleDist();
Current->DynamicObject->UpdateForce(dt, dt, true); //,true);
}
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
Current->DynamicObject->Update(dt, dt); // Ra 2015-01: tylko tu przelicza sieæ trakcyjn¹
}
if (bDynamicRemove)
{ // jeœli jest coœ do usuniêcia z listy, to trzeba na koñcu
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
if (!Current->DynamicObject->bEnabled)
{
DynamicRemove(Current->DynamicObject); // usuniêcie tego i pod³¹czonych
Current = nRootDynamic; // sprawdzanie listy od pocz¹tku
}
bDynamicRemove = false; // na razie koniec
}
return true;
};
// Winger 170204 - szukanie trakcji nad pantografami
bool TGround::GetTraction(TDynamicObject *model)
{ // aktualizacja drutu zasilaj¹cego dla ka¿dego pantografu, ¿eby odczytaæ napiêcie
// jeœli pojazd siê nie porusza, to nie ma sensu przeliczaæ tego wiêcej ni¿ raz
double fRaParam; // parametr równania parametrycznego odcinka drutu
double fVertical; // odleg³oœæ w pionie; musi byæ w zasiêgu ruchu "pionowego" pantografu
double fHorizontal; // odleg³oœæ w bok; powinna byæ mniejsza ni¿ pó³ szerokoœci pantografu
vector3 vLeft, vUp, vFront, dwys;
vector3 pant0;
vector3 vParam; // wspó³czynniki równania parametrycznego drutu
vector3 vStyk; // punkt przebicia drutu przez p³aszczyznê ruchu pantografu
vector3 vGdzie; // wektor po³o¿enia drutu wzglêdem pojazdu
vFront = model->VectorFront(); // wektor normalny dla p³aszczyzny ruchu pantografu
vUp = model->VectorUp(); // wektor pionu pud³a (pochylony od pionu na przechy³ce)
vLeft = model->VectorLeft(); // wektor odleg³oœci w bok (odchylony od poziomu na przechy³ce)
dwys = model->GetPosition(); // wspó³rzêdne œrodka pojazdu
TAnimPant *p; // wskaŸnik do obiektu danych pantografu
for (int k = 0; k < model->iAnimType[ANIM_PANTS]; ++k)
{ // pêtla po pantografach
p = model->pants[k].fParamPants;
if (k ? model->MoverParameters->PantRearUp : model->MoverParameters->PantFrontUp)
{ // jeœli pantograf podniesiony
pant0 = dwys + (vLeft * p->vPos.z) + (vUp * p->vPos.y) + (vFront * p->vPos.x);
if (p->hvPowerWire)
{ // je¿eli znamy drut z poprzedniego przebiegu
int n = 30; //¿eby siê nie zapêtli³
while (p->hvPowerWire)
{ // powtarzane a¿ do znalezienia odpowiedniego odcinka na liœcie dwukierunkowej
// obliczamy wyraz wolny równania p³aszczyzny (to miejsce nie jest odpowienie)
vParam = p->hvPowerWire->vParametric; // wspó³czynniki równania parametrycznego
fRaParam = -DotProduct(pant0, vFront);
// podstawiamy równanie parametryczne drutu do równania p³aszczyzny pantografu
// vFront.x*(t1x+t*vParam.x)+vFront.y*(t1y+t*vParam.y)+vFront.z*(t1z+t*vParam.z)+fRaDist=0;
fRaParam = -(DotProduct(p->hvPowerWire->pPoint1, vFront) + fRaParam) /
DotProduct(vParam, vFront);
if (fRaParam <
-0.001) // histereza rzêdu 7cm na 70m typowego przês³a daje 1 promil
p->hvPowerWire = p->hvPowerWire->hvNext[0];
else if (fRaParam > 1.001)
p->hvPowerWire = p->hvPowerWire->hvNext[1];
else if (p->hvPowerWire->iLast & 3)
{ // dla ostatniego i przedostatniego przês³a wymuszamy szukanie innego
p->hvPowerWire = NULL; // nie to, ¿e nie ma, ale trzeba sprawdziæ inne
// p->fHorizontal=fHorizontal; //zapamiêtanie po³o¿enia drutu
break;
}
else if (p->hvPowerWire->hvParallel)
{ // jeœli przês³o tworzy bie¿niê wspóln¹, to trzeba sprawdziæ pozosta³e
p->hvPowerWire = NULL; // nie to, ¿e nie ma, ale trzeba sprawdziæ inne
// p->fHorizontal=fHorizontal; //zapamiêtanie po³o¿enia drutu
break; // tymczasowo dla bie¿ni wspólnych poszukiwanie po ca³oœci
}
else
{ // jeœli t jest w przedziale, wyznaczyæ odleg³oœæ wzd³u¿ wektorów vUp i vLeft
vStyk = p->hvPowerWire->pPoint1 + fRaParam * vParam; // punkt styku
// p³aszczyzny z drutem
// (dla generatora ³uku
// el.)
vGdzie = vStyk - pant0; // wektor
// odleg³oœæ w pionie musi byæ w zasiêgu ruchu "pionowego" pantografu
fVertical = DotProduct(
vGdzie, vUp); // musi siê mieœciæ w przedziale ruchu pantografu
// odleg³oœæ w bok powinna byæ mniejsza ni¿ pó³ szerokoœci pantografu
fHorizontal = fabs(DotProduct(vGdzie, vLeft)) -
p->fWidth; // to siê musi mieœciæ w przedziale zale¿nym od
// szerokoœci pantografu
// jeœli w pionie albo w bok jest za daleko, to dany drut jest nieu¿yteczny
if (fHorizontal > 0) // 0.635 dla AKP-1 AKP-4E
{ // drut wyszed³ poza zakres roboczy, ale jeszcze jest nabie¿nik -
// pantograf siê unosi bez utraty pr¹du
if (fHorizontal > p->fWidthExtra) // czy wyszed³ za nabie¿nik
{
p->hvPowerWire = NULL; // dotychczasowy drut nie liczy siê
// p->fHorizontal=fHorizontal; //zapamiêtanie po³o¿enia drutu
}
else
{ // problem jest, gdy nowy drut jest wy¿ej, wtedy pantograf od³¹cza siê
// od starego, a na podniesienie do nowego potrzebuje czasu
p->PantTraction =
fVertical +
0.15 * fHorizontal / p->fWidthExtra; // na razie liniowo na
// nabie¿niku, dok³adnoœæ
// poprawi siê póŸniej
// p->fHorizontal=fHorizontal; //zapamiêtanie po³o¿enia drutu
}
}
else
{ // po wyselekcjonowaniu drutu, przypisaæ go do toru, ¿eby nie trzeba by³o
// szukaæ
// dla 3 koñcowych przêse³ sprawdziæ wszystkie dostêpne przês³a
// bo mog¹ byæ umieszczone równolegle nad torem - po³¹czyæ w pierœcieñ
// najlepiej, jakby odcinki równoleg³e by³y oznaczone we wpisach
// WriteLog("Drut: "+AnsiString(fHorizontal)+" "+AnsiString(fVertical));
p->PantTraction = fVertical;
// p->fHorizontal=fHorizontal; //zapamiêtanie po³o¿enia drutu
break; // koniec pêtli, aktualny drut pasuje
}
}
if (--n <= 0) // coœ za d³ugo to szukanie trwa
p->hvPowerWire = NULL;
}
}
if (!p->hvPowerWire) // else nie, bo móg³ zostaæ wyrzucony
{ // poszukiwanie po okolicznych sektorach
int c = GetColFromX(dwys.x) + 1;
int r = GetRowFromZ(dwys.z) + 1;
TSubRect *tmp;
TGroundNode *node;
p->PantTraction = 5.0; // taka za du¿a wartoœæ
for (int j = r - 2; j <= r; j++)
for (int i = c - 2; i <= c; i++)
{ // poszukiwanie po najbli¿szych sektorach niewiele da przy wiêkszym
// zagêszczeniu
tmp = FastGetSubRect(i, j);
if (tmp)
{ // dany sektor mo¿e nie mieæ nic w œrodku
for (node = tmp->nRenderWires; node;
node = node->nNext3) // nastêpny z grupy
if (node->iType ==
TP_TRACTION) // w grupie tej s¹ druty oraz inne linie
{
vParam =
node->hvTraction
->vParametric; // wspó³czynniki równania parametrycznego
fRaParam = -DotProduct(pant0, vFront);
fRaParam = -(DotProduct(node->hvTraction->pPoint1, vFront) +
fRaParam) /
DotProduct(vParam, vFront);
if ((fRaParam >= -0.001) ? (fRaParam <= 1.001) : false)
{ // jeœli tylko jest w przedziale, wyznaczyæ odleg³oœæ wzd³u¿
// wektorów vUp i vLeft
vStyk = node->hvTraction->pPoint1 +
fRaParam * vParam; // punkt styku p³aszczyzny z
// drutem (dla generatora ³uku
// el.)
vGdzie = vStyk - pant0; // wektor
fVertical = DotProduct(
vGdzie,
vUp); // musi siê mieœciæ w przedziale ruchu pantografu
if (fVertical >= 0.0) // jeœli ponad pantografem (bo mo¿e
// ³apaæ druty spod wiaduktu)
if (Global::bEnableTraction ?
fVertical < p->PantWys - 0.15 :
false) // jeœli drut jest ni¿ej ni¿ 15cm pod
// œlizgiem
{ // prze³¹czamy w tryb po³amania, o ile jedzie;
// (bEnableTraction) aby da³o siê jeŸdziæ na
// koœlawych
// sceneriach
fHorizontal = fabs(DotProduct(vGdzie, vLeft)) -
p->fWidth; // i do tego jeszcze
// wejdzie pod œlizg
if (fHorizontal <= 0.0) // 0.635 dla AKP-1 AKP-4E
{
p->PantWys =
-1.0; // ujemna liczba oznacza po³amanie
p->hvPowerWire = NULL; // bo inaczej siê zasila
// w nieskoñczonoœæ z
// po³amanego
// p->fHorizontal=fHorizontal; //zapamiêtanie
// po³o¿enia drutu
if (model->MoverParameters->EnginePowerSource
.CollectorParameters.CollectorsNo >
0) // liczba pantografów
--model->MoverParameters->EnginePowerSource
.CollectorParameters
.CollectorsNo; // teraz bêdzie
// mniejsza
if (DebugModeFlag)
ErrorLog(
"Pant. break: at " +
FloatToStrF(pant0.x, ffFixed, 7, 2) +
" " +
FloatToStrF(pant0.y, ffFixed, 7, 2) +
" " +
FloatToStrF(pant0.z, ffFixed, 7, 2));
}
}
else if (fVertical < p->PantTraction) // ale ni¿ej, ni¿
// poprzednio
// znaleziony
{
fHorizontal =
fabs(DotProduct(vGdzie, vLeft)) - p->fWidth;
if (fHorizontal <= 0.0) // 0.635 dla AKP-1 AKP-4E
{ // to siê musi mieœciæ w przedziale zaleznym od
// szerokoœci pantografu
p->hvPowerWire =
node->hvTraction; // jakiœ znaleziony
p->PantTraction =
fVertical; // zapamiêtanie nowej wysokoœci
// p->fHorizontal=fHorizontal; //zapamiêtanie
// po³o¿enia drutu
}
else if (fHorizontal <
p->fWidthExtra) // czy zmieœci³ siê w
// zakresie nabie¿nika?
{ // problem jest, gdy nowy drut jest wy¿ej, wtedy
// pantograf od³¹cza siê od starego, a na
// podniesienie do nowego potrzebuje czasu
fVertical +=
0.15 * fHorizontal /
p->fWidthExtra; // korekta wysokoœci o
// nabie¿nik - drut nad
// nabie¿nikiem jest
// geometrycznie jakby nieco
// wy¿ej
if (fVertical <
p->PantTraction) // gdy po korekcie jest
// ni¿ej, ni¿ poprzednio
// znaleziony
{ // gdyby to wystarczy³o, to mo¿emy go uznaæ
p->hvPowerWire =
node->hvTraction; // mo¿e byæ
p->PantTraction =
fVertical; // na razie liniowo na
// nabie¿niku, dok³adnoœæ
// poprawi siê póŸniej
// p->fHorizontal=fHorizontal;
// //zapamiêtanie po³o¿enia drutu
}
}
}
} // warunek na parametr drutu <0;1>
} // pêtla po drutach
} // sektor istnieje
} // pêtla po sektorach
} // koniec poszukiwania w sektorach
if (!p->hvPowerWire) // jeœli drut nie znaleziony
if (!Global::bLiveTraction) // ale mo¿na oszukiwaæ
model->pants[k].fParamPants->PantTraction = 1.4; // to dajemy coœ tam dla picu
} // koniec obs³ugi podniesionego
else
p->hvPowerWire = NULL; // pantograf opuszczony
}
// if (model->fWahaczeAmp<model->MoverParameters->DistCounter)
//{//nieu¿ywana normalnie zmienna ogranicza powtórzone logowania
// model->fWahaczeAmp=model->MoverParameters->DistCounter;
// ErrorLog(FloatToStrF(1000.0*model->MoverParameters->DistCounter,ffFixed,7,3)+","+FloatToStrF(p->PantTraction,ffFixed,7,3)+","+FloatToStrF(p->fHorizontal,ffFixed,7,3)+","+FloatToStrF(p->PantWys,ffFixed,7,3)+","+AnsiString(p->hvPowerWire?1:0));
// //
// if (p->fHorizontal>1.0)
//{
// //Global::iPause|=1; //zapauzowanie symulacji
// Global::fTimeSpeed=1; //spowolnienie czasu do obejrzenia pantografu
// return true; //³apacz
//}
//}
return true;
};
bool TGround::RenderDL(vector3 pPosition)
{ // renderowanie scenerii z Display List - faza nieprzezroczystych
glDisable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.45); // im mniejsza wartoœæ, tym wiêksza ramka, domyœlnie 0.1f
++TGroundRect::iFrameNumber; // zwiêszenie licznika ramek (do usuwniania nadanimacji)
CameraDirection.x = sin(Global::pCameraRotation); // wektor kierunkowy
CameraDirection.z = cos(Global::pCameraRotation);
int tr, tc;
TGroundNode *node;
glColor3f(1.0f, 1.0f, 1.0f);
glEnable(GL_LIGHTING);
int n = 2 * iNumSubRects; //(2*==2km) promieñ wyœwietlanej mapy w sektorach
int c = GetColFromX(pPosition.x);
int r = GetRowFromZ(pPosition.z);
TSubRect *tmp;
for (node = srGlobal.nRenderHidden; node; node = node->nNext3)
node->RenderHidden(); // rednerowanie globalnych (nie za czêsto?)
int i, j, k;
// renderowanie czo³gowe dla obiektów aktywnych a niewidocznych
for (j = r - n; j <= r + n; j++)
for (i = c - n; i <= c + n; i++)
if ((tmp = FastGetSubRect(i, j)) != NULL)
{
tmp->LoadNodes(); // oznaczanie aktywnych sektorów
for (node = tmp->nRenderHidden; node; node = node->nNext3)
node->RenderHidden();
tmp->RenderSounds(); // jeszcze dŸwiêki pojazdów by siê przyda³y, równie¿
// niewidocznych
}
// renderowanie progresywne - zale¿ne od FPS oraz kierunku patrzenia
iRendered = 0; // iloœæ renderowanych sektorów
vector3 direction;
for (k = 0; k < Global::iSegmentsRendered; ++k) // sektory w kolejnoœci odleg³oœci
{ // przerobione na u¿ycie SectorOrder
i = SectorOrder[k].x; // na starcie oba >=0
j = SectorOrder[k].y;
do
{
if (j <= 0)
i = -i; // pierwszy przebieg: j<=0, i>=0; drugi: j>=0, i<=0; trzeci: j<=0, i<=0
// czwarty: j>=0, i>=0;
j = -j; // i oraz j musi byæ zmienione wczeœniej, ¿eby continue dzia³a³o
direction = vector3(i, 0, j); // wektor od kamery do danego sektora
if (LengthSquared3(direction) > 5) // te blisko s¹ zawsze wyœwietlane
{
direction = SafeNormalize(direction); // normalizacja
if (CameraDirection.x * direction.x + CameraDirection.z * direction.z < 0.55)
continue; // pomijanie sektorów poza k¹tem patrzenia
}
Rects[(i + c) / iNumSubRects][(j + r) / iNumSubRects]
.RenderDL(); // kwadrat kilometrowy nie zawsze, bo szkoda FPS
if ((tmp = FastGetSubRect(i + c, j + r)) != NULL)
if (tmp->iNodeCount) // o ile s¹ jakieœ obiekty, bo po co puste sektory przelatywaæ
pRendered[iRendered++] = tmp; // tworzenie listy sektorów do renderowania
} while ((i < 0) || (j < 0)); // s¹ 4 przypadki, oprócz i=j=0
}
for (i = 0; i < iRendered; i++)
pRendered[i]->RenderDL(); // renderowanie nieprzezroczystych
return true;
}
bool TGround::RenderAlphaDL(vector3 pPosition)
{ // renderowanie scenerii z Display List - faza przezroczystych
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04); // im mniejsza wartoœæ, tym wiêksza ramka, domyœlnie 0.1f
TGroundNode *node;
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
TSubRect *tmp;
// Ra: renderowanie progresywne - zale¿ne od FPS oraz kierunku patrzenia
int i;
for (i = iRendered - 1; i >= 0; --i) // od najdalszych
{ // przezroczyste trójk¹ty w oddzielnym cyklu przed modelami
tmp = pRendered[i];
for (node = tmp->nRenderRectAlpha; node; node = node->nNext3)
node->RenderAlphaDL(); // przezroczyste modele
}
for (i = iRendered - 1; i >= 0; --i) // od najdalszych
{ // renderowanie przezroczystych modeli oraz pojazdów
pRendered[i]->RenderAlphaDL();
}
glDisable(GL_LIGHTING); // linie nie powinny œwieciæ
for (i = iRendered - 1; i >= 0; --i) // od najdalszych
{ // druty na koñcu, ¿eby siê nie robi³y bia³e plamy na tle lasu
tmp = pRendered[i];
for (node = tmp->nRenderWires; node; node = node->nNext3)
node->RenderAlphaDL(); // druty
}
return true;
}
bool TGround::RenderVBO(vector3 pPosition)
{ // renderowanie scenerii z VBO - faza nieprzezroczystych
glDisable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.45); // im mniejsza wartoœæ, tym wiêksza ramka, domyœlnie 0.1f
++TGroundRect::iFrameNumber; // zwiêszenie licznika ramek
CameraDirection.x = sin(Global::pCameraRotation); // wektor kierunkowy
CameraDirection.z = cos(Global::pCameraRotation);
int tr, tc;
TGroundNode *node;
glColor3f(1.0f, 1.0f, 1.0f);
glEnable(GL_LIGHTING);
int n = 2 * iNumSubRects; //(2*==2km) promieñ wyœwietlanej mapy w sektorach
int c = GetColFromX(pPosition.x);
int r = GetRowFromZ(pPosition.z);
TSubRect *tmp;
for (node = srGlobal.nRenderHidden; node; node = node->nNext3)
node->RenderHidden(); // rednerowanie globalnych (nie za czêsto?)
int i, j, k;
// renderowanie czo³gowe dla obiektów aktywnych a niewidocznych
for (j = r - n; j <= r + n; j++)
for (i = c - n; i <= c + n; i++)
{
if ((tmp = FastGetSubRect(i, j)) != NULL)
{
for (node = tmp->nRenderHidden; node; node = node->nNext3)
node->RenderHidden();
tmp->RenderSounds(); // jeszcze dŸwiêki pojazdów by siê przyda³y, równie¿
// niewidocznych
}
}
// renderowanie progresywne - zale¿ne od FPS oraz kierunku patrzenia
iRendered = 0; // iloœæ renderowanych sektorów
vector3 direction;
for (k = 0; k < Global::iSegmentsRendered; ++k) // sektory w kolejnoœci odleg³oœci
{ // przerobione na u¿ycie SectorOrder
i = SectorOrder[k].x; // na starcie oba >=0
j = SectorOrder[k].y;
do
{
if (j <= 0)
i = -i; // pierwszy przebieg: j<=0, i>=0; drugi: j>=0, i<=0; trzeci: j<=0, i<=0
// czwarty: j>=0, i>=0;
j = -j; // i oraz j musi byæ zmienione wczeœniej, ¿eby continue dzia³a³o
direction = vector3(i, 0, j); // wektor od kamery do danego sektora
if (LengthSquared3(direction) > 5) // te blisko s¹ zawsze wyœwietlane
{
direction = SafeNormalize(direction); // normalizacja
if (CameraDirection.x * direction.x + CameraDirection.z * direction.z < 0.55)
continue; // pomijanie sektorów poza k¹tem patrzenia
}
Rects[(i + c) / iNumSubRects][(j + r) / iNumSubRects]
.RenderVBO(); // kwadrat kilometrowy nie zawsze, bo szkoda FPS
if ((tmp = FastGetSubRect(i + c, j + r)) != NULL)
if (tmp->iNodeCount) // je¿eli s¹ jakieœ obiekty, bo po co puste sektory przelatywaæ
pRendered[iRendered++] = tmp; // tworzenie listy sektorów do renderowania
} while ((i < 0) || (j < 0)); // s¹ 4 przypadki, oprócz i=j=0
}
// dodaæ rednerowanie terenu z E3D - jedno VBO jest u¿ywane dla ca³ego modelu, chyba ¿e jest ich
// wiêcej
if (Global::pTerrainCompact)
Global::pTerrainCompact->TerrainRenderVBO(TGroundRect::iFrameNumber);
for (i = 0; i < iRendered; i++)
{ // renderowanie nieprzezroczystych
pRendered[i]->RenderVBO();
}
return true;
}
bool TGround::RenderAlphaVBO(vector3 pPosition)
{ // renderowanie scenerii z VBO - faza przezroczystych
glEnable(GL_BLEND);
glAlphaFunc(GL_GREATER, 0.04); // im mniejsza wartoœæ, tym wiêksza ramka, domyœlnie 0.1f
TGroundNode *node;
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
TSubRect *tmp;
int i;
for (i = iRendered - 1; i >= 0; --i) // od najdalszych
{ // renderowanie przezroczystych trójk¹tów sektora
tmp = pRendered[i];
tmp->LoadNodes(); // ewentualne tworzenie siatek
if (tmp->StartVBO())
{
for (node = tmp->nRenderRectAlpha; node; node = node->nNext3)
if (node->iVboPtr >= 0)
node->RenderAlphaVBO(); // nieprzezroczyste obiekty terenu
tmp->EndVBO();
}
}
for (i = iRendered - 1; i >= 0; --i) // od najdalszych
pRendered[i]->RenderAlphaVBO(); // przezroczyste modeli oraz pojazdy
glDisable(GL_LIGHTING); // linie nie powinny œwieciæ
for (i = iRendered - 1; i >= 0; --i) // od najdalszych
{ // druty na koñcu, ¿eby siê nie robi³y bia³e plamy na tle lasu
tmp = pRendered[i];
if (tmp->StartVBO())
{
for (node = tmp->nRenderWires; node; node = node->nNext3)
node->RenderAlphaVBO(); // przezroczyste modele
tmp->EndVBO();
}
}
return true;
};
//---------------------------------------------------------------------------
void TGround::Navigate(String ClassName, UINT Msg, WPARAM wParam, LPARAM lParam)
{ // wys³anie komunikatu do steruj¹cego
HWND h = FindWindow(ClassName.c_str(), 0); // mo¿na by to zapamiêtaæ
if (h == 0)
h = FindWindow(0, ClassName.c_str()); // mo¿na by to zapamiêtaæ
SendMessage(h, Msg, wParam, lParam);
};
//--------------------------------
void TGround::WyslijEvent(const AnsiString &e, const AnsiString &d)
{ // Ra: jeszcze do wyczyszczenia
DaneRozkaz r;
r.iSygn = 'EU07';
r.iComm = 2; // 2 - event
int i = e.Length(), j = d.Length();
r.cString[0] = char(i);
strcpy(r.cString + 1, e.c_str()); // zakoñczony zerem
r.cString[i + 2] = char(j); // licznik po zerze koñcz¹cym
strcpy(r.cString + 3 + i, d.c_str()); // zakoñczony zerem
COPYDATASTRUCT cData;
cData.dwData = 'EU07'; // sygnatura
cData.cbData = 12 + i + j; // 8+dwa liczniki i dwa zera koñcz¹ce
cData.lpData = &r;
Navigate("TEU07SRK", WM_COPYDATA, (WPARAM)Global::hWnd, (LPARAM)&cData);
CommLog(AnsiString(Now()) + " " + IntToStr(r.iComm) + " " + e + " sent");
};
//---------------------------------------------------------------------------
void TGround::WyslijUszkodzenia(const AnsiString &t, char fl)
{ // wys³anie informacji w postaci pojedynczego tekstu
DaneRozkaz r;
r.iSygn = 'EU07';
r.iComm = 13; // numer komunikatu
int i = t.Length();
r.cString[0] = char(fl);
r.cString[1] = char(i);
strcpy(r.cString + 2, t.c_str()); // z zerem koñcz¹cym
COPYDATASTRUCT cData;
cData.dwData = 'EU07'; // sygnatura
cData.cbData = 11 + i; // 8+licznik i zero koñcz¹ce
cData.lpData = &r;
Navigate("TEU07SRK", WM_COPYDATA, (WPARAM)Global::hWnd, (LPARAM)&cData);
CommLog(AnsiString(Now()) + " " + IntToStr(r.iComm) + " " + t + " sent");
};
//---------------------------------------------------------------------------
void TGround::WyslijString(const AnsiString &t, int n)
{ // wys³anie informacji w postaci pojedynczego tekstu
DaneRozkaz r;
r.iSygn = 'EU07';
r.iComm = n; // numer komunikatu
int i = t.Length();
r.cString[0] = char(i);
strcpy(r.cString + 1, t.c_str()); // z zerem koñcz¹cym
COPYDATASTRUCT cData;
cData.dwData = 'EU07'; // sygnatura
cData.cbData = 10 + i; // 8+licznik i zero koñcz¹ce
cData.lpData = &r;
Navigate("TEU07SRK", WM_COPYDATA, (WPARAM)Global::hWnd, (LPARAM)&cData);
CommLog(AnsiString(Now()) + " " + IntToStr(r.iComm) + " " + t + " sent");
};
//---------------------------------------------------------------------------
void TGround::WyslijWolny(const AnsiString &t)
{ // Ra: jeszcze do wyczyszczenia
WyslijString(t, 4); // tor wolny
};
//--------------------------------
void TGround::WyslijNamiary(TGroundNode *t)
{ // wys³anie informacji o pojeŸdzie - (float), d³ugoœæ ramki bêdzie zwiêkszana w miarê potrzeby
// WriteLog("Wysylam pojazd");
DaneRozkaz r;
r.iSygn = 'EU07';
r.iComm = 7; // 7 - dane pojazdu
int i = 32, j = t->asName.Length();
r.iPar[0] = i; // iloϾ danych liczbowych
r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba)
r.fPar[2] = t->DynamicObject->MoverParameters->Loc.X; // pozycja X
r.fPar[3] = t->DynamicObject->MoverParameters->Loc.Y; // pozycja Y
r.fPar[4] = t->DynamicObject->MoverParameters->Loc.Z; // pozycja Z
r.fPar[5] = t->DynamicObject->MoverParameters->V; // prêdkoœæ ruchu X
r.fPar[6] = t->DynamicObject->MoverParameters->nrot * M_PI *
t->DynamicObject->MoverParameters->WheelDiameter; // prêdkoœæ obrotowa kó£
r.fPar[7] = 0; // prêdkoœæ ruchu Z
r.fPar[8] = t->DynamicObject->MoverParameters->AccS; // przyspieszenie X
r.fPar[9] = t->DynamicObject->MoverParameters->AccN; // przyspieszenie Y //na razie nie
r.fPar[10] = t->DynamicObject->MoverParameters->AccV; // przyspieszenie Z
r.fPar[11] = t->DynamicObject->MoverParameters->DistCounter; // przejechana odleg³oœæ w km
r.fPar[12] = t->DynamicObject->MoverParameters->PipePress; // ciœnienie w PG
r.fPar[13] = t->DynamicObject->MoverParameters->ScndPipePress; // ciœnienie w PZ
r.fPar[14] = t->DynamicObject->MoverParameters->BrakePress; // ciœnienie w CH
r.fPar[15] = t->DynamicObject->MoverParameters->Compressor; // ciœnienie w ZG
r.fPar[16] = t->DynamicObject->MoverParameters->Itot; // Pr¹d ca³kowity
r.iPar[17] = t->DynamicObject->MoverParameters->MainCtrlPos; // Pozycja NJ
r.iPar[18] = t->DynamicObject->MoverParameters->ScndCtrlPos; // Pozycja NB
r.iPar[19] = t->DynamicObject->MoverParameters->MainCtrlActualPos; // Pozycja jezdna
r.iPar[20] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania
r.iPar[21] = t->DynamicObject->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania
r.iPar[22] = t->DynamicObject->MoverParameters->ResistorsFlag * 1 +
t->DynamicObject->MoverParameters->ConverterFlag * 2 +
+t->DynamicObject->MoverParameters->CompressorFlag * 4 +
t->DynamicObject->MoverParameters->Mains * 8 +
+t->DynamicObject->MoverParameters->DoorLeftOpened * 16 +
t->DynamicObject->MoverParameters->DoorRightOpened * 32 +
+t->DynamicObject->MoverParameters->FuseFlag * 64 +
t->DynamicObject->MoverParameters->DepartureSignal * 128;
// WriteLog("Zapisalem stare");
// WriteLog("Mam patykow "+IntToStr(t->DynamicObject->iAnimType[ANIM_PANTS]));
for (int p = 0; p < 4; p++)
{
// WriteLog("Probuje pant "+IntToStr(p));
if (p < t->DynamicObject->iAnimType[ANIM_PANTS])
{
r.fPar[23 + p] = t->DynamicObject->pants[p].fParamPants->PantWys; // stan pantografów 4
// WriteLog("Zapisalem pant "+IntToStr(p));
}
else
{
r.fPar[23 + p] = -2;
// WriteLog("Nie mam pant "+IntToStr(p));
}
}
// WriteLog("Zapisalem pantografy");
for (int p = 0; p < 3; p++)
r.fPar[27 + p] =
t->DynamicObject->MoverParameters->ShowCurrent(p + 1); // amperomierze kolejnych grup
// WriteLog("zapisalem prady");
r.iPar[30] = t->DynamicObject->MoverParameters->WarningSignal; // trabienie
r.fPar[31] = t->DynamicObject->MoverParameters->RunningTraction.TractionVoltage; // napiecie WN
// WriteLog("Parametry gotowe");
i <<= 2; // iloœæ bajtów
r.cString[i] = char(j); // na koñcu nazwa, ¿eby jakoœ zidentyfikowaæ
strcpy(r.cString + i + 1, t->asName.c_str()); // zakoñczony zerem
COPYDATASTRUCT cData;
cData.dwData = 'EU07'; // sygnatura
cData.cbData = 10 + i + j; // 8+licznik i zero koñcz¹ce
cData.lpData = &r;
// WriteLog("Ramka gotowa");
Navigate("TEU07SRK", WM_COPYDATA, (WPARAM)Global::hWnd, (LPARAM)&cData);
// WriteLog("Ramka poszla!");
CommLog(AnsiString(Now()) + " " + IntToStr(r.iComm) + " " + t->asName + " sent");
};
//
void TGround::WyslijObsadzone()
{ // wys³anie informacji o pojeŸdzie
DaneRozkaz2 r;
r.iSygn = 'EU07';
r.iComm = 12; // kod 12
for (int i; i<1984; i++) r.cString[i] = 0;
int i = 0;
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
if (Current->DynamicObject->Mechanik)
{
strcpy(r.cString + 64 * i, Current->DynamicObject->asName.c_str());
r.fPar[16 * i + 4] = Current->DynamicObject->GetPosition().x;
r.fPar[16 * i + 5] = Current->DynamicObject->GetPosition().y;
r.fPar[16 * i + 6] = Current->DynamicObject->GetPosition().z;
r.iPar[16 * i + 7] = Current->DynamicObject->Mechanik->GetAction();
strcpy(r.cString + 64 * i + 32, Current->DynamicObject->GetTrack()->IsolatedName().c_str());
strcpy(r.cString + 64 * i + 48, Current->DynamicObject->Mechanik->Timetable()->TrainName.c_str());
i++;
if (i>30) break;
}
while (i <= 30)
{
strcpy(r.cString + 64 * i, AnsiString("none").c_str());
r.fPar[16 * i + 4] = 1;
r.fPar[16 * i + 5] = 2;
r.fPar[16 * i + 6] = 3;
r.iPar[16 * i + 7] = 0;
strcpy(r.cString + 64 * i + 32, AnsiString("none").c_str());
strcpy(r.cString + 64 * i + 48, AnsiString("none").c_str());
i++;
}
COPYDATASTRUCT cData;
cData.dwData = 'EU07'; // sygnatura
cData.cbData = 8 + 1984; // 8+licznik i zero koñcz¹ce
cData.lpData = &r;
// WriteLog("Ramka gotowa");
Navigate("TEU07SRK", WM_COPYDATA, (WPARAM)Global::hWnd, (LPARAM)&cData);
CommLog(AnsiString(Now()) + " " + IntToStr(r.iComm) + " obsadzone" + " sent");
}
//--------------------------------
void TGround::WyslijParam(int nr, int fl)
{ // wys³anie parametrów symulacji w ramce (nr) z flagami (fl)
DaneRozkaz r;
r.iSygn = 'EU07';
r.iComm = nr; // zwykle 5
r.iPar[0] = fl; // flagi istotnoœci kolejnych parametrów
int i = 0; // domyœlnie brak danych
switch (nr)
{ // mo¿na tym przesy³aæ ró¿ne zestawy parametrów
case 5: // czas i pauza
r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba)
r.iPar[2] = Global::iPause; // stan zapauzowania
i = 8; // dwa parametry po 4 bajty ka¿dy
break;
}
COPYDATASTRUCT cData;
cData.dwData = 'EU07'; // sygnatura
cData.cbData = 12 + i; // 12+rozmiar danych
cData.lpData = &r;
Navigate("TEU07SRK", WM_COPYDATA, (WPARAM)Global::hWnd, (LPARAM)&cData);
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TGround::RadioStop(vector3 pPosition)
{ // zatrzymanie poci¹gów w okolicy
TGroundNode *node;
TSubRect *tmp;
int c = GetColFromX(pPosition.x);
int r = GetRowFromZ(pPosition.z);
int i, j;
int n = 2 * iNumSubRects; // przegl¹danie czo³gowe okolicznych torów w kwadracie 4km×4km
for (j = r - n; j <= r + n; j++)
for (i = c - n; i <= c + n; i++)
if ((tmp = FastGetSubRect(i, j)) != NULL)
for (node = tmp->nRootNode; node != NULL; node = node->nNext2)
if (node->iType == TP_TRACK)
node->pTrack->RadioStop(); // przekazanie do ka¿dego toru w ka¿dym segmencie
};
TDynamicObject * TGround::DynamicNearest(vector3 pPosition, double distance, bool mech)
{ // wyszukanie pojazdu najbli¿szego wzglêdem (pPosition)
TGroundNode *node;
TSubRect *tmp;
TDynamicObject *dyn = NULL;
int c = GetColFromX(pPosition.x);
int r = GetRowFromZ(pPosition.z);
int i, j, k;
double sqm = distance * distance, sqd; // maksymalny promien poszukiwañ do kwadratu
for (j = r - 1; j <= r + 1; j++) // plus dwa zewnêtrzne sektory, ³¹cznie 9
for (i = c - 1; i <= c + 1; i++)
if ((tmp = FastGetSubRect(i, j)) != NULL)
for (node = tmp->nRootNode; node; node = node->nNext2) // nastêpny z sektora
if (node->iType == TP_TRACK) // Ra: przebudowaæ na u¿ycie tabeli torów?
for (k = 0; k < node->pTrack->iNumDynamics; k++)
if (mech ? (node->pTrack->Dynamics[k]->Mechanik != NULL) :
true) // czy ma mieæ obsadê
if ((sqd = SquareMagnitude(
node->pTrack->Dynamics[k]->GetPosition() - pPosition)) <
sqm)
{
sqm = sqd; // nowa odleg³oœæ
dyn = node->pTrack->Dynamics[k]; // nowy lider
}
return dyn;
};
TDynamicObject * TGround::CouplerNearest(vector3 pPosition, double distance, bool mech)
{ // wyszukanie pojazdu, którego sprzêg jest najbli¿ej wzglêdem (pPosition)
TGroundNode *node;
TSubRect *tmp;
TDynamicObject *dyn = NULL;
int c = GetColFromX(pPosition.x);
int r = GetRowFromZ(pPosition.z);
int i, j, k;
double sqm = distance * distance, sqd; // maksymalny promien poszukiwañ do kwadratu
for (j = r - 1; j <= r + 1; j++) // plus dwa zewnêtrzne sektory, ³¹cznie 9
for (i = c - 1; i <= c + 1; i++)
if ((tmp = FastGetSubRect(i, j)) != NULL)
for (node = tmp->nRootNode; node; node = node->nNext2) // nastêpny z sektora
if (node->iType == TP_TRACK) // Ra: przebudowaæ na u¿ycie tabeli torów?
for (k = 0; k < node->pTrack->iNumDynamics; k++)
if (mech ? (node->pTrack->Dynamics[k]->Mechanik != NULL) :
true) // czy ma mieæ obsadê
{
if ((sqd = SquareMagnitude(
node->pTrack->Dynamics[k]->HeadPosition() - pPosition)) <
sqm)
{
sqm = sqd; // nowa odleg³oœæ
dyn = node->pTrack->Dynamics[k]; // nowy lider
}
if ((sqd = SquareMagnitude(
node->pTrack->Dynamics[k]->RearPosition() - pPosition)) <
sqm)
{
sqm = sqd; // nowa odleg³oœæ
dyn = node->pTrack->Dynamics[k]; // nowy lider
}
}
return dyn;
};
//---------------------------------------------------------------------------
void TGround::DynamicRemove(TDynamicObject *dyn)
{ // Ra: usuniêcie pojazdów ze scenerii (gdy dojad¹ na koniec i nie sa potrzebne)
TDynamicObject *d = dyn->Prev();
if (d) // jeœli coœ jest z przodu
DynamicRemove(d); // zaczynamy od tego z przodu
else
{ // jeœli mamy ju¿ tego na pocz¹tku
TGroundNode **n, *node;
d = dyn; // od pierwszego
while (d)
{
if (d->MyTrack)
d->MyTrack->RemoveDynamicObject(d); // usuniêcie z toru o ile nie usuniêty
n = &nRootDynamic; // lista pojazdów od pocz¹tku
// node=NULL; //nie znalezione
while (*n ? (*n)->DynamicObject != d : false)
{ // usuwanie z listy pojazdów
n = &((*n)->nNext); // sprawdzenie kolejnego pojazdu na liœcie
}
if ((*n)->DynamicObject == d)
{ // jeœli znaleziony
node = (*n); // zapamiêtanie wêz³a, aby go usun¹æ
(*n) = node->nNext; // pominiêcie na liœcie
Global::TrainDelete(d);
d = d->Next(); // przejœcie do kolejnego pojazdu, póki jeszcze jest
delete node; // usuwanie fizyczne z pamiêci
}
else
d = NULL; // coœ nie tak!
}
}
};
//---------------------------------------------------------------------------
void TGround::TerrainRead(const AnsiString &f){
// Ra: wczytanie trójk¹tów terenu z pliku E3D
};
//---------------------------------------------------------------------------
void TGround::TerrainWrite()
{ // Ra: zapisywanie trójk¹tów terenu do pliku E3D
if (Global::pTerrainCompact->TerrainLoaded())
return; // jeœli zosta³o wczytane, to nie ma co dalej robiæ
if (Global::asTerrainModel.IsEmpty())
return;
// Trójk¹ty s¹ zapisywane kwadratami kilometrowymi.
// Kwadrat 500500 jest na œrodku (od 0.0 do 1000.0 na OX oraz OZ).
// Ewentualnie w numerowaniu kwadratów uwzglêdnic wpis //$g.
// Trójk¹ty s¹ grupowane w submodele wg tekstury.
// Triangle_strip oraz triangle_fan s¹ rozpisywane na pojedyncze trójk¹ty,
// chyba ¿e dla danej tekstury wychodzi tylko jeden submodel.
TModel3d *m = new TModel3d(); // wirtualny model roboczy z oddzielnymi submodelami
TSubModel *sk; // wskaŸnik roboczy na submodel kwadratu
TSubModel *st; // wskaŸnik roboczy na submodel tekstury
// Zliczamy kwadraty z trójk¹tami, iloœæ tekstur oraz wierzcho³ków.
// Iloœæ kwadratów i iloœæ tekstur okreœli iloœæ submodeli.
// int sub=0; //ca³kowita iloœæ submodeli
// int ver=0; //ca³kowita iloœæ wierzcho³ków
int i, j, k; // indeksy w pêtli
TGroundNode *Current;
float8 *ver; // trójk¹ty
TSubModel::iInstance = 0; // pozycja w tabeli wierzcho³ków liczona narastaj¹co
for (i = 0; i < iNumRects; ++i) // pêtla po wszystkich kwadratach kilometrowych
for (j = 0; j < iNumRects; ++j)
if (Rects[i][j].iNodeCount)
{ // o ile s¹ jakieœ trójk¹ty w œrodku
sk = new TSubModel(); // nowy submodel dla kawadratu
// numer kwadratu XXXZZZ, przy czym X jest ujemne - XXX roœnie na wschód, ZZZ roœnie
// na pó³noc
sk->NameSet(AnsiString(1000 * (500 + i - iNumRects / 2) + (500 + j - iNumRects / 2))
.c_str()); // nazwa=numer kwadratu
m->AddTo(NULL, sk); // dodanie submodelu dla kwadratu
for (Current = Rects[i][j].nRootNode; Current; Current = Current->nNext2)
if (Current->TextureID)
switch (Current->iType)
{ // pêtla po trójk¹tach - zliczanie wierzcho³ków, dodaje submodel dla
// ka¿dej tekstury
case GL_TRIANGLES:
Current->iVboPtr = sk->TriangleAdd(
m, Current->TextureID,
Current->iNumVerts); // zwiêkszenie iloœci trójk¹tów w submodelu
m->iNumVerts +=
Current->iNumVerts; // zwiêkszenie ca³kowitej iloœci wierzcho³ków
break;
case GL_TRIANGLE_STRIP: // na razie nie, bo trzeba przerabiaæ na pojedyncze
// trójk¹ty
break;
case GL_TRIANGLE_FAN: // na razie nie, bo trzeba przerabiaæ na pojedyncze
// trójk¹ty
break;
}
for (Current = Rects[i][j].nRootNode; Current; Current = Current->nNext2)
if (Current->TextureID)
switch (Current->iType)
{ // pêtla po trójk¹tach - dopisywanie wierzcho³ków
case GL_TRIANGLES:
// ver=sk->TrianglePtr(TTexturesManager::GetName(Current->TextureID).c_str(),Current->iNumVerts);
// //wskaŸnik na pocz¹tek
ver = sk->TrianglePtr(Current->TextureID, Current->iVboPtr,
Current->Ambient, Current->Diffuse,
Current->Specular); // wskaŸnik na pocz¹tek
// WriteLog("Zapis "+AnsiString(Current->iNumVerts)+" trójk¹tów w
// ("+AnsiString(i)+","+AnsiString(j)+") od
// "+AnsiString(Current->iVboPtr)+" dla
// "+AnsiString(Current->TextureID));
Current->iVboPtr = -1; // bo to by³o tymczasowo u¿ywane
for (k = 0; k < Current->iNumVerts; ++k)
{ // przepisanie wspó³rzêdnych
ver[k].Point.x = Current->Vertices[k].Point.x;
ver[k].Point.y = Current->Vertices[k].Point.y;
ver[k].Point.z = Current->Vertices[k].Point.z;
ver[k].Normal.x = Current->Vertices[k].Normal.x;
ver[k].Normal.y = Current->Vertices[k].Normal.y;
ver[k].Normal.z = Current->Vertices[k].Normal.z;
ver[k].tu = Current->Vertices[k].tu;
ver[k].tv = Current->Vertices[k].tv;
}
break;
case GL_TRIANGLE_STRIP: // na razie nie, bo trzeba przerabiaæ na pojedyncze
// trójk¹ty
break;
case GL_TRIANGLE_FAN: // na razie nie, bo trzeba przerabiaæ na pojedyncze
// trójk¹ty
break;
}
}
m->SaveToBinFile(AnsiString("models\\" + Global::asTerrainModel).c_str());
};
//---------------------------------------------------------------------------
void TGround::TrackBusyList()
{ // wys³anie informacji o wszystkich zajêtych odcinkach
TGroundNode *Current;
TTrack *Track;
AnsiString name;
for (Current = nRootOfType[TP_TRACK]; Current; Current = Current->nNext)
if (!Current->asName.IsEmpty()) // musi byæ nazwa
if (Current->pTrack->iNumDynamics) // osi to chyba nie ma jak policzyæ
WyslijString(Current->asName, 8); // zajêty
};
//---------------------------------------------------------------------------
void TGround::IsolatedBusyList()
{ // wys³anie informacji o wszystkich odcinkach izolowanych
TIsolated *Current;
for (Current = TIsolated::Root(); Current; Current = Current->Next())
if (Current->Busy()) // sprawdŸ zajêtoœæ
WyslijString(Current->asName, 11); // zajêty
else
WyslijString(Current->asName, 10); // wolny
WyslijString("none", 10); // informacja o koñcu listy
};
//---------------------------------------------------------------------------
void TGround::IsolatedBusy(const AnsiString t)
{ // wys³anie informacji o odcinku izolowanym (t)
// Ra 2014-06: do wyszukania u¿yæ drzewka nazw
TIsolated *Current;
for (Current = TIsolated::Root(); Current; Current = Current->Next())
if (Current->asName == t) // wyszukiwanie odcinka o nazwie (t)
if (Current->Busy()) // sprawdŸ zajetoœæ
{
WyslijString(Current->asName, 11); // zajêty
return; // nie sprawdzaj dalszych
}
WyslijString(t, 10); // wolny
};
//---------------------------------------------------------------------------
void TGround::Silence(vector3 gdzie)
{ // wyciszenie wszystkiego w sektorach przed przeniesieniem kamery z (gdzie)
int tr, tc;
TGroundNode *node;
int n = 2 * iNumSubRects; //(2*==2km) promieñ wyœwietlanej mapy w sektorach
int c = GetColFromX(gdzie.x); // sektory wg dotychczasowej pozycji kamery
int r = GetRowFromZ(gdzie.z);
TSubRect *tmp;
int i, j, k;
// renderowanie czo³gowe dla obiektów aktywnych a niewidocznych
for (j = r - n; j <= r + n; j++)
for (i = c - n; i <= c + n; i++)
if ((tmp = FastGetSubRect(i, j)) != NULL)
{ // tylko dŸwiêki interesuj¹
for (node = tmp->nRenderHidden; node; node = node->nNext3)
node->RenderHidden();
tmp->RenderSounds(); // dŸwiêki pojazdów by siê przyda³o wy³¹czyæ
}
};
//---------------------------------------------------------------------------