mirror of
https://github.com/HowestDAE/dae16-VerhulstBram.git
synced 2026-02-04 09:19:19 +01:00
Init
This commit is contained in:
222
Engine/BaseGame.cpp
Normal file
222
Engine/BaseGame.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#include "base.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include "BaseGame.h"
|
||||
|
||||
BaseGame::BaseGame(const Window& window)
|
||||
: m_Window{ window }
|
||||
, m_Viewport{ 0,0,window.width,window.height }
|
||||
, m_pWindow{ nullptr }
|
||||
, m_pContext{ nullptr }
|
||||
, m_Initialized{ false }
|
||||
, m_MaxElapsedSeconds{ 0.1f }
|
||||
{
|
||||
InitializeGameEngine();
|
||||
}
|
||||
|
||||
BaseGame::~BaseGame()
|
||||
{
|
||||
CleanupGameEngine();
|
||||
}
|
||||
|
||||
void BaseGame::InitializeGameEngine()
|
||||
{
|
||||
// disable console close window button
|
||||
#ifdef _WIN32
|
||||
HWND hwnd = GetConsoleWindow();
|
||||
HMENU hmenu = GetSystemMenu(hwnd, FALSE);
|
||||
EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED);
|
||||
#endif
|
||||
|
||||
// Initialize SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO /*| SDL_INIT_AUDIO*/) < 0)
|
||||
{
|
||||
std::cerr << "BaseGame::Initialize( ), error when calling SDL_Init: " << SDL_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Use OpenGL 2.1
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
|
||||
// Create window
|
||||
m_pWindow = SDL_CreateWindow(
|
||||
m_Window.title.c_str(),
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
int(m_Window.width),
|
||||
int(m_Window.height),
|
||||
SDL_WINDOW_OPENGL);
|
||||
if (m_pWindow == nullptr)
|
||||
{
|
||||
std::cerr << "BaseGame::Initialize( ), error when calling SDL_CreateWindow: " << SDL_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create OpenGL context
|
||||
m_pContext = SDL_GL_CreateContext(m_pWindow);
|
||||
if (m_pContext == nullptr)
|
||||
{
|
||||
std::cerr << "BaseGame::Initialize( ), error when calling SDL_GL_CreateContext: " << SDL_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the swap interval for the current OpenGL context,
|
||||
// synchronize it with the vertical retrace
|
||||
if (m_Window.isVSyncOn)
|
||||
{
|
||||
if (SDL_GL_SetSwapInterval(1) < 0)
|
||||
{
|
||||
std::cerr << "BaseGame::Initialize( ), error when calling SDL_GL_SetSwapInterval: " << SDL_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
|
||||
// Set the Projection matrix to the identity matrix
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
// Set up a two-dimensional orthographic viewing region.
|
||||
glOrtho(0, m_Window.width, 0, m_Window.height, -1, 1); // y from bottom to top
|
||||
|
||||
// Set the viewport to the client window area
|
||||
// The viewport is the rectangular region of the window where the image is drawn.
|
||||
glViewport(0, 0, int(m_Window.width), int(m_Window.height));
|
||||
|
||||
// Set the Modelview matrix to the identity matrix
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
// Enable color blending and use alpha blending
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Initialize PNG loading
|
||||
/*
|
||||
int imgFlags = IMG_INIT_PNG;
|
||||
if ( !( IMG_Init( imgFlags ) & imgFlags ) )
|
||||
{
|
||||
std::cerr << "BaseGame::Initialize( ), error when calling IMG_Init: " << IMG_GetError( ) << std::endl;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
// Initialize SDL_ttf
|
||||
if (TTF_Init() == -1)
|
||||
{
|
||||
std::cerr << "BaseGame::Initialize( ), error when calling TTF_Init: " << TTF_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//Initialize SDL_mixer
|
||||
|
||||
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
|
||||
{
|
||||
std::cerr << "BaseGame::Initialize( ), error when calling Mix_OpenAudio: " << Mix_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_Initialized = true;
|
||||
}
|
||||
|
||||
void BaseGame::Run()
|
||||
{
|
||||
if (!m_Initialized)
|
||||
{
|
||||
std::cerr << "BaseGame::Run( ), BaseGame not correctly initialized, unable to run the BaseGame\n";
|
||||
std::cin.get();
|
||||
return;
|
||||
}
|
||||
|
||||
// Main loop flag
|
||||
bool quit{ false };
|
||||
|
||||
// Set start time
|
||||
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
|
||||
|
||||
//The event loop
|
||||
SDL_Event e{};
|
||||
while (!quit)
|
||||
{
|
||||
// Poll next event from queue
|
||||
while (SDL_PollEvent(&e) != 0)
|
||||
{
|
||||
// Handle the polled event
|
||||
switch (e.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
quit = true;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
this->ProcessKeyDownEvent(e.key);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
this->ProcessKeyUpEvent(e.key);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
e.motion.y = int(m_Window.height) - e.motion.y;
|
||||
this->ProcessMouseMotionEvent(e.motion);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
e.button.y = int(m_Window.height) - e.button.y;
|
||||
this->ProcessMouseDownEvent(e.button);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
e.button.y = int(m_Window.height) - e.button.y;
|
||||
this->ProcessMouseUpEvent(e.button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!quit)
|
||||
{
|
||||
// Get current time
|
||||
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
|
||||
|
||||
// Calculate elapsed time
|
||||
float elapsedSeconds = std::chrono::duration<float>(t2 - t1).count();
|
||||
|
||||
// Update current time
|
||||
t1 = t2;
|
||||
|
||||
// Prevent jumps in time caused by break points
|
||||
elapsedSeconds = std::min(elapsedSeconds, m_MaxElapsedSeconds);
|
||||
|
||||
// Call the BaseGame object 's Update function, using time in seconds (!)
|
||||
this->Update(elapsedSeconds);
|
||||
|
||||
// Draw in the back buffer
|
||||
this->Draw();
|
||||
|
||||
// Update screen: swap back and front buffer
|
||||
SDL_GL_SwapWindow(m_pWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseGame::CleanupGameEngine()
|
||||
{
|
||||
SDL_GL_DeleteContext(m_pContext);
|
||||
|
||||
SDL_DestroyWindow(m_pWindow);
|
||||
m_pWindow = nullptr;
|
||||
|
||||
//Quit SDL subsystems
|
||||
Mix_Quit();
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
|
||||
// enable console close window button
|
||||
#ifdef _WIN32
|
||||
HWND hwnd = GetConsoleWindow();
|
||||
HMENU hmenu = GetSystemMenu(hwnd, FALSE);
|
||||
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED);
|
||||
#endif
|
||||
|
||||
}
|
||||
72
Engine/BaseGame.h
Normal file
72
Engine/BaseGame.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include "structs.h"
|
||||
#include "SDL.h"
|
||||
// https://BaseGameprogrammingpatterns.com/subclass-sandbox.html
|
||||
|
||||
|
||||
class BaseGame
|
||||
{
|
||||
public:
|
||||
explicit BaseGame( const Window& window );
|
||||
BaseGame( const BaseGame& other ) = delete;
|
||||
BaseGame& operator=( const BaseGame& other ) = delete;
|
||||
BaseGame(BaseGame&& other) = delete;
|
||||
BaseGame& operator=(BaseGame&& other) = delete;
|
||||
virtual ~BaseGame( );
|
||||
|
||||
void Run( );
|
||||
|
||||
virtual void Update(float elapsedSec)
|
||||
{
|
||||
|
||||
}
|
||||
virtual void Draw() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Event handling
|
||||
virtual void ProcessKeyDownEvent(const SDL_KeyboardEvent& e)
|
||||
{
|
||||
|
||||
}
|
||||
virtual void ProcessKeyUpEvent(const SDL_KeyboardEvent& e)
|
||||
{
|
||||
|
||||
}
|
||||
virtual void ProcessMouseMotionEvent(const SDL_MouseMotionEvent& e)
|
||||
{
|
||||
|
||||
}
|
||||
virtual void ProcessMouseDownEvent(const SDL_MouseButtonEvent& e)
|
||||
{
|
||||
|
||||
}
|
||||
virtual void ProcessMouseUpEvent(const SDL_MouseButtonEvent& e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const Rectf& GetViewPort() const
|
||||
{
|
||||
return m_Viewport;
|
||||
}
|
||||
|
||||
private:
|
||||
// DATA MEMBERS
|
||||
// The window properties
|
||||
const Window m_Window;
|
||||
const Rectf m_Viewport;
|
||||
// The window we render to
|
||||
SDL_Window* m_pWindow;
|
||||
// OpenGL context
|
||||
SDL_GLContext m_pContext;
|
||||
// Init info
|
||||
bool m_Initialized;
|
||||
// Prevent timing jumps when debugging
|
||||
const float m_MaxElapsedSeconds;
|
||||
|
||||
// FUNCTIONS
|
||||
void InitializeGameEngine( );
|
||||
void CleanupGameEngine( );
|
||||
};
|
||||
173
Engine/Engine.vcxproj
Normal file
173
Engine/Engine.vcxproj
Normal file
@@ -0,0 +1,173 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BaseGame.cpp" />
|
||||
<ClCompile Include="Matrix2x3.cpp" />
|
||||
<ClCompile Include="SoundEffect.cpp" />
|
||||
<ClCompile Include="SoundStream.cpp" />
|
||||
<ClCompile Include="structs.cpp" />
|
||||
<ClCompile Include="SVGParser.cpp" />
|
||||
<ClCompile Include="Texture.cpp" />
|
||||
<ClCompile Include="utils.cpp" />
|
||||
<ClCompile Include="Vector2f.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BaseGame.h" />
|
||||
<ClInclude Include="Matrix2x3.h" />
|
||||
<ClInclude Include="base.h" />
|
||||
<ClInclude Include="SoundEffect.h" />
|
||||
<ClInclude Include="SoundStream.h" />
|
||||
<ClInclude Include="structs.h" />
|
||||
<ClInclude Include="SVGParser.h" />
|
||||
<ClInclude Include="Texture.h" />
|
||||
<ClInclude Include="Transform.h" />
|
||||
<ClInclude Include="utils.h" />
|
||||
<ClInclude Include="Vector2f.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{5adab721-cb6c-4ef5-89eb-20ec51a13cfc}</ProjectGuid>
|
||||
<RootNamespace>Engine</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>..\Libraries\SDLTtf\SDL2_ttf-2.20.2\include;..\Libraries\SDLImage\SDL2_image-2.6.3\include;..\Libraries\SDLMixer\SDL2_mixer-2.6.3\include;..\Libraries\SDLMain\SDL2-2.26.3\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>..\Libraries\SDLTtf\SDL2_ttf-2.20.2\lib\x64;..\Libraries\SDL2_mixer-2.6.3\lib\x64;..\Libraries\SDLImage\SDL2_image-2.6.3\lib\x64;..\Libraries\SDLMain\SDL2-2.26.3\lib\x64;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>EnableAllWarnings</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<TreatWarningAsError>false</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
81
Engine/Engine.vcxproj.filters
Normal file
81
Engine/Engine.vcxproj.filters
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BaseGame.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Matrix2x3.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SoundEffect.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SoundStream.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="structs.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SVGParser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Texture.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vector2f.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BaseGame.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Matrix2x3.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="base.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SoundEffect.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SoundStream.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="structs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SVGParser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Texture.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vector2f.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Transform.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
208
Engine/Matrix2x3.cpp
Normal file
208
Engine/Matrix2x3.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#include "base.h"
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "Matrix2x3.h"
|
||||
|
||||
|
||||
Matrix2x3::Matrix2x3(Vector2f dirX, Vector2f dirY, Vector2f orig)
|
||||
: dirX{ dirX }, dirY{ dirY }, orig{ orig }
|
||||
{}
|
||||
|
||||
Matrix2x3::Matrix2x3(float e1X, float e1Y, float e2X, float e2Y, float oX, float oY)
|
||||
: Matrix2x3{ Vector2f{e1X, e1Y}, Vector2f{e2X, e2Y}, Vector2f{oX, oY} }
|
||||
{}
|
||||
|
||||
Vector2f Matrix2x3::Transform(const Vector2f& vector) const
|
||||
{
|
||||
return Vector2f{ vector.x * dirX + vector.y * dirY };
|
||||
}
|
||||
|
||||
Point2f Matrix2x3::Transform(const Point2f& point) const
|
||||
{
|
||||
Vector2f v{ Transform( Vector2f{ point } ) + orig };
|
||||
return v.ToPoint2f();
|
||||
}
|
||||
|
||||
std::vector<Point2f> Matrix2x3::Transform(const Rectf & r) const
|
||||
{
|
||||
std::vector<Point2f> vertices{ 4 };
|
||||
vertices[0] = Transform( Point2f{ r.left, r.bottom } );
|
||||
vertices[1] = Transform( Point2f{ r.left, r.bottom + r.height } );
|
||||
vertices[2] = Transform( Point2f{ r.left + r.width, r.bottom + r.height } );
|
||||
vertices[3] = Transform( Point2f{ r.left + r.width, r.bottom } );
|
||||
return vertices;
|
||||
}
|
||||
|
||||
void Matrix2x3::Transform( const Rectf& r, Point2f* transVertices ) const
|
||||
{
|
||||
transVertices[0] = Transform( Point2f{ r.left, r.bottom } );
|
||||
transVertices[1] = Transform( Point2f{ r.left, r.bottom + r.height } );
|
||||
transVertices[2] = Transform( Point2f{ r.left + r.width, r.bottom + r.height } );
|
||||
transVertices[3] = Transform( Point2f{ r.left + r.width, r.bottom } );
|
||||
}
|
||||
|
||||
|
||||
std::vector<Point2f> Matrix2x3::Transform( const std::vector<Point2f>& vertices ) const
|
||||
{
|
||||
size_t nrVectices{ vertices.size( ) };
|
||||
std::vector<Point2f> transformedVertices{ nrVectices };
|
||||
for ( size_t idx{ 0 }; idx < nrVectices; ++idx )
|
||||
{
|
||||
transformedVertices[idx] = Transform( vertices[idx] );
|
||||
}
|
||||
return transformedVertices;
|
||||
}
|
||||
|
||||
void Matrix2x3::Transform( const std::vector<Point2f>& vertices, Point2f* transVertices ) const
|
||||
{
|
||||
Transform( vertices.data( ), transVertices, vertices.size( ) );
|
||||
}
|
||||
|
||||
|
||||
void Matrix2x3::Transform( const Point2f* vertices, Point2f* transVertices, size_t nrVertices ) const
|
||||
{
|
||||
for ( size_t idx{ 0 }; idx < nrVertices; ++idx )
|
||||
{
|
||||
transVertices[idx] = Transform( vertices[idx] );
|
||||
}
|
||||
}
|
||||
|
||||
float Matrix2x3::Determinant() const
|
||||
{
|
||||
return dirX.x * dirY.y - dirX.y * dirY.x;
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::Inverse() const
|
||||
{
|
||||
//Calculate Determinant
|
||||
float det = Determinant();
|
||||
|
||||
//1)calculate matrix of minors
|
||||
//2)Use the alternating law of signs to produce the matrix of cofactors
|
||||
//3)Transpose
|
||||
//4)the inverse matrix is 1/Determinant * the resulting matrix
|
||||
return Matrix2x3{
|
||||
Vector2f(+dirY.y, -dirX.y) / det,
|
||||
Vector2f(-dirY.x, +dirX.x) / det,
|
||||
Vector2f(dirY.x * orig.y - dirY.y * orig.x, -(dirX.x * orig.y - dirX.y * orig.x)) / det
|
||||
};
|
||||
}
|
||||
|
||||
bool Matrix2x3::Equals(const Matrix2x3& other, float epsilon ) const
|
||||
{
|
||||
return dirX.Equals(other.dirX, epsilon) &&
|
||||
dirY.Equals(other.dirY, epsilon) &&
|
||||
orig.Equals(other.orig, epsilon);
|
||||
}
|
||||
|
||||
std::string Matrix2x3::ToString() const
|
||||
{
|
||||
return std::string( "Matrix2x3( x( ") +
|
||||
std::to_string(dirX.x) + ", " + std::to_string( dirX.y )
|
||||
+ " ), y( " + std::to_string( dirY.x ) + ", " + std::to_string( dirY.y )
|
||||
+ " ), orig( " + std::to_string( orig.x ) + ", " + std::to_string( orig.y ) + " ) )";
|
||||
}
|
||||
|
||||
void Matrix2x3::SetAsIdentity()
|
||||
{
|
||||
dirX = Vector2f{1, 0};
|
||||
dirY = Vector2f{0, 1};
|
||||
orig = Vector2f{0, 0};
|
||||
}
|
||||
|
||||
void Matrix2x3::SetAsRotate(float degrees)
|
||||
{
|
||||
float radians = degrees * 3.1415926535f / 180;
|
||||
dirX = Vector2f{ cos( radians ), sin( radians ) };
|
||||
dirY = Vector2f{ -sin( radians ), cos( radians ) };
|
||||
orig = Vector2f{ 0, 0 };
|
||||
}
|
||||
void Matrix2x3::SetAsTranslate(float tx, float ty)
|
||||
{
|
||||
dirX = Vector2f{ 1, 0 };
|
||||
dirY = Vector2f{ 0, 1 };
|
||||
orig = Vector2f{ tx, ty };
|
||||
}
|
||||
|
||||
void Matrix2x3::SetAsTranslate(Vector2f pt)
|
||||
{
|
||||
dirX = Vector2f{ 1, 0 };
|
||||
dirY = Vector2f{ 0, 1 };
|
||||
orig = Vector2f{ pt.x, pt.y };
|
||||
}
|
||||
|
||||
void Matrix2x3::SetAsScale(float scaleX, float scaleY)
|
||||
{
|
||||
dirX = Vector2f{ scaleX, 0 };
|
||||
dirY = Vector2f{ 0, scaleY };
|
||||
orig = Vector2f{ 0, 0 };
|
||||
}
|
||||
|
||||
void Matrix2x3::SetAsScale(float scale)
|
||||
{
|
||||
SetAsScale(scale, scale);
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::CreateRotationMatrix(float degrees)
|
||||
{
|
||||
float radians = degrees * 3.1415926535f / 180;
|
||||
return Matrix2x3( Vector2f{ cos( radians ), sin( radians ) }, Vector2f{ -sin(radians), cos( radians ) }, Vector2f{} );
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::CreateIdentityMatrix()
|
||||
{
|
||||
return Matrix2x3( Vector2f{ 1, 0 }, Vector2f{ 0, 1 }, Vector2f{} );
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::CreateScalingMatrix(float scale)
|
||||
{
|
||||
return CreateScalingMatrix(scale, scale);
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::CreateScalingMatrix(Vector2f scaleVector)
|
||||
{
|
||||
return CreateScalingMatrix(scaleVector.x, scaleVector.y);
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::CreateScalingMatrix(float scaleX, float scaleY)
|
||||
{
|
||||
return Matrix2x3( Vector2f{ scaleX, 0 }, Vector2f{ 0, scaleY }, Vector2f{} );
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::CreateTranslationMatrix(Vector2f origin)
|
||||
{
|
||||
return Matrix2x3( Vector2f{ 1, 0 }, Vector2f{ 0, 1 }, origin );
|
||||
}
|
||||
|
||||
Matrix2x3 Matrix2x3::CreateTranslationMatrix(float tx, float ty)
|
||||
{
|
||||
return CreateTranslationMatrix( Vector2f{ tx, ty } );
|
||||
}
|
||||
|
||||
// Operator overloading functionality
|
||||
bool operator==(const Matrix2x3& lhs, const Matrix2x3& rhs)
|
||||
{
|
||||
return lhs.Equals(rhs);
|
||||
}
|
||||
|
||||
bool operator!=(const Matrix2x3& lhs, const Matrix2x3& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
Matrix2x3 operator*(const Matrix2x3& lhs, const Matrix2x3& rhs)
|
||||
{
|
||||
return Matrix2x3{
|
||||
Vector2f{ rhs.dirX.x * lhs.dirX.x + rhs.dirX.y * lhs.dirY.x, rhs.dirX.x * lhs.dirX.y + rhs.dirX.y * lhs.dirY.y },
|
||||
Vector2f{ rhs.dirY.x * lhs.dirX.x + rhs.dirY.y * lhs.dirY.x, rhs.dirY.x * lhs.dirX.y + rhs.dirY.y * lhs.dirY.y },
|
||||
Vector2f{ rhs.orig.x * lhs.dirX.x + rhs.orig.y * lhs.dirY.x + lhs.orig.x, rhs.orig.x * lhs.dirX.y + rhs.orig.y * lhs.dirY.y + lhs.orig.y }
|
||||
};
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Matrix2x3& matrix )
|
||||
{
|
||||
os << matrix.ToString( );
|
||||
return os;
|
||||
}
|
||||
|
||||
125
Engine/Matrix2x3.h
Normal file
125
Engine/Matrix2x3.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "Vector2f.h"
|
||||
|
||||
struct Matrix2x3 final
|
||||
{
|
||||
// -------------------------
|
||||
// Constructors
|
||||
// -------------------------
|
||||
// Default constructor results in a unity matrix
|
||||
explicit Matrix2x3( Vector2f dirX = Vector2f{ 1, 0 }, Vector2f dirY = Vector2f{ 0, 1 }, Vector2f origTrans = Vector2f{ 0, 0 } );
|
||||
// Constructor, using floats, all required
|
||||
explicit Matrix2x3( float e1X, float e1Y, float e2X, float e2Y, float oX, float oY );
|
||||
|
||||
// -------------------------
|
||||
// General Methods
|
||||
// -------------------------
|
||||
// Transform a vector by this matrix, no translation
|
||||
// Vector2f vTransformed = mat.Transform(v);
|
||||
Vector2f Transform( const Vector2f& v ) const;
|
||||
|
||||
// Transform a point by this matrix, including translation
|
||||
// Point2f pTransformed = mat.Transform(p);
|
||||
Point2f Transform( const Point2f& p ) const;
|
||||
|
||||
// Transform a Rectf by this matrix, including translation
|
||||
// std::vector<Point2f> transformedVertices = mat.Transform(r);
|
||||
std::vector<Point2f> Transform( const Rectf& r ) const;
|
||||
|
||||
|
||||
// Transform a Rectf by this matrix, including translation
|
||||
// Pass pointer to resulting array (size should be at least 4)
|
||||
// Point2f transformedVertices[4];
|
||||
// Transform(r, transformedVertices);
|
||||
void Transform( const Rectf& r, Point2f* transVertices ) const;
|
||||
|
||||
// Transform a Polygon by this matrix, including translation
|
||||
// std::vector<Point2f> transformedVertices = mat.Transform(vertices);
|
||||
std::vector<Point2f> Transform( const std::vector<Point2f>& vertices ) const;
|
||||
|
||||
// Transform a Polygon by this matrix, including translation
|
||||
// Pass pointer to resulting array (size should be at least size of polygon)
|
||||
// Point2f transformedVertices[vertices.size()];
|
||||
// Transform(vertices, transformedVertices);
|
||||
void Transform( const std::vector<Point2f>& vertices, Point2f* transVertices ) const;
|
||||
|
||||
// Transform a Polygon by this matrix, including translation
|
||||
// Point2f transformedVertices[nrVertices];
|
||||
// Transform(vertices, nrVertices, transformedVertices);
|
||||
void Transform( const Point2f* vertices, Point2f* transVertices, size_t nrVertices ) const;
|
||||
|
||||
// Calculate the determinant
|
||||
float Determinant( ) const;
|
||||
|
||||
// Calculate the inverse matrix
|
||||
Matrix2x3 Inverse( ) const;
|
||||
|
||||
// Are two matrices equal within a threshold?
|
||||
// mat1.Equals(mat2)
|
||||
bool Equals( const Matrix2x3& other, float epsilon = 0.001f ) const;
|
||||
|
||||
// Creates a string containing a text representation of the values of the matrix
|
||||
std::string ToString( ) const;
|
||||
|
||||
// Converts this matrix into a Identity matrix
|
||||
void SetAsIdentity( );
|
||||
// Converts this matrix into a Rotate matrix
|
||||
void SetAsRotate( float degrees );
|
||||
// Converts this matrix into a Translation matrix
|
||||
void SetAsTranslate( float tx, float ty );
|
||||
// Converts this matrix into a Translation matrix
|
||||
void SetAsTranslate( Vector2f pt );
|
||||
// Converts this matrix into a Scale matrix
|
||||
void SetAsScale( float sx, float sy );
|
||||
// Converts this matrix into a Scale matrix
|
||||
void SetAsScale( float s );
|
||||
|
||||
// -------------------------------------------
|
||||
// Static Matrix2x3 object creation methods
|
||||
// -------------------------------------------
|
||||
// Instantiate a rotation matrix:
|
||||
// Matrix matRot = Matrix::Rotation(45.0f);
|
||||
static Matrix2x3 CreateRotationMatrix( float degrees );
|
||||
// Instantiate an identity matrix:
|
||||
// Matrix2x3 matId = Matrix2x3::Identity();
|
||||
static Matrix2x3 CreateIdentityMatrix( );
|
||||
// Instantiate a scale matrix:
|
||||
// Matrix matScale = Matrix::Scaling(2.0f);
|
||||
static Matrix2x3 CreateScalingMatrix( float scale );
|
||||
// Instantiate a scale matrix:
|
||||
// Matrix matScale = Matrix::Scaling(2.0f,-3.0f);
|
||||
static Matrix2x3 CreateScalingMatrix( float scaleX, float scaleY );
|
||||
// Instantiate a scale matrix:
|
||||
// Matrix matScale = Matrix::Scaling( Vector2f(2.0f,-3.0f) );
|
||||
static Matrix2x3 CreateScalingMatrix( Vector2f scaleVector );
|
||||
// Instantiate a translation matrix:
|
||||
// Matrix matTrans = Matrix::Translation( Vector2f(2.0f,3.0f) );
|
||||
static Matrix2x3 CreateTranslationMatrix( Vector2f origin );
|
||||
// Instantiate a translation matrix:
|
||||
// Matrix matTrans = Matrix::Translation(2.0f, 3.0f);
|
||||
static Matrix2x3 CreateTranslationMatrix( float tx, float ty );
|
||||
|
||||
// -------------------------
|
||||
// Datamembers
|
||||
// -------------------------
|
||||
Vector2f dirX; // The first matrix vector (the "x-axis"), 1st column
|
||||
Vector2f dirY; // The second matrix vector (the "y-axis"), second column
|
||||
Vector2f orig; // The origin of the coordinate matrix (the "translation"), third column
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// Operators
|
||||
// -------------------------
|
||||
// Are two matrices exactly equal?
|
||||
// mat1 == mat2
|
||||
bool operator==( const Matrix2x3& lhs, const Matrix2x3& rhs );
|
||||
// Are two matrices exactly unequal?
|
||||
// mat1 != mat2
|
||||
bool operator!=( const Matrix2x3& lhs, const Matrix2x3& rhs );
|
||||
// Multiply matrices
|
||||
// Matrix2x3 matProduct {mat1 * mat2};
|
||||
Matrix2x3 operator*( const Matrix2x3& lhs, const Matrix2x3& rhs );
|
||||
// Send matrix to output stream
|
||||
// std::cout << mat;
|
||||
std::ostream& operator<<( std::ostream& os, const Matrix2x3& matrix );
|
||||
479
Engine/SVGParser.cpp
Normal file
479
Engine/SVGParser.cpp
Normal file
@@ -0,0 +1,479 @@
|
||||
#include "base.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "SVGParser.h"
|
||||
|
||||
bool SVGParser::GetVerticesFromSvgFile( const std::string& filePath, std::vector<std::vector<Point2f>> &vertices )
|
||||
{
|
||||
// Open the file
|
||||
std::ifstream svgStream( filePath.c_str( ) );
|
||||
|
||||
if ( !svgStream )
|
||||
{
|
||||
std::cerr << "SVGParser::GetVerticesFromSvgFile(..), failed to load vertices from file " << filePath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the file
|
||||
std::string svgLine;
|
||||
std::string svgString;
|
||||
while ( !svgStream.eof() )
|
||||
{
|
||||
getline( svgStream, svgLine );
|
||||
svgString += svgLine;
|
||||
}
|
||||
|
||||
// close the file
|
||||
svgStream.close( );
|
||||
|
||||
// Cleanup
|
||||
RemoveSpaces( svgString );
|
||||
|
||||
if ( ! GetVerticesFromSvgString(svgString, vertices))
|
||||
{
|
||||
std::cerr << "SVGParser::GetVerticesFromSvgFile(..), malformed or unsupported information in file " << filePath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the viewbox rect to flip the y coordinates. SVG has org topleft, the framework bottom left.
|
||||
std::string viewBoxValue;
|
||||
if ( ! GetAttributeValue(svgString, "viewBox", viewBoxValue))
|
||||
{
|
||||
std::cerr << "SVGParser::GetVerticesFromSvgFile(..), no viewbox information found in " << filePath << std::endl;
|
||||
vertices.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
Rectf viewBox{};
|
||||
std::stringstream sstream{ viewBoxValue };
|
||||
sstream >> viewBox.left >> viewBox.bottom >> viewBox.width >> viewBox.height;
|
||||
|
||||
//std::vector<std::vector<Point2f>> vertices{ vertices };
|
||||
for (size_t i{}; i < vertices.size(); ++i)
|
||||
{
|
||||
// flip the y coordinate
|
||||
for (Point2f& p : vertices[i])
|
||||
{
|
||||
p.y = viewBox.height - p.y;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SVGParser::RemoveSpaces( std::string& svgString )
|
||||
{
|
||||
// Remove spaces before and = chars
|
||||
size_t foundPos{};
|
||||
while ( ( foundPos = svgString.find( " =" ) ) != std::string::npos )
|
||||
{
|
||||
svgString.replace( foundPos, 2, "=" );
|
||||
}
|
||||
// Remove spaces after and = chars
|
||||
while ( ( foundPos = svgString.find( "= " ) ) != std::string::npos )
|
||||
{
|
||||
svgString.replace( foundPos, 2, "=" );
|
||||
}
|
||||
//std::cout << svgString.size( ) << "\n";
|
||||
|
||||
// Remove spaces before and > chars
|
||||
while ( ( foundPos = svgString.find( " >" ) ) != std::string::npos )
|
||||
{
|
||||
svgString.replace( foundPos, 2, ">" );
|
||||
}
|
||||
// Remove spaces after and < chars
|
||||
while ( ( foundPos = svgString.find( "< " ) ) != std::string::npos )
|
||||
{
|
||||
svgString.replace( foundPos, 2, "<" );
|
||||
}
|
||||
//std::cout << svgString << "\n";
|
||||
|
||||
}
|
||||
|
||||
bool SVGParser::GetVerticesFromSvgString(std::string& svgString, std::vector<std::vector<Point2f>> &vertices)
|
||||
{
|
||||
size_t startPosContent{};
|
||||
size_t endPosContent{};
|
||||
|
||||
|
||||
std::string pathElementContent;
|
||||
|
||||
// Get path element until none has been found anymore
|
||||
while (GetElementContent(svgString, "path", pathElementContent, startPosContent, endPosContent))
|
||||
{
|
||||
// Vector of Point2f to fill with a path's vertices
|
||||
std::vector<Point2f> verticesVector;
|
||||
|
||||
// Get d attribute value
|
||||
std::string pathDataValue{};
|
||||
if (!GetAttributeValue(pathElementContent, " d", pathDataValue))
|
||||
{
|
||||
std::cerr << "SVGParser::GetVerticesFromSvgString(..), path element doesn't contain a d-attribute.\n ";
|
||||
vertices.clear();
|
||||
return false;
|
||||
}
|
||||
// Process the path data
|
||||
if (!GetVerticesFromPathData(pathDataValue, verticesVector))
|
||||
{
|
||||
std::cerr << "SVGParser::GetVerticesFromSvgString(..), error while extracting vertices from the path. \n";
|
||||
vertices.clear();
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (verticesVector.size() == 0)
|
||||
{
|
||||
std::cerr << "Empty verticesVector in GetVerticesFromSvgString(..), no vertices found in the path element" << std::endl;
|
||||
vertices.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// DEBUG: Read vertices of current vector
|
||||
//for (Point2f& p : verticesVector)
|
||||
//{
|
||||
// std::cout << p.x << " " << p.y << std::endl;
|
||||
//}
|
||||
|
||||
// Add the vector to the vector array
|
||||
vertices.push_back(verticesVector);
|
||||
}
|
||||
|
||||
if (vertices.size() == 0)
|
||||
{
|
||||
std::cerr << "Empty vertices in GetVerticesFromSvgString(..), no path element(s) found" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVGParser::GetVerticesFromPathData( const std::string& pathData, std::vector<Point2f> &vertices )
|
||||
{
|
||||
std::string pathCmdChars( ( "mMZzLlHhVvCcSsQqTtAa" ) );
|
||||
|
||||
// Use streamstream for parsing
|
||||
std::stringstream ss( pathData );
|
||||
|
||||
char cmd{ 0 };
|
||||
Point2f cursor{};
|
||||
Point2f startPoint{};//At the end of the z command, the new current point is set to the initial point of the current subpath.
|
||||
|
||||
bool isOpen = true;
|
||||
|
||||
// http://www.w3.org/TR/SVG/paths.html#Introduction
|
||||
Point2f vertex{};
|
||||
char pathCommand{};
|
||||
ss >> pathCommand;
|
||||
while ( !ss.eof( ) )
|
||||
{
|
||||
//if ( strchr( pathCmdChars.c_str( ), pathCommand ) != 0 )
|
||||
// if the command is a valid command letter, proceed
|
||||
if(pathCmdChars.find(pathCommand) != std::string::npos)
|
||||
{
|
||||
cmd = pathCommand;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not a command, then put it back
|
||||
// Attempts to decrease the current location in the stream by one character,
|
||||
// making the last character extracted from the stream once again available to be extracted by input operation
|
||||
ss.putback( pathCommand );
|
||||
}
|
||||
|
||||
switch ( cmd )
|
||||
{
|
||||
case ( 'Z' ):
|
||||
case ( 'z' ):
|
||||
isOpen = true;
|
||||
break;
|
||||
|
||||
case ( 'M' ):
|
||||
case ( 'm' ):
|
||||
if ( isOpen )
|
||||
{
|
||||
cursor = FirstSvgPoint( ss, cursor, cmd, isOpen, true );
|
||||
startPoint = cursor;
|
||||
vertices.push_back( cursor );
|
||||
isOpen = false;
|
||||
break;
|
||||
}
|
||||
// Fallthrough when isOpen
|
||||
case ( 'L' )://lineto
|
||||
case ( 'l' ):
|
||||
vertex = NextSvgPoint( ss, cursor, cmd, isOpen, true );
|
||||
vertices.push_back( vertex );
|
||||
break;
|
||||
|
||||
case ( 'h' ): // horizontal lineto
|
||||
case ( 'H' ):
|
||||
vertex = NextSvgCoordX( ss, cursor, cmd, isOpen );
|
||||
vertices.push_back( vertex );
|
||||
break;
|
||||
|
||||
case ( 'v' ): // vertical lineto
|
||||
case ( 'V' ):
|
||||
vertex = NextSvgCoordY( ss, cursor, cmd, isOpen );
|
||||
vertices.push_back( vertex );
|
||||
break;
|
||||
|
||||
case ( 'C' ):
|
||||
case ( 'c' ):
|
||||
std::cerr << "SVGParser::GetVerticesFromPathData, beziers are not supported.\nHave another look at the guide, or select all nodes in inkscape and press shift + L\n";
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cerr << "SVGParser::GetVerticesFromPathData, " << cmd << " is not a supported SVG command";
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
// Next command
|
||||
ss >> pathCommand;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SVGParser::GetElementContent( const std::string& svgText, const std::string& elementName, std::string& elementContent, size_t& startContentPos, size_t& endContentPos )
|
||||
{
|
||||
// 2 possible formats
|
||||
// <ElementName> content <ElementName/>
|
||||
|
||||
// Temporary start and end positions for checking
|
||||
size_t tempStartPos{ startContentPos };
|
||||
size_t tempEndPos{ endContentPos };
|
||||
|
||||
std::string startElement = "<" + elementName + ">";
|
||||
std::string endElement = "<" + elementName + "/>";
|
||||
if ( (tempStartPos = svgText.find( startElement )) != std::string::npos )
|
||||
{
|
||||
tempStartPos += startElement.length( );
|
||||
if ( (tempEndPos = svgText.find( endElement ) ) != std::string::npos )
|
||||
{
|
||||
elementContent = svgText.substr(tempStartPos, tempEndPos - tempStartPos);
|
||||
startContentPos = tempStartPos;
|
||||
endContentPos = tempEndPos;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// or
|
||||
// <ElementName content />
|
||||
|
||||
tempStartPos = startContentPos;
|
||||
tempEndPos = endContentPos;
|
||||
|
||||
startElement = "<" + elementName;
|
||||
endElement = "/>";
|
||||
if ( (tempStartPos = svgText.find( startElement, tempStartPos) ) != std::string::npos )
|
||||
{
|
||||
tempStartPos += startElement.length( );
|
||||
if ( (tempEndPos = svgText.find( endElement ) ) != std::string::npos )
|
||||
{
|
||||
elementContent = svgText.substr(tempStartPos, tempEndPos - tempStartPos);
|
||||
startContentPos = tempStartPos;
|
||||
endContentPos = tempEndPos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool SVGParser::GetAttributeValue( const std::string& svgText, const std::string& attributeName, std::string& attributeValue )
|
||||
{
|
||||
std::string searchAttributeName{ attributeName + "="};
|
||||
|
||||
size_t attributePos = svgText.find( searchAttributeName );
|
||||
if( attributePos == std::string::npos )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t openingDoubleQuotePos{ svgText.find( "\"", attributePos ) };
|
||||
if ( openingDoubleQuotePos == std::string::npos )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t closingDoubleQuotePos{ svgText.find( "\"", openingDoubleQuotePos + 1) };
|
||||
if ( closingDoubleQuotePos == std::string::npos )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
attributeValue = svgText.substr( openingDoubleQuotePos + 1, closingDoubleQuotePos - openingDoubleQuotePos - 1);
|
||||
//std::cout << attributeName << ":" << attributeValue << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Skips any optional commas in the stream
|
||||
// SVG has a really funky format,
|
||||
// not sure this code works for all cases.
|
||||
// TODO: Test cases!
|
||||
void SVGParser::SkipSvgComma( std::stringstream& svgStream, bool isRequired )
|
||||
{
|
||||
while ( true )
|
||||
{
|
||||
char c = char(svgStream.get( ));
|
||||
|
||||
if ( svgStream.eof( ) )
|
||||
{
|
||||
if ( isRequired )
|
||||
{
|
||||
std::cerr << "SVGParser::SkipSvgComma, expected comma or whitespace\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( c == ( ',' ) )
|
||||
return;
|
||||
|
||||
if ( !isspace( c ) )
|
||||
{
|
||||
svgStream.unget( );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float SVGParser::ReadSvgValue( std::stringstream& svgStream, float defaultValue )
|
||||
{
|
||||
float s{};
|
||||
svgStream >> s;
|
||||
|
||||
if ( svgStream.eof( ) )
|
||||
{
|
||||
s = defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
SkipSvgComma( svgStream, false );
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
float SVGParser::ReadSvgValue( std::stringstream& svgStream, bool separatorRequired )
|
||||
{
|
||||
float s{};
|
||||
svgStream >> s;
|
||||
SkipSvgComma( svgStream, separatorRequired );
|
||||
return s;
|
||||
}
|
||||
|
||||
// Reads a single point
|
||||
Point2f SVGParser::ReadSvgPoint( std::stringstream& svgStream )
|
||||
{
|
||||
//std::cout << "ReadSvgPoint: " << svgStream.str() << "\n";
|
||||
Point2f p{};
|
||||
p.x = ReadSvgValue( svgStream, true );
|
||||
p.y = ReadSvgValue( svgStream, false );
|
||||
return p;
|
||||
}
|
||||
|
||||
Point2f SVGParser::FirstSvgPoint( std::stringstream& svgStream, Point2f& cursor, char cmd, bool isOpen, bool advance )
|
||||
{
|
||||
if ( !isOpen )
|
||||
{
|
||||
std::cerr << "SVGParser::FirstSvgPoint, expected 'Z' or 'z' command";
|
||||
}
|
||||
|
||||
Point2f p = ReadSvgPoint( svgStream );
|
||||
|
||||
if ( islower( cmd ) )
|
||||
{
|
||||
// Relative point
|
||||
p.x = cursor.x + p.x;
|
||||
p.y = cursor.y + p.y;
|
||||
}
|
||||
|
||||
if ( advance )
|
||||
{
|
||||
cursor = p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
// Read the next point,
|
||||
// taking into account relative and absolute positioning.
|
||||
// Advances the cursor if requested.
|
||||
// Throws an exception if the figure is not open
|
||||
Point2f SVGParser::NextSvgPoint( std::stringstream& svgStream, Point2f& cursor, char cmd, bool isOpen, bool advance )
|
||||
{
|
||||
if ( isOpen )
|
||||
{
|
||||
std::cerr << "SVGParser::NextSvgPoint, expected 'M' or 'm' command\n";
|
||||
}
|
||||
|
||||
Point2f p = ReadSvgPoint( svgStream );
|
||||
|
||||
if ( islower( cmd ) )
|
||||
{
|
||||
// Relative point
|
||||
p.x = cursor.x + p.x;
|
||||
p.y = cursor.y + p.y;
|
||||
}
|
||||
|
||||
if ( advance )
|
||||
{
|
||||
cursor = p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Reads next point, given only the new x coordinate
|
||||
Point2f SVGParser::NextSvgCoordX( std::stringstream& svgStream, Point2f& cursor, char cmd, bool isOpen )
|
||||
{
|
||||
if ( isOpen )
|
||||
{
|
||||
std::cerr << "SVGParser::NextSvgCoordX, expected 'M' or 'm' command\n";
|
||||
}
|
||||
|
||||
float c;
|
||||
svgStream >> c;
|
||||
|
||||
if ( islower( cmd ) )
|
||||
{
|
||||
// Relative point
|
||||
cursor.x += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.x = c;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
// Reads next point, given only the new y coordinate
|
||||
Point2f SVGParser::NextSvgCoordY( std::stringstream& svgStream, Point2f& cursor, char cmd, bool isOpen )
|
||||
{
|
||||
if ( isOpen )
|
||||
{
|
||||
std::cerr << "SVGParser::NextSvgCoordY, expected 'M' or 'm' command\n";
|
||||
}
|
||||
|
||||
float c{};
|
||||
svgStream >> c;
|
||||
|
||||
if ( islower( cmd ) )
|
||||
{
|
||||
// Relative point
|
||||
cursor.y += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.y = c;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
49
Engine/SVGParser.h
Normal file
49
Engine/SVGParser.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include "structs.h"
|
||||
class SVGParser final
|
||||
{
|
||||
public:
|
||||
// The only function to be called
|
||||
static bool GetVerticesFromSvgFile(const std::string& filePath, std::vector<std::vector<Point2f>> &vertices);
|
||||
|
||||
private:
|
||||
//static bool LoadGeometryFromSvgStream(unsigned char* pBlob, int blobSize, std::vector<Point2f> &vertices);
|
||||
static void RemoveSpaces( std::string& svgString );
|
||||
static bool GetVerticesFromSvgString(std::string& svgText, std::vector<std::vector<Point2f>> &vertices);
|
||||
static bool GetVerticesFromPathData( const std::string& pathData, std::vector<Point2f> &vertices );
|
||||
static bool GetElementContent( const std::string& svgText, const std::string& elementName, std::string& elementValue, size_t& startContentPos, size_t& endContentPos);
|
||||
static bool GetAttributeValue( const std::string& svgText, const std::string& attributeName, std::string& attributeValue );
|
||||
|
||||
|
||||
// Skips any optional commas in the stream
|
||||
// SVG has a really funky format,
|
||||
// not sure this code works for all cases.
|
||||
// TODO: Test cases!
|
||||
static void SkipSvgComma(std::stringstream& stream, bool isRequired);
|
||||
static float ReadSvgValue(std::stringstream& stream, float defaultValue);
|
||||
static float ReadSvgValue(std::stringstream& stream, bool separatorRequired);
|
||||
|
||||
// Reads a single point
|
||||
static Point2f ReadSvgPoint( std::stringstream& stream );
|
||||
|
||||
// Read the first point,
|
||||
// taking into account relative and absolute positioning.
|
||||
// Stores this point, needed when path is closed
|
||||
// Advances the cursor if requested.
|
||||
static Point2f FirstSvgPoint(std::stringstream& stream, Point2f& cursor, char cmd, bool isOpen, bool advance);
|
||||
|
||||
// Read the next point,
|
||||
// taking into account relative and absolute positioning.
|
||||
// Advances the cursor if requested.
|
||||
// Throws an exception if the figure is not open
|
||||
static Point2f NextSvgPoint(std::stringstream& stream, Point2f& cursor, char cmd, bool isOpen, bool advance);
|
||||
|
||||
// Reads next point, given only the new x coordinate
|
||||
static Point2f NextSvgCoordX(std::stringstream& stream, Point2f& cursor, char cmd, bool isOpen);
|
||||
|
||||
// Reads next point, given only the new y coordinate
|
||||
static Point2f NextSvgCoordY(std::stringstream& ssRef, Point2f& cursor, char cmd, bool isOpen);
|
||||
};
|
||||
77
Engine/SoundEffect.cpp
Normal file
77
Engine/SoundEffect.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "base.h"
|
||||
#include <iostream>
|
||||
#include "SoundEffect.h"
|
||||
|
||||
SoundEffect::SoundEffect( const std::string& path )
|
||||
:m_pMixChunk{ Mix_LoadWAV( path.c_str( ) ) }
|
||||
{
|
||||
if ( m_pMixChunk == nullptr )
|
||||
{
|
||||
const std::string errorMsg = "SoundEffect: Failed to load " + path + ",\nSDL_mixer Error: " + Mix_GetError( );
|
||||
std::cerr << errorMsg;
|
||||
}
|
||||
}
|
||||
SoundEffect::~SoundEffect( )
|
||||
{
|
||||
Mix_FreeChunk( m_pMixChunk );
|
||||
m_pMixChunk = nullptr;
|
||||
}
|
||||
|
||||
bool SoundEffect::IsLoaded( ) const
|
||||
{
|
||||
return m_pMixChunk != nullptr;
|
||||
}
|
||||
|
||||
bool SoundEffect::Play( const int loops ) const
|
||||
{
|
||||
// Don't save the channel as a data member,
|
||||
// because when it stops playing the channel becomes free
|
||||
// and available for usage by other effects
|
||||
if ( m_pMixChunk != nullptr )
|
||||
{
|
||||
const int channel{ Mix_PlayChannel( -1, m_pMixChunk, loops ) };
|
||||
return channel == -1 ? false : true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundEffect::SetVolume( const int value )
|
||||
{
|
||||
if ( m_pMixChunk != nullptr )
|
||||
{
|
||||
Mix_VolumeChunk( m_pMixChunk, value );
|
||||
}
|
||||
}
|
||||
|
||||
int SoundEffect::GetVolume( ) const
|
||||
{
|
||||
if ( m_pMixChunk != nullptr )
|
||||
{
|
||||
return Mix_VolumeChunk( m_pMixChunk, -1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundEffect::StopAll( )
|
||||
{
|
||||
Mix_HaltChannel(-1 );
|
||||
}
|
||||
|
||||
void SoundEffect::PauseAll( )
|
||||
{
|
||||
Mix_Pause( -1 );
|
||||
}
|
||||
void SoundEffect::ResumeAll( )
|
||||
{
|
||||
Mix_Resume( -1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
24
Engine/SoundEffect.h
Normal file
24
Engine/SoundEffect.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
class Mix_Chunk;
|
||||
class SoundEffect final
|
||||
{
|
||||
public:
|
||||
explicit SoundEffect( const std::string& path );
|
||||
~SoundEffect( );
|
||||
SoundEffect(const SoundEffect& other) = delete;
|
||||
SoundEffect& operator=(const SoundEffect& rhs) = delete;
|
||||
SoundEffect( SoundEffect&& other) = delete;
|
||||
SoundEffect& operator=( SoundEffect&& rhs) = delete;
|
||||
|
||||
bool IsLoaded( ) const;
|
||||
bool Play( const int loops ) const;
|
||||
void SetVolume( const int value );
|
||||
int GetVolume( ) const;
|
||||
static void StopAll( );
|
||||
static void PauseAll( );
|
||||
static void ResumeAll( );
|
||||
|
||||
private:
|
||||
Mix_Chunk* m_pMixChunk;
|
||||
};
|
||||
67
Engine/SoundStream.cpp
Normal file
67
Engine/SoundStream.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "base.h"
|
||||
#include <iostream>
|
||||
#include "SoundStream.h"
|
||||
|
||||
SoundStream::SoundStream( const std::string& path )
|
||||
:m_pMixMusic{ Mix_LoadMUS( path.c_str( )) }
|
||||
{
|
||||
if ( m_pMixMusic == nullptr )
|
||||
{
|
||||
std::string errorMsg = "SoundStream: Failed to load " + path + ",\nSDL_mixer Error: " + Mix_GetError( );
|
||||
std::cerr << errorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
SoundStream::~SoundStream( )
|
||||
{
|
||||
Mix_FreeMusic( m_pMixMusic );
|
||||
m_pMixMusic = nullptr;
|
||||
}
|
||||
|
||||
bool SoundStream::IsLoaded( ) const
|
||||
{
|
||||
return m_pMixMusic != nullptr;
|
||||
}
|
||||
|
||||
bool SoundStream::Play(bool repeat ) const
|
||||
{
|
||||
if ( m_pMixMusic != nullptr )
|
||||
{
|
||||
int result{ Mix_PlayMusic( m_pMixMusic, repeat ? -1 : 1 ) };
|
||||
return result == 0 ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundStream::Stop( )
|
||||
{
|
||||
Mix_HaltMusic( );
|
||||
}
|
||||
void SoundStream::Pause( )
|
||||
{
|
||||
Mix_PauseMusic( );
|
||||
}
|
||||
|
||||
void SoundStream::Resume( )
|
||||
{
|
||||
Mix_ResumeMusic( );
|
||||
}
|
||||
|
||||
int SoundStream::GetVolume( )
|
||||
{
|
||||
return Mix_VolumeMusic( -1 );
|
||||
}
|
||||
|
||||
bool SoundStream::IsPlaying( )
|
||||
{
|
||||
return Mix_PlayingMusic( ) == 0 ? false : true;
|
||||
}
|
||||
|
||||
void SoundStream::SetVolume( int value )
|
||||
{
|
||||
Mix_VolumeMusic( value );
|
||||
}
|
||||
|
||||
24
Engine/SoundStream.h
Normal file
24
Engine/SoundStream.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
class SoundStream final
|
||||
{
|
||||
public:
|
||||
explicit SoundStream( const std::string& path );
|
||||
~SoundStream( );
|
||||
SoundStream( const SoundStream& other ) = delete;
|
||||
SoundStream& operator=( const SoundStream& rhs ) = delete;
|
||||
SoundStream(SoundStream&& other) = delete;
|
||||
SoundStream& operator=(SoundStream&& other) = delete;
|
||||
|
||||
bool IsLoaded( ) const;
|
||||
bool Play( bool repeat ) const;
|
||||
static void Stop( );
|
||||
static void Pause( );
|
||||
static void Resume( );
|
||||
static void SetVolume(int value );
|
||||
static int GetVolume( );
|
||||
static bool IsPlaying( );
|
||||
|
||||
private:
|
||||
Mix_Music *m_pMixMusic;
|
||||
};
|
||||
353
Engine/Texture.cpp
Normal file
353
Engine/Texture.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include "base.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "Texture.h"
|
||||
|
||||
|
||||
Texture::Texture( const std::string& imagePath )
|
||||
:m_Id{ }
|
||||
,m_Width{ 10.0f }
|
||||
,m_Height{ 10.0f }
|
||||
,m_CreationOk{ false }
|
||||
{
|
||||
CreateFromImage( imagePath );
|
||||
}
|
||||
|
||||
Texture::Texture( const std::string& text, TTF_Font *pFont, const Color4f& textColor )
|
||||
:m_Id{}
|
||||
,m_Width{ 10.0f }
|
||||
,m_Height{ 10.0f }
|
||||
,m_CreationOk{ false }
|
||||
{
|
||||
CreateFromString( text, pFont, textColor );
|
||||
}
|
||||
|
||||
Texture::Texture( const std::string& text, const std::string& fontPath, int ptSize, const Color4f& textColor )
|
||||
:m_Id{}
|
||||
,m_Width{ 10.0f }
|
||||
,m_Height{ 10.0f }
|
||||
,m_CreationOk{ false }
|
||||
{
|
||||
CreateFromString( text, fontPath, ptSize, textColor );
|
||||
}
|
||||
Texture::Texture( Texture&& other ) noexcept
|
||||
:m_Id{ other.m_Id }
|
||||
,m_Width{ other.m_Width }
|
||||
,m_Height{ other.m_Height }
|
||||
,m_CreationOk{ other.m_CreationOk }
|
||||
{
|
||||
other.m_Id = 0;
|
||||
other.m_CreationOk = false;
|
||||
}
|
||||
|
||||
Texture& Texture::operator=( Texture&& other ) noexcept
|
||||
{
|
||||
if (this != &other)// no self assignment
|
||||
{
|
||||
|
||||
m_Id = other.m_Id;
|
||||
m_Width = other.m_Width;
|
||||
m_Height = other.m_Height;
|
||||
m_CreationOk = other.m_CreationOk;
|
||||
other.m_Id = 0;
|
||||
other.m_CreationOk = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
glDeleteTextures( 1, &m_Id );
|
||||
}
|
||||
|
||||
void Texture::CreateFromImage( const std::string& path )
|
||||
{
|
||||
m_CreationOk = true;
|
||||
|
||||
// Load image at specified path
|
||||
SDL_Surface* pLoadedSurface = IMG_Load(path.c_str());
|
||||
if (pLoadedSurface == nullptr)
|
||||
{
|
||||
std::cerr << "Texture::CreateFromImage, error when calling IMG_Load: " << SDL_GetError() << std::endl;
|
||||
m_CreationOk = false;
|
||||
return;
|
||||
}
|
||||
CreateFromSurface(pLoadedSurface);
|
||||
|
||||
// Free loaded surface
|
||||
SDL_FreeSurface(pLoadedSurface);
|
||||
|
||||
}
|
||||
|
||||
void Texture::CreateFromString( const std::string& text, const std::string& fontPath, int ptSize, const Color4f& textColor )
|
||||
{
|
||||
m_CreationOk = true;
|
||||
|
||||
// Create font
|
||||
TTF_Font *pFont{};
|
||||
pFont = TTF_OpenFont( fontPath.c_str( ), ptSize );
|
||||
if(pFont == nullptr )
|
||||
{
|
||||
std::cerr << "Texture::CreateFromString, error when calling TTF_OpenFont: " << TTF_GetError( ) << std::endl;
|
||||
m_CreationOk = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create texture using this font and close font afterwards
|
||||
CreateFromString( text, pFont, textColor );
|
||||
TTF_CloseFont( pFont );
|
||||
}
|
||||
|
||||
void Texture::CreateFromString( const std::string& text, TTF_Font *pFont, const Color4f& color )
|
||||
{
|
||||
m_CreationOk = true;
|
||||
if ( pFont == nullptr )
|
||||
{
|
||||
std::cerr << "Texture::CreateFromString, invalid TTF_Font pointer\n" ;
|
||||
m_CreationOk = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Render text surface
|
||||
SDL_Color textColor{};
|
||||
textColor.r = Uint8( color.r * 255 );
|
||||
textColor.g = Uint8( color.g * 255 );
|
||||
textColor.b = Uint8( color.b * 255 );
|
||||
textColor.a = Uint8( color.a * 255 );
|
||||
|
||||
SDL_Surface* pLoadedSurface = TTF_RenderText_Blended( pFont, text.c_str( ), textColor );
|
||||
if ( pLoadedSurface == nullptr )
|
||||
{
|
||||
std::cerr << "Texture::CreateFromString, error when calling TTF_RenderText_Blended: " << TTF_GetError( ) << std::endl;
|
||||
m_CreationOk = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy to video memory
|
||||
CreateFromSurface( pLoadedSurface );
|
||||
|
||||
// Free loaded surface
|
||||
SDL_FreeSurface( pLoadedSurface );
|
||||
}
|
||||
|
||||
void Texture::CreateFromSurface( SDL_Surface* pSurface )
|
||||
{
|
||||
m_CreationOk = true;
|
||||
|
||||
//Get image dimensions
|
||||
m_Width = float(pSurface->w);
|
||||
m_Height =float( pSurface->h);
|
||||
|
||||
// Get pixel format information and translate to OpenGl format
|
||||
GLenum pixelFormat{ GL_RGB };
|
||||
switch ( pSurface->format->BytesPerPixel )
|
||||
{
|
||||
case 3:
|
||||
if ( pSurface->format->Rmask == 0x000000ff )
|
||||
{
|
||||
pixelFormat = GL_RGB;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixelFormat = GL_BGR;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if ( pSurface->format->Rmask == 0x000000ff )
|
||||
{
|
||||
pixelFormat = GL_RGBA;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixelFormat = GL_BGRA;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Texture::CreateFromSurface, unknow pixel format, BytesPerPixel: " << pSurface->format->BytesPerPixel << "\nUse 32 bit or 24 bit images.\n";
|
||||
m_CreationOk = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//Generate an array of textures. We only want one texture (one element array), so trick
|
||||
//it by treating "texture" as array of length one.
|
||||
glGenTextures(1, &m_Id);
|
||||
|
||||
//Select (bind) the texture we just generated as the current 2D texture OpenGL is using/modifying.
|
||||
//All subsequent changes to OpenGL's texturing state for 2D textures will affect this texture.
|
||||
glBindTexture(GL_TEXTURE_2D, m_Id);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, pSurface->pitch / pSurface->format->BytesPerPixel);
|
||||
// check for errors. Can happen if a texture is created while a static pointer is being initialized, even before the call to the main function.
|
||||
GLenum e = glGetError();
|
||||
if (e != GL_NO_ERROR)
|
||||
{
|
||||
std::cerr << "Texture::CreateFromSurface, error binding textures, Error id = " << e << '\n';
|
||||
std::cerr << "Can happen if a texture is created before performing the initialization code (e.g. a static Texture object).\n";
|
||||
std::cerr << "There might be a white rectangle instead of the image.\n";
|
||||
}
|
||||
|
||||
// Specify the texture's data.
|
||||
// This function is a bit tricky, and it's hard to find helpful documentation.
|
||||
// A summary:
|
||||
// GL_TEXTURE_2D: The currently bound 2D texture (i.e. the one we just made)
|
||||
// 0: The mipmap level. 0, since we want to update the base level mipmap image (i.e., the image itself,
|
||||
// not cached smaller copies)
|
||||
// GL_RGBA: Specifies the number of color components in the texture.
|
||||
// This is how OpenGL will store the texture internally (kinda)--
|
||||
// It's essentially the texture's type.
|
||||
// surface->w: The width of the texture
|
||||
// surface->h: The height of the texture
|
||||
// 0: The border. Don't worry about this if you're just starting.
|
||||
// pixelFormat: The format that the *data* is in--NOT the texture!
|
||||
// GL_UNSIGNED_BYTE: The type the data is in. In SDL, the data is stored as an array of bytes, with each channel
|
||||
// getting one byte. This is fairly typical--it means that the image can store, for each channel,
|
||||
// any value that fits in one byte (so 0 through 255). These values are to be interpreted as
|
||||
// *unsigned* values (since 0x00 should be dark and 0xFF should be bright).
|
||||
// surface->pixels: The actual data. As above, SDL's array of bytes.
|
||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, pSurface->w, pSurface->h, 0, pixelFormat, GL_UNSIGNED_BYTE, pSurface->pixels );
|
||||
|
||||
// Set the minification and magnification filters. In this case, when the texture is minified (i.e., the texture's pixels (texels) are
|
||||
// *smaller* than the screen pixels you're seeing them on, linearly filter them (i.e. blend them together). This blends four texels for
|
||||
// each sample--which is not very much. Mipmapping can give better results. Find a texturing tutorial that discusses these issues
|
||||
// further. Conversely, when the texture is magnified (i.e., the texture's texels are *larger* than the screen pixels you're seeing
|
||||
// them on), linearly filter them. Qualitatively, this causes "blown up" (overmagnified) textures to look blurry instead of blocky.
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
||||
}
|
||||
|
||||
void Texture::Draw( const Point2f& dstBottomLeft, const Rectf& srcRect ) const
|
||||
{
|
||||
const float epsilon{ 0.001f };
|
||||
if ( !m_CreationOk )
|
||||
{
|
||||
if (!(srcRect.width > epsilon && srcRect.height > epsilon)) // No srcRect specified
|
||||
{
|
||||
DrawFilledRect(Rectf{ dstBottomLeft.x, dstBottomLeft.y, m_Width, m_Height });
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawFilledRect(Rectf{ dstBottomLeft.x, dstBottomLeft.y, srcRect.width, srcRect.height });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Rectf dstRect{ dstBottomLeft.x, dstBottomLeft.y, srcRect.width, srcRect.height };
|
||||
if (!(srcRect.width > epsilon && srcRect.height > epsilon)) // No srcRect specified
|
||||
{
|
||||
dstRect.width = m_Width;
|
||||
dstRect.height = m_Height;
|
||||
}
|
||||
Draw( dstRect, srcRect );
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::Draw( const Rectf& dstRect, const Rectf& srcRect ) const
|
||||
{
|
||||
const float epsilon{ 0.001f };
|
||||
if ( !m_CreationOk )
|
||||
{
|
||||
DrawFilledRect( dstRect );
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine texture coordinates using srcRect and default destination width and height
|
||||
float textLeft{};
|
||||
float textRight{};
|
||||
float textTop{};
|
||||
float textBottom{};
|
||||
|
||||
float defaultDestWidth{};
|
||||
float defaultDestHeight{};
|
||||
if ( !( srcRect.width > epsilon && srcRect.height > epsilon) ) // No srcRect specified
|
||||
{
|
||||
// Use complete texture
|
||||
textLeft = 0.0f;
|
||||
textRight = 1.0f;
|
||||
textTop = 0.0f;
|
||||
textBottom = 1.0f;
|
||||
|
||||
defaultDestHeight = m_Height;
|
||||
defaultDestWidth = m_Width;
|
||||
}
|
||||
else // srcRect specified
|
||||
{
|
||||
// Convert to the range [0.0, 1.0]
|
||||
textLeft = srcRect.left / m_Width;
|
||||
textRight = ( srcRect.left + srcRect.width ) / m_Width;
|
||||
textTop = ( srcRect.bottom - srcRect.height ) / m_Height;
|
||||
textBottom = srcRect.bottom / m_Height;
|
||||
|
||||
defaultDestHeight = srcRect.height;
|
||||
defaultDestWidth = srcRect.width;
|
||||
}
|
||||
|
||||
// Determine vertex coordinates
|
||||
float vertexLeft{ dstRect.left };
|
||||
float vertexBottom{ dstRect.bottom };
|
||||
float vertexRight{};
|
||||
float vertexTop{};
|
||||
if ( !( dstRect.width > 0.001f && dstRect.height > 0.001f ) ) // If no size specified use default size
|
||||
{
|
||||
vertexRight = vertexLeft + defaultDestWidth;
|
||||
vertexTop = vertexBottom + defaultDestHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexRight = vertexLeft + dstRect.width;
|
||||
vertexTop = vertexBottom + dstRect.height;
|
||||
|
||||
}
|
||||
|
||||
// Tell opengl which texture we will use
|
||||
glBindTexture( GL_TEXTURE_2D, m_Id );
|
||||
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
|
||||
|
||||
// Draw
|
||||
glEnable( GL_TEXTURE_2D );
|
||||
{
|
||||
glBegin( GL_QUADS );
|
||||
{
|
||||
glTexCoord2f( textLeft, textBottom );
|
||||
glVertex2f( vertexLeft, vertexBottom );
|
||||
|
||||
glTexCoord2f( textLeft, textTop );
|
||||
glVertex2f( vertexLeft, vertexTop );
|
||||
|
||||
glTexCoord2f( textRight, textTop );
|
||||
glVertex2f( vertexRight, vertexTop );
|
||||
|
||||
glTexCoord2f( textRight, textBottom );
|
||||
glVertex2f( vertexRight, vertexBottom );
|
||||
}
|
||||
glEnd( );
|
||||
}
|
||||
glDisable( GL_TEXTURE_2D );
|
||||
}
|
||||
|
||||
float Texture::GetWidth() const
|
||||
{
|
||||
return m_Width;
|
||||
}
|
||||
|
||||
float Texture::GetHeight() const
|
||||
{
|
||||
return m_Height;
|
||||
}
|
||||
|
||||
bool Texture::IsCreationOk( ) const
|
||||
{
|
||||
return m_CreationOk;
|
||||
}
|
||||
|
||||
void Texture::DrawFilledRect(const Rectf& rect) const
|
||||
{
|
||||
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_POLYGON);
|
||||
{
|
||||
glVertex2f(rect.left, rect.bottom);
|
||||
glVertex2f(rect.left + rect.width, rect.bottom);
|
||||
glVertex2f(rect.left + rect.width, rect.bottom + rect.height);
|
||||
glVertex2f(rect.left , rect.bottom + rect.height);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
}
|
||||
37
Engine/Texture.h
Normal file
37
Engine/Texture.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "base.h"
|
||||
|
||||
class Texture final
|
||||
{
|
||||
public:
|
||||
explicit Texture( const std::string& imagePath );
|
||||
explicit Texture( const std::string& text, TTF_Font *pFont, const Color4f& textColor );
|
||||
explicit Texture( const std::string& text, const std::string& fontPath, int ptSize, const Color4f& textColor );
|
||||
Texture( const Texture& other ) = delete;
|
||||
Texture& operator=( const Texture& other ) = delete;
|
||||
Texture( Texture&& other ) noexcept;
|
||||
Texture& operator=( Texture&& other ) noexcept;
|
||||
~Texture();
|
||||
|
||||
void Draw(const Point2f& dstBottomLeft = {}, const Rectf& srcRect = {}) const;
|
||||
void Draw( const Rectf& dstRect, const Rectf& srcRect = {} ) const;
|
||||
|
||||
float GetWidth() const;
|
||||
float GetHeight() const;
|
||||
bool IsCreationOk( ) const;
|
||||
|
||||
private:
|
||||
//DATA MEMBERS
|
||||
GLuint m_Id;
|
||||
float m_Width;
|
||||
float m_Height;
|
||||
bool m_CreationOk;
|
||||
|
||||
// FUNCTIONS
|
||||
void CreateFromImage( const std::string& path );
|
||||
void CreateFromString( const std::string& text, TTF_Font *pFont, const Color4f& textColor );
|
||||
void CreateFromString( const std::string& text, const std::string& fontPath, int ptSize, const Color4f& textColor );
|
||||
void CreateFromSurface( SDL_Surface *pSurface );
|
||||
SDL_Surface * STBImageLoad( const std::string& path );
|
||||
void DrawFilledRect(const Rectf& dstRect) const;
|
||||
};
|
||||
24
Engine/Transform.h
Normal file
24
Engine/Transform.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "base.h"
|
||||
#include "Vector2f.h"
|
||||
|
||||
struct Transform
|
||||
{
|
||||
public:
|
||||
void ApplyTransformation() const
|
||||
{
|
||||
glPushMatrix();
|
||||
glTranslatef(Position.x, Position.y, 0);
|
||||
glRotatef(Rotation, 0, 0, 1);
|
||||
glScalef(Scale.x, Scale.y, 1);
|
||||
}
|
||||
|
||||
void ResetTransformation() const
|
||||
{
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
Vector2f Position{};
|
||||
float Rotation{};
|
||||
Vector2f Scale{ 1.f, 1.f };
|
||||
};
|
||||
236
Engine/Vector2f.cpp
Normal file
236
Engine/Vector2f.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "base.h"
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "Vector2f.h"
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Vector2f Constructors
|
||||
//-----------------------------------------------------------------
|
||||
Vector2f::Vector2f( )
|
||||
:Vector2f{ 0.0f, 0.0f }
|
||||
{
|
||||
}
|
||||
|
||||
Vector2f::Vector2f( float x, float y )
|
||||
: x{ x }
|
||||
, y{ y }
|
||||
{
|
||||
}
|
||||
|
||||
Vector2f::Vector2f( const Point2f& fromPoint, const Point2f& tillPoint )
|
||||
: Vector2f{ tillPoint.x - fromPoint.x, tillPoint.y - fromPoint.y }
|
||||
{
|
||||
}
|
||||
|
||||
Vector2f::Vector2f(const Point2f & point)
|
||||
: Vector2f{ Point2f{ 0.0f, 0.0f }, point }
|
||||
{
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// Methods
|
||||
// -------------------------
|
||||
bool Vector2f::Equals(const Vector2f& other, float epsilon) const
|
||||
{
|
||||
return ( abs(x - other.x) < epsilon ) && ( abs(y - other.y) < epsilon );
|
||||
}
|
||||
|
||||
Point2f Vector2f::ToPoint2f() const
|
||||
{
|
||||
return Point2f{ x, y };
|
||||
}
|
||||
|
||||
float Vector2f::DotProduct(const Vector2f& other) const
|
||||
{
|
||||
return x * other.x + y * other.y;
|
||||
}
|
||||
|
||||
float Vector2f::CrossProduct(const Vector2f& other) const
|
||||
{
|
||||
return x * other.y - y * other.x;
|
||||
}
|
||||
|
||||
std::string Vector2f::ToString() const
|
||||
{
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << std::fixed;
|
||||
buffer << std::setprecision( 2 );
|
||||
buffer << "Vector2f(" << x << ", " << y << ")";
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
float Vector2f::Norm() const
|
||||
{
|
||||
return Length();
|
||||
}
|
||||
|
||||
float Vector2f::Length() const
|
||||
{
|
||||
return sqrt( x * x + y * y );
|
||||
}
|
||||
|
||||
float Vector2f::SquaredLength() const
|
||||
{
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
float Vector2f::AngleWith(const Vector2f& other) const
|
||||
{
|
||||
//https://stackoverflow.com/questions/21483999/using-atan2-to-find-angle-between-two-vectors
|
||||
// not calling dot and cross functions to prevent the overhead
|
||||
return atan2(x * other.y - other.x * y, x * other.x + y * other.y);
|
||||
}
|
||||
|
||||
Vector2f Vector2f::Normalized(float epsilon) const
|
||||
{
|
||||
float length{ Length( ) };
|
||||
if ( length < epsilon )
|
||||
{
|
||||
return Vector2f{ 0, 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
return Vector2f{ x / length, y / length };
|
||||
}
|
||||
}
|
||||
|
||||
Vector2f Vector2f::Orthogonal() const
|
||||
{
|
||||
return Vector2f{ -y, x };
|
||||
}
|
||||
|
||||
Vector2f Vector2f::Reflect( const Vector2f& surfaceNormal ) const
|
||||
{
|
||||
return (*this) - 2 * ( this->DotProduct( surfaceNormal ) * surfaceNormal );
|
||||
}
|
||||
|
||||
void Vector2f::Set(float newX, float newY)
|
||||
{
|
||||
x = newX;
|
||||
y = newY;
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// Member operators
|
||||
// -------------------------
|
||||
Vector2f Vector2f::operator-( ) const
|
||||
{
|
||||
return Vector2f{ -x, -y };
|
||||
}
|
||||
Vector2f Vector2f::operator+ ( ) const
|
||||
{
|
||||
return Vector2f{ x, y };
|
||||
}
|
||||
|
||||
Vector2f& Vector2f::operator*=(float rhs)
|
||||
{
|
||||
x *= rhs;
|
||||
y *= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2f& Vector2f::operator/=(float rhs)
|
||||
{
|
||||
*this *= 1 / rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2f& Vector2f::operator+=(const Vector2f& rhs)
|
||||
{
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2f& Vector2f::operator-=(const Vector2f& rhs)
|
||||
{
|
||||
*this += -rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2f::operator Point2f()
|
||||
{
|
||||
return Point2f{ x,y };
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// Non-member operators
|
||||
// -------------------------
|
||||
Vector2f operator*( float lhs, Vector2f rhs )
|
||||
{
|
||||
return rhs *= lhs;
|
||||
}
|
||||
|
||||
Vector2f operator*( Vector2f lhs, float rhs )
|
||||
{
|
||||
return lhs *= rhs;
|
||||
}
|
||||
|
||||
Vector2f operator/( Vector2f lhs, float rhs )
|
||||
{
|
||||
return lhs *= (1 / rhs);
|
||||
}
|
||||
|
||||
Vector2f operator+( Vector2f lhs, const Vector2f& rhs )
|
||||
{
|
||||
return lhs += rhs;
|
||||
}
|
||||
|
||||
Vector2f operator-( Vector2f lhs, const Vector2f& rhs )
|
||||
{
|
||||
return lhs += -rhs;
|
||||
}
|
||||
|
||||
bool operator==( const Vector2f& lhs, const Vector2f& rhs )
|
||||
{
|
||||
return ( lhs.Equals( rhs ) );
|
||||
}
|
||||
|
||||
bool operator!=( const Vector2f& lhs, const Vector2f& rhs )
|
||||
{
|
||||
return !( lhs == rhs );
|
||||
}
|
||||
|
||||
std::ostream& operator<< ( std::ostream& lhs, const Vector2f& rhs )
|
||||
{
|
||||
lhs << rhs.ToString( );
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// Point2f related operators
|
||||
Point2f& operator+=(Point2f& lhs, const Vector2f& rhs)
|
||||
{
|
||||
lhs.x += rhs.x;
|
||||
lhs.y += rhs.y;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
Point2f operator+(Point2f lhs, const Vector2f& rhs)
|
||||
{
|
||||
lhs += rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
Point2f& operator-=(Point2f& lhs, const Vector2f& rhs)
|
||||
{
|
||||
lhs.x -= rhs.x;
|
||||
lhs.y -= rhs.y;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
Point2f operator-(Point2f lhs, const Vector2f& rhs)
|
||||
{
|
||||
lhs -= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
|
||||
Vector2f operator-(const Point2f& lhs, const Point2f& rhs)
|
||||
{
|
||||
Vector2f v{ lhs.x - rhs.x, lhs.y - rhs.y };
|
||||
return v;
|
||||
}
|
||||
109
Engine/Vector2f.h
Normal file
109
Engine/Vector2f.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
#include "structs.h"
|
||||
|
||||
struct Vector2f final
|
||||
{
|
||||
// -------------------------
|
||||
// Constructors
|
||||
// -------------------------
|
||||
Vector2f( );
|
||||
explicit Vector2f( float x, float y );
|
||||
explicit Vector2f( const Point2f& fromPoint, const Point2f& tillPoint );
|
||||
explicit Vector2f( const Point2f& point );
|
||||
|
||||
// -------------------------
|
||||
// Member operators
|
||||
// -------------------------
|
||||
Vector2f operator-( ) const;
|
||||
Vector2f operator+( ) const;
|
||||
Vector2f& operator*=( float rhs);
|
||||
Vector2f& operator/=( float rhs);
|
||||
Vector2f& operator+=( const Vector2f& rhs);
|
||||
Vector2f& operator-=( const Vector2f& rhs);
|
||||
explicit operator Point2f();
|
||||
|
||||
// -------------------------
|
||||
// Methods
|
||||
// -------------------------
|
||||
// Convert to Point2f
|
||||
Point2f ToPoint2f( ) const;
|
||||
|
||||
// Are two vectors equal within a threshold?
|
||||
// u.Equals(v)
|
||||
bool Equals( const Vector2f& other, float epsilon = 0.001f ) const;
|
||||
|
||||
// Convert to String
|
||||
std::string ToString( ) const;
|
||||
|
||||
// DotProduct
|
||||
// float d = u.DotProduct(v);
|
||||
float DotProduct( const Vector2f& other ) const;
|
||||
|
||||
// CrossProduct
|
||||
// float d = u.CrossProduct(v);
|
||||
float CrossProduct( const Vector2f& other ) const;
|
||||
|
||||
// Norm of a vector
|
||||
// float l = v.Norm();
|
||||
float Norm( ) const;
|
||||
|
||||
// Length of a vector:
|
||||
// float l = v.Length();
|
||||
float Length( ) const;
|
||||
|
||||
// Square Length of a vector.
|
||||
// Faster alternative for Length, sqrt is not executed.
|
||||
float SquaredLength( ) const;
|
||||
|
||||
// AngleWith returns the smallest angle with another vector within the range [-PI/2, PI/2].
|
||||
// A pos angle is counter clockwise from this to the other
|
||||
// float angle = u.AngleWith(v);
|
||||
float AngleWith( const Vector2f& other ) const;
|
||||
|
||||
|
||||
// Returns normalized form of a vector
|
||||
// Vector2f n = v.Normalized();
|
||||
Vector2f Normalized( float epsilon = 0.001f ) const;
|
||||
|
||||
// Returns the orthogonal of the Vector2f
|
||||
// Vector2f w = v.Orthogonal();
|
||||
Vector2f Orthogonal( ) const;
|
||||
|
||||
// Returns a vector that is the reflection of the Vector2f
|
||||
// surfaceNormal: represents the normal of the surface at the reflection point
|
||||
Vector2f Reflect( const Vector2f& surfaceNormal ) const;
|
||||
|
||||
// Sets the values of x and y
|
||||
void Set( float newX, float newY );
|
||||
|
||||
// -------------------------
|
||||
// Datamembers
|
||||
// -------------------------
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
// -------------------------
|
||||
// Non member operators
|
||||
// -------------------------
|
||||
Vector2f operator*( float lhs, Vector2f rhs );
|
||||
Vector2f operator*( Vector2f lhs, float rhs );
|
||||
Vector2f operator/( Vector2f lhs, float rhs );
|
||||
|
||||
Vector2f operator+( Vector2f lhs, const Vector2f& rhs );
|
||||
Vector2f operator-( Vector2f lhs, const Vector2f& rhs );
|
||||
|
||||
bool operator==( const Vector2f& lhs, const Vector2f& rhs );
|
||||
bool operator!=( const Vector2f& lhs, const Vector2f& rhs );
|
||||
|
||||
std::ostream& operator<< ( std::ostream& lhs, const Vector2f& rhs );
|
||||
|
||||
// Translating a point by a vector
|
||||
Point2f& operator+=(Point2f& lhs, const Vector2f& rhs);
|
||||
Point2f operator+(Point2f lhs, const Vector2f& rhs);
|
||||
|
||||
Point2f& operator-=(Point2f& lhs, const Vector2f& rhs);
|
||||
Point2f operator-(Point2f lhs, const Vector2f& rhs);
|
||||
|
||||
// The difference vector between 2 points
|
||||
Vector2f operator-( const Point2f& lhs, const Point2f& rhs);
|
||||
|
||||
36
Engine/base.h
Normal file
36
Engine/base.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
//ML Detection Extension
|
||||
#ifdef _DEBUG
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <cstdlib>
|
||||
#include <crtdbg.h>
|
||||
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
|
||||
#define new DEBUG_NEW
|
||||
#endif
|
||||
|
||||
// SDL libs
|
||||
#pragma comment(lib, "SDL2.lib")
|
||||
#pragma comment(lib, "SDL2main.lib")
|
||||
|
||||
// OpenGL libs
|
||||
#pragma comment (lib,"opengl32.lib")
|
||||
#pragma comment (lib,"Glu32.lib")
|
||||
|
||||
// SDL extension libs
|
||||
#pragma comment(lib, "SDL2_image.lib")
|
||||
#pragma comment(lib, "SDL2_ttf.lib")
|
||||
#pragma comment(lib, "SDL2_mixer.lib")
|
||||
|
||||
// SDL and OpenGL Includes
|
||||
#pragma warning(disable : 26812)
|
||||
#pragma warning(disable : 4820)
|
||||
#include <SDL.h>
|
||||
#include <SDL_opengl.h>
|
||||
#include <SDL_ttf.h>
|
||||
#include <SDL_mixer.h>
|
||||
#include <SDL_image.h>
|
||||
|
||||
#pragma warning(default : 26812)
|
||||
#include "structs.h"
|
||||
|
||||
97
Engine/structs.cpp
Normal file
97
Engine/structs.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "base.h"
|
||||
#include "structs.h"
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Window Constructors
|
||||
//-----------------------------------------------------------------
|
||||
Window::Window( const std::string& title , float width , float height , bool isVSyncOn )
|
||||
:title{ title }
|
||||
,width{ width }
|
||||
,height{ height }
|
||||
,isVSyncOn{ isVSyncOn }
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Point2f Constructors
|
||||
//-----------------------------------------------------------------
|
||||
Point2f::Point2f( )
|
||||
:Point2f{ 0.0f, 0.0f }
|
||||
{
|
||||
}
|
||||
Point2f::Point2f( float x, float y )
|
||||
:x{ x }, y{ y }
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Rectf Constructors
|
||||
//-----------------------------------------------------------------
|
||||
Rectf::Rectf( )
|
||||
:Rectf{ 0.0f, 0.0f, 0.0f, 0.0f }
|
||||
{
|
||||
}
|
||||
|
||||
Rectf::Rectf( float left, float bottom, float width, float height )
|
||||
:left{ left }
|
||||
,bottom{ bottom }
|
||||
,width{ width }
|
||||
,height{ height }
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Color4f Constructors
|
||||
//-----------------------------------------------------------------
|
||||
Color4f::Color4f( )
|
||||
:Color4f{ 0.0f, 0.0f, 0.0f, 1.0f }
|
||||
{
|
||||
}
|
||||
|
||||
Color4f::Color4f( float r, float g, float b, float a )
|
||||
:r{ r }
|
||||
,g{ g }
|
||||
,b{ b }
|
||||
,a{ a }
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Circlef Constructors
|
||||
//-----------------------------------------------------------------
|
||||
Circlef::Circlef( )
|
||||
:Circlef{ 0.0f, 0.0f, 0.0f }
|
||||
{
|
||||
}
|
||||
|
||||
Circlef::Circlef( float centerX, float centerY, float radius )
|
||||
:Circlef{ Point2f{ centerX, centerY }, radius }
|
||||
{
|
||||
}
|
||||
|
||||
Circlef::Circlef( const Point2f& center, float radius )
|
||||
:center{ center }
|
||||
,radius{ radius }
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Ellipsef Constructors
|
||||
//-----------------------------------------------------------------
|
||||
Ellipsef::Ellipsef( )
|
||||
:Ellipsef{ 0.0f, 0.0f, 0.0f, 0.0f }
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Ellipsef::Ellipsef( const Point2f& center, float radiusX, float radiusY )
|
||||
: center{ center }
|
||||
, radiusX{ radiusX }
|
||||
, radiusY{ radiusY }
|
||||
{
|
||||
}
|
||||
|
||||
Ellipsef::Ellipsef( float centerX, float centerY, float radiusX, float radiusY )
|
||||
: Ellipsef{ Point2f{ centerX, centerY }, radiusX, radiusY }
|
||||
{
|
||||
}
|
||||
73
Engine/structs.h
Normal file
73
Engine/structs.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
struct Window
|
||||
{
|
||||
explicit Window( const std::string& title = "Title", float width = 320.0f,
|
||||
float height = 180.0f, bool isVSyncOn = true );
|
||||
|
||||
std::string title;
|
||||
float width;
|
||||
float height;
|
||||
bool isVSyncOn;
|
||||
};
|
||||
|
||||
struct Point2f
|
||||
{
|
||||
Point2f( );
|
||||
explicit Point2f( float x, float y );
|
||||
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct Rectf
|
||||
{
|
||||
Rectf( );
|
||||
explicit Rectf( float left, float bottom, float width, float height );
|
||||
|
||||
float left;
|
||||
float bottom;
|
||||
float width;
|
||||
float height;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct Color4f
|
||||
{
|
||||
Color4f( );
|
||||
explicit Color4f( float r, float g, float b, float a );
|
||||
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
};
|
||||
|
||||
struct Circlef
|
||||
{
|
||||
Circlef( );
|
||||
explicit Circlef( const Point2f& center, float radius );
|
||||
explicit Circlef( float centerX, float centerY, float radius );
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
};
|
||||
|
||||
|
||||
struct Ellipsef
|
||||
{
|
||||
Ellipsef( );
|
||||
explicit Ellipsef( const Point2f& center, float radiusX, float radiusY );
|
||||
explicit Ellipsef( float centerX, float centerY, float radiusX, float radiusY );
|
||||
|
||||
Point2f center;
|
||||
float radiusX;
|
||||
float radiusY;
|
||||
};
|
||||
|
||||
|
||||
|
||||
686
Engine/utils.cpp
Normal file
686
Engine/utils.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
#include "base.h"
|
||||
//#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
#pragma region OpenGLDrawFunctionality
|
||||
void utils::SetColor( const Color4f& color )
|
||||
{
|
||||
glColor4f( color.r, color.g, color.b, color.a );
|
||||
}
|
||||
|
||||
void utils::DrawPoint( float x, float y, float pointSize )
|
||||
{
|
||||
glPointSize( pointSize );
|
||||
glBegin( GL_POINTS );
|
||||
{
|
||||
glVertex2f( x, y );
|
||||
}
|
||||
glEnd( );
|
||||
}
|
||||
|
||||
void utils::DrawPoint( const Point2f& p, float pointSize )
|
||||
{
|
||||
DrawPoint( p.x, p.y, pointSize );
|
||||
}
|
||||
|
||||
void utils::DrawPoints( Point2f *pVertices, int nrVertices, float pointSize )
|
||||
{
|
||||
glPointSize( pointSize );
|
||||
glBegin( GL_POINTS );
|
||||
{
|
||||
for ( int idx{ 0 }; idx < nrVertices; ++idx )
|
||||
{
|
||||
glVertex2f( pVertices[idx].x, pVertices[idx].y );
|
||||
}
|
||||
}
|
||||
glEnd( );
|
||||
}
|
||||
|
||||
void utils::DrawLine( float x1, float y1, float x2, float y2, float lineWidth )
|
||||
{
|
||||
glLineWidth( lineWidth );
|
||||
glBegin( GL_LINES );
|
||||
{
|
||||
glVertex2f( x1, y1 );
|
||||
glVertex2f( x2, y2 );
|
||||
}
|
||||
glEnd( );
|
||||
}
|
||||
|
||||
void utils::DrawLine( const Point2f& p1, const Point2f& p2, float lineWidth )
|
||||
{
|
||||
DrawLine( p1.x, p1.y, p2.x, p2.y, lineWidth );
|
||||
}
|
||||
|
||||
void utils::DrawTriangle(const Point2f& p1, const Point2f& p2, const Point2f& p3, float lineWidth)
|
||||
{
|
||||
glLineWidth(lineWidth);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
{
|
||||
glVertex2f(p1.x, p1.y);
|
||||
glVertex2f(p2.x, p2.y);
|
||||
glVertex2f(p3.x, p3.y);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void utils::FillTriangle(const Point2f& p1, const Point2f& p2, const Point2f& p3)
|
||||
{
|
||||
glBegin(GL_TRIANGLES);
|
||||
{
|
||||
glVertex2f(p1.x, p1.y);
|
||||
glVertex2f(p2.x, p2.y);
|
||||
glVertex2f(p3.x, p3.y);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void utils::DrawRect( float left, float bottom, float width, float height, float lineWidth )
|
||||
{
|
||||
if (width > 0 && height > 0 && lineWidth > 0)
|
||||
{
|
||||
|
||||
glLineWidth(lineWidth);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
{
|
||||
glVertex2f(left, bottom);
|
||||
glVertex2f(left + width, bottom);
|
||||
glVertex2f(left + width, bottom + height);
|
||||
glVertex2f(left, bottom + height);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void utils::DrawRect( const Point2f& bottomLeft, float width, float height, float lineWidth )
|
||||
{
|
||||
DrawRect( bottomLeft.x, bottomLeft.y, width, height, lineWidth );
|
||||
}
|
||||
|
||||
void utils::DrawRect( const Rectf& rect, float lineWidth )
|
||||
{
|
||||
DrawRect( rect.left, rect.bottom, rect.width, rect.height, lineWidth );
|
||||
}
|
||||
|
||||
void utils::FillRect( float left, float bottom, float width, float height )
|
||||
{
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
|
||||
glBegin(GL_POLYGON);
|
||||
{
|
||||
glVertex2f(left, bottom);
|
||||
glVertex2f(left + width, bottom);
|
||||
glVertex2f(left + width, bottom + height);
|
||||
glVertex2f(left, bottom + height);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void utils::FillRect( const Point2f& bottomLeft, float width, float height )
|
||||
{
|
||||
FillRect( bottomLeft.x, bottomLeft.y, width, height );
|
||||
}
|
||||
|
||||
void utils::FillRect( const Rectf& rect )
|
||||
{
|
||||
FillRect( rect.left, rect.bottom, rect.width, rect.height );
|
||||
}
|
||||
|
||||
void utils::DrawEllipse( float centerX, float centerY, float radX, float radY, float lineWidth )
|
||||
{
|
||||
if (radX > 0 && radY > 0 && lineWidth > 0)
|
||||
{
|
||||
|
||||
float dAngle{ radX > radY ? float(g_Pi / radX) : float(g_Pi / radY) };
|
||||
|
||||
glLineWidth(lineWidth);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
{
|
||||
for (float angle = 0.0; angle < float(2 * g_Pi); angle += dAngle)
|
||||
{
|
||||
glVertex2f(centerX + radX * cos(angle), centerY + radY * sin(angle));
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void utils::DrawEllipse( const Point2f& center, float radX, float radY, float lineWidth )
|
||||
{
|
||||
DrawEllipse( center.x, center.y, radX, radY, lineWidth );
|
||||
}
|
||||
|
||||
void utils::DrawEllipse( const Ellipsef& ellipse, float lineWidth )
|
||||
{
|
||||
DrawEllipse( ellipse.center.x, ellipse.center.y, ellipse.radiusX, ellipse.radiusY, lineWidth );
|
||||
}
|
||||
|
||||
void utils::FillEllipse( float centerX, float centerY, float radX, float radY )
|
||||
{
|
||||
if (radX > 0 && radY > 0)
|
||||
{
|
||||
|
||||
float dAngle{ radX > radY ? float(g_Pi / radX) : float(g_Pi / radY) };
|
||||
|
||||
glBegin(GL_POLYGON);
|
||||
{
|
||||
for (float angle = 0.0; angle < float(2 * g_Pi); angle += dAngle)
|
||||
{
|
||||
glVertex2f(centerX + radX * cos(angle), centerY + radY * sin(angle));
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void utils::FillEllipse( const Ellipsef& ellipse )
|
||||
{
|
||||
FillEllipse( ellipse.center.x, ellipse.center.y, ellipse.radiusX, ellipse.radiusY );
|
||||
}
|
||||
|
||||
void utils::FillEllipse( const Point2f& center, float radX, float radY )
|
||||
{
|
||||
FillEllipse( center.x, center.y, radX, radY );
|
||||
}
|
||||
|
||||
void utils::DrawArc( float centerX, float centerY, float radX, float radY, float fromAngle, float tillAngle, float lineWidth )
|
||||
{
|
||||
if ( fromAngle > tillAngle )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float dAngle{ radX > radY ? float( g_Pi / radX ) : float( g_Pi / radY ) };
|
||||
|
||||
glLineWidth( lineWidth );
|
||||
glBegin( GL_LINE_STRIP );
|
||||
{
|
||||
for ( float angle = fromAngle; angle < tillAngle; angle += dAngle )
|
||||
{
|
||||
glVertex2f( centerX + radX * cos( angle ), centerY + radY * sin( angle ) );
|
||||
}
|
||||
glVertex2f( centerX + radX * cos( tillAngle ), centerY + radY * sin( tillAngle ) );
|
||||
}
|
||||
glEnd( );
|
||||
|
||||
}
|
||||
|
||||
void utils::DrawArc( const Point2f& center, float radX, float radY, float fromAngle, float tillAngle, float lineWidth )
|
||||
{
|
||||
DrawArc( center.x, center.y, radX, radY, fromAngle, tillAngle, lineWidth );
|
||||
}
|
||||
|
||||
void utils::FillArc( float centerX, float centerY, float radX, float radY, float fromAngle, float tillAngle )
|
||||
{
|
||||
if ( fromAngle > tillAngle )
|
||||
{
|
||||
return;
|
||||
}
|
||||
float dAngle{ radX > radY ? float( g_Pi / radX ) : float( g_Pi / radY ) };
|
||||
|
||||
glBegin( GL_POLYGON );
|
||||
{
|
||||
glVertex2f( centerX, centerY );
|
||||
for ( float angle = fromAngle; angle < tillAngle; angle += dAngle )
|
||||
{
|
||||
glVertex2f( centerX + radX * cos( angle ), centerY + radY * sin( angle ) );
|
||||
}
|
||||
glVertex2f( centerX + radX * cos( tillAngle ), centerY + radY * sin( tillAngle ) );
|
||||
}
|
||||
glEnd( );
|
||||
}
|
||||
|
||||
void utils::FillArc( const Point2f& center, float radX, float radY, float fromAngle, float tillAngle )
|
||||
{
|
||||
FillArc( center.x, center.y, radX, radY, fromAngle, tillAngle );
|
||||
}
|
||||
|
||||
void utils::DrawPolygon( const std::vector<Point2f>& vertices, bool closed, float lineWidth )
|
||||
{
|
||||
DrawPolygon( vertices.data( ), vertices.size( ), closed, lineWidth );
|
||||
}
|
||||
|
||||
void utils::DrawPolygon( const Point2f* pVertices, size_t nrVertices, bool closed, float lineWidth )
|
||||
{
|
||||
glLineWidth( lineWidth );
|
||||
closed ? glBegin( GL_LINE_LOOP ) : glBegin( GL_LINE_STRIP );
|
||||
{
|
||||
for ( size_t idx{ 0 }; idx < nrVertices; ++idx )
|
||||
{
|
||||
glVertex2f( pVertices[idx].x, pVertices[idx].y );
|
||||
}
|
||||
}
|
||||
glEnd( );
|
||||
}
|
||||
|
||||
void utils::FillPolygon( const std::vector<Point2f>& vertices )
|
||||
{
|
||||
FillPolygon( vertices.data( ), vertices.size( ) );
|
||||
}
|
||||
|
||||
void utils::FillPolygon( const Point2f *pVertices, size_t nrVertices )
|
||||
{
|
||||
glBegin( GL_POLYGON );
|
||||
{
|
||||
for ( size_t idx{ 0 }; idx < nrVertices; ++idx )
|
||||
{
|
||||
glVertex2f( pVertices[idx].x, pVertices[idx].y );
|
||||
}
|
||||
}
|
||||
glEnd( );
|
||||
}
|
||||
#pragma endregion OpenGLDrawFunctionality
|
||||
|
||||
#pragma region CollisionFunctionality
|
||||
float utils::GetDistance(float x1, float y1, float x2, float y2)
|
||||
{
|
||||
return (sqrtf((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)));
|
||||
}
|
||||
|
||||
float utils::GetDistance(const Point2f& p1, const Point2f& p2)
|
||||
{
|
||||
return GetDistance(p1.x, p1.y, p2.x, p2.y);
|
||||
}
|
||||
|
||||
bool utils::IsPointInRect( const Point2f& p, const Rectf& r )
|
||||
{
|
||||
return ( p.x >= r.left &&
|
||||
p.x <= r.left + r.width &&
|
||||
p.y >= r.bottom &&
|
||||
p.y <= r.bottom + r.height );
|
||||
}
|
||||
|
||||
bool utils::IsPointInCircle( const Point2f& p, const Circlef& c )
|
||||
{
|
||||
float squaredDist{ (p.x - c.center.x) * (p.x - c.center.x) + (p.y - c.center.y) * (p.y - c.center.y) };
|
||||
float squaredRadius{ c.radius * c.radius };
|
||||
return ( squaredRadius >= squaredDist );
|
||||
}
|
||||
|
||||
bool utils::IsOverlapping( const Point2f& a, const Point2f& b, const Rectf& r )
|
||||
{
|
||||
// if one of the line segment end points is in the rect
|
||||
if ( utils::IsPointInRect( a, r ) || utils::IsPointInRect( b, r ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
HitInfo hitInfo{};
|
||||
Point2f vertices[]{ Point2f {r.left, r.bottom},
|
||||
Point2f{ r.left + r.width, r.bottom },
|
||||
Point2f{ r.left + r.width, r.bottom + r.height },
|
||||
Point2f{ r.left, r.bottom + r.height } };
|
||||
|
||||
return Raycast( vertices, 4, a, b, hitInfo );
|
||||
}
|
||||
|
||||
bool utils::IsOverlapping( const Rectf& r1, const Rectf& r2 )
|
||||
{
|
||||
// If one rectangle is on left side of the other
|
||||
if ( ( r1.left + r1.width ) < r2.left || ( r2.left + r2.width ) < r1.left )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If one rectangle is under the other
|
||||
if ( r1.bottom > ( r2.bottom + r2.height ) || r2.bottom > ( r1.bottom + r1.height ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool utils::IsOverlapping( const Rectf& r, const Circlef& c )
|
||||
{
|
||||
// Is center of circle in the rectangle?
|
||||
if (IsPointInRect(c.center, r))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Check line segments
|
||||
if (utils::DistPointLineSegment(c.center, Point2f{ r.left, r.bottom }, Point2f{ r.left, r.bottom + r.height }) <= c.radius)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if ( utils::DistPointLineSegment( c.center, Point2f{ r.left, r.bottom }, Point2f{ r.left + r.width, r.bottom } ) <= c.radius )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (utils::DistPointLineSegment(c.center, Point2f{ r.left + r.width, r.bottom + r.height }, Point2f{ r.left, r.bottom + r.height }) <= c.radius)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (utils::DistPointLineSegment(c.center, Point2f{ r.left + r.width, r.bottom + r.height }, Point2f{ r.left + r.width, r.bottom }) <= c.radius)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool utils::IsOverlapping( const Circlef& c1, const Circlef& c2 )
|
||||
{
|
||||
// squared distance between centers
|
||||
float xDistance{ c1.center.x - c2.center.x };
|
||||
float yDistance{ c1.center.y - c2.center.y };
|
||||
float squaredDistance{ xDistance * xDistance + yDistance * yDistance };
|
||||
|
||||
float squaredTouchingDistance{ (c1.radius + c2.radius) * (c1.radius + c2.radius) };
|
||||
return (squaredDistance < squaredTouchingDistance);
|
||||
}
|
||||
|
||||
bool utils::IsOverlapping( const Point2f& a, const Point2f& b, const Circlef& c )
|
||||
{
|
||||
return utils::DistPointLineSegment( c.center, a, b ) <= c.radius;
|
||||
}
|
||||
|
||||
bool utils::IsOverlapping( const std::vector<Point2f>& vertices, const Circlef& c )
|
||||
{
|
||||
return IsOverlapping( vertices.data( ), vertices.size( ), c );
|
||||
}
|
||||
|
||||
bool utils::IsOverlapping( const Point2f* vertices, size_t nrVertices, const Circlef& c )
|
||||
{
|
||||
// Verify whether one of vertices is in circle
|
||||
for ( size_t i{ 0 }; i < nrVertices; ++i )
|
||||
{
|
||||
if ( IsPointInCircle( vertices[i], c ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify whether one of the polygon edges overlaps with circle
|
||||
for ( size_t i{ 0 }; i < nrVertices; ++i )
|
||||
{
|
||||
if ( DistPointLineSegment( c.center, vertices[i], vertices[( i + 1 ) % nrVertices] ) <= c.radius )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No overlapping with edges, verify whether circle is completely inside the polygon
|
||||
if ( IsPointInPolygon( c.center, vertices, nrVertices ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool utils::IsPointInPolygon( const Point2f& p, const std::vector<Point2f>& vertices )
|
||||
{
|
||||
return IsPointInPolygon( p, vertices.data( ), vertices.size( ) );
|
||||
}
|
||||
|
||||
bool utils::IsPointInPolygon( const Point2f& p, const Point2f* vertices, size_t nrVertices )
|
||||
{
|
||||
if ( nrVertices < 2 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 1. First do a simple test with axis aligned bounding box around the polygon
|
||||
float xMin{ vertices[0].x };
|
||||
float xMax{ vertices[0].x };
|
||||
float yMin{ vertices[0].y };
|
||||
float yMax{ vertices[0].y };
|
||||
for ( size_t idx{ 1 }; idx < nrVertices; ++idx )
|
||||
{
|
||||
if (xMin > vertices[idx].x)
|
||||
{
|
||||
xMin = vertices[idx].x;
|
||||
}
|
||||
if (xMax < vertices[idx].x)
|
||||
{
|
||||
xMax = vertices[idx].x;
|
||||
}
|
||||
if (yMin > vertices[idx].y)
|
||||
{
|
||||
yMin = vertices[idx].y;
|
||||
}
|
||||
if (yMax < vertices[idx].y)
|
||||
{
|
||||
yMax = vertices[idx].y;
|
||||
}
|
||||
}
|
||||
if (p.x < xMin || p.x > xMax || p.y < yMin || p.y > yMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Draw a virtual ray from anywhere outside the polygon to the point
|
||||
// and count how often it hits any side of the polygon.
|
||||
// If the number of hits is even, it's outside of the polygon, if it's odd, it's inside.
|
||||
int numberOfIntersectionPoints{0};
|
||||
Point2f p2{ xMax + 10.0f, p.y }; // Horizontal line from point to point outside polygon (p2)
|
||||
|
||||
// Count the number of intersection points
|
||||
float lambda1{}, lambda2{};
|
||||
for ( size_t i{ 0 }; i < nrVertices; ++i )
|
||||
{
|
||||
if ( IntersectLineSegments( vertices[i], vertices[( i + 1 ) % nrVertices], p, p2, lambda1, lambda2 ) )
|
||||
{
|
||||
if ( lambda1 > 0 && lambda1 <= 1 && lambda2 > 0 && lambda2 <= 1 )
|
||||
{
|
||||
++numberOfIntersectionPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numberOfIntersectionPoints % 2 == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool utils::IntersectLineSegments( const Point2f& p1, const Point2f& p2, const Point2f& q1, const Point2f& q2, float& outLambda1, float& outLambda2, float epsilon )
|
||||
{
|
||||
bool intersecting{ false };
|
||||
|
||||
Vector2f p1p2{ p1, p2 };
|
||||
Vector2f q1q2{ q1, q2 };
|
||||
|
||||
// Cross product to determine if parallel
|
||||
float denom = p1p2.CrossProduct( q1q2 );
|
||||
|
||||
// Don't divide by zero
|
||||
if ( std::abs( denom ) > epsilon )
|
||||
{
|
||||
intersecting = true;
|
||||
|
||||
Vector2f p1q1{ p1, q1 };
|
||||
|
||||
float num1 = p1q1.CrossProduct( q1q2 );
|
||||
float num2 = p1q1.CrossProduct( p1p2 );
|
||||
outLambda1 = num1 / denom;
|
||||
outLambda2 = num2 / denom;
|
||||
}
|
||||
else // are parallel
|
||||
{
|
||||
// Connect start points
|
||||
Vector2f p1q1{ p1, q1 };
|
||||
|
||||
// Cross product to determine if segments and the line connecting their start points are parallel,
|
||||
// if so, than they are on a line
|
||||
// if not, then there is no intersection
|
||||
if (std::abs( p1q1.CrossProduct(q1q2) ) > epsilon)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the 4 conditions
|
||||
outLambda1 = 0;
|
||||
outLambda2 = 0;
|
||||
if (utils::IsPointOnLineSegment(p1, q1, q2) ||
|
||||
utils::IsPointOnLineSegment(p2, q1, q2) ||
|
||||
utils::IsPointOnLineSegment(q1, p1, p2) ||
|
||||
utils::IsPointOnLineSegment(q2, p1, p2))
|
||||
{
|
||||
intersecting = true;
|
||||
}
|
||||
}
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
bool utils::Raycast( const std::vector<Point2f>& vertices, const Point2f& rayP1, const Point2f& rayP2, HitInfo& hitInfo )
|
||||
{
|
||||
return Raycast( vertices.data( ), vertices.size( ), rayP1, rayP2, hitInfo );
|
||||
}
|
||||
|
||||
bool utils::Raycast( const Point2f* vertices, const size_t nrVertices, const Point2f& rayP1, const Point2f& rayP2, HitInfo& hitInfo )
|
||||
{
|
||||
if ( nrVertices == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<HitInfo> hits;
|
||||
|
||||
Rectf r1, r2;
|
||||
// r1: minimal AABB rect enclosing the ray
|
||||
r1.left = std::min( rayP1.x, rayP2.x );
|
||||
r1.bottom = std::min( rayP1.y, rayP2.y );
|
||||
r1.width = std::max( rayP1.x, rayP2.x ) - r1.left;
|
||||
r1.height = std::max( rayP1.y, rayP2.y ) - r1.bottom;
|
||||
|
||||
// Line-line intersections.
|
||||
for ( size_t idx{ 0 }; idx <= nrVertices; ++idx )
|
||||
{
|
||||
// Consider line segment between 2 consecutive vertices
|
||||
// (modulo to allow closed polygon, last - first vertice)
|
||||
Point2f q1 = vertices[( idx + 0 ) % nrVertices];
|
||||
Point2f q2 = vertices[( idx + 1 ) % nrVertices];
|
||||
|
||||
// r2: minimal AABB rect enclosing the 2 vertices
|
||||
r2.left = std::min( q1.x, q2.x );
|
||||
r2.bottom = std::min( q1.y, q2.y );
|
||||
r2.width = std::max( q1.x, q2.x ) - r2.left;
|
||||
r2.height = std::max( q1.y, q2.y ) - r2.bottom;
|
||||
|
||||
if ( IsOverlapping( r1, r2 ) )
|
||||
{
|
||||
float lambda1{};
|
||||
float lambda2{};
|
||||
if ( IntersectLineSegments( rayP1, rayP2, q1, q2, lambda1, lambda2 ) )
|
||||
{
|
||||
if ( lambda1 > 0 && lambda1 <= 1 && lambda2 > 0 && lambda2 <= 1 )
|
||||
{
|
||||
HitInfo linesHitInfo{};
|
||||
linesHitInfo.lambda = lambda1;
|
||||
linesHitInfo.intersectPoint = Point2f{ rayP1.x + ( ( rayP2.x - rayP1.x ) * lambda1 ), rayP1.y + ( ( rayP2.y - rayP1.y ) * lambda1 ) };
|
||||
linesHitInfo.normal = Vector2f{ q2 - q1 }.Orthogonal( ).Normalized( );
|
||||
hits.push_back(linesHitInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( hits.size( ) == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get closest intersection point and copy it into the hitInfo parameter
|
||||
hitInfo = *std::min_element
|
||||
(
|
||||
hits.begin( ), hits.end( ),
|
||||
[]( const HitInfo& first, const HitInfo& last )
|
||||
{
|
||||
return first.lambda < last.lambda;
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool utils::IsPointOnLineSegment( const Point2f& p, const Point2f& a, const Point2f& b )
|
||||
{
|
||||
Vector2f ap{ a, p }, bp{ b, p };
|
||||
// If not on same line, return false
|
||||
if ( abs( ap.CrossProduct( bp ) ) > 0.001f )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Both vectors must point in opposite directions if p is between a and b
|
||||
if ( ap.DotProduct( bp ) > 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float utils::DistPointLineSegment( const Point2f& p, const Point2f& a, const Point2f& b )
|
||||
{
|
||||
Vector2f ab{ a, b };
|
||||
Vector2f ap{ a, p };
|
||||
Vector2f abNorm{ ab.Normalized() };
|
||||
float distToA{ abNorm.DotProduct(ap) };
|
||||
|
||||
// If distToA is negative, then the closest point is A
|
||||
// return the distance a, p
|
||||
if ( distToA < 0 )
|
||||
{
|
||||
return ap.Length( );
|
||||
}
|
||||
// If distToA is > than dist(a,b) then the closest point is B
|
||||
// return the distance b, p
|
||||
float distAB{ ab.Length() };
|
||||
if ( distToA > distAB )
|
||||
{
|
||||
return Vector2f{ b, p }.Length( );
|
||||
}
|
||||
|
||||
// Closest point is between A and B, calc intersection point
|
||||
Vector2f intersection{ abNorm.DotProduct(ap) * abNorm + Vector2f{ a } };
|
||||
return Vector2f{ p - intersection }.Length( );
|
||||
}
|
||||
|
||||
bool utils::IntersectRectLine(const Rectf& r, const Point2f& p1, const Point2f& p2, float& intersectMin, float& intersectMax)
|
||||
{
|
||||
// Parameters
|
||||
// input:
|
||||
// r: axis aligned bounding box, start and end points of line segment.
|
||||
// p1, p2: line
|
||||
// output:
|
||||
// intersectMin and intersectMax: in the interval [0,1] if intersection point is on the line segment.
|
||||
// return
|
||||
// true if there is an intersection
|
||||
|
||||
// Example of how to use
|
||||
//float min{};
|
||||
//float max{};
|
||||
//if (utils::IntersectRectLine(rect, p1, p2, min, max))
|
||||
//{
|
||||
// Point2f intersectP1{ p1 + (Vector2f(p2) - Vector2f(p1)) * min };
|
||||
// Point2f intersectP2{ p1 + (Vector2f(p2) - Vector2f(p1)) * max };
|
||||
//}
|
||||
|
||||
// 4 floats to convert rect space to line space
|
||||
// x1: value between 0 and 1 where 0 is on p1 and 1 is on p2, <0 and >1 means intersection is not on line segment
|
||||
float x1{ (r.left - p1.x) / (p2.x - p1.x) };
|
||||
float x2{ (r.left + r.width - p1.x) / (p2.x - p1.x) };
|
||||
float y1{ (r.bottom - p1.y) / (p2.y - p1.y) };
|
||||
float y2{ (r.bottom + r.height - p1.y) / (p2.y - p1.y) };
|
||||
|
||||
using std::max; using std::min;
|
||||
float tMin{ max(min(x1,x2),min(y1,y2)) };
|
||||
float tMax{ min(max(x1,x2), max(y1,y2)) };
|
||||
if (tMin > tMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
intersectMin = tMin;
|
||||
intersectMax = tMax;
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion CollisionFunctionality
|
||||
86
Engine/utils.h
Normal file
86
Engine/utils.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "Vector2f.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
const float g_Pi{ 3.1415926535f };
|
||||
|
||||
#pragma region OpenGLDrawFunctionality
|
||||
|
||||
void SetColor( const Color4f& color );
|
||||
|
||||
void DrawPoint( float x, float y, float pointSize = 1.0f );
|
||||
void DrawPoint( const Point2f& p, float pointSize = 1.0f );
|
||||
void DrawPoints( Point2f *pVertices, int nrVertices, float pointSize = 1.0f );
|
||||
|
||||
void DrawLine( float x1, float y1, float x2, float y2, float lineWidth = 1.0f );
|
||||
void DrawLine( const Point2f& p1, const Point2f& p2, float lineWidth = 1.0f );
|
||||
|
||||
void DrawTriangle(const Point2f& p1, const Point2f& p2, const Point2f& p3, float lineWidth = 1);
|
||||
void FillTriangle(const Point2f& p1, const Point2f& p2, const Point2f& p3);
|
||||
|
||||
void DrawRect(float left, float bottom, float width, float height, float lineWidth = 1.0f);
|
||||
void DrawRect(const Point2f& bottomLeft, float width, float height, float lineWidth = 1.0f);
|
||||
void DrawRect(const Rectf& rect, float lineWidth = 1.0f);
|
||||
void FillRect(float left, float bottom, float width, float height);
|
||||
void FillRect(const Point2f& bottomLeft, float width, float height);
|
||||
void FillRect(const Rectf& rect);
|
||||
|
||||
void DrawEllipse(float centerX, float centerY, float radX, float radY, float lineWidth = 1.0f);
|
||||
void DrawEllipse(const Point2f& center, float radX, float radY, float lineWidth = 1.0f);
|
||||
void DrawEllipse(const Ellipsef& ellipse , float lineWidth = 1.0f );
|
||||
void FillEllipse( float centerX, float centerY, float radX, float radY );
|
||||
void FillEllipse(const Ellipsef& ellipse );
|
||||
void FillEllipse(const Point2f& center, float radX, float radY);
|
||||
|
||||
// Draws an arc. The angle parameters are in radians, not in degrees.
|
||||
void DrawArc(float centerX, float centerY, float radX, float radY, float fromAngle, float tillAngle, float lineWidth = 1.0f);
|
||||
// Draws an arc. The angle parameters are in radians, not in degrees.
|
||||
void DrawArc(const Point2f& center, float radX, float radY, float fromAngle, float tillAngle, float lineWidth = 1.0f);
|
||||
// Fills an arc. The angle parameters are in radians, not in degrees.
|
||||
void FillArc(float centerX, float centerY, float radX, float radY, float fromAngle, float tillAngle);
|
||||
// Fills an arc. The angle parameters are in radians, not in degrees.
|
||||
void FillArc(const Point2f& center, float radX, float radY, float fromAngle, float tillAngle);
|
||||
|
||||
void DrawPolygon( const std::vector<Point2f>& vertices, bool closed = true, float lineWidth = 1.0f );
|
||||
void DrawPolygon( const Point2f* pVertices, size_t nrVertices, bool closed = true, float lineWidth = 1.0f );
|
||||
void FillPolygon( const std::vector<Point2f>& vertices);
|
||||
void FillPolygon( const Point2f* pVertices, size_t nrVertices);
|
||||
#pragma endregion OpenGLDrawFunctionality
|
||||
|
||||
#pragma region CollisionFunctionality
|
||||
struct HitInfo
|
||||
{
|
||||
float lambda;
|
||||
Point2f intersectPoint;
|
||||
Vector2f normal;
|
||||
};
|
||||
|
||||
float GetDistance(float x1, float y1, float x2, float y2);
|
||||
float GetDistance(const Point2f& p1, const Point2f& p2);
|
||||
|
||||
bool IsPointInRect(const Point2f& p, const Rectf& r);
|
||||
bool IsPointInCircle(const Point2f& p, const Circlef& c);
|
||||
bool IsPointInPolygon( const Point2f& p, const std::vector<Point2f>& vertices );
|
||||
bool IsPointInPolygon( const Point2f& p, const Point2f* vertices, size_t nrVertices );
|
||||
|
||||
bool IsOverlapping( const Point2f& a, const Point2f& b, const Circlef& c );
|
||||
bool IsOverlapping( const Point2f& a, const Point2f& b, const Rectf& r );
|
||||
|
||||
bool IsOverlapping(const Rectf & r1, const Rectf & r2);
|
||||
bool IsOverlapping( const Rectf& r, const Circlef& c );
|
||||
bool IsOverlapping( const Circlef& c1, const Circlef& c2 );
|
||||
bool IsOverlapping( const std::vector<Point2f>& vertices, const Circlef& c );
|
||||
bool IsOverlapping( const Point2f* vertices, size_t nrVertices, const Circlef& c );
|
||||
bool Raycast( const Point2f* vertices, const size_t nrVertices, const Point2f& rayP1, const Point2f& rayP2, HitInfo& hitInfo );
|
||||
bool Raycast( const std::vector<Point2f>& vertices, const Point2f& rayP1, const Point2f& rayP2, HitInfo& hitInfo );
|
||||
|
||||
bool IntersectLineSegments(const Point2f& p1, const Point2f& p2, const Point2f& q1, const Point2f& q2, float& outLambda1, float& outLambda2, float epsilon = 1e-6);
|
||||
float DistPointLineSegment(const Point2f& p, const Point2f& a, const Point2f& b);
|
||||
bool IsPointOnLineSegment(const Point2f& p, const Point2f& a, const Point2f& b);
|
||||
bool IntersectRectLine(const Rectf& r, const Point2f& p1, const Point2f& p2, float& intersectMin, float& intersectMax);
|
||||
|
||||
#pragma endregion CollisionFunctionality
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user