From b5ae395c426fbd1c6e94dcce4f48734685557ea3 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 8 Mar 2017 23:54:20 +0100 Subject: [PATCH] camera frustum --- Camera.cpp | 14 ++++ Camera.h | 10 +++ DynObj.cpp | 8 +- EU07.cpp | 2 +- Globals.cpp | 6 +- Model3d.cpp | 21 ++++- World.cpp | 15 +++- frustum.cpp | 178 ++++++++++++++++++++++++++++++++++++++++ frustum.h | 67 +++++++++++++++ maszyna.vcxproj | 2 + maszyna.vcxproj.filters | 6 ++ 11 files changed, 314 insertions(+), 15 deletions(-) create mode 100644 frustum.cpp create mode 100644 frustum.h diff --git a/Camera.cpp b/Camera.cpp index ffd58d66..e92180ee 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -169,3 +169,17 @@ void TCamera::Stop() Type = tp_Follow; Velocity = vector3(0, 0, 0); }; + +// returns true if specified object is within camera frustum, false otherwise +bool +TCamera::IsVisible( TDynamicObject const *Dynamic ) const { + + // sphere test is faster than AABB, so we'll use it here + float3 diagonal( + Dynamic->MoverParameters->Dim.L, + Dynamic->MoverParameters->Dim.H, + Dynamic->MoverParameters->Dim.W ); + float const radius = static_cast(diagonal.Length()) * 0.5f; + + return ( m_frustum.sphere_inside( Dynamic->GetPosition(), radius ) > 0.0f ); +} diff --git a/Camera.h b/Camera.h index 0d1ab315..2ec975da 100644 --- a/Camera.h +++ b/Camera.h @@ -11,6 +11,9 @@ http://mozilla.org/MPL/2.0/. #define CameraH #include "dumb3d.h" +#include "frustum.h" +#include "dynobj.h" + using namespace Math3D; //--------------------------------------------------------------------------- @@ -25,6 +28,8 @@ class TCamera { private: vector3 pOffset; // nie używane (zerowe) + cFrustum m_frustum; + public: // McZapkie: potrzebuje do kiwania na boki double Pitch; double Yaw; // w środku: 0=do przodu; na zewnątrz: 0=na południe @@ -51,6 +56,11 @@ class TCamera void SetCabMatrix(vector3 &p); void RaLook(); void Stop(); + inline + void + SetFrustum() { m_frustum.calculate(); } + bool + IsVisible( TDynamicObject const *Dynamic ) const; // bool GetMatrix(matrix4x4 &Matrix); vector3 PtNext, PtPrev; }; diff --git a/DynObj.cpp b/DynObj.cpp index cf31b015..1a3a2dc3 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3703,6 +3703,7 @@ void TDynamicObject::TurnOff() void TDynamicObject::Render() { // rysowanie elementów nieprzezroczystych // youBy - sprawdzamy, czy jest sens renderowac +/* double modelrotate; vector3 tempangle; // zmienne @@ -3713,8 +3714,7 @@ void TDynamicObject::Render() if (ObjSqrDist < 500) // jak jest blisko - do 70m modelrotate = 0.01; // mały kąt, żeby nie znikało else - { // Global::pCameraRotation to kąt bewzględny w świecie (zero - na - // północ) + { // Global::pCameraRotation to kąt bewzględny w świecie (zero - na północ) tempangle = (vPosition - Global::pCameraPosition); // wektor od kamery modelrotate = ABuAcos(tempangle); // określenie kąta // if (modelrotate>M_PI) modelrotate-=(2*M_PI); @@ -3730,8 +3730,8 @@ void TDynamicObject::Render() if (modelrotate < maxrot) renderme = true; - - if (renderme) +*/ + if (Global::pCamera->IsVisible(this)) { TSubModel::iInstance = (size_t)this; //żeby nie robić cudzych animacji // AnsiString asLoadName=""; diff --git a/EU07.cpp b/EU07.cpp index aeae755f..3e3eedcf 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -93,7 +93,7 @@ void window_resize_callback(GLFWwindow *window, int w, int h) { Global::ScreenWidth = w; Global::ScreenHeight = h; - Global::fDistanceFactor = h / 768.0f; // not sure if this is really something we want to use + Global::fDistanceFactor = std::max( 0.5f, h / 768.0f ); // not sure if this is really something we want to use glViewport(0, 0, w, h); } diff --git a/Globals.cpp b/Globals.cpp index b0e54344..9693076d 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -495,9 +495,9 @@ void Global::ConfigParse(cParser &Parser) { Parser.getTokens(); - Parser >> token; - - Global::bUseVBO = (token == "yes"); + Parser >> Global::bUseVBO; + // NOTE: temporary override until render paths are sorted out + Global::bUseVBO = false; } else if (token == "feedbackmode") { diff --git a/Model3d.cpp b/Model3d.cpp index d3378b69..040a0eec 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1050,7 +1050,9 @@ void TSubModel::RaAnimation(TAnimType a) void TSubModel::RenderDL() { // główna procedura renderowania przez DL - if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) + if( ( iVisible ) + && ( fSquareDist >= (fSquareMinDist / Global::fDistanceFactor) ) + && ( fSquareDist <= (fSquareMaxDist * Global::fDistanceFactor) ) ) { if (iFlags & 0xC000) { @@ -1147,7 +1149,9 @@ void TSubModel::RenderDL() void TSubModel::RenderAlphaDL() { // renderowanie przezroczystych przez DL - if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) + if( ( iVisible ) + && ( fSquareDist >= (fSquareMinDist / Global::fDistanceFactor) ) + && ( fSquareDist <= (fSquareMaxDist * Global::fDistanceFactor) ) ) { if (iFlags & 0xC000) { @@ -1227,7 +1231,9 @@ void TSubModel::RenderAlphaDL() void TSubModel::RenderVBO() { // główna procedura renderowania przez VBO - if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) + if( ( iVisible ) + && ( fSquareDist >= (fSquareMinDist / Global::fDistanceFactor) ) + && ( fSquareDist <= (fSquareMaxDist * Global::fDistanceFactor) ) ) { if (iFlags & 0xC000) { @@ -1400,7 +1406,9 @@ void TSubModel::RenderVBO() void TSubModel::RenderAlphaVBO() { // renderowanie przezroczystych przez VBO - if (iVisible && (fSquareDist >= fSquareMinDist) && (fSquareDist < fSquareMaxDist)) + if( ( iVisible ) + && ( fSquareDist >= (fSquareMinDist / Global::fDistanceFactor) ) + && ( fSquareDist <= (fSquareMaxDist * Global::fDistanceFactor) ) ) { if (iFlags & 0xC000) { @@ -1475,6 +1483,8 @@ void TSubModel::RaArrayFill(CVertNormTex *Vert) Next->RaArrayFill(Vert); }; +// NOTE: leftover from static distance factor adjustment. +// TODO: get rid of it, once we have the dynamic adjustment code in place void TSubModel::AdjustDist() { // aktualizacja odległości faz LoD, zależna od // rozdzielczości pionowej oraz multisamplingu @@ -2120,10 +2130,13 @@ void TModel3d::Init() } if (iNumVerts) { +/* // NOTE: we will be applying distance factor dynamically during render, + // so we're leaving the defined ranges intact if (Global::fDistanceFactor != 1.0) // trochę zaoszczędzi czasu na modelach z wieloma submocelami Root->AdjustDist(); // aktualizacja odległości faz LoD, zależnie od // rozdzielczości pionowej oraz multisamplingu +*/ if (Global::bUseVBO) { if (!m_pVNT) // jeśli nie ma jeszcze tablicy (wczytano z pliku diff --git a/World.cpp b/World.cpp index 3cf31fe2..94d7ab85 100644 --- a/World.cpp +++ b/World.cpp @@ -30,8 +30,6 @@ http://mozilla.org/MPL/2.0/. #include "Console.h" #include "color.h" -#define TEXTURE_FILTER_CONTROL_EXT 0x8500 -#define TEXTURE_LOD_BIAS_EXT 0x8501 //--------------------------------------------------------------------------- // GLUTAPI void APIENTRY glutBitmapCharacterDLL(void *font, int character); @@ -283,6 +281,7 @@ bool TWorld::Init( GLFWwindow *w ) { glEnable(GL_CULL_FACE); // Cull back-facing triangles glLineWidth(1.0f); glPointSize(3.0f); + glEnable( GL_POINT_SMOOTH ); #ifdef EU07_USE_OLD_LIGHTING_MODEL // ----------- LIGHTING SETUP ----------- // Light values and coordinates @@ -1110,13 +1109,22 @@ bool TWorld::Update() int updatecount = 1; if( dt > m_primaryupdaterate ) // normalnie 0.01s { +/* + // NOTE: experimentally disabled physics update cap auto const iterations = std::ceil(dt / m_primaryupdaterate); updatecount = std::min( 20, static_cast( iterations ) ); +*/ + updatecount = std::ceil( dt / m_primaryupdaterate ); +/* + // NOTE: changing dt wrecks things further down the code. re-acquire proper value later or cleanup here dt = dt / iterations; // Ra: fizykę lepiej by było przeliczać ze stałym krokiem +*/ } // NOTE: updates are limited to 20, but dt is distributed over potentially many more iterations // this means at count > 20 simulation and render are going to desync. is that right? - Ground.Update(dt, updatecount); // tu zrobić tylko coklatkową aktualizację przesunięć + // NOTE: experimentally changing this to prevent the desync. + // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps + Ground.Update(dt / updatecount, updatecount); // tu zrobić tylko coklatkową aktualizację przesunięć /* if (DebugModeFlag) if (Global::bActive) // nie przyspieszać, gdy jedzie w tle :) @@ -1437,6 +1445,7 @@ bool TWorld::Render() glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix glLoadIdentity(); Camera.SetMatrix(); // ustawienie macierzy kamery względem początku scenerii + Camera.SetFrustum(); // update camera frustum to match current data if( !Global::bWireFrame ) { // bez nieba w trybie rysowania linii diff --git a/frustum.cpp b/frustum.cpp new file mode 100644 index 00000000..f0244f5a --- /dev/null +++ b/frustum.cpp @@ -0,0 +1,178 @@ +/* +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/. +*/ + +#include "stdafx.h" +#include "frustum.h" + +void +cFrustum::calculate() { + + float proj[ 16 ]; + float modl[ 16 ]; + float clip[ 16 ]; + + glGetFloatv( GL_PROJECTION_MATRIX, proj ); + glGetFloatv( GL_MODELVIEW_MATRIX, modl ); + + // multiply the matrices to retrieve clipping planes + clip[ 0 ] = modl[ 0 ] * proj[ 0 ] + modl[ 1 ] * proj[ 4 ] + modl[ 2 ] * proj[ 8 ] + modl[ 3 ] * proj[ 12 ]; + clip[ 1 ] = modl[ 0 ] * proj[ 1 ] + modl[ 1 ] * proj[ 5 ] + modl[ 2 ] * proj[ 9 ] + modl[ 3 ] * proj[ 13 ]; + clip[ 2 ] = modl[ 0 ] * proj[ 2 ] + modl[ 1 ] * proj[ 6 ] + modl[ 2 ] * proj[ 10 ] + modl[ 3 ] * proj[ 14 ]; + clip[ 3 ] = modl[ 0 ] * proj[ 3 ] + modl[ 1 ] * proj[ 7 ] + modl[ 2 ] * proj[ 11 ] + modl[ 3 ] * proj[ 15 ]; + + clip[ 4 ] = modl[ 4 ] * proj[ 0 ] + modl[ 5 ] * proj[ 4 ] + modl[ 6 ] * proj[ 8 ] + modl[ 7 ] * proj[ 12 ]; + clip[ 5 ] = modl[ 4 ] * proj[ 1 ] + modl[ 5 ] * proj[ 5 ] + modl[ 6 ] * proj[ 9 ] + modl[ 7 ] * proj[ 13 ]; + clip[ 6 ] = modl[ 4 ] * proj[ 2 ] + modl[ 5 ] * proj[ 6 ] + modl[ 6 ] * proj[ 10 ] + modl[ 7 ] * proj[ 14 ]; + clip[ 7 ] = modl[ 4 ] * proj[ 3 ] + modl[ 5 ] * proj[ 7 ] + modl[ 6 ] * proj[ 11 ] + modl[ 7 ] * proj[ 15 ]; + + clip[ 8 ] = modl[ 8 ] * proj[ 0 ] + modl[ 9 ] * proj[ 4 ] + modl[ 10 ] * proj[ 8 ] + modl[ 11 ] * proj[ 12 ]; + clip[ 9 ] = modl[ 8 ] * proj[ 1 ] + modl[ 9 ] * proj[ 5 ] + modl[ 10 ] * proj[ 9 ] + modl[ 11 ] * proj[ 13 ]; + clip[ 10 ] = modl[ 8 ] * proj[ 2 ] + modl[ 9 ] * proj[ 6 ] + modl[ 10 ] * proj[ 10 ] + modl[ 11 ] * proj[ 14 ]; + clip[ 11 ] = modl[ 8 ] * proj[ 3 ] + modl[ 9 ] * proj[ 7 ] + modl[ 10 ] * proj[ 11 ] + modl[ 11 ] * proj[ 15 ]; + + clip[ 12 ] = modl[ 12 ] * proj[ 0 ] + modl[ 13 ] * proj[ 4 ] + modl[ 14 ] * proj[ 8 ] + modl[ 15 ] * proj[ 12 ]; + clip[ 13 ] = modl[ 12 ] * proj[ 1 ] + modl[ 13 ] * proj[ 5 ] + modl[ 14 ] * proj[ 9 ] + modl[ 15 ] * proj[ 13 ]; + clip[ 14 ] = modl[ 12 ] * proj[ 2 ] + modl[ 13 ] * proj[ 6 ] + modl[ 14 ] * proj[ 10 ] + modl[ 15 ] * proj[ 14 ]; + clip[ 15 ] = modl[ 12 ] * proj[ 3 ] + modl[ 13 ] * proj[ 7 ] + modl[ 14 ] * proj[ 11 ] + modl[ 15 ] * proj[ 15 ]; + + // get the sides of the frustum. + m_frustum[ side_RIGHT ][ plane_A ] = clip[ 3 ] - clip[ 0 ]; + m_frustum[ side_RIGHT ][ plane_B ] = clip[ 7 ] - clip[ 4 ]; + m_frustum[ side_RIGHT ][ plane_C ] = clip[ 11 ] - clip[ 8 ]; + m_frustum[ side_RIGHT ][ plane_D ] = clip[ 15 ] - clip[ 12 ]; + normalize_plane( side_RIGHT ); + + m_frustum[ side_LEFT ][ plane_A ] = clip[ 3 ] + clip[ 0 ]; + m_frustum[ side_LEFT ][ plane_B ] = clip[ 7 ] + clip[ 4 ]; + m_frustum[ side_LEFT ][ plane_C ] = clip[ 11 ] + clip[ 8 ]; + m_frustum[ side_LEFT ][ plane_D ] = clip[ 15 ] + clip[ 12 ]; + normalize_plane( side_LEFT ); + + m_frustum[ side_BOTTOM ][ plane_A ] = clip[ 3 ] + clip[ 1 ]; + m_frustum[ side_BOTTOM ][ plane_B ] = clip[ 7 ] + clip[ 5 ]; + m_frustum[ side_BOTTOM ][ plane_C ] = clip[ 11 ] + clip[ 9 ]; + m_frustum[ side_BOTTOM ][ plane_D ] = clip[ 15 ] + clip[ 13 ]; + normalize_plane( side_BOTTOM ); + + m_frustum[ side_TOP ][ plane_A ] = clip[ 3 ] - clip[ 1 ]; + m_frustum[ side_TOP ][ plane_B ] = clip[ 7 ] - clip[ 5 ]; + m_frustum[ side_TOP ][ plane_C ] = clip[ 11 ] - clip[ 9 ]; + m_frustum[ side_TOP ][ plane_D ] = clip[ 15 ] - clip[ 13 ]; + normalize_plane( side_TOP ); + + m_frustum[ side_BACK ][ plane_A ] = clip[ 3 ] - clip[ 2 ]; + m_frustum[ side_BACK ][ plane_B ] = clip[ 7 ] - clip[ 6 ]; + m_frustum[ side_BACK ][ plane_C ] = clip[ 11 ] - clip[ 10 ]; + m_frustum[ side_BACK ][ plane_D ] = clip[ 15 ] - clip[ 14 ]; + normalize_plane( side_BACK ); + + m_frustum[ side_FRONT ][ plane_A ] = clip[ 3 ] + clip[ 2 ]; + m_frustum[ side_FRONT ][ plane_B ] = clip[ 7 ] + clip[ 6 ]; + m_frustum[ side_FRONT ][ plane_C ] = clip[ 11 ] + clip[ 10 ]; + m_frustum[ side_FRONT ][ plane_D ] = clip[ 15 ] + clip[ 14 ]; + normalize_plane( side_FRONT ); +} + +bool +cFrustum::point_inside( float const X, float const Y, float const Z ) const { + + // cycle through the sides of the frustum, checking if the point is behind them + for( int idx = 0; idx < 6; ++idx ) { + if( m_frustum[ idx ][ plane_A ] * X + + m_frustum[ idx ][ plane_B ] * Y + + m_frustum[ idx ][ plane_C ] * Z + + m_frustum[ idx ][ plane_D ] <= 0 ) + return false; + } + + // the point is in front of each frustum plane, i.e. inside of the frustum + return true; +} + +float +cFrustum::sphere_inside( float const X, float const Y, float const Z, float const Radius ) const { + + float distance; + // go through all the sides of the frustum. bail out as soon as possible + for( int idx = 0; idx < 6; ++idx ) { + distance = + m_frustum[ idx ][ plane_A ] * X + + m_frustum[ idx ][ plane_B ] * Y + + m_frustum[ idx ][ plane_C ] * Z + + m_frustum[ idx ][ plane_D ]; + if( distance <= -Radius ) + return 0.0f; + } + return distance + Radius; +} + +bool +cFrustum::cube_inside( float const X, float const Y, float const Z, float const Size ) const { + + for( int idx = 0; idx < 6; ++idx ) { + if( m_frustum[ idx ][ plane_A ] * ( X - Size ) + + m_frustum[ idx ][ plane_B ] * ( Y - Size ) + + m_frustum[ idx ][ plane_C ] * ( Z - Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + if( m_frustum[ idx ][ plane_A ] * ( X + Size ) + + m_frustum[ idx ][ plane_B ] * ( Y - Size ) + + m_frustum[ idx ][ plane_C ] * ( Z - Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + if( m_frustum[ idx ][ plane_A ] * ( X - Size ) + + m_frustum[ idx ][ plane_B ] * ( Y + Size ) + + m_frustum[ idx ][ plane_C ] * ( Z - Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + if( m_frustum[ idx ][ plane_A ] * ( X + Size ) + + m_frustum[ idx ][ plane_B ] * ( Y + Size ) + + m_frustum[ idx ][ plane_C ] * ( Z - Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + if( m_frustum[ idx ][ plane_A ] * ( X - Size ) + + m_frustum[ idx ][ plane_B ] * ( Y - Size ) + + m_frustum[ idx ][ plane_C ] * ( Z + Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + if( m_frustum[ idx ][ plane_A ] * ( X + Size ) + + m_frustum[ idx ][ plane_B ] * ( Y - Size ) + + m_frustum[ idx ][ plane_C ] * ( Z + Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + if( m_frustum[ idx ][ plane_A ] * ( X - Size ) + + m_frustum[ idx ][ plane_B ] * ( Y + Size ) + + m_frustum[ idx ][ plane_C ] * ( Z + Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + if( m_frustum[ idx ][ plane_A ] * ( X + Size ) + + m_frustum[ idx ][ plane_B ] * ( Y + Size ) + + m_frustum[ idx ][ plane_C ] * ( Z + Size ) + + m_frustum[ idx ][ plane_D ] > 0 ) + continue; + + return false; + } + + return true; +} + +void cFrustum::normalize_plane( cFrustum::side const Side ) { + + float magnitude = + std::sqrt( + m_frustum[ Side ][ plane_A ] * m_frustum[ Side ][ plane_A ] + + m_frustum[ Side ][ plane_B ] * m_frustum[ Side ][ plane_B ] + + m_frustum[ Side ][ plane_C ] * m_frustum[ Side ][ plane_C ] ); + + m_frustum[ Side ][ plane_A ] /= magnitude; + m_frustum[ Side ][ plane_B ] /= magnitude; + m_frustum[ Side ][ plane_C ] /= magnitude; + m_frustum[ Side ][ plane_D ] /= magnitude; +} diff --git a/frustum.h b/frustum.h new file mode 100644 index 00000000..be8b9265 --- /dev/null +++ b/frustum.h @@ -0,0 +1,67 @@ +/* +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/. +*/ + +#pragma once + +#include "float3d.h" +#include "dumb3d.h" + +// generic frustum class. used to determine if objects are inside current view area + +class cFrustum { + +public: +// constructors: + +// methods: + // update the frustum to match current view orientation + void + calculate(); + // returns true if specified point is inside of the frustum + inline + bool + point_inside( float3 const &Point ) const { return point_inside( Point.x, Point.y, Point.z ); } + inline + bool + point_inside( Math3D::vector3 const &Point ) const { return point_inside( static_cast( Point.x ), static_cast( Point.y ), static_cast( Point.z ) ); } + bool + point_inside( float const X, float const Y, float const Z ) const; + // tests if the sphere is in frustum, returns the distance between origin and sphere centre + inline + float + sphere_inside( float3 const &Center, float const Radius ) const { return sphere_inside( Center.x, Center.y, Center.z, Radius ); } + inline + float + sphere_inside( Math3D::vector3 const &Center, float const Radius ) const { return sphere_inside( static_cast( Center.x ), static_cast( Center.y ), static_cast( Center.z ), Radius ); } + float + sphere_inside( float const X, float const Y, float const Z, float const Radius ) const; + // returns true if specified cube is inside of the frustum. Size = half of the length + inline + bool + cube_inside( float3 const &Center, float const Size ) const { return cube_inside( Center.x, Center.y, Center.z, Size ); } + inline + bool + cube_inside( Math3D::vector3 const &Center, float const Size ) const { return cube_inside( static_cast( Center.x ), static_cast( Center.y ), static_cast( Center.z ), Size ); } + bool + cube_inside( float const X, float const Y, float const Z, float const Size ) const; + +protected: +// types: + // planes of the frustum + enum side { side_RIGHT = 0, side_LEFT = 1, side_BOTTOM = 2, side_TOP = 3, side_BACK = 4, side_FRONT = 5 }; + // parameters of the frustum plane: A, B, C define plane normal, D defines distance from origin + enum plane { plane_A = 0, plane_B = 1, plane_C = 2, plane_D = 3 }; + +// methods: + void + normalize_plane( cFrustum::side const Side ); // normalizes a plane (A side) from the frustum + +// members: + float m_frustum[6][4]; // holds the A B C and D values (normal & distance) for each side of the frustum. +}; diff --git a/maszyna.vcxproj b/maszyna.vcxproj index 19e4a244..ad2f4f28 100644 --- a/maszyna.vcxproj +++ b/maszyna.vcxproj @@ -101,6 +101,7 @@ + @@ -166,6 +167,7 @@ + diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index 39c0f4ba..525c37ab 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -201,6 +201,9 @@ Source Files + + Source Files + @@ -389,6 +392,9 @@ Header Files + + Header Files +