continued refactoring: lines, terrain models; render culling optimizations and fixes

This commit is contained in:
tmj-fstate
2017-10-17 19:49:14 +02:00
parent 630b9ecf1b
commit 87348a2ab8
25 changed files with 1062 additions and 244 deletions

View File

@@ -232,9 +232,14 @@ void TAnimContainer::UpdateModel() {
fTranslateSpeed = 0.0; // wyłączenie przeliczania wektora
if (LengthSquared3(vTranslation) <= 0.0001) // jeśli jest w punkcie początkowym
iAnim &= ~2; // wyłączyć zmianę pozycji submodelu
if (evDone)
Global::AddToQuery(evDone, NULL); // wykonanie eventu informującego o
// zakończeniu
if( evDone ) {
// wykonanie eventu informującego o zakończeniu
#ifdef EU07_USE_OLD_GROUNDCODE
Global::AddToQuery( evDone, NULL );
#else
simulation::Events.AddToQuery( evDone, nullptr );
#endif
}
}
}
if (fRotateSpeed != 0.0)
@@ -299,9 +304,14 @@ void TAnimContainer::UpdateModel() {
if (!anim)
{ // nie potrzeba przeliczać już
fRotateSpeed = 0.0;
if (evDone)
Global::AddToQuery(evDone, NULL); // wykonanie eventu informującego o
// zakończeniu
if( evDone ) {
// wykonanie eventu informującego o zakończeniu
#ifdef EU07_USE_OLD_GROUNDCODE
Global::AddToQuery( evDone, NULL );
#else
simulation::Events.AddToQuery( evDone, nullptr );
#endif
}
}
}
if( fAngleSpeed != 0.f ) {
@@ -330,7 +340,11 @@ void TAnimContainer::PrepareModel()
fAngleSpeed = 0.0; // wyłączenie przeliczania wektora
if( evDone ) {
// wykonanie eventu informującego o zakończeniu
#ifdef EU07_USE_OLD_GROUNDCODE
Global::AddToQuery( evDone, NULL );
#else
simulation::Events.AddToQuery( evDone, nullptr );
#endif
}
}
else

View File

@@ -149,6 +149,10 @@ public:
material_data const *
Material() const {
return &m_materialdata; }
inline
TModel3d *
Model() {
return pModel; }
// members
static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania

View File

@@ -18,9 +18,6 @@ http://mozilla.org/MPL/2.0/.
//---------------------------------------------------------------------------
// TViewPyramid TCamera::OrgViewPyramid;
//={vector3(-1,1,1),vector3(1,1,1),vector3(-1,-1,1),vector3(1,-1,1),vector3(0,0,0)};
void TCamera::Init(vector3 NPos, vector3 NAngle)
{
@@ -37,8 +34,8 @@ void TCamera::Init(vector3 NPos, vector3 NAngle)
void TCamera::OnCursorMove(double x, double y)
{
// McZapkie-170402: zeby mysz dzialala zawsze if (Type==tp_Follow)
Pitch += y;
Yaw += -x;
Yaw -= x;
Pitch -= y;
if (Yaw > M_PI)
Yaw -= 2 * M_PI;
else if (Yaw < -M_PI)
@@ -62,7 +59,7 @@ TCamera::OnCommand( command_data const &Command ) {
OnCursorMove(
reinterpret_cast<double const &>( Command.param1 ) * 0.005 * Global::fMouseXScale / Global::ZoomFactor,
reinterpret_cast<double const &>( Command.param2 ) * -0.01 * Global::fMouseYScale / Global::ZoomFactor );
reinterpret_cast<double const &>( Command.param2 ) * 0.01 * Global::fMouseYScale / Global::ZoomFactor );
break;
}

View File

@@ -5501,3 +5501,18 @@ vehicle_table::update_traction( TDynamicObject *Vehicle ) {
}
}
}
// legacy method, sends list of vehicles over network
void
vehicle_table::DynamicList( bool const Onlycontrolled ) const {
// odesłanie nazw pojazdów dostępnych na scenerii (nazwy, szczególnie wagonów, mogą się powtarzać!)
for( auto const *vehicle : m_items ) {
if( ( false == Onlycontrolled )
|| ( vehicle->Mechanik != nullptr ) ) {
// same nazwy pojazdów
multiplayer::WyslijString( vehicle->asName, 6 );
}
}
// informacja o końcu listy
multiplayer::WyslijString( "none", 6 );
}

View File

@@ -495,6 +495,9 @@ public:
// legacy method, checks for presence and height of traction wire for specified vehicle
void
update_traction( TDynamicObject *Vehicle );
// legacy method, sends list of vehicles over network
void
DynamicList( bool const Onlycontrolled = false ) const;
};
//---------------------------------------------------------------------------

View File

@@ -46,8 +46,7 @@ TEvent::~TEvent() {
switch (Type)
{ // sprzątanie
case tp_Multiple:
// SafeDeleteArray(Params[9].asText); //nie usuwać - nazwa obiektu powiązanego zamieniana na
// wskaźnik
// SafeDeleteArray(Params[9].asText); //nie usuwać - nazwa obiektu powiązanego zamieniana na wskaźnik
if (iFlags & conditional_memstring) // o ile jest łańcuch do porównania w memcompare
SafeDeleteArray(Params[10].asText);
break;
@@ -82,8 +81,7 @@ void TEvent::Conditions(cParser *parser, std::string s)
if (!asNodeName.empty())
{ // podczepienie łańcucha, jeśli nie jest pusty
// BUG: source of a memory leak -- the array never gets deleted. fix the destructor
Params[9].asText = new char[asNodeName.size() + 1]; // usuwane i zamieniane na
// wskaźnik
Params[9].asText = new char[asNodeName.size() + 1]; // usuwane i zamieniane na wskaźnik
strcpy(Params[9].asText, asNodeName.c_str());
}
parser->getTokens();
@@ -860,17 +858,17 @@ event_manager::AddToQuery( TEvent *Event, TDynamicObject *Owner ) {
}
//if (DebugModeFlag)
WriteLog(
"EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues & Track command ( "
+ std::string( Event->Params[ 0 ].asText ) + " "
+ std::to_string( Event->Params[ 1 ].asdouble ) + " "
+ std::to_string( Event->Params[ 2 ].asdouble ) + " )" );
"EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues & Track command - ["
+ std::string{ Event->Params[ 0 ].asText } + "] ["
+ to_string( Event->Params[ 1 ].asdouble, 2 ) + "] ["
+ to_string( Event->Params[ 2 ].asdouble, 2 ) + " ]" );
}
//else if (DebugModeFlag)
WriteLog(
"EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues ( "
+ std::string( Event->Params[ 0 ].asText ) + " "
+ std::to_string( Event->Params[ 1 ].asdouble ) + " "
+ std::to_string( Event->Params[ 2 ].asdouble ) + " )" );
"EVENT EXECUTED" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": AddValues - ["
+ std::string( Event->Params[ 0 ].asText ) + "] ["
+ to_string( Event->Params[ 1 ].asdouble, 2 ) + "] ["
+ to_string( Event->Params[ 2 ].asdouble, 2 ) + "]" );
}
// jeśli jest kolejny o takiej samej nazwie, to idzie do kolejki (and if there's no joint event it'll be set to null and processing will end here)
do {
@@ -975,13 +973,11 @@ event_manager::CheckQuery() {
break;
case tp_GetValues: {
if( m_workevent->Activator ) {
/*
// TODO: re-enable when messaging module is in place
if( Global::iMultiplayer ) {
// potwierdzenie wykonania dla serwera (odczyt semafora już tak nie działa)
WyslijEvent( tmpEvent->asName, tmpEvent->Activator->name() );
multiplayer::WyslijEvent( m_workevent->asName, m_workevent->Activator->name() );
}
*/
m_workevent->Params[ 9 ].asMemCell->PutCommand(
m_workevent->Activator->Mechanik,
#ifdef EU07_USE_OLD_GROUNDCODE
@@ -1124,13 +1120,10 @@ event_manager::CheckQuery() {
m_workevent->Params[ 1 ].asdouble,
m_workevent->Params[ 2 ].asdouble );
}
/*
// TODO: re-enable when messaging module is in place
if( Global::iMultiplayer ) {
// dajemy znać do serwera o przełożeniu
WyslijEvent( m_workevent->asName, "" ); // wysłanie nazwy eventu przełączajacego
multiplayer::WyslijEvent( m_workevent->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;
}
@@ -1170,21 +1163,18 @@ event_manager::CheckQuery() {
}
}
}
/*
// TODO: re-enable when messaging component is in place
if( Global::iMultiplayer ) {
// dajemy znać do serwera o wykonaniu
if( ( m_workevent->iFlags & conditional_anyelse ) == 0 ) {
// jednoznaczne tylko, gdy nie było else
if( m_workevent->Activator ) {
WyslijEvent( m_workevent->asName, m_workevent->Activator->name() );
multiplayer::WyslijEvent( m_workevent->asName, m_workevent->Activator->name() );
}
else {
WyslijEvent( m_workevent->asName, "" );
multiplayer::WyslijEvent( m_workevent->asName, "" );
}
}
}
*/
}
break;
}
@@ -1273,8 +1263,8 @@ event_manager::CheckQuery() {
+ to_string( m_workevent->Params[ 9 ].asMemCell->Value2(), 2 ) + "]" );
}
else {
// TODO: re-enable when cell manager is in place
/*
// TODO: re-enable when cell manager is in place
// lista wszystkich
for( TGroundNode *Current = nRootOfType[ TP_MEMCELL ]; Current; Current = Current->nNext ) {
WriteLog( "Memcell \"" + Current->asName + "\": "

View File

@@ -1001,13 +1001,12 @@ TDynamicObject *Global::DynamicNearest()
{ // ustalenie pojazdu najbliższego kamerze
return pGround->DynamicNearest(pCamera->Pos);
};
#endif
TDynamicObject *Global::CouplerNearest()
{ // ustalenie pojazdu najbliższego kamerze
return pGround->CouplerNearest(pCamera->Pos);
};
#endif
bool Global::AddToQuery(TEvent *event, TDynamicObject *who)
{
#ifdef EU07_USE_OLD_GROUNDCODE
@@ -1016,6 +1015,7 @@ bool Global::AddToQuery(TEvent *event, TDynamicObject *who)
return simulation::Events.AddToQuery( event, who );
#endif
};
//---------------------------------------------------------------------------
bool Global::DoEvents()

View File

@@ -299,8 +299,8 @@ class Global
static std::string GetNextSymbol();
#ifdef EU07_USE_OLD_GROUNDCODE
static TDynamicObject * DynamicNearest();
#endif
static TDynamicObject * CouplerNearest();
#endif
static bool AddToQuery(TEvent *event, TDynamicObject *who);
static bool DoEvents();
static std::string Bezogonkow(std::string str, bool _ = false);

View File

@@ -243,7 +243,9 @@ void TSubRect::NodeAdd(TGroundNode *Node)
break;
case TP_TRACK: // TODO: tory z cieniem (tunel, canyon) też dać bez łączenia?
++iTracks; // jeden tor więcej
#ifdef EU07_USE_OLD_GROUNDCODE
Node->pTrack->RaOwnerSet(this); // do którego sektora ma zgłaszać animację
#endif
// NOTE: track merge/sort temporarily disabled to simplify unification of render code
// TODO: refactor sorting as universal part of drawing process in the renderer
Node->nNext3 = nRenderRect;
@@ -340,6 +342,7 @@ TTrack * TSubRect::FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude)
return NULL;
};
#ifdef EU07_USE_OLD_GROUNDCODE
bool TSubRect::RaTrackAnimAdd(TTrack *t)
{ // aktywacja animacji torów w VBO (zwrotnica, obrotnica)
if( false == m_geometrycreated ) {
@@ -365,6 +368,7 @@ void TSubRect::RaAnimate( unsigned int const Framestamp ) {
m_framestamp = Framestamp;
};
#endif
TTraction * TSubRect::FindTraction(glm::dvec3 const &Point, int &iConnection, TTraction *Exclude)
{ // szukanie przęsła w sektorze, którego koniec jest najbliższy (*Point)
@@ -604,7 +608,7 @@ TGroundNode * TGround::DynamicFind(std::string const &Name)
return Current;
return NULL;
};
#endif
void
TGround::DynamicList(bool all)
{ // odesłanie nazw pojazdów dostępnych na scenerii (nazwy, szczególnie wagonów, mogą się
@@ -614,7 +618,7 @@ TGround::DynamicList(bool all)
multiplayer::WyslijString(Current->asName, 6); // same nazwy pojazdów
multiplayer::WyslijString("none", 6); // informacja o końcu listy
};
#ifdef EU07_USE_OLD_GROUNDCODE
// wyszukiwanie obiektu o podanej nazwie i konkretnym typie
TGroundNode *
TGround::FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType) {
@@ -648,7 +652,7 @@ TGround::GetRect( double x, double z ) {
return nullptr;
}
};
#ifdef EU07_USE_OLD_GROUNDCODE
// convert tp_terrain model to a series of triangle nodes
void
TGround::convert_terrain( TGroundNode const *Terrain ) {
@@ -683,7 +687,7 @@ TGround::convert_terrain( TSubModel const *Submodel ) {
delete groundnode;
}
}
#endif
double fTrainSetVel = 0;
double fTrainSetDir = 0;
double fTrainSetDist = 0; // odległość składu od punktu 1 w stronę punktu 2
@@ -2766,7 +2770,7 @@ void TGround::InitTraction()
}
delete[] nEnds; // nie potrzebne już
};
#endif
void TGround::TrackJoin(TGroundNode *Current)
{ // wyszukiwanie sąsiednich torów do podłączenia (wydzielone na użytek obrotnicy)
TTrack *Track = Current->pTrack;
@@ -2774,11 +2778,7 @@ void TGround::TrackJoin(TGroundNode *Current)
int iConnection;
if (!Track->CurrentPrev())
{
#ifdef EU07_USE_OLD_GROUNDCODE
tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_0(), iConnection, Current); // Current do pominięcia
#else
std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_0(), Track );
#endif
switch (iConnection)
{
case 0:
@@ -2791,11 +2791,7 @@ void TGround::TrackJoin(TGroundNode *Current)
}
if (!Track->CurrentNext())
{
#ifdef EU07_USE_OLD_GROUNDCODE
tmp = FindTrack(Track->CurrentSegment()->FastGetPoint_1(), iConnection, Current);
#else
std::tie( tmp, iConnection ) = simulation::Region->find_path( Track->CurrentSegment()->FastGetPoint_1(), Track );
#endif
switch (iConnection)
{
case 0:
@@ -2807,7 +2803,7 @@ void TGround::TrackJoin(TGroundNode *Current)
}
}
}
#ifdef EU07_USE_OLD_GROUNDCODE
// McZapkie-070602: wyzwalacze zdarzen
bool TGround::InitLaunchers()
{
@@ -3755,7 +3751,7 @@ bool TGround::GetTraction(TDynamicObject *model)
return true;
};
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
@@ -3800,6 +3796,7 @@ TDynamicObject * TGround::DynamicNearest(vector3 pPosition, double distance, boo
}
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;
@@ -3829,6 +3826,7 @@ TDynamicObject * TGround::CouplerNearest(vector3 pPosition, double distance, boo
}
return dyn;
};
#endif
//---------------------------------------------------------------------------
void TGround::DynamicRemove(TDynamicObject *dyn)
{ // Ra: usunięcie pojazdów ze scenerii (gdy dojadą na koniec i nie sa potrzebne)
@@ -3951,7 +3949,7 @@ void TGround::TerrainWrite()
};
//---------------------------------------------------------------------------
#ifdef EU07_USE_OLD_GROUNDCODE
void TGround::TrackBusyList()
{ // wysłanie informacji o wszystkich zajętych odcinkach
TGroundNode *Current;
@@ -3987,6 +3985,7 @@ void TGround::IsolatedBusy(const std::string t)
}
multiplayer::WyslijString(t, 10); // wolny
};
#endif
//---------------------------------------------------------------------------
void TGround::Silence(vector3 gdzie)

View File

@@ -128,14 +128,8 @@ public:
void InitNormals();
void RenderHidden(); // obsługa dźwięków i wyzwalaczy zdarzeń
};
/*
struct bounding_area {
glm::vec3 center; // mid point of the rectangle
float radius { 0.0f }; // radius of the bounding sphere
};
*/
class TSubRect : /*public Resource,*/ public CMesh
class TSubRect : public CMesh
{ // sektor składowy kwadratu kilometrowego
public:
scene::bounding_area m_area;
@@ -165,8 +159,10 @@ class TSubRect : /*public Resource,*/ public CMesh
void Sort(); // optymalizacja obiektów w sektorze (sortowanie wg tekstur)
TTrack * FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude);
TTraction * FindTraction(glm::dvec3 const &Point, int &iConnection, TTraction *Exclude);
#ifdef EU07_USE_OLD_GROUNDCODE
bool RaTrackAnimAdd(TTrack *t); // zgłoszenie toru do animacji
void RaAnimate( unsigned int const Framestamp ); // przeliczenie animacji torów
#endif
void RenderSounds(); // dźwięki pojazdów z niewidocznych sektorów
};
@@ -273,9 +269,6 @@ class TGround
bool CheckQuery();
TGroundNode * DynamicFindAny(std::string const &Name);
TGroundNode * DynamicFind(std::string const &Name);
#endif
void DynamicList(bool all = false);
#ifdef EU07_USE_OLD_GROUNDCODE
TGroundNode * FindGroundNode(std::string const &asNameToFind, TGroundNodeType const iNodeType);
#endif
TGroundRect * GetRect( double x, double z );
@@ -295,25 +288,23 @@ class TGround
return (int)(x / fSubRectSize + fHalfTotalNumSubRects); };
#ifdef EU07_USE_OLD_GROUNDCODE
TEvent * FindEvent(const std::string &asEventName);
#endif
void TrackJoin(TGroundNode *Current);
private:
// convert tp_terrain model to a series of triangle nodes
void convert_terrain( TGroundNode const *Terrain );
void convert_terrain( TSubModel const *Submodel );
#ifdef EU07_USE_OLD_GROUNDCODE
void RaTriangleDivider(TGroundNode *node);
void Navigate(std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lParam);
#endif
public:
#ifdef EU07_USE_OLD_GROUNDCODE
void DynamicList( bool all = false );
void TrackBusyList();
void IsolatedBusyList();
void IsolatedBusy( const std::string t );
void RadioStop(vector3 pPosition);
TDynamicObject * DynamicNearest(vector3 pPosition, double distance = 20.0, bool mech = false);
TDynamicObject * CouplerNearest(vector3 pPosition, double distance = 20.0, bool mech = false);
#endif
void DynamicRemove(TDynamicObject *dyn);
void TerrainRead(std::string const &f);
void TerrainWrite();

View File

@@ -176,8 +176,14 @@ void TMemCell::StopCommandSent()
if (!bCommand)
return;
bCommand = false;
if (OnSent) // jeśli jest event
Global::AddToQuery(OnSent, NULL);
if( OnSent ) {
// jeśli jest event
#ifdef EU07_USE_OLD_GROUNDCODE
Global::AddToQuery( OnSent, NULL );
#else
simulation::Events.AddToQuery( OnSent, nullptr );
#endif
}
};
void TMemCell::AssignEvents(TEvent *e)

View File

@@ -19,7 +19,9 @@ Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
#include "logs.h"
#include "mczapkie/mctools.h"
#include "Usefull.h"
#ifdef EU07_USE_OLD_GROUNDCODE
#include "ground.h"
#endif
#include "renderer.h"
#include "Timer.h"
#include "mtable.h"
@@ -988,11 +990,11 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const &
// data offset is used to determine data offset of each submodel into single shared geometry bank
// (the offsets are part of legacy system which we now need to work around for backward compatibility)
if( Child )
Child->create_geometry( Dataoffset, Bank );
if( false == Vertices.empty() ) {
tVboPtr = static_cast<int>( Dataoffset );
Dataoffset += Vertices.size();
// conveniently all relevant custom node types use GL_POINTS, or we'd have to determine the type on individual basis
@@ -1003,10 +1005,29 @@ TSubModel::create_geometry( std::size_t &Dataoffset, geometrybank_handle const &
m_geometry = GfxRenderer.Insert( Vertices, Bank, type );
}
if( m_geometry != NULL ) {
// calculate bounding radius while we're at it
// NOTE: doesn't take into account transformation hierarchy TODO: implement it
float squaredradius{ 0.f };
for( auto const &vertex : GfxRenderer.Vertices( m_geometry ) ) {
squaredradius = static_cast<float>( glm::length2( vertex.position ) );
if( squaredradius > m_boundingradius ) {
m_boundingradius = squaredradius;
}
}
if( m_boundingradius > 0.f ) { m_boundingradius = std::sqrt( m_boundingradius ); }
if( Parent ) {
// propagate radius up the chain
Parent->m_boundingradius = std::max(
Parent->m_boundingradius,
m_boundingradius );
}
}
if( Next )
Next->create_geometry( Dataoffset, Bank );
}
#ifdef EU07_USE_OLD_GROUNDCODE
// places contained geometry in provided ground node
void
TSubModel::convert( TGroundNode &Groundnode ) const {
@@ -1063,7 +1084,7 @@ TSubModel::convert( TGroundNode &Groundnode ) const {
Groundnode.fSquareRadius += squareradius;
}
}
#endif
void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular )
{ // ustawienie kolorów dla modelu terenu
f4Ambient = glm::vec4( Ambient, 1.0f );
@@ -1759,18 +1780,11 @@ void TModel3d::Init()
return; // operacje zostały już wykonane
if (Root)
{
if (iFlags & 0x0200) // jeśli wczytano z pliku tekstowego
{ // jest jakiś dziwny błąd, że obkręcany ma być tylko ostatni submodel
// głównego łańcucha
// TSubModel *p=Root;
// do
//{p->InitialRotate(true); //ostatniemu należy się konwersja układu
// współrzędnych
// p=p->NextGet();
//}
// while (p->NextGet())
// Root->InitialRotate(false); //a poprzednim tylko optymalizacja
Root->InitialRotate(true); // argumet określa, czy wykonać pierwotny obrót
if (iFlags & 0x0200) {
// jeśli wczytano z pliku tekstowego jest jakiś dziwny błąd,
// że obkręcany ma być tylko ostatni submodel głównego łańcucha
// argumet określa, czy wykonać pierwotny obrót
Root->InitialRotate(true);
}
iFlags |= Root->FlagsCheck() | 0x8000; // flagi całego modelu
if (iNumVerts) {
@@ -1788,11 +1802,6 @@ void TModel3d::Init()
}
};
void TModel3d::BreakHierarhy()
{
Error("Not implemented yet :(");
};
//-----------------------------------------------------------------------------
// 2012-02 funkcje do tworzenia terenu z E3D
//-----------------------------------------------------------------------------

View File

@@ -50,7 +50,12 @@ enum TAnimType // rodzaj animacji
at_Undefined = 0x800000FF // animacja chwilowo nieokreślona
};
#ifdef EU07_USE_OLD_GROUNDCODE
class TGroundNode;
#endif
namespace scene {
class shape_node;
}
class TSubModel
{ // klasa submodelu - pojedyncza siatka, punkt świetlny albo grupa punktów
@@ -59,6 +64,7 @@ class TSubModel
friend class opengl_renderer;
friend class TModel3d; // temporary workaround. TODO: clean up class content/hierarchy
friend class TDynamicObject; // temporary etc
friend class scene::shape_node; // temporary etc
public:
enum normalization {
@@ -131,10 +137,8 @@ private:
public: // chwilowo
float3 v_TransVector { 0.0f, 0.0f, 0.0f };
/*
basic_vertex *Vertices; // roboczy wskaźnik - wczytanie T3D do VBO
*/
vertex_array Vertices;
float m_boundingradius { 0 };
size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
TAnimType b_aAnim{ at_None }; // kody animacji oddzielnie, bo zerowane
public:
@@ -163,9 +167,6 @@ public:
TSubModel * NextGet() { return Next; };
TSubModel * ChildGet() { return Child; };
int TriangleAdd(TModel3d *m, material_handle tex, int tri);
/*
basic_vertex * TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular );
*/
void SetRotate(float3 vNewRotateAxis, float fNewAngle);
void SetRotateXYZ(vector3 vNewAngles);
void SetRotateXYZ(float3 vNewAngles);
@@ -215,8 +216,9 @@ public:
std::vector<float4x4>&);
void serialize_geometry( std::ostream &Output ) const;
// places contained geometry in provided ground node
#ifdef EU07_USE_OLD_GROUNDCODE
void convert( TGroundNode &Groundnode ) const;
#endif
};
class TModel3d : public CMesh
@@ -236,6 +238,11 @@ private:
std::string asBinary; // nazwa pod którą zapisać model binarny
std::string m_filename;
public:
float bounding_radius() const {
return (
Root ?
Root->m_boundingradius :
0.f ); }
inline TSubModel * GetSMRoot() { return (Root); };
TModel3d();
~TModel3d();
@@ -246,7 +253,6 @@ public:
void LoadFromBinFile(std::string const &FileName, bool dynamic);
bool LoadFromFile(std::string const &FileName, bool dynamic);
void SaveToBinFile(std::string const &FileName);
void BreakHierarhy();
int Flags() const { return iFlags; };
void Init();
std::string NameGet() { return m_filename; };

105
Track.cpp
View File

@@ -84,16 +84,14 @@ TIsolated::TIsolated(std::string const &n, TIsolated *i) :
// utworznie obwodu izolowanego. nothing to do here.
};
// TODO: put this in the cleanup routine on exit
/*
TIsolated *p=pRoot;
while (pRoot)
{
p=pRoot;
p->pNext=NULL;
delete p;
}
*/
void TIsolated::DeleteAll() {
while( pRoot ) {
auto *next = pRoot->Next();
delete pRoot;
pRoot = next;
}
}
TIsolated * TIsolated::Find(std::string const &n)
{ // znalezienie obiektu albo utworzenie nowego
@@ -1065,7 +1063,11 @@ bool TTrack::InMovement()
return false;
};
#ifdef EU07_USE_OLD_GROUNDCODE
void TTrack::RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined)
#else
void TTrack::RaAssign( scene::basic_cell *gn, TAnimModel *am, TEvent *done, TEvent *joined )
#endif
{ // Ra: wiązanie toru z modelem obrotnicy
if (eType == tt_Table)
{
@@ -2259,7 +2261,7 @@ bool TTrack::Switch(int i, float const t, float const d)
}
else if (eType == tt_Table)
{ // blokowanie (0, szukanie torów) lub odblokowanie (1, rozłączenie) obrotnicy
if (i)
if (i) // NOTE: this condition seems opposite to intention/comment? TODO: investigate this
{ // 0: rozłączenie sąsiednich torów od obrotnicy
if (trPrev) // jeśli jest tor od Point1 obrotnicy
if (iPrevDirection) // 0:dołączony Point1, 1:dołączony Point2
@@ -2276,25 +2278,34 @@ bool TTrack::Switch(int i, float const t, float const d)
fVelocity = 0.0; // AI, nie ruszaj się!
if (SwitchExtension->pOwner)
SwitchExtension->pOwner->RaTrackAnimAdd(this); // dodanie do listy animacyjnej
// TODO: unregister path ends in the owner cell
}
else
{ // 1: ustalenie finalnego położenia (gdy nie było animacji)
RaAnimate(); // ostatni etap animowania
// zablokowanie pozycji i połączenie do sąsiednich torów
// TODO: register new position of the path endpoints with the region
#ifdef EU07_USE_OLD_GROUNDCODE
Global::pGround->TrackJoin(SwitchExtension->pMyNode);
#else
simulation::Region->TrackJoin( this );
#endif
if (trNext || trPrev)
{
fVelocity = 6.0; // jazda dozwolona
if (trPrev)
if (trPrev->fVelocity ==
0.0) // ustawienie 0 da możliwość zatrzymania AI na obrotnicy
trPrev->VelocitySet(6.0); // odblokowanie dołączonego toru do jazdy
if (trNext)
if (trNext->fVelocity == 0.0)
trNext->VelocitySet(6.0);
if (SwitchExtension->evPlus) // w starych sceneriach może nie być
Global::AddToQuery(SwitchExtension->evPlus,
NULL); // potwierdzenie wykonania (np. odpala WZ)
if( ( trPrev )
&& ( trPrev->fVelocity == 0.0 ) ) {
// ustawienie 0 da możliwość zatrzymania AI na obrotnicy
trPrev->VelocitySet( 6.0 ); // odblokowanie dołączonego toru do jazdy
}
if( ( trNext )
&& ( trNext->fVelocity == 0.0 ) ) {
trNext->VelocitySet( 6.0 );
}
if( SwitchExtension->evPlus ) { // w starych sceneriach może nie być
// potwierdzenie wykonania (np. odpala WZ)
Global::AddToQuery( SwitchExtension->evPlus, nullptr );
}
}
}
SwitchExtension->CurrentIndex = i; // zapamiętanie stanu zablokowania
@@ -2528,7 +2539,11 @@ TTrack * TTrack::RaAnimate()
cosa = -hlen * std::cos(glm::radians(SwitchExtension->fOffset));
SwitchExtension->vTrans = ac->TransGet();
vector3 middle =
#ifdef EU07_USE_OLD_GROUNDCODE
SwitchExtension->pMyNode->pCenter +
#else
SwitchExtension->pMyNode->area().center +
#endif
SwitchExtension->vTrans; // SwitchExtension->Segments[0]->FastGetPoint(0.5);
Segment->Init(middle + vector3(sina, 0.0, cosa),
middle - vector3(sina, 0.0, cosa), 5.0); // nowy odcinek
@@ -2774,6 +2789,11 @@ TTrack * TTrack::Connected(int s, double &d) const
path_table::~path_table() {
TIsolated::DeleteAll();
}
// legacy method, initializes tracks after deserialization from scenario file
void
path_table::InitTracks() {
@@ -2957,3 +2977,46 @@ path_table::InitTracks() {
isolated = isolated->Next();
}
}
// legacy method, sends list of occupied paths over network
void
path_table::TrackBusyList() const {
// wysłanie informacji o wszystkich zajętych odcinkach
for( auto const *path : m_items ) {
if( ( false == path->name().empty() ) // musi być nazwa
&& ( false == path->Dynamics.empty() ) ) {
// zajęty
multiplayer::WyslijString( path->name(), 8 );
}
}
}
// legacy method, sends list of occupied path sections over network
void
path_table::IsolatedBusyList() const {
// wysłanie informacji o wszystkich odcinkach izolowanych
TIsolated *Current;
for( Current = TIsolated::Root(); Current; Current = Current->Next() ) {
if( Current->Busy() ) { multiplayer::WyslijString( Current->asName, 11 ); }
else { multiplayer::WyslijString( Current->asName, 10 ); }
}
multiplayer::WyslijString( "none", 10 ); // informacja o końcu listy
}
// legacy method, sends state of specified path section over network
void
path_table::IsolatedBusy( std::string const &Name ) const {
// 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 == Name ) {
if( Current->Busy() ) { multiplayer::WyslijString( Current->asName, 11 ); }
else { multiplayer::WyslijString( Current->asName, 10 ); }
// nie sprawdzaj dalszych
return;
}
}
multiplayer::WyslijString( Name, 10 ); // wolny (technically not found but, eh)
}

34
Track.h
View File

@@ -13,11 +13,15 @@ http://mozilla.org/MPL/2.0/.
#include <vector>
#include <deque>
#include "scenenode.h"
#include "Segment.h"
#include "material.h"
#include "scenenode.h"
#include "names.h"
namespace scene {
class basic_cell;
}
enum TTrackType {
tt_Unknown,
tt_Normal,
@@ -71,7 +75,11 @@ class TSwitchExtension
};
struct
{ // zmienne potrzebne tylko dla obrotnicy/przesuwnicy
#ifdef EU07_USE_OLD_GROUNDCODE
TGroundNode *pMyNode; // dla obrotnicy do wtórnego podłączania torów
#else
scene::basic_cell *pMyNode; // TODO: convert this to observer pattern
#endif
// TAnimContainer *pAnim; //animator modelu dla obrotnicy
TAnimModel *pModel; // na razie model
};
@@ -83,7 +91,11 @@ class TSwitchExtension
};
};
bool bMovement = false; // czy w trakcie animacji
#ifdef EU07_USE_OLD_GROUNDCODE
TSubRect *pOwner = nullptr; // sektor, któremu trzeba zgłosić animację
#else
scene::basic_cell *pOwner = nullptr; // TODO: convert this to observer pattern
#endif
TTrack *pNextAnim = nullptr; // następny tor do animowania
TEvent *evPlus = nullptr,
*evMinus = nullptr; // zdarzenia sygnalizacji rozprucia
@@ -103,6 +115,7 @@ class TIsolated
TMemCell *pMemCell = nullptr; // automatyczna komórka pamięci, która współpracuje z odcinkiem izolowanym
TIsolated();
TIsolated(const std::string &n, TIsolated *i);
static void DeleteAll();
static TIsolated * Find(const std::string &n); // znalezienie obiektu albo utworzenie nowego
void Modify(int i, TDynamicObject *o); // dodanie lub odjęcie osi
bool Busy() {
@@ -245,11 +258,20 @@ public:
#endif
void RenderDynSounds(); // odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania
#ifdef EU07_USE_OLD_GROUNDCODE
void RaOwnerSet(TSubRect *o) {
if (SwitchExtension)
SwitchExtension->pOwner = o; };
#else
void RaOwnerSet( scene::basic_cell *o ) {
if( SwitchExtension ) { SwitchExtension->pOwner = o; } };
#endif
bool InMovement(); // czy w trakcie animacji?
#ifdef EU07_USE_OLD_GROUNDCODE
void RaAssign(TGroundNode *gn, TAnimModel *am, TEvent *done, TEvent *joined);
#else
void RaAssign( scene::basic_cell *gn, TAnimModel *am, TEvent *done, TEvent *joined );
#endif
void RaAnimListAdd(TTrack *t);
TTrack * RaAnimate();
@@ -278,9 +300,19 @@ public:
class path_table : public basic_table<TTrack> {
public:
~path_table();
// legacy method, initializes tracks after deserialization from scenario file
void
InitTracks();
// legacy method, sends list of occupied paths over network
void
TrackBusyList() const;
// legacy method, sends list of occupied path sections over network
void
IsolatedBusyList() const;
// legacy method, sends state of specified path section over network
void
IsolatedBusy( std::string const &Name ) const;
};
//---------------------------------------------------------------------------

View File

@@ -318,6 +318,8 @@ bool TWorld::Init( GLFWwindow *Window ) {
Controlled = NULL;
mvControlled = NULL;
Camera.Type = tp_Free;
DebugCamera = Camera;
Global::DebugCameraPosition = DebugCamera.Pos;
}
// if (!Global::bMultiplayer) //na razie włączone
@@ -543,7 +545,7 @@ void TWorld::OnKeyDown(int cKey)
#ifdef EU07_USE_OLD_GROUNDCODE
TDynamicObject *tmp = Ground.DynamicNearest( Camera.Pos, 50, true ); //łapiemy z obsadą
#else
TDynamicObject *tmp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 50, true ) );
TDynamicObject *tmp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 50, true, false ) );
#endif
if( ( tmp != nullptr )
&& ( tmp != Controlled ) ) {
@@ -688,8 +690,12 @@ void TWorld::OnKeyDown(int cKey)
}
else if( ( cKey == GLFW_KEY_PAUSE ) && ( Global::ctrlState ) && ( Global::shiftState ) ) {
//[Ctrl]+[Break] hamowanie wszystkich pojazdów w okolicy // added shift to prevent odd issue with glfw producing pause presses on its own
if (Controlled->MoverParameters->Radio)
Ground.RadioStop(Camera.Pos);
if( Controlled->MoverParameters->Radio )
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.RadioStop( Camera.Pos );
#else
simulation::Region->RadioStop( Camera.Pos );
#endif
}
else if (!Global::iPause) //||(cKey==VK_F4)) //podczas pauzy sterownaie nie działa, F4 tak
if (Train)
@@ -725,7 +731,7 @@ void TWorld::OnKeyDown(int cKey)
#ifdef EU07_USE_OLD_GROUNDCODE
TDynamicObject *temp = Global::DynamicNearest();
#else
TDynamicObject *temp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) );
TDynamicObject *temp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) );
#endif
if (temp)
{
@@ -739,7 +745,11 @@ void TWorld::OnKeyDown(int cKey)
}
else if (cKey == Global::Keys[k_EndSign])
{ // Ra 2014-07: zabrane z kabiny
#ifdef EU07_USE_OLD_GROUNDCODE
TDynamicObject *tmp = Global::CouplerNearest(); // domyślnie wyszukuje do 20m
#else
TDynamicObject *tmp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, true ) );
#endif
if (tmp)
{
int CouplNr = (LengthSquared3(tmp->HeadPosition() - Camera.Pos) >
@@ -772,7 +782,7 @@ void TWorld::OnKeyDown(int cKey)
#ifdef EU07_USE_OLD_GROUNDCODE
TDynamicObject *temp = Global::DynamicNearest();
#else
TDynamicObject *temp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) );
TDynamicObject *temp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) );
#endif
if (temp)
{
@@ -795,7 +805,7 @@ void TWorld::OnKeyDown(int cKey)
#ifdef EU07_USE_OLD_GROUNDCODE
TDynamicObject *temp = Global::DynamicNearest();
#else
TDynamicObject *temp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false ) );
TDynamicObject *temp = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 20, false, false ) );
#endif
if (temp)
{
@@ -835,6 +845,8 @@ void TWorld::InOutKey( bool const Near )
Train->Dynamic()->bDisplayCab = false;
DistantView( Near );
}
DebugCamera = Camera;
Global::DebugCameraPosition = DebugCamera.Pos;
}
else
{ // jazda w kabinie
@@ -1172,9 +1184,9 @@ TWorld::Update_Camera( double const Deltatime ) {
if( !d )
d = Ground.DynamicNearest( Camera.Pos, 1000 ); // dalej szukanie, jesli bliżej nie ma
#else
TDynamicObject *d = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 300, false ) );
TDynamicObject *d = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 300, false, false ) );
if( !d )
d = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 1000, false ) ); // dalej szukanie, jesli bliżej nie ma
d = std::get<TDynamicObject *>( simulation::Region->find_vehicle( Global::pCameraPosition, 1000, false, false ) ); // dalej szukanie, jesli bliżej nie ma
#endif
if( d && pDynamicNearest ) {
// jeśli jakiś jest znaleziony wcześniej
@@ -1361,7 +1373,7 @@ TWorld::Update_UI() {
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.DynamicNearest( Camera.Pos ) :
#else
std::get<TDynamicObject *>( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) :
std::get<TDynamicObject *>( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) :
#endif
Controlled ); // w trybie latania lokalizujemy wg mapy
@@ -1446,7 +1458,7 @@ TWorld::Update_UI() {
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.DynamicNearest( Camera.Pos ) :
#else
std::get<TDynamicObject *>( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) :
std::get<TDynamicObject *>( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) :
#endif
Controlled ); // w trybie latania lokalizujemy wg mapy
@@ -1714,7 +1726,7 @@ TWorld::Update_UI() {
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.DynamicNearest( Camera.Pos ) :
#else
std::get<TDynamicObject *>( simulation::Region->find_vehicle( Camera.Pos, 20, false ) ) :
std::get<TDynamicObject *>( simulation::Region->find_vehicle( Camera.Pos, 20, false, false ) ) :
#endif
Controlled ); // w trybie latania lokalizujemy wg mapy
if( tmp == nullptr ) {
@@ -1908,7 +1920,6 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) + " rcvd" );
#ifdef EU07_USE_OLD_GROUNDCODE
if( Global::iMultiplayer ) {
// WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1));
TEvent *e = Ground.FindEvent(
std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) );
if( e )
@@ -1918,7 +1929,6 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
}
#else
if( Global::iMultiplayer ) {
// WriteLog("Komunikat: "+AnsiString(pRozkaz->Name1));
auto *event = simulation::Events.FindEvent( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) );
if( event != nullptr ) {
if( ( event->Type == tp_Multiple )
@@ -2027,7 +2037,6 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
if (t)
multiplayer::WyslijNamiary(t); // wysłanie informacji o pojeździe
#else
/*
// TODO: re-enable when messaging component is in place
auto *vehicle = (
pRozkaz->cString[ 1 ] == '*' ?
@@ -2036,27 +2045,42 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
if( vehicle != nullptr ) {
multiplayer::WyslijNamiary( vehicle ); // wysłanie informacji o pojeździe
}
*/
#endif
}
else {
// dla pustego wysyłamy ramki 6 z nazwami pojazdów AI (jeśli potrzebne wszystkie, to rozpoznać np. "*")
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.DynamicList();
#else
simulation::Vehicles.DynamicList();
#endif
}
}
break;
case 8: // ponowne wysłanie informacji o zajętych odcinkach toru
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy track" + " rcvd");
Ground.TrackBusyList();
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.TrackBusyList();
#else
simulation::Paths.TrackBusyList();
#endif
break;
case 9: // ponowne wysłanie informacji o zajętych odcinkach izolowanych
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " all busy isolated" + " rcvd");
Ground.IsolatedBusyList();
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.IsolatedBusyList();
#else
simulation::Paths.IsolatedBusyList();
#endif
break;
case 10: // badanie zajętości jednego odcinka izolowanego
CommLog(Now() + " " + to_string(pRozkaz->iComm) + " " +
std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])) + " rcvd");
#ifdef EU07_USE_OLD_GROUNDCODE
Ground.IsolatedBusy(std::string(pRozkaz->cString + 1, (unsigned)(pRozkaz->cString[0])));
#else
simulation::Paths.IsolatedBusy( std::string( pRozkaz->cString + 1, (unsigned)( pRozkaz->cString[ 0 ] ) ) );
#endif
break;
case 11: // ustawienie parametrów ruchu pojazdu
// Ground.IsolatedBusy(AnsiString(pRozkaz->cString+1,(unsigned)(pRozkaz->cString[0])));
@@ -2096,7 +2120,25 @@ void TWorld::OnCommandGet(multiplayer::DaneRozkaz *pRozkaz)
}
}
#else
// TODO: implement
if( pRozkaz->cString[ 1 ] ) // jeśli długość nazwy jest niezerowa
{ // szukamy pierwszego pojazdu o takiej nazwie i odsyłamy parametry ramką #13
auto *lookup = (
pRozkaz->cString[ 2 ] == '*' ?
simulation::Vehicles.find( Global::asHumanCtrlVehicle ) : // nazwa pojazdu użytkownika
simulation::Vehicles.find( std::string( pRozkaz->cString + 2, (unsigned)pRozkaz->cString[ 1 ] ) ) ); // nazwa pojazdu
if( lookup == nullptr ) { break; } // nothing found, nothing to do
auto *d { lookup };
while( d != nullptr ) {
d->Damage( pRozkaz->cString[ 0 ] );
d = d->Next(); // pozostałe też
}
d = lookup->Prev();
while( d != nullptr ) {
d->Damage( pRozkaz->cString[ 0 ] );
d = d->Prev(); // w drugą stronę też
}
multiplayer::WyslijUszkodzenia( lookup->asName, lookup->MoverParameters->EngDmgFlag ); // zwrot informacji o pojeździe
}
#endif
break;
}

View File

@@ -93,6 +93,7 @@ WyslijWolny(const std::string &t)
WyslijString(t, 4); // tor wolny
}
#ifdef EU07_USE_OLD_GROUNDCODE
void
WyslijNamiary(TGroundNode *t)
{ // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby
@@ -169,6 +170,84 @@ WyslijNamiary(TGroundNode *t)
// WriteLog("Ramka poszla!");
CommLog( Now() + " " + std::to_string(r.iComm) + " " + t->asName + " sent");
}
#else
void
WyslijNamiary(TDynamicObject const *Vehicle)
{ // wysłanie informacji o pojeździe - (float), długość ramki będzie zwiększana w miarę potrzeby
// WriteLog("Wysylam pojazd");
DaneRozkaz r;
r.iSygn = MAKE_ID4( 'E', 'U', '0', '7' );
r.iComm = 7; // 7 - dane pojazdu
int i = 32;
size_t j = Vehicle->asName.length();
r.iPar[0] = i; // ilość danych liczbowych
r.fPar[1] = Global::fTimeAngleDeg / 360.0; // aktualny czas (1.0=doba)
r.fPar[2] = Vehicle->MoverParameters->Loc.X; // pozycja X
r.fPar[3] = Vehicle->MoverParameters->Loc.Y; // pozycja Y
r.fPar[4] = Vehicle->MoverParameters->Loc.Z; // pozycja Z
r.fPar[5] = Vehicle->MoverParameters->V; // prędkość ruchu X
r.fPar[6] = Vehicle->MoverParameters->nrot * M_PI *
Vehicle->MoverParameters->WheelDiameter; // prędkość obrotowa kóŁ
r.fPar[7] = 0; // prędkość ruchu Z
r.fPar[8] = Vehicle->MoverParameters->AccS; // przyspieszenie X
r.fPar[9] = Vehicle->MoverParameters->AccN; // przyspieszenie Y //na razie nie
r.fPar[10] = Vehicle->MoverParameters->AccV; // przyspieszenie Z
r.fPar[11] = Vehicle->MoverParameters->DistCounter; // przejechana odległość w km
r.fPar[12] = Vehicle->MoverParameters->PipePress; // ciśnienie w PG
r.fPar[13] = Vehicle->MoverParameters->ScndPipePress; // ciśnienie w PZ
r.fPar[14] = Vehicle->MoverParameters->BrakePress; // ciśnienie w CH
r.fPar[15] = Vehicle->MoverParameters->Compressor; // ciśnienie w ZG
r.fPar[16] = Vehicle->MoverParameters->Itot; // Prąd całkowity
r.iPar[17] = Vehicle->MoverParameters->MainCtrlPos; // Pozycja NJ
r.iPar[18] = Vehicle->MoverParameters->ScndCtrlPos; // Pozycja NB
r.iPar[19] = Vehicle->MoverParameters->MainCtrlActualPos; // Pozycja jezdna
r.iPar[20] = Vehicle->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania
r.iPar[21] = Vehicle->MoverParameters->ScndCtrlActualPos; // Pozycja bocznikowania
r.iPar[22] = Vehicle->MoverParameters->ResistorsFlag * 1 +
Vehicle->MoverParameters->ConverterFlag * 2 +
+Vehicle->MoverParameters->CompressorFlag * 4 +
Vehicle->MoverParameters->Mains * 8 +
+Vehicle->MoverParameters->DoorLeftOpened * 16 +
Vehicle->MoverParameters->DoorRightOpened * 32 +
+Vehicle->MoverParameters->FuseFlag * 64 +
Vehicle->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 < Vehicle->iAnimType[ANIM_PANTS])
{
r.fPar[23 + p] = Vehicle->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] =
Vehicle->MoverParameters->ShowCurrent(p + 1); // amperomierze kolejnych grup
// WriteLog("zapisalem prady");
r.iPar[30] = Vehicle->MoverParameters->WarningSignal; // trabienie
r.fPar[31] = Vehicle->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, Vehicle->asName.c_str()); // zakończony zerem
COPYDATASTRUCT cData;
cData.dwData = MAKE_ID4( 'E', 'U', '0', '7' ); // sygnatura
cData.cbData = (DWORD)(10 + i + j); // 8+licznik i zero kończące
cData.lpData = &r;
// WriteLog("Ramka gotowa");
Navigate( "TEU07SRK", WM_COPYDATA, (WPARAM)glfwGetWin32Window( Global::window ), (LPARAM)&cData );
// WriteLog("Ramka poszla!");
CommLog( Now() + " " + std::to_string(r.iComm) + " " + Vehicle->asName + " sent");
}
#endif
void
WyslijObsadzone()

View File

@@ -11,7 +11,11 @@ http://mozilla.org/MPL/2.0/.
#include <string>
#ifdef EU07_USE_OLD_GROUNDCODE
class TGroundNode;
#else
class TDynamicObject;
#endif
namespace multiplayer {
@@ -40,7 +44,11 @@ void Navigate( std::string const &ClassName, UINT Msg, WPARAM wParam, LPARAM lPa
void WyslijEvent( const std::string &e, const std::string &d );
void WyslijString( const std::string &t, int n );
void WyslijWolny( const std::string &t );
#ifdef EU07_USE_OLD_GROUNDCODE
void WyslijNamiary( TGroundNode *t );
#else
void WyslijNamiary( TDynamicObject const *Vehicle );
#endif
void WyslijParam( int nr, int fl );
void WyslijUszkodzenia( const std::string &t, char fl );
void WyslijObsadzone(); // -> skladanie wielu pojazdow

View File

@@ -349,6 +349,15 @@ opengl_renderer::Render() {
void
opengl_renderer::Render_pass( rendermode const Mode ) {
#ifdef EU07_USE_DEBUG_CAMERA
// setup world camera for potential visualization
setup_pass(
m_worldcamera,
rendermode::color,
0.f,
1.0,
true );
#endif
setup_pass( m_renderpass, Mode );
switch( m_renderpass.draw_mode ) {
@@ -365,14 +374,6 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
#endif
shadowcamera = m_renderpass.camera; // cache shadow camera placement for visualization
setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack
#ifdef EU07_USE_DEBUG_CAMERA
setup_pass(
m_worldcamera,
rendermode::color,
0.f,
1.0,
true );
#endif
// setup shadowmap matrix
m_shadowtexturematrix =
//bias from [-1, 1] to [0, 1] };
@@ -383,7 +384,6 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
glm::mat4{ glm::mat3{ shadowcamera.modelview() } },
glm::vec3{ m_renderpass.camera.position() - shadowcamera.position() } );
}
if( ( true == m_environmentcubetexturesupport )
&& ( true == World.InitPerformed() ) ) {
// potentially update environmental cube map
@@ -391,7 +391,6 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack
}
}
::glViewport( 0, 0, Global::iWindowWidth, Global::iWindowHeight );
if( World.InitPerformed() ) {
@@ -420,7 +419,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) {
::glColor4f( 1.f, 0.9f, 0.8f, 1.f );
::glDisable( GL_LIGHTING );
::glDisable( GL_TEXTURE_2D );
shadowcamera.draw( m_renderpass.camera.position() - shadowcamera.position() );
if( true == Global::RenderShadows ) {
shadowcamera.draw( m_renderpass.camera.position() - shadowcamera.position() );
}
if( DebugCameraFlag ) {
::glColor4f( 0.8f, 1.f, 0.9f, 1.f );
m_worldcamera.camera.draw( m_renderpass.camera.position() - m_worldcamera.camera.position() );
@@ -1920,7 +1921,7 @@ opengl_renderer::Render( section_sequence::iterator First, section_sequence::ite
// render
#ifdef EU07_USE_DEBUG_CULLING
// debug
float const width = scene::EU07_SECTIONSIZE * 0.5f;
float const width = section->m_area.radius;
float const height = scene::EU07_SECTIONSIZE * 0.2f;
glDisable( GL_LIGHTING );
glDisable( GL_TEXTURE_2D );
@@ -2008,11 +2009,10 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
// first pass draws elements which we know are located in section banks, to reduce vbo switching
while( First != Last ) {
auto const *cell = First->second;
/*
// przeliczenia animacji torów w sektorze
Groundsubcell->RaAnimate( m_framestamp );
*/
auto *cell = First->second;
// przeliczenia animacji torów w sektorze
cell->RaAnimate( m_framestamp );
switch( m_renderpass.draw_mode ) {
case rendermode::color: {
// since all shapes of the section share center point we can optimize out a few calls here
@@ -2023,7 +2023,7 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
// render
#ifdef EU07_USE_DEBUG_CULLING
// debug
float const width = scene::EU07_CELLSIZE * 0.5f;
float const width = cell->m_area.radius;
float const height = scene::EU07_CELLSIZE * 0.15f;
glDisable( GL_LIGHTING );
glDisable( GL_TEXTURE_2D );
@@ -2149,25 +2149,8 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
void
opengl_renderer::Render( scene::shape_node const &Shape, bool const Ignorerange ) {
/*
double distancesquared;
switch( m_renderpass.draw_mode ) {
case rendermode::shadows: {
// 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees
distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor;
break;
}
default: {
distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor;
break;
}
}
if( ( distancesquared < Node->fSquareMinRadius )
|| ( distancesquared >= Node->fSquareRadius ) ) {
return false;
}
*/
auto const &data{ Shape.data() };
auto const &data { Shape.data() };
if( false == Ignorerange ) {
double distancesquared;
@@ -3075,8 +3058,8 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) {
// render
m_geometry.draw( Node->Piece->geometry );
// ++m_debugstats.lines;
// ++m_debugstats.drawcalls;
++m_debugstats.lines;
++m_debugstats.drawcalls;
// post-render cleanup
::glPopMatrix();
@@ -3183,8 +3166,9 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque
while( first != Last ) {
auto const *cell = first->second;
// TODO: include lines here
if( false == cell->m_traction.empty() ) {
if( ( false == cell->m_traction.empty()
|| ( false == cell->m_lines.empty() ) ) ) {
// since all shapes of the cell share center point we can optimize out a few calls here
::glPushMatrix();
auto const originoffset { cell->m_area.center - m_renderpass.camera.position() };
@@ -3196,6 +3180,7 @@ opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_seque
Bind_Material( null_handle );
// render
for( auto *traction : cell->m_traction ) { Render_Alpha( traction ); }
for( auto &lines : cell->m_lines ) { Render_Alpha( lines ); }
// post-render cleanup
::glLineWidth( 1.0 );
if( !Global::bSmoothTraction ) {
@@ -3287,6 +3272,48 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) {
++m_debugstats.traction;
++m_debugstats.drawcalls;
}
void
opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) {
auto const &data { Lines.data() };
double distancesquared;
switch( m_renderpass.draw_mode ) {
case rendermode::shadows: {
// 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees
distancesquared = SquareMagnitude( ( data.area.center - Global::pCameraPosition ) / Global::ZoomFactor ) / Global::fDistanceFactor;
break;
}
default: {
distancesquared = SquareMagnitude( ( data.area.center - m_renderpass.camera.position() ) / Global::ZoomFactor ) / Global::fDistanceFactor;
break;
}
}
if( ( distancesquared < data.rangesquared_min )
|| ( distancesquared >= data.rangesquared_max ) ) {
return;
}
// setup
auto const distance { static_cast<float>( std::sqrt( distancesquared ) ) };
auto const linealpha = (
data.line_width > 0.f ?
10.f * data.line_width
/ std::max(
0.5f * data.area.radius + 1.f,
distance - ( 0.5f * data.area.radius ) ) :
1.f ); // negative width means the lines are always opague
::glColor4fv(
glm::value_ptr(
glm::vec4{
glm::vec3{ data.lighting.diffuse * Global::DayLight.ambient }, // w zaleznosci od koloru swiatla
std::min( 1.f, linealpha ) } ) );
::glLineWidth( clamp( 0.5f * linealpha + data.line_width * data.area.radius / 1000.f, 1.f, 8.f ) );
// render
m_geometry.draw( data.geometry );
++m_debugstats.lines;
++m_debugstats.drawcalls;
}
#endif
bool
opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) {

View File

@@ -340,6 +340,8 @@ private:
Render_Alpha( TAnimModel *Instance );
void
Render_Alpha( TTraction *Traction );
void
Render_Alpha( scene::lines_node const &Lines );
#endif
bool
Render_Alpha( TDynamicObject *Dynamic );

298
scene.cpp
View File

@@ -130,6 +130,44 @@ basic_cell::update_traction( TDynamicObject *Vehicle, int const Pantographindex
}
}
// legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell
void
basic_cell::radio_stop() {
for( auto *path : m_paths ) {
path->RadioStop();
}
}
// legacy method, adds specified path to the list of pieces undergoing state change
bool
basic_cell::RaTrackAnimAdd( TTrack *Track ) {
if( false == m_geometrycreated ) {
// nie ma animacji, gdy nie widać
return true;
}
if (tTrackAnim)
tTrackAnim->RaAnimListAdd(Track);
else
tTrackAnim = Track;
return false; // będzie animowane...
}
// legacy method, updates geometry for pieces in the animation list
void
basic_cell::RaAnimate( unsigned int const Framestamp ) {
if( ( tTrackAnim == nullptr )
|| ( Framestamp == m_framestamp ) ) {
// nie ma nic do animowania
return;
}
tTrackAnim = tTrackAnim->RaAnimate(); // przeliczenie animacji kolejnego
m_framestamp = Framestamp;
}
// adds provided shape to the cell
void
basic_cell::insert( shape_node Shape ) {
@@ -160,15 +198,51 @@ basic_cell::insert( shape_node Shape ) {
shapes.emplace_back( Shape );
}
// adds provided lines to the cell
void
basic_cell::insert( lines_node Lines ) {
m_active = true;
auto const &linesdata { Lines.data() };
for( auto &targetlines : m_lines ) {
// try to merge shapes with matching view ranges...
auto const &targetlinesdata { targetlines.data() };
if( ( linesdata.rangesquared_min == targetlinesdata.rangesquared_min )
&& ( linesdata.rangesquared_max == targetlinesdata.rangesquared_max )
// ...and located close to each other (within arbitrary limit of 10m)
&& ( glm::length( linesdata.area.center - targetlinesdata.area.center ) < 10.0 ) ) {
if( true == targetlines.merge( Lines ) ) {
// if the shape was merged there's nothing left to do
return;
}
}
}
// otherwise add the shape to the relevant list
Lines.origin( m_area.center );
m_lines.emplace_back( Lines );
}
// adds provided path to the cell
void
basic_cell::insert( TTrack *Path ) {
m_active = true;
// TODO: add animation hook
Path->origin( m_area.center );
m_paths.emplace_back( Path );
// animation hook
#ifndef EU07_USE_OLD_GROUNDCODE
Path->RaOwnerSet( this );
#endif
// re-calculate cell radius, in case track extends outside the cell's boundaries
auto endpoints = Path->endpoints();
for( auto &endpoint : endpoints ) {
m_area.radius = std::max<float>(
m_area.radius,
glm::length( m_area.center - endpoint ) + 25.f ); // extra margin to prevent driven vehicle from flicking
}
}
// adds provided traction piece to the cell
@@ -179,6 +253,13 @@ basic_cell::insert( TTraction *Traction ) {
Traction->origin( m_area.center );
m_traction.emplace_back( Traction );
// re-calculate cell radius, in case traction piece extends outside the cell's boundaries
auto endpoints = Traction->endpoints();
for( auto &endpoint : endpoints ) {
m_area.radius = std::max<float>(
m_area.radius,
glm::length( m_area.center - endpoint ) ); // adding arbitrary safety margin
}
}
// adds provided model instance to the cell
@@ -203,6 +284,15 @@ basic_cell::insert( TAnimModel *Instance ) {
// opaque pieces
m_instancesopaque.emplace_back( Instance );
}
// re-calculate cell radius, in case model extends outside the cell's boundaries
if( Instance->Model() ) {
auto const modelradius{ Instance->Model()->bounding_radius() };
if( modelradius > 0.f ) {
m_area.radius = std::max<float>(
m_area.radius,
glm::length( m_area.center - Instance->location() ) + modelradius ); // adding arbitrary safety margin
}
}
}
// adds provided sound instance to the cell
@@ -251,7 +341,7 @@ basic_cell::register_end( TTraction *Traction ) {
// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) {
basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) {
TDynamicObject *vehiclenearest { nullptr };
float leastdistance { std::numeric_limits<float>::max() };
@@ -264,7 +354,16 @@ basic_cell::find( glm::dvec3 const &Point, float const Radius, bool const Onlyco
&& ( vehicle->Mechanik == nullptr ) ) {
continue;
}
distance = glm::length2( glm::dvec3{ vehicle->GetPosition() } - Point );
if( false == Findbycoupler ) {
// basic search, checks vehicles' center points
distance = glm::length2( glm::dvec3{ vehicle->GetPosition() } - Point );
}
else {
// alternative search, checks positions of vehicles' couplers
distance = std::min(
glm::length2( glm::dvec3{ vehicle->HeadPosition() } - Point ),
glm::length2( glm::dvec3{ vehicle->RearPosition() } - Point ) );
}
if( ( distance > distancecutoff )
|| ( distance > leastdistance ) ){
continue;
@@ -372,10 +471,13 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) {
for( auto *path : m_paths ) { path->create_geometry( Bank ); }
for( auto *traction : m_traction ) { traction->create_geometry( Bank ); }
#endif
for( auto &lines : m_lines ) { lines.create_geometry( Bank ); }
// arrange content by assigned materials to minimize state switching
std::sort(
std::begin( m_paths ), std::end( m_paths ),
TTrack::sort_by_material );
m_geometrycreated = true; // helper for legacy animation code, get rid of it after refactoring
}
@@ -384,11 +486,9 @@ basic_cell::create_geometry( geometrybank_handle const &Bank ) {
void
basic_section::update( glm::dvec3 const &Location, float const Radius ) {
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) };
for( auto &cell : m_cells ) {
if( glm::length2( cell.area().center - Location ) < squaredradii ) {
if( glm::length2( cell.area().center - Location ) < ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) {
// we reject cells which aren't within our area of interest
cell.update();
}
@@ -408,16 +508,28 @@ basic_section::update_traction( TDynamicObject *Vehicle, int const Pantographind
auto const pantographposition = position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x );
auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) };
for( auto &cell : m_cells ) {
// we reject early cells which aren't within our area of interest
if( glm::length2( cell.area().center - pantographposition ) < squaredradii ) {
if( glm::length2( cell.area().center - pantographposition ) < ( ( cell.area().radius + radius ) * ( cell.area().radius + radius ) ) ) {
cell.update_traction( Vehicle, Pantographindex );
}
}
}
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
basic_section::radio_stop( glm::dvec3 const &Location, float const Radius ) {
for( auto &cell : m_cells ) {
if( glm::length2( cell.area().center - Location ) < ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) {
// we reject cells which aren't within our area of interest
cell.radio_stop();
}
}
}
// adds provided shape to the section
void
basic_section::insert( shape_node Shape ) {
@@ -444,9 +556,16 @@ basic_section::insert( shape_node Shape ) {
}
}
// adds provided lines to the section
void
basic_section::insert( lines_node Lines ) {
cell( Lines.data().area.center ).insert( Lines );
}
// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) {
basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) {
// go through sections within radius of interest, and pick the nearest candidate
TDynamicObject
@@ -456,14 +575,12 @@ basic_section::find( glm::dvec3 const &Point, float const Radius, bool const Onl
distancefound,
distancenearest { std::numeric_limits<float>::max() };
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + Radius, 2 ) };
for( auto &cell : m_cells ) {
// we reject early cells which aren't within our area of interest
if( glm::length2( cell.area().center - Point ) > squaredradii ) {
if( glm::length2( cell.area().center - Point ) > ( ( cell.area().radius + Radius ) * ( cell.area().radius + Radius ) ) ) {
continue;
}
std::tie( vehiclefound, distancefound ) = cell.find( Point, Radius, Onlycontrolled );
std::tie( vehiclefound, distancefound ) = cell.find( Point, Radius, Onlycontrolled, Findbycoupler );
if( ( vehiclefound != nullptr )
&& ( distancefound < distancenearest ) ) {
@@ -503,11 +620,10 @@ basic_section::find( glm::dvec3 const &Point, TTraction const *Other, int const
endpointnearest { -1 };
auto const radius { 0.0 }; // { EU07_CELLSIZE * 0.5 }; // experimentally limited, check if it has any negative effect
auto const squaredradii { std::pow( ( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) + radius, 2 ) };
for( auto &cell : m_cells ) {
// we reject early cells which aren't within our area of interest
if( glm::length2( cell.area().center - Point ) > squaredradii ) {
if( glm::length2( cell.area().center - Point ) > ( ( cell.area().radius + radius ) * ( cell.area().radius + radius ) ) ) {
continue;
}
std::tie( tractionfound, endpointfound, distancefound ) = cell.find( Point, Other, Currentdirection );
@@ -618,8 +734,49 @@ basic_region::update_traction( TDynamicObject *Vehicle, int const Pantographinde
}
}
// legacy method, links specified path piece with potential neighbours
void
basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) {
basic_region::TrackJoin( TTrack *Track ) {
// wyszukiwanie sąsiednich torów do podłączenia (wydzielone na użytek obrotnicy)
TTrack *matchingtrack;
int endpointid;
if( Track->CurrentPrev() == nullptr ) {
std::tie( matchingtrack, endpointid ) = find_path( Track->CurrentSegment()->FastGetPoint_0(), Track );
switch( endpointid ) {
case 0:
Track->ConnectPrevPrev( matchingtrack, 0 );
break;
case 1:
Track->ConnectPrevNext( matchingtrack, 1 );
break;
}
}
if( Track->CurrentNext() == nullptr ) {
std::tie( matchingtrack, endpointid ) = find_path( Track->CurrentSegment()->FastGetPoint_1(), Track );
switch( endpointid ) {
case 0:
Track->ConnectNextPrev( matchingtrack, 0 );
break;
case 1:
Track->ConnectNextNext( matchingtrack, 1 );
break;
}
}
}
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
basic_region::RadioStop( glm::dvec3 const &Location ) {
auto const range = 2000.f;
auto const &sectionlist = sections( Location, range );
for( auto *section : sectionlist ) {
section->radio_stop( Location, range );
}
}
void
basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool const Transform ) {
// shape might need to be split into smaller pieces, so we create list of nodes instead of just single one
// using deque so we can do single pass iterating and addding generated pieces without invalidating anything
@@ -628,38 +785,41 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) {
if( shape.m_data.vertices.empty() ) { return; }
// adjust input if necessary:
if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) {
// rotate...
auto const rotation = glm::radians( Scratchpad.location_rotation );
for( auto &vertex : shape.m_data.vertices ) {
vertex.position = glm::rotateZ<double>( vertex.position, rotation.z );
vertex.position = glm::rotateX<double>( vertex.position, rotation.x );
vertex.position = glm::rotateY<double>( vertex.position, rotation.y );
vertex.normal = glm::rotateZ( vertex.normal, rotation.z );
vertex.normal = glm::rotateX( vertex.normal, rotation.x );
vertex.normal = glm::rotateY( vertex.normal, rotation.y );
if( true == Transform ) {
// shapes generated from legacy terrain come with world space coordinates and don't need processing
if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) {
// rotate...
auto const rotation = glm::radians( Scratchpad.location_rotation );
for( auto &vertex : shape.m_data.vertices ) {
vertex.position = glm::rotateZ<double>( vertex.position, rotation.z );
vertex.position = glm::rotateX<double>( vertex.position, rotation.x );
vertex.position = glm::rotateY<double>( vertex.position, rotation.y );
vertex.normal = glm::rotateZ( vertex.normal, rotation.z );
vertex.normal = glm::rotateX( vertex.normal, rotation.x );
vertex.normal = glm::rotateY( vertex.normal, rotation.y );
}
}
}
if( ( false == Scratchpad.location_offset.empty() )
&& ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) {
// ...and move
auto const offset = Scratchpad.location_offset.top();
for( auto &vertex : shape.m_data.vertices ) {
vertex.position += offset;
if( ( false == Scratchpad.location_offset.empty() )
&& ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) {
// ...and move
auto const offset = Scratchpad.location_offset.top();
for( auto &vertex : shape.m_data.vertices ) {
vertex.position += offset;
}
}
}
// calculate bounding area
for( auto const &vertex : shape.m_data.vertices ) {
shape.m_data.area.center += vertex.position;
}
shape.m_data.area.center /= shape.m_data.vertices.size();
// trim the shape if needed. trimmed parts will be added to list as separate nodes
for( std::size_t index = 0; index < shapes.size(); ++index ) {
while( true == RaTriangleDivider( shapes[ index ], shapes ) ) {
; // all work is done during expression check
// calculate bounding area
for( auto const &vertex : shape.m_data.vertices ) {
shape.m_data.area.center += vertex.position;
}
shape.m_data.area.center /= shape.m_data.vertices.size();
// trim the shape if needed. trimmed parts will be added to list as separate nodes
for( std::size_t index = 0; index < shapes.size(); ++index ) {
while( true == RaTriangleDivider( shapes[ index ], shapes ) ) {
; // all work is done during expression check
}
// with the trimming done we can calculate shape's bounding radius
shape.compute_radius();
}
// with the trimming done we can calculate shape's bounding radius
shape.compute_radius();
}
// move the data into appropriate section(s)
for( auto &shape : shapes ) {
@@ -679,6 +839,50 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad ) {
}
}
// inserts provided lines in the region
void
basic_region::insert_lines( lines_node Lines, scratch_data &Scratchpad ) {
if( Lines.m_data.vertices.empty() ) { return; }
// transform point coordinates if needed
if( Scratchpad.location_rotation != glm::vec3( 0, 0, 0 ) ) {
// rotate...
auto const rotation = glm::radians( Scratchpad.location_rotation );
for( auto &vertex : Lines.m_data.vertices ) {
vertex.position = glm::rotateZ<double>( vertex.position, rotation.z );
vertex.position = glm::rotateX<double>( vertex.position, rotation.x );
vertex.position = glm::rotateY<double>( vertex.position, rotation.y );
}
}
if( ( false == Scratchpad.location_offset.empty() )
&& ( Scratchpad.location_offset.top() != glm::dvec3( 0, 0, 0 ) ) ) {
// ...and move
auto const offset = Scratchpad.location_offset.top();
for( auto &vertex : Lines.m_data.vertices ) {
vertex.position += offset;
}
}
// calculate bounding area
for( auto const &vertex : Lines.m_data.vertices ) {
Lines.m_data.area.center += vertex.position;
}
Lines.m_data.area.center /= Lines.m_data.vertices.size();
Lines.compute_radius();
// move the data into appropriate section
if( point_inside( Lines.m_data.area.center ) ) {
// NOTE: nodes placed outside of region boundaries are discarded
section( Lines.m_data.area.center ).insert( Lines );
}
else {
ErrorLog(
"Bad scenario: lines node" + (
Lines.m_name.empty() ?
"" :
" \"" + Lines.m_name + "\"" )
+ " placed in location outside region bounds (" + to_string( Lines.m_data.area.center ) + ")" );
}
}
// inserts provided track in the region
void
basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) {
@@ -776,7 +980,7 @@ basic_region::insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpa
// find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers
std::tuple<TDynamicObject *, float>
basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled ) {
basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) {
auto const &sectionlist = sections( Point, Radius );
// go through sections within radius of interest, and pick the nearest candidate
@@ -788,7 +992,7 @@ basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool co
nearestdistance { std::numeric_limits<float>::max() };
for( auto *section : sectionlist ) {
std::tie( foundvehicle, founddistance ) = section->find( Point, Radius, Onlycontrolled );
std::tie( foundvehicle, founddistance ) = section->find( Point, Radius, Onlycontrolled, Findbycoupler );
if( ( foundvehicle != nullptr )
&& ( founddistance < nearestdistance ) ) {

58
scene.h
View File

@@ -61,9 +61,21 @@ public:
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell
void
radio_stop();
// legacy method, adds specified path to the list of pieces undergoing state change
bool
RaTrackAnimAdd( TTrack *Track );
// legacy method, updates geometry for pieces in the animation list
void
RaAnimate( unsigned int const Framestamp );
// adds provided shape to the cell
void
insert( shape_node Shape );
// adds provided lines to the cell
void
insert( lines_node Lines );
// adds provided path to the cell
void
insert( TTrack *Path );
@@ -85,9 +97,9 @@ public:
// registers provided traction piece in the lookup directory of the cell
void
register_end( TTraction *Traction );
// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled );
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find( glm::dvec3 const &Point, TTrack const *Exclude );
@@ -111,19 +123,19 @@ public:
private:
// types
using shapenode_sequence = std::vector<shape_node>;
using linesnode_sequence = std::vector<lines_node>;
using path_sequence = std::vector<TTrack *>;
// using path_set = std::unordered_set<TTrack *>;
using traction_sequence = std::vector<TTraction *>;
// using traction_set = std::unordered_set<TTraction *>;
using instance_sequence = std::vector<TAnimModel *>;
using sound_sequence = std::vector<TTextSound *>;
using eventlauncher_sequence = std::vector<TEventLauncher *>;
// members
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_CELLSIZE + 0.25 * EU07_CELLSIZE ) };
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_CELLSIZE ) };
bool m_active { false }; // whether the cell holds any actual data
// content
shapenode_sequence m_shapesopaque; // opaque pieces of geometry
shapenode_sequence m_shapestranslucent; // translucent pieces of geometry
linesnode_sequence m_lines;
path_sequence m_paths; // path pieces
instance_sequence m_instancesopaque;
instance_sequence m_instancetranslucent;
@@ -135,6 +147,10 @@ private:
path_sequence paths;
traction_sequence traction;
} m_directories;
// animation of owned items (legacy code, clean up along with track refactoring)
bool m_geometrycreated { false };
unsigned int m_framestamp { 0 }; // id of last rendered gfx frame
TTrack *tTrackAnim = nullptr; // obiekty do przeliczenia animacji
};
// basic scene partitioning structure, holds terrain geometry and collection of cells
@@ -150,22 +166,33 @@ public:
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
radio_stop( glm::dvec3 const &Location, float const Radius );
// adds provided shape to the section
void
insert( shape_node Shape );
// adds provided lines to the section
void
insert( lines_node Lines );
// adds provided node to the section
template <class Type_>
void
insert( Type_ *Node ) {
cell( Node->location() ).insert( Node ); }
auto &targetcell { cell( Node->location() ) };
targetcell.insert( Node );
// some node types can extend bounding area of the target cell
m_area.radius = std::max<float>(
m_area.radius,
glm::length( m_area.center - targetcell.area().center ) + targetcell.area().radius ); }
// registers provided node in the lookup directory of the section enclosing specified point
template <class Type_>
void
register_node( Type_ *Node, glm::dvec3 const &Point ) {
cell( Point ).register_end( Node ); }
// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled );
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find( glm::dvec3 const &Point, TTrack const *Exclude );
@@ -223,9 +250,18 @@ public:
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
void
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
// legacy method, links specified path piece with potential neighbours
void
TrackJoin( TTrack *Track );
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
void
RadioStop( glm::dvec3 const &Location );
// inserts provided shape in the region
void
insert_shape( shape_node Shape, scratch_data &Scratchpad );
insert_shape( shape_node Shape, scratch_data &Scratchpad, bool const Transform );
// inserts provided lines in the region
void
insert_lines( lines_node Lines, scratch_data &Scratchpad );
// inserts provided track in the region
void
insert_path( TTrack *Path, scratch_data &Scratchpad );
@@ -241,9 +277,9 @@ public:
// inserts provided event launcher in the region
void
insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpad );
// find a vehicle located nearest to specified point, within specified radius, optionally ignoring vehicles without drivers. reurns: located vehicle and distance
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
std::tuple<TDynamicObject *, float>
find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled );
find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
std::tuple<TTrack *, int>
find_path( glm::dvec3 const &Point, TTrack const *Exclude );

View File

@@ -207,6 +207,65 @@ shape_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) {
return *this;
}
// imports data from provided submodel
shape_node &
shape_node::convert( TSubModel const *Submodel ) {
m_name = Submodel->pName;
m_data.lighting.ambient = Submodel->f4Ambient;
m_data.lighting.diffuse = Submodel->f4Diffuse;
m_data.lighting.specular = Submodel->f4Specular;
m_data.material = Submodel->m_material;
m_data.translucent = ( true == GfxRenderer.Material( m_data.material ).has_alpha );
// NOTE: we set unlimited view range typical for terrain, because we don't expect to convert any other 3d models
m_data.rangesquared_max = std::numeric_limits<double>::max();
if( Submodel->m_geometry == null_handle ) { return *this; }
int vertexcount { 0 };
std::vector<world_vertex> importedvertices;
world_vertex vertex, vertex1, vertex2;
for( auto const &sourcevertex : GfxRenderer.Vertices( Submodel->m_geometry ) ) {
vertex.position = sourcevertex.position;
vertex.normal = sourcevertex.normal;
vertex.texture = sourcevertex.texture;
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
if( false == degenerate( vertex1.position, vertex2.position, vertex.position ) ) {
importedvertices.emplace_back( vertex1 );
importedvertices.emplace_back( vertex2 );
importedvertices.emplace_back( vertex );
}
// start a new triangle
vertexcount = -1;
}
++vertexcount;
}
if( true == importedvertices.empty() ) { return *this; }
// assign imported geometry to the node...
m_data.vertices.swap( importedvertices );
// ...and calculate center...
for( auto const &vertex : m_data.vertices ) {
m_data.area.center += vertex.position;
}
m_data.area.center /= m_data.vertices.size();
// ...and bounding area
double squareradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squareradius = std::max(
squareradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = std::max<float>(
m_data.area.radius,
std::sqrt( squareradius ) );
return *this;
}
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
shape_node::merge( shape_node &Shape ) {
@@ -260,6 +319,151 @@ shape_node::compute_radius() {
}
// restores content of the node from provded input stream
lines_node &
lines_node::deserialize( cParser &Input, scene::node_data const &Nodedata ) {
// import common data
m_name = Nodedata.name;
m_data.rangesquared_min = Nodedata.range_min * Nodedata.range_min;
m_data.rangesquared_max = (
Nodedata.range_max >= 0.0 ?
Nodedata.range_max * Nodedata.range_max :
std::numeric_limits<double>::max() );
// material
Input.getTokens( 3, false );
Input
>> m_data.lighting.diffuse.r
>> m_data.lighting.diffuse.g
>> m_data.lighting.diffuse.b;
m_data.lighting.diffuse /= 255.f;
m_data.lighting.diffuse.a = 1.f;
Input.getTokens( 1, false );
Input
>> m_data.line_width;
m_data.line_width = std::min( 30.f, m_data.line_width ); // 30 pix equals rougly width of a signal pole viewed from ~1m away
// geometry
enum subtype {
lines,
line_strip,
line_loop
} const nodetype = (
Nodedata.type == "lines" ? lines :
Nodedata.type == "line_strip" ? line_strip :
line_loop );
std::size_t vertexcount { 0 };
world_vertex vertex, vertex0, vertex1;
std::string token = Input.getToken<std::string>();
do {
vertex.position.x = std::atof( token.c_str() );
Input.getTokens( 2, false );
Input
>> vertex.position.y
>> vertex.position.z;
// convert all data to gl_lines to allow data merge for matching nodes
switch( nodetype ) {
case lines: {
m_data.vertices.emplace_back( vertex );
break;
}
case line_strip: {
if( vertexcount > 0 ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex );
}
vertex1 = vertex;
++vertexcount;
break;
}
case line_loop: {
if( vertexcount == 0 ) {
vertex0 = vertex;
vertex1 = vertex;
}
else {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex );
}
vertex1 = vertex;
++vertexcount;
break;
}
default: { break; }
}
token = Input.getToken<std::string>();
} while( token != "endline" );
// add closing line for the loop
if( ( nodetype == line_loop )
&& ( vertexcount > 2 ) ) {
m_data.vertices.emplace_back( vertex1 );
m_data.vertices.emplace_back( vertex0 );
}
if( m_data.vertices.size() % 2 != 0 ) {
ErrorLog( "Lines node specified odd number of vertices, encountered in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" );
m_data.vertices.pop_back();
}
return *this;
}
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
lines_node::merge( lines_node &Lines ) {
if( ( m_data.line_width != Lines.m_data.line_width )
|| ( m_data.lighting != Lines.m_data.lighting ) ) {
// can't merge nodes with different appearance
return false;
}
// add geometry from provided node
m_data.area.center =
interpolate(
m_data.area.center, Lines.m_data.area.center,
static_cast<float>( Lines.m_data.vertices.size() ) / ( Lines.m_data.vertices.size() + m_data.vertices.size() ) );
m_data.vertices.insert(
std::end( m_data.vertices ),
std::begin( Lines.m_data.vertices ), std::end( Lines.m_data.vertices ) );
// NOTE: we could recalculate radius with something other than brute force, but it'll do
compute_radius();
return true;
}
// generates renderable version of held non-instanced geometry in specified geometry bank
void
lines_node::create_geometry( geometrybank_handle const &Bank ) {
vertex_array vertices; vertices.reserve( m_data.vertices.size() );
for( auto const &vertex : m_data.vertices ) {
vertices.emplace_back(
vertex.position - m_data.origin,
vertex.normal,
vertex.texture );
}
m_data.geometry = GfxRenderer.Insert( vertices, Bank, GL_LINES );
std::vector<world_vertex>().swap( m_data.vertices ); // hipster shrink_to_fit
}
// calculates node's bounding radius
void
lines_node::compute_radius() {
auto squaredradius { 0.0 };
for( auto const &vertex : m_data.vertices ) {
squaredradius = std::max(
squaredradius,
glm::length2( vertex.position - m_data.area.center ) );
}
m_data.area.radius = static_cast<float>( std::sqrt( squaredradius ) );
}
/*
memory_node &
memory_node::deserialize( cParser &Input, node_data const &Nodedata ) {
@@ -279,6 +483,8 @@ memory_node::deserialize( cParser &Input, node_data const &Nodedata ) {
*/
} // scene
namespace editor {
basic_node::basic_node( scene::node_data const &Nodedata ) :

View File

@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
#include "vertex.h"
#include "openglgeometrybank.h"
#include "parser.h"
#include "model3d.h"
struct lighting_data {
@@ -104,6 +105,9 @@ public:
// restores content of the node from provded input stream
shape_node &
deserialize( cParser &Input, scene::node_data const &Nodedata );
// imports data from provided submodel
shape_node &
convert( TSubModel const *Submodel );
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
merge( shape_node &Shape );
@@ -132,6 +136,59 @@ private:
shapenode_data m_data;
};
// holds a group of untextured lines
class lines_node {
friend class basic_region; // region might want to modify node content when it's being inserted
public:
// types
struct linesnode_data {
// placement and visibility
scene::bounding_area area; // bounding area, in world coordinates
bool visible { true }; // visibility flag
double rangesquared_min { 0.0 }; // visibility range, min
double rangesquared_max { 0.0 }; // visibility range, max
// material data
lighting_data lighting;
float line_width; // thickness of stored lines
// geometry data
std::vector<world_vertex> vertices; // world space source data of the geometry
glm::dvec3 origin; // world position of the relative coordinate system origin
geometry_handle geometry { 0, 0 }; // relative origin-centered chunk of geometry held by gfx renderer
};
// methods
// restores content of the node from provded input stream
lines_node &
deserialize( cParser &Input, scene::node_data const &Nodedata );
// adds content of provided node to already enclosed geometry. returns: true if merge could be performed
bool
merge( lines_node &Lines );
// generates renderable version of held non-instanced geometry in specified geometry bank
void
create_geometry( geometrybank_handle const &Bank );
// calculates shape's bounding radius
void
compute_radius();
// set visibility
void
visible( bool State ) {
m_data.visible = State; }
// set origin point
void
origin( glm::dvec3 Origin ) {
m_data.origin = Origin; }
// data access
linesnode_data const &
data() const {
return m_data; }
private:
// members
std::string m_name;
linesnode_data m_data;
};
/*
// holds geometry for specific piece of track/road/waterway

View File

@@ -352,7 +352,29 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad
if( nodedata.range_min < 0.0 ) {
// convert and import 3d terrain
// TODO: implement this
auto *instance { deserialize_model( Input, Scratchpad, nodedata ) };
// model import can potentially fail
if( instance == nullptr ) { return; }
// go through submodels, and import them as shapes
auto const cellcount = instance->TerrainCount() + 1; // zliczenie submodeli
for( auto i = 1; i < cellcount; ++i ) {
auto *submodel = instance->TerrainSquare( i - 1 );
simulation::Region->insert_shape(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
// if there's more than one group of triangles in the cell they're held as children of the primary submodel
submodel = submodel->ChildGet();
while( submodel != nullptr ) {
simulation::Region->insert_shape(
scene::shape_node().convert( submodel ),
Scratchpad,
false );
submodel = submodel->NextGet();
}
}
// with the import done we can get rid of the source model
delete instance;
}
else {
// regular instance of 3d mesh
@@ -372,14 +394,20 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad
|| ( nodedata.type == "triangle_strip" )
|| ( nodedata.type == "triangle_fan" ) ) {
simulation::Region->insert_shape( scene::shape_node().deserialize( Input, nodedata ), Scratchpad );
simulation::Region->insert_shape(
scene::shape_node().deserialize(
Input, nodedata ),
Scratchpad,
true );
}
else if( ( nodedata.type == "lines" )
|| ( nodedata.type == "line_strip" )
|| ( nodedata.type == "line_loop" ) ) {
// TODO: implement
skip_until( Input, "endline" );
simulation::Region->insert_lines(
scene::lines_node().deserialize(
Input, nodedata ),
Scratchpad );
}
else if( nodedata.type == "memcell" ) {