diff --git a/Chili Framework 2016.sln b/Chili Framework 2016.sln index 7351d448..b2e8bd8a 100644 --- a/Chili Framework 2016.sln +++ b/Chili Framework 2016.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2035 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Engine", "Engine\Engine.vcxproj", "{FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}" EndProject @@ -15,8 +15,8 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Debug|x64.ActiveCfg = Debug|x64 {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Debug|x64.Build.0 = Debug|x64 - {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Debug|x86.ActiveCfg = Debug|Win32 - {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Debug|x86.Build.0 = Debug|Win32 + {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Debug|x86.ActiveCfg = Debug|x64 + {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Debug|x86.Build.0 = Debug|x64 {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Release|x64.ActiveCfg = Release|x64 {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Release|x64.Build.0 = Release|x64 {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2}.Release|x86.ActiveCfg = Release|Win32 @@ -25,4 +25,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8E96B628-7273-433B-B7CE-8284E30105BC} + EndGlobalSection EndGlobal diff --git a/Engine/ALIB_Bezier.h b/Engine/ALIB_Bezier.h new file mode 100644 index 00000000..4b0ddecf --- /dev/null +++ b/Engine/ALIB_Bezier.h @@ -0,0 +1,40 @@ +#pragma once + +#include "JC_Shape.h" +#include "Camera.h" + +class ALIB_Bezier : public JC_Shape +{ +public: + ALIB_Bezier(JC_Point2d P, JC_Point2d Q, JC_Point2d R,Color color_in = Colors::White) + : + P(P), + Q(Q), + R(R), + JC_Shape(color_in) + {} + + void Draw(Camera cam) override + { + cam.DrawBezier(P, Q, R, Base_Color); + } + bool IsInRange(const JC_Point2d& M) override + { + return false; + } + + std::wstring MakeDescription() override + { + + std::wstring PosP = L"X: " + std::to_wstring(P.x) + L"\n Y: " + std::to_wstring(P.y); + std::wstring PosQ = L"X: " + std::to_wstring(Q.x) + L"\n Y: " + std::to_wstring(Q.y); + std::wstring PosR = L"X: " + std::to_wstring(R.x) + L"\n Y: " + std::to_wstring(R.y); + + return std::wstring(L"First point:\n " + PosP + L"Second point:\n " + PosQ + L"Third point:\n " + PosR); + + } + +private: + JC_Point2d P, Q, R; + +}; \ No newline at end of file diff --git a/Engine/Camera.h b/Engine/Camera.h new file mode 100644 index 00000000..e132c1ce --- /dev/null +++ b/Engine/Camera.h @@ -0,0 +1,100 @@ +#pragma once + +#include "CordinateTrasformerh.h" +#include "JC_Vector2.h" + +//Camera class should be responsible for scaling onad trafromation of the drawn objects +//It is the part of the screen pipeline + +class Camera +{ +public: + Camera (CoordinateTrasformer& ct) + : + ct(ct) + {} + + JC_Point2d GetPos()const + { + return Camera_Pos; + } + + void MoveBy(const JC_Vector2d& offset) + { + Camera_Pos += offset; + } + + void MoweTo(const JC_Point2d pos_in) + { + Camera_Pos = pos_in; + } + + void SetScale(double new_scale) + { + scale = new_scale; + } + + double GetScale() const + { + return scale; + } + + void DrawLine(JC_Point2d P_in, JC_Point2d Q_in, Color Color_in) + { + P_in -= Camera_Pos; + Q_in -= Camera_Pos; + ct.DrawLine(std::move(P_in), std::move(Q_in), std::move(Color_in)); + } + void DrawPoliLine(std::vector point_data, Color Color_in) + { + for (auto &P : point_data) + { + P -= Camera_Pos; + } + + ct.DrawPoliLine(std::move(point_data), std::move(Color_in)); + } + void DrawBezier(JC_Point2d P_in, JC_Point2d Q_in, JC_Point2d R_in, Color Color_in) + { + P_in -= Camera_Pos; + Q_in -= Camera_Pos; + R_in -= Camera_Pos; + ct.DrawBezier(std::move(P_in), std::move(Q_in), std::move(R_in) ,std::move(Color_in)); + } + + void DrawMPBezier(std::vector point_data, Color Color_in) + { + for (auto &P : point_data) + { + P -= Camera_Pos; + } + + ct.DrawMPBezier(std::move(point_data), std::move(Color_in)); + } + + void DrawCircle(JC_Point2d pos, double radius, int t, Color Color_in) + { + pos -= Camera_Pos; + ct.DrawCircle(std::move(pos), std::move(radius), t, std::move(Color_in)); + } + + // trasforms Point screen coordinates to mathematical coordinates regardles from cmaera position + + template JC_Point2 TrasformPoint(JC_Point2 input) + { + //fixes disconection between screen and math coordinates + input.x += (T)(Camera_Pos.x); + input.y -= (T)(Camera_Pos.y); + + JC_Vector2 offset = { (T)(Graphics::ScreenWidth / 2), (T)(Graphics::ScreenHeight / 2) }; + input -= offset; + input.y *= -1; + return input; + } + +private: + double scale = 1.0; + JC_Point2d Camera_Pos = { 0.0,0.0 }; + CoordinateTrasformer& ct; + +}; \ No newline at end of file diff --git a/Engine/ChiliRectangle.h b/Engine/ChiliRectangle.h new file mode 100644 index 00000000..6f627c44 --- /dev/null +++ b/Engine/ChiliRectangle.h @@ -0,0 +1,67 @@ +#pragma once + +#include "JC_Vector2.h" + +template +class CRectangle +{ +public: + CRectangle(T left_in, T right_in, T top_in, T bottom_in) + : + left(left_in), + right(right_in), + top(top_in), + bottom(bottom_in) + {} + CRectangle(const JC_Vector2& topLeft, const JC_Vector2& bottomRight) + : + CRectangle(topLeft.x, bottomRight.x, topLeft.y, bottomRight.y) + {} + CRectangle(const JC_Vector2& topLeft, T width, T height) + : + CRectangle(topLeft, topLeft + JC_Vector2(width, height)) + {} + bool IsOverlappingWith(const CRectangle& other) const + { + return right > other.left && left < other.right + && bottom > other.top && top < other.bottom; + } + bool IsContainedBy(const CRectangle& other) const + { + return left >= other.left && right <= other.right && + top >= other.top && bottom <= other.bottom; + } + bool Contains(const JC_Vector2& point) const + { + return point.x >= left && point.x < right && point.y >= top && point.y < bottom; + } + CRectangle FromCenter(const JC_Vector2& center, T halfWidth, T halfHeight) + { + const JC_Vector2 half(halfWidth, halfHeight); + return CRectangle(center - half, center + half); + } + CRectangle GetExpanded(T offset) const + { + return CRectangle(left - offset, right + offset, top - offset, bottom + offset); + } + JC_Vector2 GetCenter() const + { + return JC_Vector2((left + right) / (T)2, (top + bottom) / (T)2); + } + T GetWidth() const + { + return right - left; + } + T GetHeight() const + { + return bottom - top; + } +public: + T left; + T right; + T top; + T bottom; +}; + +typedef CRectangle RectI; +typedef CRectangle RectD; \ No newline at end of file diff --git a/Engine/ChiliUtil.h b/Engine/ChiliUtil.h new file mode 100644 index 00000000..a4569b92 --- /dev/null +++ b/Engine/ChiliUtil.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +// remove an element from a vector +// messes up the order of elements +// (faster than erase and doesn't need iter) +template +inline void remove_element(std::vector& vec, size_t index) +{ + // swap element to be removed with element at back + std::swap(vec[index], vec.back()); + // back is now what we want dead, so pop back! + vec.pop_back(); +} + +// binary search finds matching element +// otherwise returns end +// Acc is a functor used to access the search keys in the elements +template +auto binary_find(Iter begin, Iter end, const T& val, + Acc acc = [](const Iter::value_type& obj) + ->const Iter::value_type& { return obj; }) +{ + // Finds the lower bound in at most log(last - first) + 1 comparisons + const auto i = std::lower_bound(begin, end, val, + [acc](const Iter::value_type& lhs, const T& rhs) + { + return acc(lhs) < rhs; + } + ); + + // if i is end, not found and do not dereference + // else i is either greater than or equal to value + // if not greater than, must be equal, so return it + if (i != end && !(val < acc(*i))) + { + return i; // found + } + else + { + return end; // not found + } +} + +// uses remove_if to remove elements matching predicate over entire container +// and then calls erase to remove the 'empty' husks at the end +template +void remove_erase_if(Container& container, Pred pred) +{ + // this destroys all elements matching the predicate + // and fills the spaces with elements from the end + const auto new_end = std::remove_if(container.begin(), container.end(), pred); + // erase garbage husk element at end + container.erase(new_end, container.end()); +} \ No newline at end of file diff --git a/Engine/ChiliWin.h b/Engine/ChiliWin.h index 4a21edc9..f2602b5b 100644 --- a/Engine/ChiliWin.h +++ b/Engine/ChiliWin.h @@ -23,44 +23,51 @@ // target Windows 7 or later #define _WIN32_WINNT 0x0601 #include + // The following #defines disable a bunch of unused windows stuff. If you // get weird errors when trying to do some windows stuff, try removing some // (or all) of these defines (it will increase build time though). -#define WIN32_LEAN_AND_MEAN -#define NOGDICAPMASKS -#define NOSYSMETRICS -#define NOMENUS -#define NOICONS -#define NOSYSCOMMANDS -#define NORASTEROPS -#define OEMRESOURCE -#define NOATOM -#define NOCLIPBOARD -#define NOCOLOR -#define NOCTLMGR -#define NODRAWTEXT -#define NOKERNEL -#define NONLS -#define NOMEMMGR -#define NOMETAFILE -#define NOMINMAX -#define NOOPENFILE -#define NOSCROLL -#define NOSERVICE -#define NOSOUND -#define NOTEXTMETRIC -#define NOWH -#define NOCOMM -#define NOKANJI -#define NOHELP -#define NOPROFILER -#define NODEFERWINDOWPOS -#define NOMCX -#define NORPC -#define NOPROXYSTUB -#define NOIMAGE -#define NOTAPE +#ifndef FULL_WINTARD + +#define WIN32_LEAN_AND_MEAN //to exclude APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets. +#define NOGDICAPMASKS +#define NOSYSMETRICS +#define NOICONS +#define NOSYSCOMMANDS +#define NORASTEROPS +#define OEMRESOURCE +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCTLMGR +#define NOKERNEL +#define NONLS +#define NOMEMMGR +#define NOMETAFILE +#define NOOPENFILE +#define NOSCROLL +#define NOSERVICE +#define NOSOUND +#define NOTEXTMETRIC +#define NOWH +#define NOCOMM +#define NOKANJI +#define NOHELP +#define NOPROFILER +#define NODEFERWINDOWPOS +#define NOMCX +#define NORPC +#define NOPROXYSTUB +#define NOIMAGE +#define NOTAPE +#endif // FULL_WINTARD + +//#define NODRAWTEXT +#define NOMINMAX +//#define NOMENUS +#ifndef STRICT #define STRICT +#endif #include \ No newline at end of file diff --git a/Engine/Colors.h b/Engine/Colors.h index 23883414..da23791b 100644 --- a/Engine/Colors.h +++ b/Engine/Colors.h @@ -26,31 +26,39 @@ class Color unsigned int dword; public: constexpr Color() : dword() {} - constexpr Color( const Color& col ) + constexpr Color(const Color& col) : - dword( col.dword ) + dword(col.dword) {} - constexpr Color( unsigned int dw ) + constexpr Color(unsigned int dw) : - dword( dw ) + dword(dw) {} - constexpr Color( unsigned char x,unsigned char r,unsigned char g,unsigned char b ) + constexpr Color(unsigned char x, unsigned char r, unsigned char g, unsigned char b) : - dword( (x << 24u) | (r << 16u) | (g << 8u) | b ) + dword((x << 24u) | (r << 16u) | (g << 8u) | b) {} - constexpr Color( unsigned char r,unsigned char g,unsigned char b ) + constexpr Color(unsigned char r, unsigned char g, unsigned char b) : - dword( (r << 16u) | (g << 8u) | b ) + dword((r << 16u) | (g << 8u) | b) {} - constexpr Color( Color col,unsigned char x ) + constexpr Color(Color col, unsigned char x) : - Color( (x << 24u) | col.dword ) + Color((x << 24u) | col.dword) {} - Color& operator =( Color color ) + Color& operator =(Color color) { dword = color.dword; return *this; } + bool operator==(const Color& rhs) const + { + return dword == rhs.dword; + } + bool operator!=(const Color& rhs) const + { + return !(*this == rhs); + } constexpr unsigned char GetX() const { return dword >> 24u; @@ -71,23 +79,23 @@ class Color { return dword & 0xFFu; } - void SetX( unsigned char x ) + void SetX(unsigned char x) { dword = (dword & 0xFFFFFFu) | (x << 24u); } - void SetA( unsigned char a ) + void SetA(unsigned char a) { - SetX( a ); + SetX(a); } - void SetR( unsigned char r ) + void SetR(unsigned char r) { dword = (dword & 0xFF00FFFFu) | (r << 16u); } - void SetG( unsigned char g ) + void SetG(unsigned char g) { dword = (dword & 0xFFFF00FFu) | (g << 8u); } - void SetB( unsigned char b ) + void SetB(unsigned char b) { dword = (dword & 0xFFFFFF00u) | b; } @@ -95,18 +103,18 @@ class Color namespace Colors { - static constexpr Color MakeRGB( unsigned char r,unsigned char g,unsigned char b ) + static constexpr Color MakeRGB(unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; } - static constexpr Color White = MakeRGB( 255u,255u,255u ); - static constexpr Color Black = MakeRGB( 0u,0u,0u ); - static constexpr Color Gray = MakeRGB( 0x80u,0x80u,0x80u ); - static constexpr Color LightGray = MakeRGB( 0xD3u,0xD3u,0xD3u ); - static constexpr Color Red = MakeRGB( 255u,0u,0u ); - static constexpr Color Green = MakeRGB( 0u,255u,0u ); - static constexpr Color Blue = MakeRGB( 0u,0u,255u ); - static constexpr Color Yellow = MakeRGB( 255u,255u,0u ); - static constexpr Color Cyan = MakeRGB( 0u,255u,255u ); - static constexpr Color Magenta = MakeRGB( 255u,0u,255u ); + static constexpr Color White = MakeRGB(255u, 255u, 255u); + static constexpr Color Black = MakeRGB(0u, 0u, 0u); + static constexpr Color Gray = MakeRGB(0x80u, 0x80u, 0x80u); + static constexpr Color LightGray = MakeRGB(0xD3u, 0xD3u, 0xD3u); + static constexpr Color Red = MakeRGB(255u, 0u, 0u); + static constexpr Color Green = MakeRGB(0u, 255u, 0u); + static constexpr Color Blue = MakeRGB(0u, 0u, 255u); + static constexpr Color Yellow = MakeRGB(255u, 255u, 0u); + static constexpr Color Cyan = MakeRGB(0u, 255u, 255u); + static constexpr Color Magenta = MakeRGB(255u, 0u, 255u); } \ No newline at end of file diff --git a/Engine/CordinateTrasformerh.h b/Engine/CordinateTrasformerh.h new file mode 100644 index 00000000..1d8b8cf4 --- /dev/null +++ b/Engine/CordinateTrasformerh.h @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include "Graphics.h" +#include "Colors.h" +#include "JC_Vector2.h" + + +class CoordinateTrasformer +{ +public: + + CoordinateTrasformer (Graphics& gfx) + : + gfx(gfx) + {} + + void DrawLine(JC_Point2d P_in, JC_Point2d Q_in, Color Color_in) + { + JC_Vector2d offset = { double(Graphics::ScreenWidth / 2),double(Graphics::ScreenHeight / 2) }; + + P_in.y *= -1; + Q_in.y *= -1; + + P_in += offset; + Q_in += offset; + + + gfx.DrawLine(P_in, Q_in, Color_in); + } + + + void DrawPoliLine(std::vector point_data, Color Color_in) + { + JC_Vector2d offset = { double(Graphics::ScreenWidth / 2),double(Graphics::ScreenHeight / 2) }; + + for (auto &P : point_data) + { + P.y *= -1; + P += offset; + } + + gfx.DrawPoliLine(point_data, Color_in); + } + + + void DrawBezier(JC_Point2d P_in, JC_Point2d Q_in, JC_Point2d R_in, Color Color_in) + { + JC_Vector2d offset = { double(Graphics::ScreenWidth / 2),double(Graphics::ScreenHeight / 2) }; + + P_in.y *= -1; + Q_in.y *= -1; + R_in.y *= -1; + + P_in += offset; + Q_in += offset; + R_in += offset; + + gfx.DrawBezier(P_in, Q_in, R_in, Color_in); + } + + + void DrawMPBezier(std::vector point_data, Color Color_in) + { + JC_Vector2d offset = { double(Graphics::ScreenWidth / 2),double(Graphics::ScreenHeight / 2) }; + + for (auto &P : point_data) + { + P.y *= -1; + P += offset; + } + + gfx.DrawBezier(point_data, Color_in); + } + + + void DrawCircle(JC_Point2d pos, double radius, int t, Color c) + { + JC_Vector2d offset = { double(Graphics::ScreenWidth / 2),double(Graphics::ScreenHeight / 2) }; + + pos.y *= -1.0; + pos += offset; + + gfx.DrawCircle(pos, radius, t, c); + } + +private: + Graphics& gfx; + + + +}; \ No newline at end of file diff --git a/Engine/Engine.vcxproj b/Engine/Engine.vcxproj index c6e66c51..1de8e2dc 100644 --- a/Engine/Engine.vcxproj +++ b/Engine/Engine.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -21,32 +21,32 @@ {FFCA512B-49FC-4FC8-8A73-C4F87D322FF2} Engine - 8.1 + 10.0.17134.0 Application true - v140 + v141 Unicode Application false - v140 + v141 true Unicode Application true - v140 + v141 Unicode Application false - v140 + v141 true Unicode @@ -80,6 +80,7 @@ Fast MultiThreadedDebug false + stdcpplatest @@ -94,6 +95,7 @@ VectorCall false MultiThreadedDebug + stdcpplatest @@ -110,6 +112,7 @@ MultiThreaded false NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + stdcpplatest true @@ -129,6 +132,7 @@ MultiThreaded false NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + stdcpplatest true @@ -136,30 +140,53 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -228,6 +255,15 @@ + + + + + + + + + diff --git a/Engine/Engine.vcxproj.filters b/Engine/Engine.vcxproj.filters index 2026aaba..fa9d4f72 100644 --- a/Engine/Engine.vcxproj.filters +++ b/Engine/Engine.vcxproj.filters @@ -1,10 +1,6 @@  - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd @@ -16,11 +12,36 @@ {8b6c92a8-f65d-451e-80e3-d65773602b92} + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {7429fb29-146a-47ac-b5ad-e8731b226a6d} + + + {a1ba54ce-e6f2-47b3-93dd-e28c6615d142} + + + {991129b5-249e-4da4-bad5-8426e98a3a3c} + + + {ab08727f-c0bc-4aac-a38c-7a209aa66dcb} + + + {85608aef-b314-4019-9eef-58cd5dee7256} + + + {b309b936-8445-487c-909c-da4141e3b864} + + + {b9b4aa28-3d6d-4412-b8b4-6ff6c3af9881} + + + {17f84fd5-42d9-44cc-a348-7ca1040c068a} + - - Header Files - Header Files @@ -42,12 +63,6 @@ Header Files - - Header Files - - - Header Files - Header Files @@ -57,6 +72,66 @@ Header Files + + Header Files\Geometric shapes + + + Header Files\CameraPipeline + + + Header Files\CameraPipeline + + + Header Files\CameraPipeline + + + Header Files\Geometric shapes + + + Header Files\Geometric shapes + + + Header Files\Geometric shapes + + + Header Files\MathLib + + + Header Files\MathLib + + + Resource Files + + + Header Files\Geometric shapes + + + Header Files\Geometric shapes + + + Header Files\Chili util + + + Header Files\Chili util + + + Header Files\Chili util + + + Header Files\Chili Spriite loading + + + Header Files\Chili Spriite loading + + + Header Files\Chili Spriite loading + + + Header Files\Chili Spriite loading + + + Header Files\Chili util + @@ -86,6 +161,24 @@ Source Files + + Source Files\Shapes + + + Source Files\MathLib + + + Header Files\Geometric shapes + + + Source Files + + + Source Files + + + Source Files + @@ -104,5 +197,32 @@ Resource Files + + Added Sprites + + + Added Sprites + + + Added Sprites + + + Added Sprites + + + Added Sprites + + + Added Sprites + + + Added Sprites + + + Added Sprites + + + Added Sprites + \ No newline at end of file diff --git a/Engine/Font.cpp b/Engine/Font.cpp new file mode 100644 index 00000000..07eed270 --- /dev/null +++ b/Engine/Font.cpp @@ -0,0 +1,61 @@ +#include "Font.h" +#include +#include "SpriteEffect.h" + +Font::Font(const std::wstring& filename, Color chroma) + : + surface(filename), + // calculate glyph dimensions from bitmap dimensions + glyphWidth(surface.GetWidth() / nColumns), + glyphHeight(surface.GetHeight() / nRows), + chroma(chroma) +{ + // verify that bitmap had valid dimensions + assert(glyphWidth * nColumns == surface.GetWidth()); + assert(glyphHeight * nRows == surface.GetHeight()); +} + +void Font::DrawText(const std::string& text, const JC_Vector2i& pos, Color color, Graphics& gfx) const +{ + // create effect functor + SpriteEffect::Substitution e{ chroma,color }; + // curPos is the pos that we are drawing to on the screen + auto curPos = pos; + for (auto c : text) + { + // on a newline character, reset x position and move down by 1 glyph height + if (c == '\n') + { + // carriage return + curPos.x = pos.x; + // line feed + curPos.y += glyphHeight; + // we don't want to advance the character position right for a newline + continue; + } + // only draw characters that are on the font sheet + // start at firstChar + 1 because might as well skip ' ' as well + else if (c >= firstChar + 1 && c <= lastChar) + { + // use DrawSpriteSubstitute so that we can choose the color of the font rendered + gfx.DrawSprite(curPos.x, curPos.y, MapGlyphRect(c), surface, e); + } + // advance screen pos for next character + curPos.x += glyphWidth; + } +} + +RectI Font::MapGlyphRect(char c) const +{ + assert(c >= firstChar && c <= lastChar); + // font sheet glyphs start at ' ', calculate index into sheet + const int glyphIndex = c - ' '; + // map 1d glyphIndex to 2D coordinates + const int yGlyph = glyphIndex / nColumns; + const int xGlyph = glyphIndex % nColumns; + // convert the sheet grid coords to pixel coords in sheet + return RectI( + { xGlyph * glyphWidth,yGlyph * glyphHeight }, + glyphWidth, glyphHeight + ); +} \ No newline at end of file diff --git a/Engine/Font.h b/Engine/Font.h new file mode 100644 index 00000000..0734d9c4 --- /dev/null +++ b/Engine/Font.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Graphics.h" +#include "Surface.h" + +class Font +{ +public: + Font(const std::wstring& filename, Color chroma = Colors::White); + void DrawText(const std::string& text, const JC_Vector2i& pos, Color color, Graphics& gfx) const; +private: + RectI MapGlyphRect(char c) const; +private: + // holds the font sheet bitmap data + Surface surface; + // this gives the dimensions of a glyph in the font sheet + int glyphWidth; + int glyphHeight; + // number of rows / columns in the font sheet (this is fixed) + static constexpr int nColumns = 32; + static constexpr int nRows = 3; + // font sheet chroma color + Color chroma; + // start and end drawable character codes + static constexpr char firstChar = ' '; + static constexpr char lastChar = '~'; +}; \ No newline at end of file diff --git a/Engine/GDIPlusManager.cpp b/Engine/GDIPlusManager.cpp new file mode 100644 index 00000000..897401a8 --- /dev/null +++ b/Engine/GDIPlusManager.cpp @@ -0,0 +1,42 @@ +// gdiplus needs a lot of dumb windows shit +// enable that shit for this translation unit only#define FULL_WINTARD +#define FULL_WINTARD +#include "ChiliWin.h" +#include "GDIPlusManager.h" +// gdiplus needs min/max, but we disable that (even in +// full wintard mode), so we need to inject min/max into +// the Gdiplus namespace +#include +namespace Gdiplus +{ + using std::min; + using std::max; +} +#include + +// we need to link to the gdip library somewhere +// might as well be here +#pragma comment( lib,"gdiplus.lib" ) + +ULONG_PTR GDIPlusManager::token = 0; +int GDIPlusManager::refCount = 0; + +GDIPlusManager::GDIPlusManager() +{ + if (refCount++ == 0) + { + Gdiplus::GdiplusStartupInput input; + input.GdiplusVersion = 1; + input.DebugEventCallback = nullptr; + input.SuppressBackgroundThread = false; + Gdiplus::GdiplusStartup(&token, &input, nullptr); + } +} + +GDIPlusManager::~GDIPlusManager() +{ + if (--refCount == 0) + { + Gdiplus::GdiplusShutdown(token); + } +} diff --git a/Engine/GDIPlusManager.h b/Engine/GDIPlusManager.h new file mode 100644 index 00000000..29cdf1bd --- /dev/null +++ b/Engine/GDIPlusManager.h @@ -0,0 +1,36 @@ +/****************************************************************************************** +* Chili DirectX Framework Version 16.10.01 * +* GDIPlusManager.h * +* Copyright 2016 PlanetChili * +* * +* This file is part of The Chili DirectX Framework. * +* * +* The Chili DirectX Framework is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* The Chili DirectX Framework is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with The Chili DirectX Framework. If not, see . * +******************************************************************************************/ +#pragma once +#include "ChiliWin.h" + +class GDIPlusManager +{ +public: + // when a gdipman is ctored when no others exist, it will init gdip + GDIPlusManager(); + // when a gdipman is dtored and it is the last existing, it will cleanup gdip + ~GDIPlusManager(); +private: + // gdip creation token (for API calls) + static ULONG_PTR token; + // refcount to keep track of when necessary to init/cleanup + static int refCount; +}; \ No newline at end of file diff --git a/Engine/Game.cpp b/Engine/Game.cpp index dbdb5218..c4ef5bde 100644 --- a/Engine/Game.cpp +++ b/Engine/Game.cpp @@ -20,26 +20,383 @@ ******************************************************************************************/ #include "MainWindow.h" #include "Game.h" +#include "Mouse.h" +#include "CordinateTrasformerh.h" +#include "ChiliUtil.h" +#include -Game::Game( MainWindow& wnd ) +Game::Game(MainWindow& wnd) : - wnd( wnd ), - gfx( wnd ) -{ + wnd(wnd), + gfx(wnd), + ct(gfx), + cam(ct) + //, + //camCtrl(wnd.mouse,cam) + +{ } void Game::Go() { gfx.BeginFrame(); + ProcesInput(); UpdateModel(); ComposeFrame(); gfx.EndFrame(); } +void Game::ProcesInput() +{ + switch (wnd.ShapeState) + { + case MainWindow::MWShapeState::TwoPointCircle: + { + while (!wnd.mouse.IsEmpty()) + { + const auto e = wnd.mouse.Read(); + + if (e.GetType() == Mouse::Event::Type::LPress) + { + if (input == 0) + { + + first_point_engagement = true; + P = cam.TrasformPoint(wnd.mouse.GetPos()); + } + if (input == 1) + { + Q = wnd.mouse.GetPos(); + Q = cam.TrasformPoint(Q); + Shapes.push_back(std::make_unique(P, Q)); + //Shapes.push_back(std::make_unique(P, Q)); + } + + input++; + + if (input >= 2) + { + input = 0; + first_point_engagement = false; + } + } + if (e.GetType() == Mouse::Event::Type::RPress) + { + input = 0; + first_point_engagement = false; + } + } + if (first_point_engagement) + { + + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + cam.DrawCircle(P, GetDistanceTo(P, Q), 2, Colors::Red); + cam.DrawLine(P, Q, Colors::Red); + } + break; + } + case MainWindow::MWShapeState::ThreePointCircle: + { + while (!wnd.mouse.IsEmpty()) + { + const auto e = wnd.mouse.Read(); + + if (e.GetType() == Mouse::Event::Type::LPress) + { + if (input == 0) + { + first_point_engagement = true; + P = cam.TrasformPoint(wnd.mouse.GetPos()); + } + + if (input == 1) + { + second_point_engagement = true; + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + } + + if (input == 2) + { + R = cam.TrasformPoint(wnd.mouse.GetPos()); + if (P != Q && Q != R && !(LineSlopeBetween2Points(P, R) == LineSlopeBetween2Points(R, Q))) + Shapes.push_back(std::make_unique(P, Q, R)); + } + + input++; + + if (input >= 3) + { + input = 0; + second_point_engagement = false; + } + } + if (e.GetType() == Mouse::Event::Type::RPress) + { + input = 0; + first_point_engagement = false; + second_point_engagement = false; + } + } + //Draw circle from two points when left mouse button is pressed for the first time + if (first_point_engagement) + { + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + auto Temp = GetMidPoint(P, Q); + cam.DrawCircle(Temp, GetDistanceTo(Temp, Q), 2, Colors::Red); + } + if (second_point_engagement) + { + first_point_engagement = false; + R = cam.TrasformPoint(wnd.mouse.GetPos()); + if (P == Q) + { + input = 0; + second_point_engagement = false; + } + else if (Q != R && !(LineSlopeBetween2Points(P, R) == LineSlopeBetween2Points(R, Q))) + { + auto Temp = CalculateCentre(P, Q, R); + cam.DrawCircle(Temp, GetDistanceTo(Temp, R), 2, Colors::Red); + cam.DrawCircle(P, 10, 1, Colors::Red); + cam.DrawCircle(Q, 10, 1, Colors::Red); + cam.DrawCircle(R, 10, 1, Colors::Red); + //cam.DrawLine(CalculateCentre(P, Q, R), R, Colors::Red); + } + + } + break; + } + case MainWindow::MWShapeState::LineSegment: + { + while (!wnd.mouse.IsEmpty()) + { + const auto e = wnd.mouse.Read(); + + if (e.GetType() == Mouse::Event::Type::LPress) + { + if (input == 0) + { + first_point_engagement = true; + P = cam.TrasformPoint(wnd.mouse.GetPos()); + } + if (input == 1) + { + first_point_engagement = false; + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + Shapes.push_back(std::make_unique(P, Q)); + } + + input++; + + if (input >= 2) + { + input = 0; + first_point_engagement = false; + } + } + if (e.GetType() == Mouse::Event::Type::RPress) + { + + first_point_engagement = false; + } + } + if (first_point_engagement) + { + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + cam.DrawLine(P, Q, Colors::Red); + } + break; + } + case MainWindow::MWShapeState::PoliLine: + { + while (!wnd.mouse.IsEmpty()) + { + const auto e = wnd.mouse.Read(); + + if (e.GetType() == Mouse::Event::Type::LPress) + { + if(input==0) + first_point_engagement = true; + if (input>0) + { + first_point_engagement = false; + second_point_engagement = true; + } + + P = cam.TrasformPoint(wnd.mouse.GetPos()); + point_data.push_back(P); + input++; + + + } + if (second_point_engagement) + { + if (e.GetType() == Mouse::Event::Type::RPress) + { + + if (point_data.size() > 1) + { + Shapes.push_back(std::make_unique(point_data)); + input = 0; + first_point_engagement = false; + second_point_engagement = false; + point_data.clear(); + + } + + } + + } + + } + if (first_point_engagement) + { + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + cam.DrawLine(P, Q, Colors::Red); + } + if (second_point_engagement) + { + cam.DrawPoliLine(point_data, Colors::Red); + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + cam.DrawLine(P, Q, Colors::Red); + + } + break; + + } + case MainWindow::MWShapeState::BezierCurve: + { + while (!wnd.mouse.IsEmpty()) + { + const auto e = wnd.mouse.Read(); + + if (e.GetType() == Mouse::Event::Type::LPress) + { + if (input == 0) + { + first_point_engagement = true; + P = cam.TrasformPoint(wnd.mouse.GetPos()); + } + + if (input == 1) + { + second_point_engagement = true; + R = cam.TrasformPoint(wnd.mouse.GetPos()); + } + + if (input == 2) + { + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + Shapes.push_back(std::make_unique(P, Q, R)); + } + + input++; + + if (input >= 3) + { + input = 0; + second_point_engagement = false; + } + } + if (e.GetType() == Mouse::Event::Type::RPress) + { + input = 0; + first_point_engagement = false; + second_point_engagement = false; + } + } + //Draw bezier from two points when left mouse button is pressed for the first time + if (first_point_engagement) + { + R = cam.TrasformPoint(wnd.mouse.GetPos()); + cam.DrawLine(P,R, Colors::Red); + } + if (second_point_engagement) + { + first_point_engagement = false; + Q = cam.TrasformPoint(wnd.mouse.GetPos()); + + cam.DrawBezier(P,Q, R ,Colors::Red); + + } + break; + } + case MainWindow::MWShapeState::Null: + { + while (!wnd.mouse.IsEmpty()) + { + const auto e = wnd.mouse.Read(); + + if (e.GetType() == Mouse::Event::Type::LPress) + { + for (auto& i : Shapes) + { + if (wnd.kbd.KeyIsPressed(VK_SHIFT)) + { + if (i.get()->IsInRange(static_cast(cam.TrasformPoint(wnd.mouse.GetPos())))) + i.get()->SetSelectionFlag(false); + } + else + { + if (i.get()->IsInRange(static_cast(cam.TrasformPoint(wnd.mouse.GetPos())))) + i.get()->SetSelectionFlag(true); + } + } + } + } + break; + } + break; + } + + if (wnd.kbd.KeyIsPressed(VK_ESCAPE)) + { + + wnd.ShapeState = MainWindow::MWShapeState::Null; + + input = 0; + first_point_engagement = false; + second_point_engagement = false; + + for (auto &c : Shapes) + { + c.get()->SetSelectionFlag(false); + } + } + + if (wnd.kbd.KeyIsPressed(VK_DELETE)) + { + remove_erase_if(Shapes, std::mem_fn(&JC_Shape::ReadyForRemoval)); + } + + if (wnd.kbd.KeyIsPressed(VK_RETURN)) + { + for (auto& i : Shapes) + { + if (i.get()->IsSelected()) + { + wnd.ShowMessageBox(L"Description", L"Only single shape wil be show \n\n " + i.get()->MakeDescription()); + break; + } + } + + } +} + void Game::UpdateModel() { + for (auto &c : Shapes) + { + c.get()->UpdateColor(); + } } void Game::ComposeFrame() { + for (auto &c : Shapes) + { + c.get()->Draw(cam); + } + } + diff --git a/Engine/Game.h b/Engine/Game.h index 1702e223..226afe75 100644 --- a/Engine/Game.h +++ b/Engine/Game.h @@ -20,9 +20,23 @@ ******************************************************************************************/ #pragma once +#include + #include "Keyboard.h" #include "Mouse.h" #include "Graphics.h" +#include "CordinateTrasformerh.h" +#include "Camera.h" +#include "MouseCameraControler.h" + +#include "JC_Shape.h" +#include "JC_Vector2.h" +#include "JC_Math.h" +#include "JC_Circle.h" +#include "JC_Line.h" +#include "JC_Poli.h" +#include "ALIB_Bezier.h" + class Game { @@ -31,16 +45,48 @@ class Game Game( const Game& ) = delete; Game& operator=( const Game& ) = delete; void Go(); + + + private: void ComposeFrame(); void UpdateModel(); + void ProcesInput(); /********************************/ /* User Functions */ + + + /********************************/ private: MainWindow& wnd; Graphics gfx; /********************************/ /* User Variables */ + CoordinateTrasformer ct; + Camera cam; + //MouseCameraController camCtrl; + std::vector> Shapes; + unsigned short input = 0; + + bool first_point_engagement = false; + bool second_point_engagement = false; + + JC_Point2d P, Q, R; + std::vector point_data; + /********************************/ -}; \ No newline at end of file + +}; + + +//enum class GameCreationState + //{ + // Null, + // FirstPoint, + // SecondPoint, + // ThirdPoint, + // NextPoint, + // Count + //}; + //GameCreationState CreoState = GameCreationState::Null; \ No newline at end of file diff --git a/Engine/Graphics.cpp b/Engine/Graphics.cpp index bdff4ebf..d0cfe7f5 100644 --- a/Engine/Graphics.cpp +++ b/Engine/Graphics.cpp @@ -26,6 +26,9 @@ #include #include +#include "ChiliRectangle.h" + + // Ignore the intellisense error "cannot open source file" for .shh files. // They will be created during the build sequence before the preprocessor runs. namespace FramebufferShaders @@ -34,6 +37,21 @@ namespace FramebufferShaders #include "FramebufferVS.shh" } +Color Graphics::GetPixel(int x, int y) const +{ + assert(x >= 0); + assert(x < int(Graphics::ScreenWidth)); + assert(y >= 0); + assert(y < int(Graphics::ScreenHeight)); + return pSysBuffer[Graphics::ScreenWidth * y + x]; +} + +RectI Graphics::GetScreenRect() +{ + return{ 0,ScreenWidth,0,ScreenHeight }; +} + + #pragma comment( lib,"d3d11.lib" ) #define CHILI_GFX_EXCEPTION( hr,note ) Graphics::Exception( hr,note,_CRT_WIDE(__FILE__),__LINE__ ) @@ -201,6 +219,7 @@ Graphics::Graphics( HWNDKey& key ) throw CHILI_GFX_EXCEPTION( hr,L"Creating vertex buffer" ); } + ////////////////////////////////////////// // create input layout for fullscreen quad @@ -317,6 +336,258 @@ void Graphics::PutPixel( int x,int y,Color c ) } +// line with per pixel clipping +void Graphics::DrawLine(double x1, double y1, double x2, double y2, Color c) +{ + const double dx = x2 - x1; + const double dy = y2 - y1; + + if (dx == 0.0f && dy == 0.0f) + { + if (x1 >= 0 && x1 < ScreenWidth && y1 >= 0 && y1 < ScreenHeight) + PutPixel((int)x1, (int)y1, Colors::Red); + } + else if (abs(dx)>abs(dy)) + { + if (dx < 0.0f) + { + std::swap(x1, x2); + std::swap(y1, y2); + } + + double m = dy / dx; + double b = y1 - m * x1; + + for (double x = x1; x <= x2; x++) + { + + double y = m * x + b; + // issue! why there is need for substraction of one ? + if (x >= 0 && x < ScreenWidth-1 && y >= 0 && y < ScreenHeight-1) + PutPixel((int)(x + 0.5), (int)(y + 0.5), c); + + } + } + else + { + if (dy < 0.0f) + { + std::swap(x1, x2); + std::swap(y1, y2); + } + + double m = dx / dy; + double b = x1 - m * y1; + + for (double y = y1; y <= y2; y++) + { + double x = m * y + b; + // issue! why there is need for substraction of one ? + if (x >= 0 && x < ScreenWidth-1 && y >= 0 && y < ScreenHeight-1) + PutPixel((int)(x + 0.5), (int)(y + 0.5), c); + } + } +} + +void Graphics::DrawPoliLine(std::vector point_data, Color Color_in) +{ + JC_Point2d Current; + JC_Point2d Previous; + + + if (point_data.size() > 1) + { + for (int i = 1; i < point_data.size(); i++) + { + Current = point_data[i]; + Previous = point_data[i - 1]; + DrawLine(Previous, Current, Color_in); + } + } + else + { + + } + +} + + +void Graphics::DrawCircle(double _ox, double _oy, double _outer_radius, const CRectangle& _clip, int t,Color C) noexcept +{// For outline thickness of 1 + const auto rSq_inner = Square(_outer_radius - t); + const auto rSq_outer = Square(_outer_radius); + + const auto outer_double = _outer_radius * 2.0; + + // Calculate the bounding rectangle of the circle + const auto left = _ox - _outer_radius; + const auto top = _oy - _outer_radius; + const auto right = _ox + _outer_radius; + const auto bottom = _oy + _outer_radius; + + // Clip the bounding rectangle to screen boundaries and translate + // back to -radius ( left_clip, top_clip ), +radius ( right_clip, bottom_clip ) + + const auto left_clip = std::max(0.0, -left) - _outer_radius; + const auto top_clip = std::max(0.0, -top) - _outer_radius; + const auto right_clip = std::min(ScreenWidth - left -1.0, outer_double) - _outer_radius; + const auto bottom_clip = std::min(ScreenHeight - top - 1.0, outer_double) - _outer_radius; + + // Loop through clipped bounding rectangle, from top to bottom, + // left to right skipping any pixels contained in the _clip Rect passed + // as parameter to the function + for (double y = top_clip; y < bottom_clip; ++y) + { + for (double x = left_clip; x < right_clip; ++x) + { + const auto sqDist = Square(x) + Square(y); + if (sqDist > rSq_inner && sqDist < rSq_outer) + { + const auto px = x + _ox; + const auto py = y + _oy; + + if (!_clip.Contains(JC_Vector2{ px, py })) + { + PutPixel(int(std::round(px)), int(std::round(py)), C); + } + } + } + } +} + + +void Graphics::DrawBezier(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R, Color color) noexcept +{ + const auto range0 = (Q - P); + const auto range1 = (R - Q); + const auto range2 = (range1 - range0); + const auto doubleRange0 = (range0 * 2.0); + + constexpr auto step = .01; + auto prev = P; + for (double t = step; t <= 1.0; t += step) + { + const auto S = P + (doubleRange0 + (range2 * t)) * t; + + DrawLine(prev, S, color); + prev = S; + } +} + + +void Graphics::DrawBezier(std::vector point_data, Color color) noexcept +{ +/* const auto range0 = (Q - P); + const auto range1 = (R - Q); + const auto range2 = (range1 - range0); + const auto doubleRange0 = (range0 * 2.0); + + constexpr auto step = .1; + auto prev = P; + for (double t = step; t <= 1.0; t += step) + { + const auto S = P + (doubleRange0 + (range2 * t)) * t; + + DrawLine(prev, S, color); + prev = S; + } +*/ +} + + +/* +void Graphics::DrawCircle(double Ox, double Oy, double R, Color& c) +{ + //issue with continuity drawing for large circles + + for (double theta = 0; theta < 360; theta += 0.2) + { + double x = (double)(R * std::cos(PI_D*theta / 180)); + double y = (double)(R * std::sin(PI_D*theta / 180)); + + int xi = (int)(x + 0.5f + Ox); + int yi = (int)(y + 0.5f + Oy); + + if (xi >= 0 && xi < ScreenWidth && yi >= 0 && yi < ScreenHeight) + PutPixel(xi, yi, c); + } + + + + double x = 0.7071067811865475; + + double Rx = (x * R) + 0.5; + + double radsqr = R * R; + + //draw Circle with per pixel clipping + + for (int xi = 0; xi <= (int)Rx; xi++) + { + int yi = (int)(std::sqrt(radsqr - (xi*xi)) + 0.5f); + + + if (Ox + xi >= 0 && Ox + xi < ScreenWidth - 1 && Oy + yi >= 0 && Oy + yi < ScreenHeight-1) + PutPixel((int)Ox + xi, (int)Oy + yi, c); + + if (Ox + yi >= 0 && Ox + yi < ScreenWidth - 1 && Oy + xi >= 0 && Oy + xi < ScreenHeight-1) + PutPixel((int)Ox + yi, (int)Oy + xi, c); + + if (Ox -xi >= 0 && Ox -xi < ScreenWidth - 1 && Oy + yi >= 0 && Oy + yi < ScreenHeight-1) + PutPixel((int)Ox - xi, (int)Oy + yi, c); + + if (Ox -yi >= 0 && Ox -yi < ScreenWidth - 1 && Oy + xi >= 0 && Oy + xi < ScreenHeight-1) + PutPixel((int)Ox - yi, (int)Oy + xi, c); + + if (Ox -xi >= 0 && Ox -xi < ScreenWidth - 1 && Oy -yi >= 0 && Oy -yi < ScreenHeight-1) + PutPixel((int)Ox - xi, (int)Oy - yi, c); + + if (Ox -yi >= 0 && Ox-yi < ScreenWidth - 1 && Oy -xi >= 0 && Oy -xi < ScreenHeight-1) + PutPixel((int)Ox - yi, (int)Oy - xi, c); + + if (Ox + xi >= 0 && Ox + xi < ScreenWidth-1 && Oy -yi >= 0 && Oy -yi < ScreenHeight-1) + PutPixel((int)Ox + xi, (int)Oy - yi, c); + + if (Ox + yi >= 0 && Ox + yi < ScreenWidth-1 && Oy -xi >= 0 && Oy -xi < ScreenHeight-1) + PutPixel((int)Ox + yi, (int)Oy - xi, c); + } + + +} +*/ + + + +/* +void Graphics::DrawArc(double Ox, double Oy, double R , double theta_begin, double theta_end, Color c) +{ + + bool theta_range = theta_end - theta_begin > 0.0; + + + if (theta_begin == theta_end) + { + DrawCircle(Ox, Oy ,R,c); + } + else + { + for (double theta = theta_begin; + theta_range ? theta < theta_end : theta > theta_end; + theta_range ? theta += 0.2 : theta -= 0.2) + { + double x = (double)(R * std::cos(PI_F*theta / 180)); + double y = (double)(R * std::sin(PI_F*theta / 180)); + + + //Draw arc + PutPixel((int)(x + 0.5f + Ox), (int)(y + 0.5f + Oy), c); + } + } +} +*/ + + + ////////////////////////////////////////////////// // Graphics Exception Graphics::Exception::Exception( HRESULT hr,const std::wstring& note,const wchar_t* file,unsigned int line ) diff --git a/Engine/Graphics.h b/Engine/Graphics.h index 9a44e3fe..0392bfcf 100644 --- a/Engine/Graphics.h +++ b/Engine/Graphics.h @@ -19,11 +19,18 @@ * along with The Chili DirectX Framework. If not, see . * ******************************************************************************************/ #pragma once -#include "ChiliWin.h" #include #include +#include +#include + +#include "ChiliWin.h" #include "ChiliException.h" #include "Colors.h" +#include "JC_Vector2.h" +#include "JC_Math.h" +#include "ChiliRectangle.h" +#include "Surface.h" class Graphics { @@ -52,11 +59,152 @@ class Graphics Graphics& operator=( const Graphics& ) = delete; void EndFrame(); void BeginFrame(); + + Color GetPixel(int x, int y) const; + void PutPixel( int x,int y,int r,int g,int b ) { PutPixel( x,y,{ unsigned char( r ),unsigned char( g ),unsigned char( b ) } ); } void PutPixel( int x,int y,Color c ); + + + /***** Start Draw Line Functions *****/ + + + void DrawLine(const JC_Point2d& P, const JC_Point2d& Q, Color c) + { + DrawLine(P.x, P.y, Q.x, Q.y, c); + } + void DrawLine(double x1, double y1, double x2, double y2, Color c); + + void DrawPoliLine(std::vector point_data, Color Color_in); + + /***** END Draw Line Functions ****/ + + + + /***** Start Draw Circle Functions *****/ + + template + void DrawCircle(JC_Point2 vO, T2 R, int t , Color c) + { + RectD Bound{ (double)ScreenWidth ,0,(double)ScreenHeight,0 }; + DrawCircle((double)vO.x, (double)vO.y, (double)R, Bound, t, c); + } + void DrawCircle(double _ox, double _oy, double _outer_radius, const CRectangle& _clip, int t, Color _color) noexcept; + + //void DrawCircle(double Ox, double Oy, double R, Color& c); + + /***** END Draw Circle Functions ****/ + + + + + void DrawBezier(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R, Color color) noexcept; + void DrawBezier(const std::vector point_data, Color color) noexcept; + + + + + /***** Start Draw Arc Functions *****/ + + //void DrawArc(double Ox, double Oy, double R, double theta_begin, double theta_end, Color c); + + /***** End Draw Arc Functions *****/ + + + template + void DrawSprite(int x, int y, const Surface& s, E effect, bool reversed = false) + { + DrawSprite(x, y, s.GetRect(), s, effect, reversed); + } + template + void DrawSprite(int x, int y, const RectI& srcRect, const Surface& s, E effect, bool reversed = false) + { + DrawSprite(x, y, srcRect, GetScreenRect(), s, effect, reversed); + } + template + void DrawSprite(int x, int y, RectI srcRect, const RectI& clip, const Surface& s, E effect, bool reversed = false) + { + assert(srcRect.left >= 0); + assert(srcRect.right <= s.GetWidth()); + assert(srcRect.top >= 0); + assert(srcRect.bottom <= s.GetHeight()); + + // mirror in x depending on reversed bool switch + if (!reversed) + { + // cliping is different depending on mirroring status + if (x < clip.left) + { + srcRect.left += clip.left - x; + x = clip.left; + } + if (y < clip.top) + { + srcRect.top += clip.top - y; + y = clip.top; + } + if (x + srcRect.GetWidth() > clip.right) + { + srcRect.right -= x + srcRect.GetWidth() - clip.right; + } + if (y + srcRect.GetHeight() > clip.bottom) + { + srcRect.bottom -= y + srcRect.GetHeight() - clip.bottom; + } + for (int sy = srcRect.top; sy < srcRect.bottom; sy++) + { + for (int sx = srcRect.left; sx < srcRect.right; sx++) + { + effect( + // no mirroring + s.GetPixel(sx, sy), + x + sx - srcRect.left, + y + sy - srcRect.top, + *this + ); + } + } + } + else + { + if (x < clip.left) + { + srcRect.right -= clip.left - x; + x = clip.left; + } + if (y < clip.top) + { + srcRect.top += clip.top - y; + y = clip.top; + } + if (x + srcRect.GetWidth() > clip.right) + { + srcRect.left += x + srcRect.GetWidth() - clip.right; + } + if (y + srcRect.GetHeight() > clip.bottom) + { + srcRect.bottom -= y + srcRect.GetHeight() - clip.bottom; + } + const int xOffset = srcRect.left + srcRect.right - 1; + for (int sy = srcRect.top; sy < srcRect.bottom; sy++) + { + for (int sx = srcRect.left; sx < srcRect.right; sx++) + { + effect( + // mirror in x + s.GetPixel(xOffset - sx, sy), + x + sx - srcRect.left, + y + sy - srcRect.top, + *this + ); + } + } + } + } + ~Graphics(); private: Microsoft::WRL::ComPtr pSwapChain; @@ -73,6 +221,8 @@ class Graphics D3D11_MAPPED_SUBRESOURCE mappedSysBufferTexture; Color* pSysBuffer = nullptr; public: - static constexpr int ScreenWidth = 800; - static constexpr int ScreenHeight = 600; + static constexpr int ScreenWidth =1440; + static constexpr int ScreenHeight = 900; + static RectI GetScreenRect(); + }; \ No newline at end of file diff --git a/Engine/JC_Circle.cpp b/Engine/JC_Circle.cpp new file mode 100644 index 00000000..8a64b25c --- /dev/null +++ b/Engine/JC_Circle.cpp @@ -0,0 +1,32 @@ +#include "JC_Circle.h" + +JC_Circle::JC_Circle(const JC_Point2d& P_in, const JC_Point2d& Q_in, Color color_in) + : + JC_Shape(color_in), + O(P_in), + radius(GetDistanceTo(P_in, Q_in)) + +{} + + +JC_Circle::JC_Circle(const JC_Point2d& P_in, const JC_Point2d& Q_in, const JC_Point2d& R_in, Color color_in) + : + JC_Shape(color_in), + O(CalculateCentre(P_in, Q_in, R_in)), + radius(GetDistanceTo(O, R_in)) + +{} + + +void JC_Circle::Draw(Camera cam_in) +{ + cam_in.DrawCircle(O, radius, 2, Base_Color); +} + + +bool JC_Circle::IsInRange(const JC_Point2d& mouse_in) +{ + double distance = GetDistanceTo(O, mouse_in); + return (distance <= (radius + halfwidth) && + distance >= (radius - halfwidth)); +} diff --git a/Engine/JC_Circle.h b/Engine/JC_Circle.h new file mode 100644 index 00000000..f83727d9 --- /dev/null +++ b/Engine/JC_Circle.h @@ -0,0 +1,40 @@ +#pragma once + + +#include "JC_Shape.h" + + + + + +class JC_Circle : public JC_Shape +{ +public: + + JC_Circle(const JC_Point2d& P_in, const JC_Point2d& Q_in, Color color_in = Colors::White); + + //Constructor which is responsible for creating circle using three points + JC_Circle(const JC_Point2d& P_in, const JC_Point2d& Q_in, const JC_Point2d& R_in, Color color_in = Colors::White); + + void Draw(Camera cam_in) override; + bool IsInRange(const JC_Point2d& mouse_in) override; + + + std::wstring MakeDescription() override + { + + std::wstring PosO = L"X: " + std::to_wstring(O.x) + L"\n Y: " + std::to_wstring(O.y); + std::wstring Radius = std::to_wstring(radius); + std::wstring Diameter = std::to_wstring(radius*2 * PI_D); + std::wstring Area = std::to_wstring( radius * radius * PI_D); + + return std::wstring(L"Centre:\n " + PosO + L"\n\nRadius: " + Radius + L"\n\nDiameter: " + Diameter + L"\n\nArea: " + Area); + + } + +private: + JC_Point2d O; + double radius; + + +}; \ No newline at end of file diff --git a/Engine/JC_Line.h b/Engine/JC_Line.h new file mode 100644 index 00000000..7e31f15b --- /dev/null +++ b/Engine/JC_Line.h @@ -0,0 +1,49 @@ +#pragma once + +#include "JC_Shape.h" +#include "Camera.h" + +class JC_Line : public JC_Shape +{ +public: + JC_Line(const JC_Point2d& P_in, const JC_Point2d& Q_in, Color color_in = Colors::White) + : + P(P_in), + Q(Q_in), + JC_Shape(color_in) + {} + + void Draw(Camera cam) override + { + cam.DrawLine(P, Q, Base_Color); + } + bool IsInRange(const JC_Point2d& M) override + { + JC_Point2d C = ClosestPoint(P, Q, M); + + if (IsBetween2Points(P, Q, C)) + { + if ((abs(GetDistanceTo(M,C)) < halfwidth)) + return true; + + else + return false; + } + else + return false; + } + std::wstring MakeDescription() override + { + + std::wstring PosP = std::to_wstring(P.x) + L" " + std::to_wstring(P.y); + std::wstring PosQ = std::to_wstring(Q.x) + L" " + std::to_wstring(Q.y); + std::wstring Lenght = std::to_wstring(GetDistanceTo(P,Q)); + + return std::wstring(L"Start:\n" + PosP + L"\n\n" + L" End \n " + PosQ + L"\n\n" + L"Lenght: " + Lenght); + + } + +private: + JC_Point2d P, Q; //Start and end points of the line + +}; \ No newline at end of file diff --git a/Engine/JC_MP_Bezier.h b/Engine/JC_MP_Bezier.h new file mode 100644 index 00000000..f953c43a --- /dev/null +++ b/Engine/JC_MP_Bezier.h @@ -0,0 +1,40 @@ +#pragma once + +#include "JC_Shape.h" + + + + +// Atempt to write multi point bezier curve + + + +class JC_MP_Bezier : public JC_Shape +{ +public: + JC_MP_Bezier(std::vector &line, Color color_in = Colors::White) + : + point_data(line), + JC_Shape(color_in) + {} + + void Draw(Camera cam) override + { + cam.DrawMPBezier(point_data, Base_Color); + } + bool IsInRange(const JC_Point2d& M) override + { + return false; + } + + std::wstring MakeDescription() override + { + + + return std::wstring(L"Not yet operatable"); + + } + +private: + std::vector point_data; +}; \ No newline at end of file diff --git a/Engine/JC_Math.cpp b/Engine/JC_Math.cpp new file mode 100644 index 00000000..1118fba5 --- /dev/null +++ b/Engine/JC_Math.cpp @@ -0,0 +1,124 @@ +#include "JC_Math.h" + + +int CalcFactorial(int n) +{ + if (n > 1) + return n * CalcFactorial(n - 1); + else + return 1; +} + +int Binomial_Make(int N, int K) +{ + int f_N = CalcFactorial(N); + int f_K = CalcFactorial(K); + int f_NK = CalcFactorial(N - K); + + return (f_N / (f_K * f_NK)); +} + +JC_Point2d CalculateCentre(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R) +{ + //when we have 2 flat lines in order under right angle + if (std::abs(P.x) == std::abs(R.x) && std::abs(Q.y) == std::abs(R.y)) + { + JC_Point2d C; + C.x = (R.x + P.x) / 2; + C.y = (Q.y + R.y) / 2; + return C; + } + //when we have 2 flat lines in reverce order under right angle + else if (std::abs(P.y) == std::abs(R.y) && std::abs(Q.x) == std::abs(R.x)) + { + JC_Point2d C; + C.x = R.x + Q.x / 2; + C.y = P.y + R.y / 2; + return C; + } + else if (std::abs(P.x) == std::abs(R.x) && std::abs(Q.y) != std::abs(R.y) || std::abs(P.y) == std::abs(R.y) && std::abs(Q.x) != std::abs(R.x)) + { + JC_Point2d C = CalculateSpecificCentre(P, R, Q); + return C; + } + else if (std::abs(P.x) != std::abs(R.x) && std::abs(Q.y) == std::abs(R.y) || std::abs(P.y) == std::abs(R.y) && std::abs(Q.x) != std::abs(R.x)) + { + JC_Point2d C = CalculateSpecificCentre(R, Q, P); + return C; + } + else + { + JC_Point2d C = CalculateSpecificCentre(P, Q, R); + return C; + + } + +} + +JC_Point2d CalculateSpecificCentre(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R) +{ + + + double m1 = LineSlopeBetween2Points(P, R); + double m2 = LineSlopeBetween2Points(Q, R); + + //If slopes are the same lines are parallel ,do nothing (infinite radius) + assert(m2 - m1 != 0.0); + + // calculate perpendicular line slope... + + double m1_perp = InverceLineSlope(m1); + double m2_perp = InverceLineSlope(m2); + + //mid point of orginally inserted lines + + JC_Point2d mid_PR = GetMidPoint(P, R); + + JC_Point2d mid_QR = GetMidPoint(Q, R); + + + // y=mx+b find b part of perpendicular line + // y-y1 = m(x-x1) = > y = mx - m*x1 + y1 + //https://www.varsitytutors.com/hotmath/hotmath_help/topics/point-slope-form.html + + double a = (-m1_perp * mid_PR.x + mid_PR.y); + double b = (-m2_perp * mid_QR.x + mid_QR.y); + + // find crossection + //http://www.ambrsoft.com/MathCalc/Line/TwoLinesIntersection/TwoLinesIntersection.htm + + JC_Point2d C; + C.x = (a - b) / (m2_perp - m1_perp); + C.y = ((m1_perp * b) - (m2_perp * a)) / (m1_perp - m2_perp); + + return C; + +} + +JC_Point2d ClosestPoint(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R) +{ + const double a = P.y - Q.y; + const double b = Q.x - P.x; + const double c = P.x * Q.y - Q.x * P.y; + + JC_Point2d C; + + C.x = (b * (b * R.x - (a * R.y)) - (a * c)) / (Square(a) + Square(b)); + C.y = (a * ((-b * R.x )+ ( a * R.y)) - (b * c)) / (Square(a) + Square(b)); + + + return C; +} + + + +bool IsBetween2Points(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R) +{ + JC_Point2d C = ClosestPoint(P, Q, R); + + if ((GetDistanceTo(P, C) < GetDistanceTo(P, Q)) && (GetDistanceTo(Q, C) < GetDistanceTo(P, Q))) + return true; + else + return false; + +} diff --git a/Engine/JC_Math.h b/Engine/JC_Math.h new file mode 100644 index 00000000..b9f9e245 --- /dev/null +++ b/Engine/JC_Math.h @@ -0,0 +1,79 @@ +#pragma once + +#include "JC_Vector2.h" +#include +#include + +constexpr double PI_F = 3.1415926536; +constexpr double PI_D = 3.1415926535897932; + + + + template + inline auto Square(const T& x) + { + return x * x; + } + + + inline float Slope(const float x1, const float y1, const float x2, const float y2) + { + float dx = x2 - x1; + float dy = y2 - y1; + + float m = dy / dx; + return m; + + } + + inline double LineSlopeBetween2Points(const JC_Point2d& P, const JC_Point2d& Q) + { + double dx = Q.x - P.x; + double dy = Q.y - P.y; + + //assert(dx != 0.0); + + double m = dy / dx; + + return m; + + } + + inline double InverceLineSlope(const double& m) + { + //assert(m != 0.0f); + + return -1 / m; + } + + + int CalcFactorial(int n); + + int Binomial_Make(int N, int K); + + + + // We have couple of types of centre calculation: + // When we can Determine slope of both lines, then we proces points in seen order. + // When we can't determine slope of any of lines (parallel to the window sides) + // in this situation we are calculating centre using line mid point rule + // When we are unable to determine solope of one the lines + // in this situation we are simply swaping order of the points. + JC_Point2d CalculateCentre(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R); + + + //Used to calculate centre from points from which created lines have caclulatable slope (at least one line) + JC_Point2d CalculateSpecificCentre(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R); + + // First two points create line third we are exsamining + JC_Point2d ClosestPoint(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R); + + + + + bool IsBetween2Points(const JC_Point2d & P, const JC_Point2d & Q, const JC_Point2d & R); + + + + + diff --git a/Engine/JC_Poli.h b/Engine/JC_Poli.h new file mode 100644 index 00000000..9cc8be6a --- /dev/null +++ b/Engine/JC_Poli.h @@ -0,0 +1,47 @@ +#pragma once + +#include "JC_Shape.h" + +class JC_Poliline : public JC_Shape +{ +public: + JC_Poliline(std::vector point_data, Color color_in = Colors::White) + : + PointData(point_data), + JC_Shape(color_in) + {} + + void Draw(Camera cam) override + { + cam.DrawPoliLine(PointData, Base_Color); + } + + bool IsInRange(const JC_Point2d& M) override + { + JC_Point2d Current; + JC_Point2d Previous; + JC_Point2d C; + for (int i = 1 ; i < PointData.size(); i++) + { + Current = PointData[i]; + Previous = PointData[i - 1]; + C = ClosestPoint(Previous, Current, M); + + if (IsBetween2Points(Previous, Current, C)) + { + if ((abs(GetDistanceTo(M, C)) < halfwidth)) + { + return true; + } + } + } + return false; + } + std::wstring MakeDescription() override + { + return std::wstring(L"Not yet operatable"); + } + +private: + std::vector PointData; +}; \ No newline at end of file diff --git a/Engine/JC_Shape.h b/Engine/JC_Shape.h new file mode 100644 index 00000000..a8a531c8 --- /dev/null +++ b/Engine/JC_Shape.h @@ -0,0 +1,51 @@ +#pragma once +#include "JC_Vector2.h" +#include "Camera.h" +class JC_Shape + +{ +public: + + + void virtual Draw(Camera cam) = 0; + bool virtual IsInRange(const JC_Point2d& M) = 0; + std::wstring virtual MakeDescription() = 0; + + void UpdateColor() + { + if (selectedflag) + Base_Color = Colors::Yellow; + else + Base_Color = Colors::White; + } + + void SetSelectionFlag(bool flag) + { + selectedflag = flag; + } + + + bool ReadyForRemoval() const + { + return selectedflag; + } + + bool IsSelected() const + { + return selectedflag; + } + +protected: + + JC_Shape( Color& color_in) + : + Base_Color(color_in) + {} + + Color Base_Color; + static constexpr double halfwidth = 20.0; + +private: + bool selectedflag = false; + +}; diff --git a/Engine/JC_Vector2.h b/Engine/JC_Vector2.h new file mode 100644 index 00000000..99b12cde --- /dev/null +++ b/Engine/JC_Vector2.h @@ -0,0 +1,248 @@ + + +#pragma once + +#include +#include + +template struct JC_Point2 +{ + JC_Point2() {}; + JC_Point2(T xin, T yin) + : + x(xin), + y(yin) + {} + + // point to point conversion equals operator + template + JC_Point2& operator=(const JC_Point2& other) + { + x = T(other.x); + y = T(other.y); + return *this; + } + + templateexplicit JC_Point2(const JC_Point2& other) + : + x(T(other.x)), y(T(other.y)) + {} + + T x, y; +}; +template struct JC_Vector2 +{ + JC_Vector2() {}; + JC_Vector2( T xin, T yin) + : + x(xin), + y(yin) + {} + + // vector to vector conversion equals operator + template + JC_Vector2& operator=(const JC_Vector2& other) + { + x = T(other.x); + y = T(other.y); + return *this; + } + + + //templateexplicit JC_Vector2(const JC_Vector2& other) + // : + // x(T(other.x)), y(T(other.y)) + //{} + + + T x, y; +}; + +using JC_Point2d = JC_Point2; +using JC_Point2f = JC_Point2; +using JC_Point2i = JC_Point2; + +using JC_Vector2d = JC_Vector2; +using JC_Vector2f = JC_Vector2; +using JC_Vector2i = JC_Vector2; + + + +template bool operator== (const JC_Point2& lhs, const JC_Point2& rhs) +{ + return ((lhs.x == rhs.x) && (lhs.y == rhs.y)); +} +template bool operator!= (const JC_Point2& lhs, const JC_Point2& rhs) +{ + return ((lhs.x != rhs.x) || (lhs.y != rhs.y)); +} + + + +template JC_Vector2& operator+=(JC_Vector2& lhs, const JC_Vector2& rhs) +{ + lhs.x += rhs.x; + lhs.y += rhs.y; + + return lhs; +} +template JC_Point2& operator+=(JC_Point2& lhs, const JC_Vector2& rhs) +{ + lhs.x += rhs.x; + lhs.y += rhs.y; + + return lhs; +} +template JC_Point2& operator+=(JC_Point2& lhs, const JC_Point2& rhs) +{ + lhs.x += rhs.x; + lhs.y += rhs.y; + + return lhs; +} + + +template JC_Vector2& operator-=(JC_Vector2& lhs, const JC_Vector2& rhs) +{ + lhs.x -= rhs.x; + lhs.y -= rhs.y; + + return lhs; +} +template JC_Point2& operator-=(JC_Point2& lhs, const JC_Vector2& rhs) +{ + lhs.x -= rhs.x; + lhs.y -= rhs.y; + + return lhs; +} +template JC_Point2& operator-=(JC_Point2& lhs, const JC_Point2& rhs) +{ + lhs.x -= rhs.x; + lhs.y -= rhs.y; + + return lhs; +} + +template JC_Vector2& operator*=(JC_Vector2& v, T s) +{ + v.x *= s; + v.y *= s; + return v; +} +template JC_Vector2& operator/=(JC_Vector2& v, T s) +{ + v.x /= s; + v.y /= s; + return v; +} +template JC_Vector2 operator+(const JC_Vector2& lhs, const JC_Vector2& rhs) +{ + return JC_Vector2(lhs) += rhs; +} +template JC_Point2 operator+(const JC_Point2& lhs, const JC_Vector2& rhs) +{ + return { lhs.x + rhs.x, lhs.y + rhs.y }; + +} +template T operator-(const T& lhs) +{ + return T(-lhs.x, -lhs.y); +} + +template JC_Vector2 operator-(const JC_Vector2& lhs, const JC_Vector2& rhs) +{ + return JC_Vector2(lhs) -= rhs; +} +template JC_Point2 operator-(const JC_Point2& lhs, const JC_Vector2& rhs) +{ + return JC_Point2(lhs) -= rhs; +} +template JC_Vector2 operator-(const JC_Point2& lhs, const JC_Point2& rhs) +{ + return { lhs.x - rhs.x, lhs.y - rhs.y }; +} +template JC_Vector2 operator*(const JC_Vector2& v, T s) +{ + return { v.x * s, v.y * s }; +} +template JC_Vector2 operator*(T s, const JC_Vector2& v) +{ + return JC_Vector2(v) *= s; +} +template JC_Vector2 operator/(const JC_Vector2& v, T s) +{ + return { v.x / s,v.y / s }; +} + + +template JC_Point2 operator*=(JC_Point2 lhs, T rhs) +{ + lhs.x *= rhs; + lhs.y *= rhs; + return lhs; +} +template JC_Point2 operator/=(JC_Point2 lhs, T rhs) +{ + lhs.x /= rhs; + lhs.y /= rhs; + return lhs; +} +template JC_Point2 operator*=(T lhs, JC_Point2 rhs) +{ + rhs.x *= lhs; + rhs.y *= lhs; + return rhs; +} +template JC_Point2 operator/=(T lhs , JC_Point2 rhs) +{ + rhs.x /= lhs; + rhs.y /= lhs; + return rhs; +} + +template T dot_product(const JC_Vector2& v0, const JC_Vector2& v1) +{ + return (v0.x * v1.x) + (v0.y + v1.y); +} +template T length_sq(const JC_Vector2& v) +{ + return dot_product(v, v); +} +template T length(const JC_Vector2& v) +{ + return T(std::sqrt(double(length_sq(v)))); +} +template JC_Vector2 normalize(const JC_Vector2& v) +{ + const auto len = length(v); + if (std::is_floating_point_v) + { + return (len == T(0)) ? v : v * (T(1) / len); + } + + return (len == 0) ? v : v / len; +} + + + + + +template T GetDistanceToSq(const JC_Point2& v0, const JC_Point2& v1) +{ + return ((v1.x - v0.x)*(v1.x - v0.x)+ (v1.y - v0.y)*(v1.y - v0.y)); +} +template T GetDistanceTo(const JC_Point2& v0, const JC_Point2& v1) +{ + return std::sqrt(GetDistanceToSq(v0, v1)); +} +template JC_Point2 GetMidPoint(const JC_Point2& v0, const JC_Point2& v1) +{ + auto v = v1 - v0; + v = (std::is_floating_point_v) ? + v * T(.5) : + v / T(2); + + return v0 + v; +} + diff --git a/Engine/MainWindow.cpp b/Engine/MainWindow.cpp index c2dc5f18..975caf25 100644 --- a/Engine/MainWindow.cpp +++ b/Engine/MainWindow.cpp @@ -45,8 +45,8 @@ MainWindow::MainWindow( HINSTANCE hInst,wchar_t * pArgs ) wr.right = Graphics::ScreenWidth + wr.left; wr.top = 100; wr.bottom = Graphics::ScreenHeight + wr.top; - AdjustWindowRect( &wr,WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,FALSE ); - hWnd = CreateWindow( wndClassName,L"Chili DirectX Framework", + AdjustWindowRect(&wr, WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, TRUE); + hWnd = CreateWindow( wndClassName,L"JCAD", WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU, wr.left,wr.top,wr.right - wr.left,wr.bottom - wr.top, nullptr,nullptr,hInst,this ); @@ -128,12 +128,90 @@ LRESULT WINAPI MainWindow::_HandleMsgThunk( HWND hWnd,UINT msg,WPARAM wParam,LPA return pWnd->HandleMsg( hWnd,msg,wParam,lParam ); } -LRESULT MainWindow::HandleMsg( HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam ) + +LRESULT MainWindow::HandleMsg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch( msg ) + switch (msg) + { + + case WM_CREATE: + { + HMENU hMenu, hSubMenu; + + hMenu = CreateMenu(); + + hSubMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hSubMenu, L"&File"); + { + AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, L"E&xit"); + } + + hSubMenu = CreatePopupMenu(); + + AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hSubMenu, L"&Shapes"); + { + AppendMenu(hSubMenu, MF_STRING, ID_SHAPES_TwoPointCircle, L"&Circle from 2 points"); + AppendMenu(hSubMenu, MF_SEPARATOR, NULL, NULL); + AppendMenu(hSubMenu, MF_STRING, ID_SHAPES_ThreePointCircle, L"&Circle from 3 points"); + AppendMenu(hSubMenu, MF_SEPARATOR, NULL, NULL); + AppendMenu(hSubMenu, MF_STRING, ID_SHAPES_LineSegment, L"&Line Segment"); + AppendMenu(hSubMenu, MF_SEPARATOR, NULL, NULL); + AppendMenu(hSubMenu, MF_STRING, ID_SHAPES_PoliLine, L"&PoliLine"); + AppendMenu(hSubMenu, MF_SEPARATOR, NULL, NULL); + AppendMenu(hSubMenu, MF_STRING, ID_SHAPES_BezierCurve, L"&Bezier Curve"); + + } + + + + SetMenu(hWnd, hMenu); + + break; + } + case WM_COMMAND: { + switch (LOWORD(wParam)) + { + case ID_FILE_EXIT: + + PostMessage(hWnd, WM_CLOSE, 0, 0); + break; + case ID_SHAPES_TwoPointCircle: + ShapeState = MWShapeState::TwoPointCircle; + ShowMessageBox(L"Info", L"Create circle form 2 points"); + + break; + + case ID_SHAPES_ThreePointCircle: + ShapeState = MWShapeState::ThreePointCircle; + ShowMessageBox(L"Info", L"Create circle form 3 points"); + + break; + + + case ID_SHAPES_LineSegment: + ShapeState = MWShapeState::LineSegment; + ShowMessageBox(L"Info", L"Create line Segment"); + + break; + + case ID_SHAPES_PoliLine: + ShapeState = MWShapeState::PoliLine; + ShowMessageBox(L"Info", L" Create PoliLine"); + + break; + + case ID_SHAPES_BezierCurve: + ShapeState = MWShapeState::BezierCurve; + ShowMessageBox(L"Info", L"Create 3 point Bezier Curve"); + + break; + + } + break; + } case WM_DESTROY: - PostQuitMessage( 0 ); + PostQuitMessage(0); break; case WM_KILLFOCUS: kbd.ClearState(); @@ -141,92 +219,109 @@ LRESULT MainWindow::HandleMsg( HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam ) // ************ KEYBOARD MESSAGES ************ // case WM_KEYDOWN: - if( !(lParam & 0x40000000) || kbd.AutorepeatIsEnabled() ) // no thank you on the autorepeat + if (!(lParam & 0x40000000) || kbd.AutorepeatIsEnabled()) // no thank you on the autorepeat { - kbd.OnKeyPressed( static_cast(wParam) ); + kbd.OnKeyPressed(static_cast(wParam)); } break; case WM_KEYUP: - kbd.OnKeyReleased( static_cast(wParam) ); + kbd.OnKeyReleased(static_cast(wParam)); break; case WM_CHAR: - kbd.OnChar( static_cast(wParam) ); + kbd.OnChar(static_cast(wParam)); break; // ************ END KEYBOARD MESSAGES ************ // // ************ MOUSE MESSAGES ************ // case WM_MOUSEMOVE: { - POINTS pt = MAKEPOINTS( lParam ); - if( pt.x > 0 && pt.x < Graphics::ScreenWidth && pt.y > 0 && pt.y < Graphics::ScreenHeight ) + POINTS pt = MAKEPOINTS(lParam); + if (pt.x > 0 && pt.x < Graphics::ScreenWidth && pt.y > 0 && pt.y < Graphics::ScreenHeight) { - mouse.OnMouseMove( pt.x,pt.y ); - if( !mouse.IsInWindow() ) + mouse.OnMouseMove(pt.x, pt.y); + if (!mouse.IsInWindow()) { - SetCapture( hWnd ); + SetCapture(hWnd); mouse.OnMouseEnter(); } } else { - if( wParam & (MK_LBUTTON | MK_RBUTTON) ) + if (wParam & (MK_LBUTTON | MK_RBUTTON)) { - pt.x = std::max( short( 0 ),pt.x ); - pt.x = std::min( short( Graphics::ScreenWidth - 1 ),pt.x ); - pt.y = std::max( short( 0 ),pt.y ); - pt.y = std::min( short( Graphics::ScreenHeight - 1 ),pt.y ); - mouse.OnMouseMove( pt.x,pt.y ); + pt.x = std::max(short(0), pt.x); + pt.x = std::min(short(Graphics::ScreenWidth - 1), pt.x); + pt.y = std::max(short(0), pt.y); + pt.y = std::min(short(Graphics::ScreenHeight - 1), pt.y); + mouse.OnMouseMove(pt.x, pt.y); } else { ReleaseCapture(); mouse.OnMouseLeave(); - mouse.OnLeftReleased( pt.x,pt.y ); - mouse.OnRightReleased( pt.x,pt.y ); + mouse.OnLeftReleased(pt.x, pt.y); + mouse.OnRightReleased(pt.x, pt.y); + mouse.OnWheelReleased(pt.x, pt.y); } } break; } case WM_LBUTTONDOWN: { - const POINTS pt = MAKEPOINTS( lParam ); - mouse.OnLeftPressed( pt.x,pt.y ); - SetForegroundWindow( hWnd ); + const POINTS pt = MAKEPOINTS(lParam); + mouse.OnLeftPressed(pt.x, pt.y); + SetForegroundWindow(hWnd); break; } case WM_RBUTTONDOWN: { - const POINTS pt = MAKEPOINTS( lParam ); - mouse.OnRightPressed( pt.x,pt.y ); + const POINTS pt = MAKEPOINTS(lParam); + mouse.OnRightPressed(pt.x, pt.y); break; } case WM_LBUTTONUP: { - const POINTS pt = MAKEPOINTS( lParam ); - mouse.OnLeftReleased( pt.x,pt.y ); + const POINTS pt = MAKEPOINTS(lParam); + mouse.OnLeftReleased(pt.x, pt.y); break; } case WM_RBUTTONUP: { - const POINTS pt = MAKEPOINTS( lParam ); - mouse.OnRightReleased( pt.x,pt.y ); + const POINTS pt = MAKEPOINTS(lParam); + mouse.OnRightReleased(pt.x, pt.y); break; } case WM_MOUSEWHEEL: { - const POINTS pt = MAKEPOINTS( lParam ); - if( GET_WHEEL_DELTA_WPARAM( wParam ) > 0 ) + const POINTS pt = MAKEPOINTS(lParam); + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { - mouse.OnWheelUp( pt.x,pt.y ); + mouse.OnWheelUp(pt.x, pt.y); } - else if( GET_WHEEL_DELTA_WPARAM( wParam ) < 0 ) + else if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) { - mouse.OnWheelDown( pt.x,pt.y ); + mouse.OnWheelDown(pt.x, pt.y); } break; } + case WM_MBUTTONDOWN: + { + const POINTS pt = MAKEPOINTS(lParam); + mouse.OnWheelPressed(pt.x, pt.y); + break; + } + case WM_MBUTTONUP: + { + const POINTS pt = MAKEPOINTS(lParam); + mouse.OnWheelReleased(pt.x, pt.y); + break; + } // ************ END MOUSE MESSAGES ************ // } - return DefWindowProc( hWnd,msg,wParam,lParam ); -} \ No newline at end of file + return DefWindowProc(hWnd, msg, wParam, lParam); +} + + + + diff --git a/Engine/MainWindow.h b/Engine/MainWindow.h index f45db5c4..026b0178 100644 --- a/Engine/MainWindow.h +++ b/Engine/MainWindow.h @@ -50,6 +50,18 @@ class MainWindow : public HWNDKey virtual std::wstring GetExceptionType() const override { return L"Windows Exception"; } }; public: + enum class MWShapeState + { + Null, + TwoPointCircle, + ThreePointCircle, + LineSegment, + PoliLine, + BezierCurve, + Count + }; + MWShapeState ShapeState = MWShapeState::Null; + MainWindow( HINSTANCE hInst,wchar_t* pArgs ); MainWindow( const MainWindow& ) = delete; MainWindow& operator=( const MainWindow& ) = delete; diff --git a/Engine/Mouse.cpp b/Engine/Mouse.cpp index 68d528d1..6d495067 100644 --- a/Engine/Mouse.cpp +++ b/Engine/Mouse.cpp @@ -21,7 +21,7 @@ #include "Mouse.h" -std::pair Mouse::GetPos() const +JC_Point2i Mouse::GetPos() const { return { x,y }; } @@ -46,6 +46,11 @@ bool Mouse::RightIsPressed() const return rightIsPressed; } +bool Mouse::WheelIsPressed() const +{ + return wheelIsPressed; +} + bool Mouse::IsInWindow() const { return isInWindow; @@ -121,6 +126,22 @@ void Mouse::OnRightReleased( int x,int y ) TrimBuffer(); } +void Mouse::OnWheelPressed(int x, int y) +{ + wheelIsPressed = true; + + buffer.push(Mouse::Event(Mouse::Event::Type::WheelPress, *this)); + TrimBuffer(); +} + +void Mouse::OnWheelReleased(int x, int y) +{ + wheelIsPressed = false; + + buffer.push(Mouse::Event(Mouse::Event::Type::WheelRelease, *this)); + TrimBuffer(); +} + void Mouse::OnWheelUp( int x,int y ) { buffer.push( Mouse::Event( Mouse::Event::Type::WheelUp,*this ) ); diff --git a/Engine/Mouse.h b/Engine/Mouse.h index 187939ef..8252a60a 100644 --- a/Engine/Mouse.h +++ b/Engine/Mouse.h @@ -20,6 +20,7 @@ ******************************************************************************************/ #pragma once #include +#include "JC_Vector2.h" class Mouse { @@ -34,6 +35,8 @@ class Mouse LRelease, RPress, RRelease, + WheelPress, + WheelRelease, WheelUp, WheelDown, Move, @@ -43,6 +46,7 @@ class Mouse Type type; bool leftIsPressed; bool rightIsPressed; + bool wheelIsPressed; int x; int y; public: @@ -51,6 +55,7 @@ class Mouse type( Type::Invalid ), leftIsPressed( false ), rightIsPressed( false ), + wheelIsPressed( false ), x( 0 ), y( 0 ) {} @@ -58,7 +63,8 @@ class Mouse : type( type ), leftIsPressed( parent.leftIsPressed ), - rightIsPressed( parent.rightIsPressed ), + rightIsPressed( parent.rightIsPressed), + wheelIsPressed( parent.wheelIsPressed), x( parent.x ), y( parent.y ) {} @@ -70,7 +76,7 @@ class Mouse { return type; } - std::pair GetPos() const + JC_Point2i GetPos() const { return{ x,y }; } @@ -90,16 +96,21 @@ class Mouse { return rightIsPressed; } + bool WheelIsPressed() const + { + return wheelIsPressed; + } }; public: Mouse() = default; Mouse( const Mouse& ) = delete; Mouse& operator=( const Mouse& ) = delete; - std::pair GetPos() const; + JC_Point2i GetPos() const; int GetPosX() const; int GetPosY() const; bool LeftIsPressed() const; bool RightIsPressed() const; + bool WheelIsPressed() const; bool IsInWindow() const; Mouse::Event Read(); bool IsEmpty() const @@ -115,6 +126,8 @@ class Mouse void OnLeftReleased( int x,int y ); void OnRightPressed( int x,int y ); void OnRightReleased( int x,int y ); + void OnWheelPressed(int x, int y); + void OnWheelReleased(int x, int y); void OnWheelUp( int x,int y ); void OnWheelDown( int x,int y ); void TrimBuffer(); @@ -124,6 +137,7 @@ class Mouse int y; bool leftIsPressed = false; bool rightIsPressed = false; + bool wheelIsPressed = false; bool isInWindow = false; std::queue buffer; }; \ No newline at end of file diff --git a/Engine/MouseCameraControler.h b/Engine/MouseCameraControler.h new file mode 100644 index 00000000..68660c27 --- /dev/null +++ b/Engine/MouseCameraControler.h @@ -0,0 +1,51 @@ +#pragma once +#include "Camera.h" +#include "Mouse.h" + +class MouseCameraController +{ +public: + MouseCameraController(Mouse& mouse, Camera& cam) + : + mouse(mouse), + cam(cam) + {} + void UpdateInput() + { + while (!mouse.IsEmpty()) + { + const auto e = mouse.Read(); + switch (e.GetType()) + { + case Mouse::Event::Type::WheelPress: + engaged = true; + lastPos = e.GetPos(); + break; + case Mouse::Event::Type::WheelRelease: + engaged = false; + break; + case Mouse::Event::Type::WheelUp: + cam.SetScale(cam.GetScale() * zoomFactor); + break; + case Mouse::Event::Type::WheelDown: + cam.SetScale(cam.GetScale() / zoomFactor); + break; + } + } + + if (engaged) + { + const auto curPos = (JC_Point2d)mouse.GetPos(); + auto delta = curPos - lastPos; + delta.x = -delta.x; // fixes the disconnect between screen coords and math coords + cam.MoveBy(delta / cam.GetScale()); + lastPos = curPos; + } + } +private: + static constexpr float zoomFactor = 1.05f; + bool engaged = false; + JC_Point2d lastPos; + Mouse& mouse; + Camera& cam; +}; \ No newline at end of file diff --git a/Engine/Resource.h b/Engine/Resource.h index b1731dc0..87fafa5e 100644 Binary files a/Engine/Resource.h and b/Engine/Resource.h differ diff --git a/Engine/SoundEffect.h b/Engine/SoundEffect.h index b9e303d1..105f3a6d 100644 --- a/Engine/SoundEffect.h +++ b/Engine/SoundEffect.h @@ -1,5 +1,5 @@ -/****************************************************************************************** - * Chili DirectX Framework Sound Pack Version 16.11.11 * +/****************************************************************************************** + * Chili DirectX Framework Sound Pack Version 16.11.11 * * SoundEffect.h * * Copyright 2016 PlanetChili.net * * * @@ -23,26 +23,49 @@ #include #include #include +#include +#include class SoundEffect { public: - SoundEffect( const std::initializer_list& wavFiles,bool soft_fail = false,float freqStdDevFactor = 0.06f ) + // this ctor reads from a .sfx file to configure/load a sound effect + SoundEffect(const std::wstring& filename) + { + std::wifstream sfxFile(filename); + // first line is the freq stddev + float freqStdDevFactor; + sfxFile >> freqStdDevFactor; + sfxFile.ignore(); + // remaining lines are the sound files + std::vector soundFileNames; + for (std::wstring s; std::getline(sfxFile, s); ) + { + soundFileNames.push_back(std::move(s)); + } + // now load the dumb sound effect matrix + *this = SoundEffect(std::move(soundFileNames), true, freqStdDevFactor); + } + SoundEffect(std::vector wavFiles, bool soft_fail = false, float freqStdDevFactor = 0.06f) : - freqDist( 0.0f,freqStdDevFactor ), - soundDist( 0,unsigned int( wavFiles.size() - 1 ) ) + freqDist(0.0f, freqStdDevFactor), + soundDist(0, unsigned int(wavFiles.size() - 1)) { - sounds.reserve( wavFiles.size() ); - for( auto& f : wavFiles ) + sounds.reserve(wavFiles.size()); + for (auto& f : wavFiles) { try { - sounds.emplace_back( f ); + sounds.emplace_back(f); } - catch( const SoundSystem::FileException& e ) + catch (const SoundSystem::FileException& e) { - if( soft_fail ) + if (soft_fail) { +#ifndef NDEBUG + // throw anyways if in debug (we devs wanna know!) + throw e; +#endif sounds.emplace_back(); } else @@ -53,12 +76,23 @@ class SoundEffect } } template - void Play( T& rng,float vol = 1.0f ) + void Play(T& rng, float vol = 1.0f) const + { + sounds[soundDist(rng)].Play(exp2(freqDist(rng)), vol); + } + // NOT THREAD SAFE! + // calls main play function with default rng + void Play(float vol = 1.0f) const { - sounds[soundDist( rng )].Play( exp2( freqDist( rng ) ),vol ); + Play(defaultRng, vol); } private: - std::uniform_int_distribution soundDist; - std::normal_distribution freqDist; + // make distribs mutable so that Play can be const + // (dist call not really a substantial mutation anyways...) + mutable std::uniform_int_distribution soundDist; + mutable std::normal_distribution freqDist; std::vector sounds; + // global default rng for sound effects + // not thread safe! + static std::mt19937 defaultRng; }; \ No newline at end of file diff --git a/Engine/SpriteEffect.h b/Engine/SpriteEffect.h new file mode 100644 index 00000000..ea06b1ba --- /dev/null +++ b/Engine/SpriteEffect.h @@ -0,0 +1,141 @@ +#pragma once + +#include "Colors.h" +#include "Graphics.h" + +namespace SpriteEffect +{ + class Chroma + { + public: + Chroma(Color c) + : + chroma(c) + {} + void operator()(Color cSrc, int xDest, int yDest, Graphics& gfx) const + { + if (cSrc != chroma) + { + gfx.PutPixel(xDest, yDest, cSrc); + } + } + private: + Color chroma; + }; + class Substitution + { + public: + Substitution(Color c, Color s) + : + chroma(c), + sub(s) + {} + void operator()(Color cSrc, int xDest, int yDest, Graphics& gfx) const + { + if (cSrc != chroma) + { + gfx.PutPixel(xDest, yDest, sub); + } + } + private: + Color chroma = Colors::Magenta; + Color sub; + }; + class Copy + { + public: + void operator()(Color cSrc, int xDest, int yDest, Graphics& gfx) const + { + gfx.PutPixel(xDest, yDest, cSrc); + } + }; + class Ghost + { + public: + Ghost(Color c) + : + chroma(c) + {} + void operator()(Color src, int xDest, int yDest, Graphics& gfx) const + { + if (src != chroma) + { + const Color dest = gfx.GetPixel(xDest, yDest); + const Color blend = { + unsigned char((src.GetR() + dest.GetR()) / 2), + unsigned char((src.GetG() + dest.GetG()) / 2), + unsigned char((src.GetB() + dest.GetB()) / 2) + }; + gfx.PutPixel(xDest, yDest, blend); + } + } + private: + Color chroma; + }; + // dissolves image by scanline and blends drawn pixels with a color + // good for dying enemies i guess + class DissolveHalfTint + { + public: + DissolveHalfTint(Color chroma, Color tint, float percent) + : + chroma(chroma), + // divide channels by 2 via shift, mask to prevent bleeding between channels + tint_pre((tint.dword >> 1u) & 0b01111111011111110111111101111111u), + filled(int(float(height) * percent)) + {} + void operator()(Color src, int xDest, int yDest, Graphics& gfx) const + { + // height mask determines frequency of vertical dissolve sections + if (src != chroma && (yDest & height_mask) < filled) + { + const Color blend = tint_pre.dword + + // divide channels by 2 via shift, mask to prevent bleeding between channels + ((src.dword >> 1u) & 0b01111111011111110111111101111111u); + gfx.PutPixel(xDest, yDest, blend); + } + } + private: + Color chroma; + Color tint_pre; + static constexpr int height = 4; + static constexpr int height_mask = height - 1; + int filled; + }; + // blends sprite with whatever is on the screen + // using the per-pixel alpha stored in the src pixels + class AlphaBlendBaked + { + public: + void operator()(Color src, int xDest, int yDest, Graphics& gfx) const + { + // pre-extract alpha complement + const int cAlpha = 255 - src.GetA(); + // reject drawing pixels if alpha == 0 (full transparent) + // this will give a huge speedup if there are large sections + // of blank space (pretty common), but slower if unpredictable + // patters of transparency in a 50/50 mix (not really forseeable) + if (cAlpha != 255) + { + const Color dst = gfx.GetPixel(xDest, yDest); + // blend channels by linear interpolation using integer math + // (basic idea: src * alpha + dst * (1.0 - alpha), where alpha is from 0 to 1 + // we divide by 256 because it can be done with bit shift + // it gives us at most 0.4% error, but this is negligible + // optimized version has alpha premultiplied in src, all we need to do is + // scale dst by calpha and then pack back into dword and add to src dword + // there will be no overflow between channels because alpha + calpha == 255 + // + // we can multiply the red and blue channels together in one operation + // because the results will not overflow into neighboring channels + // after, we shift to divide, and then mask to clear out the shifted garbo + // channels are left in their byte position for easy combining with an add + const int rb = (((dst.dword & 0xFF00FFu) * cAlpha) >> 8) & 0xFF00FFu; + const int g = (((dst.dword & 0x00FF00u) * cAlpha) >> 8) & 0x00FF00u; + // add multiplied dst channels together with premultiplied src channels + // and write the resulting interpolated color to the screen + gfx.PutPixel(xDest, yDest, rb + g + src.dword); + } + } + }; +} \ No newline at end of file diff --git a/Engine/Star.h b/Engine/Star.h new file mode 100644 index 00000000..ad06e006 --- /dev/null +++ b/Engine/Star.h @@ -0,0 +1,46 @@ +#pragma once + +/****************************************************************************************** +* Chili DirectX Framework Version 16.07.20 * +* Star.h * +* Copyright 2016 PlanetChili * +* * +* This file is part of The Chili DirectX Framework. * +* * +* The Chili DirectX Framework is free software: you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation, either version 3 of the License, or * +* (at your option) any later version. * +* * +* The Chili DirectX Framework is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with The Chili DirectX Framework. If not, see . * +******************************************************************************************/ + +#include +#include "JC_Vector2.h" +#include "JC_Math.h" + +class Star +{ +public: + static std::vector Make(float outerRadius, float innerRadius, int nFlares = 5) + { + std::vector star; + star.reserve(nFlares * 2); + const double dTheta = 2.0f * PI_D / double(nFlares * 2); + for (int i = 0; i < nFlares * 2; i++) + { + const double rad = (i % 2 == 0) ? outerRadius : innerRadius; + star.emplace_back( + rad * cos(double(i) * dTheta), + rad * sin(double(i) * dTheta) + ); + } + return star; + } +}; \ No newline at end of file diff --git a/Engine/Surface.cpp b/Engine/Surface.cpp new file mode 100644 index 00000000..ec7d4524 --- /dev/null +++ b/Engine/Surface.cpp @@ -0,0 +1,175 @@ +#include "Surface.h" +// gdiplus needs a lot of dumb windows shit +// enable that shit for this translation unit only +#define FULL_WINTARD +#include "ChiliWin.h" +#include +// gdiplus needs min/max, but we disable that (even in +// full wintard mode), so we need to inject min/max into +// the Gdiplus namespace +namespace Gdiplus +{ + using std::min; + using std::max; +} +#include +#include +#include + +namespace gdi = Gdiplus; + +Surface::Surface(const std::wstring& filename) +{ + // filename must be at least 4 chars long + if (filename.length() < 4) + { + // generate narrow string of filename + std::string narrow(filename.begin(), filename.end()); + throw std::runtime_error("Surface::Surface bad file name: " + narrow); + } + + // open image file with gdiplus (not only .bmp files) + gdi::Bitmap bitmap(filename.c_str()); + + // check if file loaded successfully, throw exception if didn't + if (bitmap.GetLastStatus() != gdi::Ok) + { + // generate narrow string of filename + std::string narrow(filename.begin(), filename.end()); + // throw exception with error message + // could possibly add more info with lookup of error code name / desc + // but I don't want to right now cuz im lazy + throw std::runtime_error("Surface::Surface failed to load file: " + narrow); + } + + // allocate Surface resources and set dimensions + width = bitmap.GetWidth(); + height = bitmap.GetHeight(); + pPixels = new Color[width * height]; + + // test if pixel format is alpha, and save result + const bool isAlpha = gdi::IsAlphaPixelFormat(bitmap.GetPixelFormat()) == TRUE; + + // loop through image dimensions, copy from gdip bitmap to surface + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + // need this to receive color of pixel + gdi::Color pixel; + // read color from gdip bitmap + bitmap.GetPixel(x, y, &pixel); + // write to surface (with alpha channel if exists) + if (isAlpha) + { + PutPixel(x, y, { pixel.GetA(),pixel.GetR(),pixel.GetG(),pixel.GetB() }); + } + else + { + PutPixel(x, y, { pixel.GetR(),pixel.GetG(),pixel.GetB() }); + } + } + } + + // check to see whether filename starts with "pm_" + // (actually, being lazy so only checking if contains "pm_") + // if so, gotta bake that alpha yo + if (filename.find(L"pm_") != std::wstring::npos) + { + BakeAlpha(); + } +} + +Surface::Surface(int width, int height) + : + width(width), + height(height), + pPixels(new Color[width*height]) +{ +} + +Surface::Surface(const Surface& rhs) + : + Surface(rhs.width, rhs.height) +{ + const int nPixels = width * height; + for (int i = 0; i < nPixels; i++) + { + pPixels[i] = rhs.pPixels[i]; + } +} + +Surface::~Surface() +{ + delete[] pPixels; + pPixels = nullptr; +} + +Surface& Surface::operator=(const Surface& rhs) +{ + // prevent self assignment + if (this != &rhs) + { + width = rhs.width; + height = rhs.height; + + delete[] pPixels; + pPixels = new Color[width*height]; + + const int nPixels = width * height; + for (int i = 0; i < nPixels; i++) + { + pPixels[i] = rhs.pPixels[i]; + } + } + return *this; +} + +void Surface::PutPixel(int x, int y, Color c) +{ + assert(x >= 0); + assert(x < width); + assert(y >= 0); + assert(y < height); + pPixels[y * width + x] = c; +} + +Color Surface::GetPixel(int x, int y) const +{ + assert(x >= 0); + assert(x < width); + assert(y >= 0); + assert(y < height); + return pPixels[y * width + x]; +} + +int Surface::GetWidth() const +{ + return width; +} + +int Surface::GetHeight() const +{ + return height; +} + +RectI Surface::GetRect() const +{ + return{ 0,width,0,height }; +} + +void Surface::BakeAlpha() +{ + const int nPixels = GetWidth() * GetHeight(); + for (int i = 0; i < nPixels; i++) + { + auto pix = pPixels[i]; + const int alpha = pix.GetA(); + // premulitply alpha time each channel + pix.SetR((pix.GetR() * alpha) / 256); + pix.SetG((pix.GetG() * alpha) / 256); + pix.SetB((pix.GetB() * alpha) / 256); + // write back to surface + pPixels[i] = pix; + } +} \ No newline at end of file diff --git a/Engine/Surface.h b/Engine/Surface.h new file mode 100644 index 00000000..cc8f6860 --- /dev/null +++ b/Engine/Surface.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Colors.h" +#include +#include "ChiliRectangle.h" + +class Surface +{ +public: + // loading filename that begins with "pm_" + // will trigger alpha premultiply 'baking' + Surface(const std::wstring& filename); + Surface(int width, int height); + Surface(const Surface&); + ~Surface(); + Surface& operator=(const Surface&); + void PutPixel(int x, int y, Color c); + Color GetPixel(int x, int y) const; + int GetWidth() const; + int GetHeight() const; + RectI GetRect() const; + // this function performs alpha premultiplication + // which enables more efficient alpha blending + void BakeAlpha(); +private: + Color* pPixels = nullptr; + int width; + int height; +}; \ No newline at end of file diff --git a/Engine/sprite/Circle.png b/Engine/sprite/Circle.png new file mode 100644 index 00000000..2b7b4afd Binary files /dev/null and b/Engine/sprite/Circle.png differ diff --git a/Engine/sprite/Circle_1.png b/Engine/sprite/Circle_1.png new file mode 100644 index 00000000..e73060c2 Binary files /dev/null and b/Engine/sprite/Circle_1.png differ diff --git a/Engine/sprite/Circle_2.png b/Engine/sprite/Circle_2.png new file mode 100644 index 00000000..47590435 Binary files /dev/null and b/Engine/sprite/Circle_2.png differ diff --git a/Engine/sprite/Ellipse.png b/Engine/sprite/Ellipse.png new file mode 100644 index 00000000..eb461960 Binary files /dev/null and b/Engine/sprite/Ellipse.png differ diff --git a/Engine/sprite/Ellipse_1.png b/Engine/sprite/Ellipse_1.png new file mode 100644 index 00000000..c9225c40 Binary files /dev/null and b/Engine/sprite/Ellipse_1.png differ diff --git a/Engine/sprite/Ellipse_2.png b/Engine/sprite/Ellipse_2.png new file mode 100644 index 00000000..85fb8114 Binary files /dev/null and b/Engine/sprite/Ellipse_2.png differ diff --git a/Engine/sprite/select.png b/Engine/sprite/select.png new file mode 100644 index 00000000..fd5322e9 Binary files /dev/null and b/Engine/sprite/select.png differ diff --git a/Engine/sprite/select_1.png b/Engine/sprite/select_1.png new file mode 100644 index 00000000..475f6279 Binary files /dev/null and b/Engine/sprite/select_1.png differ diff --git a/Engine/sprite/select_2.png b/Engine/sprite/select_2.png new file mode 100644 index 00000000..ce350e0e Binary files /dev/null and b/Engine/sprite/select_2.png differ