From 80b8da6749c2e333acbb71d220e5fdc5010b65be Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 27 Feb 2017 02:58:38 +0100 Subject: [PATCH] dynamically assigned render lights --- DynObj.cpp | 2 + DynObj.h | 6 +-- EU07.cpp | 3 ++ Globals.cpp | 12 +++-- Globals.h | 4 +- Ground.cpp | 101 ++++++++++++++++++++++++------------ Ground.h | 8 ++- World.cpp | 110 +++------------------------------------- dumb3d.h | 10 +++- lightarray.cpp | 75 +++++++++++++++++++++++++++ lightarray.h | 45 ++++++++++++++++ maszyna.vcxproj | 4 +- maszyna.vcxproj.filters | 6 +++ renderer.cpp | 78 ++++++++++++++++++++++++++++ renderer.h | 34 ++++++++++--- sky.cpp | 4 +- 16 files changed, 344 insertions(+), 158 deletions(-) create mode 100644 lightarray.cpp create mode 100644 lightarray.h diff --git a/DynObj.cpp b/DynObj.cpp index 18784aeb..6ff5fb66 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3760,12 +3760,14 @@ void TDynamicObject::Render() // double ObjDist= SquareMagnitude(Global::pCameraPosition-pos); if (this == Global::pUserDynamic) { // specjalne ustawienie, aby nie trzęsło +#ifdef EU07_USE_OLD_LIGHTING_MODEL if (Global::bSmudge) { // jak jest widoczna smuga, to pojazd renderować po // wyrenderowaniu smugi glPopMatrix(); // a to trzeba zebrać przed wyjściem return; } +#endif // if (Global::pWorld->) //tu trzeba by ustawić animacje na modelu // zewnętrznym glLoadIdentity(); // zacząć od macierzy jedynkowej diff --git a/DynObj.h b/DynObj.h index 2c675cc0..b5ff3e04 100644 --- a/DynObj.h +++ b/DynObj.h @@ -420,7 +420,7 @@ public: // modele składowe pojazdu void Render(); void RenderAlpha(); void RenderSounds(); - inline vector3 GetPosition() + inline vector3 GetPosition() const { return vPosition; }; @@ -436,7 +436,7 @@ public: // modele składowe pojazdu { return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; }; - inline vector3 VectorFront() + inline vector3 VectorFront() const { return vFront; }; @@ -456,7 +456,7 @@ public: // modele składowe pojazdu { return MoverParameters->Vel; }; - inline double GetLength() + inline double GetLength() const { return MoverParameters->Dim.L; }; diff --git a/EU07.cpp b/EU07.cpp index 4265784f..1f0e01b9 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -718,6 +718,9 @@ int WINAPI WinMain(HINSTANCE hInstance, // instance fullscreen)) return 0; // quit if window was not created SetForegroundWindow(hWnd); + + GfxRenderer.Init(); + // McZapkie: proba przeplukania klawiatury Console *pConsole = new Console(); // Ra: nie wiem, czy ma to sens, ale jakoś zainicjowac trzeba while (Console::Pressed(VK_F10)) diff --git a/Globals.cpp b/Globals.cpp index 36a331af..532ace49 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -104,8 +104,7 @@ GLfloat Global::diffuseLight[] = {0.85f, 0.85f, 0.80f, 1.0f}; GLfloat Global::specularLight[] = {0.95f, 0.94f, 0.90f, 1.0f}; #else opengl_light Global::DayLight; -opengl_light Global::VehicleLight; -opengl_light Global::VehicleLightRear; +int Global::DynamicLightCount{ 3 }; #endif GLfloat Global::whiteLight[] = {1.00f, 1.00f, 1.00f, 1.0f}; GLfloat Global::noLight[] = {0.00f, 0.00f, 0.00f, 1.0f}; @@ -577,7 +576,14 @@ void Global::ConfigParse(cParser &Parser) std::tm *localtime = std::localtime(&timenow); Global::fMoveLight = localtime->tm_yday + 1; // numer bieżącego dnia w roku } - // TODO: calculate lights single time here for static setup. or get rid of static setup + } + else if( token == "dynamiclights" ) { + // number of dynamic lights in the scene + Parser.getTokens( 1, false ); + Parser >> Global::DynamicLightCount; + // clamp the light number + Global::DynamicLightCount = std::min( 7, Global::DynamicLightCount ); // max 8 lights per opengl specs, and one used for sun + Global::DynamicLightCount = std::max( 1, Global::DynamicLightCount ); // at least one light for controlled vehicle } else if (token == "smoothtraction") { diff --git a/Globals.h b/Globals.h index 2690f9a0..0236006e 100644 --- a/Globals.h +++ b/Globals.h @@ -233,9 +233,7 @@ class Global #else // TODO: put these things in the renderer static opengl_light DayLight; - // TODO: possibly create a pool of few lights which can be attached to nearby vehicles - static opengl_light VehicleLight; - static opengl_light VehicleLightRear; + static int DynamicLightCount; #endif static GLfloat whiteLight[]; static GLfloat noLight[]; diff --git a/Ground.cpp b/Ground.cpp index d8185741..5a298282 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -1841,29 +1841,20 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) case TP_DYNAMIC: tmp->DynamicObject = new TDynamicObject(); // tmp->DynamicObject->Load(Parser); - parser->getTokens(); - *parser >> token; - str1 = token; // katalog - // McZapkie: doszedl parametr ze zmienialna skora - parser->getTokens(); - *parser >> token; - Skin = token; // tekstura wymienna - parser->getTokens(); - *parser >> token; - str3 = token; // McZapkie-131102: model w MMD + parser->getTokens(3); + *parser + >> str1 // katalog + >> Skin // tekstura wymienna + >> str3; // McZapkie-131102: model w MMD if (bTrainSet) { // jeśli pojazd jest umieszczony w składzie str = asTrainSetTrack; - parser->getTokens(); - *parser >> tf1; // Ra: -1 oznacza odwrotne wstawienie, normalnie w składzie 0 - parser->getTokens(); - *parser >> token; - DriverType = token; // McZapkie:010303 - w przyszlosci rozne - // konfiguracje mechanik/pomocnik itp + parser->getTokens(3); + *parser + >> tf1 // Ra: -1 oznacza odwrotne wstawienie, normalnie w składzie 0 + >> DriverType // McZapkie:010303 - w przyszlosci rozne konfiguracje mechanik/pomocnik itp + >> str4; tf3 = fTrainSetVel; // prędkość - parser->getTokens(); - *parser >> token; - str4 = token; int2 = str4.find("."); // yB: wykorzystuje tutaj zmienna, ktora potem bedzie ladunkiem if (int2 != string::npos) // yB: jesli znalazl kropke, to ja przetwarza jako parametry { @@ -1892,17 +1883,12 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) fTrainSetDist = 0; // zerowanie dodatkowego przesunięcia asTrainName = ""; // puste oznacza jazdę pojedynczego bez rozkładu, "none" jest dla // składu (trainset) - parser->getTokens(); - *parser >> token; - str = token; // track - parser->getTokens(); - *parser >> tf1; // Ra: -1 oznacza odwrotne wstawienie - parser->getTokens(); - *parser >> token; - DriverType = token; // McZapkie:010303: obsada - parser->getTokens(); - *parser >> - tf3; // prędkość, niektórzy wpisują tu "3" jako sprzęg, żeby nie było tabliczki + parser->getTokens(4); + *parser + >> str // track + >> tf1 // Ra: -1 oznacza odwrotne wstawienie + >> DriverType // McZapkie:010303: obsada + >> tf3; // prędkość, niektórzy wpisują tu "3" jako sprzęg, żeby nie było tabliczki iTrainSetWehicleNumber = 0; TempConnectionType[iTrainSetWehicleNumber] = 3; // likwidacja tabliczki na końcu? } @@ -1911,8 +1897,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) if (int2 > 0) { // jeżeli ładunku jest więcej niż 0, to rozpoznajemy jego typ parser->getTokens(); - *parser >> token; - str2 = token; // LoadType + *parser >> str2; // LoadType if (str2 == "enddynamic") // idiotoodporność: ładunek bez podanego typu { str2 = ""; @@ -1986,6 +1971,16 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) } if (token.compare("enddynamic") != 0) Error("enddynamic statement missing"); +/* + if( tmp->DynamicObject->MoverParameters->LightPowerSource.SourceType != TPowerSource::NotDefined ) { + // if the vehicle has defined light source, it can (potentially) emit light, so add it to the light array +*/ + if( tmp->DynamicObject->MoverParameters->SecuritySystem.SystemType != 0 ) { + // we check for presence of security system, as a way to determine whether the vehicle is a controllable engine + // NOTE: this isn't 100% precise, e.g. middle EZT module comes with security system, while it has no lights + m_lights.insert( tmp->DynamicObject ); + } + break; case TP_MODEL: if (rmin < 0) @@ -4558,6 +4553,25 @@ bool TGround::Update(double dt, int iter) return true; }; +// updates scene lights array +void +TGround::Update_Lights() { + + m_lights.update(); + // arrange the light array from closest to farthest from current position of the camera + auto const camera = Global::pCameraPosition; + std::sort( + m_lights.data.begin(), + m_lights.data.end(), + [&]( light_array::light_record const &Left, light_array::light_record const &Right ) { + // move lights which are off at the end... + if( Left.intensity == 0.0f ) { return false; } + if( Right.intensity == 0.0f ) { return true; } + // ...otherwise prefer closer and/or brigher light sources + return ((camera - Left.position).LengthSquared() * (1.0f - Left.intensity)) < ((camera - Right.position).LengthSquared() * (1.0f - Right.intensity)); + } ); +} + // Winger 170204 - szukanie trakcji nad pantografami bool TGround::GetTraction(TDynamicObject *model) { // aktualizacja drutu zasilającego dla każdego pantografu, żeby odczytać napięcie @@ -4808,6 +4822,27 @@ bool TGround::GetTraction(TDynamicObject *model) return true; }; +bool +TGround::Render( Math3D::vector3 const &Camera ) { + + GfxRenderer.Update_Lights( m_lights ); + + if( Global::bUseVBO ) { // renderowanie przez VBO + if( !RenderVBO( Camera ) ) + return false; + if( !RenderAlphaVBO( Camera ) ) + return false; + } + else { // renderowanie przez Display List + if( !RenderDL( Camera ) ) + return false; + if( !RenderAlphaDL( Camera ) ) + return false; + } + + return true; +} + bool TGround::RenderDL(vector3 pPosition) { // renderowanie scenerii z Display List - faza nieprzezroczystych glDisable(GL_BLEND); @@ -5339,6 +5374,8 @@ void TGround::DynamicRemove(TDynamicObject *dyn) node = (*n); // zapamiętanie węzła, aby go usunąć (*n) = node->nNext; // pominięcie na liście Global::TrainDelete(d); + // remove potential entries in the light array + m_lights.remove( d ); d = d->Next(); // przejście do kolejnego pojazdu, póki jeszcze jest delete node; // usuwanie fizyczne z pamięci } diff --git a/Ground.h b/Ground.h index b78023d6..41f1672d 100644 --- a/Ground.h +++ b/Ground.h @@ -16,7 +16,9 @@ http://mozilla.org/MPL/2.0/. #include "ResourceManager.h" #include "Texture.h" #include "dumb3d.h" +#include "Float3d.h" #include "Names.h" +#include "lightarray.h" using namespace Math3D; @@ -303,11 +305,11 @@ class TGround TNames *sTracks = nullptr; // posortowane nazwy torów i eventów #else typedef std::unordered_map event_map; -// typedef std::unordered_map groundnode_map; event_map m_eventmap; -// groundnode_map m_memcellmap, m_modelmap, m_trackmap; TNames m_trackmap; #endif + light_array m_lights; // collection of dynamic light sources present in the scene + private: // metody prywatne bool EventConditon(TEvent *e); @@ -350,8 +352,10 @@ class TGround void MoveGroundNode(vector3 pPosition); void UpdatePhys(double dt, int iter); // aktualizacja fizyki stałym krokiem bool Update(double dt, int iter); // aktualizacja przesunięć zgodna z FPS + void Update_Lights(); // updates scene lights array bool AddToQuery(TEvent *Event, TDynamicObject *Node); bool GetTraction(TDynamicObject *model); + bool Render( Math3D::vector3 const &Camera ); bool RenderDL(vector3 pPosition); bool RenderAlphaDL(vector3 pPosition); bool RenderVBO(vector3 pPosition); diff --git a/World.cpp b/World.cpp index 940ee222..9fd96101 100644 --- a/World.cpp +++ b/World.cpp @@ -512,25 +512,6 @@ bool TWorld::Init(HWND NhWnd, HDC hDC) Global::DayLight.diffuse[ 0 ] = 255.0 / 255.0; Global::DayLight.diffuse[ 1 ] = 242.0 / 255.0; Global::DayLight.diffuse[ 2 ] = 231.0 / 255.0; - - Global::VehicleLight.id = opengl_renderer::vehiclelight; - // directional light - Global::VehicleLight.position[ 3 ] = 1.0f; - ::glLightf( opengl_renderer::vehiclelight, GL_SPOT_CUTOFF, 20.0f ); - ::glLightf( opengl_renderer::vehiclelight, GL_SPOT_EXPONENT, 10.0f ); - ::glLightf( opengl_renderer::vehiclelight, GL_CONSTANT_ATTENUATION, 0.0f ); - ::glLightf( opengl_renderer::vehiclelight, GL_LINEAR_ATTENUATION, 0.035f ); - // halogen light. TODO: allow light type definition - Global::VehicleLight.diffuse[ 0 ] = 255.0 / 255.0; - Global::VehicleLight.diffuse[ 1 ] = 241.0 / 255.0; - Global::VehicleLight.diffuse[ 2 ] = 224.0 / 255.0; - // rear light - Global::VehicleLightRear = Global::VehicleLight; - Global::VehicleLightRear.id = opengl_renderer::vehiclelightrear; - ::glLightf( opengl_renderer::vehiclelightrear, GL_SPOT_CUTOFF, 20.0f ); - ::glLightf( opengl_renderer::vehiclelightrear, GL_SPOT_EXPONENT, 10.0f ); - ::glLightf( opengl_renderer::vehiclelightrear, GL_CONSTANT_ATTENUATION, 0.0f ); - ::glLightf( opengl_renderer::vehiclelightrear, GL_LINEAR_ATTENUATION, 0.035f ); #endif Ground.Init(Global::SceneryFile, hDC); @@ -1256,6 +1237,8 @@ bool TWorld::Update() Ground.CheckQuery(); + Ground.Update_Lights(); + if( Train != nullptr ) { TSubModel::iInstance = reinterpret_cast( Train->Dynamic() ); Train->Update( dt ); @@ -1275,13 +1258,6 @@ bool TWorld::Update() ( Train->Dynamic()->fShade <= 0.0 ? ( Global::fLuminance <= 0.5 ) : ( Train->Dynamic()->fShade * Global::fLuminance <= 0.5 ) ) ); - - // match the vehicle light position with new position of the vehicle - Global::VehicleLight.set_position( Train->Dynamic()->GetPosition() + ( Train->Dynamic()->VectorFront() * Train->Dynamic()->GetLength() * 0.45 ) ); - Global::VehicleLight.direction = Train->Dynamic()->VectorFront(); - Global::VehicleLightRear.set_position( Train->Dynamic()->GetPosition() - ( Train->Dynamic()->VectorFront() * Train->Dynamic()->GetLength() * 0.45 ) ); - Global::VehicleLightRear.direction = Train->Dynamic()->VectorFront(); - Global::VehicleLightRear.direction.RotateY( M_PI ); } m_init = true; @@ -1556,78 +1532,11 @@ bool TWorld::Render() Environment.render(); } - // enable vehicle light, if it's present and on - if( nullptr != Train ) { + if( false == Ground.Render( Camera.Pos ) ) { return false; } - auto const &frontlights = Train->Controlled()->iLights[ 0 ]; - int const frontlightcount = 0 + - ( ( frontlights & 1 ) ? 1 : 0 ) + - ( ( frontlights & 4 ) ? 1 : 0 ) + - ( ( frontlights & 16 ) ? 1 : 0 ); - - if( ( true == Train->Controlled()->Battery ) - && ( frontlightcount > 0 ) ) { - // halogen light. TODO: allow light type definition - Global::VehicleLight.ambient[ 0 ] = 0.15f * 255.0 * frontlightcount / 255.0; - Global::VehicleLight.ambient[ 1 ] = 0.15f * 241.0 * frontlightcount / 255.0; - Global::VehicleLight.ambient[ 2 ] = 0.15f * 224.0 * frontlightcount / 255.0; - - ::glLightf( opengl_renderer::vehiclelight, GL_LINEAR_ATTENUATION, 0.3f / pow(frontlightcount, 2) ); - glEnable( GfxRenderer.vehiclelight ); - Global::VehicleLight.apply_intensity(); - Global::VehicleLight.apply_angle(); - } - else { - glDisable( GfxRenderer.vehiclelight ); - } - auto const &rearlights = Train->Controlled()->iLights[ 1 ]; - int const rearlightcount = 0 + - ( ( rearlights & 1 ) ? 1 : 0 ) + - ( ( rearlights & 4 ) ? 1 : 0 ) + - ( ( rearlights & 16 ) ? 1 : 0 ); - - if( ( true == Train->Controlled()->Battery ) - && ( rearlightcount > 0 ) ) { - // halogen light. TODO: allow light type definition - Global::VehicleLightRear.ambient[ 0 ] = 0.15f * 255.0 * rearlightcount / 255.0; - Global::VehicleLightRear.ambient[ 1 ] = 0.15f * 241.0 * rearlightcount / 255.0; - Global::VehicleLightRear.ambient[ 2 ] = 0.15f * 224.0 * rearlightcount / 255.0; - - ::glLightf( opengl_renderer::vehiclelightrear, GL_LINEAR_ATTENUATION, 0.3f / pow( rearlightcount, 2 ) ); - glEnable( GfxRenderer.vehiclelightrear ); - Global::VehicleLightRear.apply_intensity(); - Global::VehicleLightRear.apply_angle(); - } - else { - glDisable( GfxRenderer.vehiclelightrear ); - } - } - - if (Global::bUseVBO) - { // renderowanie przez VBO - if (!Ground.RenderVBO(Camera.Pos)) - return false; - if (!Ground.RenderAlphaVBO(Camera.Pos)) - return false; - } - else - { // renderowanie przez Display List - if (!Ground.RenderDL(Camera.Pos)) - return false; - if (!Ground.RenderAlphaDL(Camera.Pos)) - return false; - } -/* - TSubModel::iInstance = (int)(Train ? Train->Dynamic() : 0); //żeby nie robić cudzych animacji - // if (Camera.Type==tp_Follow) - if (Train) - Train->Update(); -*/ Render_Cab(); Render_UI(); -// glFlush(); - // Global::bReCompile=false; //Ra: już zrobiona rekompilacja ResourceManager::Sweep( Timer::GetSimulationTime() ); return true; @@ -1652,6 +1561,7 @@ TWorld::Render_Cab() { // ABu: Rendering kabiny jako ostatniej, zeby bylo widac przez szyby, tylko w widoku ze srodka return; } + /* // ABu: Rendering kabiny jako ostatniej, zeby bylo widac przez szyby, tylko w widoku ze srodka if( ( Train->Dynamic()->mdKabina != Train->Dynamic()->mdModel ) && @@ -1777,18 +1687,18 @@ TWorld::Render_Cab() { glEnable( GL_FOG ); } } -*/ + glEnable( GL_LIGHTING ); // po renderowaniu smugi jest to wyłączone // Ra: pojazd użytkownika należało by renderować po smudze, aby go nie rozświetlała Global::bSmudge = false; // aby model użytkownika się teraz wyrenderował dynamic->Render(); dynamic->RenderAlpha(); // przezroczyste fragmenty pojazdów na torach -/* + } // yB: moje smuuugi 1 - koniec else - glEnable( GL_LIGHTING ); // po renderowaniu drutów może być to wyłączone. TODO: have the wires render take care of its own shit -*/ +*/ glEnable( GL_LIGHTING ); // po renderowaniu drutów może być to wyłączone. TODO: have the wires render take care of its own shit + if( dynamic->mdKabina ) // bo mogła zniknąć przy przechodzeniu do innego pojazdu { #ifdef EU07_USE_OLD_LIGHTING_MODEL @@ -1874,10 +1784,6 @@ TWorld::Render_Cab() { glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseCabLight ); glLightfv( GL_LIGHT0, GL_SPECULAR, specularCabLight ); #else - // cab shouldn't get affected by the vehicle light, so we disable it here - ::glDisable( GfxRenderer.vehiclelight ); - ::glDisable( GfxRenderer.vehiclelightrear ); - if( dynamic->InteriorLightLevel > 0.0f ) { // crude way to light the cabin, until we have something more complete in place diff --git a/dumb3d.h b/dumb3d.h index 0d93b8e3..23dd5ce5 100644 --- a/dumb3d.h +++ b/dumb3d.h @@ -83,7 +83,8 @@ class vector3 void inline Normalize(); void inline SafeNormalize(); - double inline Length(); + double inline Length() const; + double inline LengthSquared() const; void inline Zero() { x = y = z = 0.0; @@ -435,11 +436,16 @@ void inline vector3::Normalize() z *= il; } -double inline vector3::Length() +double inline vector3::Length() const { return SQRT_FUNCTION(x * x + y * y + z * z); } +double inline vector3::LengthSquared() const { + + return ( x * x + y * y + z * z ); +} + inline bool operator==(const matrix4x4 &m1, const matrix4x4 &m2) { for (int x = 0; x < 4; ++x) diff --git a/lightarray.cpp b/lightarray.cpp new file mode 100644 index 00000000..6ad61a7f --- /dev/null +++ b/lightarray.cpp @@ -0,0 +1,75 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +/* + MaSzyna EU07 locomotive simulator + Copyright (C) 2001-2004 Marcin Wozniak and others + +*/ + +#include "stdafx.h" +#include "lightarray.h" +#include "dynobj.h" + +void +light_array::insert( TDynamicObject const *Owner ) { + + // we're only storing lights for locos, which have two sets of lights, front and rear + // for a more generic role this function would have to be tweaked to add vehicle type-specific light combinations + data.emplace_back( Owner, 0 ); + data.emplace_back( Owner, 1 ); +} + +void +light_array::remove( TDynamicObject const *Owner ) { + + data.erase( + std::remove_if( + data.begin(), + data.end(), + [=]( light_record const &light ){ return light.owner == Owner; } ), + data.end() ); +} + +// updates records in the collection +void +light_array::update() { + + for( auto &light : data ) { + // update light parameters to match current data of the owner + if( light.index == 0 ) { + // front light set + light.position = light.owner->GetPosition() + ( light.owner->VectorFront() * light.owner->GetLength() * 0.45 ); + light.direction = light.owner->VectorFront(); + } + else { + // rear light set + light.position = light.owner->GetPosition() - ( light.owner->VectorFront() * light.owner->GetLength() * 0.45 ); + light.direction = light.owner->VectorFront(); + light.direction.x = -light.direction.x; + light.direction.z = -light.direction.z; + } + // determine intensity of this light set + if( true == light.owner->MoverParameters->Battery ) { + // with battery on, the intensity depends on the state of activated switches + auto const &lightbits = light.owner->iLights[ light.index ]; + light.count = 0 + + ( ( lightbits & 1 ) ? 1 : 0 ) + + ( ( lightbits & 4 ) ? 1 : 0 ) + + ( ( lightbits & 16 ) ? 1 : 0 ); + + light.intensity = 0.15f * light.count; // TODO: intensity can be affected further by dim switch or other factors + } + else { + // with battery off the lights are off + light.intensity = 0.0f; + light.count = 0; + } + } +} diff --git a/lightarray.h b/lightarray.h new file mode 100644 index 00000000..34b317d9 --- /dev/null +++ b/lightarray.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include "dumb3d.h" +#include "float3d.h" +#include "dynobj.h" + +// collection of virtual light sources present in the scene +// used by the renderer to determine most suitable placement for actual light sources during render +struct light_array { + +public: +// types + struct light_record { + + light_record( TDynamicObject const *Owner, int const Lightindex) : + owner(Owner), index(Lightindex) + {}; + + TDynamicObject const *owner; // the object in world which 'carries' the light + int index{ -1 }; // 0: front lights, 1: rear lights + Math3D::vector3 position; // position of the light in 3d scene + Math3D::vector3 direction; // direction of the light in 3d scene + float3 color{ 255.0f / 255.0f, 241.0f / 255.0f, 224.0f / 255.0f }; // color of the light, default is halogen light + float intensity{ 0.0f }; // (combined) intensity of the light(s) + int count{ 0 }; // number (or pattern) of active light(s) + }; + +// methods + // adds records for lights of specified owner to the collection + void + insert( TDynamicObject const *Owner ); + // removes records for lights of specified owner from the collection + void + remove( TDynamicObject const *Owner ); + // updates records in the collection + void + update(); + +// types + typedef std::vector lightrecord_array; + +// members + lightrecord_array data; +}; diff --git a/maszyna.vcxproj b/maszyna.vcxproj index 0f287655..b9d71414 100644 --- a/maszyna.vcxproj +++ b/maszyna.vcxproj @@ -104,6 +104,7 @@ + @@ -166,6 +167,7 @@ + @@ -222,4 +224,4 @@ - + \ No newline at end of file diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index e38ea0db..e124aab2 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -201,6 +201,9 @@ Source Files + + Source Files + @@ -392,6 +395,9 @@ Header Files + + Header Files + diff --git a/renderer.cpp b/renderer.cpp index 13fdb85b..809560cc 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -9,7 +9,85 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "renderer.h" +#include "globals.h" opengl_renderer GfxRenderer; +void +opengl_renderer::Init() { + + // create dynamic light pool + for( int idx = 0; idx < Global::DynamicLightCount; ++idx ) { + + opengl_light light; + light.id = GL_LIGHT1 + idx; + + light.position[ 3 ] = 1.0f; + ::glLightf( light.id, GL_SPOT_CUTOFF, 20.0f ); + ::glLightf( light.id, GL_SPOT_EXPONENT, 10.0f ); + ::glLightf( light.id, GL_CONSTANT_ATTENUATION, 0.0f ); + ::glLightf( light.id, GL_LINEAR_ATTENUATION, 0.035f ); + + m_lights.emplace_back( light ); + } +} + +void +opengl_renderer::Update_Lights( light_array const &Lights ) { + + int const count = std::min( m_lights.size(), Lights.data.size() ); + if( count == 0 ) { return; } + + auto renderlight = m_lights.begin(); + + for( auto const &scenelight : Lights.data ) { + + if( renderlight == m_lights.end() ) { + // we ran out of lights to assign + break; + } + if( scenelight.intensity == 0.0f ) { + // all lights past this one are bound to be off + break; + } + if( ( Global::pCameraPosition - scenelight.position ).Length() > 1000.0f ) { + // we don't care about lights past arbitrary limit of 1 km. + // but there could still be weaker lights which are closer, so keep looking + continue; + } + // if the light passed tests so far, it's good enough + renderlight->set_position( scenelight.position ); + renderlight->direction = scenelight.direction; + + renderlight->diffuse[ 0 ] = scenelight.color.x; + renderlight->diffuse[ 1 ] = scenelight.color.y; + renderlight->diffuse[ 2 ] = scenelight.color.z; + renderlight->ambient[ 0 ] = scenelight.color.x * scenelight.intensity; + renderlight->ambient[ 1 ] = scenelight.color.y * scenelight.intensity; + renderlight->ambient[ 2 ] = scenelight.color.z * scenelight.intensity; + + ::glLightf( renderlight->id, GL_LINEAR_ATTENUATION, 0.3f / std::pow( scenelight.count, 2 ) ); + ::glEnable( renderlight->id ); + + renderlight->apply_intensity(); + renderlight->apply_angle(); + + ++renderlight; + } + + while( renderlight != m_lights.end() ) { + // if we went through all scene lights and there's still opengl lights remaining, kill these + ::glDisable( renderlight->id ); + ++renderlight; + } +} + +void +opengl_renderer::Disable_Lights() { + + for( int idx = 0; idx < m_lights.size() + 1; ++idx ) { + + ::glDisable( GL_LIGHT0 + idx ); + } +} //--------------------------------------------------------------------------- diff --git a/renderer.h b/renderer.h index 69e39ca7..ed0c3ba5 100644 --- a/renderer.h +++ b/renderer.h @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "opengl/glew.h" #include "texture.h" +#include "lightarray.h" #include "dumb3d.h" struct opengl_light { @@ -23,10 +24,10 @@ struct opengl_light { GLfloat specular[ 4 ]; opengl_light() { - position[ 0 ] = position[ 1 ] = position[ 2 ] = 0.0f; position[ 3 ] = 1.0f; - ambient[ 0 ] = ambient[ 1 ] = ambient[ 2 ] = 0.0f; ambient[ 3 ] = 1.0f; - diffuse[ 0 ] = diffuse[ 1 ] = diffuse[ 2 ] = diffuse[ 3 ] = 1.0f; - specular[ 0 ] = specular[ 1 ] = specular[ 2 ] = specular[ 3 ] = 1.0f; + position[ 0 ] = position[ 1 ] = position[ 2 ] = 0.0f; position[ 3 ] = 1.0f; // 0,0,0,1 + ambient[ 0 ] = ambient[ 1 ] = ambient[ 2 ] = 0.0f; ambient[ 3 ] = 1.0f; // 0,0,0,1 + diffuse[ 0 ] = diffuse[ 1 ] = diffuse[ 2 ] = diffuse[ 3 ] = 1.0f; // 1,1,1,1 + specular[ 0 ] = specular[ 1 ] = specular[ 2 ] = specular[ 3 ] = 1.0f; // 1,1,1,1 } inline @@ -72,33 +73,50 @@ struct opengl_material { class opengl_renderer { public: - GLenum static const sunlight{ GL_LIGHT0 }; - GLenum static const vehiclelight{ GL_LIGHT1 }; - GLenum static const vehiclelightrear{ GL_LIGHT2 }; +// types + +// methods + void + Init(); + + void + Update_Lights( light_array const &Lights ); + + void + Disable_Lights(); texture_manager::size_type GetTextureId( std::string Filename, std::string const &Dir, int const Filter = -1, bool const Loadnow = true ) { return m_textures.GetTextureId( Filename, Dir, Filter, Loadnow ); } + void Bind( texture_manager::size_type const Id ) { // temporary until we separate the renderer m_textures.Bind( Id ); } + opengl_texture & Texture( texture_manager::size_type const Id ) { return m_textures.Texture( Id ); } +// members + GLenum static const sunlight{ GL_LIGHT0 }; + private: +// types enum class rendermode { color }; + typedef std::vector opengllight_array; + +// members rendermode renderpass{ rendermode::color }; - + opengllight_array m_lights; texture_manager m_textures; }; diff --git a/sky.cpp b/sky.cpp index 78932cf7..7c3aa79f 100644 --- a/sky.cpp +++ b/sky.cpp @@ -36,7 +36,7 @@ void TSky::Render( float3 const &Tint ) glLightfv(GL_LIGHT0, GL_POSITION, lightPos); #else ::glEnable( GL_LIGHTING ); - ::glDisable( GL_LIGHT0 ); + GfxRenderer.Disable_Lights(); ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, &Tint.x ); #endif if (Global::bUseVBO) @@ -56,7 +56,7 @@ void TSky::Render( float3 const &Tint ) #else GLfloat noambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, noambient ); - ::glEnable( GL_LIGHT0 ); + ::glEnable( GL_LIGHT0 ); // other lights will be enabled during lights update ::glDisable( GL_LIGHTING ); #endif }