build 170626. individual self-illumination levels for interior sections, automatic merging of suitable terrain geometry during load from text files, unused resource release by geometry bank manager, support for ui backgrounds with width:height ratio other than 4:3, comment parsing optimization, minor bug fixes

This commit is contained in:
tmj-fstate
2017-06-26 16:57:25 +02:00
parent 34a899239f
commit 5d7b3fb036
18 changed files with 608 additions and 339 deletions

View File

@@ -527,6 +527,39 @@ void TDynamicObject::UpdateLeverEnum(TAnim *pAnim)
pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fParam[*pAnim->iIntBase]);
};
// sets light levels for registered interior sections
void
TDynamicObject::toggle_lights() {
if( true == SectionLightsActive ) {
// switch all lights off
for( auto &sectionlight : SectionLightLevels ) {
sectionlight.level = 0.0f;
}
SectionLightsActive = false;
}
else {
std::string compartmentname;
// set lights with probability depending on the compartment type. TODO: expose this in .mmd file
for( auto &sectionlight : SectionLightLevels ) {
compartmentname = sectionlight.compartment->pName;
if( ( compartmentname.find( "corridor" ) != std::string::npos )
|| ( compartmentname.find( "korytarz" ) != std::string::npos ) ) {
// corridors are lit 100% of time
sectionlight.level = 0.75f;
}
else if(
( compartmentname.find( "compartment" ) != std::string::npos )
|| ( compartmentname.find( "przedzial" ) != std::string::npos ) ) {
// compartments are lit with 75% probability
sectionlight.level = ( Random() < 0.75 ? 0.75f : 0.05f );
}
}
SectionLightsActive = true;
}
}
// ABu 29.01.05 przeklejone z render i renderalpha: *********************
void TDynamicObject::ABuLittleUpdate(double ObjSqrDist)
{ // ABu290105: pozbierane i uporzadkowane powtarzajace
@@ -946,6 +979,13 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist)
}
// else btHeadSignals23.TurnOff();
}
// interior light levels
for( auto const &section : SectionLightLevels ) {
section.compartment->SetLightLevel( section.level, true );
if( section.load != nullptr ) {
section.load->SetLightLevel( section.level, true );
}
}
}
// ABu 29.01.05 koniec przeklejenia *************************************
@@ -1949,6 +1989,41 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
btMechanik1.Init("mechanik1", mdLowPolyInt, false);
btMechanik2.Init("mechanik2", mdLowPolyInt, false);
TurnOff(); // resetowanie zmiennych submodeli
if( mdLowPolyInt != nullptr ) {
// check the low poly interior for potential compartments of interest, ie ones which can be individually lit
// TODO: definition of relevant compartments in the .mmd file
TSubModel *submodel { nullptr };
if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); }
if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); }
if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); }
// passenger car compartments
std::vector<std::string> nameprefixes = { "corridor", "korytarz", "compartment", "przedzial" };
int compartmentindex;
std::string compartmentname;
for( auto const &nameprefix : nameprefixes ) {
compartmentindex = 0;
do {
compartmentname =
nameprefix + (
compartmentindex < 10 ?
"0" + std::to_string( compartmentindex ) :
std::to_string( compartmentindex ) );
submodel = mdLowPolyInt->GetFromName( compartmentname.c_str() );
if( submodel != nullptr ) {
// if specified compartment was found we check also for potential matching section in the currently assigned load
// NOTE: if the load gets changed this will invalidate stored pointers. TODO: rebuild the table on load change
SectionLightLevels.emplace_back(
submodel,
( mdLoad != nullptr ?
mdLoad->GetFromName( compartmentname.c_str() ):
nullptr ),
0.0f );
}
++compartmentindex;
} while( submodel != nullptr );
}
}
// wyszukiwanie zderzakow
if (mdModel) // jeśli ma w czym szukać
for (int i = 0; i < 2; i++)
@@ -3416,6 +3491,20 @@ bool TDynamicObject::Update(double dt, double dt1)
dDoorMoveR = 0;
}
// compartment lights
/*
if( ( ctOwner != nullptr ?
ctOwner->Controlling()->Battery != SectionLightsActive :
MoverParameters->Battery != SectionLightsActive ) ) {
// if the vehicle has a controller, we base the light state on state of the controller otherwise we check the vehicle itself
*/
// the version above won't work as the vehicles don't turn batteries off :|
if( ( ctOwner != nullptr ?
ctOwner->Controlling()->Battery != SectionLightsActive :
SectionLightsActive == true ) ) { // without controller lights are off. NOTE: this likely mess up the EMU
toggle_lights();
}
// ABu-160303 sledzenie toru przed obiektem: *******************************
// Z obserwacji: v>0 -> Coupler 0; v<0 ->coupler1 (Ra: prędkość jest związana
// z pojazdem)

View File

@@ -143,12 +143,8 @@ class TAnim
struct material_data {
int textures_alpha{ 0x30300030 }; // maska przezroczystości tekstur. default: tekstury wymienne nie mają przezroczystości
texture_handle replacable_skins[ 5 ]; // McZapkie:zmienialne nadwozie
texture_handle replacable_skins[ 5 ] = { NULL, NULL, NULL, NULL, NULL }; // McZapkie:zmienialne nadwozie
int multi_textures{ 0 }; //<0 tekstury wskazane wpisem, >0 tekstury z przecinkami, =0 jedna
material_data() {
::SecureZeroMemory( replacable_skins, sizeof( replacable_skins ) );
}
};
class TDynamicObject { // klasa pojazdu
@@ -169,8 +165,7 @@ private: // położenie pojazdu w świecie oraz parametry ruchu
float fAxleDist; // rozstaw wózków albo osi do liczenia proporcji zacienienia
vector3 modelRot; // obrot pudła względem świata - do przeanalizowania, czy potrzebne!!!
// bool bCameraNear; //blisko kamer są potrzebne dodatkowe obliczenia szczegółów
TDynamicObject * ABuFindNearestObject( TTrack *Track, TDynamicObject *MyPointer,
int &CouplNr );
TDynamicObject * ABuFindNearestObject( TTrack *Track, TDynamicObject *MyPointer, int &CouplNr );
public: // parametry położenia pojazdu dostępne publicznie
std::string asTrack; // nazwa toru początkowego; wywalić?
@@ -191,8 +186,18 @@ public: // modele składowe pojazdu
TModel3d *mdLoad; // model zmiennego ładunku
TModel3d *mdKabina; // model kabiny dla użytkownika; McZapkie-030303: to z train.h
TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly
float3 InteriorLight{ 0.9f * 255.0f / 255.0f, 0.9f * 216.0f / 255.0f, 0.9f * 176.0f / 255.0f }; // tungsten light. TODO: allow definition of light type?
float InteriorLightLevel{ 0.0f }; // current level of interior lighting
float3 InteriorLight { 0.9f * 255.0f / 255.0f, 0.9f * 216.0f / 255.0f, 0.9f * 176.0f / 255.0f }; // tungsten light. TODO: allow definition of light type?
float InteriorLightLevel { 0.0f }; // current level of interior lighting
struct section_light {
TSubModel *compartment;
TSubModel *load;
float level;
section_light( TSubModel *Compartment, TSubModel *Load, float const Level ) :
compartment(Compartment), load(Load), level(Level)
{}
};
std::vector<section_light> SectionLightLevels; // table of light levels for specific compartments of associated 3d model
bool SectionLightsActive { false }; // flag indicating whether section lights were set.
float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?)
private: // zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe
@@ -200,7 +205,9 @@ public: // modele składowe pojazdu
public:
inline
material_data const *Material() const { return &m_materialdata; }
material_data const
*Material() const {
return &m_materialdata; }
// tymczasowo udostępnione do wyszukiwania drutu
int iAnimType[ ANIM_TYPES ]; // 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki
private:
@@ -226,6 +233,7 @@ public: // modele składowe pojazdu
void UpdateLeverFloat(TAnim *pAnim); // animacja gałki zależna od float
void UpdateLeverInt(TAnim *pAnim); // animacja gałki zależna od int (wartość)
void UpdateLeverEnum(TAnim *pAnim); // animacja gałki zależna od int (lista kątów)
void toggle_lights(); // switch light levels for registered interior sections
private: // Ra: ciąg dalszy animacji, dopiero do ogarnięcia
// ABuWozki 060504
vector3 bogieRot[2]; // Obroty wozkow w/m korpusu

View File

@@ -67,7 +67,6 @@ std::string LogComment;
TGroundNode::TGroundNode()
{ // nowy obiekt terenu - pusty
iType = GL_POINTS;
Vertices = NULL;
nNext = nNext2 = NULL;
iCount = 0; // wierzchołków w trójkącie
// iNumPts=0; //punktów w linii
@@ -115,9 +114,6 @@ TGroundNode::~TGroundNode()
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
@@ -125,33 +121,31 @@ TGroundNode::~TGroundNode()
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);
SafeDelete( Piece );
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 ) :
*/
TGroundNode::TGroundNode( TGroundNodeType t ) :
TGroundNode() {
// utworzenie obiektu
iNumVerts = n;
if( iNumVerts ) {
Vertices = new TGroundVertex[ iNumVerts ];
}
iType = t;
switch (iType) {
// zależnie od typu
case GL_TRIANGLES: {
Piece = new piece_node;
break;
}
case TP_TRACK: {
pTrack = new TTrack( this );
break;
@@ -171,36 +165,36 @@ void TGroundNode::InitNormals()
switch (iType)
{
case GL_TRIANGLE_STRIP:
v1 = Vertices[0].position - Vertices[1].position;
v2 = Vertices[1].position - Vertices[2].position;
v1 = Piece->vertices[0].position - Piece->vertices[1].position;
v2 = Piece->vertices[1].position - Piece->vertices[2].position;
n1 = glm::normalize(glm::cross(v1, v2));
if (Vertices[0].normal == glm::vec3())
Vertices[0].normal = n1;
v3 = Vertices[2].position - Vertices[3].position;
if (Piece->vertices[0].normal == glm::vec3())
Piece->vertices[0].normal = n1;
v3 = Piece->vertices[2].position - Piece->vertices[3].position;
n2 = glm::normalize(glm::cross(v3, v2));
if (Vertices[1].normal == glm::vec3())
Vertices[1].normal = (n1 + n2) * 0.5f;
if (Piece->vertices[1].normal == glm::vec3())
Piece->vertices[1].normal = (n1 + n2) * 0.5f;
for ( i = 2; i < iNumVerts - 2; i += 2)
{
v4 = Vertices[i - 1].position - Vertices[i].position;
v5 = Vertices[i].position - Vertices[i + 1].position;
v4 = Piece->vertices[i - 1].position - Piece->vertices[i].position;
v5 = Piece->vertices[i].position - Piece->vertices[i + 1].position;
n3 = glm::normalize(glm::cross(v3, v4));
n4 = glm::normalize(glm::cross(v5, v4));
if (Vertices[i].normal == glm::vec3())
Vertices[i].normal = (n1 + n2 + n3) / 3.0f;
if (Vertices[i + 1].normal == glm::vec3())
Vertices[i + 1].normal = (n2 + n3 + n4) / 3.0f;
if (Piece->vertices[i].normal == glm::vec3())
Piece->vertices[i].normal = (n1 + n2 + n3) / 3.0f;
if (Piece->vertices[i + 1].normal == glm::vec3())
Piece->vertices[i + 1].normal = (n2 + n3 + n4) / 3.0f;
n1 = n3;
n2 = n4;
v3 = v5;
}
if (Vertices[i].normal == glm::vec3())
Vertices[i].normal = (n1 + n2) / 2.0f;
if (Piece->vertices[i].normal == glm::vec3())
Piece->vertices[i].normal = (n1 + n2) / 2.0f;
if (i + 1 < iNumVerts)
{
if (Vertices[i + 1].normal == glm::vec3())
Vertices[i + 1].normal = n2;
if (Piece->vertices[i + 1].normal == glm::vec3())
Piece->vertices[i + 1].normal = n2;
}
else
WriteLog("odd number of vertices, normals may be wrong!");
@@ -212,21 +206,21 @@ void TGroundNode::InitNormals()
case GL_TRIANGLES:
for (i = 0; i < iNumVerts; i += 3)
{
v1 = Vertices[i + 0].position - Vertices[i + 1].position;
v2 = Vertices[i + 1].position - Vertices[i + 2].position;
v1 = Piece->vertices[i + 0].position - Piece->vertices[i + 1].position;
v2 = Piece->vertices[i + 1].position - Piece->vertices[i + 2].position;
n1 = glm::normalize(glm::cross(v1, v2));
if (Vertices[i + 0].normal == glm::vec3())
Vertices[i + 0].normal = (n1);
if (Vertices[i + 1].normal == glm::vec3())
Vertices[i + 1].normal = (n1);
if (Vertices[i + 2].normal == glm::vec3())
Vertices[i + 2].normal = (n1);
if( Piece->vertices[i + 0].normal == glm::vec3() )
Piece->vertices[i + 0].normal = (n1);
if( Piece->vertices[i + 1].normal == glm::vec3() )
Piece->vertices[i + 1].normal = (n1);
if( Piece->vertices[i + 2].normal == glm::vec3() )
Piece->vertices[i + 2].normal = (n1);
t1 = glm::vec2(
std::floor( Vertices[ i + 0 ].texture.s ),
std::floor( Vertices[ i + 0 ].texture.t ) );
Vertices[ i + 1 ].texture -= t1;
Vertices[ i + 2 ].texture -= t1;
Vertices[ i + 0 ].texture -= t1;
std::floor( Piece->vertices[ i + 0 ].texture.s ),
std::floor( Piece->vertices[ i + 0 ].texture.t ) );
Piece->vertices[ i + 1 ].texture -= t1;
Piece->vertices[ i + 2 ].texture -= t1;
Piece->vertices[ i + 0 ].texture -= t1;
}
break;
}
@@ -349,8 +343,10 @@ void TSubRect::NodeAdd(TGroundNode *Node)
}
#endif
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
}
@@ -380,22 +376,14 @@ void TSubRect::Sort() {
TTrack * TSubRect::FindTrack(vector3 *Point, int &iConnection, TTrack *Exclude)
{ // szukanie toru, którego koniec jest najbliższy (*Point)
for (int i = 0; i < iTracks; ++i)
if (tTracks[i] != Exclude) // można użyć tabelę torów, bo jest mniejsza
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
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;
};
@@ -449,30 +437,20 @@ void TSubRect::LoadNodes() {
auto *node { nRootNode };
while( node != nullptr ) {
switch (node->iType) {
case GL_TRIANGLES: {
vertex_array vertices;
for( int idx = 0; idx < node->iNumVerts; ++idx ) {
vertices.emplace_back(
node->Vertices[ idx ].position - node->m_rootposition,
node->Vertices[ idx ].normal,
node->Vertices[ idx ].texture );
}
node->m_geometry = GfxRenderer.Insert( vertices, m_geometrybank, GL_TRIANGLES );
SafeDeleteArray( node->Vertices );
node->iNumVerts = 0;
break;
}
case GL_TRIANGLES:
case GL_LINES: {
vertex_array vertices;
for( int idx = 0; idx < node->iNumPts; ++idx ) {
for( auto const &vertex : node->Piece->vertices ) {
vertices.emplace_back(
node->Points[ idx ] - node->m_rootposition,
glm::vec3(),
glm::vec2() );
vertex.position - node->m_rootposition,
vertex.normal,
vertex.texture );
}
node->m_geometry = GfxRenderer.Insert( vertices, m_geometrybank, GL_LINES );
SafeDeleteArray( node->Vertices );
node->iNumPts = 0;
node->Piece->geometry = GfxRenderer.Insert( vertices, m_geometrybank, node->iType );
node->Piece->vertices.swap( std::vector<TGroundVertex>() ); // hipster shrink_to_fit
// TODO: get rid of the vertex counters, they're obsolete at this point
if( node->iType == GL_LINES ) { node->iNumVerts = 0; }
else { node->iNumPts = 0; }
break;
}
case TP_TRACK:
@@ -529,6 +507,59 @@ TGroundRect::Init() {
}
};
// dodanie obiektu do sektora na etapie rozdzielania na sektory
void
TGroundRect::NodeAdd( TGroundNode *Node ) {
// override visibility ranges, to ensure the content is drawn from far enough
Node->fSquareRadius = 50000.0 * 50000.0;
Node->fSquareMinRadius = 0.0;
// if the cell already has a node with matching material settings, we can just add the new geometry to it
if( ( Node->iType == GL_TRIANGLES ) ) {
// cell node only receives opaque geometry, so we can skip transparency test
auto matchingnode { nRenderRect };
while( ( matchingnode != nullptr )
&& ( false == mergeable( *Node, *matchingnode ) ) ) {
// search will get us either a matching node, or a nullptr
matchingnode = matchingnode->nNext3;
}
if( matchingnode != nullptr ) {
// a valid match, so dump the content into it
// updating centre points isn't strictly necessary since render is based off the cell's middle, but, eh
matchingnode->pCenter =
interpolate(
matchingnode->pCenter, Node->pCenter,
static_cast<float>( Node->iNumVerts ) / ( Node->iNumVerts + matchingnode->iNumVerts ) );
matchingnode->iNumVerts += Node->iNumVerts;
matchingnode->Piece->vertices.resize( matchingnode->iNumVerts, TGroundVertex() );
matchingnode->Piece->vertices.insert(
std::end( matchingnode->Piece->vertices ),
std::begin( Node->Piece->vertices ), std::end( Node->Piece->vertices ) );
// clear content of the node we're copying. a minor memory saving at best, but still a saving
Node->Piece->vertices.swap( std::vector<TGroundVertex>() );
Node->iNumVerts = 0;
// since we've put the data in existing node we can skip adding the new one...
return;
// ...for others, they'll go through the regular procedure, along with other non-mergeable types
}
}
return TSubRect::NodeAdd( Node );
}
// compares two provided nodes, returns true if their content can be merged
bool
TGroundRect::mergeable( TGroundNode const &Left, TGroundNode const &Right ) {
// since view ranges and transparency type for all nodes put through this method are guaranteed to be equal,
// we can skip their tests and only do the material check.
// TODO, TBD: material as dedicated type, and refactor this method into a simple equality test
return ( ( Left.TextureID == Right.TextureID )
&& ( Left.Ambient == Right.Ambient )
&& ( Left.Diffuse == Right.Diffuse )
&& ( Left.Specular == Right.Specular ) );
}
//---------------------------------------------------------------------------
BYTE TempConnectionType[ 200 ]; // Ra: sprzêgi w sk³adzie; ujemne, gdy odwrotnie
@@ -594,19 +625,19 @@ void TGround::Free()
nRootDynamic = NULL;
}
TGroundNode * TGround::DynamicFindAny(std::string asNameToFind)
TGroundNode * TGround::DynamicFindAny(std::string const &Name)
{ // wyszukanie pojazdu o podanej nazwie, szukanie po wszystkich (użyć drzewa!)
for (TGroundNode *Current = nRootDynamic; Current; Current = Current->nNext)
if ((Current->asName == asNameToFind))
if ((Current->asName == Name))
return Current;
return NULL;
};
TGroundNode * TGround::DynamicFind(std::string asNameToFind)
TGroundNode * TGround::DynamicFind(std::string const &Name)
{ // 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))
if ((Current->asName == Name))
return Current;
return NULL;
};
@@ -685,12 +716,12 @@ void TGround::RaTriangleDivider(TGroundNode *node)
double x1 = x0 + 1400.0;
double z0 = 1000.0 * std::floor(0.001 * node->pCenter.z) - 200.0;
double z1 = z0 + 1400.0;
if ((node->Vertices[0].position.x >= x0) && (node->Vertices[0].position.x <= x1) &&
(node->Vertices[0].position.z >= z0) && (node->Vertices[0].position.z <= z1) &&
(node->Vertices[1].position.x >= x0) && (node->Vertices[1].position.x <= x1) &&
(node->Vertices[1].position.z >= z0) && (node->Vertices[1].position.z <= z1) &&
(node->Vertices[2].position.x >= x0) && (node->Vertices[2].position.x <= x1) &&
(node->Vertices[2].position.z >= z0) && (node->Vertices[2].position.z <= z1))
if ((node->Piece->vertices[0].position.x >= x0) && (node->Piece->vertices[0].position.x <= x1) &&
(node->Piece->vertices[0].position.z >= z0) && (node->Piece->vertices[0].position.z <= z1) &&
(node->Piece->vertices[1].position.x >= x0) && (node->Piece->vertices[1].position.x <= x1) &&
(node->Piece->vertices[1].position.z >= z0) && (node->Piece->vertices[1].position.z <= z1) &&
(node->Piece->vertices[2].position.x >= x0) && (node->Piece->vertices[2].position.x <= x1) &&
(node->Piece->vertices[2].position.z >= z0) && (node->Piece->vertices[2].position.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
@@ -700,47 +731,46 @@ void TGround::RaTriangleDivider(TGroundNode *node)
x1 -= 200.0; // przestawienie na siatkę
z0 += 200.0;
z1 -= 200.0;
mul = (node->Vertices[0].position.x - x0) * (node->Vertices[1].position.x - x0); // AB na wschodzie
mul = (node->Piece->vertices[0].position.x - x0) * (node->Piece->vertices[1].position.x - x0); // AB na wschodzie
if (mul < min)
min = mul, divide = 0;
mul = (node->Vertices[1].position.x - x0) * (node->Vertices[2].position.x - x0); // BC na wschodzie
mul = (node->Piece->vertices[1].position.x - x0) * (node->Piece->vertices[2].position.x - x0); // BC na wschodzie
if (mul < min)
min = mul, divide = 1;
mul = (node->Vertices[2].position.x - x0) * (node->Vertices[0].position.x - x0); // CA na wschodzie
mul = (node->Piece->vertices[2].position.x - x0) * (node->Piece->vertices[0].position.x - x0); // CA na wschodzie
if (mul < min)
min = mul, divide = 2;
mul = (node->Vertices[0].position.x - x1) * (node->Vertices[1].position.x - x1); // AB na zachodzie
mul = (node->Piece->vertices[0].position.x - x1) * (node->Piece->vertices[1].position.x - x1); // AB na zachodzie
if (mul < min)
min = mul, divide = 8;
mul = (node->Vertices[1].position.x - x1) * (node->Vertices[2].position.x - x1); // BC na zachodzie
mul = (node->Piece->vertices[1].position.x - x1) * (node->Piece->vertices[2].position.x - x1); // BC na zachodzie
if (mul < min)
min = mul, divide = 9;
mul = (node->Vertices[2].position.x - x1) * (node->Vertices[0].position.x - x1); // CA na zachodzie
mul = (node->Piece->vertices[2].position.x - x1) * (node->Piece->vertices[0].position.x - x1); // CA na zachodzie
if (mul < min)
min = mul, divide = 10;
mul = (node->Vertices[0].position.z - z0) * (node->Vertices[1].position.z - z0); // AB na południu
mul = (node->Piece->vertices[0].position.z - z0) * (node->Piece->vertices[1].position.z - z0); // AB na południu
if (mul < min)
min = mul, divide = 4;
mul = (node->Vertices[1].position.z - z0) * (node->Vertices[2].position.z - z0); // BC na południu
mul = (node->Piece->vertices[1].position.z - z0) * (node->Piece->vertices[2].position.z - z0); // BC na południu
if (mul < min)
min = mul, divide = 5;
mul = (node->Vertices[2].position.z - z0) * (node->Vertices[0].position.z - z0); // CA na południu
mul = (node->Piece->vertices[2].position.z - z0) * (node->Piece->vertices[0].position.z - z0); // CA na południu
if (mul < min)
min = mul, divide = 6;
mul = (node->Vertices[0].position.z - z1) * (node->Vertices[1].position.z - z1); // AB na północy
mul = (node->Piece->vertices[0].position.z - z1) * (node->Piece->vertices[1].position.z - z1); // AB na północy
if (mul < min)
min = mul, divide = 12;
mul = (node->Vertices[1].position.z - z1) * (node->Vertices[2].position.z - z1); // BC na północy
mul = (node->Piece->vertices[1].position.z - z1) * (node->Piece->vertices[2].position.z - z1); // BC na północy
if (mul < min)
min = mul, divide = 13;
mul = (node->Vertices[2].position.z - z1) * (node->Vertices[0].position.z - z1); // CA na północy
mul = (node->Piece->vertices[2].position.z - z1) * (node->Piece->vertices[0].position.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 = new TGroundNode(GL_TRIANGLES); // a ten jest nowy
// kopiowanie parametrów, przydałby się konstruktor kopiujący
ntri->TextureID = node->TextureID;
ntri->iFlags = node->iFlags;
ntri->Ambient = node->Ambient;
@@ -753,42 +783,44 @@ void TGround::RaTriangleDivider(TGroundNode *node)
ntri->nNext = nRootOfType[GL_TRIANGLES];
nRootOfType[GL_TRIANGLES] = ntri; // dopisanie z przodu do listy
++iNumNodes;
ntri->iNumVerts = 3;
ntri->Piece->vertices.resize( 3 );
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
ntri->Piece->vertices[2] = node->Piece->vertices[2]; // wierzchołek C jest wspólny
ntri->Piece->vertices[1] = node->Piece->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);
node->Piece->vertices[1].SetByZ(node->Piece->vertices[0], node->Piece->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
node->Piece->vertices[1].SetByX(node->Piece->vertices[0], node->Piece->vertices[1], (divide & 8) ? x1 : x0);
ntri->Piece->vertices[0] = node->Piece->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
ntri->Piece->vertices[0] = node->Piece->vertices[0]; // wierzchołek A jest wspólny
ntri->Piece->vertices[2] = node->Piece->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);
node->Piece->vertices[2].SetByZ(node->Piece->vertices[1], node->Piece->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
node->Piece->vertices[2].SetByX(node->Piece->vertices[1], node->Piece->vertices[2], (divide & 8) ? x1 : x0);
ntri->Piece->vertices[1] = node->Piece->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
ntri->Piece->vertices[1] = node->Piece->vertices[1]; // wierzchołek B jest wspólny
ntri->Piece->vertices[2] = node->Piece->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);
node->Piece->vertices[2].SetByZ(node->Piece->vertices[2], node->Piece->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
node->Piece->vertices[2].SetByX(node->Piece->vertices[2], node->Piece->vertices[0], (divide & 8) ? x1 : x0);
ntri->Piece->vertices[0] = node->Piece->vertices[2]; // wierzchołek D jest wspólny
break;
}
// przeliczenie środków ciężkości obu
node->pCenter = ( node->Vertices[ 0 ].position + node->Vertices[ 1 ].position + node->Vertices[ 2 ].position ) / 3.0;
ntri->pCenter = ( ntri->Vertices[ 0 ].position + ntri->Vertices[ 1 ].position + ntri->Vertices[ 2 ].position ) / 3.0;
node->pCenter = ( node->Piece->vertices[ 0 ].position + node->Piece->vertices[ 1 ].position + node->Piece->vertices[ 2 ].position ) / 3.0;
ntri->pCenter = ( ntri->Piece->vertices[ 0 ].position + ntri->Piece->vertices[ 1 ].position + ntri->Piece->vertices[ 2 ].position ) / 3.0;
RaTriangleDivider(node); // rekurencja, bo nawet na TD raz nie wystarczy
RaTriangleDivider(ntri);
};
@@ -1218,7 +1250,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser)
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.empty()) // jest pusta gdy "none"
@@ -1291,18 +1322,6 @@ TGroundNode * TGround::AddGroundNode(cParser *parser)
&& ( true == GfxRenderer.Texture( tmp->TextureID ).has_alpha ) ) ?
0x20 :
0x10 );
if( (tmp->iType == GL_TRIANGLES)
&& (tmp->iFlags & 0x10)
&& (Global::pTerrainCompact->TerrainLoaded()) ) {
// 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);
}
else
{
TGroundVertex vertex, vertex1, vertex2;
std::size_t vertexcount { 0 };
@@ -1346,9 +1365,17 @@ TGroundNode * TGround::AddGroundNode(cParser *parser)
if( vertexcount == 0 ) { vertex1 = vertex; }
else if( vertexcount == 1 ) { vertex2 = vertex; }
else if( vertexcount >= 2 ) {
importedvertices.emplace_back( vertex1 );
importedvertices.emplace_back( vertex2 );
// swap order every other triangle, to maintain consistent winding
if( vertexcount % 2 == 0 ) {
importedvertices.emplace_back( vertex1 );
importedvertices.emplace_back( vertex2 );
}
else {
importedvertices.emplace_back( vertex2 );
importedvertices.emplace_back( vertex1 );
}
importedvertices.emplace_back( vertex );
vertex1 = vertex2;
vertex2 = vertex;
}
@@ -1363,25 +1390,27 @@ TGroundNode * TGround::AddGroundNode(cParser *parser)
} while (token.compare("endtri") != 0);
tmp->iType = GL_TRIANGLES;
tmp->Piece = new piece_node();
tmp->iNumVerts = importedvertices.size();
auto const nv = importedvertices.size();
tmp->Init(nv); // utworzenie tablicy wierzchołków
if( tmp->iNumVerts > 0 ) {
for( std::size_t i = 0; i < nv; ++i ) {
tmp->pCenter += importedvertices[ i ].position;
tmp->Piece->vertices.swap( importedvertices );
for( auto const &vertex : tmp->Piece->vertices ) {
tmp->pCenter += vertex.position;
}
tmp->pCenter /= tmp->iNumVerts;
r = 0;
for( auto const &vertex : tmp->Piece->vertices ) {
tf = SquareMagnitude( vertex.position - tmp->pCenter );
if( tf > r )
r = tf;
}
tmp->fSquareRadius += r;
RaTriangleDivider( tmp ); // Ra: dzielenie trójkątów jest teraz całkiem wydajne
}
tmp->pCenter /= (nv > 0 ? nv : 1);
r = 0;
for (std::size_t i = 0; i < nv; ++i)
{
tmp->Vertices[i] = importedvertices[i];
tf = SquareMagnitude( tmp->Vertices[ i ].position - tmp->pCenter );
if (tf > r)
r = tf;
}
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:
@@ -1394,6 +1423,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser)
>> tmp->Diffuse.b
>> tmp->fLineThickness;
tmp->Diffuse /= 255.0f;
tmp->fLineThickness = std::min( 30.0, tmp->fLineThickness ); // 30 pix equals rougly width of a signal pole viewed from ~1m away
TGroundVertex vertex, vertex0, vertex1;
std::size_t vertexcount{ 0 };
@@ -1455,15 +1485,25 @@ TGroundNode * TGround::AddGroundNode(cParser *parser)
importedvertices.pop_back();
}
tmp->iType = GL_LINES;
tmp->Piece = new piece_node();
tmp->iNumPts = importedvertices.size();
auto const nv = importedvertices.size();
tmp->Points = new glm::dvec3[ nv ];
tmp->iNumPts = nv;
for( std::size_t i = 0; i < nv; ++i ) {
tmp->Points[ i ] = importedvertices[ i ].position;
tmp->pCenter += importedvertices[ i ].position;
if( false == importedvertices.empty() ) {
tmp->Piece->vertices.swap( importedvertices );
glm::dvec3 minpoint( std::numeric_limits<double>::max(), 0.0, std::numeric_limits<double>::max() );
glm::dvec3 maxpoint( std::numeric_limits<double>::lowest(), 0.0, std::numeric_limits<double>::lowest() );
for( auto const &vertex : tmp->Piece->vertices ) {
tmp->pCenter += vertex.position;
minpoint.x = std::min( minpoint.x, vertex.position.x );
minpoint.z = std::min( minpoint.z, vertex.position.z );
maxpoint.x = std::max( maxpoint.x, vertex.position.x );
maxpoint.z = std::max( maxpoint.z, vertex.position.z );
}
tmp->pCenter /= tmp->iNumPts;
tmp->m_radius = static_cast<float>( glm::distance( maxpoint, minpoint ) * 0.5 );
}
tmp->pCenter /= ( nv > 0 ? nv : 1 );
break;
}
}
@@ -1559,11 +1599,6 @@ void TGround::FirstInit()
}
else {
// dodajemy do kwadratu kilometrowego
// override visibility ranges, to ensure the content is drawn from far enough
// TODO: subclass NodeAdd() for the ground cell and specialize it, instead of this
// TODO: given their view ranges are the same, combine the geometry in single per-material nodes
Current->fSquareRadius = 50000.0 * 50000.0;
Current->fSquareMinRadius = 0.0;
GetRect( Current->pCenter.x, Current->pCenter.z )->NodeAdd( Current );
}
}
@@ -1632,7 +1667,7 @@ bool TGround::Init(std::string File)
switch( LastNode->iType ) {
// validate the new node
case GL_TRIANGLES: {
if( LastNode->Vertices == nullptr ) {
if( true == LastNode->Piece->vertices.empty() ) {
SafeDelete( LastNode );
}
break;
@@ -1692,7 +1727,7 @@ bool TGround::Init(std::string File)
}
else
{
Error("Scene parse error near " + token);
ErrorLog("Scene parsing error in file \"" + parser.Name() + "\", unexpected token \"" + token + "\"");
// break;
}
}
@@ -1722,20 +1757,21 @@ bool TGround::Init(std::string File)
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)
if( LastNode ) {
// ostatni wczytany obiekt
if( LastNode->iType == TP_DYNAMIC ) {
// o ile jest pojazdem (na ogół jest, ale kto wie...)
if( ( iTrainSetWehicleNumber > 0 )
&& ( TempConnectionType[ iTrainSetWehicleNumber - 1 ] == 0 ) ) {
// jeśli ostatni pojazd ma sprzęg 0 to założymy mu końcówki blaszane
// (jak AI się odpali, to sobie poprawi)
LastNode->DynamicObject->RaLightsSet( -1, 2 + 32 + 64 );
}
}
}
bTrainSet = false;
fTrainSetVel = 0;
// iTrainSetConnection=0;
nTrainSetNode = nTrainSetDriver = NULL;
nTrainSetNode = nTrainSetDriver = nullptr;
iTrainSetWehicleNumber = 0;
}
else if (str == "event")

View File

@@ -93,15 +93,15 @@ struct TGroundVertex
texture = interpolate( v1.texture, v2.texture, static_cast<float>( factor ) );
}
};
/*
// ground node holding single, unique piece of 3d geometry. TBD, TODO: unify this with basic 3d model node
struct mesh_node {
struct piece_node {
std::vector<TGroundVertex> vertices;
geometry_handle geometry; // geometry prepared for drawing
geometry_handle geometry { 0,0 }; // geometry prepared for drawing
};
*/
class TGroundNode /*: public Resource*/
{ // obiekt scenerii
// obiekt scenerii
class TGroundNode {
friend class opengl_renderer;
@@ -117,9 +117,8 @@ public:
TSubModel *smTerrain; // modele terenu (kwadratow kilometrowych)
TAnimModel *Model; // model z animacjami
TDynamicObject *DynamicObject; // pojazd
glm::dvec3 *Points; // punkty dla linii
piece_node *Piece; // non-instanced piece of geometry
TTrack *pTrack; // trajektoria ruchu
TGroundVertex *Vertices; // wierzchołki dla trójkątów
TMemCell *MemCell; // komórka pamięci
TEventLauncher *EvLaunch; // wyzwalacz zdarzeń
TTraction *hvTraction; // drut zasilający
@@ -128,6 +127,7 @@ public:
TGroundNode *nNode; // obiekt renderujący grupowo ma tu wskaźnik na listę obiektów
};
Math3D::vector3 pCenter; // współrzędne środka do przydzielenia sektora
float m_radius { 0.0f }; // bounding radius of geometry stored in the node. TODO: reuse bounding_area struct for radius and center
glm::dvec3 m_rootposition; // position of the ground (sub)rectangle holding the node, in the 3d world
// visualization-related data
// TODO: wrap these in a struct, when cleaning objects up
@@ -139,9 +139,6 @@ public:
int iNumPts; // dla linii
int iCount; // dla terenu
};
// NOTE: geometry handle is duplicated in (anim)model(3d), as well as in track and traction type nodes
// TODO: clean this up when node types are refactored into an inheritance/composition scheme
geometry_handle m_geometry; // geometry of the submodel
int iFlags; // tryb przezroczystości: 0x10-nieprz.,0x20-przezroczysty,0x30-mieszany
texture_handle TextureID; // główna (jedna) tekstura obiektu
glm::vec3
@@ -152,9 +149,11 @@ public:
bool bVisible;
TGroundNode();
TGroundNode(TGroundNodeType t, int n = 0);
TGroundNode(TGroundNodeType t);
~TGroundNode();
/*
void Init(int n);
*/
void InitNormals();
/*
void Release();
@@ -200,7 +199,7 @@ class TSubRect : /*public Resource,*/ public CMesh
/*
virtual void Release(); // zwalnianie VBO sektora
*/
void NodeAdd(TGroundNode *Node); // dodanie obiektu do sektora na etapie rozdzielania na sektory
virtual void NodeAdd(TGroundNode *Node); // dodanie obiektu do sektora na etapie rozdzielania na sektory
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);
@@ -242,17 +241,19 @@ public:
};
// pobranie wskaźnika do małego kwadratu, bez tworzenia jeśli nie ma
TSubRect * FastGetSubRect(int iCol, int iRow) {
return (pSubRects ? pSubRects + iRow * iNumSubRects + iCol : NULL);
};
return (
pSubRects ?
pSubRects + iRow * iNumSubRects + iCol :
nullptr); };
void NodeAdd( TGroundNode *Node ); // dodanie obiektu do sektora na etapie rozdzielania na sektory
// compares two provided nodes, returns true if their content can be merged
bool mergeable( TGroundNode const &Left, TGroundNode const &Right );
// optymalizacja obiektów w sektorach
void Optimize() {
if( pSubRects ) {
for( int i = iNumSubRects * iNumSubRects - 1; i >= 0; --i ) {
// optymalizacja obiektów w sektorach
pSubRects[ i ].Sort();
}
}
};
pSubRects[ i ].Sort(); } } };
static int iFrameNumber; // numer kolejny wyświetlanej klatki
TGroundNode *nTerrain { nullptr }; // model terenu z E3D - użyć nRootMesh?
@@ -308,8 +309,8 @@ class TGround
bool AddToQuery(TEvent *Event, TDynamicObject *Node);
bool GetTraction(TDynamicObject *model);
bool CheckQuery();
TGroundNode * DynamicFindAny(std::string asNameToFind);
TGroundNode * DynamicFind(std::string asNameToFind);
TGroundNode * DynamicFindAny(std::string const &Name);
TGroundNode * DynamicFind(std::string const &Name);
void DynamicList(bool all = false);
TGroundNode * FindGroundNode(std::string asNameToFind, TGroundNodeType iNodeType);
TGroundRect * GetRect(double x, double z)
@@ -343,7 +344,9 @@ class TGround
public:
void WyslijEvent(const std::string &e, const std::string &d);
/*
int iRendered; // ilość renderowanych sektorów, pobierana przy pokazywniu FPS
*/
void WyslijString(const std::string &t, int n);
void WyslijWolny(const std::string &t);
void WyslijNamiary(TGroundNode *t);

View File

@@ -78,6 +78,23 @@ void TSubModel::NameSet(std::string const &Name)
pName = Name;
};
// sets light level (alpha component of illumination color) to specified value
void
TSubModel::SetLightLevel( float const Level, bool const Includechildren, bool const Includesiblings ) {
f4Emision.a = Level;
if( true == Includesiblings ) {
auto sibling { this };
while( ( sibling = sibling->Next ) != nullptr ) {
sibling->f4Emision.a = Level;
}
}
if( ( true == Includechildren )
&& ( Child != nullptr ) ) {
Child->SetLightLevel( Level, true, true ); // node's children include child's siblings and children
}
}
int TSubModel::SeekFaceNormal(std::vector<unsigned int> const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, vertex_array const &Vertices)
{ // szukanie punktu stycznego do (pt), zwraca numer wierzchołka, a nie trójkąta
int facecount = iNumVerts / 3; // bo maska powierzchni jest jedna na trójkąt
@@ -315,9 +332,9 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
}
else
{ // jeśli tylko nazwa pliku, to dawać bieżącą ścieżkę do tekstur
TextureNameSet(texture.c_str());
TextureNameSet(texture);
if( texture.find_first_of( "/\\" ) == texture.npos ) {
texture.insert( 0, Global::asCurrentTexturePath.c_str() );
texture.insert( 0, Global::asCurrentTexturePath );
}
TextureID = GfxRenderer.GetTextureId( texture, szTexturePath );
// renderowanie w cyklu przezroczystych tylko jeśli:
@@ -429,7 +446,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
--facecount; // o jeden trójkąt mniej
iNumVerts -= 3; // czyli o 3 wierzchołki
i -= 3; // wczytanie kolejnego w to miejsce
WriteLog("Degenerated triangle ignored in: \"" + pName + "\", verticle " + std::to_string(i));
WriteLog("Degenerated triangle ignored in: \"" + pName + "\", vertice " + std::to_string(i));
}
if (i > 0) {
// jeśli pierwszy trójkąt będzie zdegenerowany, to zostanie usunięty i nie ma co sprawdzać
@@ -487,6 +504,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
glm::vec3() ); // przepisanie do wierzchołka trójkąta
}
}
Vertices.resize( iNumVerts ); // in case we had some degenerate triangles along the way
/*
delete[] wsp;
delete[] n;
@@ -534,6 +552,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
else if( eType == TP_FREESPOTLIGHT ) {
// single light points only have single data point, duh
Vertices.emplace_back();
iNumVerts = 1;
}
// Visible=true; //się potem wyłączy w razie potrzeby
// iFlags|=0x0200; //wczytano z pliku tekstowego (jest właścicielem tablic)
@@ -711,25 +730,20 @@ void TSubModel::InitialRotate(bool doit)
{ // jeśli jest jednostkowy transform, to przeliczamy
// wierzchołki, a mnożenie podajemy dalej
float swapcopy;
// if( false == Vertices.empty() ) {
/*
for( auto &vertex : Vertices ) {
*/
for( auto &vertex : Vertices ) {
vertex.position.x = -vertex.position.x; // zmiana znaku X
swapcopy = vertex.position.y; // zamiana Y i Z
vertex.position.y = vertex.position.z;
vertex.position.z = swapcopy;
// wektory normalne również trzeba przekształcić, bo się źle oświetlają
if( eType != TP_STARS ) {
// gwiazdki mają kolory zamiast normalnych, to // ich wtedy nie ruszamy
vertex.normal.x = -vertex.normal.x; // zmiana znaku X
swapcopy = vertex.normal.y; // zamiana Y i Z
vertex.normal.y = vertex.normal.z;
vertex.normal.z = swapcopy;
}
vertex.position.x = -vertex.position.x; // zmiana znaku X
swapcopy = vertex.position.y; // zamiana Y i Z
vertex.position.y = vertex.position.z;
vertex.position.z = swapcopy;
// wektory normalne również trzeba przekształcić, bo się źle oświetlają
if( eType != TP_STARS ) {
// gwiazdki mają kolory zamiast normalnych, to // ich wtedy nie ruszamy
vertex.normal.x = -vertex.normal.x; // zmiana znaku X
swapcopy = vertex.normal.y; // zamiana Y i Z
vertex.normal.y = vertex.normal.z;
vertex.normal.z = swapcopy;
}
// }
}
if (Child)
Child->InitialRotate(doit); // potomne ewentualnie obrócimy
}

View File

@@ -56,6 +56,7 @@ class TSubModel
friend class opengl_renderer;
friend class TModel3d; // temporary workaround. TODO: clean up class content/hierarchy
friend class TDynamicObject; // temporary etc
private:
int iNext{ NULL };
@@ -85,47 +86,41 @@ private:
float4x4 *fMatrix = nullptr; // pojedyncza precyzja wystarcza
int iMatrix; // w pliku binarnym jest numer matrycy
};
int iNumVerts{ -1 }; // ilość wierzchołków (1 dla FreeSpotLight)
int iNumVerts { -1 }; // ilość wierzchołków (1 dla FreeSpotLight)
int tVboPtr; // początek na liście wierzchołków albo indeksów
int iTexture{ 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak
float fVisible{ 0.0f }; // próg jasności światła do załączenia submodelu
float fLight{ -1.0f }; // próg jasności światła do zadziałania selfillum
int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak
float fVisible { 0.0f }; // próg jasności światła do załączenia submodelu
float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum
glm::vec4
f4Ambient{ 1.0f,1.0f,1.0f,1.0f },
f4Diffuse{ 1.0f,1.0f,1.0f,1.0f },
f4Specular{ 0.0f,0.0f,0.0f,1.0f },
f4Emision{ 1.0f,1.0f,1.0f,1.0f };
float fWireSize{ 0.0f }; // nie używane, ale wczytywane
float fSquareMaxDist{ 10000.0f * 10000.0f };
float fSquareMinDist{ 0.0f };
f4Ambient { 1.0f,1.0f,1.0f,1.0f },
f4Diffuse { 1.0f,1.0f,1.0f,1.0f },
f4Specular { 0.0f,0.0f,0.0f,1.0f },
f4Emision { 1.0f,1.0f,1.0f,1.0f };
float fWireSize { 0.0f }; // nie używane, ale wczytywane
float fSquareMaxDist { 10000.0f * 10000.0f };
float fSquareMinDist { 0.0f };
// McZapkie-050702: parametry dla swiatla:
float fNearAttenStart{ 40.0f };
float fNearAttenEnd{ 80.0f };
bool bUseNearAtten{ false }; // te 3 zmienne okreslaja rysowanie aureoli wokol zrodla swiatla
int iFarAttenDecay{ 0 }; // ta zmienna okresla typ zaniku natezenia swiatla (0:brak, 1,2: potega 1/R)
float fFarDecayRadius{ 100.0f }; // normalizacja j.w.
float fCosFalloffAngle{ 0.5f }; // cosinus kąta stożka pod którym widać światło
float fCosHotspotAngle{ 0.3f }; // cosinus kąta stożka pod którym widać aureolę i zwiększone natężenie światła
float fCosViewAngle{ 0.0f }; // cos kata pod jakim sie teraz patrzy
float fNearAttenStart { 40.0f };
float fNearAttenEnd { 80.0f };
bool bUseNearAtten { false }; // te 3 zmienne okreslaja rysowanie aureoli wokol zrodla swiatla
int iFarAttenDecay { 0 }; // ta zmienna okresla typ zaniku natezenia swiatla (0:brak, 1,2: potega 1/R)
float fFarDecayRadius { 100.0f }; // normalizacja j.w.
float fCosFalloffAngle { 0.5f }; // cosinus kąta stożka pod którym widać światło
float fCosHotspotAngle { 0.3f }; // cosinus kąta stożka pod którym widać aureolę i zwiększone natężenie światła
float fCosViewAngle { 0.0f }; // cos kata pod jakim sie teraz patrzy
TSubModel *Next{ nullptr };
TSubModel *Child{ nullptr };
/*
intptr_t iVboPtr;
*/
geometry_handle m_geometry{ NULL, NULL }; // geometry of the submodel
texture_handle TextureID{ NULL }; // numer tekstury, -1 wymienna, 0 brak
bool bWire{ false }; // nie używane, ale wczytywane
/*
GLuint uiDisplayList; // roboczy numer listy wyświetlania
*/
float Opacity{ 1.0f };
float f_Angle{ 0.0f };
float3 v_RotateAxis{ 0.0f, 0.0f, 0.0f };
TSubModel *Next { nullptr };
TSubModel *Child { nullptr };
geometry_handle m_geometry { NULL, NULL }; // geometry of the submodel
texture_handle TextureID { NULL }; // numer tekstury, -1 wymienna, 0 brak
bool bWire { false }; // nie używane, ale wczytywane
float Opacity { 1.0f };
float f_Angle { 0.0f };
float3 v_RotateAxis { 0.0f, 0.0f, 0.0f };
float3 v_Angles { 0.0f, 0.0f, 0.0f };
public: // chwilowo
float3 v_TransVector{ 0.0f, 0.0f, 0.0f };
float3 v_TransVector { 0.0f, 0.0f, 0.0f };
/*
basic_vertex *Vertices; // roboczy wskaźnik - wczytanie T3D do VBO
*/
@@ -192,18 +187,14 @@ public:
int Flags() { return iFlags; };
void UnFlagNext() { iFlags &= 0x00FFFFFF; };
void ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular );
inline float3 Translation1Get()
{
return fMatrix ? *(fMatrix->TranslationGet()) + v_TransVector : v_TransVector;
}
inline float3 Translation2Get()
{
return *(fMatrix->TranslationGet()) + Child->Translation1Get();
}
int GetTextureId()
{
return TextureID;
}
// sets light level (alpha component of illumination color) to specified value
void SetLightLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false );
inline float3 Translation1Get() {
return fMatrix ? *(fMatrix->TranslationGet()) + v_TransVector : v_TransVector; }
inline float3 Translation2Get() {
return *(fMatrix->TranslationGet()) + Child->Translation1Get(); }
int GetTextureId() {
return TextureID; }
void ParentMatrix(float4x4 *m);
float MaxY( float4x4 const &m );
void AdjustDist();

View File

@@ -457,7 +457,7 @@ void TPythonScreens::init(cParser &parser, TModel3d *model, std::string const &n
WriteLog( "Python Screen: submodel " + subModelName + " not found - Ignoring screen" );
return; // nie ma takiego sub modelu w danej kabinie pomijamy
}
int textureId = GfxRenderer.Texture(subModel->GetTextureId()).id;
auto textureId = subModel->GetTextureId();
if (textureId <= 0)
{
WriteLog( "Python Screen: invalid texture id " + std::to_string(textureId) + " - Ignoring screen" );

View File

@@ -817,8 +817,8 @@ texture_manager::bind( texture_handle const Texture ) {
::glBindTexture( GL_TEXTURE_2D, texture(Texture).id );
m_activetexture = texture(Texture).id;
#else
if( Texture( Id ).bind() == resource_state::good ) {
m_activetexture = Id;
if( texture( Texture ).bind() == resource_state::good ) {
m_activetexture = Texture;
}
else {
// TODO: bind a special 'error' texture on failure
@@ -839,7 +839,7 @@ texture_manager::delete_textures() {
for( auto const &texture : m_textures ) {
// usunięcie wszyskich tekstur (bez usuwania struktury)
if( ( texture.id > 0 )
&& ( texture.id != -1 ) ) {
&& ( texture.id != -1 ) ) {
::glDeleteTextures( 1, &texture.id );
}
}
@@ -876,7 +876,7 @@ texture_manager::info() const {
+ std::to_string( readytexturecount )
+ " ("
+ to_string( readytexturesize / 1024.0f, 2 ) + " mb)"
+ " in vram, ";
+ " in vram, "
#endif
+ std::to_string( totaltexturecount )
+ " ("

View File

@@ -29,8 +29,18 @@ struct opengl_texture {
// methods
void load();
resource_state bind();
resource_state create();
resource_state
bind();
resource_state
create();
inline
int
width() const {
return data_width; }
inline
int
height() const {
return data_height; }
// members
GLuint id{ (GLuint)-1 }; // associated GL resource
bool has_alpha{ false }; // indicates the texture has alpha channel

View File

@@ -2156,7 +2156,6 @@ TTrack * TTrack::RaAnimate()
if (SwitchExtension->RightSwitch)
{ // nowa wersja z SPKS, ale odwrotnie lewa/prawa
vertex_array vertices;
if( TextureID1 ) {
// left blade
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts3, -nnumPts, fTexLength, 1.0, 0, 2, SwitchExtension->fOffset2 );
@@ -2171,7 +2170,6 @@ TTrack * TTrack::RaAnimate()
}
}
else { // lewa działa lepiej niż prawa
vertex_array vertices;
if( TextureID1 ) {
// right blade
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, origin, rpts4, -nnumPts, fTexLength, 1.0, 0, 2, -SwitchExtension->fOffset2 );

View File

@@ -5889,9 +5889,8 @@ bool TTrain::Update( double const Deltatime )
}
case 2: {
//światło wewnętrzne zapalone (255 216 176)
if( mvOccupied->ConverterFlag ==
true ) // jasnosc dla zalaczonej przetwornicy
{
if( mvOccupied->ConverterFlag == true ) {
// jasnosc dla zalaczonej przetwornicy
DynamicObject->InteriorLightLevel = 1.0f;
}
else {
@@ -6441,6 +6440,10 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
{
DynamicObject->mdKabina->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego
set_cab_controls();
// HACK: for some reason simulation at the start is slow until a sound is played
// until we do a proper fix, try to play a 'silent' sound when cab is entered
// TBD: it could be instead a legit sound of door closing
play_sound( dsbSwitch, DSBVOLUME_MIN );
return true;
}
return (token == "none");

View File

@@ -55,7 +55,7 @@ geometry_bank::create( vertex_array &Vertices, unsigned int const Type ) {
m_chunks.emplace_back( Vertices, Type );
// NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
geometry_handle chunkhandle { 0, static_cast<std::uint32_t>(m_chunks.size()) };
// template method
// template method implementation
create_( chunkhandle );
// all done
return chunkhandle;
@@ -81,7 +81,7 @@ geometry_bank::replace( vertex_array &Vertices, geometry_handle const &Geometry,
chunk.vertices.resize( Offset + Vertices.size(), basic_vertex() );
chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) );
}
// template method
// template method implementation
replace_( Geometry );
// all done
return true;
@@ -99,10 +99,17 @@ geometry_bank::append( vertex_array &Vertices, geometry_handle const &Geometry )
// draws geometry stored in specified chunk
void
geometry_bank::draw( geometry_handle const &Geometry, unsigned int const Streams ) {
// template method
// template method implementation
draw_( Geometry, Streams );
}
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
void
geometry_bank::release() {
// template method implementation
release_();
}
vertex_array const &
geometry_bank::vertices( geometry_handle const &Geometry ) const {
@@ -209,6 +216,13 @@ opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, unsigned int con
*/
}
// release () subclass details
void
opengl_vbogeometrybank::release_() {
delete_buffer();
}
void
opengl_vbogeometrybank::bind_buffer() {
@@ -225,6 +239,7 @@ opengl_vbogeometrybank::delete_buffer() {
::glDeleteBuffers( 1, &m_buffer );
if( m_activebuffer == m_buffer ) {
m_activebuffer = NULL;
bind_streams( stream::none );
}
m_buffer = NULL;
m_buffercapacity = 0;
@@ -314,6 +329,19 @@ opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, unsigned int cons
::glCallList( chunkrecord.list );
}
// release () subclass details
void
opengl_dlgeometrybank::release_() {
for( auto &chunkrecord : m_chunkrecords ) {
if( chunkrecord.list != 0 ) {
::glDeleteLists( chunkrecord.list, 1 );
chunkrecord.list = 0;
}
chunkrecord.streams = stream::none;
}
}
void
opengl_dlgeometrybank::delete_list( geometry_handle const &Geometry ) {
// NOTE: given it's our own internal method we trust it to be called with valid parameters
@@ -327,12 +355,37 @@ opengl_dlgeometrybank::delete_list( geometry_handle const &Geometry ) {
// geometry bank manager, holds collection of geometry banks
// performs a resource sweep
void
geometrybank_manager::update() {
m_resourcetimestamp = std::chrono::steady_clock::now();
// garbage collection sweep is limited to a number of records per call, to reduce impact on framerate
auto const sweeplastindex =
std::min(
m_resourcesweepindex + geometrybank_manager::unusedresourcesweepsize,
m_geometrybanks.size() );
auto const blanktimestamp { std::chrono::steady_clock::time_point() };
for( auto bankindex = m_resourcesweepindex; bankindex < sweeplastindex; ++bankindex ) {
if( ( m_geometrybanks[ bankindex ].second != blanktimestamp )
&& ( m_resourcetimestamp - m_geometrybanks[ bankindex ].second > geometrybank_manager::unusedresourcetimetolive ) ) {
m_geometrybanks[ bankindex ].first->release();
m_geometrybanks[ bankindex ].second = blanktimestamp;
}
}
m_resourcesweepindex = (
m_resourcesweepindex + geometrybank_manager::unusedresourcesweepsize >= m_geometrybanks.size() ?
0 : // if the next sweep chunk is beyond actual data, so start anew
m_resourcesweepindex + geometrybank_manager::unusedresourcesweepsize );
}
// creates a new geometry bank. returns: handle to the bank or NULL
geometrybank_handle
geometrybank_manager::create_bank() {
if( true == Global::bUseVBO ) { m_geometrybanks.emplace_back( std::make_shared<opengl_vbogeometrybank>() ); }
else { m_geometrybanks.emplace_back( std::make_shared<opengl_dlgeometrybank>() ); }
if( true == Global::bUseVBO ) { m_geometrybanks.emplace_back( std::make_shared<opengl_vbogeometrybank>(), std::chrono::steady_clock::time_point() ); }
else { m_geometrybanks.emplace_back( std::make_shared<opengl_dlgeometrybank>(), std::chrono::steady_clock::time_point() ); }
// NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
return geometrybank_handle( m_geometrybanks.size(), 0 );
}
@@ -341,7 +394,7 @@ geometrybank_manager::create_bank() {
geometry_handle
geometrybank_manager::create_chunk( vertex_array &Vertices, geometrybank_handle const &Geometry, int const Type ) {
auto const newchunkhandle = bank( Geometry )->create( Vertices, Type );
auto const newchunkhandle = bank( Geometry ).first->create( Vertices, Type );
if( newchunkhandle.chunk != 0 ) { return geometry_handle( Geometry.bank, newchunkhandle.chunk ); }
else { return geometry_handle( 0, 0 ); }
@@ -351,14 +404,14 @@ geometrybank_manager::create_chunk( vertex_array &Vertices, geometrybank_handle
bool
geometrybank_manager::replace( vertex_array &Vertices, geometry_handle const &Geometry, std::size_t const Offset ) {
return bank( Geometry )->replace( Vertices, Geometry, Offset );
return bank( Geometry ).first->replace( Vertices, Geometry, Offset );
}
// adds supplied vertex data at the end of specified chunk
bool
geometrybank_manager::append( vertex_array &Vertices, geometry_handle const &Geometry ) {
return bank( Geometry )->append( Vertices, Geometry );
return bank( Geometry ).first->append( Vertices, Geometry );
}
// draws geometry stored in specified chunk
void
@@ -366,12 +419,15 @@ geometrybank_manager::draw( geometry_handle const &Geometry, unsigned int const
if( Geometry == NULL ) { return; }
return bank( Geometry )->draw( Geometry, Streams );
auto &bankrecord = bank( Geometry );
bankrecord.second = m_resourcetimestamp;
bankrecord.first->draw( Geometry, Streams );
}
// provides direct access to vertex data of specfied chunk
vertex_array const &
geometrybank_manager::vertices( geometry_handle const &Geometry ) const {
return bank( Geometry )->vertices( Geometry );
return bank( Geometry ).first->vertices( Geometry );
}

View File

@@ -102,6 +102,9 @@ public:
template <typename Iterator_>
void
draw( Iterator_ First, Iterator_ Last, unsigned int const Streams = basic_streams ) { while( First != Last ) { draw( *First, Streams ); ++First; } }
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
void
release();
// provides direct access to vertex data of specfied chunk
vertex_array const &
vertices( geometry_handle const &Geometry ) const;
@@ -142,6 +145,8 @@ private:
virtual void replace_( geometry_handle const &Geometry ) = 0;
// draw() subclass details
virtual void draw_( geometry_handle const &Geometry, unsigned int const Streams ) = 0;
// resource release subclass details
virtual void release_() = 0;
};
// opengl vbo-based variant of the geometry bank
@@ -149,6 +154,8 @@ private:
class opengl_vbogeometrybank : public geometry_bank {
public:
// constructors:
opengl_vbogeometrybank() = default;
// destructor
~opengl_vbogeometrybank() {
delete_buffer(); }
@@ -179,6 +186,9 @@ private:
// draw() subclass details
void
draw_( geometry_handle const &Geometry, unsigned int const Streams );
// release () subclass details
void
release_();
void
bind_buffer();
void
@@ -200,7 +210,9 @@ private:
class opengl_dlgeometrybank : public geometry_bank {
public:
// methods:
// constructors:
opengl_dlgeometrybank() = default;
// destructor:
~opengl_dlgeometrybank() {
for( auto &chunkrecord : m_chunkrecords ) {
::glDeleteLists( chunkrecord.list, 1 ); } }
@@ -224,6 +236,9 @@ private:
// draw() subclass details
void
draw_( geometry_handle const &Geometry, unsigned int const Streams );
// release () subclass details
void
release_();
void
delete_list( geometry_handle const &Geometry );
@@ -240,6 +255,8 @@ class geometrybank_manager {
public:
// methods:
// performs a resource sweep
void update();
// creates a new geometry bank. returns: handle to the bank or NULL
geometrybank_handle
create_bank();
@@ -267,23 +284,31 @@ public:
private:
// types:
typedef std::deque< std::shared_ptr<geometry_bank> > geometrybank_sequence;
typedef std::pair<
std::shared_ptr<geometry_bank>,
std::chrono::steady_clock::time_point > geometrybanktimepoint_pair;
typedef std::deque< geometrybanktimepoint_pair > geometrybanktimepointpair_sequence;
// members:
geometrybank_sequence m_geometrybanks;
std::chrono::nanoseconds const unusedresourcetimetolive { std::chrono::seconds { 60 } };
geometrybanktimepointpair_sequence::size_type const unusedresourcesweepsize { 300 };
geometrybanktimepointpair_sequence m_geometrybanks;
geometrybanktimepointpair_sequence::size_type m_resourcesweepindex { 0 };
std::chrono::steady_clock::time_point m_resourcetimestamp { std::chrono::steady_clock::now() };
// methods
inline
bool
valid( geometry_handle const &Geometry ) {
return ( ( Geometry.bank != 0 )
&& ( Geometry.bank <= m_geometrybanks.size() ) ); }
return ( ( Geometry.bank != 0 )
&& ( Geometry.bank <= m_geometrybanks.size() ) ); }
inline
geometrybank_sequence::value_type &
geometrybanktimepointpair_sequence::value_type &
bank( geometry_handle const Geometry ) {
return m_geometrybanks[ Geometry.bank - 1 ]; }
inline
geometrybank_sequence::value_type const &
geometrybanktimepointpair_sequence::value_type const &
bank( geometry_handle const Geometry ) const {
return m_geometrybanks[ Geometry.bank - 1 ]; }

View File

@@ -201,14 +201,19 @@ std::string cParser::readQuotes(char const Quote) { // read the stream until spe
return token;
}
std::string cParser::readComment( std::string const &Break ) { // pobieranie znaków aż do znalezienia znacznika końca
std::string token = "";
void cParser::skipComment( std::string const &Endmark ) { // pobieranie znaków aż do znalezienia znacznika końca
std::string input = "";
auto const endmarksize = Endmark.size();
while( mStream->peek() != EOF ) { // o ile nie koniec pliku
token += mStream->get(); // pobranie znaku
if( token.rfind( Break ) != std::string::npos ) // szukanie znacznika końca
input += mStream->get(); // pobranie znaku
if( input.find( Endmark ) != std::string::npos ) // szukanie znacznika końca
break;
if( input.size() >= endmarksize ) {
// keep the read text short, to avoid pointless string re-allocations on longer comments
input = input.substr( 1 );
}
}
return token;
return;
}
bool cParser::findQuotes( std::string &String ) {
@@ -228,7 +233,7 @@ bool cParser::trimComments(std::string &String)
{
if (String.rfind((*cmIt).first) != std::string::npos)
{
readComment((*cmIt).second);
skipComment((*cmIt).second);
String.resize(String.rfind((*cmIt).first));
return true;
}

View File

@@ -92,7 +92,7 @@ class cParser //: public std::stringstream
// methods:
std::string readToken(bool ToLower = true, const char *Break = "\n\r\t ;");
std::string readQuotes( char const Quote = '\"' );
std::string readComment( std::string const &Break = "\n\r\t ;" );
void skipComment( std::string const &Endmark );
bool findQuotes( std::string &String );
bool trimComments( std::string &String );
std::size_t count();

View File

@@ -157,6 +157,7 @@ opengl_renderer::Render() {
// frustum tests are performed in 'world space' but after we set up frustum
// we no longer need camera translation, only rotation
::glMultMatrixd( glm::value_ptr( glm::dmat4( glm::dmat3( worldcamera ))));
Render( &World.Environment );
Render( &World.Ground );
@@ -538,19 +539,26 @@ opengl_renderer::Render( TGroundNode *Node ) {
}
case GL_LINES: {
if( ( Node->m_geometry == NULL )
if( ( Node->Piece->geometry == NULL )
|| ( Node->fLineThickness > 0.0 ) ) {
return false;
}
// setup
// w zaleznosci od koloru swiatla
auto const distance = std::sqrt( distancesquared );
auto const linealpha =
10.0 * Node->fLineThickness
/ std::max(
0.5 * Node->m_radius + 1.0,
distance - ( 0.5 * Node->m_radius ) );
::glColor4fv(
glm::value_ptr(
glm::vec4(
Node->Diffuse * glm::make_vec3( Global::DayLight.ambient ),
std::min(
1.0,
1000.0 * Node->fLineThickness / ( distancesquared + 1.0 ) ) ) ) );
Node->Diffuse * glm::make_vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla
1.0 ) ) ); // if the thickness is defined negative, lines are always drawn opaque
auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 32.0 );
if( linewidth > 1.0 ) {
::glLineWidth( static_cast<float>( linewidth ) );
}
GfxRenderer.Bind( 0 );
@@ -559,16 +567,18 @@ opengl_renderer::Render( TGroundNode *Node ) {
::glTranslated( originoffset.x, originoffset.y, originoffset.z );
// render
m_geometry.draw( Node->m_geometry );
m_geometry.draw( Node->Piece->geometry );
// post-render cleanup
::glPopMatrix();
if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); }
return true;
}
case GL_TRIANGLES: {
if( ( Node->m_geometry == NULL )
if( ( Node->Piece->geometry == NULL )
|| ( ( Node->iFlags & 0x10 ) == 0 ) ) {
return false;
}
@@ -582,7 +592,7 @@ opengl_renderer::Render( TGroundNode *Node ) {
::glTranslated( originoffset.x, originoffset.y, originoffset.z );
// render
m_geometry.draw( Node->m_geometry );
m_geometry.draw( Node->Piece->geometry );
// post-render cleanup
::glPopMatrix();
@@ -750,7 +760,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
// ...luminance
if( Global::fLuminance < Submodel->fLight ) {
// zeby swiecilo na kolorowo
::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr(Submodel->f4Diffuse) );
::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) );
}
// main draw call
@@ -772,7 +782,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) // kąt większy niż maksymalny stożek swiatła
{
float lightlevel = 1.0f;
float lightlevel = 1.0f; // TODO, TBD: parameter to control light strength
// view angle attenuation
float const anglefactor = ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle );
// distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation
@@ -946,7 +956,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) {
}
float const linealpha = static_cast<float>(
std::min(
1.2,
1.25,
5000 * Node->hvTraction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre
::glLineWidth( linealpha );
// McZapkie-261102: kolor zalezy od materialu i zasniedzenia
@@ -982,17 +992,23 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) {
}
case GL_LINES: {
if( ( Node->m_geometry == NULL )
if( ( Node->Piece->geometry == NULL )
|| ( Node->fLineThickness < 0.0 ) ) {
return false;
}
// setup
auto const linewidth = clamp( 100.0 * Node->fLineThickness / ( distancesquared + 1.0 ), 1.0, 10.0 );
auto const distance = std::sqrt( distancesquared );
auto const linealpha =
10.0 * Node->fLineThickness
/ std::max(
0.5 * Node->m_radius + 1.0,
distance - ( 0.5 * Node->m_radius ) );
::glColor4fv(
glm::value_ptr(
glm::vec4(
Node->Diffuse * glm::make_vec3( Global::DayLight.ambient ), // w zaleznosci od koloru swiatla
std::min( 1.0, linewidth ) ) ) );
std::min( 1.0, linealpha ) ) ) );
auto const linewidth = clamp( 0.5 * linealpha + Node->fLineThickness * Node->m_radius / 1000.0, 1.0, 32.0 );
if( linewidth > 1.0 ) {
::glLineWidth( static_cast<float>(linewidth) );
}
@@ -1004,20 +1020,18 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) {
::glTranslated( originoffset.x, originoffset.y, originoffset.z );
// render
m_geometry.draw( Node->m_geometry );
m_geometry.draw( Node->Piece->geometry );
// post-render cleanup
if( linewidth > 1.0 ) {
::glLineWidth( 1.0f );
}
::glPopMatrix();
if( linewidth > 1.0 ) { ::glLineWidth( 1.0f ); }
return true;
}
case GL_TRIANGLES: {
if( ( Node->m_geometry == NULL )
if( ( Node->Piece->geometry == NULL )
|| ( ( Node->iFlags & 0x20 ) == 0 ) ) {
return false;
}
@@ -1031,7 +1045,7 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) {
::glTranslated( originoffset.x, originoffset.y, originoffset.z );
// render
m_geometry.draw( Node->m_geometry );
m_geometry.draw( Node->Piece->geometry );
// post-render cleanup
::glPopMatrix();
@@ -1188,7 +1202,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
// ...luminance
if( Global::fLuminance < Submodel->fLight ) {
// zeby swiecilo na kolorowo
::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr(Submodel->f4Diffuse) );
::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) );
}
// main draw call
@@ -1311,7 +1325,7 @@ opengl_renderer::Update ( double const Deltatime ) {
else if( framerate > 60.0 ) { targetsegments = 225; targetfactor = 1.5f; }
else if( framerate > 30.0 ) { targetsegments = 90; targetfactor = Global::ScreenHeight / 768.0f; }
else { targetsegments = 9; targetfactor = Global::ScreenHeight / 768.0f * 0.75f; }
/*
if( targetsegments > Global::iSegmentsRendered ) {
Global::iSegmentsRendered = std::min( targetsegments, Global::iSegmentsRendered + 5 );
@@ -1320,6 +1334,7 @@ opengl_renderer::Update ( double const Deltatime ) {
Global::iSegmentsRendered = std::max( targetsegments, Global::iSegmentsRendered - 5 );
}
*/
if( targetfactor > Global::fDistanceFactor ) {
Global::fDistanceFactor = std::min( targetfactor, Global::fDistanceFactor + 0.05f );
@@ -1343,6 +1358,8 @@ opengl_renderer::Update ( double const Deltatime ) {
}
// TODO: add garbage collection and other less frequent works here
m_geometry.update();
if( true == DebugModeFlag ) {
m_debuginfo = m_textures.info();
}

View File

@@ -164,12 +164,22 @@ void
ui_layer::render_background() {
if( m_background == 0 ) return;
// NOTE: we limit/expect the background to come with 4:3 ratio.
// TODO, TBD: if we expose texture width or ratio from texture object, this limitation could be lifted
GfxRenderer.Bind( m_background );
quad( float4( 0.0f, 0.0f, 1024.0f, 768.0f ), float4( 1.0f, 1.0f, 1.0f, 1.0f ) );
auto const height { 768.0f };
auto const &texture = GfxRenderer.Texture( m_background );
float const width = (
texture.width() == texture.height() ?
1024.0f : // legacy mode, square texture displayed as 4:3 image
texture.width() / ( texture.height() / 768.0f ) );
quad(
float4(
( 1024.0f * 0.5f ) - ( width * 0.5f ),
( 768.0f * 0.5f ) - ( height * 0.5f ),
( 1024.0f * 0.5f ) - ( width * 0.5f ) + width,
( 768.0f * 0.5f ) - ( height * 0.5f ) + height ),
float4( 1.0f, 1.0f, 1.0f, 1.0f ) );
}
void
@@ -200,7 +210,11 @@ ui_layer::quad( float4 const &Coordinates, float4 const &Color ) {
Global::iWindowHeight / 768.0 :
Global::iWindowHeight / 768.0 * screenratio / ( 4.0f / 3.0f ) );
float const height = 768.0f * heightratio;
/*
float const heightratio = Global::iWindowHeight / 768.0f;
float const height = 768.0f * heightratio;
float const width = Global::iWindowWidth * heightratio;
*/
glColor4fv(&Color.x);
glBegin( GL_TRIANGLE_STRIP );

View File

@@ -1,5 +1,5 @@
#pragma once
#define VERSION_MAJOR 17
#define VERSION_MINOR 617
#define VERSION_MINOR 626
#define VERSION_REVISION 0