From 5bae08a2d8d0dba34268150d96071a4546ab49c5 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Thu, 1 Dec 2011 23:24:58 +0100 Subject: [PATCH 01/21] Implemented the new graphics API: - Removed the internal classes sf::Renderer and sf::Matrix3 - Split sf::Drawable into sf::Drawable and sf::Transformable - Added sf::Transform - Added sf::Vertex - Added sf::VertexArray - Types of shapes are now handled with their own derived class - Modified the Pong example --- examples/opengl/OpenGL.cpp | 11 +- examples/pong/Pong.cpp | 218 ++++--- examples/pong/resources/background.jpg | Bin 90083 -> 0 bytes examples/pong/resources/ball.png | Bin 249 -> 0 bytes examples/pong/resources/paddle_left.png | Bin 762 -> 0 bytes examples/pong/resources/paddle_right.png | Bin 683 -> 0 bytes examples/shader/Shader.cpp | 10 +- examples/sound/Sound.cpp | 6 +- examples/sound_capture/SoundCapture.cpp | 3 +- examples/win32/Win32.cpp | 10 +- include/SFML/Graphics.hpp | 10 +- .../SFML/Graphics/BlendMode.hpp | 86 +-- include/SFML/Graphics/CircleShape.hpp | 129 ++++ include/SFML/Graphics/Color.hpp | 42 +- include/SFML/Graphics/ConvexShape.hpp | 164 ++++++ include/SFML/Graphics/Drawable.hpp | 528 +---------------- include/SFML/Graphics/Glyph.hpp | 8 +- include/SFML/Graphics/Matrix3.hpp | 176 ------ include/SFML/Graphics/Matrix3.inl | 147 ----- include/SFML/Graphics/PrimitiveType.hpp | 53 ++ include/SFML/Graphics/Rect.hpp | 30 + include/SFML/Graphics/Rect.inl | 18 + include/SFML/Graphics/RectangleShape.hpp | 129 ++++ include/SFML/Graphics/RenderStates.hpp | 173 ++++++ include/SFML/Graphics/RenderTarget.hpp | 168 +++--- include/SFML/Graphics/RenderWindow.hpp | 29 +- include/SFML/Graphics/Renderer.hpp | 363 ------------ include/SFML/Graphics/Shader.hpp | 17 +- include/SFML/Graphics/Shape.hpp | 555 ++++++------------ include/SFML/Graphics/Sprite.hpp | 204 ++++--- include/SFML/Graphics/StarShape.hpp | 178 ++++++ include/SFML/Graphics/Text.hpp | 119 ++-- include/SFML/Graphics/Texture.hpp | 64 +- include/SFML/Graphics/Transform.hpp | 450 ++++++++++++++ include/SFML/Graphics/Transformable.hpp | 414 +++++++++++++ include/SFML/Graphics/Vertex.hpp | 143 +++++ include/SFML/Graphics/VertexArray.hpp | 220 +++++++ include/SFML/Graphics/View.hpp | 38 +- include/SFML/System/Utf.hpp | 3 - include/SFML/System/Vector3.hpp | 2 +- include/SFML/Window/Context.hpp | 14 + src/SFML/Graphics/CMakeLists.txt | 26 +- src/SFML/Graphics/CircleShape.cpp | 73 +++ src/SFML/Graphics/Color.cpp | 1 + src/SFML/Graphics/ConvexShape.cpp | 85 +++ src/SFML/Graphics/Drawable.cpp | 314 ---------- src/SFML/Graphics/Font.cpp | 10 +- src/SFML/Graphics/RectangleShape.cpp | 76 +++ src/SFML/Graphics/RenderStates.cpp | 98 ++++ src/SFML/Graphics/RenderTarget.cpp | 241 +++++--- src/SFML/Graphics/Renderer.cpp | 348 ----------- src/SFML/Graphics/Shader.cpp | 2 +- src/SFML/Graphics/Shape.cpp | 414 +++++-------- src/SFML/Graphics/Sprite.cpp | 155 ++--- src/SFML/Graphics/StarShape.cpp | 107 ++++ src/SFML/Graphics/Text.cpp | 306 ++++------ src/SFML/Graphics/Texture.cpp | 74 ++- src/SFML/Graphics/Transform.cpp | 270 +++++++++ src/SFML/Graphics/Transformable.cpp | 210 +++++++ src/SFML/Graphics/Vertex.cpp | 77 +++ src/SFML/Graphics/VertexArray.cpp | 150 +++++ src/SFML/Graphics/View.cpp | 89 +-- src/SFML/Window/Context.cpp | 7 + src/SFML/Window/GlContext.cpp | 7 + src/SFML/Window/GlContext.hpp | 10 + 65 files changed, 4756 insertions(+), 3326 deletions(-) delete mode 100644 examples/pong/resources/background.jpg delete mode 100644 examples/pong/resources/ball.png delete mode 100644 examples/pong/resources/paddle_left.png delete mode 100644 examples/pong/resources/paddle_right.png rename src/SFML/Graphics/Matrix3.cpp => include/SFML/Graphics/BlendMode.hpp (76%) create mode 100644 include/SFML/Graphics/CircleShape.hpp create mode 100644 include/SFML/Graphics/ConvexShape.hpp delete mode 100644 include/SFML/Graphics/Matrix3.hpp delete mode 100644 include/SFML/Graphics/Matrix3.inl create mode 100644 include/SFML/Graphics/PrimitiveType.hpp create mode 100644 include/SFML/Graphics/RectangleShape.hpp create mode 100644 include/SFML/Graphics/RenderStates.hpp delete mode 100644 include/SFML/Graphics/Renderer.hpp create mode 100644 include/SFML/Graphics/StarShape.hpp create mode 100644 include/SFML/Graphics/Transform.hpp create mode 100644 include/SFML/Graphics/Transformable.hpp create mode 100644 include/SFML/Graphics/Vertex.hpp create mode 100644 include/SFML/Graphics/VertexArray.hpp create mode 100644 src/SFML/Graphics/CircleShape.cpp create mode 100644 src/SFML/Graphics/ConvexShape.cpp delete mode 100644 src/SFML/Graphics/Drawable.cpp create mode 100644 src/SFML/Graphics/RectangleShape.cpp create mode 100644 src/SFML/Graphics/RenderStates.cpp delete mode 100644 src/SFML/Graphics/Renderer.cpp create mode 100644 src/SFML/Graphics/StarShape.cpp create mode 100644 src/SFML/Graphics/Transform.cpp create mode 100644 src/SFML/Graphics/Transformable.cpp create mode 100644 src/SFML/Graphics/Vertex.cpp create mode 100644 src/SFML/Graphics/VertexArray.cpp diff --git a/examples/opengl/OpenGL.cpp b/examples/opengl/OpenGL.cpp index fd00839d..f86522ee 100644 --- a/examples/opengl/OpenGL.cpp +++ b/examples/opengl/OpenGL.cpp @@ -4,7 +4,6 @@ //////////////////////////////////////////////////////////// #include #include -#include //////////////////////////////////////////////////////////// /// Entry point of application @@ -77,9 +76,9 @@ int main() } // Draw the background - window.SaveGLStates(); + window.PushGLStates(); window.Draw(background); - window.RestoreGLStates(); + window.PopGLStates(); // Activate the window before using OpenGL commands. // This is useless here because we have only one window which is @@ -138,12 +137,12 @@ int main() glEnd(); // Draw some text on top of our OpenGL object - window.SaveGLStates(); + window.PushGLStates(); sf::Text text("SFML / OpenGL demo"); - text.SetPosition(250.f, 450.f); text.SetColor(sf::Color(255, 255, 255, 170)); + text.SetPosition(250.f, 450.f); window.Draw(text); - window.RestoreGLStates(); + window.PopGLStates(); // Finally, display the rendered frame on screen window.Display(); diff --git a/examples/pong/Pong.cpp b/examples/pong/Pong.cpp index 0e9abfc4..a203dea7 100644 --- a/examples/pong/Pong.cpp +++ b/examples/pong/Pong.cpp @@ -19,172 +19,216 @@ int main() { std::srand(static_cast(std::time(NULL))); - // Defines PI - const float PI = 3.14159f; + // Define some constants + const float pi = 3.14159f; + const int gameWidth = 800; + const int gameHeight = 600; + sf::Vector2f paddleSize(25, 100); + float ballRadius = 10.f; // Create the window of the application - sf::RenderWindow window(sf::VideoMode(800, 600, 32), "SFML Pong"); + sf::RenderWindow window(sf::VideoMode(gameWidth, gameHeight, 32), "SFML Pong"); // Load the sounds used in the game sf::SoundBuffer ballSoundBuffer; if (!ballSoundBuffer.LoadFromFile("resources/ball.wav")) - { return EXIT_FAILURE; - } sf::Sound ballSound(ballSoundBuffer); - // Load the textures used in the game - sf::Texture backgroundTexture, leftPaddleTexture, rightPaddleTexture, ballTexture; - if (!backgroundTexture.LoadFromFile("resources/background.jpg") || - !leftPaddleTexture.LoadFromFile("resources/paddle_left.png") || - !rightPaddleTexture.LoadFromFile("resources/paddle_right.png") || - !ballTexture.LoadFromFile("resources/ball.png")) - { - return EXIT_FAILURE; - } + // Create the left paddle + sf::RectangleShape leftPaddle; + leftPaddle.SetSize(paddleSize - sf::Vector2f(3, 3)); + leftPaddle.SetOutlineThickness(3); + leftPaddle.SetOutlineColor(sf::Color::Black); + leftPaddle.SetFillColor(sf::Color(100, 100, 200)); + leftPaddle.SetOrigin(paddleSize / 2.f); + + // Create the right paddle + sf::RectangleShape rightPaddle; + rightPaddle.SetSize(paddleSize - sf::Vector2f(3, 3)); + rightPaddle.SetOutlineThickness(3); + rightPaddle.SetOutlineColor(sf::Color::Black); + rightPaddle.SetFillColor(sf::Color(200, 100, 100)); + rightPaddle.SetOrigin(paddleSize / 2.f); + + // Create the ball + sf::CircleShape ball; + ball.SetRadius(ballRadius - 3); + ball.SetOutlineThickness(3); + ball.SetOutlineColor(sf::Color::Black); + ball.SetFillColor(sf::Color::White); + ball.SetOrigin(ballRadius / 2, ballRadius / 2); // Load the text font sf::Font font; if (!font.LoadFromFile("resources/sansation.ttf")) return EXIT_FAILURE; - // Initialize the end text - sf::Text end; - end.SetFont(font); - end.SetCharacterSize(60); - end.Move(150.f, 200.f); - end.SetColor(sf::Color(50, 50, 250)); - - // Create the sprites of the background, the paddles and the ball - sf::Sprite background(backgroundTexture); - sf::Sprite leftPaddle(leftPaddleTexture); - sf::Sprite rightPaddle(rightPaddleTexture); - sf::Sprite ball(ballTexture); - - leftPaddle.Move(10, (window.GetView().GetSize().y - leftPaddle.GetSize().y) / 2); - rightPaddle.Move(window.GetView().GetSize().x - rightPaddle.GetSize().x - 10, (window.GetView().GetSize().y - rightPaddle.GetSize().y) / 2); - ball.Move((window.GetView().GetSize().x - ball.GetSize().x) / 2, (window.GetView().GetSize().y - ball.GetSize().y) / 2); + // Initialize the pause message + sf::Text pauseMessage; + pauseMessage.SetFont(font); + pauseMessage.SetCharacterSize(40); + pauseMessage.SetPosition(170.f, 150.f); + pauseMessage.SetColor(sf::Color::White); + pauseMessage.SetString("Welcome to SFML pong!\nPress space to start the game"); // Define the paddles properties sf::Clock AITimer; - const float AITime = 0.1f; - float leftPaddleSpeed = 400.f; - float rightPaddleSpeed = 400.f; + const sf::Uint32 AITime = 300; + const float paddleSpeed = 400.f; + float rightPaddleSpeed = 0.f; + const float ballSpeed = 400.f; + float ballAngle = 0.f; // to be changed later - // Define the ball properties - float ballSpeed = 400.f; - float ballAngle; - do - { - // Make sure the ball initial angle is not too much vertical - ballAngle = std::rand() * 2 * PI / RAND_MAX; - } while (std::abs(std::cos(ballAngle)) < 0.7f); - - bool isPlaying = true; + bool isPlaying = false; while (window.IsOpened()) { // Handle events sf::Event event; while (window.PollEvent(event)) { - // Window closed or escape key pressed : exit + // Window closed or escape key pressed: exit if ((event.Type == sf::Event::Closed) || ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Keyboard::Escape))) { window.Close(); break; } + + // Space key pressed: play + if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Keyboard::Space)) + { + if (!isPlaying) + { + // (re)start the game + isPlaying = true; + + // Reset the position of the paddles and ball + leftPaddle.SetPosition(10 + paddleSize.x / 2, gameHeight / 2); + rightPaddle.SetPosition(gameWidth - 10 - paddleSize.x / 2, gameHeight / 2); + ball.SetPosition(gameWidth / 2, gameHeight / 2); + + // Reset the ball angle + do + { + // Make sure the ball initial angle is not too much vertical + ballAngle = (std::rand() % 360) * 2 * pi / 360; + } + while (std::abs(std::cos(ballAngle)) < 0.7f); + } + } } if (isPlaying) { + float deltaTime = window.GetFrameTime() / 1000.f; + // Move the player's paddle - if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Up) && (leftPaddle.GetPosition().y > 5.f)) - leftPaddle.Move(0.f, -leftPaddleSpeed * window.GetFrameTime() / 1000.f); - if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Down) && (leftPaddle.GetPosition().y < window.GetView().GetSize().y - leftPaddle.GetSize().y - 5.f)) - leftPaddle.Move(0.f, leftPaddleSpeed * window.GetFrameTime() / 1000.f); + if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Up) && + (leftPaddle.GetPosition().y - paddleSize.y / 2 > 5.f)) + { + leftPaddle.Move(0.f, -paddleSpeed * deltaTime); + } + if (sf::Keyboard::IsKeyPressed(sf::Keyboard::Down) && + (leftPaddle.GetPosition().y + paddleSize.y / 2 < gameHeight - 5.f)) + { + leftPaddle.Move(0.f, paddleSpeed * deltaTime); + } // Move the computer's paddle - if (((rightPaddleSpeed < 0.f) && (rightPaddle.GetPosition().y > 5.f)) || - ((rightPaddleSpeed > 0.f) && (rightPaddle.GetPosition().y < window.GetView().GetSize().y - rightPaddle.GetSize().y - 5.f))) + if (((rightPaddleSpeed < 0.f) && (rightPaddle.GetPosition().y - paddleSize.y / 2 > 5.f)) || + ((rightPaddleSpeed > 0.f) && (rightPaddle.GetPosition().y + paddleSize.y / 2 < gameHeight - 5.f))) { - rightPaddle.Move(0.f, rightPaddleSpeed * window.GetFrameTime() / 1000.f); + rightPaddle.Move(0.f, rightPaddleSpeed * deltaTime); } // Update the computer's paddle direction according to the ball position if (AITimer.GetElapsedTime() > AITime) { AITimer.Reset(); - if ((rightPaddleSpeed < 0) && (ball.GetPosition().y + ball.GetSize().y > rightPaddle.GetPosition().y + rightPaddle.GetSize().y)) - rightPaddleSpeed = -rightPaddleSpeed; - if ((rightPaddleSpeed > 0) && (ball.GetPosition().y < rightPaddle.GetPosition().y)) - rightPaddleSpeed = -rightPaddleSpeed; + if (ball.GetPosition().y + ballRadius > rightPaddle.GetPosition().y + paddleSize.y / 2) + rightPaddleSpeed = paddleSpeed; + else if (ball.GetPosition().y - ballRadius < rightPaddle.GetPosition().y - paddleSize.y / 2) + rightPaddleSpeed = -paddleSpeed; + else + rightPaddleSpeed = 0.f; } // Move the ball - float factor = ballSpeed * window.GetFrameTime() / 1000.f; - ball.Move(std::cos(ballAngle) * factor, std::sin(ballAngle) * factor); + float factor = ballSpeed * deltaTime; + ball.Move(std::cos(ballAngle) * factor, std::sin(ballAngle) * factor); // Check collisions between the ball and the screen - if (ball.GetPosition().x < 0.f) + if (ball.GetPosition().x - ballRadius < 0.f) { isPlaying = false; - end.SetString("You lost !\n(press escape to exit)"); + pauseMessage.SetString("You lost !\nPress space to restart or\nescape to exit"); } - if (ball.GetPosition().x + ball.GetSize().x > window.GetView().GetSize().x) + if (ball.GetPosition().x + ballRadius > 800) { isPlaying = false; - end.SetString("You won !\n(press escape to exit)"); + pauseMessage.SetString("You won !\nPress space to restart or\nescape to exit"); } - if (ball.GetPosition().y < 0.f) + if (ball.GetPosition().y - ballRadius < 0.f) { ballSound.Play(); ballAngle = -ballAngle; - ball.SetY(0.1f); + ball.SetPosition(ball.GetPosition().x, ballRadius + 0.1f); } - if (ball.GetPosition().y + ball.GetSize().y > window.GetView().GetSize().y) + if (ball.GetPosition().y + ballRadius > gameHeight) { ballSound.Play(); ballAngle = -ballAngle; - ball.SetY(window.GetView().GetSize().y - ball.GetSize().y - 0.1f); + ball.SetPosition(ball.GetPosition().x, gameHeight - ballRadius - 0.1f); } // Check the collisions between the ball and the paddles // Left Paddle - if (ball.GetPosition().x < leftPaddle.GetPosition().x + leftPaddle.GetSize().x && - ball.GetPosition().x > leftPaddle.GetPosition().x + (leftPaddle.GetSize().x / 2.0f) && - ball.GetPosition().y + ball.GetSize().y >= leftPaddle.GetPosition().y && - ball.GetPosition().y <= leftPaddle.GetPosition().y + leftPaddle.GetSize().y) + if (ball.GetPosition().x - ballRadius < leftPaddle.GetPosition().x + paddleSize.x / 2 && + ball.GetPosition().x - ballRadius > leftPaddle.GetPosition().x && + ball.GetPosition().y + ballRadius >= leftPaddle.GetPosition().y - paddleSize.y / 2 && + ball.GetPosition().y - ballRadius <= leftPaddle.GetPosition().y + paddleSize.y / 2) { + if (ball.GetPosition().y > leftPaddle.GetPosition().y) + ballAngle = pi - ballAngle + (std::rand() % 20) * pi / 180; + else + ballAngle = pi - ballAngle - (std::rand() % 20) * pi / 180; + ballSound.Play(); - ballAngle = PI - ballAngle; - ball.SetX(leftPaddle.GetPosition().x + leftPaddle.GetSize().x + 0.1f); + ball.SetPosition(leftPaddle.GetPosition().x + ballRadius + paddleSize.x / 2 + 0.1f, ball.GetPosition().y); } // Right Paddle - if (ball.GetPosition().x + ball.GetSize().x > rightPaddle.GetPosition().x && - ball.GetPosition().x + ball.GetSize().x < rightPaddle.GetPosition().x + (rightPaddle.GetSize().x / 2.0f) && - ball.GetPosition().y + ball.GetSize().y >= rightPaddle.GetPosition().y && - ball.GetPosition().y <= rightPaddle.GetPosition().y + rightPaddle.GetSize().y) + if (ball.GetPosition().x + ballRadius > rightPaddle.GetPosition().x - paddleSize.x / 2 && + ball.GetPosition().x + ballRadius < rightPaddle.GetPosition().x && + ball.GetPosition().y + ballRadius >= rightPaddle.GetPosition().y - paddleSize.y / 2 && + ball.GetPosition().y - ballRadius <= rightPaddle.GetPosition().y + paddleSize.y / 2) { + if (ball.GetPosition().y > rightPaddle.GetPosition().y) + ballAngle = pi - ballAngle + (std::rand() % 20) * pi / 180; + else + ballAngle = pi - ballAngle - (std::rand() % 20) * pi / 180; + ballSound.Play(); - ballAngle = PI - ballAngle; - ball.SetX(rightPaddle.GetPosition().x - ball.GetSize().x - 0.1f); + ball.SetPosition(rightPaddle.GetPosition().x - ballRadius - paddleSize.x / 2 - 0.1f, ball.GetPosition().y); } } // Clear the window - window.Clear(); + window.Clear(sf::Color(50, 200, 50)); - // Draw the background, paddles and ball sprites - window.Draw(background); - window.Draw(leftPaddle); - window.Draw(rightPaddle); - window.Draw(ball); - - // If the game is over, display the end message - if (!isPlaying) - window.Draw(end); + if (isPlaying) + { + // Draw the paddles and the ball + window.Draw(leftPaddle); + window.Draw(rightPaddle); + window.Draw(ball); + } + else + { + // Draw the pause message + window.Draw(pauseMessage); + } // Display things on screen window.Display(); diff --git a/examples/pong/resources/background.jpg b/examples/pong/resources/background.jpg deleted file mode 100644 index 06dfdb8b896d61f514afae267c2efff92e67906c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90083 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%0ADe#?&Gk2Sa$`BL+_J+QJ?6~`I zHy#PhX-muU477zm*oXMVSzy|7a$@C3v3j?*mn3#1-f%_ktz4Ve zrA^}p-MU@walT@b{H7^cpWza;Gu;{KBUm*~(uBI!JO5d7(Vgsk1KfFfufavnKKDuD zG>T!ppuH5-*GUT>>pKLJ*Q0F^m&}x@-=}?h2}N7(YOL|ByJ75NtZ({`-V&sL^C$?@ z_&2X!ynUjsF5Fyo3I9z;knKAji24RlZY!|%L_AOGy}kx@l-L?kel`BNDC;zS*hnOK zf~ah&>WrS$L%8}u1FoAFt!ua18;6PDS)1J@huRDxd8Js5@f&Pt{1z7EWJrN-HC#9O z1FcKT*Gymg_IGF|8`iD^Eh#j9WA*VPS+Dkc=5E$%t;w zY+OpjU3vDMRGZeVlZwk*lV? z*QPY$f+d_uN7!M%V4AjOqyGE5iB=cuSMX>WHE!ln$gP`=tgep!Eb&+z(blU~?+Nbs zB6T~Htph! zm>#W)t4dkzHEEm>+g!#s%8$o=WWQa;!(;`aLfDzxcr;cdNAHgla+?*@!j$uiKesKa z;2Neg$npE!vh8wE%R>1MtD6LJ?B;De&WQYS2nV>F@vRkVea62_3aErooI=V>!d$A_ zUx*xs1m~5vf7)TU8~9Kg+N%9U!{0lV{<+cxFT`XSMkLv1rA03HBuVT#<+Vkr>6;|Q zgRCz}wzQ*29EQd{uT!oA{{uxX^I73jT}!ehr(fX~sNfNMN=u4)3!ueVxdJy!+d(Ii z&&zI(U49~iXOOtxs%9!+850UKKuVR8%?$wAZr6{fMcZpJbg@pyIr?39AYMLA_3L6$ zE=SdQfx9nTq>TaOI*hMI$Ece%o5WvgC?8>87q*lwOQe&{Ue^=rbq3hfr6((F>PK@$ zYhepHJnOneP(xK+q$Qj&kw#cEP#upb z{vn(tOEa)`+tUqI4m+Y+=#!v}R_$#~j9a07C_Cz$;7*^~nW(1SngEKivJK>gMP4$} zG2(=NwWg#!i)=@4O*@8i+aV}NqrMVlLfPElVCfmD{iPJR`ndz|aX@0JohvuN>nyw^C`AkCS@`b((A&@9_ zV)i=#UXmBz!4k8j0U-vPZxJX)=d^OiC}1+jK*F_`NOcBXm$CEGK2RIOjIzGj3)K`W zC55^fK_nO~wGW|`8KD(=3BFIlpw9l&A>``?1LguI9dJI2lCq&?B-Ml}_Y z`QC$AJd08mxXM1KUci#EW$46li$rgU2|>vs%}qPaW?475=zX#`0awMeGami3(1k4G z6`0rY884abE<8QbDZPwE8D(ECImq6wTGI<{ouLNFI~QG}^u4?l()ddu7<&93MHH1R z@Nfw~JB9Y##CDugiR)%znjU!q`o?ZQrpCkcQscRUF!<&qW|Pt{dSu1;ZkT41&$B1k zn(PiN? zwS?)o6xOsebBMlByX+GUhzyfJw2lvzE#~lkro^d(?#)x&i=n5Qzuh(+rU`^zrOo|D z0&4Wb6M3SrrnYnSFo6WBGA}DbucDc+7ohzd%h$l2N(Xr-xFQ*Fq(avhM=i9K$)E`X z0^#axIcN|w$JaB^`D5Sy+1I>Kh$`a-p4_{k%9tpP$TA)9u`{aJvgw|{ASkW$O6~;E z9Pg1fS^5byM$VT2-f8b9n?usK<)C3?-_>}59UL;X)}{u%p-MuFMQ6|+TN8)P0zB7Z z5v#L)Zpyc+(UUPE?7{y8WhF8Gguy?KEN55xLO9XFNW2EhIPI_zJCm7!431^dIjmKi zub2q9Ma^ruiPjF4_G}D~+extYbO>xTys3#M_pRoJZ_kW`BX5kZ#Vo&U3=1EY}bH%@-429;H>Oz;GuNd4qE6=3sA==I8p1r z#(G!SG{0HeFZB6nG3Z?0^P%sm<8=n=s$C_>Hc;kS-7o2cXDYdQUDb)w^{S$j$YbG7 zQuZ%TbqUc)`4`_`SW>&boA1quH!ofADVULU+C&U-qwAS0I-HLUlCOqqY5B??*`N?2 zru?VlgVKXVpjxbKQFOv-ghlYXc)DW1g4BS|R70)1O*SW)@UD!gTlN6nYDc)qW#?$E z!z@mGK=v0B4L^~Dzs3V(Rj^=n&qe>cN|Fau>sWrAH6;j!xM)O>V${cy2OBQHFKK$~ z*{Z^`$aK7gU!}Xv?QI()m@=1vfL?fH$~Be|{%@dukBF|ZB>irS$pub}CMi&-1~z!7 zfD9IwEN~&~>h9#9@W}DwOitr18H<586HxbeOK_0(lvmc$UL*(9V$u7e)$jV2xzoSP zITa#6Hw^KiP3=oCYP;R{H>KL_f{{n6Z$O1WcLuYLZF68q&>D+56YdNr0Vr4 zjDQ~403q{uxIV%So|Oa^l4q0WxKNAu4Z!H!B5(yw8{1+7C9JGch=9jE-tS?ppDQ(d z1{EibQ^Utdu()2ob7kij?%B{9;6N;1ymUD0OE&gFk}e+N zzi~`ji>}x29@EI;5ye<1h-cLx@PY`q?=Q-=RTWfC6g!aNwdb= z#a1CgQB((#yr*b2|0$M$4iA;>hn7<|$VuO|O8=%7r9O!y$cOhgV%(@4uR&JCyQ&zK zjFdN)G8$ofDI<8+=L?<3+S`+puHSW+HA}lLKyy=XmnV_@&*4jDA^AY=_B(dRZC14| z4du9`ayUC1lZ~MR_<6j?Do>2|QS^oJE#pFsY@geG=r=fv0h|*k0TTMxJRpI zr9f1=w}fqz{y%3jIu3}#7?nJTYkBl<0E_e_PVkVpA7opU7`7&7i65tUUDU+iEIt!f`3lXU<9*NSLY-&+Hwmc*cOb^S_l~GzZ{SlE_$=8Ec#ziIUHn z3Y!5=Tdx`ZxjRs_xI|%UILkTCJNT}&Fb-8yOAv}^AOdGQ?#vUCFLz&qZ7eZA`@n2l zS18Qi4C=3X$s&<3OU17}_karGSVj?h?;!<-0UduWW}gf6+xi5E_r|3l#1c-YVRH6- zSOS~~-X;ZW&N-9>$sP z;!oG08c_3bSgzd~zGSeRG?$bFpbpxj?9Ht6gJz303FFAEPNE~_1m9s0dBW1Ug9>w8 zJ9wHkxo;tO7B1o0!$p?h4JLoJfFKs;V6^`bZYEvI7vo)cYL`o9q5nSG^=^&rfS1N> zR;9oaWZeh024gQdjR~NVMP&K*7-vbw+w$Rc$`#8K7y)jx$!4X|K^eNeQ+BRy5vD1q z<*x>^L4GJA%ZTRwekdfQT#d2cq@^Ys-qo#xve;)NMbCOwJH0}gD8>Cy zAAcxEdlYbd3(43f<6Y+Y?4*N}Y2e==hJ~fqNdy!)A4(b?2~3hD8@>T=>10b;6C+zD zx?Faer8AYRHusOVel~{(VJBfDq{w4)WyBVMs z!Iifr&NEih5(L+X7N&`kk@o1mTM8(3pgFh?Ua+2<6ktr2$vg?<`%~m{x>y6^ z&+||vnkDYHr2W%-)A$T^Kx~8+u4LItYlkW06I#bk*~8nXpvXrTwac%k>06Pg7-e}z zsFp>w~tGON_R9r?Xc6St4xoUp3dv>^py z4rzc}Bf3)KcUKP5_Fbj+jNu{u)8GVmd2|MZ1!Z*S{ls8`6kezr-#T%wPl__d0gO6= zFF(@NxhHW8srxDRY>s%6&%X><9x+78I6nasg&hlQW(B0U-68+PoW`<}%YCy!WVw z0_WJ#GOdN@o(9P;)r_mUt-C0he>C?D1`Ozfh9ds3W~_O{y)XnuB{t8{+9Aq_?Zw08 zyD|>PqXUKG&Vq&vc1Hs*6MXyf+J7$sye;Bo6p|&|V{WBM>bt3{Jr4u+X8NEXbsjA1 zO@frjr=sAPIa0+~&+*b70+g+5+{E1NjK2Yc(;WY9l88GOHYK|AcG+gO#Xi*Q0aIlj z#)_Tu#9*W7!RFS4-Y&)oQk`G!?R#lyvCBq9e_HJ_!DM3NB`zG7^==iqsMX*oISmdg z;^GlIg%PIm>p-|}~D$Wni`W}JgKW(jGR^Hk9 z*JAsh3#)@l7mU5`28mFyzO(=GoJJY?Z6#wE!IbrDm_CzMSFaE{FD^43mbUq|YbMX2 zlxez7SLr>>2{&L*adc5)`gh+_=V#NdCZP%t6f@mNYd7JI1%BFKFVt)tUZ#3ft-8kj zJ4}C{Rt&i>*ux92-D0iTh^Nvs@Zf}Hu4-?nN9P~xzhG{F( zbiHN#69b-7>-shsCk_kQU+24cRsVjh%se1en~r#`VtkB1y*?fRm+|Q@lw-h#xq|r! z4mFcJOl%~k0tP09F?vb~EqD|CO4zR6Cnd@=D#q3C!W(Mm9p=~^Sa7eE$pfMd z=xQYH{PyvFu1?mwtw%c=r_`cB*8<-Zy}ENZ?*ht?M1_TuET(T5Qm@l*Rm1>3b{dwsR@qw3Og(O4gW= z(i;BzH~Ng+wPv50j+j6tr!(G;*#;9ILR;3cEHFQN?;F4kpPp7#?liE84^l*Li+Op$ zT%yZ@KW}k?#3VwM6x>6AJClEX==_W#d3PwX)ntBK;^)EEnc511lr8d_iWL__m9}98 z1-PwzM0@jQ)j^9v%q)blArn}ED#r0R8aTvwQ5@l!**%sA+S^pg#7sejK^>}wUNwZ$ zpLScL%G%=G8TcMpV`4HGXxwU3-;f?oJ{Gf`+jKu^vHs-ba!bIa#h!qX=DZkmxXmw#c|6UIiHE{Y^s;Pvjo-52L&FK8eG?%r2YR5 z!#;p-33mruQ9?W*Fq`9%XvJ4CrsLTXaJOfTi4}0EFEhIxDREX5PJcY|3PJm7DVQYk zY?zKubxFg7J2xv0s|}KxhuMW*0RA8>r{=MCjG9ML;?2~&Bn)4?p){>)ssd3gqQPlsLXyp+M`3`NMPf! z_9>*cWbk1YV?v0%=rFdW!q-r<9d^PHM%CH!)>~Kcu2#~_6Gk$~-Z8$fCSPDEnR!m` zdo2oO3!NegU5wALSx}B1*I)?!)FLT8j)DB36TpdViZdQ;(d*jHMR7Sl-m`_~9cK$) zv`+}dIEu?GkMt*wZb=%wYWG0`eftJzv0B84 ztD@D~tDiv9>X7kJ^8@(N*xHn^Sh$XNipevv5iAP41tj`nfhTW|1IxT0V*jj`(ixc` z>P`IONJb}Fr_@;FhigG7ujUS0iBKEB@hP;d*nEOS-d`pashvQrO+8^iY?hs`D7XiQ zed3?eegMP%YTo-^zb0Yer-Su^)gl^rS6;z=&1ani3n}TgXr?r)@oYos;50EkD=`#N z%EToyIEi$0Djxzp+jH|4#)0##YRRi0!bH(w13b*Jf>QoUtIT|oxA@;hPBeo~XM%q- z^0~U#O`uG4#LV=o{H`pl`(alW!D&{C8I1}@`U10vd6ufy{Mo9TJS;_HN1+i_tzQn;=ocPBoB`bEu zAeHvRiJGH@e&H{N#5_n)JgDhjCyS3+QIRiTy{}w zkSM@yQGcX}z6eD^5H;{{MEgC=+q=hH)xOQ`yT|Vu2_i;rYo|P2X>8Oa*}ahwKgmY6 z$a+HAvYeAo!+bg)c)AQus7a}Iu|-3vn&v7XHH2uZ@m?=SBBz}ZiQrXjAVqgKL1)kd=%34cRPR?2m+^`cj}m5Uux zGym*E`SZjA;q2jBJI2XJM{T}V+Ks1JPgm=J;>M_tiJ5Y*Av$-cH(0o z;Dg*3d1657vsjC!SuYmxtmb{^c!}zoAG>I{ve}%$?$3D^(;`F7CQl60-(ptL_FS|R zH67=;Y)AhV=%5`K=k7Ko&n#YXcFMD|x2P#hCv*7A6XSZAjU<>%rU2=1oOgGX9ULUw{f5RC^0X5Tq$P74&INOavy^No_!}{~XYJ|JJ#8Fx*&N>y+j&;2|RQM;qlwr34>$L8_?$kL+o5lW^ zGe0To(!QH%4ZELFD#fX(h`8H0?aVH~o@y=H;gr4K%+Y-cp12%m8j zIi4(I8@1_yQX?f6$=j72b=rF`HExwQ43^M&-l~skp2Q4*Z>w$viJVx3gade$7%T|+ zq0=u!;<bu`=6U`w9n}DM=gZl>oR}$&MO8s;v=VT~2bRh$o%rL^x2(#k;PV6gNZ8^}I9X_YiZh zjSSQ4d~}i?OGiP(6$-pX+LwbvV2t}EigfEwqvYnfm4HB|NN{~r;+d7TQ^w(Iq{MsH z$8Kq}#(Y?j{ZQl2ah3a!ZU|Jb97ed^;|;7U3Jzc)jI%PjHW|?p_T3ntqhE}#Nqa~t zew2aTUe71ZR`jwL9Lt*RAmTZV50r9VF?9tYvkzMK3|rv19h10t8p~QkDkL(_R*|GE zLY@%gQjlr9D+AU@2lf%8g{Y4K@YngD2@r&mqi8<-wHh7MiC|cjfzN41JuFh_@Y=Kv z0qE*nUqcdPh=5l1Mt|0%F|@UQgiA)2jT;3+=$}{R1I&kR{0HNjjx18O8?s|6+_73L8vrusp)X`9F9oq8OZg{HDa*f_g({|zz{rOJoW21o z;o!e9p&Fa@0%s59;6kC`EbZQpepD>R|D`9Ow5?)iV|Bg-%EgM63PQKf4ulY}MV9ClHjYD^*#!91oU9+_S8F==oQH3Uqvje!l4bfd1vdnq6Ri|9)e z!Li3FN7)DlmQih(=^G$oYjpf}93{FjSWKD~K)zf}2?npViDoh@>UjBW(&N9b4c&lN zL5C-@lHt1C`tR1{!bS6*QNw6yT0Q}Rc$&Obv_)$HFrr+S>gL8Tg*wKX9<5E4W7AjA-uwkK{GV15L#07$p-+;T`D(!E8arT!M){}8dZkfJ0o9jbk`j=KWI#)nQmWl(r)&@J?@Gy%_ z+I+!qj1ywhBK>b|RnUDmdtE1iPgouQyJeNKlaceQoBgzz3zqPxJv0~I2wpMK%9-_g zCXL_bq280F;=FZK^p&Hy{>vf+Uls-g2uVBgN|+(W9_{+vqe!b4-vA$(R3@U|cUq-06rnD>g+ zykwe6GSz+-nDRNAo_y?_k7Q1_pUREE;mGfV)<78WKAut!N^lM7SePvu*4F7_<$ zUHLW}cC#-Vl)I83-^47Q%WOZxe?Jfk+r)UjilC1q2kX>uicMISeObH17uN?tl5S5q zydx$qta=Suqu$)KU%Q{1tEi-OjJdUlH!qybtrA($1uC(oB<&5vE{rcnRz%ta?EVFU z!^BqNV^-0;ImWoD>tk9_T* zn9g65VR5EeF=`8jE@l*l7UUx<~}5H^wrz-gF6 zwXJ4;ks=nY0@j5B_&~g&Zr9Re6p>Ec&vN24&)6?bcAx1e<#9rvIIC>|%H8wyApFOk z@1otka9Wb;jFC*%h_cZ~&=T3|WHIxG*#CA5o2o{A5$e=Qi~BZb@3tn;QYc%d)8u&F z@Ug-Bmo*0QJvW@3O_x3C0?F+VU#lfrp6Bm94&sK{{UA0+hyC2~B1vRQhW@NKYx*=9 zlbPgeM;NPF80Y>iT|u0UwJfV&w>TGKw#^`4N^r)s@8|<~9M%h_?Ke?nl#ILROcTuWa;K?vzgM*bWqvv-5gC{h&wT1-`8$4 z{zsT`j;onUHWq?&MmcFh{3FW<|J&PUdl>(dOo^d`OCW?wljVO7y)5_bBUUuin0lc@ zJgO+K9HyBxM**J+`B*O@pvr8-%a0Wl+wbb-Zt+=$vl9!}l{SJQ(U|d4^$8kkbwXmK zZd=rH;Gj?adMli4e0{q{CI86^9kfJ;I5~k(6SdgfFcH|$Fo_@=c3%P44J_&lcSo3Q z+g%LbR?~q?P$;&8)3UZv?4QG78nq^T3wy34k?7q-$0mmNXvDS^qrvM01R!FjaivHY zp{pMaiVp_twxnC6t*KDFU{XCbQckO=`8Q|O)p~*wMvlvtDJym*`|HWCVstEmLQx|f znkH|x(9&e)BqG41zedm=O(zguY-y@Ls+fr99kdm#0P@cCFzne>?Egw*j9t&>gd+ze zmI#s-jW$+Pu%lfbuW7lYcq7m;7Z0N(|5;Q!g&@wcq10X*DoHzhUYmdBywT0}ybv^X zdJ*ydM~bb0--}LF^231)J#kSi79`k5`XMN@oGZtWNWSK!_QFF#w1oEGkP=^5%!^pW zb|t7de*+e(WrrK{izB}Qv4(cUq%lZY=@{>U9%aMXX|t<{(4F#YB1-WK<85&i`eQ(&OkI9k2A@)q~38^015FYpc~tT-P?_?mdinoAQ*1Y zj)b7k)UtxL+LfRxHYU7Al`)3)STi_3wH~!Vu5*D32-1#Tg}=B%770t{uf73)WCk5f zvA9jMz{XU4(%|7(of`hBUJd?7 zYCIZQ36U*eF_$C=D>qliL$2T>R2pLE5fpr2`GJq#LfgLA*4Cb9iHJ%S%))d8 zIIz0=%d+a=?q;nq`!!1WucfhfjKT5|=#a0b3N;f#p8Fd|%s$o*)r)O|??HUZ8r-EZ zr_o^oEf zS!<*@jWL10R+NS>d4$!C8=D+VrayuFnxhCz2}P$UcT?t>Y))gFLi|d~6K~~Yldxq1 zL^orF<$zD>2UH9izjST>TUv_q*BTJ{S(mKw7qPJf$74T-Hx380`nliF zUM6-vA6@WD`en^V_tazcVcmPy=lb+7%TaNW=Ghschv%=Nd~M4{+c)5vjh>_Y8z4nJ zWb60a%WJ+?-Fw0B^8B3;1z)UQonGKWXUw*FKQ`km68{zRkx4kPPwMPn^YsuOQ~wY;So2F&&rY9y z3VBJH&i}4gp-(8-Fy6)fiUt#1oAq9=>_x>tJoQDxTU?-fb$Inr6H3T*vsdeYJ9n|! zH{??ZNZ^F6Td*hFiZ{{y(*J`O^{T0+OcDfr9IVo{lI^Bw7>iF^r{Poiw0`<7WLqo- zl7QRq-1Wg)SHAimzY0e+RC!M`=#03b2Y-TO|0j-zTJF(CIUetT7h%eurp}b=zYNnB z;bwL2zokxov;Ab?!W3U0^)RFVgect-0$NV~Qy65HZCow?P{F?YQ!w6?t}wona=OiW za6wf}L7n)oO1@h3dT6Ts(@;6CKsUNo8F0H zKD<#mFS}f@lV_|_&f-gV&{erLXoNhxheM&5ElwWIkaM*rakwkcnhWe&Dc;=fYn;Mx z(yvvyx44}%hRA^Wi!Bq^wE<7=dX_EYs(^4Q;k^egJS(dVNJSBF}n;_a%0&*2$w@!qi97W=bl;qt!-@D`{65)Tr-#S`KjoV->oltLqyMmoj24 z%p`^dnoyf;Eae+R!^=friOwRq1&WDeSdSDf-vFJS`6HzFp(MPP+>ST(?Qg*NqbK@J z|Fwu1tVpb^5X|9|v_9mm`1-AAaY22TfATmwJD8PlyW?ZlJz=NTU*BDYHW#Vdax%%~OwPwq7x~?uNna2r6TI?Ag-ZGZb_Pt{ zv3BV5Y3d$>To-+Utgeh~rDYSS-g}TAm8Cj0q=!)w-rOscGe3V$KNax_9>B>D{m~e_ z*;nGXo9=7{F4z!GmQL!6PU90PYlrpP2)I(>5f!Q=we4w3@|)^@d$fovUUXQ-Ka8+8 zGOppF_Tvs!YbTgPo86(M6>;Cc=Y-7EoPa{h9X5gYzl28y_ zO>mhkJyLR5ASo(EZ zs}n>7JHbqIj9)e5D30($#5h6Akb$+)FKt0_78U0yom(n)sUfwqIjrhdnm;rn?wO;} zd~~a*^l!(g74IuGM%r|C7x+ZpryOlGbgqA~1h7ve%G(pLXHo9=y!v&GZyd(7mFc`} z!L+TxV`_Utdcir5V*!mD&1F|bOX&-Z6E#C!JSdCdRV#n8rh+uzmb75x0x;Df3}v%- z!^9GsRidRRX*Q6xxlZ*SIxVP)unBK!p)6%jufTNEQ0BZNDf*8z27e71Y-&ZL1hBLT zPlX8NRg6)nvZr=FLNMYwL_{(qxK&4+`Vq--4Ke2TC~U3+GCcm+E;;!L#G3KHV7sdm zAj%)g)-{#0_I$V;j-RP>bb9C1HX_|pbvHMGOnOIGB>c8^;!b{Bk==UuwbeGu^bM$0 zmUp-64>c7UV=BNcfq8O{iIcHVsm~2Hy78`_1I`Z#o>+R`++ee;0povl(-hurIDyHj zk7dOSp%GIWAB{At)K+M;Vzw!^eI!Y7qiA|Zn&Dp(WAIk%haRc#^A>MYWW?>IZF|~b zIdfIHT$gXT(KlYeP*w2`)7W=W@?yTtZ4k^MtYZhBAizA`_w*Y)Lxy7AfHmxN#WVps zEowbNJyHn-m!&P0@D2=m4VDfT$r2sxi~t@<7LH+UV9!4sDcj6zxo>=o-EZr`Q7B%N z_`m9MwCVZepIm!SpAAVb^AkmPr>^Z^v7_+EY&l1ix^pNZXcX~62`soc(|*K>+d zo0bo6+4iOD>-{AL(l`_cK`~TDSWXX+sM7f3U*{sDc5LEco1YYwueq$(p<%49!aX{T z_9^-$$3))%K3A5-l~{v+g`YIW0<*TV>fZp1e^vq$i(hn1#g)q!#~JUUUeaLSfKjhv zj{h_(M;}6;a{O91FUlt_;vbs70nuka;Wq2p>GT`WEMH;~ZGSydA2_j>MdnW>FQN34 zSLAm+PNXbsN^)>n!=U)cab} zGdiau>T=MdGcBJ&u^IELHEYw~oAOYNeJ9^-#RXt%)~fxw;K!EIb(7*6(&;BQ3_M+> zt&)(c%f$dTM^?33>ne8bR7D@N**L)jBzAGVJy(s;Id_|4-_7FO&t)I=_{y0~P+!|` zUJCo6Do|*@C7_@(e#np1*^vtMPcr|r>eV=y@irfgdv+agUn?P6nDCG&`mZkwrA5ho z=m=~NCtKSi=SaZ4R;(~gz+{W%YP)u+woq8!tU?GADqibhloc*Dhw-)QPa$FeBr3>6 zQO|EKm793{4UZd4+D~WKk5%XLo83_4Ez&s5WC^bx7UpdhcQjPs#S$v*?j`X0#i6Rx zK%4|OFiZ79tJNM@#?t1^UUcF7wa~gJ-*&FnG90W(6dE6|sNMx)Sy{GoHSZYuKC{Uw zkGO0Ro0DeDeW)}0E_E*Ne7Qo###7pkR4jEK>P&ddfbYv^n++Z9FO4OzUFyaE z+6T$*HhVQ)UijX0rq0AfqDEShqT13E$|<$yy}a)+U$EV1Zcmdx`x6nKwx1vB$+mWf zPNw>`A0SP@J?vXx>KVSOktHr=#r!ww{H$i_mc+IPIpIn~F*6~{Ze5Q+?Mzb4Jr&*+ zOrB%xq7&~SYt5QE+lit99fzUQ>RXoT(5GJ00gtt$2vu_`#KrtT0cGzu=+7?+>htSfa)l)Zt*yVLXVqbZXyTG{Sec`p-@floK2n8GV{2B? zKw{h4>{G%Z;NQ@#&r%MGjE`j~m|$G#_illMVKXjhO3qTti&ef*Lar!-(MW}?$rTD{0QJ^M-d7)U?E?xOBj zcKWnotIt7J==1cpSwD-*pF=p?nh1Nqpn10}=%+2XFIB_65OJht41{|DSaBfrP73)H`#SiIzjuqh|d&5&PPNma(t0 zc=vv*xgJ!^dey+d?BbKoF-(n4 z>QTGc#yq1GUTaV)1HXc?_*av79`YPju{W&0q8LA7iEjk5j68ndvqn1#<$O)aSTBwZ zGu!}fyo30DHDMl*Se&b;kgt5!0O21I%vLU>Sgt1;BX^{k!1EMnEiB0--8%V^O$`{% z12*8MVbLMR7{pJVXDX5A7Zvq;P&Zcf^W-bR5FAN&A@fm1yV24i+%XPa>I+z-#18yj za^j^l_YpRZ*s#UO_<>S;Q6TO*GxP$NTNu5qBt&xU1!Tc*9~ET>q_ljP2lX3>hX|DU zD547qoMED2!@vr!kVY7Hi(}eE*{1E<*nyc?ee?Lx?v=nG(i&FA2yUxTIuxLg5s#2G zW^FJq3}ngi-#?88NUS7q6{gQe{OVMt=>V*{U#c+;9E5Yl4lTr!8Iv5bP0ep_zwCEj ztbZC(OKUYDgi?<-tyHh52ae)2Vt0r=R-|-kQz92-ovsllWDs}C9T&3 z#{!a@SB#J4h`?)1e+nKa9IQQuuYc=$VwE^XucxtymB3ivWnyUffo-rKho7heh? zxQBg~cD1vbh~^ukKt63`a#x6-CM#(?$m#!4R-5V z_WJWF=!^JM;8uCGykFof%lNO-Bf8d;r&15$Q~q&H6A(LSvFyU1!nLQ{t!>YG_~Y$H zzl99#jJ1(nYY0?s&v3_<^;bNyAn!nj9H<_;%5t`Z{3`HDTZN61IMtWAB#JqfrecId za$GmxtxVp`?U8FD9yCf>on@|a{G;p=Urn1< z{CooPX4b+Om*NA@wUq!5i!5v_q&`Fa>6m9}p6)c2yUA!wGpc^E)#7gbX=_6e8BokuIMW zJgSuI5uhO@nE8sp)Yy&(C#`fUEh4O2b~ETDu8=d5(}Hj@>@ zMyIARO{}6}g}U7yJ?f0GTfTSijz`*|&9U5A>C+AZ(NA2_#dhZrDeWcNrhOH2tTzi;Ha@?|2bu6o->FSQ|MYjCPuJn;xf)Y8&$=3Azk=NNC=) z$hqx9AAHtwr6<3idIhChsi$Kg+*~G7v zTao*<6_$-k9l@v)IVhth(dtNeyJMmXh0f4ivYgyHw=L0CY2Vl2GZSX{8TPVrux`51@s)3Hmf4nJI)JZ%Lk=1D_ zp5>dqWhbbhktR5Y_|~zf6jh5Ax>w?2QShThb!@GNjDHHI;C+aHg?WJ<)iq9pC_GAo?fJ2YJQ{Hsct0(XYtj&>jj?NKc-)kcXH(=byfT|ehg&PJp9e1dmq*6!% zlB223d~OUkMpi}gZG29-DC4L#8;zrZ(+L#K_v)r51mR>j=qZlIGN&@my#TEERMXH_ zK<@Qqk$6KadFJ$+TnmLKv0>CbF(_72b14Iw&_eHm2Wx@gI9BmVEQTp0P&0R}+@5>X ztnot|9oS?Xvejqmt?mRe%6qE+0K%AtY1D$Sx#|I^`i#!I-J5?JGgQQcXE#C$<@`nD%LZCRP{B+~%;+N)fvTM}P*9VWvV(Eu&yQ!L4;Jra}mn zPs22Coo+F~;~#RhR9g3&9gBB+30&?#i{yr*JTXcJ;%@mZR!1fD@7xM=@S?Si^Koa4 zAAX(%9mHm6nGbh|oj|;ZM|=ZDi%Y!5WS$iq%#|ah<*?V5!>N|56n9`Q?!kAB$vpuy zGjrYjGZWO)i?e~x4N}+qb&;c5TXCCIwLJjM7^Bai@&OI=M!}L!KN_;O)ouU;$`4L7 zv}VRXsdpZEfGZPSM;SY}S@JN1>Pa@;h?N3rFnBJKF0ovCowAhB{9?7mjo%ze{K&lS{|&Ap%=REd$Z!s9+>ha<3F^~U#1(4f-;_0N*T z5~s=^V)x;~q$=J)a%Wqdq`XO@vIGS%Y%nR+JWCAuFu1cN~G#)(jzkwNvF*4W@{P207<} zS?pRR`p4ml*BaQeu*9FOSlo{vSnpNB$ulSk#PT3hutddj>&KV=MN6w;T-O#QKh|&I zOf94&Wz4wfwSHb$NFor+%&jvKd#Ay-A%4g;ldSa>#O?JAfe{`U|2^9RQ4(F8sgpD>J5s-=h~q`TkT1 zwsPq{wh8+nel=R!OvwACCLKm9;}}adC*nV;WNkF@oExan{;gxwZI=c+i;ds3wO{Iy zL`Q2i&Ig9@tcSBX279IVBo%mG&5X815CPbkpPzP*8?1x$b8mE7sE1Lqw@e%B(a=3- z=UqEF7uVqa^Mz*V$?U~q?QE7)gX=?}tIdXj1lx_CMvmyP)NSxd82u{Nlr`HV_FmnS z@71orZ47U>`!^cg&UdvtVcu>(rCO?VsyQK~&bn7Iep6Pae@}a2Stlv$V~V#A(WP|i zxoh&GzmR;OHr6GURRZxxb+`|sO7)%T7b;P=Ke4q;(W0$|#7qotC>)6tMUH=IBpz%9 zQ$=$$ZV0-c9P^qym-4c4yPt=F73G{HV`27yfEdbEhb5!MJZfL{~fd;va~rm&PE z^}mZ1i(j4eV$92<2M5EoAB9(3YC1x`eq#rtNV*ra+UU{W%w5WHm(c1mStzkN8 z{{Ul68slmaK6hh`ecBkifs`u&_foG#fIID+6Q0cu?L~m=GalG!E8qLG0!| zH@H)9>Jr2;>dZ0XO+!rpvA4X8F62+gujN@yI{M*jya6c-l-}e5A zYv}e0j#bjPGT`$!50?jKeoy+JXaQebwf6q-S2xxxXBg~}dVyUd`E9DVpdCyw$o0E# z{$Q?hZXg)r$B?JqPSuylC#WX3wvB-&B=TBj-SqV&6W;2yyvqBbRI-l_?M62!cbZ}k zSnK54>r*n*FclqqxyOm8Mt;c;|X;Vpo>5n9`T z(_Tj0(-gtH(!^&BSo9bbubbIqBYHN^Rib*^Zt;M(m1OBRs}+VW?=9aY3*wM#%Vr^4 zc#kTS_odeY&9TfHP^wCE>JnsfHN0Yy%xDekT7qyNkLuC^;{$?DeidZasVKtP5$0an(HTV|#ACQ@j6pN`)vcw=DFLk{{uHZel5dT~l}{qn-KL#)8)U4F{p~5r zEfUK#o-|yTCLZk;J$e}210B&GbqjlGB2M}@#&J&DM{ZAji1L=WC5Lr#0Ue+%fg4;X z`&2pgQ~ji`@vA1V)21K^`!n{W6y>gyrht1%p>fthpZL};aE^8r(W=jVYu+YiZ-pdo zmR$C1G1W6$%(^Crju#Li<-RFtbWJS>a`8#%;}xW9!;>14G}VbF;~+wP%J*WWa4lkE z&vLd~ttQQQi5b*>tiqNykRU9M3Pq={yYvX;od-P3ETUPQR{XvrQXF;bn1 zk-0W8_UI2W;yZkk)czHv@oI)VyH9-rsAytfc_0-dwf8E#Ac&rL6;9o&Fv6n+@j0U@ zAY)^q3!eaM2V<^XvDp+oO*h)=E9{BasFU-czO8v4M2#RlWc=#Xs(Y;lW*1{` zasnq09yH{82dFY{1Wn#$g;p-Pa~n1uOm&inKDBcm z%nGs-aWOUJ`_(!*2UYuD6Sj>!$UJ=N|3X*}-G+O(C`uE9&;U1rMF-50kA-ila9?zqC z>HTu! z<681S%HQstonQJv)ufAWc9V5kfYh#dvY-3bsu?v|rr={dc_FVgELfgKBv>|bQPg<~ zlxKUoGv+{|!62B7peOAqqV{v#eHy`N2Qs7@&AVj8304P`2}&NNg=TncNf_+&%spZD zdUkxQHpe|`FSA#SbJ^ODDlZHE;K*hxD+3<$v{Dh_+E!z;us`-#pB(3;Q$o96>e~+{ z6nO1!B4Vttu;vJ)?X-YqJ6kp+`m73iOlt-^a!w!#j#5x%3vYr0D}9_|r4{;)497kj z!lutu1jJHjglb1F(xh&$El4ohW3KjV(P~4F(uMa$KW(YNm=7-h0GL08LvmWHfJpCj zrT~eqVL!XYGM9Gf0ZZAObp%$vE$mzl$VkH;(;@sQazcyjVh2eo9T>gYHa1n&ZFjQW zp*;7EJszW{vC9b$_lC6X>tr3)$?Ei2mRUF=#=dV+DiM2%#=~)UY)cp+2dhn0jds}} zLlYM8GSWMjSv#!=>6(r&vUjG1eid=1mgBI)+~x50w*Gm|LU`2Rq;}{}+09NEE+oM9 zh{5uv7e~9e88^y{;etO3cZ_!eo@*1jaFAz~;G|j!0l`>@ltQ+TnQ=<(muj`WD)7X!_24wo);i^%%8yN&=j8{sjTFHNyc|EfO6p~D z_vJ;T-QD_p+a_7%NR8OQRXF_`SEuheVWk}RH=c*(_PaH6F$PPJ7pbQkW7<=dxCcFG z5$M;bMkJMok*Do6>*Q=VXaMF98uD|gEmV=ue~B3O1gv41;&IYk8VuVa>VzVx$L(tB z8*>=kc-ha55XB<6Rg48bJN#>(jZ*o@S7xp@c6ik0Aj|vA(B;u(L5ScapA~xAlFs9J z?jSt#H3Dd_6ClHb_GPVAd0$jJnu9@gYWND^54T>EQocce@}T}Tu*+qTUCs|9Ehbzc z-Dgz;ob}MEDQd1el_ylx4;GeBh_5yM*~2u?nzn0ST2qPRKi?XHX(3a*(2>>H)nkVs z$ghg}X8k0Dk1Cq(+_!m;_$tafIKHr&+lPT;P9%}WH&9x$^U0`n68)$yQd{Zp$V#+f zyy=+qJ2G=2jXac^wKL98?#BQ=WYXf|=?{Gr^;I>W5>G}eii<5aGl1FqhP@??iY`}h z9-z_Yx{^_j$d10s0fC=`M(jEhLVVj9g_aw9VWP*C5?iQBhTe-k6w=o^8E*2$Azw2~ ze>YlD-4YKx)`>yp*kopF;<@bN4cBymSQ5)J4d~N8>8Uo)TqXiD0rrWn9S-dm^np(- zn$qyuk056Xak{_ZHI?0A#X)a$)LO?;j310l_CugLlJpkHWKSG&nof5t4i|5BgV{*s3n@bDlz^H0YH^>PTCltv#CnDdv2U z9f(2UzZb&mb`zw70q_MlXn7sK6TQ6mn24Sljf+QOaA~&wKyNV70olIK60>0T^-<~#=GQP zwGSgP2*vmOq^LY=?eD+ds5P9iu0#?KjZPi!?IM+jy< zeWw=hNW}RyV&NTf|Yei^Byl9SX++YiM{nj;88d;72BXv2VZ%4j25;5^Ln}^I+ED{IVBFgtD|J7! z#i?;Jy5TPFJ{3uP`jknoG}LbPn9O|7<6fTj$yIkW)YWdvpH)dv2XKb5wG{xY!a(&DOjv=NwsB`PTd>j>YlR zZifRcyEpEtUpn8$L66ksh!(Y%3wS_3cfY8`g0S zeVYChc(u#G-RZ43_=wl=s^`&zE`G7(T8|FTGslq{4>MV{PvPJh79(gbSv${Y$D~8> zq-ED`%MvDu2Sgu*GY-v47r7#5kQJGBM&XxxcpUpz6`FKn_7+8H)ovZI_aKAGe<~4p z^O+0cf{LHX6GD*@drKjM~K(ZqI!O z5%h0eL$6caNvcx6Ke9K5hao*6nMU62CwKrgE1&*rRSI92pDic;?O*yr)e_Uv2Gb+k zhAXMvDPldpO5G}%c7g&hZDZXW)AFx9JXoGaCK~uS!IyPHc$EIYEeJwdA;-Jgc;ebxM`B_@-A z%Ad3`{*>`9KqDOM8jx-xT=NI;q9xM-n6B-Qm_LOd)g@&mz$cbyey1ES+B6Q{>rr^H z7>iS$L;&4df;>w8R9LQOM&3GwhB~Hy(vUFNKNhuM$D7n%Ejd>U3xV%c;ZJUe3z)SV zn8r?{rjI!P04gQMw{|1?E9)0LmM_YvNz)E96SqM0MY_`CD~nl&$MU4Nm&XHX@B2Hlc06NID>`oCs){{_szYs1h^;=(BFfA8{u!z&hyiRm6Gw9+*>2YTXFU)9Dd z8A2;ox{AZ6Qnj`_&1kBA%8I(!ZDQ8rZgzBJqqd`OfMOW^D%P@gcInP!4#TTrwmpK- zBTcy3)@bGhbsKU%lLPdtP70cu%oUER*E9Z8ix>Sk?Rz!B1YM5kQN!rgx?x<;`A%9} zr>8xykgSE;=o5*sKSsV4>-*pT04V(a%;>41`B}Jv0m9-#LX!SDxO&9Lmv*MpTB49G zOpH8QN&=WJ4+z2@L95Itty=OiJTGKGcP1i+}D>5({{k<^bJunwhx} z9q6oLZd=t}&ma{WMRyxzZTNERYEo@A^t?S=0DB0In~L$|{WYDwI#ym)kPX`uQPPiQ z`M@$OzI)UQ0=8yB$gO5u%bD=f>52*DwEJBcj(%+?;kAe^2BRQ6omh%b)<6*GcPtAevF~TZ zQRC8YAsyv~`tDNg6iPC6EO{ZQ?vxC}Qyh7z%}wO;zd#*GCy4Pt#D1uwdcXrMzJz!Y z!m7p2=;405{{XtEJ>A259y!NRidY-eRxQ_8wT@gdVh4Z}*xEZd!jlxkHLf_7*L&3A z&+7w&UWAa_uVff<%}b*fub5cAlW1(FZ_!*QlTc*UA>H1JX_KQZCj@CB-Sr$Ad31G= z5A;$6Lyb5KBD&{YLESe@+2oX;ugfBq7ZPsxWL48?+GVNl+DKcOQ^K&Rr2~^2&ehVWvnZ+7MvbB@Ba<7BzH6_&5#_G#%2Ue{yN#>9ck7wj?Ktj#c zsJ8nfsJY6*Jhz1oT~x*f<65(G-ZhxEmgWv1+WZNqZnIvxlZIL;I98XRYQ*Fel|p}$n`OK-#N*Q2<2jES8+x(q#O*fE9&3 zNxG{I&5oAZHvYZcz|T$q5XT%_R4EEI{X&UTwY!+#A$6mYu)U?;&3?R zSKI6~>MF`ogG=OG6S$Ylp_zV9TB$}V-nnbUe(+Z`mRXPW+Z|1HXXP{K-&gdl=$`v$ zv^BSVLTiX+QFA9|m`4Qh!SJtzHBXUurF}blJ201sJZ໬_o{{SlAw^N*|UNLQ0 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%0Avai`sdIF2)q5baj| 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 M?N_VSGSrj*+1th1p8x;= diff --git a/examples/pong/resources/ball.png b/examples/pong/resources/ball.png deleted file mode 100644 index c296859b6bc4de85878d2fa3a840e767acc2543a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%>?NMQuI$%2goNa@^DaD21`2T& zctjR6FmQbUVMeDlCNqG7Gdx`!Ln?0FopzD;fC7gLcl3_`c~))5`pvGbYG9r9W#4)0 z`HC(lEahXCPRV;Fz*KOaDfCMFd6%h++&GV0t4tCpcq!+S@0}*;A-UyLfw{-3)gF`6 zro8k{al9P7Q_!*JV$ZUv|F8UA?y>1rd8l>G{Bx6Do%tru#=Xky^X!k;mb}_L!6;)& uh;F^(rM3HZsl1b$QgyQaZdX9jL?%8K|4i1Ef*n9NGI+ZBxvX!A z|33cyDNAI0g z%**Aq&@wxe9~5kmdUQ+L%*wf1!pCB+v+%|37oMGW>1CO_Xf=p|-P#Opq^LVD)3vz!O8(_+55^;0+f8iFtyp*AO6r8K&w@_8N;fF|eBZ4%o|2(#fzy;u@+hyaI>xVfXk+rk zP0XnsFZWok&Hh>QCShk`xy^}Fy1TP$8y-u<%0JY+w@Git&8AR>8GCAyuC=MYtp5Dc z;>5u#iOaXG;N29zT3Kc0G@0z%RXf%l=e#UlvpXd$s(e$Io|{Zj+5Km7p|dPo56|?} znR%irY`)d0#e3L_&Yk?syd*AazQJn`uSemyw+^h{_=4zLrxn? fnb%+a@>%xCuF$Vx?d(awgvsFP>gTe~DWM4fsTN2B diff --git a/examples/pong/resources/paddle_right.png b/examples/pong/resources/paddle_right.png deleted file mode 100644 index d408c5fd66ba31ecb60331a252a7e8f2e8b24a27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 683 zcmeAS@N?(olHy`uVBq!ia0vp^l0clo!3HFYLuc0jDbW(wh!W?b)Wnj^{5*w_%-mE4 z_Ujx%f*KZEO!FQB6|k3h`nm#zgt-lbCeECD7bwJ8;1OBOz`*qZgc+UIn9N{cVAAw- zaSW-r^>)^7|Hlpj$I`FQmSFnmYNOKifj_43(o3#?|0g|a>1)#C5RvNYXkRp;eQ)(= z-#rIgS}cS_Jd7`8{E0Z9zdv&N{Fn3RUMz~adzbaJsbO1ybc}5H<`VhUrF}k=1T59> z-~L-0{Py`O^XYf~+&%2&a)942%k1R4H#)0s-c`Mval*=8s`bOCChyOI`=>S@&0S$> zaUkzxnboB`X*XrIY*&BX_4{`vA6x#^Kd-+tAKR$+VO^@O>RU6__I4(ZzW$TjqEh<% z;udCl{rfpHZLgf~x%18Pe7tH7;ygE&Fmcar`1mGOhi8h;d1X6xmV)bVxu)Dd{WFgz zIxR=WDPl$RQi)f0PIew(*jk^xV#47Vf$TTjuP)4S?}&Q+pf)7P*hSI&cz=Un-siX9 z0{gyaD^6T0(5m60$Q9V(B(jJAbD!jq-}-F~0cpA3CqKQj>k1Ir@M}VSjmnMMH}fwC zZ+@v9pC;SjkY@bpu%f8Hzlhr8UvWxXHQ zvCh1+MCM2^yEUXus*#<4@*O+#sr*M|R$5$Zjb>=oIX%F4|wyvBA#O`e3H(yzVu9O_DUJMGgFU28pC zvwF?hQAxvXsecond; + return &myIterator->second; } private : @@ -144,8 +144,8 @@ int main() sf::Text shaderStr; shaderStr.SetFont(font); shaderStr.SetCharacterSize(20); - shaderStr.SetPosition(5.f, 0.f); shaderStr.SetColor(sf::Color(250, 100, 30)); + shaderStr.SetPosition(5.f, 0.f); shaderStr.SetString("Background shader: \"" + backgroundShader.GetName() + "\"\n" "Flower shader: \"" + entityShader.GetName() + "\"\n" "Global shader: \"" + globalShader.GetName() + "\"\n"); @@ -154,8 +154,8 @@ int main() sf::Text infoStr; infoStr.SetFont(font); infoStr.SetCharacterSize(20); - infoStr.SetPosition(5.f, 500.f); infoStr.SetColor(sf::Color(250, 100, 30)); + infoStr.SetPosition(5.f, 500.f); infoStr.SetString("Move your mouse to change the shaders' parameters\n" "Press numpad 1/4 to change the background shader\n" "Press numpad 2/5 to change the flower shader\n" @@ -249,8 +249,8 @@ void DisplayError() // Define a string for displaying the error message sf::Text error("Sorry, your system doesn't support shaders"); - error.SetPosition(100.f, 250.f); error.SetColor(sf::Color(200, 100, 150)); + error.SetPosition(100.f, 250.f); // Start the game loop while (window.IsOpened()) diff --git a/examples/sound/Sound.cpp b/examples/sound/Sound.cpp index 8215d48b..fd31ce7a 100644 --- a/examples/sound/Sound.cpp +++ b/examples/sound/Sound.cpp @@ -35,7 +35,8 @@ void PlaySound() sf::Sleep(100); // Display the playing position - std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() << " sec "; + std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() / 1000.f << " sec "; + std::cout << std::flush; } std::cout << std::endl << std::endl; } @@ -68,7 +69,8 @@ void PlayMusic() sf::Sleep(100); // Display the playing position - std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << music.GetPlayingOffset() << " sec "; + std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << music.GetPlayingOffset() / 1000.f << " sec "; + std::cout << std::flush; } std::cout << std::endl; } diff --git a/examples/sound_capture/SoundCapture.cpp b/examples/sound_capture/SoundCapture.cpp index 83e85009..41b7da76 100644 --- a/examples/sound_capture/SoundCapture.cpp +++ b/examples/sound_capture/SoundCapture.cpp @@ -76,7 +76,8 @@ int main() while (sound.GetStatus() == sf::Sound::Playing) { // Display the playing position - std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() << " sec"; + std::cout << "\rPlaying... " << std::fixed << std::setprecision(2) << sound.GetPlayingOffset() / 1000.f << " sec"; + std::cout << std::flush; // Leave some CPU time for other threads sf::Sleep(100); diff --git a/examples/win32/Win32.cpp b/examples/win32/Win32.cpp index 97c94e42..34db7907 100644 --- a/examples/win32/Win32.cpp +++ b/examples/win32/Win32.cpp @@ -81,8 +81,8 @@ INT WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, INT) return EXIT_FAILURE; sf::Sprite sprite1(texture1); sf::Sprite sprite2(texture2); - sprite1.SetOrigin(sprite1.GetSize() / 2.f); - sprite1.SetPosition(sprite1.GetSize() / 2.f); + sprite1.SetOrigin(texture1.GetWidth() / 2.f, texture1.GetHeight() / 2.f); + sprite1.SetPosition(sprite1.GetOrigin()); // Create a clock for measuring elapsed time sf::Clock clock; @@ -100,16 +100,18 @@ INT WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, INT) } else { + sf::Uint32 time = clock.GetElapsedTime(); + // Clear views SFMLView1.Clear(); SFMLView2.Clear(); // Draw sprite 1 on view 1 - sprite1.SetRotation(clock.GetElapsedTime() * 0.1f); + sprite1.SetRotation(time * 0.1f); SFMLView1.Draw(sprite1); // Draw sprite 2 on view 2 - sprite2.SetX(cos(clock.GetElapsedTime() * 0.001f) * 100.f); + sprite2.SetPosition(std::cos(time * 0.001f) * 100.f, 0.f); SFMLView2.Draw(sprite2); // Display each view on screen diff --git a/include/SFML/Graphics.hpp b/include/SFML/Graphics.hpp index ba994331..f20aedd4 100644 --- a/include/SFML/Graphics.hpp +++ b/include/SFML/Graphics.hpp @@ -30,18 +30,26 @@ //////////////////////////////////////////////////////////// #include +#include #include #include #include #include -#include +#include #include #include #include #include +#include +#include +#include +#include #include #include #include +#include +#include +#include #include diff --git a/src/SFML/Graphics/Matrix3.cpp b/include/SFML/Graphics/BlendMode.hpp similarity index 76% rename from src/SFML/Graphics/Matrix3.cpp rename to include/SFML/Graphics/BlendMode.hpp index 7de4b0f6..448903bd 100644 --- a/src/SFML/Graphics/Matrix3.cpp +++ b/include/SFML/Graphics/BlendMode.hpp @@ -1,40 +1,46 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include - - -namespace sf -{ -//////////////////////////////////////////////////////////// -// Static member data -//////////////////////////////////////////////////////////// -const Matrix3 Matrix3::Identity(1, 0, 0, - 0, 1, 0, - 0, 0, 1); - -} // namespace sf +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_BLENDMODE_HPP +#define SFML_BLENDMODE_HPP + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \ingroup graphics +/// \brief Available blending modes for drawing +/// +//////////////////////////////////////////////////////////// +enum BlendMode +{ + BlendAlpha, ///< Pixel = Source * Source.a + Dest * (1 - Source.a) + BlendAdd, ///< Pixel = Source + Dest + BlendMultiply, ///< Pixel = Source * Dest + BlendNone ///< Pixel = Source +}; + +} // namespace sf + + +#endif // SFML_BLENDMODE_HPP diff --git a/include/SFML/Graphics/CircleShape.hpp b/include/SFML/Graphics/CircleShape.hpp new file mode 100644 index 00000000..fb181948 --- /dev/null +++ b/include/SFML/Graphics/CircleShape.hpp @@ -0,0 +1,129 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_CIRCLESHAPE_HPP +#define SFML_CIRCLESHAPE_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Specialized shape representing a circle +/// +//////////////////////////////////////////////////////////// +class SFML_API CircleShape : public Shape +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a circle with a radius of 10. + /// + //////////////////////////////////////////////////////////// + CircleShape(); + + //////////////////////////////////////////////////////////// + /// \brief Set the radius of the circle + /// + /// The default radius of a circle is 10. + /// + /// \param radius New radius of the circle + /// + /// \see GetRadius + /// + //////////////////////////////////////////////////////////// + void SetRadius(float radius); + + //////////////////////////////////////////////////////////// + /// \brief Get the radius of the circle + /// + /// \return Radius of the circle + /// + /// \see SetRadius + /// + //////////////////////////////////////////////////////////// + float GetRadius() const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Get the number of points defining the shape + /// + /// \return Number of points of the shape + /// + //////////////////////////////////////////////////////////// + virtual unsigned int GetOutlinePointsCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a point of the shape + /// + /// \param index Index of the point to get + /// + /// \return Index-th point of the shape + /// + //////////////////////////////////////////////////////////// + virtual Vector2f GetOutlinePoint(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + float myRadius; ///< Radius of the circle +}; + +} // namespace sf + + +#endif // SFML_CIRCLESHAPE_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::CircleShape +/// \ingroup graphics +/// +/// This class inherits all the functions of sf::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of sf::Shape (outline, color, texture, ...). +/// +/// Usage example: +/// \code +/// sf::CircleShape circle; +/// circle.SetRadius(150); +/// circle.SetOutlineColor(sf::Color::Red); +/// circle.SetOutlineThickness(5); +/// circle.SetPosition(10, 20); +/// ... +/// window.Draw(circle); +/// \endcode +/// +/// \see sf::Shape, sf::StarShape, sf::RectangleShape, sf::ConvexShape +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Color.hpp b/include/SFML/Graphics/Color.hpp index 8307ad72..4838239d 100644 --- a/include/SFML/Graphics/Color.hpp +++ b/include/SFML/Graphics/Color.hpp @@ -64,14 +64,15 @@ public : //////////////////////////////////////////////////////////// // Static member data //////////////////////////////////////////////////////////// - static const Color Black; ///< Black predefined color - static const Color White; ///< White predefined color - static const Color Red; ///< Red predefined color - static const Color Green; ///< Green predefined color - static const Color Blue; ///< Blue predefined color - static const Color Yellow; ///< Yellow predefined color - static const Color Magenta; ///< Magenta predefined color - static const Color Cyan; ///< Cyan predefined color + static const Color Black; ///< Black predefined color + static const Color White; ///< White predefined color + static const Color Red; ///< Red predefined color + static const Color Green; ///< Green predefined color + static const Color Blue; ///< Blue predefined color + static const Color Yellow; ///< Yellow predefined color + static const Color Magenta; ///< Magenta predefined color + static const Color Cyan; ///< Cyan predefined color + static const Color Transparent; ///< Transparent (black) predefined color //////////////////////////////////////////////////////////// // Member data @@ -197,27 +198,28 @@ SFML_API Color& operator *=(Color& left, const Color& right); /// manipulated very easily: /// /// \code -/// sf::Color c1(255, 0, 0); // red -/// c1.red = 0; // make it black -/// c1.blue = 128; // make it dark blue +/// sf::Color color(255, 0, 0); // red +/// color.red = 0; // make it black +/// color.blue = 128; // make it dark blue /// \endcode /// /// The fourth component of colors, named "alpha", represents /// the opacity of the color. A color with an alpha value of /// 255 will be fully opaque, while an alpha value of 0 will /// make a color fully transparent, whatever the value of the -/// other components. +/// other components is. /// /// The most common colors are already defined as static variables: /// \code -/// sf::Color black = sf::Color::Black; -/// sf::Color white = sf::Color::White; -/// sf::Color red = sf::Color::Red; -/// sf::Color green = sf::Color::Green; -/// sf::Color blue = sf::Color::Blue; -/// sf::Color yellow = sf::Color::Yellow; -/// sf::Color magenta = sf::Color::Magenta; -/// sf::Color cyan = sf::Color::Cyan; +/// sf::Color black = sf::Color::Black; +/// sf::Color white = sf::Color::White; +/// sf::Color red = sf::Color::Red; +/// sf::Color green = sf::Color::Green; +/// sf::Color blue = sf::Color::Blue; +/// sf::Color yellow = sf::Color::Yellow; +/// sf::Color magenta = sf::Color::Magenta; +/// sf::Color cyan = sf::Color::Cyan; +/// sf::Color transparent = sf::Color::Transparent; /// \endcode /// /// Colors can also be added and modulated (multiplied) using the diff --git a/include/SFML/Graphics/ConvexShape.hpp b/include/SFML/Graphics/ConvexShape.hpp new file mode 100644 index 00000000..ac0d5be8 --- /dev/null +++ b/include/SFML/Graphics/ConvexShape.hpp @@ -0,0 +1,164 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_CONVEXSHAPE_HPP +#define SFML_CONVEXSHAPE_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Specialized shape representing a convex polygon +/// +//////////////////////////////////////////////////////////// +class SFML_API ConvexShape : public Shape +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a default triangle, just so that it's not empty. + /// + //////////////////////////////////////////////////////////// + ConvexShape(); + + //////////////////////////////////////////////////////////// + /// \brief Set the number of points of the polygon + /// + /// The default number of points of a polygon is 6. + /// + /// \param count New number of points of the polygon + /// + /// \see GetPointsCount + /// + //////////////////////////////////////////////////////////// + void SetPointsCount(unsigned int count); + + //////////////////////////////////////////////////////////// + /// \brief Get the number of points of the polygon + /// + /// \return Number of points of the polygon + /// + /// \see SetPointsCount + /// + //////////////////////////////////////////////////////////// + unsigned int GetPointsCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the position of a point + /// + /// Don't forget that the polygon must remain convex, and + /// the points need to stay ordered! + /// + /// \param index Index of the point to change + /// \param point New position of the point + /// + /// \see GetPoint + /// + //////////////////////////////////////////////////////////// + void SetPoint(unsigned int index, const Vector2f& point); + + //////////////////////////////////////////////////////////// + /// \brief Get the position of a point + /// + /// \param index Index of the point to get + /// + /// \return Position of the index-th point of the polygon + /// + /// \see SetPoint + /// + //////////////////////////////////////////////////////////// + Vector2f GetPoint(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Get the number of points defining the shape + /// + /// \return Number of points of the shape + /// + //////////////////////////////////////////////////////////// + virtual unsigned int GetOutlinePointsCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a point of the shape + /// + /// \param index Index of the point to get + /// + /// \return Index-th point of the shape + /// + //////////////////////////////////////////////////////////// + virtual Vector2f GetOutlinePoint(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::vector myPoints; ///< Points composing the convex polygon +}; + +} // namespace sf + + +#endif // SFML_CONVEXSHAPE_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::ConvexShape +/// \ingroup graphics +/// +/// This class inherits all the functions of sf::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of sf::Shape (outline, color, texture, ...). +/// +/// It is important to keep in mind that a convex shape must +/// always be... convex, otherwise it may not be drawn correctly. +/// Moreover, the points must be defined in order; using a random +/// order would result in an incorrect shape. +/// +/// Usage example: +/// \code +/// sf::ConvexShape polygon; +/// polygon.SetPointsCount(3); +/// polygon.SetPoint(0, sf::Vector2f(0, 0)); +/// polygon.SetPoint(1, sf::Vector2f(0, 10)); +/// polygon.SetPoint(2, sf::Vector2f(25, 5)); +/// polygon.SetOutlineColor(sf::Color::Red); +/// polygon.SetOutlineThickness(5); +/// polygon.SetPosition(10, 20); +/// ... +/// window.Draw(polygon); +/// \endcode +/// +/// \see sf::Shape, sf::StarShape, sf::RectangleShape, sf::CircleShape +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Drawable.hpp b/include/SFML/Graphics/Drawable.hpp index fbee1be8..707068fd 100644 --- a/include/SFML/Graphics/Drawable.hpp +++ b/include/SFML/Graphics/Drawable.hpp @@ -28,32 +28,14 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include -#include +#include +#include namespace sf { -class Renderer; class RenderTarget; -namespace Blend -{ - //////////////////////////////////////////////////////////// - /// \ingroup graphics - /// \brief Available blending modes for drawable objects - /// - //////////////////////////////////////////////////////////// - enum Mode - { - Alpha, ///< Pixel = Src * Src.a + Dest * (1 - Src.a) - Add, ///< Pixel = Src + Dest - Multiply, ///< Pixel = Src * Dest - None ///< Pixel = Src - }; -} - //////////////////////////////////////////////////////////// /// \brief Abstract base class for objects that can be drawn /// to a render target @@ -67,449 +49,24 @@ public : /// \brief Virtual destructor /// //////////////////////////////////////////////////////////// - virtual ~Drawable(); - - //////////////////////////////////////////////////////////// - /// \brief Set the position of the object - /// - /// This function completely overwrites the previous position. - /// See Move to apply an offset based on the previous position instead. - /// The default position of a drawable object is (0, 0). - /// - /// \param x X coordinate of the new position - /// \param y Y coordinate of the new position - /// - /// \see Move, SetX, SetY, GetPosition - /// - //////////////////////////////////////////////////////////// - void SetPosition(float x, float y); - - //////////////////////////////////////////////////////////// - /// \brief Set the position of the object - /// - /// This function completely overwrites the previous position. - /// See Move to apply an offset based on the previous position instead. - /// The default position of a drawable object is (0, 0). - /// - /// \param position New position - /// - /// \see Move, SetX, SetY, GetPosition - /// - //////////////////////////////////////////////////////////// - void SetPosition(const Vector2f& position); - - //////////////////////////////////////////////////////////// - /// \brief Set the X position of the object - /// - /// \param x New X coordinate - /// - /// \see SetY, SetPosition, GetPosition - /// - //////////////////////////////////////////////////////////// - void SetX(float x); - - //////////////////////////////////////////////////////////// - /// \brief Set the Y position of the object - /// - /// \param y New Y coordinate - /// - /// \see SetX, SetPosition, GetPosition - /// - //////////////////////////////////////////////////////////// - void SetY(float y); - - //////////////////////////////////////////////////////////// - /// \brief Set the scale factors of the object - /// - /// \a factorX and \a factorY must be strictly positive, - /// otherwise they are ignored. - /// This function completely overwrites the previous scale. - /// See Scale to add a factor based on the previous scale instead. - /// The default scale of a drawable object is (1, 1). - /// - /// \param factorX New horizontal scale factor - /// \param factorY New vertical scale factor - /// - /// \see Scale, SetScaleX, SetScaleY, GetScale - /// - //////////////////////////////////////////////////////////// - void SetScale(float factorX, float factorY); - - //////////////////////////////////////////////////////////// - /// \brief Set the scale factors of the object - /// - /// \a scale.x and \a scale.y must be strictly positive, - /// otherwise they are ignored. - /// This function completely overwrites the previous scale. - /// See Scale to add a factor based on the previous scale instead. - /// The default scale of a drawable object is (1, 1). - /// - /// \param factors New scale factors - /// - /// \see Scale, SetScaleX, SetScaleY, GetScale - /// - //////////////////////////////////////////////////////////// - void SetScale(const Vector2f& factors); - - //////////////////////////////////////////////////////////// - /// \brief Set the X scale factor of the object - /// - /// \a factor must be strictly positive, otherwise it is ignored. - /// - /// \param factor New horizontal scale factor - /// - /// \see SetScaleY, SetScale, GetScale - /// - //////////////////////////////////////////////////////////// - void SetScaleX(float factor); - - //////////////////////////////////////////////////////////// - /// \brief Set the Y scale factor of the object - /// - /// \a factor must be strictly positive, otherwise it is ignored. - /// - /// \param factor New vertical scale factor - /// - /// \see SetScaleX, SetScale, GetScale - /// - //////////////////////////////////////////////////////////// - void SetScaleY(float factor); - - //////////////////////////////////////////////////////////// - /// \brief Set the local origin of the object - /// - /// The origin of an object defines the center point for - /// all transformations (position, scale, rotation). - /// The coordinates of this point must be relative to the - /// top-left corner of the object, and ignore all - /// transformations (position, scale, rotation). - /// The default origin of a drawable object is (0, 0). - /// - /// \param x X coordinate of the new origin - /// \param y Y coordinate of the new origin - /// - /// \see GetOrigin - /// - //////////////////////////////////////////////////////////// - void SetOrigin(float x, float y); - - //////////////////////////////////////////////////////////// - /// \brief Set the local origin of the object - /// - /// The origin of an object defines the center point for - /// all transformations (position, scale, rotation). - /// The coordinates of this point must be relative to the - /// top-left corner of the object, and ignore all - /// transformations (position, scale, rotation). - /// The default origin of a drawable object is (0, 0). - /// - /// \param origin New origin - /// - /// \see GetOrigin - /// - //////////////////////////////////////////////////////////// - void SetOrigin(const Vector2f& origin); - - //////////////////////////////////////////////////////////// - /// \brief Set the orientation of the object - /// - /// This function completely overwrites the previous rotation. - /// See Rotate to add an angle based on the previous rotation instead. - /// The default rotation of a drawable object is 0. - /// - /// \param angle New rotation, in degrees - /// - /// \see Rotate, GetRotation - /// - //////////////////////////////////////////////////////////// - void SetRotation(float angle); - - //////////////////////////////////////////////////////////// - /// \brief Set the global color of the object - /// - /// This global color affects the entire object, and modulates - /// (multiplies) its original pixels. - /// The default color is white. - /// - /// \param color New color - /// - /// \see GetColor - /// - //////////////////////////////////////////////////////////// - void SetColor(const Color& color); - - //////////////////////////////////////////////////////////// - /// \brief Set the blending mode of the object - /// - /// This property defines how the pixels of an object are - /// blended with the pixels of the render target to which - /// it is drawn. To know more about the blending modes - /// available, see the sf::Blend::Mode enum. - /// The default blend mode is Blend::Alpha. - /// - /// \param mode New blending mode - /// - /// \see GetBlendMode - /// - //////////////////////////////////////////////////////////// - void SetBlendMode(Blend::Mode mode); - - //////////////////////////////////////////////////////////// - /// \brief Get the position of the object - /// - /// \return Current position - /// - /// \see SetPosition - /// - //////////////////////////////////////////////////////////// - const Vector2f& GetPosition() const; - - //////////////////////////////////////////////////////////// - /// \brief Get the current scale of the object - /// - /// \return Current scale factors - /// - /// \see SetScale - /// - //////////////////////////////////////////////////////////// - const Vector2f& GetScale() const; - - //////////////////////////////////////////////////////////// - /// \brief Get the local origin of the object - /// - /// \return Current origin - /// - /// \see SetOrigin - /// - //////////////////////////////////////////////////////////// - const Vector2f& GetOrigin() const; - - //////////////////////////////////////////////////////////// - /// \brief Get the orientation of the object - /// - /// The rotation is always in the range [0, 360]. - /// - /// \return Current rotation, in degrees - /// - /// \see SetRotation - /// - //////////////////////////////////////////////////////////// - float GetRotation() const; - - //////////////////////////////////////////////////////////// - /// \brief Get the color of the object - /// - /// \return Current color - /// - /// \see SetColor - /// - //////////////////////////////////////////////////////////// - const Color& GetColor() const; - - //////////////////////////////////////////////////////////// - /// \brief Get the blend mode of the object - /// - /// \return Current blend mode - /// - /// \see SetBlendMode - /// - //////////////////////////////////////////////////////////// - Blend::Mode GetBlendMode() const; - - //////////////////////////////////////////////////////////// - /// \brief Move the object by a given offset - /// - /// This function adds to the current position of the object, - /// unlike SetPosition which overwrites it. - /// Thus, it is equivalent to the following code: - /// \code - /// sf::Vector2f pos = object.GetPosition(); - /// object.SetPosition(pos.x + offsetX, pos.y + offsetY); - /// \endcode - /// - /// \param offsetX X offset - /// \param offsetY Y offset - /// - /// \see SetPosition - /// - //////////////////////////////////////////////////////////// - void Move(float offsetX, float offsetY); - - //////////////////////////////////////////////////////////// - /// \brief Move the object by a given offset - /// - /// This function adds to the current position of the object, - /// unlike SetPosition which overwrites it. - /// Thus, it is equivalent to the following code: - /// \code - /// object.SetPosition(object.GetPosition() + offset); - /// \endcode - /// - /// \param offset Offset - /// - /// \see SetPosition - /// - //////////////////////////////////////////////////////////// - void Move(const Vector2f& offset); - - //////////////////////////////////////////////////////////// - /// \brief Scale the object - /// - /// This function multiplies the current scale of the object, - /// unlike SetScale which overwrites it. - /// Thus, it is equivalent to the following code: - /// \code - /// sf::Vector2f scale = object.GetScale(); - /// object.SetScale(scale.x * factorX, scale.y * factorY); - /// \endcode - /// - /// \param factorX Horizontal scale factor - /// \param factorY Vertical scale factor - /// - /// \see SetScale - /// - //////////////////////////////////////////////////////////// - void Scale(float factorX, float factorY); - - //////////////////////////////////////////////////////////// - /// \brief Scale the object - /// - /// This function multiplies the current scale of the object, - /// unlike SetScale which overwrites it. - /// Thus, it is equivalent to the following code: - /// \code - /// sf::Vector2f scale = object.GetScale(); - /// object.SetScale(scale.x * factor.x, scale.y * factor.y); - /// \endcode - /// - /// \param factor Scale factors - /// - /// \see SetScale - /// - //////////////////////////////////////////////////////////// - void Scale(const Vector2f& factor); - - //////////////////////////////////////////////////////////// - /// \brief Rotate the object - /// - /// This function ads to the current rotation of the object, - /// unlike SetRotation which overwrites it. - /// Thus, it is equivalent to the following code: - /// \code - /// object.SetRotation(object.GetRotation() + angle); - /// \endcode - /// - /// \param angle Angle of rotation, in degrees - /// - //////////////////////////////////////////////////////////// - void Rotate(float angle); - - //////////////////////////////////////////////////////////// - /// \brief Transform a point in object local coordinates - /// - /// This function takes a point in global coordinates, and - /// transforms it in coordinates local to the object. - /// In other words, it applies the inverse of all the - /// transformations applied to the object (origin, - /// translation, rotation and scale). - /// - /// \param point Point to transform - /// - /// \return The transformed point - /// - /// \see TransformToGlobal - /// - //////////////////////////////////////////////////////////// - Vector2f TransformToLocal(const Vector2f& point) const; - - //////////////////////////////////////////////////////////// - /// \brief Transform a local point in global coordinates - /// - /// This function takes a point in local coordinates, and - /// transforms it in global coordinates. In other words, - /// it applies the same transformations that are applied - /// to the object (origin, translation, rotation and scale). - /// - /// \param point Point to transform - /// - /// \return The transformed point - /// - /// \see TransformToLocal - /// - //////////////////////////////////////////////////////////// - Vector2f TransformToGlobal(const Vector2f& point) const; - -protected : - - //////////////////////////////////////////////////////////// - /// \brief Default constructor - /// - //////////////////////////////////////////////////////////// - Drawable(); - - //////////////////////////////////////////////////////////// - /// \brief Get the transform matrix of the object - /// - /// \return Transform matrix - /// - /// \see GetInverseMatrix - /// - //////////////////////////////////////////////////////////// - const Matrix3& GetMatrix() const; - - //////////////////////////////////////////////////////////// - /// \brief Get the inverse transform matrix of the object - /// - /// \return Inverse transform matrix - /// - /// \see GetMatrix - /// - //////////////////////////////////////////////////////////// - const Matrix3& GetInverseMatrix() const; + virtual ~Drawable() {} private : friend class RenderTarget; - //////////////////////////////////////////////////////////// - /// \brief Draw the object to a render target - /// - /// This function applies the common states of the object, - /// then calls the virtual Render functions to let the derived - /// class draw the geometry of the object. - /// - /// \param target Render target - /// \param renderer Renderer providing low-level rendering commands - /// - //////////////////////////////////////////////////////////// - void Draw(RenderTarget& target, Renderer& renderer) const; - //////////////////////////////////////////////////////////// /// \brief Draw the object to a render target /// /// This is a pure virtual function that has to be implemented /// by the derived class to define how the drawable should be - /// rendered. + /// drawn. /// - /// \param target Render target - /// \param renderer Renderer providing low-level rendering commands + /// \param target Render target to draw to + /// \param states Current render states /// //////////////////////////////////////////////////////////// - virtual void Render(RenderTarget& target, Renderer& renderer) const = 0; - - //////////////////////////////////////////////////////////// - // Member data - //////////////////////////////////////////////////////////// - Vector2f myPosition; ///< Position of the object on screen - Vector2f myScale; ///< Scale of the object - Vector2f myOrigin; ///< Origin of translation / rotation / scaling of the object - float myRotation; ///< Orientation of the object, in degrees - Color myColor; ///< Overlay color of the object - Blend::Mode myBlendMode; ///< Blending mode - mutable Matrix3 myMatrix; ///< Precomputed transform matrix gathering the translation / rotation / scale / center - mutable Matrix3 myInvMatrix; ///< Precomputed inverse transform matrix gathering the translation / rotation / scale / center - mutable bool myMatrixUpdated; ///< Do we need to recompute the transform matrix ? - mutable bool myInvMatrixUpdated; ///< Do we need to recompute the inverse transform matrix ? + virtual void Draw(RenderTarget& target, RenderStates states) const = 0; }; } // namespace sf @@ -522,44 +79,16 @@ private : /// \class sf::Drawable /// \ingroup graphics /// -/// sf::Drawable defines the attributes and operations that -/// are common to all the drawable classes: -/// \li transformations (position, rotation, scale, local origin) -/// \li global overlay color -/// \li blending mode with background pixels -/// \li the ability to be drawn on a sf::RenderTarget (either RenderWindow or RenderTexture) +/// sf::Drawable is a very simple base class that allows objects +/// of derived classes to be drawn to a sf::RenderTarget. /// -/// Please note that all these attributes are hardware accelerated, -/// therefore they are extremely cheap to use (unlike older -/// libraries that perform slow transformations on the CPU, such as -/// rotation or scale). +/// All you have to do in your derived class is to override the +/// Draw virtual function. /// -/// Usage example: -/// \code -/// // Here we'll use a sf::Sprite to demonstrate the features of sf::Drawable -/// sf::Sprite drawable = /* ...whatever... */; -/// -/// drawable.SetOrigin(10, 20); // set its origin to the local point (10, 20) -/// drawable.SetPosition(100, 100); // set its position to (100, 100) -/// drawable.SetRotation(45); // set its orientation to 45 degrees -/// drawable.SetColor(sf::Color::Red); // set its global color to red -/// drawable.SetBlendingMode(sf::Blend::Add); // set an additive blend mode -/// -/// window.Draw(drawable); // finally draw it (window is a sf::RenderWindow) -/// \endcode -/// -/// Deriving your own class from sf::Drawable is possible, however -/// you have to use the sf::Renderer class instead of direct OpenGL -/// calls, which is more limited. To create a derived drawable class, -/// all you have to do is to override the virtual Render function. -/// -/// One of the main benefits of creating your own drawable class is -/// that you can build hierarchies of drawable objects. Indeed, -/// when you draw a drawable inside the Render function of another -/// drawable, the former inherits the transformations and color of -/// the latter and combines them with its own attributes. -/// This way, you can apply global transformations/color to a set -/// of drawables as if it was a single entity. +/// Note that inheriting from sf::Drawable is not mandatory, +/// but it allows this nice syntax "window.Draw(object)" rather +/// than "object.Draw(window)", which is more consistent with other +/// SFML classes. /// /// Example: /// \code @@ -571,26 +100,27 @@ private : /// /// private : /// -/// virtual void Render(sf::RenderTarget& target, sf::Renderer& renderer) const +/// virtual void Draw(sf::RenderTarget& target, RenderStates states) const /// { -/// // Low-level geometry rendering -/// renderer.SetTexture(&myTexture); -/// renderer.Begin(sf::Renderer::QuadList); -/// renderer.AddVertex(...); -/// renderer.AddVertex(...); -/// renderer.AddVertex(...); -/// renderer.AddVertex(...); -/// renderer.End(); +/// // You can draw other high-level objects +/// target.Draw(mySprite, states); /// -/// // High-level drawable rendering -/// target.Draw(mySubSprite); +/// // ... or use the low-level API +/// states.Texture = &myTexture; +/// target.Draw(myVertices, states); +/// +/// // ... or draw with OpenGL directly +/// glBegin(GL_QUADS); +/// ... +/// glEnd(); /// } /// +/// sf::Sprite mySprite; /// sf::Texture myTexture; -/// sf::Sprite mySubSprite; +/// sf::VertexArray myVertices; /// }; /// \endcode /// -/// \see sf::Shape, sf::Sprite, sf::Text +/// \see sf::RenderTarget /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Glyph.hpp b/include/SFML/Graphics/Glyph.hpp index c0a4fdaf..38ff22e7 100644 --- a/include/SFML/Graphics/Glyph.hpp +++ b/include/SFML/Graphics/Glyph.hpp @@ -51,9 +51,9 @@ public : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - int Advance; ///< Offset to move horizontically to the next character - IntRect Bounds; ///< Bounding rectangle of the glyph, in coordinates relative to the baseline - IntRect SubRect; ///< Texture coordinates of the glyph inside the font's texture + int Advance; ///< Offset to move horizontically to the next character + IntRect Bounds; ///< Bounding rectangle of the glyph, in coordinates relative to the baseline + IntRect TextureRect; ///< Texture coordinates of the glyph inside the font's texture }; } // namespace sf @@ -71,7 +71,7 @@ public : /// The sf::Glyph structure provides the information needed /// to handle the glyph: /// \li its coordinates in the font's texture -/// \li its bounding rect +/// \li its bounding rectangle /// \li the offset to apply to get the starting position of the next glyph /// /// \see sf::Font diff --git a/include/SFML/Graphics/Matrix3.hpp b/include/SFML/Graphics/Matrix3.hpp deleted file mode 100644 index f2bc76c0..00000000 --- a/include/SFML/Graphics/Matrix3.hpp +++ /dev/null @@ -1,176 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -#ifndef SFML_MATRIX3_HPP -#define SFML_MATRIX3_HPP - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - - -namespace sf -{ -//////////////////////////////////////////////////////////// -/// \brief Utility class to manipulate 3x3 matrices of floats -/// -//////////////////////////////////////////////////////////// -class SFML_API Matrix3 -{ -public : - - //////////////////////////////////////////////////////////// - /// \brief Default constructor - /// - /// This constructor creates an identity matrix. - /// - //////////////////////////////////////////////////////////// - Matrix3(); - - //////////////////////////////////////////////////////////// - /// \brief Construct a matrix from its 9 elements - /// - /// \param a00 Element (0, 0) of the matrix - /// \param a01 Element (0, 1) of the matrix - /// \param a02 Element (0, 2) of the matrix - /// \param a10 Element (1, 0) of the matrix - /// \param a11 Element (1, 1) of the matrix - /// \param a12 Element (1, 2) of the matrix - /// \param a20 Element (2, 0) of the matrix - /// \param a21 Element (2, 1) of the matrix - /// \param a22 Element (2, 2) of the matrix - /// - //////////////////////////////////////////////////////////// - Matrix3(float a00, float a01, float a02, - float a10, float a11, float a12, - float a20, float a21, float a22); - - //////////////////////////////////////////////////////////// - /// \brief Transform a point by the matrix - /// - /// \param point Point to transform - /// - /// \return Transformed point - /// - //////////////////////////////////////////////////////////// - Vector2f Transform(const Vector2f& point) const; - - //////////////////////////////////////////////////////////// - /// \brief Return the inverse of the matrix - /// - /// If the inverse cannot be computed, the identity matrix - /// is returned. - /// - /// \return A new matrix which is the inverse of self - /// - //////////////////////////////////////////////////////////// - Matrix3 GetInverse() const; - - //////////////////////////////////////////////////////////// - /// \brief Return the elements of the matrix - /// - /// This function returns an array of 16 floats containing - /// the corresponding 4x4 matrix, so that it is directly - /// compatible with OpenGL functions. - /// - /// \return Pointer to the 4x4 matrix elements - /// - //////////////////////////////////////////////////////////// - const float* Get4x4Elements() const; - - //////////////////////////////////////////////////////////// - /// \brief Overload of binary operator * - /// - /// \param right Right operand of the multiplication - /// - /// \return New matrix which is the result of self * \a right - /// - //////////////////////////////////////////////////////////// - Matrix3 operator *(const Matrix3& right) const; - - //////////////////////////////////////////////////////////// - /// \brief Build a matrix from a set of transformations - /// - /// \param origin Origin for the transformations - /// \param translation Translation offset - /// \param rotation Rotation angle in degrees - /// \param scale Scaling factors - /// - /// \return New Matrix3 containing the transformations - /// - /// \see Projection - /// - //////////////////////////////////////////////////////////// - static Matrix3 Transformation(const Vector2f& origin, const Vector2f& translation, float rotation, const Vector2f& scale); - - //////////////////////////////////////////////////////////// - /// \brief Build a 2D project matrix - /// - /// \param center Center of the view - /// \param size Size of the view - /// \param rotation Angle of rotation of the view, in degrees - /// - /// \return New Matrix3 containing the projection - /// - /// \see Transformation - /// - //////////////////////////////////////////////////////////// - static Matrix3 Projection(const Vector2f& center, const Vector2f& size, float rotation); - - //////////////////////////////////////////////////////////// - // Static member data - //////////////////////////////////////////////////////////// - static const Matrix3 Identity; ///< The identity matrix - -private : - - //////////////////////////////////////////////////////////// - // Member data - //////////////////////////////////////////////////////////// - float myData[16]; /// Matrix elements (we directly store it as a 4x4 matrix for optimization purpose) -}; - -#include - -} // namespace sf - - -#endif // SFML_MATRIX3_HPP - - -//////////////////////////////////////////////////////////// -/// \class sf::Matrix3 -/// \ingroup graphics -/// -/// Matrix3 is only meant for internal use, its interface is -/// limited and its implementation is optimized for OpenGL -/// rendering. -/// -/// This type is not used at all in the public API of SFML. -/// -//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Matrix3.inl b/include/SFML/Graphics/Matrix3.inl deleted file mode 100644 index e7f7f629..00000000 --- a/include/SFML/Graphics/Matrix3.inl +++ /dev/null @@ -1,147 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////// -inline Matrix3::Matrix3() -{ - myData[0] = 1.f; myData[4] = 0.f; myData[8] = 0.f; myData[12] = 0.f; - myData[1] = 0.f; myData[5] = 1.f; myData[9] = 0.f; myData[13] = 0.f; - myData[2] = 0.f; myData[6] = 0.f; myData[10] = 1.f; myData[14] = 0.f; - myData[3] = 0.f; myData[7] = 0.f; myData[11] = 0.f; myData[15] = 1.f; -} - - -//////////////////////////////////////////////////////////// -inline Matrix3::Matrix3(float a00, float a01, float a02, - float a10, float a11, float a12, - float a20, float a21, float a22) -{ - myData[0] = a00; myData[4] = a01; myData[8] = 0.f; myData[12] = a02; - myData[1] = a10; myData[5] = a11; myData[9] = 0.f; myData[13] = a12; - myData[2] = 0.f; myData[6] = 0.f; myData[10] = 1.f; myData[14] = 0.f; - myData[3] = a20; myData[7] = a21; myData[11] = 0.f; myData[15] = a22; -} - - -//////////////////////////////////////////////////////////// -inline Vector2f Matrix3::Transform(const Vector2f& point) const -{ - return Vector2f(myData[0] * point.x + myData[4] * point.y + myData[12], - myData[1] * point.x + myData[5] * point.y + myData[13]); -} - - -//////////////////////////////////////////////////////////// -inline Matrix3 Matrix3::GetInverse() const -{ - // Compute the determinant - float det = myData[0] * (myData[15] * myData[5] - myData[7] * myData[13]) - - myData[1] * (myData[15] * myData[4] - myData[7] * myData[12]) + - myData[3] * (myData[13] * myData[4] - myData[5] * myData[12]); - - // Compute the inverse if determinant is not zero - if (det != 0.f) // don't use an epsilon because the determinant may *really* be tiny - { - return Matrix3( (myData[15] * myData[5] - myData[7] * myData[13]) / det, - -(myData[15] * myData[4] - myData[7] * myData[12]) / det, - (myData[13] * myData[4] - myData[5] * myData[12]) / det, - -(myData[15] * myData[1] - myData[3] * myData[13]) / det, - (myData[15] * myData[0] - myData[3] * myData[12]) / det, - -(myData[13] * myData[0] - myData[1] * myData[12]) / det, - (myData[7] * myData[1] - myData[3] * myData[5]) / det, - -(myData[7] * myData[0] - myData[3] * myData[4]) / det, - (myData[5] * myData[0] - myData[1] * myData[4]) / det); - } - else - { - return Identity; - } -} - - -//////////////////////////////////////////////////////////// -inline const float* Matrix3::Get4x4Elements() const -{ - return myData; -} - - -//////////////////////////////////////////////////////////// -inline Matrix3 Matrix3::operator *(const Matrix3& right) const -{ - return Matrix3(myData[0] * right.myData[0] + myData[4] * right.myData[1] + myData[12] * right.myData[3], - myData[0] * right.myData[4] + myData[4] * right.myData[5] + myData[12] * right.myData[7], - myData[0] * right.myData[12] + myData[4] * right.myData[13] + myData[12] * right.myData[15], - myData[1] * right.myData[0] + myData[5] * right.myData[1] + myData[13] * right.myData[3], - myData[1] * right.myData[4] + myData[5] * right.myData[5] + myData[13] * right.myData[7], - myData[1] * right.myData[12] + myData[5] * right.myData[13] + myData[13] * right.myData[15], - myData[3] * right.myData[0] + myData[7] * right.myData[1] + myData[15] * right.myData[3], - myData[3] * right.myData[4] + myData[7] * right.myData[5] + myData[15] * right.myData[7], - myData[3] * right.myData[12] + myData[7] * right.myData[13] + myData[15] * right.myData[15]); -} - - -//////////////////////////////////////////////////////////// -inline Matrix3 Matrix3::Transformation(const Vector2f& origin, const Vector2f& translation, float rotation, const Vector2f& scale) -{ - // Combine the transformations - float angle = -rotation * 3.141592654f / 180.f; - float cosine = static_cast(std::cos(angle)); - float sine = static_cast(std::sin(angle)); - float sxCos = scale.x * cosine; - float syCos = scale.y * cosine; - float sxSin = scale.x * sine; - float sySin = scale.y * sine; - float tx = -origin.x * sxCos - origin.y * sySin + translation.x; - float ty = origin.x * sxSin - origin.y * syCos + translation.y; - - // Construct the matrix - return Matrix3( sxCos, sySin, tx, - -sxSin, syCos, ty, - 0.f, 0.f, 1.f); -} - - -//////////////////////////////////////////////////////////// -inline Matrix3 Matrix3::Projection(const Vector2f& center, const Vector2f& size, float rotation) -{ - // Rotation components - float angle = rotation * 3.141592654f / 180.f; - float cosine = static_cast(std::cos(angle)); - float sine = static_cast(std::sin(angle)); - float tx = -center.x * cosine - center.y * sine + center.x; - float ty = center.x * sine - center.y * cosine + center.y; - - // Projection components - float a = 2.f / size.x; - float b = -2.f / size.y; - float c = -a * center.x; - float d = -b * center.y; - - // Rebuild the projection matrix - return Matrix3( a * cosine, a * sine, a * tx + c, - -b * sine, b * cosine, b * ty + d, - 0.f, 0.f, 1.f); -} diff --git a/include/SFML/Graphics/PrimitiveType.hpp b/include/SFML/Graphics/PrimitiveType.hpp new file mode 100644 index 00000000..a7458b8a --- /dev/null +++ b/include/SFML/Graphics/PrimitiveType.hpp @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_PRIMITIVETYPE_HPP +#define SFML_PRIMITIVETYPE_HPP + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \ingroup graphics +/// \brief Types of primitives that a sf::VertexArray can render +/// +/// Points and lines have no area, therefore their thickness +/// will always be 1 pixel, regarldess the current transform +/// and view. +/// +//////////////////////////////////////////////////////////// +enum PrimitiveType +{ + Points, ///< List of individual points + Lines, ///< List of individual lines + LinesStrip, ///< List of connected lines, a point uses the previous point to form a line + Triangles, ///< List of individual triangles + TrianglesStrip, ///< List of connected triangles, a point uses the two previous points to form a triangle + TrianglesFan, ///< List of connected triangles, a point uses the common center and the previous point to form a triangle + Quads ///< List of individual quads +}; + +} // namespace sf + + +#endif // SFML_PRIMITIVETYPE_HPP diff --git a/include/SFML/Graphics/Rect.hpp b/include/SFML/Graphics/Rect.hpp index 9bb3f039..0f7b1c08 100644 --- a/include/SFML/Graphics/Rect.hpp +++ b/include/SFML/Graphics/Rect.hpp @@ -154,6 +154,36 @@ public : T Height; ///< Height of the rectangle }; +//////////////////////////////////////////////////////////// +/// \relates Rect +/// \brief Overload of binary operator == +/// +/// This operator compares strict equality between two rectangles. +/// +/// \param left Left operand (a rectangle) +/// \param right Right operand (a rectangle) +/// +/// \return True if \a left is equal to \a right +/// +//////////////////////////////////////////////////////////// +template +bool operator ==(const Rect& left, const Rect& right); + +//////////////////////////////////////////////////////////// +/// \relates Rect +/// \brief Overload of binary operator != +/// +/// This operator compares strict difference between two rectangles. +/// +/// \param left Left operand (a rectangle) +/// \param right Right operand (a rectangle) +/// +/// \return True if \a left is not equal to \a right +/// +//////////////////////////////////////////////////////////// +template +bool operator !=(const Rect& left, const Rect& right); + #include // Create typedefs for the most common types diff --git a/include/SFML/Graphics/Rect.inl b/include/SFML/Graphics/Rect.inl index 98688511..e978758f 100644 --- a/include/SFML/Graphics/Rect.inl +++ b/include/SFML/Graphics/Rect.inl @@ -118,3 +118,21 @@ bool Rect::Intersects(const Rect& rectangle, Rect& intersection) const return false; } } + + +//////////////////////////////////////////////////////////// +template +inline bool operator ==(const Rect& left, const Rect& right) +{ + return (left.Left == right.Left) && (left.Width == right.Width) && + (left.Top == right.Top) && (left.Height == right.Height); +} + + +//////////////////////////////////////////////////////////// +template +inline bool operator !=(const Rect& left, const Rect& right) +{ + return (left.Left != right.Left) || (left.Width != right.Width) || + (left.Top != right.Top) || (left.Height != right.Height); +} diff --git a/include/SFML/Graphics/RectangleShape.hpp b/include/SFML/Graphics/RectangleShape.hpp new file mode 100644 index 00000000..0e9393f9 --- /dev/null +++ b/include/SFML/Graphics/RectangleShape.hpp @@ -0,0 +1,129 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_RECTANGLESHAPE_HPP +#define SFML_RECTANGLESHAPE_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Specialized shape representing a rectangle +/// +//////////////////////////////////////////////////////////// +class SFML_API RectangleShape : public Shape +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a 10x10 rectangle. + /// + //////////////////////////////////////////////////////////// + RectangleShape(); + + //////////////////////////////////////////////////////////// + /// \brief Set the size of the rectangle + /// + /// The default size of a rectangle is 10x10. + /// + /// \param size New size of the rectangle + /// + /// \see GetSize + /// + //////////////////////////////////////////////////////////// + void SetSize(const Vector2f& size); + + //////////////////////////////////////////////////////////// + /// \brief Get the size of the rectangle + /// + /// \return Size of the rectangle + /// + /// \see SetSize + /// + //////////////////////////////////////////////////////////// + const Vector2f& GetSize() const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Get the number of points defining the shape + /// + /// \return Number of points of the shape + /// + //////////////////////////////////////////////////////////// + virtual unsigned int GetOutlinePointsCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a point of the shape + /// + /// \param index Index of the point to get + /// + /// \return Index-th point of the shape + /// + //////////////////////////////////////////////////////////// + virtual Vector2f GetOutlinePoint(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Vector2f mySize; ///< Size of the rectangle +}; + +} // namespace sf + + +#endif // SFML_RECTANGLESHAPE_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::RectangleShape +/// \ingroup graphics +/// +/// This class inherits all the functions of sf::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of sf::Shape (outline, color, texture, ...). +/// +/// Usage example: +/// \code +/// sf::RectangleShape rectangle; +/// rectangle.SetSize(sf::Vector2f(100, 50)); +/// rectangle.SetOutlineColor(sf::Color::Red); +/// rectangle.SetOutlineThickness(5); +/// rectangle.SetPosition(10, 20); +/// ... +/// window.Draw(rectangle); +/// \endcode +/// +/// \see sf::Shape, sf::StarShape, sf::CircleShape, sf::ConvexShape +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/RenderStates.hpp b/include/SFML/Graphics/RenderStates.hpp new file mode 100644 index 00000000..13d09d99 --- /dev/null +++ b/include/SFML/Graphics/RenderStates.hpp @@ -0,0 +1,173 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_RENDERSTATES_HPP +#define SFML_RENDERSTATES_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +class Shader; +class Texture; + +//////////////////////////////////////////////////////////// +/// \brief Define the states used for drawing to a RenderTarget +/// +//////////////////////////////////////////////////////////// +class SFML_API RenderStates +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Constructing a default set of render states is equivalent + /// to using sf::RenderStates::Default. + /// The default set defines: + /// \li the BlendAlpha blend mode + /// \li the identity transform + /// \li a null texture + /// \li a null shader + /// + //////////////////////////////////////////////////////////// + RenderStates(); + + //////////////////////////////////////////////////////////// + /// \brief Construct a default set of render states with a custom blend mode + /// + /// \param blendMode Blend mode to use + /// + //////////////////////////////////////////////////////////// + RenderStates(sf::BlendMode blendMode); + + //////////////////////////////////////////////////////////// + /// \brief Construct a default set of render states with a custom transform + /// + /// \param transform Transform to use + /// + //////////////////////////////////////////////////////////// + RenderStates(const sf::Transform& transform); + + //////////////////////////////////////////////////////////// + /// \brief Construct a default set of render states with a custom texture + /// + /// \param texture Texture to use + /// + //////////////////////////////////////////////////////////// + RenderStates(const sf::Texture* texture); + + //////////////////////////////////////////////////////////// + /// \brief Construct a default set of render states with a custom shader + /// + /// \param shader Shader to use + /// + //////////////////////////////////////////////////////////// + RenderStates(const sf::Shader* shader); + + //////////////////////////////////////////////////////////// + /// \brief Construct a set of render states with all its attributes + /// + /// \param blendMode Blend mode to use + /// \param transform Transform to use + /// \param texture Texture to use + /// \param shader Shader to use + /// + //////////////////////////////////////////////////////////// + RenderStates(sf::BlendMode blendMode, const sf::Transform& transform, + const sf::Texture* texture, const sf::Shader* shader); + + //////////////////////////////////////////////////////////// + // Static member data + //////////////////////////////////////////////////////////// + static const RenderStates Default; ///< Special instance holding the default render states + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + sf::BlendMode BlendMode; ///< Blending mode + sf::Transform Transform; ///< Transform + const sf::Texture* Texture; ///< Texture + const sf::Shader* Shader; ///< Shader +}; + +} // namespace sf + + +#endif // SFML_RENDERSTATES_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::RenderStates +/// \ingroup graphics +/// +/// There are four global states that can be applied to +/// the drawn objects: +/// \li the blend mode: how pixels of the object are blended with the background +/// \li the transform: how the object is positioned/rotated/scaled +/// \li the texture: what image is mapped to the object +/// \li the shader: what custom effect is applied to the object +/// +/// High-level objects such as sprites or text force some of +/// these states when they are drawn. For example, a sprite +/// will set its own texture, so that you don't have to care +/// about it when drawing the sprite. +/// +/// The transform is a special case: sprites, texts and shapes +/// (and it's a good idea to do it with your own drawable classes +/// too) combine their transform with the one that is passed in the +/// RenderStates structure. So that you can use a "global" transform +/// on top of each object's transform. +/// +/// Most objects, especially high-level drawables, can be drawn +/// directly without defining render states explicitely -- the +/// default set of states is ok in most cases. +/// \code +/// window.Draw(sprite); +/// \endcode +/// +/// If you want to use a single specific render state, +/// for example a shader, you can pass it directly to the Draw +/// function: sf::RenderStates has an implicit one-argument +/// constructor for each state. +/// \code +/// window.Draw(sprite, shader); +/// \endcode +/// +/// When you're inside the Draw function of a drawable +/// object (inherited from sf::Drawable), you can +/// either pass the render states unmodified, or change +/// some of them. +/// For example, a transformable object will combine the +/// current transform with its own transform. A sprite will +/// set its texture. Etc. +/// +/// \see sf::RenderTarget, sf::Drawable +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/RenderTarget.hpp b/include/SFML/Graphics/RenderTarget.hpp index 961a26a2..548c09df 100644 --- a/include/SFML/Graphics/RenderTarget.hpp +++ b/include/SFML/Graphics/RenderTarget.hpp @@ -31,14 +31,17 @@ #include #include #include -#include #include +#include +#include +#include +#include namespace sf { class Drawable; -class Shader; +class Vertex; //////////////////////////////////////////////////////////// /// \brief Base class for all render targets (window, texture, ...) @@ -65,61 +68,17 @@ public : //////////////////////////////////////////////////////////// void Clear(const Color& color = Color(0, 0, 0, 255)); - //////////////////////////////////////////////////////////// - /// \brief Draw an object into the target - /// - /// This function draws anything that inherits from the - /// sf::Drawable base class (sf::Sprite, sf::Shape, sf::Text, - /// or even your own derived classes). - /// - /// \param object Object to draw - /// - //////////////////////////////////////////////////////////// - void Draw(const Drawable& object); - - //////////////////////////////////////////////////////////// - /// \brief Draw an object into the target with a shader - /// - /// This function draws anything that inherits from the - /// sf::Drawable base class (sf::Sprite, sf::Shape, sf::Text, - /// or even your own derived classes). - /// The shader alters the way that the pixels are processed - /// right before being written to the render target. - /// - /// \param object Object to draw - /// \param shader Shader to use for drawing the object - /// - //////////////////////////////////////////////////////////// - void Draw(const Drawable& object, const Shader& shader); - - //////////////////////////////////////////////////////////// - /// \brief Return the width of the rendering region of the target - /// - /// \return Width in pixels - /// - /// \see GetHeight - /// - //////////////////////////////////////////////////////////// - virtual unsigned int GetWidth() const = 0; - - //////////////////////////////////////////////////////////// - /// \brief Return the height of the rendering region of the target - /// - /// \return Height in pixels - /// - /// \see GetWidth - /// - //////////////////////////////////////////////////////////// - virtual unsigned int GetHeight() const = 0; - //////////////////////////////////////////////////////////// /// \brief Change the current active view /// + /// The view is like a 2D camera, it controls which part of + /// the 2D scene is visible, and how it is viewed in the + /// render-target. /// The new view will affect everything that is drawn, until - /// another view is activated. + /// another view is set. /// The render target keeps its own copy of the view object, /// so it is not necessary to keep the original one alive - /// as long as it is in use. + /// after calling this function. /// To restore the original view of the target, you can pass /// the result of GetDefaultView() to this function. /// @@ -131,7 +90,7 @@ public : void SetView(const View& view); //////////////////////////////////////////////////////////// - /// \brief Retrieve the view currently in use in the render target + /// \brief Get the view currently in use in the render target /// /// \return The view object that is currently used /// @@ -218,11 +177,52 @@ public : //////////////////////////////////////////////////////////// Vector2f ConvertCoords(unsigned int x, unsigned int y, const View& view) const; + //////////////////////////////////////////////////////////// + /// \brief Draw a drawable object to the render-target + /// + /// \param drawable Object to draw + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + void Draw(const Drawable& drawable, const RenderStates& states = RenderStates::Default); + + //////////////////////////////////////////////////////////// + /// \brief Draw primitives defined by an array of vertices + /// + /// \param vertices Pointer to the vertices + /// \param verticesCount Number of vertices in the array + /// \param type Type of primitives to draw + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + void Draw(const Vertex* vertices, unsigned int verticesCount, + PrimitiveType type, const RenderStates& states = RenderStates::Default); + + //////////////////////////////////////////////////////////// + /// \brief Return the width of the rendering region of the target + /// + /// \return Width in pixels + /// + /// \see GetHeight + /// + //////////////////////////////////////////////////////////// + virtual unsigned int GetWidth() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Return the height of the rendering region of the target + /// + /// \return Height in pixels + /// + /// \see GetWidth + /// + //////////////////////////////////////////////////////////// + virtual unsigned int GetHeight() const = 0; + //////////////////////////////////////////////////////////// /// \brief Save the current OpenGL render states and matrices /// /// This function can be used when you mix SFML drawing - /// and direct OpenGL rendering. Combined with RestoreGLStates, + /// and direct OpenGL rendering. Combined with PopGLStates, /// it ensures that: /// \li SFML's internal states are not messed up by your OpenGL code /// \li your OpenGL states are not modified by a call to a SFML function @@ -231,34 +231,60 @@ public : /// calls Draw functions. Example: /// \code /// // OpenGL code here... - /// window.SaveGLStates(); + /// window.PushGLStates(); /// window.Draw(...); /// window.Draw(...); - /// window.RestoreGLStates(); + /// window.PopGLStates(); /// // OpenGL code here... /// \endcode /// - /// Note that this function is quite expensive and should be - /// used wisely. It is provided for convenience, and the best - /// results will be achieved if you handle OpenGL states - /// yourself (because you really know which states have really - /// changed, and need to be saved / restored). + /// Note that this function is quite expensive: it saves all the + /// possible OpenGL states and matrices, even the ones you + /// don't care about. Therefore it should be used wisely. + /// It is provided for convenience, but the best results will + /// be achieved if you handle OpenGL states yourself (because + /// you know which states have really changed, and need to be + /// saved and restored). Take a look at the ResetGLStates + /// function if you do so. /// - /// \see RestoreGLStates + /// \see PopGLStates /// //////////////////////////////////////////////////////////// - void SaveGLStates(); + void PushGLStates(); //////////////////////////////////////////////////////////// /// \brief Restore the previously saved OpenGL render states and matrices /// - /// See the description of SaveGLStates to get a detailed + /// See the description of PushGLStates to get a detailed /// description of these functions. /// - /// \see SaveGLStates + /// \see PushGLStates /// //////////////////////////////////////////////////////////// - void RestoreGLStates(); + void PopGLStates(); + + //////////////////////////////////////////////////////////// + /// \brief Reset the internal OpenGL states so that the target is ready for drawing + /// + /// This function can be used when you mix SFML drawing + /// and direct OpenGL rendering, if you choose not to use + /// PushGLStates/PopGLStates. It makes sure that all OpenGL + /// states needed by SFML are set, so that subsequent Draw() + /// calls will work as expected. + /// + /// Example: + /// \code + /// // OpenGL code here... + /// glPushAttrib(...); + /// window.ResetGLStates(); + /// window.Draw(...); + /// window.Draw(...); + /// glPopAttrib(...); + /// // OpenGL code here... + /// \endcode + /// + //////////////////////////////////////////////////////////// + void ResetGLStates(); protected : @@ -296,11 +322,9 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Renderer myRenderer; ///< Renderer that will process the rendering commands of the window - View myDefaultView; ///< Default view - View myCurrentView; ///< Current active view - bool myStatesSaved; ///< Are we between a SaveGLStates and a RestoreGLStates? - bool myViewHasChanged; ///< Has the current view changed? + View myDefaultView; ///< Default view + View myView; ///< Current view + bool myViewChanged; ///< Has the current view changed since last Draw? }; } // namespace sf @@ -328,8 +352,8 @@ private : /// On top of that, render targets are still able to render direct /// OpenGL stuff. It is even possible to mix together OpenGL calls /// and regular SFML drawing commands. When doing so, make sure that -/// OpenGL states are not messed up by calling the SaveGLStates / -/// RestoreGLStates functions. +/// OpenGL states are not messed up by calling the +/// PushGLStates/PopGLStates functions. /// /// \see sf::RenderWindow, sf::RenderTexture, sf::View /// diff --git a/include/SFML/Graphics/RenderWindow.hpp b/include/SFML/Graphics/RenderWindow.hpp index b6263d7f..3f5ceaa0 100644 --- a/include/SFML/Graphics/RenderWindow.hpp +++ b/include/SFML/Graphics/RenderWindow.hpp @@ -188,15 +188,14 @@ private : /// classes of the graphics module. /// /// sf::RenderWindow is derived from sf::Window, thus it inherits -/// all its features: mouse/keyboard/joystick input, events, window -/// handling, OpenGL rendering, etc. See the documentation of -/// sf::Window for a more complete description of all these features -/// and code samples. +/// all its features: events, window management, OpenGL rendering, +/// etc. See the documentation of sf::Window for a more complete +/// description of all these features, as well as code examples. /// /// On top of that, sf::RenderWindow adds more features related to /// 2D drawing with the graphics module (see its base class /// sf::RenderTarget for more details). -/// Here is a typical rendering / event loop with a sf::RenderWindow: +/// Here is a typical rendering and event loop with a sf::RenderWindow: /// /// \code /// // Declare and create a new render-window @@ -220,10 +219,10 @@ private : /// // Clear the whole window before rendering a new frame /// window.Clear(); /// -/// // Draw some sprites / shapes / texts -/// window.Draw(sprite); // sprite is a sf::Sprite -/// window.Draw(shape); // shape is a sf::Shape -/// window.Draw(text); // text is a sf::Text +/// // Draw some graphical entities +/// window.Draw(sprite); +/// window.Draw(circle); +/// window.Draw(text); /// /// // End the current frame and display its contents on screen /// window.Display(); @@ -232,9 +231,7 @@ private : /// /// Like sf::Window, sf::RenderWindow is still able to render direct /// OpenGL stuff. It is even possible to mix together OpenGL calls -/// and regular SFML drawing commands. When doing so, make sure that -/// OpenGL states are not messed up by calling the SaveGLStates / -/// RestoreGLStates functions. +/// and regular SFML drawing commands. /// /// \code /// // Create the render window @@ -256,9 +253,9 @@ private : /// ... /// /// // Draw a background sprite -/// window.SaveGLStates(); +/// window.PushGLStates(); /// window.Draw(sprite); -/// window.RestoreGLStates(); +/// window.PopGLStates(); /// /// // Draw a 3D object using OpenGL /// glBegin(GL_QUADS); @@ -267,9 +264,9 @@ private : /// glEnd(); /// /// // Draw text on top of the 3D object -/// window.SaveGLStates(); +/// window.PushGLStates(); /// window.Draw(text); -/// window.RestoreGLStates(); +/// window.PopGLStates(); /// /// // Finally, display the rendered frame on screen /// window.Display(); diff --git a/include/SFML/Graphics/Renderer.hpp b/include/SFML/Graphics/Renderer.hpp deleted file mode 100644 index 4d774c80..00000000 --- a/include/SFML/Graphics/Renderer.hpp +++ /dev/null @@ -1,363 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -#ifndef SFML_RENDERER_HPP -#define SFML_RENDERER_HPP - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include - - -namespace sf -{ -class Shader; -class Texture; - -//////////////////////////////////////////////////////////// -/// \brief Handles the low-level rendering (states and geometry) -/// -//////////////////////////////////////////////////////////// -class SFML_API Renderer : GlResource, NonCopyable -{ -public : - - //////////////////////////////////////////////////////////// - /// \brief Types of primitives to be rendererd - /// - //////////////////////////////////////////////////////////// - enum PrimitiveType - { - TriangleList, ///< Simple list of triangles - TriangleStrip, ///< Triangle strip (consecutive triangles always share two points) - TriangleFan, ///< Triangle fan (one center point + outline points) - QuadList ///< Simple list of quads - }; - -public : - - //////////////////////////////////////////////////////////// - /// \brief Construct the renderer with its owner render target - /// - /// \param target Owner render target - /// - //////////////////////////////////////////////////////////// - Renderer(RenderTarget& target); - - //////////////////////////////////////////////////////////// - /// \brief Initialize the renderer (set the default states, etc.) - /// - //////////////////////////////////////////////////////////// - void Initialize(); - - //////////////////////////////////////////////////////////// - /// \brief Save the current OpenGL render states and matrices - /// - /// \see RestoreGLStates - /// - //////////////////////////////////////////////////////////// - void SaveGLStates(); - - //////////////////////////////////////////////////////////// - /// \brief Restore the previously saved OpenGL render states and matrices - /// - /// \see SaveGLStates - /// - //////////////////////////////////////////////////////////// - void RestoreGLStates(); - - //////////////////////////////////////////////////////////// - /// Clear the color buffer - /// - /// \param color Color to use to clear the color buffer - /// - //////////////////////////////////////////////////////////// - void Clear(const Color& color); - - //////////////////////////////////////////////////////////// - /// \brief Save the current render states - /// - /// \see PopStates - /// - //////////////////////////////////////////////////////////// - void PushStates(); - - //////////////////////////////////////////////////////////// - /// \brief Restore the previously saved render states - /// - /// \see PushStates - /// - //////////////////////////////////////////////////////////// - void PopStates(); - - //////////////////////////////////////////////////////////// - /// \brief Set a new model-view matrix - /// - /// \param matrix New model-view matrix - /// - /// \see ApplyModelView - /// - //////////////////////////////////////////////////////////// - void SetModelView(const Matrix3& matrix); - - //////////////////////////////////////////////////////////// - /// \brief Combine a new model-view matrix with the current one - /// - /// \param matrix Model-view matrix to combine - /// - /// \see SetModelView - /// - //////////////////////////////////////////////////////////// - void ApplyModelView(const Matrix3& matrix); - - //////////////////////////////////////////////////////////// - /// \brief Set a new projection matrix - /// - /// \param matrix New projection matrix - /// - /// \see ApplyProjection - /// - //////////////////////////////////////////////////////////// - void SetProjection(const Matrix3& matrix); - - //////////////////////////////////////////////////////////// - /// \brief Set the current global color - /// - /// This color will be modulated with each vertex's color. - /// - /// \param color New global color - /// - /// \see ApplyColor - /// - //////////////////////////////////////////////////////////// - void SetColor(const Color& color); - - //////////////////////////////////////////////////////////// - /// \brief Modulate the current global color with a new one - /// - /// This color will be modulated with each vertex's color. - /// - /// \param color Color to modulate - /// - /// \see SetColor - /// - //////////////////////////////////////////////////////////// - void ApplyColor(const Color& color); - - //////////////////////////////////////////////////////////// - /// \brief Set the current viewport - /// - /// \param viewport New viewport to apply - /// - //////////////////////////////////////////////////////////// - void SetViewport(const IntRect& viewport); - - //////////////////////////////////////////////////////////// - /// \brief Set the current alpha-blending mode - /// - /// \param mode New blending mode - /// - //////////////////////////////////////////////////////////// - void SetBlendMode(Blend::Mode mode); - - //////////////////////////////////////////////////////////// - /// \brief Set the current texture - /// - /// \param texture New texture - /// - //////////////////////////////////////////////////////////// - void SetTexture(const Texture* texture); - - //////////////////////////////////////////////////////////// - /// \brief Set the current shader - /// - /// \param shader New Shader - /// - //////////////////////////////////////////////////////////// - void SetShader(const Shader* shader); - - //////////////////////////////////////////////////////////// - /// \brief Begin rendering a new geometry batch - /// - /// You need to call End() to complete the batch and trigger - /// the actual rendering of the geometry that you passed - /// between Begin() and End(). - /// - /// Usage: - /// \code - /// renderer.Begin(Renderer::TriangleList); - /// renderer.AddVertex(...); - /// renderer.AddVertex(...); - /// renderer.AddVertex(...); - /// renderer.End(); - /// \endcode - /// - /// \param type Type of the primitives that are going to be rendered - /// - /// \see End - /// - //////////////////////////////////////////////////////////// - void Begin(PrimitiveType type); - - //////////////////////////////////////////////////////////// - /// \brief End the current geometry batch and render it - /// - /// \see Begin - /// - //////////////////////////////////////////////////////////// - void End(); - - //////////////////////////////////////////////////////////// - /// \brief Add a new vertex (position only) - /// - /// This function adds a new vertex to the current batch. - /// This is equivalent to calling AddVertex(x, y, 0, 0, Color::White). - /// - /// \param x X coordinate of the vertex - /// \param y Y coordinate of the vertex - /// - //////////////////////////////////////////////////////////// - void AddVertex(float x, float y); - - //////////////////////////////////////////////////////////// - /// \brief Add a new vertex (position + texture coordinates) - /// - /// This function adds a new vertex to the current batch. - /// This is equivalent to calling AddVertex(x, y, u, v, Color::White). - /// - /// \param x X coordinate of the vertex - /// \param y Y coordinate of the vertex - /// \param u X texture coordinate of the vertex - /// \param v Y texture coordinate of the vertex - /// - //////////////////////////////////////////////////////////// - void AddVertex(float x, float y, float u, float v); - - //////////////////////////////////////////////////////////// - /// \brief Add a new vertex (position + color) - /// - /// This function adds a new vertex to the current batch. - /// This is equivalent to calling AddVertex(x, y, 0, 0, color). - /// - /// \param x X coordinate of the vertex - /// \param y Y coordinate of the vertex - /// \param color Color of the vertex - /// - //////////////////////////////////////////////////////////// - void AddVertex(float x, float y, const Color& color); - - //////////////////////////////////////////////////////////// - /// \brief Add a new vertex (position + texture coordinates + color) - /// - /// This function adds a new vertex to the current batch. - /// - /// \param x X coordinate of the vertex - /// \param y Y coordinate of the vertex - /// \param u X texture coordinate of the vertex - /// \param v Y texture coordinate of the vertex - /// \param color Color of the vertex - /// - //////////////////////////////////////////////////////////// - void AddVertex(float x, float y, float u, float v, const Color& color); - -private : - - //////////////////////////////////////////////////////////// - /// \brief Process a new vertex - /// - /// This function is called by all the public overloads of AddVertex, - /// it processes a new vertex to be rendered. - /// - /// \param x X coordinate of the vertex - /// \param y Y coordinate of the vertex - /// \param u X texture coordinate of the vertex - /// \param v Y texture coordinate of the vertex - /// \param r Red component of the vertex color (normalized) - /// \param g Green component of the vertex color (normalized) - /// \param b Blue component of the vertex color (normalized) - /// \param a Alpha component of the vertex color (normalized) - /// - //////////////////////////////////////////////////////////// - void ProcessVertex(float x, float y, float u, float v, float r, float g, float b, float a); - - //////////////////////////////////////////////////////////// - // Structure holding the render states that can be stacked - //////////////////////////////////////////////////////////// - struct States - { - States() : r(1.f), g(1.f), b(1.f), a(1.f) {} - - Matrix3 modelView; ///< Model-view matrix - float r, g, b, a; ///< Vertex color (normalized components for faster operations) - }; - - //////////////////////////////////////////////////////////// - // Member data - //////////////////////////////////////////////////////////// - RenderTarget& myTarget; ///< Reference to the render target owning this renderer - States myStatesStack[64]; ///< Stack of render states - States* myStates; ///< Current set of render states - const Texture* myTexture; ///< Current texture - unsigned int myTextureId; ///< Current texture identifier (the sf::Texture instance may be the same, but not the internal OpenGL texture) - const Shader* myShader; ///< Current pixel shader - Blend::Mode myBlendMode; ///< Current blending mode - IntRect myViewport; ///< Current target viewport - bool myTextureIsValid; ///< Is the cached texture valid? (if not, the cached value is ignored) - bool myShaderIsValid; ///< Is the cached shader valid? (if not, the cached value is ignored) - bool myBlendModeIsValid; ///< Is the cached blend mode valid? (if not, the cached value is ignored) - bool myViewportIsValid; ///< Is the cached viewport valid? (if not, the cached value is ignored) -}; - -} // namespace sf - - -#endif // SFML_RENDERER_HPP - - -//////////////////////////////////////////////////////////// -/// \class sf::Renderer -/// \ingroup graphics -/// -/// sf::Renderer is the abstraction layer between SFML code -/// and the low-level drawing API (OpenGL). It manages -/// render states efficiently, and provides a lightweight -/// abstraction for rendering geometry. -/// -/// The purpose of this class is to provide a single abstract -/// entry point for everything related to low-level rendering. -/// Hiding everything behind sf::Renderer makes optimizing -/// easy, as well as porting to other technologies in the future -/// (like OpenGL ES or OpenGL 3.x). -/// -/// This class is mainly meant for internal usage, you should -/// never care about it unless you write your own sf::Drawable -/// class that uses raw geometry in its Render function. -/// -//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Shader.hpp b/include/SFML/Graphics/Shader.hpp index 5f389ff8..94ce1115 100644 --- a/include/SFML/Graphics/Shader.hpp +++ b/include/SFML/Graphics/Shader.hpp @@ -440,15 +440,20 @@ private : /// window.Draw(sprite, shader); /// \endcode /// +/// ... which is in fact just a shortcut for this: +/// \code +/// sf::RenderStates states; +/// states.Shader = shader; +/// window.Draw(sprite, states); +/// \endcode +/// /// Shaders can be used on any drawable, but they are mainly -/// made for sf::Sprite. Using a shader on a sf::String is more -/// limited, because the texture of the text is not the +/// made for sprites and shapes. Using a shader on a sf::String +/// is more limited, because the texture of the text is not the /// actual text that you see on screen, it is a big texture /// containing all the characters of the font in an arbitrary /// order. Thus, texture lookups on pixels other than the current -/// one may not give you the expected result. Using a shader -/// with sf::Shape is even more limited, as shapes don't use -/// any texture. +/// one may not give you the expected result. /// /// Shaders can also be used to apply global post-effects to the /// current contents of the target (like the old sf::PostFx class @@ -462,7 +467,7 @@ private : /// The first technique is more optimized because it doesn't involve /// retrieving the target's pixels to system memory, but the /// second one doesn't impact the rendering process and can be -/// easily inserted anywhere. +/// easily inserted anywhere without impacting all the code. /// /// Like sf::Texture that can be used as a raw OpenGL texture, /// sf::Shader can also be used directly as a raw fragment diff --git a/include/SFML/Graphics/Shape.hpp b/include/SFML/Graphics/Shape.hpp index a183539a..e38c1784 100644 --- a/include/SFML/Graphics/Shape.hpp +++ b/include/SFML/Graphics/Shape.hpp @@ -29,208 +29,155 @@ // Headers //////////////////////////////////////////////////////////// #include +#include +#include #include -#include namespace sf { //////////////////////////////////////////////////////////// -/// \brief A convex, colored polygon with an optional outline +/// \brief Base class for textured shapes with outline /// //////////////////////////////////////////////////////////// -class SFML_API Shape : public Drawable +class SFML_API Shape : public Drawable, public Transformable { public : //////////////////////////////////////////////////////////// - /// \brief Default constructor - /// - /// Creates an empty shape (no point). + /// \brief Virtual destructor /// //////////////////////////////////////////////////////////// - Shape(); + virtual ~Shape(); //////////////////////////////////////////////////////////// - /// \brief Add a new point to the shape + /// \brief Change the source texture of the shape /// - /// The new point is inserted at the end of the shape. + /// The \a texture argument refers to a texture that must + /// exist as long as the shape uses it. Indeed, the shape + /// doesn't store its own copy of the texture, but rather keeps + /// a pointer to the one that you passed to this function. + /// If the source texture is destroyed and the shape tries to + /// use it, the behaviour is undefined. + /// \a texture can be NULL to disable texturing. + /// If \a resetRect is true, the TextureRect property of + /// the shape is automatically adjusted to the size of the new + /// texture. If it is false, the texture rect is left unchanged. /// - /// \param x X position of the point - /// \param y Y position of the point - /// \param color Color of the point - /// \param outlineColor Outline color of the point + /// \param texture New texture + /// \param resetRect Should the texture rect be reset to the size of the new texture? + /// + /// \see GetTexture, SetTextureRect /// //////////////////////////////////////////////////////////// - void AddPoint(float x, float y, const Color& color = Color(255, 255, 255), const Color& outlineColor = Color(0, 0, 0)); + void SetTexture(const Texture* texture, bool resetRect = false); //////////////////////////////////////////////////////////// - /// \brief Add a new point to the shape + /// \brief Set the sub-rectangle of the texture that the shape will display /// - /// The new point is inserted at the end of the shape. + /// The texture rect is useful when you don't want to display + /// the whole texture, but rather a part of it. + /// By default, the texture rect covers the entire texture. /// - /// \param position Position of the point - /// \param color Color of the point - /// \param outlineColor Outline color of the point + /// \param rect Rectangle defining the region of the texture to display + /// + /// \see GetTextureRect, SetTexture /// //////////////////////////////////////////////////////////// - void AddPoint(const Vector2f& position, const Color& color = Color(255, 255, 255), const Color& outlineColor = Color(0, 0, 0)); + void SetTextureRect(const IntRect& rect); //////////////////////////////////////////////////////////// - /// \brief Get the number of points composing the shape + /// \brief Set the fill color of the shape /// - /// \return Total number of points + /// This color is modulated (multiplied) with the shape's + /// texture if any. It can be used to colorize the shape, + /// or change its global opacity. + /// You can use sf::Color::Transparent to make the inside of + /// the shape transparent, and have the outline alone. + /// By default, the shape's fill color is opaque white. + /// + /// \param color New color of the shape + /// + /// \see GetFillColor, SetOutlineColor /// //////////////////////////////////////////////////////////// - unsigned int GetPointsCount() const; + void SetFillColor(const Color& color); //////////////////////////////////////////////////////////// - /// \brief Enable or disable the shape's filling + /// \brief Set the outline color of the shape /// - /// This option is enabled by default. + /// You can use sf::Color::Transparent to disable the outline. + /// By default, the shape's outline color is opaque white. /// - /// \param enable True to enable, false to disable + /// \param color New outline color of the shape /// - /// \see EnableOutline + /// \see GetOutlineColor, SetFillColor /// //////////////////////////////////////////////////////////// - void EnableFill(bool enable); + void SetOutlineColor(const Color& color); //////////////////////////////////////////////////////////// - /// \brief Enable or disable the shape's outline + /// \brief Set the thickness of the shape's outline /// - /// This option is enabled by default. + /// This number cannot be negative. Using zero disables + /// the outline. + /// By default, the outline thickness is 0. /// - /// \param enable True to enable, false to disable + /// \param thickness New outline thickness /// - /// \see EnableFill - /// - //////////////////////////////////////////////////////////// - void EnableOutline(bool enable); - - //////////////////////////////////////////////////////////// - /// \brief Change the position of a point - /// - /// Warning: this function doesn't check the validity of - /// \a index, if it is out of bounds (ie. in the range - /// [0, GetPointscount() - 1]) the behaviour is undefined. - /// - /// \param index Index of the point - /// \param position New position of the point - /// - /// \see GetPointPosition, SetPointColor, SetPointOutlineColor - /// - //////////////////////////////////////////////////////////// - void SetPointPosition(unsigned int index, const Vector2f& position); - - //////////////////////////////////////////////////////////// - /// \brief Change the position of a point - /// - /// Warning: this function doesn't check the validity of - /// \a index, if it is out of bounds (ie. in the range - /// [0, GetPointscount() - 1]) the behaviour is undefined. - /// - /// \param index Index of the point - /// \param x New X coordinate of the point - /// \param y New Y coordinate of the point - /// - /// \see GetPointPosition, SetPointColor, SetPointOutlineColor - /// - //////////////////////////////////////////////////////////// - void SetPointPosition(unsigned int index, float x, float y); - - //////////////////////////////////////////////////////////// - /// \brief Change the color of a point - /// - /// Warning: this function doesn't check the validity of - /// \a index, if it is out of bounds (ie. in the range - /// [0, GetPointscount() - 1]) the behaviour is undefined. - /// - /// \param index Index of the point - /// \param color New color of the point - /// - /// \see GetPointColor, SetPointPosition, SetPointOutlineColor - /// - //////////////////////////////////////////////////////////// - void SetPointColor(unsigned int index, const Color& color); - - //////////////////////////////////////////////////////////// - /// \brief Change the outline color of a point - /// - /// Warning: this function doesn't check the validity of - /// \a index, if it is out of bounds (ie. in the range - /// [0, GetPointscount() - 1]) the behaviour is undefined. - /// - /// \param index Index of the point - /// \param color New outline color of the point - /// - /// \see GetPointOutlineColor, SetPointPosition, SetPointColor - /// - //////////////////////////////////////////////////////////// - void SetPointOutlineColor(unsigned int index, const Color& color); - - //////////////////////////////////////////////////////////// - /// \brief Change the thickness of the shape outline - /// - /// \param thickness New thickness of the outline - /// - /// \see GetOutlineThickness, EnableOutline + /// \see GetOutlineThickness /// //////////////////////////////////////////////////////////// void SetOutlineThickness(float thickness); //////////////////////////////////////////////////////////// - /// \brief Get the position of a point + /// \brief Get the source texture of the shape /// - /// Warning: this function doesn't check the validity of - /// \a index, if it is out of bounds (ie. in the range - /// [0, GetPointscount() - 1]) the behaviour is undefined. + /// If the shape has no source texture, a NULL pointer is returned. + /// The returned pointer is const, which means that you can't + /// modify the texture when you retrieve it with this function. /// - /// \param index Index of the point + /// \return Pointer to the shape's texture /// - /// \return Position of the index-th point - /// - /// \see SetPointPosition, GetPointColor, GetPointOutlineColor + /// \see SetTexture /// //////////////////////////////////////////////////////////// - const Vector2f& GetPointPosition(unsigned int index) const; + const Texture* GetTexture() const; //////////////////////////////////////////////////////////// - /// \brief Get the color of a point + /// \brief Get the sub-rectangle of the texture displayed by the shape /// - /// Warning: this function doesn't check the validity of - /// \a index, if it is out of bounds (ie. in the range - /// [0, GetPointscount() - 1]) the behaviour is undefined. + /// \return Texture rectangle of the shape /// - /// \param index Index of the point - /// - /// \return Color of the index-th point - /// - /// \see SetPointColor, GetPointPosition, GetPointOutlineColor + /// \see SetTextureRect /// //////////////////////////////////////////////////////////// - const Color& GetPointColor(unsigned int index) const; + const IntRect& GetTextureRect() const; //////////////////////////////////////////////////////////// - /// \brief Get the outline color of a point + /// \brief Get the fill color of the shape /// - /// Warning: this function doesn't check the validity of - /// \a index, if it is out of bounds (ie. in the range - /// [0, GetPointscount() - 1]) the behaviour is undefined. + /// \return Fill color of the shape /// - /// \param index Index of the point - /// - /// \return Outline color of the index-th point - /// - /// \see SetPointOutlineColor, GetPointPosition, GetPointColor + /// \see SetFillColor /// //////////////////////////////////////////////////////////// - const Color& GetPointOutlineColor(unsigned int index) const; + const Color& GetFillColor() const; //////////////////////////////////////////////////////////// - /// \brief Get the thickness of the shape outline + /// \brief Get the outline color of the shape /// - /// \return Current outline thickness + /// \return Outline color of the shape + /// + /// \see SetOutlineColor + /// + //////////////////////////////////////////////////////////// + const Color& GetOutlineColor() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the outline thickness of the shape + /// + /// \return Outline thickness of the shape /// /// \see SetOutlineThickness /// @@ -238,221 +185,124 @@ public : float GetOutlineThickness() const; //////////////////////////////////////////////////////////// - /// \brief Create a new line + /// \brief Get the local bounding rectangle of the entity /// - /// This is a static function that returns a new object, - /// don't try to call it on an existing object to modify it. - /// \code - /// sf::Shape line = sf::Shape::Line(0, 0, 10, 20, 2.5f, sf::Color::Green); - /// \endcode - /// Warning: the position and color that you pass to this function - /// are used to compute the position and color of each point, - /// they have nothing to do with the global position and color - /// of the shape, inherited from sf::Drawable. - /// See the class description for more information about this. + /// The returned rectangle is in local coordinates, which means + /// that it ignores the transformations (translation, rotation, + /// scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// entity in the entity's coordinate system. /// - /// \param p1x X coordinate of the start point - /// \param p1y Y coordinate of the start point - /// \param p2x X coordinate of the end point - /// \param p2y Y coordinate of the end point - /// \param thickness Thickness of the line - /// \param color Color of the shape's points - /// \param outline Outline thickness - /// \param outlineColor Outline color of the shape's points - /// - /// \see Rectangle, Circle + /// \return Local bounding rectangle of the entity /// //////////////////////////////////////////////////////////// - static Shape Line(float p1x, float p1y, float p2x, float p2y, float thickness, const Color& color, float outline = 0.f, const Color& outlineColor = Color(0, 0, 0)); + FloatRect GetLocalBounds() const; //////////////////////////////////////////////////////////// - /// \brief Create a new line + /// \brief Get the global bounding rectangle of the entity /// - /// This is a static function that returns a new object, - /// don't try to call it on an existing object to modify it. - /// \code - /// sf::Vector2f start(0, 0); - /// sf::Vector2f end(10, 20); - /// sf::Shape line = sf::Shape::Line(start, end, 2.5f, sf::Color::Green); - /// \endcode - /// Warning: the position and color that you pass to this function - /// are used to compute the position and color of each point, - /// they have nothing to do with the global position and color - /// of the shape, inherited from sf::Drawable. - /// See the class description for more information about this. + /// The returned rectangle is in global coordinates, which means + /// that it takes in account the transformations (translation, + /// rotation, scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// sprite in the global 2D world's coordinate system. /// - /// \param start Start point - /// \param end End point - /// \param thickness Thickness of the line - /// \param color Color of the shape's points - /// \param outline Outline thickness - /// \param outlineColor Outline color of the shape's points - /// - /// \see Rectangle, Circle + /// \return Global bounding rectangle of the entity /// //////////////////////////////////////////////////////////// - static Shape Line(const Vector2f& start, const Vector2f& end, float thickness, const Color& color, float outline = 0.f, const Color& outlineColor = Color(0, 0, 0)); - - //////////////////////////////////////////////////////////// - /// \brief Create a new rectangular shape - /// - /// This is a static function that returns a new object, - /// don't try to call it on an existing object to modify it. - /// \code - /// sf::Shape rect = sf::Shape::Rectangle(10, 20, 50, 100, sf::Color::Red); - /// \endcode - /// Warning: the position and color that you pass to this function - /// are used to compute the position and color of each point, - /// they have nothing to do with the global position and color - /// of the shape, inherited from sf::Drawable. - /// See the class description for more information about this. - /// - /// \param left Left coordinate of the rectangle - /// \param top Top coordinate of the rectangle - /// \param width Width of the rectangle - /// \param height Height of the rectangle - /// \param color Color of the shape's points - /// \param outline Outline thickness - /// \param outlineColor Outline color of the shape's points - /// - /// \see Line, Circle - /// - //////////////////////////////////////////////////////////// - static Shape Rectangle(float left, float top, float width, float height, const Color& color, float outline = 0.f, const Color& outlineColor = Color(0, 0, 0)); - - //////////////////////////////////////////////////////////// - /// \brief Create a new rectangular shape - /// - /// This is a static function that returns a new object, - /// don't try to call it on an existing object to modify it. - /// \code - /// sf::FloatRect source(10, 20, 50, 100); - /// sf::Shape rect = sf::Shape::Rectangle(source, sf::Color::Red); - /// \endcode - /// Warning: the position and color that you pass to this function - /// are used to compute the position and color of each point, - /// they have nothing to do with the global position and color - /// of the shape, inherited from sf::Drawable. - /// See the class description for more information about this. - /// - /// \param rectangle Rectangle defining the shape - /// \param color Color of the shape's points - /// \param outline Outline thickness - /// \param outlineColor Outline color of the shape's points - /// - /// \see Line, Circle - /// - //////////////////////////////////////////////////////////// - static Shape Rectangle(const FloatRect& rectangle, const Color& color, float outline = 0.f, const Color& outlineColor = Color(0, 0, 0)); - - //////////////////////////////////////////////////////////// - /// \brief Create a new circular shape - /// - /// This is a static function that returns a new object, - /// don't try to call it on an existing object to modify it. - /// \code - /// sf::Shape circle = sf::Shape::Circle(0, 0, 7, sf::Color::Blue); - /// \endcode - /// Warning: the position and color that you pass to this function - /// are used to compute the position and color of each point, - /// they have nothing to do with the global position and color - /// of the shape, inherited from sf::Drawable. - /// See the class description for more information about this. - /// - /// \param x X coordinate of the center - /// \param y Y coordinate of the center - /// \param radius Radius of the circle - /// \param color Color of the shape's points - /// \param outline Outline thickness - /// \param outlineColor Outline color of the shape's points - /// - /// \see Line, Rectangle - /// - //////////////////////////////////////////////////////////// - static Shape Circle(float x, float y, float radius, const Color& color, float outline = 0.f, const Color& outlineColor = Color(0, 0, 0)); - - //////////////////////////////////////////////////////////// - /// \brief Create a new circular shape - /// - /// This is a static function that returns a new object, - /// don't try to call it on an existing object to modify it. - /// \code - /// sf::Vector2f center(0, 0); - /// sf::Shape circle = sf::Shape::Circle(center, 7, sf::Color::Blue); - /// \endcode - /// Warning: the position and color that you pass to this function - /// are used to compute the position and color of each point, - /// they have nothing to do with the global position and color - /// of the shape, inherited from sf::Drawable. - /// See the class description for more information about this. - /// - /// \param center Center of the circle - /// \param radius Radius of the circle - /// \param color Color of the shape's points - /// \param outline Outline thickness - /// \param outlineColor Outline color of the shape's points - /// - /// \see Line, Rectangle - /// - //////////////////////////////////////////////////////////// - static Shape Circle(const Vector2f& center, float radius, const Color& color, float outline = 0.f, const Color& outlineColor = Color(0, 0, 0)); + FloatRect GetGlobalBounds() const; protected : //////////////////////////////////////////////////////////// - /// \brief Draw the object to a render target - /// - /// \param target Render target - /// \param renderer Renderer providing low-level rendering commands + /// \brief Default constructor /// //////////////////////////////////////////////////////////// - virtual void Render(RenderTarget& target, Renderer& renderer) const; + Shape(); + + //////////////////////////////////////////////////////////// + /// \brief Recompute the internal geometry of the shape + /// + /// This function must be called by the derived class everytime + /// the shape's points change (ie. the result of either + /// GetPointsCount or GetPoint is different). + /// + //////////////////////////////////////////////////////////// + void Update(); private : //////////////////////////////////////////////////////////// - /// \brief Compile the shape + /// \brief Get the number of points defining the shape /// - /// This function precomputes all the internal parameters - /// needed to properly render the shape (center, outline points). + /// This function must be implemented in derived classes. + /// + /// \return Number of points of the shape /// //////////////////////////////////////////////////////////// - void Compile(); + virtual unsigned int GetOutlinePointsCount() const = 0; //////////////////////////////////////////////////////////// - /// \brief Compute the normal of a given 2D segment + /// \brief Get a point of the shape /// - /// \param p1 First point of the segment - /// \param p2 Second point of the segment - /// \param normal Variable to fill with the calculated normal + /// This function must be implemented in derived classes. /// - /// \return False if the normal couldn't be calculated (segment is null) + /// \param index Index of the point to get + /// + /// \return Index-th point of the shape /// //////////////////////////////////////////////////////////// - static bool ComputeNormal(const Vector2f& p1, const Vector2f& p2, Vector2f& normal); + virtual Vector2f GetOutlinePoint(unsigned int index) const = 0; + +private : //////////////////////////////////////////////////////////// - /// \brief Define a simple 2D point with position, normal and colors + /// \brief Draw the shape to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states /// //////////////////////////////////////////////////////////// - struct Point - { - Point(const Vector2f& position = Vector2f(0, 0), const Color& color = Color(255, 255, 255), const Color& outlineColor = Color(255, 255, 255)); + virtual void Draw(RenderTarget& target, RenderStates states) const; - Vector2f Position; ///< Position - Vector2f Normal; ///< Extruded normal - Color Col; ///< Color of the point - Color OutlineCol; ///< Outline color of the point - }; + //////////////////////////////////////////////////////////// + /// \brief Update the fill vertices' color + /// + //////////////////////////////////////////////////////////// + void UpdateFillColors(); + + //////////////////////////////////////////////////////////// + /// \brief Update the fill vertices' texture coordinates + /// + //////////////////////////////////////////////////////////// + void UpdateTexCoords(); + + //////////////////////////////////////////////////////////// + /// \brief Update the outline vertices' position + /// + //////////////////////////////////////////////////////////// + void UpdateOutline(); + + //////////////////////////////////////////////////////////// + /// \brief Update the outline vertices' color + /// + //////////////////////////////////////////////////////////// + void UpdateOutlineColors(); + +private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - std::vector myPoints; ///< Points composing the shape - float myOutline; ///< Outline thickness - bool myIsFillEnabled; ///< Should we draw the inside if the shape ? - bool myIsOutlineEnabled; ///< Should we draw the outline if the shape ? - bool myIsCompiled; ///< Compiled state of the shape + const Texture* myTexture; ///< Texture of the shape + IntRect myTextureRect; ///< Rectangle defining the area of the source texture to display + Color myFillColor; ///< Fill color + Color myOutlineColor; ///< Outline color + float myOutlineThickness; ///< Thickness of the shape's outline + VertexArray myVertices; ///< Vertex array containing the fill geometry + VertexArray myOutlineVertices; ///< Vertex array containing the outline geometry + FloatRect myInsideBounds; ///< Bounding rectangle of the inside (fill) + FloatRect myBounds; ///< Bounding rectangle of the whole shape (outline + fill) }; } // namespace sf @@ -467,73 +317,28 @@ private : /// /// sf::Shape is a drawable class that allows to define and /// display a custom convex shape on a render target. +/// It's only an abstract base, it needs to be specialized for +/// concrete types of shapes (circle, rectangle, convex polygon, +/// star, ...). /// -/// It is important to keep in mind that shapes must always be -/// convex, otherwise they may not be drawn correctly. Moreover, -/// the points must be added in the right order; using a random -/// order would also result in an incorrect shape. +/// In addition to the attributes provided by the specialized +/// shape classes, a shape always has the following attributes: +/// \li a texture +/// \li a texture rectangle +/// \li a fill color +/// \li an outline color +/// \li an outline thickness /// -/// A shape is made of points that have their own individual -/// attributes: -/// \li position (relative to the origin of the shape) -/// \li color -/// \li outline color +/// Each feature is optional, and can be disabled easily: +/// \li the texture can be null +/// \li the fill/outline colors can be sf::Color::Transparent +/// \li the outline thickness can be zero /// -/// Shapes have an outline that can be enabled or not. You can -/// control the thickness of the outline with the SetOutlineThickness -/// function. +/// You can write your own derived shape class, there are only +/// two virtual functions to override: +/// \li GetOutlinePointsCount must return the number of points of the shape +/// \li GetOutlinePoint must return the points of the shape /// -/// They also inherits all the functions from sf::Drawable: -/// position, rotation, scale, origin, global color and blend -/// mode. -/// -/// Some static functions are provided to directly create common -/// shapes such as lines, rectangles and circles: -/// \code -/// sf::Shape line = sf::Shape::Line(start, end, thickness, color); -/// sf::Shape rectangle = sf::Shape::Rectangle(rect, thickness); -/// sf::Shape circle = sf::Shape::Circle(center, radius, color); -/// \endcode -/// -/// A common mistake is to mix the individual points -/// positions / colors and the global position / color of the shape. -/// They are completely separate attributes that are combined -/// when the shape is drawn (positions are added, colors are multiplied). -/// \code -/// sf::Shape line = sf::Shape::Line(sf::Vector2f(100, 100), sf::Vector2f(200, 200), 10, sf::Color::Red); -/// -/// // --> line.GetPosition() is (0, 0), *not* (100, 100) -/// // --> line.GetColor() is white, *not* red -/// \endcode -/// So if you plan to change the position / color of your shape -/// after it is created, you'd better create the points around -/// the origin and with white color, and use only the global -/// position / color (SetPosition, SetColor). -/// -/// Usage example: -/// \code -/// // Create a shape -/// sf::Shape shape; -/// -/// // Define its points -/// shape.AddPoint(10, 10, sf::Color::White, sf::Color::Red); -/// shape.AddPoint(50, 10, sf::Color::White, sf::Color::Green); -/// shape.AddPoint(10, 50, sf::Color::White, sf::Color::Blue); -/// -/// // Enable outline only -/// shape.EnableFill(false); -/// shape.EnableOutline(true); -/// shape.SetOutlineThickness(10); -/// -/// // Display it -/// window.Draw(shape); // window is a sf::RenderWindow -/// -/// // Display static shapes -/// window.Draw(sf::Shape::Line(0, 0, 10, 20, sf::Color::Red)); -/// window.Draw(sf::Shape::Rectangle(100, 1000, 50, 20, sf::Color::Green)); -/// window.Draw(sf::Shape::Circle(500, 500, 20, sf::Color::Blue, 5, sf::Color::Black)); -/// \endcode -/// -/// \see sf::Sprite +/// \see sf::LineShape, sf::RectangleShape, sf::CircleShape, sf::ConvexShape, sf::Transformable /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Sprite.hpp b/include/SFML/Graphics/Sprite.hpp index cb754797..675a920f 100644 --- a/include/SFML/Graphics/Sprite.hpp +++ b/include/SFML/Graphics/Sprite.hpp @@ -28,8 +28,9 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include #include +#include +#include #include @@ -39,10 +40,10 @@ class Texture; //////////////////////////////////////////////////////////// /// \brief Drawable representation of a texture, with its -/// own transformations, color, blend mode, etc. +/// own transformations, color, etc. /// //////////////////////////////////////////////////////////// -class SFML_API Sprite : public Drawable +class SFML_API Sprite : public Drawable, public Transformable { public : @@ -57,11 +58,24 @@ public : //////////////////////////////////////////////////////////// /// \brief Construct the sprite from a source texture /// + /// \param texture Source texture + /// /// \see SetTexture /// //////////////////////////////////////////////////////////// explicit Sprite(const Texture& texture); + //////////////////////////////////////////////////////////// + /// \brief Construct the sprite from a sub-rectangle of a source texture + /// + /// \param texture Source texture + /// \param rectangle Sub-rectangle of the texture to assign to the sprite + /// + /// \see SetTexture, SetTextureRect + /// + //////////////////////////////////////////////////////////// + Sprite(const Texture& texture, const IntRect& rectangle); + //////////////////////////////////////////////////////////// /// \brief Change the source texture of the sprite /// @@ -70,91 +84,52 @@ public : /// doesn't store its own copy of the texture, but rather keeps /// a pointer to the one that you passed to this function. /// If the source texture is destroyed and the sprite tries to - /// use it, it may appear as a white rectangle. - /// If \a adjustToNewSize is true, the SubRect property of - /// the sprite is adjusted to the size of the new texture. If - /// it is false, the SubRect is unchanged. + /// use it, the behaviour is undefined. + /// If \a resetRect is true, the TextureRect property of + /// the sprite is automatically adjusted to the size of the new + /// texture. If it is false, the texture rect is left unchanged. /// - /// \param texture New texture - /// \param adjustToNewSize Should the sub-rect be adjusted to the size of the new texture? + /// \param texture New texture + /// \param resetRect Should the texture rect be reset to the size of the new texture? /// - /// \see GetTexture, SetSubRect + /// \see GetTexture, SetTextureRect /// //////////////////////////////////////////////////////////// - void SetTexture(const Texture& texture, bool adjustToNewSize = false); + void SetTexture(const Texture& texture, bool resetRect = false); //////////////////////////////////////////////////////////// - /// \brief Set the part of the texture that the sprite will display + /// \brief Set the sub-rectangle of the texture that the sprite will display /// - /// The sub-rectangle is useful when you don't want to display + /// The texture rect is useful when you don't want to display /// the whole texture, but rather a part of it. - /// By default, the sub-rectangle covers the entire texture. + /// By default, the texture rect covers the entire texture. /// /// \param rectangle Rectangle defining the region of the texture to display /// - /// \see GetSubRect, SetTexture + /// \see GetTextureRect, SetTexture /// //////////////////////////////////////////////////////////// - void SetSubRect(const IntRect& rectangle); + void SetTextureRect(const IntRect& rectangle); //////////////////////////////////////////////////////////// - /// \brief Change the size of the sprite + /// \brief Set the global color of the sprite /// - /// This function is just a shortcut that calls SetScale - /// with the proper values, calculated from the size of - /// the current subrect. - /// If \a width or \a height is not strictly positive, - /// this functions does nothing. + /// This color is modulated (multiplied) with the sprite's + /// texture. It can be used to colorize the sprite, or change + /// its global opacity. + /// By default, the sprite's color is opaque white. /// - /// \param width New width of the sprite - /// \param height New height of the sprite + /// \param color New color of the sprite /// - /// \see GetSize + /// \see GetColor /// //////////////////////////////////////////////////////////// - void Resize(float width, float height); - - //////////////////////////////////////////////////////////// - /// \brief Change the size of the sprite - /// - /// This function is just a shortcut that calls SetScale - /// with the proper values, calculated from the size of - /// the current subrect. - /// If \a size.x or \a size.y is not strictly positive, - /// this functions does nothing. - /// - /// \param size New size of the sprite - /// - /// \see GetSize - /// - //////////////////////////////////////////////////////////// - void Resize(const Vector2f& size); - - //////////////////////////////////////////////////////////// - /// \brief Flip the sprite horizontally - /// - /// \param flipped True to flip the sprite - /// - /// \see FlipY - /// - //////////////////////////////////////////////////////////// - void FlipX(bool flipped); - - //////////////////////////////////////////////////////////// - /// \brief Flip the sprite vertically - /// - /// \param flipped True to flip the sprite - /// - /// \see FlipX - /// - //////////////////////////////////////////////////////////// - void FlipY(bool flipped); + void SetColor(const Color& color); //////////////////////////////////////////////////////////// /// \brief Get the source texture of the sprite /// - /// If the sprite has no source texture, or if the texture - /// doesn't exist anymore, a NULL pointer is returned. + /// If the sprite has no source texture, a NULL pointer is returned. /// The returned pointer is const, which means that you can't /// modify the texture when you retrieve it with this function. /// @@ -166,48 +141,82 @@ public : const Texture* GetTexture() const; //////////////////////////////////////////////////////////// - /// \brief Get the region of the texture displayed by the sprite + /// \brief Get the sub-rectangle of the texture displayed by the sprite /// - /// \return Rectangle defining the region of the texture + /// \return Texture rectangle of the sprite /// - /// \see SetSubRect + /// \see SetTextureRect /// //////////////////////////////////////////////////////////// - const IntRect& GetSubRect() const; + const IntRect& GetTextureRect() const; //////////////////////////////////////////////////////////// - /// \brief Get the global size of the sprite + /// \brief Get the global color of the sprite /// - /// This function is a shortcut that multiplies the - /// size of the subrect by the scale factors. + /// \return Global color of the sprite /// - /// \return Size of the sprite - /// - /// \see Resize + /// \see SetColor /// //////////////////////////////////////////////////////////// - Vector2f GetSize() const; - -protected : + const Color& GetColor() const; //////////////////////////////////////////////////////////// - /// \brief Draw the object to a render target + /// \brief Get the local bounding rectangle of the entity /// - /// \param target Render target - /// \param renderer Renderer providing low-level rendering commands + /// The returned rectangle is in local coordinates, which means + /// that it ignores the transformations (translation, rotation, + /// scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// entity in the entity's coordinate system. + /// + /// \return Local bounding rectangle of the entity /// //////////////////////////////////////////////////////////// - virtual void Render(RenderTarget& target, Renderer& renderer) const; + FloatRect GetLocalBounds() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the global bounding rectangle of the entity + /// + /// The returned rectangle is in global coordinates, which means + /// that it takes in account the transformations (translation, + /// rotation, scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// sprite in the global 2D world's coordinate system. + /// + /// \return Global bounding rectangle of the entity + /// + //////////////////////////////////////////////////////////// + FloatRect GetGlobalBounds() const; private : + //////////////////////////////////////////////////////////// + /// \brief Draw the sprite to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states + /// + //////////////////////////////////////////////////////////// + virtual void Draw(RenderTarget& target, RenderStates states) const; + + //////////////////////////////////////////////////////////// + /// \brief Update the vertices' positions + /// + //////////////////////////////////////////////////////////// + void UpdatePositions(); + + //////////////////////////////////////////////////////////// + /// \brief Update the vertices' texture coordinates + /// + //////////////////////////////////////////////////////////// + void UpdateTexCoords(); + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - ResourcePtr myTexture; ///< Texture used to draw the sprite - IntRect mySubRect; ///< Sub-rectangle of source texture to assign to the sprite - bool myIsFlippedX; ///< Is the sprite flipped on the X axis ? - bool myIsFlippedY; ///< Is the sprite flipped on the Y axis ? + Vertex myVertices[4]; ///< Vertices defining the sprite's geometry + const Texture* myTexture; ///< Texture of the sprite + IntRect myTextureRect; ///< Rectangle defining the area of the source texture to display }; } // namespace sf @@ -223,11 +232,11 @@ private : /// sf::Sprite is a drawable class that allows to easily display /// a texture (or a part of it) on a render target. /// -/// It inherits all the functions from sf::Drawable: -/// position, rotation, scale, origin, global color and blend -/// mode. It also adds sprite-specific properties such as the -/// texture to use, the part of it to display, and some convenience -/// functions to flip or resize the sprite. +/// It inherits all the functions from sf::Transformable: +/// position, rotation, scale, origin. It also adds sprite-specific +/// properties such as the texture to use, the part of it to display, +/// and some convenience functions to change the overall color of the +/// sprite, or to get its bounding rectangle. /// /// sf::Sprite works in combination with the sf::Texture class, which /// loads and provides the pixel data of a given texture. @@ -237,11 +246,11 @@ private : /// and any operation on it is slow (often too slow for real-time /// applications). On the other side, a sf::Sprite is a lightweight /// object which can use the pixel data of a sf::Texture and draw -/// it with its own transformation / color / blending attributes. +/// it with its own transformation/color/blending attributes. /// /// It is important to note that the sf::Sprite instance doesn't /// copy the texture that it uses, it only keeps a reference to it. -/// Thus, a sf::Texture must not be destructed while it is +/// Thus, a sf::Texture must not be destroyed while it is /// used by a sf::Sprite (i.e. never write a function that /// uses a local sf::Texture instance for creating a sprite). /// @@ -254,13 +263,14 @@ private : /// // Create a sprite /// sf::Sprite sprite; /// sprite.SetTexture(texture); -/// sprite.SetSubRect(sf::IntRect(10, 10, 50, 30)); -/// sprite.Resize(100, 60); +/// sprite.SetTextureRect(sf::IntRect(10, 10, 50, 30)); +/// sprite.SetColor(sf::Color(255, 255, 255, 200)); +/// sprite.SetPosition(100, 25); /// -/// // Display it -/// window.Draw(sprite); // window is a sf::RenderWindow +/// // Draw it +/// window.Draw(sprite); /// \endcode /// -/// \see sf::Texture +/// \see sf::Texture, sf::Transformable /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/StarShape.hpp b/include/SFML/Graphics/StarShape.hpp new file mode 100644 index 00000000..c906a5b8 --- /dev/null +++ b/include/SFML/Graphics/StarShape.hpp @@ -0,0 +1,178 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_STARSHAPE_HPP +#define SFML_STARSHAPE_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Specialized shape representing a star +/// +//////////////////////////////////////////////////////////// +class SFML_API StarShape : public Shape +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a star with 5 points, an inner radius of 10 and + /// an outer radius of 20. + /// + //////////////////////////////////////////////////////////// + StarShape(); + + //////////////////////////////////////////////////////////// + /// \brief Set the inner radius of the star + /// + /// The default inner radius of a star is 10. + /// + /// \param radius New inner radius of the star + /// + /// \see GetInnerRadius + /// + //////////////////////////////////////////////////////////// + void SetInnerRadius(float radius); + + //////////////////////////////////////////////////////////// + /// \brief Get the inner radius of the star + /// + /// \return Inner radius of the star + /// + /// \see SetInnerRadius + /// + //////////////////////////////////////////////////////////// + float GetInnerRadius() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the outer radius of the star + /// + /// The default outer radius of a star is 20. + /// + /// \param radius New outer radius of the star + /// + /// \see GetOuterRadius + /// + //////////////////////////////////////////////////////////// + void SetOuterRadius(float radius); + + //////////////////////////////////////////////////////////// + /// \brief Get the outer radius of the star + /// + /// \return Outer radius of the star + /// + /// \see SetOuterRadius + /// + //////////////////////////////////////////////////////////// + float GetOuterRadius() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the number of points of the star + /// + /// The default number of points of a star is 5. + /// + /// \param count New number of points of the star + /// + /// \see GetPointsCount + /// + //////////////////////////////////////////////////////////// + void SetPointsCount(unsigned int count); + + //////////////////////////////////////////////////////////// + /// \brief Get the number of points of the star + /// + /// \return Number of points of the star + /// + /// \see SetPointsCount + /// + //////////////////////////////////////////////////////////// + unsigned int GetPointsCount() const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Get the number of points defining the shape + /// + /// \return Number of points of the shape + /// + //////////////////////////////////////////////////////////// + virtual unsigned int GetOutlinePointsCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a point of the shape + /// + /// \param index Index of the point to get + /// + /// \return Index-th point of the shape + /// + //////////////////////////////////////////////////////////// + virtual Vector2f GetOutlinePoint(unsigned int index) const; + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + float myInnerRadius; ///< Inner radius of the star + float myOuterRadius; ///< Outer radius of the star + unsigned int myPointsCount; ///< Number of points of the star +}; + +} // namespace sf + + +#endif // SFML_STARSHAPE_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::StarShape +/// \ingroup graphics +/// +/// This class inherits all the functions of sf::Transformable +/// (position, rotation, scale, bounds, ...) as well as the +/// functions of sf::Shape (outline, color, texture, ...). +/// +/// Usage example: +/// \code +/// sf::StarShape star; +/// star.SetInnerRadius(25); +/// star.SetInnerRadius(40); +/// star.SetPointsCount(6); +/// star.SetOutlineColor(sf::Color::Red); +/// star.SetOutlineThickness(5); +/// star.SetPosition(10, 20); +/// ... +/// window.Draw(star); +/// \endcode +/// +/// \see sf::Shape, sf::CircleShape, sf::RectangleShape, sf::ConvexShape +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Text.hpp b/include/SFML/Graphics/Text.hpp index 1aa45aa7..47f35f8e 100644 --- a/include/SFML/Graphics/Text.hpp +++ b/include/SFML/Graphics/Text.hpp @@ -28,12 +28,14 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include #include #include +#include #include #include +#include #include +#include namespace sf @@ -42,7 +44,7 @@ namespace sf /// \brief Graphical text that can be drawn to a render target /// //////////////////////////////////////////////////////////// -class SFML_API Text : public Drawable +class SFML_API Text : public Drawable, public Transformable { public : @@ -100,6 +102,12 @@ public : //////////////////////////////////////////////////////////// /// \brief Set the text's font /// + /// The \a font argument refers to a texture that must + /// exist as long as the text uses it. Indeed, the text + /// doesn't store its own copy of the font, but rather keeps + /// a pointer to the one that you passed to this function. + /// If the font is destroyed and the text tries to + /// use it, the behaviour is undefined. /// Texts have a valid font by default, which the built-in /// Font::GetDefaultFont(). /// @@ -136,6 +144,18 @@ public : //////////////////////////////////////////////////////////// void SetStyle(unsigned long style); + //////////////////////////////////////////////////////////// + /// \brief Set the global color of the text + /// + /// By default, the text's color is opaque white. + /// + /// \param color New color of the text + /// + /// \see GetColor + /// + //////////////////////////////////////////////////////////// + void SetColor(const Color& color); + //////////////////////////////////////////////////////////// /// \brief Get the text's string /// @@ -188,14 +208,23 @@ public : //////////////////////////////////////////////////////////// unsigned long GetStyle() const; + //////////////////////////////////////////////////////////// + /// \brief Get the global color of the text + /// + /// \return Global color of the text + /// + /// \see SetColor + /// + //////////////////////////////////////////////////////////// + const Color& GetColor() const; + //////////////////////////////////////////////////////////// /// \brief Return the position of the \a index-th character /// /// This function computes the visual position of a character /// from its index in the string. The returned position is - /// in local coordinates (translation, rotation, scale and - /// origin are not applied). You can easily get the corresponding - /// global position with the TransformToGlobal function. + /// in global coordinates (translation, rotation, scale and + /// origin are applied). /// If \a index is out of range, the position of the end of /// the string is returned. /// @@ -204,46 +233,63 @@ public : /// \return Position of the character /// //////////////////////////////////////////////////////////// - Vector2f GetCharacterPos(std::size_t index) const; + Vector2f FindCharacterPos(std::size_t index) const; //////////////////////////////////////////////////////////// - /// \brief Get the bounding rectangle of the text + /// \brief Get the local bounding rectangle of the entity /// - /// The returned rectangle is in global coordinates. + /// The returned rectangle is in local coordinates, which means + /// that it ignores the transformations (translation, rotation, + /// scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// entity in the entity's coordinate system. /// - /// \return Bounding rectangle of the text + /// \return Local bounding rectangle of the entity /// //////////////////////////////////////////////////////////// - FloatRect GetRect() const; - -protected : + FloatRect GetLocalBounds() const; //////////////////////////////////////////////////////////// - /// \brief Draw the object to a render target + /// \brief Get the global bounding rectangle of the entity /// - /// \param target Render target - /// \param renderer Renderer providing low-level rendering commands + /// The returned rectangle is in global coordinates, which means + /// that it takes in account the transformations (translation, + /// rotation, scale, ...) that are applied to the entity. + /// In other words, this function returns the bounds of the + /// sprite in the global 2D world's coordinate system. + /// + /// \return Global bounding rectangle of the entity /// //////////////////////////////////////////////////////////// - virtual void Render(RenderTarget& target, Renderer& renderer) const; + FloatRect GetGlobalBounds() const; private : //////////////////////////////////////////////////////////// - /// \brief Recompute the bounding rectangle + /// \brief Draw the text to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states /// //////////////////////////////////////////////////////////// - void UpdateRect() const; + virtual void Draw(RenderTarget& target, RenderStates states) const; + + //////////////////////////////////////////////////////////// + /// \brief Update the text's geometry + /// + //////////////////////////////////////////////////////////// + void UpdateGeometry(); //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - String myString; ///< String to display - ResourcePtr myFont; ///< Font used to display the string - unsigned int myCharacterSize; ///< Base size of characters, in pixels - unsigned long myStyle; ///< Text style (see Style enum) - mutable FloatRect myBaseRect; ///< Bounding rectangle of the text in object coordinates - mutable bool myRectUpdated; ///< Is the bounding rectangle up-to-date ? + String myString; ///< String to display + const Font* myFont; ///< Font used to display the string + unsigned int myCharacterSize; ///< Base size of characters, in pixels + unsigned long myStyle; ///< Text style (see the Style enum) + Color myColor; ///< Text color + VertexArray myVertices; ///< Vertex array containing the text's geometry + FloatRect myBounds; ///< Bounding rectangle of the text (in local coordinates) }; } // namespace sf @@ -259,13 +305,13 @@ private : /// sf::Text is a drawable class that allows to easily display /// some text with custom style and color on a render target. /// -/// It inherits all the functions from sf::Drawable: -/// position, rotation, scale, origin, global color and blend -/// mode. It also adds text-specific properties such as the -/// font to use, the character size, the font style (bold, -/// italic, underlined), and the text to display of course. +/// It inherits all the functions from sf::Transformable: +/// position, rotation, scale, origin. It also adds text-specific +/// properties such as the font to use, the character size, +/// the font style (bold, italic, underlined), the global color +/// and the text to display of course. /// It also provides convenience functions to calculate the -/// graphical size of the text, or to get the visual position +/// graphical size of the text, or to get the global position /// of a given character. /// /// sf::Text works in combination with the sf::Font class, which @@ -294,12 +340,17 @@ private : /// sf::Text text("hello"); /// text.SetFont(font); /// text.SetCharacterSize(30); -/// text.SetStyle(sf::Text::Regular); +/// text.SetStyle(sf::Text::Bold); +/// text.SetColor(sf::Color::Red); /// -/// // Display it -/// window.Draw(text); // window is a sf::RenderWindow +/// // Draw it +/// window.Draw(text); /// \endcode /// -/// \see sf::Font +/// Note that you don't need to load a font to draw text, +/// SFML comes with a built-in font that is implicitely used +/// by default. +/// +/// \see sf::Font, sf::Transformable /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Texture.hpp b/include/SFML/Graphics/Texture.hpp index f4a2a276..b4d9a109 100644 --- a/include/SFML/Graphics/Texture.hpp +++ b/include/SFML/Graphics/Texture.hpp @@ -46,6 +46,18 @@ class InputStream; //////////////////////////////////////////////////////////// class SFML_API Texture : public Resource, GlResource { +public : + + //////////////////////////////////////////////////////////// + /// \brief Types of texture coordinates that can be used for rendering + /// + //////////////////////////////////////////////////////////// + enum CoordinateType + { + Normalized, ///< Texture coordinates in range [0 .. 1] + Pixels ///< Texture coordinates in range [0 .. size] + }; + public : //////////////////////////////////////////////////////////// @@ -354,12 +366,23 @@ public : /// \brief Activate the texture for rendering /// /// This function is mainly used internally by the SFML - /// render system. However it can be useful when + /// rendering system. However it can be useful when /// using sf::Texture together with OpenGL code (this function /// is equivalent to glBindTexture). /// + /// The \a coordinateType argument controls how texture + /// coordinates will be interpreted. If Normalized (the default), they + /// must be in range [0 .. 1], which is the default way of handling + /// texture coordinates with OpenGL. If Pixels, they must be given + /// in pixels (range [0 .. size]). This mode is used internally by + /// the graphics classes of SFML, it makes the definition of texture + /// coordinates more intuitive for the high-level API, users don't need + /// to compute normalized values. + /// + /// \param coordinateType Type of texture coordinates to use + /// //////////////////////////////////////////////////////////// - void Bind() const; + void Bind(CoordinateType coordinateType = Normalized) const; //////////////////////////////////////////////////////////// /// \brief Enable or disable the smooth filter @@ -388,18 +411,38 @@ public : bool IsSmooth() const; //////////////////////////////////////////////////////////// - /// \brief Convert a rectangle of pixels into texture coordinates + /// \brief Enable or disable repeating /// - /// This function is used by code that needs to map the texture - /// to some OpenGL geometry. It converts the source rectangle, - /// expressed in pixels, to float coordinates in the range [0, 1]. + /// Repeating is involved when using texture coordinates + /// outside the texture rectangle [0, 0, width, height]. + /// In this case, if repeat mode is enabled, the whole texture + /// will be repeated as many times as needed to reach the + /// coordinate (for example, if the X texture coordinate is + /// 3 * width, the texture will be repeated 3 times). + /// If repeat mode is disabled, the "extra space" will instead + /// be filled with border pixels. + /// Warning: on very old graphics cards, white pixels may appear + /// when the texture is repeated. With such cards, repeat mode + /// can be used reliably only if the texture has power-of-two + /// dimensions (such as 256x128). + /// Repeating is disabled by default. /// - /// \param rectangle Rectangle to convert + /// \param repeated True to repeat the texture, false to disable repeating /// - /// \return Texture coordinates corresponding to \a rectangle + /// \see IsRepeated /// //////////////////////////////////////////////////////////// - FloatRect GetTexCoords(const IntRect& rectangle) const; + void SetRepeated(bool repeated); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the texture is repeated or not + /// + /// \return True if repeat mode is enabled, false if it is disabled + /// + /// \see SetRepeated + /// + //////////////////////////////////////////////////////////// + bool IsRepeated() const; //////////////////////////////////////////////////////////// /// \brief Overload of assignment operator @@ -452,6 +495,7 @@ private : unsigned int myTextureHeight; ///< Actual texture height (can be greater than image height because of padding) unsigned int myTexture; ///< Internal texture identifier bool myIsSmooth; ///< Status of the smooth filter + bool myIsRepeated; ///< Is the texture in repeat mode? mutable bool myPixelsFlipped; ///< To work around the inconsistency in Y orientation }; @@ -515,7 +559,7 @@ private : /// sprite.SetTexture(texture); /// /// // Draw the textured sprite -/// window.Draw(sprite); // window is a sf::RenderWindow +/// window.Draw(sprite); /// \endcode /// /// \code diff --git a/include/SFML/Graphics/Transform.hpp b/include/SFML/Graphics/Transform.hpp new file mode 100644 index 00000000..17459989 --- /dev/null +++ b/include/SFML/Graphics/Transform.hpp @@ -0,0 +1,450 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_TRANSFORM_HPP +#define SFML_TRANSFORM_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Define a 3x3 transform matrix +/// +//////////////////////////////////////////////////////////// +class SFML_API Transform +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an identity transform (a transform that does nothing). + /// + //////////////////////////////////////////////////////////// + Transform(); + + //////////////////////////////////////////////////////////// + /// \brief Construct a transform from a 3x3 matrix + /// + /// \param a00 Element (0, 0) of the matrix + /// \param a01 Element (0, 1) of the matrix + /// \param a02 Element (0, 2) of the matrix + /// \param a10 Element (1, 0) of the matrix + /// \param a11 Element (1, 1) of the matrix + /// \param a12 Element (1, 2) of the matrix + /// \param a20 Element (2, 0) of the matrix + /// \param a21 Element (2, 1) of the matrix + /// \param a22 Element (2, 2) of the matrix + /// + //////////////////////////////////////////////////////////// + Transform(float a00, float a01, float a02, + float a10, float a11, float a12, + float a20, float a21, float a22); + + //////////////////////////////////////////////////////////// + /// \brief Return the transform as a 4x4 matrix + /// + /// This function returns a pointer to an array of 16 floats + /// containing the transform elements as a 4x4 matrix, which + /// is directly compatible with OpenGL functions. + /// + /// \code + /// sf::Transform transform = ...; + /// glLoadMatrixf(transform.GetMatrix()); + /// \endcode + /// + /// \return Pointer to a 4x4 matrix + /// + //////////////////////////////////////////////////////////// + const float* GetMatrix() const; + + //////////////////////////////////////////////////////////// + /// \brief Return the inverse of the transform + /// + /// If the inverse cannot be computed, an identity transform + /// is returned. + /// + /// \return A new transform which is the inverse of self + /// + //////////////////////////////////////////////////////////// + Transform GetInverse() const; + + //////////////////////////////////////////////////////////// + /// \brief Transform a 2D point + /// + /// \param x X coordinate of the point to transform + /// \param y Y coordinate of the point to transform + /// + /// \return Transformed point + /// + //////////////////////////////////////////////////////////// + Vector2f TransformPoint(float x, float y) const; + + //////////////////////////////////////////////////////////// + /// \brief Transform a 2D point + /// + /// \param point Point to transform + /// + /// \return Transformed point + /// + //////////////////////////////////////////////////////////// + Vector2f TransformPoint(const Vector2f& point) const; + + //////////////////////////////////////////////////////////// + /// \brief Transform a rectangle + /// + /// Since SFML doesn't provide support for oriented rectangles, + /// the result of this function is always an axis-aligned + /// rectangle. Which means that if the transform contains a + /// rotation, the bounding rectangle of the transformed rectangle + /// is returned. + /// + /// \param rectangle Rectangle to transform + /// + /// \return Transformed rectangle + /// + //////////////////////////////////////////////////////////// + FloatRect TransformRect(const FloatRect& rectangle) const; + + //////////////////////////////////////////////////////////// + /// \brief Combine two transforms + /// + /// The result is a transform that is equivalent to applying + /// *this followed by \a transform. Mathematically, it is + /// equivalent to a matrix multiplication. + /// + /// \param transform Transform to combine to this transform + /// + /// \return New combined transform + /// + //////////////////////////////////////////////////////////// + Transform Combine(const Transform& transform) const; + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a translation + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Translate(100, 200).Rotate(45); + /// \endcode + /// + /// \param x Offset to apply on X axis + /// \param y Offset to apply on Y axis + /// + /// \return Reference to *this + /// + /// \see Rotate, Scale + /// + //////////////////////////////////////////////////////////// + Transform& Translate(float x, float y); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a translation + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Translate(sf::Vector2f(100, 200)).Rotate(45); + /// \endcode + /// + /// \param offset Translation offset to apply + /// + /// \return Reference to *this + /// + /// \see Rotate, Scale + /// + //////////////////////////////////////////////////////////// + Transform& Translate(const Vector2f& offset); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a rotation + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Rotate(90).Translate(50, 20); + /// \endcode + /// + /// \param angle Rotation angle, in degrees + /// + /// \return Reference to *this + /// + /// \see Translate, Scale + /// + //////////////////////////////////////////////////////////// + Transform& Rotate(float angle); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a rotation + /// + /// The center of rotation is provided for convenience as a second + /// argument, so that you can build rotations around arbitrary points + /// more easily (and efficiently) than the usual + /// Translate(-center).Rotate(angle).Translate(center). + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Rotate(90, 8, 3).Translate(50, 20); + /// \endcode + /// + /// \param angle Rotation angle, in degrees + /// \param centerX X coordinate of the center of rotation + /// \param centerY Y coordinate of the center of rotation + /// + /// \return Reference to *this + /// + /// \see Translate, Scale + /// + //////////////////////////////////////////////////////////// + Transform& Rotate(float angle, float centerX, float centerY); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a rotation + /// + /// The center of rotation is provided for convenience as a second + /// argument, so that you can build rotations around arbitrary points + /// more easily (and efficiently) than the usual + /// Translate(-center).Rotate(angle).Translate(center). + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Rotate(90, sf::Vector2f(8, 3)).Translate(sf::Vector2f(50, 20)); + /// \endcode + /// + /// \param angle Rotation angle, in degrees + /// \param center Center of rotation + /// + /// \return Reference to *this + /// + /// \see Translate, Scale + /// + //////////////////////////////////////////////////////////// + Transform& Rotate(float angle, const Vector2f& center); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a scaling + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Scale(2, 1).Rotate(45); + /// \endcode + /// + /// \param scaleX Scaling factor on the X axis + /// \param scaleY Scaling factor on the Y axis + /// + /// \return Reference to *this + /// + /// \see Translate, Rotate + /// + //////////////////////////////////////////////////////////// + Transform& Scale(float scaleX, float scaleY); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a scaling + /// + /// The center of scaling is provided for convenience as a second + /// argument, so that you can build scaling around arbitrary points + /// more easily (and efficiently) than the usual + /// Translate(-center).Scale(factors).Translate(center). + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Scale(2, 1, 8, 3).Rotate(45); + /// \endcode + /// + /// \param scaleX Scaling factor on X axis + /// \param scaleY Scaling factor on Y axis + /// \param centerX X coordinate of the center of scaling + /// \param centerY Y coordinate of the center of scaling + /// + /// \return Reference to *this + /// + /// \see Translate, Rotate + /// + //////////////////////////////////////////////////////////// + Transform& Scale(float scaleX, float scaleY, float centerX, float centerY); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a scaling + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Scale(sf::Vector2f(2, 1)).Rotate(45); + /// \endcode + /// + /// \param factors Scaling factors + /// + /// \return Reference to *this + /// + /// \see Translate, Rotate + /// + //////////////////////////////////////////////////////////// + Transform& Scale(const Vector2f& factors); + + //////////////////////////////////////////////////////////// + /// \brief Combine the current transform with a scaling + /// + /// The center of scaling is provided for convenience as a second + /// argument, so that you can build scaling around arbitrary points + /// more easily (and efficiently) than the usual + /// Translate(-center).Scale(factors).Translate(center). + /// + /// This function returns a reference to *this, so that calls + /// can be chained. + /// \code + /// sf::Transform transform; + /// transform.Scale(sf::Vector2f(2, 1), sf::Vector2f(8, 3)).Rotate(45); + /// \endcode + /// + /// \param factors Scaling factors + /// \param center Center of scaling + /// + /// \return Reference to *this + /// + /// \see Translate, Rotate + /// + //////////////////////////////////////////////////////////// + Transform& Scale(const Vector2f& factors, const Vector2f& center); + + //////////////////////////////////////////////////////////// + // Static member data + //////////////////////////////////////////////////////////// + static const Transform Identity; ///< The identity transform (does nothing) + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + float myMatrix[16]; /// 4x4 matrix defining the transformation +}; + +//////////////////////////////////////////////////////////// +/// \relates sf::Transform +/// \brief Overload of binary operator * to combine two transforms +/// +/// This call is equivalent to calling left.Combine(right). +/// +/// \param left Left operand (the first transform) +/// \param right Right operand (the second transform) +/// +/// \return New combined transform +/// +//////////////////////////////////////////////////////////// +SFML_API Transform operator *(const Transform& left, const Transform& right); + +//////////////////////////////////////////////////////////// +/// \relates sf::Transform +/// \brief Overload of binary operator *= to combine two transforms +/// +/// This call is equivalent to calling left = left.Combine(right). +/// +/// \param left Left operand (the first transform) +/// \param right Right operand (the second transform) +/// +/// \return The combined transform +/// +//////////////////////////////////////////////////////////// +SFML_API Transform& operator *=(Transform& left, const Transform& right); + +//////////////////////////////////////////////////////////// +/// \relates sf::Transform +/// \brief Overload of binary operator * to transform a point +/// +/// This call is equivalent to calling left.TransformPoint(right). +/// +/// \param left Left operand (the transform) +/// \param right Right operand (the point to transform) +/// +/// \return New transformed point +/// +//////////////////////////////////////////////////////////// +SFML_API Vector2f operator *(const Transform& left, const Vector2f& right); + +} // namespace sf + + +#endif // SFML_TRANSFORM_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::Transform +/// \ingroup graphics +/// +/// A sf::Transform specifies how to translate, rotate, scale, +/// shear, project, whatever things. In mathematical terms, it defines +/// how to transform a coordinate system into another. +/// +/// For example, if you apply a rotation transform to a sprite, the +/// result will be a rotated sprite. And anything that is transformed +/// by this rotation transform will be rotated the same way, according +/// to its initial position. +/// +/// Transforms are typically used for drawing. But they can also be +/// used for any computation that requires to transform points between +/// the local and global coordinate systems of an entity (like collision +/// detection). +/// +/// Example: +/// \code +/// // define a translation transform +/// sf::Transform translation; +/// translation.Translate(20, 50); +/// +/// // define a rotation transform +/// sf::Transform rotation; +/// rotation.Rotate(45); +/// +/// // combine them +/// sf::Transform transform = translation * rotation; +/// +/// // use the result to transform stuff... +/// sf::Vector2f point = transform.TransformPoint(10, 20); +/// sf::FloatRect rect = transform.TransformRect(sf::FloatRect(0, 0, 10, 100)); +/// \endcode +/// +/// \see sf::Transformable, sf::RenderStates +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Transformable.hpp b/include/SFML/Graphics/Transformable.hpp new file mode 100644 index 00000000..d20a16a2 --- /dev/null +++ b/include/SFML/Graphics/Transformable.hpp @@ -0,0 +1,414 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_TRANSFORMABLE_HPP +#define SFML_TRANSFORMABLE_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Decomposed transform defined by a position, a rotation and a scale +/// +//////////////////////////////////////////////////////////// +class SFML_API Transformable +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + Transformable(); + + //////////////////////////////////////////////////////////// + /// \brief Set the position of the object + /// + /// This function completely overwrites the previous position. + /// See Move to apply an offset based on the previous position instead. + /// The default position of a transformable object is (0, 0). + /// + /// \param x X coordinate of the new position + /// \param y Y coordinate of the new position + /// + /// \see Move, GetPosition + /// + //////////////////////////////////////////////////////////// + void SetPosition(float x, float y); + + //////////////////////////////////////////////////////////// + /// \brief Set the position of the object + /// + /// This function completely overwrites the previous position. + /// See Move to apply an offset based on the previous position instead. + /// The default position of a transformable object is (0, 0). + /// + /// \param position New position + /// + /// \see Move, GetPosition + /// + //////////////////////////////////////////////////////////// + void SetPosition(const Vector2f& position); + + //////////////////////////////////////////////////////////// + /// \brief Set the orientation of the object + /// + /// This function completely overwrites the previous rotation. + /// See Rotate to add an angle based on the previous rotation instead. + /// The default rotation of a transformable object is 0. + /// + /// \param angle New rotation, in degrees + /// + /// \see Rotate, GetRotation + /// + //////////////////////////////////////////////////////////// + void SetRotation(float angle); + + //////////////////////////////////////////////////////////// + /// \brief Set the scale factors of the object + /// + /// \a factorX and \a factorY must be strictly positive, + /// otherwise they are ignored. + /// This function completely overwrites the previous scale. + /// See Scale to add a factor based on the previous scale instead. + /// The default scale of a transformable object is (1, 1). + /// + /// \param factorX New horizontal scale factor + /// \param factorY New vertical scale factor + /// + /// \see Scale, GetScale + /// + //////////////////////////////////////////////////////////// + void SetScale(float factorX, float factorY); + + //////////////////////////////////////////////////////////// + /// \brief Set the scale factors of the object + /// + /// \a scale.x and \a scale.y must be strictly positive, + /// otherwise they are ignored. + /// This function completely overwrites the previous scale. + /// See Scale to add a factor based on the previous scale instead. + /// The default scale of a transformable object is (1, 1). + /// + /// \param factors New scale factors + /// + /// \see Scale, GetScale + /// + //////////////////////////////////////////////////////////// + void SetScale(const Vector2f& factors); + + //////////////////////////////////////////////////////////// + /// \brief Set the local origin of the object + /// + /// The origin of an object defines the center point for + /// all transformations (position, scale, rotation). + /// The coordinates of this point must be relative to the + /// top-left corner of the object, and ignore all + /// transformations (position, scale, rotation). + /// The default origin of a transformable object is (0, 0). + /// + /// \param x X coordinate of the new origin + /// \param y Y coordinate of the new origin + /// + /// \see GetOrigin + /// + //////////////////////////////////////////////////////////// + void SetOrigin(float x, float y); + + //////////////////////////////////////////////////////////// + /// \brief Set the local origin of the object + /// + /// The origin of an object defines the center point for + /// all transformations (position, scale, rotation). + /// The coordinates of this point must be relative to the + /// top-left corner of the object, and ignore all + /// transformations (position, scale, rotation). + /// The default origin of a transformable object is (0, 0). + /// + /// \param origin New origin + /// + /// \see GetOrigin + /// + //////////////////////////////////////////////////////////// + void SetOrigin(const Vector2f& origin); + + //////////////////////////////////////////////////////////// + /// \brief Get the position of the object + /// + /// \return Current position + /// + /// \see SetPosition + /// + //////////////////////////////////////////////////////////// + const Vector2f& GetPosition() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the orientation of the object + /// + /// The rotation is always in the range [0, 360]. + /// + /// \return Current rotation, in degrees + /// + /// \see SetRotation + /// + //////////////////////////////////////////////////////////// + float GetRotation() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the current scale of the object + /// + /// \return Current scale factors + /// + /// \see SetScale + /// + //////////////////////////////////////////////////////////// + const Vector2f& GetScale() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the local origin of the object + /// + /// \return Current origin + /// + /// \see SetOrigin + /// + //////////////////////////////////////////////////////////// + const Vector2f& GetOrigin() const; + + //////////////////////////////////////////////////////////// + /// \brief Move the object by a given offset + /// + /// This function adds to the current position of the object, + /// unlike SetPosition which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// sf::Vector2f pos = object.GetPosition(); + /// object.SetPosition(pos.x + offsetX, pos.y + offsetY); + /// \endcode + /// + /// \param offsetX X offset + /// \param offsetY Y offset + /// + /// \see SetPosition + /// + //////////////////////////////////////////////////////////// + void Move(float offsetX, float offsetY); + + //////////////////////////////////////////////////////////// + /// \brief Move the object by a given offset + /// + /// This function adds to the current position of the object, + /// unlike SetPosition which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// object.SetPosition(object.GetPosition() + offset); + /// \endcode + /// + /// \param offset Offset + /// + /// \see SetPosition + /// + //////////////////////////////////////////////////////////// + void Move(const Vector2f& offset); + + //////////////////////////////////////////////////////////// + /// \brief Rotate the object + /// + /// This function adds to the current rotation of the object, + /// unlike SetRotation which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// object.SetRotation(object.GetRotation() + angle); + /// \endcode + /// + /// \param angle Angle of rotation, in degrees + /// + //////////////////////////////////////////////////////////// + void Rotate(float angle); + + //////////////////////////////////////////////////////////// + /// \brief Scale the object + /// + /// This function multiplies the current scale of the object, + /// unlike SetScale which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// sf::Vector2f scale = object.GetScale(); + /// object.SetScale(scale.x * factorX, scale.y * factorY); + /// \endcode + /// + /// \param factorX Horizontal scale factor + /// \param factorY Vertical scale factor + /// + /// \see SetScale + /// + //////////////////////////////////////////////////////////// + void Scale(float factorX, float factorY); + + //////////////////////////////////////////////////////////// + /// \brief Scale the object + /// + /// This function multiplies the current scale of the object, + /// unlike SetScale which overwrites it. + /// Thus, it is equivalent to the following code: + /// \code + /// sf::Vector2f scale = object.GetScale(); + /// object.SetScale(scale.x * factor.x, scale.y * factor.y); + /// \endcode + /// + /// \param factor Scale factors + /// + /// \see SetScale + /// + //////////////////////////////////////////////////////////// + void Scale(const Vector2f& factor); + + //////////////////////////////////////////////////////////// + /// \brief Get the combined transform of the object + /// + /// \return Transform combining the position/rotation/scale/origin of the object + /// + /// \see GetInverseTransform + /// + //////////////////////////////////////////////////////////// + const Transform& GetTransform() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the inverse of the combined transform of the object + /// + /// \return Inversed of the combined transformations applied to the object + /// + /// \see GetTransform + /// + //////////////////////////////////////////////////////////// + const Transform& GetInverseTransform() const; + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Vector2f myOrigin; ///< Origin of translation/rotation/scaling of the object + Vector2f myPosition; ///< Position of the object in the 2D world + float myRotation; ///< Orientation of the object, in degrees + Vector2f myScale; ///< Scale of the object + mutable Transform myTransform; ///< Combined transformation of the object + mutable bool myTransformNeedUpdate; ///< Does the transform need to be recomputed? + mutable Transform myInverseTransform; ///< Combined transformation of the object + mutable bool myInverseTransformNeedUpdate; ///< Does the transform need to be recomputed? +}; + +} // namespace sf + + +#endif // SFML_TRANSFORMABLE_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::Transformable +/// \ingroup graphics +/// +/// This class is provided for convenience, on top of sf::Transform. +/// +/// sf::Transform, as a low-level class, offers a great level of +/// flexibility but it is not always convenient to manage. Indeed, +/// one can easily combine any kind of operation, such as a translation +/// followed by a rotation followed by a scaling, but once the result +/// transform is built, there's no way to go backward and, let's say, +/// change only the rotation without modifying the translation and scaling. +/// The entire transform must be recomputed, which means that you +/// need to retrieve the initial translation and scale factors as +/// well, and combine them the same way you did before updating the +/// rotation. This is a tedious operation, and it requires to store +/// all the individual components of the final transform. +/// +/// That's exactly what sf::Transformable was written for: it hides +/// these variables and the composed transform behind an easy to use +/// interface. You can set or get any of the individual components +/// without worrying about the others. It also provides the composed +/// transform (as a sf::Transform), and keeps it up-to-date. +/// +/// In addition to the position, rotation and scale, sf::Transformable +/// provides an "origin" component, which represents the local origin +/// of the three other components. Let's take an example with a 10x10 +/// pixels sprite. By default, the sprite is positionned/rotated/scaled +/// relatively to its top-left corner, because it is the local point +/// (0, 0). But if we change the origin to be (5, 5), the sprite will +/// be positionned/rotated/scaled around its center instead. And if +/// we set the origin to (10, 10), it will be transformed around its +/// bottom-right corner. +/// +/// To keep the sf::Transformable class simple, there's only one +/// origin for all the components. You cannot position the sprite +/// relatively to its top-left corner while rotating it around its +/// center, for example. To do such things, use sf::Transform directly. +/// +/// sf::Transformable can be used as a base class. It is often +/// combined with sf::Drawable -- that's what SFML's sprites, +/// texts and shapes do. +/// \code +/// class MyEntity : public sf::Transformable, public sf::Drawable +/// { +/// virtual void Draw(sf::RenderTarget& target, sf::RenderStates states) const +/// { +/// states.Transform *= GetTransform(); +/// target.Draw(..., states); +/// } +/// }; +/// +/// MyEntity entity; +/// entity.SetPosition(10, 20); +/// entity.SetRotation(45); +/// window.Draw(entity); +/// \endcode +/// +/// It can also be used as a member, if you don't want to use +/// its API directly (because you don't need all its functions, +/// or you have different naming conventions for example). +/// \code +/// class MyEntity +/// { +/// public : +/// void setPosition(const MyVector& v) +/// { +/// m_transform.SetPosition(v.x(), v.y()); +/// } +/// +/// void draw(sf::RenderTarget& target) const +/// { +/// target.Draw(..., m_transform.GetTransform()); +/// } +/// +/// private : +/// sf::Transformable m_transform; +/// }; +/// \endcode +/// +/// \see sf::Transform +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Vertex.hpp b/include/SFML/Graphics/Vertex.hpp new file mode 100644 index 00000000..21ff96ea --- /dev/null +++ b/include/SFML/Graphics/Vertex.hpp @@ -0,0 +1,143 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_VERTEX_HPP +#define SFML_VERTEX_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Define a point with color and texture coordinates +/// +//////////////////////////////////////////////////////////// +class SFML_API Vertex +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + Vertex(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex from its position + /// + /// The vertex color is white and texture coordinates are (0, 0). + /// + /// \param position Vertex position + /// + //////////////////////////////////////////////////////////// + Vertex(const Vector2f& position); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex from its position and color + /// + /// The texture coordinates are (0, 0). + /// + /// \param position Vertex position + /// \param color Vertex color + /// + //////////////////////////////////////////////////////////// + Vertex(const Vector2f& position, const Color& color); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex from its position and texture coordinates + /// + /// The vertex color is white. + /// + /// \param position Vertex position + /// \param texCoords Vertex texture coordinates + /// + //////////////////////////////////////////////////////////// + Vertex(const Vector2f& position, const Vector2i& texCoords); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex from its position, color and texture coordinates + /// + /// \param position Vertex position + /// \param color Vertex color + /// \param texCoords Vertex texture coordinates + /// + //////////////////////////////////////////////////////////// + Vertex(const Vector2f& position, const Color& color, const Vector2i& texCoords); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Vector2f Position; ///< 2D position of the vertex + sf::Color Color; ///< Color of the vertex + Vector2i TexCoords; ///< Coordinates of the texture's pixel to map to the vertex +}; + +} // namespace sf + + +#endif // SFML_VERTEX_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::Vertex +/// \ingroup graphics +/// +/// A vertex is an improved point. It has a position and other +/// extra attributes that will be used for drawing: in SFML, +/// vertices also have a color and a pair of texture coordinates. +/// +/// The vertex is the building block of drawing. Everything which +/// is visible on screen is made of vertices. They are grouped +/// as 2D primitives (triangles, quads, ...), and these primitives +/// are grouped to create even more complex 2D entities such as +/// sprites, texts, etc. +/// +/// If you use the graphical entities of SFML (sprite, text, shape) +/// you won't have to deal with vertices directly. But if you want +/// to define your own 2D entities, such as tiled maps or particle +/// systems, using vertices will allow you to get maximum performances. +/// +/// Example: +/// \code +/// // define a 100x100 square, red, with a 10x10 texture mapped on it +/// sf::Vertex vertices[] = +/// { +/// sf::Vertex(sf::Vector2f( 0, 0), sf::Color::Red, sf::Vector2i( 0, 0)), +/// sf::Vertex(sf::Vector2f( 0, 100), sf::Color::Red, sf::Vector2i( 0, 10)), +/// sf::Vertex(sf::Vector2f(100, 100), sf::Color::Red, sf::Vector2i(10, 10)), +/// sf::Vertex(sf::Vector2f(100, 0), sf::Color::Red, sf::Vector2i(10, 0)) +/// }; +/// +/// // draw it +/// window.Draw(vertices, 4, sf::Quads); +/// \endcode +/// +/// \see sf::VertexArray +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/VertexArray.hpp b/include/SFML/Graphics/VertexArray.hpp new file mode 100644 index 00000000..5c847054 --- /dev/null +++ b/include/SFML/Graphics/VertexArray.hpp @@ -0,0 +1,220 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_VERTEXARRAY_HPP +#define SFML_VERTEXARRAY_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Define a set of one or more 2D primitives +/// +//////////////////////////////////////////////////////////// +class SFML_API VertexArray : public Drawable +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty vertex array. + /// + //////////////////////////////////////////////////////////// + VertexArray(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vertex array with a type and an initial number of vertices + /// + /// \param type Type of primitives + /// \param verticesCount Initial number of vertices in the array + /// + //////////////////////////////////////////////////////////// + VertexArray(PrimitiveType type, unsigned int verticesCount = 0); + + //////////////////////////////////////////////////////////// + /// \brief Return the vertices count + /// + /// \return Number of vertices in the array + /// + //////////////////////////////////////////////////////////// + unsigned int GetVerticesCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a read-write access to a vertex by its index + /// + /// This function doesn't check \a index, it must be in range + /// [0, GetVerticesCount() - 1]. + /// + /// \param index Index of the vertex to get + /// + /// \return Reference to the index-th vertex + /// + /// \see GetVerticesCount + /// + //////////////////////////////////////////////////////////// + Vertex& operator [](unsigned int index); + + //////////////////////////////////////////////////////////// + /// \brief Get a read-only access to a vertex by its index + /// + /// This function doesn't check \a index, it must be in range + /// [0, GetVerticesCount() - 1]. + /// + /// \param index Index of the vertex to get + /// + /// \return Const reference to the index-th vertex + /// + /// \see GetVerticesCount + /// + //////////////////////////////////////////////////////////// + const Vertex& operator [](unsigned int index) const; + + //////////////////////////////////////////////////////////// + /// \brief Clear the vertex array + /// + /// This function removes all the vertices from the array. + /// It doesn't deallocate the corresponding memory, so that + /// adding new vertices after clearing doesn't involve + /// reallocating all the memory. + /// + //////////////////////////////////////////////////////////// + void Clear(); + + //////////////////////////////////////////////////////////// + /// \brief Resize the vertex array + /// + /// If \a count is greater than the current size, the previous + /// vertices are kept and new (default-constructed) vertices are + /// added. + /// If \a count is less than the current size, existing vertices + /// are removed from the array. + /// + /// \param verticesCount New size of the array (number of vertices) + /// + //////////////////////////////////////////////////////////// + void Resize(unsigned int verticesCount); + + //////////////////////////////////////////////////////////// + /// \brief Add a vertex to the array + /// + /// \param vertex Vertex to add + /// + //////////////////////////////////////////////////////////// + void Append(const Vertex& vertex); + + //////////////////////////////////////////////////////////// + /// \brief Set the type of primitives to draw + /// + /// This function defines how the vertices must be interpreted + /// when it's time to draw them: + /// \li As points + /// \li As lines + /// \li As triangles + /// \li As quads + /// The default primitive type is sf::Points. + /// + /// \param type Type of primitive + /// + //////////////////////////////////////////////////////////// + void SetPrimitiveType(PrimitiveType type); + + //////////////////////////////////////////////////////////// + /// \brief Get the type of primitives drawn by the vertex array + /// + /// \return Primitive type + /// + //////////////////////////////////////////////////////////// + PrimitiveType GetPrimitiveType() const; + + //////////////////////////////////////////////////////////// + /// \brief Compute the bounding rectangle of the vertex array + /// + /// This function returns the axis-aligned rectangle that + /// contains all the vertices of the array. + /// + /// \return Bounding rectangle of the vertex array + /// + //////////////////////////////////////////////////////////// + FloatRect GetBounds() const; + +private : + + //////////////////////////////////////////////////////////// + /// \brief Draw the vertex array to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states + /// + //////////////////////////////////////////////////////////// + virtual void Draw(RenderTarget& target, RenderStates states) const; + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::vector myVertices; ///< Vertices contained in the array + PrimitiveType myPrimitiveType; ///< Type of primitives to draw +}; + +} // namespace sf + + +#endif // SFML_VERTEXARRAY_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::VertexArray +/// \ingroup graphics +/// +/// sf::VertexArray is a very simple wrapper around a dynamic +/// array of vertices and a primitives type. +/// +/// It inherits sf::Drawable, but unlike other drawables it +/// is not transformable. +/// +/// Example: +/// \code +/// sf::VertexArray lines(sf::LinesStrip, 4); +/// lines[0].Position = sf::Vector2f(10, 0); +/// lines[1].Position = sf::Vector2f(20, 0); +/// lines[2].Position = sf::Vector2f(30, 5); +/// lines[3].Position = sf::Vector2f(40, 2); +/// +/// window.Draw(lines); +/// \endcode +/// +/// \see sf::Vertex +/// +//////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/View.hpp b/include/SFML/Graphics/View.hpp index 14a0b947..dee4eb49 100644 --- a/include/SFML/Graphics/View.hpp +++ b/include/SFML/Graphics/View.hpp @@ -30,7 +30,7 @@ //////////////////////////////////////////////////////////// #include #include -#include +#include #include @@ -241,42 +241,42 @@ public : void Zoom(float factor); //////////////////////////////////////////////////////////// - /// \brief Get the projection matrix of the view + /// \brief Get the projection transform of the view /// - /// This functions is meant for internal use only. + /// This function is meant for internal use only. /// - /// \return Projection matrix defining the view + /// \return Projection transform defining the view /// - /// \see GetInverseMatrix + /// \see GetInverseTransform /// //////////////////////////////////////////////////////////// - const Matrix3& GetMatrix() const; + const Transform& GetTransform() const; //////////////////////////////////////////////////////////// - /// \brief Get the inverse projection matrix of the view + /// \brief Get the inverse projection transform of the view /// - /// This functions is meant for internal use only. + /// This function is meant for internal use only. /// - /// \return Inverse of the projection matrix defining the view + /// \return Inverse of the projection transform defining the view /// - /// \see GetMatrix + /// \see GetTransform /// //////////////////////////////////////////////////////////// - const Matrix3& GetInverseMatrix() const; + const Transform& GetInverseTransform() const; private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Vector2f myCenter; ///< Center of the view, in scene coordinates - Vector2f mySize; ///< Size of the view, in scene coordinates - float myRotation; ///< Angle of rotation of the view rectangle, in degrees - FloatRect myViewport; ///< Viewport rectangle, expressed as a factor of the render-target's size - mutable Matrix3 myMatrix; ///< Precomputed projection matrix corresponding to the view - mutable Matrix3 myInverseMatrix; ///< Precomputed inverse projection matrix corresponding to the view - mutable bool myMatrixUpdated; ///< Internal state telling if the matrix needs to be updated - mutable bool myInvMatrixUpdated; ///< Internal state telling if the matrix needs to be updated + Vector2f myCenter; ///< Center of the view, in scene coordinates + Vector2f mySize; ///< Size of the view, in scene coordinates + float myRotation; ///< Angle of rotation of the view rectangle, in degrees + FloatRect myViewport; ///< Viewport rectangle, expressed as a factor of the render-target's size + mutable Transform myTransform; ///< Precomputed projection transform corresponding to the view + mutable Transform myInverseTransform; ///< Precomputed inverse projection transform corresponding to the view + mutable bool myTransformUpdated; ///< Internal state telling if the transform needs to be updated + mutable bool myInvTransformUpdated; ///< Internal state telling if the inverse transform needs to be updated }; } // namespace sf diff --git a/include/SFML/System/Utf.hpp b/include/SFML/System/Utf.hpp index 5fe481b6..f322f26d 100644 --- a/include/SFML/System/Utf.hpp +++ b/include/SFML/System/Utf.hpp @@ -149,7 +149,6 @@ public : /// \param begin Iterator pointing to the beginning of the input sequence /// \param end Iterator pointing to the end of the input sequence /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion /// /// \return Iterator to the end of the output sequence which has been written /// @@ -357,7 +356,6 @@ public : /// \param begin Iterator pointing to the beginning of the input sequence /// \param end Iterator pointing to the end of the input sequence /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion /// /// \return Iterator to the end of the output sequence which has been written /// @@ -566,7 +564,6 @@ public : /// \param begin Iterator pointing to the beginning of the input sequence /// \param end Iterator pointing to the end of the input sequence /// \param output Iterator pointing to the beginning of the output sequence - /// \param locale Locale to use for conversion /// /// \return Iterator to the end of the output sequence which has been written /// diff --git a/include/SFML/System/Vector3.hpp b/include/SFML/System/Vector3.hpp index 64042890..dd438c59 100644 --- a/include/SFML/System/Vector3.hpp +++ b/include/SFML/System/Vector3.hpp @@ -82,7 +82,7 @@ public : /// \relates Vector3 /// \brief Overload of unary operator - /// -/// \param right Vector to negate +/// \param left Vector to negate /// /// \return Memberwise opposite of the vector /// diff --git a/include/SFML/Window/Context.hpp b/include/SFML/Window/Context.hpp index def5acf9..48b21491 100644 --- a/include/SFML/Window/Context.hpp +++ b/include/SFML/Window/Context.hpp @@ -41,6 +41,8 @@ namespace priv class GlContext; } +typedef void* ContextId; + //////////////////////////////////////////////////////////// /// \brief Class holding a valid drawing context /// @@ -75,6 +77,18 @@ public : //////////////////////////////////////////////////////////// bool SetActive(bool active); + //////////////////////////////////////////////////////////// + /// \brief Return the identifier of the current active context + /// + /// The returned id has no special meaning, it should only be + /// used as a key to map external stuff to internal contexts. + /// This function returns 0 if no context is active. + /// + /// \return Identifier of the current context + /// + //////////////////////////////////////////////////////////// + static ContextId GetCurrentContextId(); + public : //////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/CMakeLists.txt b/src/SFML/Graphics/CMakeLists.txt index 09daa851..1a87fd40 100644 --- a/src/SFML/Graphics/CMakeLists.txt +++ b/src/SFML/Graphics/CMakeLists.txt @@ -5,9 +5,9 @@ set(SRCROOT ${PROJECT_SOURCE_DIR}/src/SFML/Graphics) # all source files set(SRC ${SRCROOT}/Arial.hpp + ${INCROOT}/BlendMode.hpp ${SRCROOT}/Color.cpp ${INCROOT}/Color.hpp - ${SRCROOT}/Drawable.cpp ${INCROOT}/Drawable.hpp ${SRCROOT}/Font.cpp ${INCROOT}/Font.hpp @@ -18,13 +18,11 @@ set(SRC ${INCROOT}/Image.hpp ${SRCROOT}/ImageLoader.cpp ${SRCROOT}/ImageLoader.hpp - ${SRCROOT}/Matrix3.cpp - ${INCROOT}/Matrix3.hpp - ${INCROOT}/Matrix3.inl + ${INCROOT}/PrimitiveType.hpp ${INCROOT}/Rect.hpp ${INCROOT}/Rect.inl - ${SRCROOT}/Renderer.cpp - ${INCROOT}/Renderer.hpp + ${SRCROOT}/RenderStates.cpp + ${INCROOT}/RenderStates.hpp ${SRCROOT}/RenderTexture.cpp ${INCROOT}/RenderTexture.hpp ${SRCROOT}/RenderTextureImpl.cpp @@ -41,14 +39,30 @@ set(SRC ${INCROOT}/Shader.hpp ${SRCROOT}/Shape.cpp ${INCROOT}/Shape.hpp + ${SRCROOT}/CircleShape.cpp + ${INCROOT}/CircleShape.hpp + ${SRCROOT}/RectangleShape.cpp + ${INCROOT}/RectangleShape.hpp + ${SRCROOT}/StarShape.cpp + ${INCROOT}/StarShape.hpp + ${SRCROOT}/ConvexShape.cpp + ${INCROOT}/ConvexShape.hpp ${SRCROOT}/Sprite.cpp ${INCROOT}/Sprite.hpp ${SRCROOT}/Text.cpp ${INCROOT}/Text.hpp ${SRCROOT}/Texture.cpp ${INCROOT}/Texture.hpp + ${SRCROOT}/Transform.cpp + ${INCROOT}/Transform.hpp + ${SRCROOT}/Transformable.cpp + ${INCROOT}/Transformable.hpp ${SRCROOT}/View.cpp ${INCROOT}/View.hpp + ${SRCROOT}/Vertex.cpp + ${INCROOT}/Vertex.hpp + ${SRCROOT}/VertexArray.cpp + ${INCROOT}/VertexArray.hpp ${SRCROOT}/stb_image/stb_image.h ${SRCROOT}/stb_image/stb_image_write.h ) diff --git a/src/SFML/Graphics/CircleShape.cpp b/src/SFML/Graphics/CircleShape.cpp new file mode 100644 index 00000000..86a6a3cb --- /dev/null +++ b/src/SFML/Graphics/CircleShape.cpp @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +CircleShape::CircleShape() : +myRadius(10) +{ +} + + +//////////////////////////////////////////////////////////// +void CircleShape::SetRadius(float radius) +{ + myRadius = radius; + Update(); +} + + +//////////////////////////////////////////////////////////// +float CircleShape::GetRadius() const +{ + return myRadius; +} + + +//////////////////////////////////////////////////////////// +unsigned int CircleShape::GetOutlinePointsCount() const +{ + return 30; +} + + +//////////////////////////////////////////////////////////// +Vector2f CircleShape::GetOutlinePoint(unsigned int index) const +{ + float angle = index * 2 * 3.141592654f / GetOutlinePointsCount(); + float x = std::cos(angle) * myRadius; + float y = std::sin(angle) * myRadius; + + return Vector2f(myRadius + x, myRadius + y); +} + +} // namespace sf diff --git a/src/SFML/Graphics/Color.cpp b/src/SFML/Graphics/Color.cpp index 0c6fccf0..bef16d5e 100644 --- a/src/SFML/Graphics/Color.cpp +++ b/src/SFML/Graphics/Color.cpp @@ -42,6 +42,7 @@ const Color Color::Blue(0, 0, 255); const Color Color::Yellow(255, 255, 0); const Color Color::Magenta(255, 0, 255); const Color Color::Cyan(0, 255, 255); +const Color Color::Transparent(0, 0, 0, 0); //////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/ConvexShape.cpp b/src/SFML/Graphics/ConvexShape.cpp new file mode 100644 index 00000000..222d5367 --- /dev/null +++ b/src/SFML/Graphics/ConvexShape.cpp @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +ConvexShape::ConvexShape() +{ + // Let's define a triangle by default... just so that it's not empty + SetPointsCount(3); + SetPoint(0, Vector2f(5, 0)); + SetPoint(1, Vector2f(0, 10)); + SetPoint(2, Vector2f(10, 10)); +} + + +//////////////////////////////////////////////////////////// +void ConvexShape::SetPointsCount(unsigned int count) +{ + myPoints.resize(count); +} + + +//////////////////////////////////////////////////////////// +unsigned int ConvexShape::GetPointsCount() const +{ + return myPoints.size(); +} + + +//////////////////////////////////////////////////////////// +void ConvexShape::SetPoint(unsigned int index, const Vector2f& point) +{ + myPoints[index] = point; +} + + +//////////////////////////////////////////////////////////// +Vector2f ConvexShape::GetPoint(unsigned int index) const +{ + return myPoints[index]; +} + + +//////////////////////////////////////////////////////////// +unsigned int ConvexShape::GetOutlinePointsCount() const +{ + return GetPointsCount(); +} + + +//////////////////////////////////////////////////////////// +Vector2f ConvexShape::GetOutlinePoint(unsigned int index) const +{ + return GetPoint(index); +} + +} // namespace sf diff --git a/src/SFML/Graphics/Drawable.cpp b/src/SFML/Graphics/Drawable.cpp deleted file mode 100644 index 5dd4336f..00000000 --- a/src/SFML/Graphics/Drawable.cpp +++ /dev/null @@ -1,314 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - - -namespace sf -{ -//////////////////////////////////////////////////////////// -Drawable::~Drawable() -{ - // Nothing to do -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetPosition(float x, float y) -{ - SetX(x); - SetY(y); -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetPosition(const Vector2f& position) -{ - SetX(position.x); - SetY(position.y); -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetX(float x) -{ - myPosition.x = x; - - myMatrixUpdated = false; - myInvMatrixUpdated = false; -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetY(float y) -{ - myPosition.y = y; - - myMatrixUpdated = false; - myInvMatrixUpdated = false; -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetScale(float factorX, float factorY) -{ - SetScaleX(factorX); - SetScaleY(factorY); -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetScale(const Vector2f& factors) -{ - SetScaleX(factors.x); - SetScaleY(factors.y); -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetScaleX(float factor) -{ - if (factor > 0) - { - myScale.x = factor; - - myMatrixUpdated = false; - myInvMatrixUpdated = false; - } -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetScaleY(float factor) -{ - if (factor > 0) - { - myScale.y = factor; - - myMatrixUpdated = false; - myInvMatrixUpdated = false; - } -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetOrigin(float x, float y) -{ - myOrigin.x = x; - myOrigin.y = y; - - myMatrixUpdated = false; - myInvMatrixUpdated = false; -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetOrigin(const Vector2f& origin) -{ - SetOrigin(origin.x, origin.y); -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetRotation(float angle) -{ - myRotation = static_cast(std::fmod(angle, 360)); - if (myRotation < 0) - myRotation += 360.f; - - myMatrixUpdated = false; - myInvMatrixUpdated = false; -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetColor(const Color& color) -{ - myColor = color; -} - - -//////////////////////////////////////////////////////////// -void Drawable::SetBlendMode(Blend::Mode mode) -{ - myBlendMode = mode; -} - - -//////////////////////////////////////////////////////////// -const Vector2f& Drawable::GetPosition() const -{ - return myPosition; -} - - -//////////////////////////////////////////////////////////// -const Vector2f& Drawable::GetScale() const -{ - return myScale; -} - - -//////////////////////////////////////////////////////////// -const Vector2f& Drawable::GetOrigin() const -{ - return myOrigin; -} - - -//////////////////////////////////////////////////////////// -float Drawable::GetRotation() const -{ - return myRotation; -} - - -//////////////////////////////////////////////////////////// -const Color& Drawable::GetColor() const -{ - return myColor; -} - - -//////////////////////////////////////////////////////////// -Blend::Mode Drawable::GetBlendMode() const -{ - return myBlendMode; -} - - -//////////////////////////////////////////////////////////// -void Drawable::Move(float offsetX, float offsetY) -{ - SetPosition(myPosition.x + offsetX, myPosition.y + offsetY); -} - - -//////////////////////////////////////////////////////////// -void Drawable::Move(const Vector2f& offset) -{ - SetPosition(myPosition + offset); -} - - -//////////////////////////////////////////////////////////// -void Drawable::Scale(float factorX, float factorY) -{ - SetScale(myScale.x * factorX, myScale.y * factorY); -} - - -//////////////////////////////////////////////////////////// -void Drawable::Scale(const Vector2f& factor) -{ - SetScale(myScale.x * factor.x, myScale.y * factor.y); -} - - -//////////////////////////////////////////////////////////// -void Drawable::Rotate(float angle) -{ - SetRotation(myRotation + angle); -} - - -//////////////////////////////////////////////////////////// -Vector2f Drawable::TransformToLocal(const Vector2f& point) const -{ - return GetInverseMatrix().Transform(point); -} - - -//////////////////////////////////////////////////////////// -Vector2f Drawable::TransformToGlobal(const Vector2f& point) const -{ - return GetMatrix().Transform(point); -} - - -//////////////////////////////////////////////////////////// -Drawable::Drawable() : -myPosition (0, 0), -myScale (1, 1), -myOrigin (0, 0), -myRotation (0), -myColor (255, 255, 255, 255), -myBlendMode (Blend::Alpha), -myMatrixUpdated (false), -myInvMatrixUpdated(false) -{ -} - - -//////////////////////////////////////////////////////////// -const Matrix3& Drawable::GetMatrix() const -{ - // First recompute it if needed - if (!myMatrixUpdated) - { - myMatrix = Matrix3::Transformation(myOrigin, myPosition, myRotation, myScale); - myMatrixUpdated = true; - } - - return myMatrix; -} - - -//////////////////////////////////////////////////////////// -const Matrix3& Drawable::GetInverseMatrix() const -{ - // First recompute it if needed - if (!myInvMatrixUpdated) - { - myInvMatrix = GetMatrix().GetInverse(); - myInvMatrixUpdated = true; - } - - return myInvMatrix; -} - - -//////////////////////////////////////////////////////////// -void Drawable::Draw(RenderTarget& target, Renderer& renderer) const -{ - // Set the current model-view matrix - renderer.ApplyModelView(GetMatrix()); - - // Set the current global color - renderer.ApplyColor(myColor); - - // Set the current alpha-blending mode - renderer.SetBlendMode(myBlendMode); - - // Let the derived class render the object geometry - Render(target, renderer); -} - -} // namespace sf diff --git a/src/SFML/Graphics/Font.cpp b/src/SFML/Graphics/Font.cpp index 5bc51c83..8e8ffc72 100644 --- a/src/SFML/Graphics/Font.cpp +++ b/src/SFML/Graphics/Font.cpp @@ -452,7 +452,7 @@ Glyph Font::LoadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c Page& page = myPages[characterSize]; // Find a good position for the new glyph into the texture - glyph.SubRect = FindGlyphRect(page, width + 2 * padding, height + 2 * padding); + glyph.TextureRect = FindGlyphRect(page, width + 2 * padding, height + 2 * padding); // Compute the glyph's bounding box glyph.Bounds.Left = bitmapGlyph->left - padding; @@ -493,10 +493,10 @@ Glyph Font::LoadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c } // Write the pixels to the texture - unsigned int x = glyph.SubRect.Left + padding; - unsigned int y = glyph.SubRect.Top + padding; - unsigned int width = glyph.SubRect.Width - 2 * padding; - unsigned int height = glyph.SubRect.Height - 2 * padding; + unsigned int x = glyph.TextureRect.Left + padding; + unsigned int y = glyph.TextureRect.Top + padding; + unsigned int width = glyph.TextureRect.Width - 2 * padding; + unsigned int height = glyph.TextureRect.Height - 2 * padding; page.Texture.Update(&myPixelBuffer[0], width, height, x, y); } diff --git a/src/SFML/Graphics/RectangleShape.cpp b/src/SFML/Graphics/RectangleShape.cpp new file mode 100644 index 00000000..ba8d0ebf --- /dev/null +++ b/src/SFML/Graphics/RectangleShape.cpp @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +RectangleShape::RectangleShape() : +mySize(10, 10) +{ +} + + +//////////////////////////////////////////////////////////// +void RectangleShape::SetSize(const Vector2f& size) +{ + mySize = size; + Update(); +} + + +//////////////////////////////////////////////////////////// +const Vector2f& RectangleShape::GetSize() const +{ + return mySize; +} + + +//////////////////////////////////////////////////////////// +unsigned int RectangleShape::GetOutlinePointsCount() const +{ + return 4; +} + + +//////////////////////////////////////////////////////////// +Vector2f RectangleShape::GetOutlinePoint(unsigned int index) const +{ + switch (index) + { + default: + case 0: return Vector2f(0, 0); + case 1: return Vector2f(mySize.x, 0); + case 2: return Vector2f(mySize.x, mySize.y); + case 3: return Vector2f(0, mySize.y); + } +} + +} // namespace sf diff --git a/src/SFML/Graphics/RenderStates.cpp b/src/SFML/Graphics/RenderStates.cpp new file mode 100644 index 00000000..83794db4 --- /dev/null +++ b/src/SFML/Graphics/RenderStates.cpp @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +const RenderStates RenderStates::Default; + + +//////////////////////////////////////////////////////////// +RenderStates::RenderStates() : +BlendMode(BlendAlpha), +Transform(), +Texture (NULL), +Shader (NULL) +{ +} + + +//////////////////////////////////////////////////////////// +RenderStates::RenderStates(const sf::Transform& transform) : +BlendMode(BlendAlpha), +Transform(transform), +Texture (NULL), +Shader (NULL) +{ +} + + +//////////////////////////////////////////////////////////// +RenderStates::RenderStates(sf::BlendMode blendMode) : +BlendMode(blendMode), +Transform(), +Texture (NULL), +Shader (NULL) +{ +} + + +//////////////////////////////////////////////////////////// +RenderStates::RenderStates(const sf::Texture* texture) : +BlendMode(BlendAlpha), +Transform(), +Texture (texture), +Shader (NULL) +{ +} + + +//////////////////////////////////////////////////////////// +RenderStates::RenderStates(const sf::Shader* shader) : +BlendMode(BlendAlpha), +Transform(), +Texture (NULL), +Shader (shader) +{ +} + + +//////////////////////////////////////////////////////////// +RenderStates::RenderStates(sf::BlendMode blendMode, const sf::Transform& transform, + const sf::Texture* texture, const sf::Shader* shader) : +BlendMode(blendMode), +Transform(transform), +Texture (texture), +Shader (shader) +{ +} + +} // namespace sf diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index 60d3b58e..4d8d593f 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -27,96 +27,37 @@ //////////////////////////////////////////////////////////// #include #include +#include +#include +#include +#include #include -#ifdef _MSC_VER - #pragma warning(disable : 4355) // "'this' : used in base member initializer list" -#endif - namespace sf { //////////////////////////////////////////////////////////// RenderTarget::RenderTarget() : -myRenderer (*this), -myStatesSaved (false), -myViewHasChanged(false) +myDefaultView(), +myView (), +myViewChanged(false) { - } //////////////////////////////////////////////////////////// RenderTarget::~RenderTarget() { - // Nothing to do } //////////////////////////////////////////////////////////// void RenderTarget::Clear(const Color& color) -{ - if (Activate(true)) - myRenderer.Clear(color); -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::Draw(const Drawable& object) { if (Activate(true)) { - // Update the projection matrix and viewport if the current view has changed - // Note: we do the changes here and not directly in SetView in order to gather - // rendering commands, which is safer in multithreaded environments - if (myViewHasChanged) - { - myRenderer.SetProjection(myCurrentView.GetMatrix()); - myRenderer.SetViewport(GetViewport(myCurrentView)); - myViewHasChanged = false; - } - - // Save the current render states - myRenderer.PushStates(); - - // Setup the shader - myRenderer.SetShader(NULL); - - // Let the object draw itself - object.Draw(*this, myRenderer); - - // Restore the previous render states - myRenderer.PopStates(); - } -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::Draw(const Drawable& object, const Shader& shader) -{ - if (Activate(true)) - { - // Update the projection matrix and viewport if the current view has changed - // Note: we do the changes here and not directly in SetView in order to gather - // rendering commands, which is safer in multithreaded environments - if (myViewHasChanged) - { - myRenderer.SetProjection(myCurrentView.GetMatrix()); - myRenderer.SetViewport(GetViewport(myCurrentView)); - myViewHasChanged = false; - } - - // Save the current render states - myRenderer.PushStates(); - - // Setup the shader - myRenderer.SetShader(&shader); - - // Let the object draw itself - object.Draw(*this, myRenderer); - - // Restore the previous render states - myRenderer.PopStates(); + GLCheck(glClearColor(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f)); + GLCheck(glClear(GL_COLOR_BUFFER_BIT)); } } @@ -124,16 +65,15 @@ void RenderTarget::Draw(const Drawable& object, const Shader& shader) //////////////////////////////////////////////////////////// void RenderTarget::SetView(const View& view) { - // Save it for later use - myCurrentView = view; - myViewHasChanged = true; + myView = view; + myViewChanged = true; } //////////////////////////////////////////////////////////// const View& RenderTarget::GetView() const { - return myCurrentView; + return myView; } @@ -175,35 +115,159 @@ Vector2f RenderTarget::ConvertCoords(unsigned int x, unsigned int y, const View& coords.y = 1.f - 2.f * (static_cast(y) - viewport.Top) / viewport.Height; // Then transform by the inverse of the view matrix - return view.GetInverseMatrix().Transform(coords); + return view.GetInverseTransform().TransformPoint(coords); } //////////////////////////////////////////////////////////// -void RenderTarget::SaveGLStates() +void RenderTarget::Draw(const Drawable& drawable, const RenderStates& states) { + drawable.Draw(*this, states); +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::Draw(const Vertex* vertices, unsigned int verticesCount, + PrimitiveType type, const RenderStates& states) +{ + // Nothing to draw? + if (!vertices || (verticesCount == 0)) + return; + if (Activate(true)) { - myRenderer.SaveGLStates(); - myStatesSaved = true; + // Apply the new view if needed + if (myViewChanged) + { + // Set the viewport + IntRect viewport = GetViewport(myView); + int top = GetHeight() - (viewport.Top + viewport.Height); + GLCheck(glViewport(viewport.Left, top, viewport.Width, viewport.Height)); - // Restore the render states and the current view, for SFML rendering - myRenderer.Initialize(); - SetView(GetView()); + // Set the projection matrix + GLCheck(glMatrixMode(GL_PROJECTION)); + GLCheck(glLoadMatrixf(myView.GetTransform().GetMatrix())); + + myViewChanged = false; + } + + // Apply the transform + GLCheck(glMatrixMode(GL_MODELVIEW)); + GLCheck(glLoadMatrixf(states.Transform.GetMatrix())); + + // Apply the blend mode + switch (states.BlendMode) + { + // Alpha blending + // glBlendFuncSeparateEXT is used when available to avoid an incorrect alpha value when the target + // is a RenderTexture -- in this case the alpha value must be written directly to the target buffer + default : + case BlendAlpha : + if (GLEW_EXT_blend_func_separate) + GLCheck(glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + else + GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + break; + + // Additive blending + case BlendAdd : + GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE)); + break; + + // Multiplicative blending + case BlendMultiply : + GLCheck(glBlendFunc(GL_DST_COLOR, GL_ZERO)); + break; + + // No blending + case BlendNone : + GLCheck(glBlendFunc(GL_ONE, GL_ZERO)); + break; + } + + // Apply the texture + if (states.Texture) + states.Texture->Bind(Texture::Pixels); + else + GLCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + // Apply the shader + if (states.Shader) + states.Shader->Bind(); + else + GLCheck(glUseProgramObjectARB(0)); + + // Setup the pointers to the vertices' components + const char* data = reinterpret_cast(vertices); + GLCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); + GLCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); + GLCheck(glTexCoordPointer(2, GL_INT, sizeof(Vertex), data + 12)); + + // Find the OpenGL primitive type + static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, + GL_TRIANGLES, GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, GL_QUADS}; + GLenum mode = modes[type]; + + // Draw the primitives + GLCheck(glDrawArrays(mode, 0, verticesCount)); } } //////////////////////////////////////////////////////////// -void RenderTarget::RestoreGLStates() +void RenderTarget::PushGLStates() { - if (myStatesSaved) + if (Activate(true)) { - if (Activate(true)) - { - myRenderer.RestoreGLStates(); - myStatesSaved = false; - } + GLCheck(glPushAttrib(GL_ALL_ATTRIB_BITS)); + GLCheck(glMatrixMode(GL_MODELVIEW)); + GLCheck(glPushMatrix()); + GLCheck(glMatrixMode(GL_PROJECTION)); + GLCheck(glPushMatrix()); + GLCheck(glMatrixMode(GL_TEXTURE)); + GLCheck(glPushMatrix()); + } + + ResetGLStates(); +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::PopGLStates() +{ + if (Activate(true)) + { + GLCheck(glPopAttrib()); + GLCheck(glMatrixMode(GL_PROJECTION)); + GLCheck(glPopMatrix()); + GLCheck(glMatrixMode(GL_MODELVIEW)); + GLCheck(glPopMatrix()); + GLCheck(glMatrixMode(GL_TEXTURE)); + GLCheck(glPopMatrix()); + } +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ResetGLStates() +{ + if (Activate(true)) + { + // Make sure that GLEW is initialized + priv::EnsureGlewInit(); + + GLCheck(glDisable(GL_LIGHTING)); + GLCheck(glDisable(GL_DEPTH_TEST)); + GLCheck(glEnable(GL_TEXTURE_2D)); + GLCheck(glEnable(GL_ALPHA_TEST)); + GLCheck(glEnable(GL_BLEND)); + GLCheck(glAlphaFunc(GL_GREATER, 0)); + GLCheck(glEnableClientState(GL_VERTEX_ARRAY)); + GLCheck(glEnableClientState(GL_COLOR_ARRAY)); + GLCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + + SetView(GetView()); } } @@ -215,9 +279,8 @@ void RenderTarget::Initialize() myDefaultView.Reset(FloatRect(0, 0, static_cast(GetWidth()), static_cast(GetHeight()))); SetView(myDefaultView); - // Initialize the renderer - if (Activate(true)) - myRenderer.Initialize(); + // Initialize the default OpenGL render-states + ResetGLStates(); } } // namespace sf diff --git a/src/SFML/Graphics/Renderer.cpp b/src/SFML/Graphics/Renderer.cpp deleted file mode 100644 index 82a60a54..00000000 --- a/src/SFML/Graphics/Renderer.cpp +++ /dev/null @@ -1,348 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include - - -namespace sf -{ -//////////////////////////////////////////////////////////// -Renderer::Renderer(RenderTarget& target) : -myTarget (target), -myTextureIsValid (false), -myShaderIsValid (false), -myBlendModeIsValid(false), -myViewportIsValid (false) -{ - myStates = &myStatesStack[0]; -} - - -//////////////////////////////////////////////////////////// -void Renderer::Initialize() -{ - // Default render states - GLCheck(glDisable(GL_LIGHTING)); - GLCheck(glDisable(GL_DEPTH_TEST)); - GLCheck(glEnable(GL_TEXTURE_2D)); - GLCheck(glEnable(GL_ALPHA_TEST)); - GLCheck(glAlphaFunc(GL_GREATER, 0)); - - // Default transform matrices - GLCheck(glMatrixMode(GL_MODELVIEW)); - GLCheck(glLoadIdentity()); - GLCheck(glMatrixMode(GL_PROJECTION)); - GLCheck(glLoadIdentity()); - - // Invalidate the cached SFML states - myTextureIsValid = false; - myShaderIsValid = false; - myBlendModeIsValid = false; - myViewportIsValid = false; -} - - -//////////////////////////////////////////////////////////// -void Renderer::SaveGLStates() -{ - // Save render states - GLCheck(glPushAttrib(GL_ALL_ATTRIB_BITS)); - - // Save matrices - GLCheck(glMatrixMode(GL_MODELVIEW)); - GLCheck(glPushMatrix()); - GLCheck(glMatrixMode(GL_PROJECTION)); - GLCheck(glPushMatrix()); -} - - -//////////////////////////////////////////////////////////// -void Renderer::RestoreGLStates() -{ - // Restore render states - GLCheck(glPopAttrib()); - - // Restore matrices - GLCheck(glMatrixMode(GL_PROJECTION)); - GLCheck(glPopMatrix()); - GLCheck(glMatrixMode(GL_MODELVIEW)); - GLCheck(glPopMatrix()); -} - - -//////////////////////////////////////////////////////////// -void Renderer::Clear(const Color& color) -{ - GLCheck(glClearColor(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f)); - GLCheck(glClear(GL_COLOR_BUFFER_BIT)); -} - - -//////////////////////////////////////////////////////////// -void Renderer::PushStates() -{ - myStates++; - *myStates = *(myStates - 1); -} - - -//////////////////////////////////////////////////////////// -void Renderer::PopStates() -{ - myStates--; -} - - -//////////////////////////////////////////////////////////// -void Renderer::SetModelView(const Matrix3& matrix) -{ - myStates->modelView = matrix; -} - - -//////////////////////////////////////////////////////////// -void Renderer::ApplyModelView(const Matrix3& matrix) -{ - myStates->modelView = myStates->modelView * matrix; -} - - -//////////////////////////////////////////////////////////// -void Renderer::SetProjection(const Matrix3& matrix) -{ - // Apply it immediately (this one is not critical for performances) - GLCheck(glMatrixMode(GL_PROJECTION)); - GLCheck(glLoadMatrixf(matrix.Get4x4Elements())); -} - - -//////////////////////////////////////////////////////////// -void Renderer::SetColor(const Color& color) -{ - myStates->r = color.r / 255.f; - myStates->g = color.g / 255.f; - myStates->b = color.b / 255.f; - myStates->a = color.a / 255.f; -} - - -//////////////////////////////////////////////////////////// -void Renderer::ApplyColor(const Color& color) -{ - myStates->r *= color.r / 255.f; - myStates->g *= color.g / 255.f; - myStates->b *= color.b / 255.f; - myStates->a *= color.a / 255.f; -} - - -//////////////////////////////////////////////////////////// -void Renderer::SetViewport(const IntRect& viewport) -{ - if ((viewport.Left != myViewport.Left) || (viewport.Width != myViewport.Width) || - (viewport.Top != myViewport.Top) || (viewport.Height != myViewport.Height) || - !myViewportIsValid) - { - // Revert the Y axis to match the OpenGL convention - int top = myTarget.GetHeight() - (viewport.Top + viewport.Height); - - // Apply the new viewport - GLCheck(glViewport(viewport.Left, top, viewport.Width, viewport.Height)); - - // Store it - myViewport = viewport; - myViewportIsValid = true; - } -} - - -//////////////////////////////////////////////////////////// -void Renderer::SetBlendMode(Blend::Mode mode) -{ - if ((mode != myBlendMode) || !myBlendModeIsValid) - { - // Apply the new blending mode - if (mode == Blend::None) - { - GLCheck(glDisable(GL_BLEND)); - } - else - { - GLCheck(glEnable(GL_BLEND)); - - switch (mode) - { - // Alpha blending - // glBlendFuncSeparateEXT is used when available to avoid an incorrect alpha value when the target - // is a RenderTexture -- in this case the alpha value must be written directly to the target buffer - default : - case Blend::Alpha : - if (GLEW_EXT_blend_func_separate) - GLCheck(glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - else - GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - break; - - // Additive blending - case Blend::Add : - GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE)); - break; - - // Multiplicative blending - case Blend::Multiply : - GLCheck(glBlendFunc(GL_DST_COLOR, GL_ZERO)); - break; - } - } - - // Store it - myBlendMode = mode; - myBlendModeIsValid = true; - } -} - - -//////////////////////////////////////////////////////////// -void Renderer::SetTexture(const Texture* texture) -{ - if ((texture != myTexture) || (texture && (texture->myTexture != myTextureId)) || !myTextureIsValid) - { - // Apply the new texture - if (texture) - texture->Bind(); - else - GLCheck(glBindTexture(GL_TEXTURE_2D, 0)); - - // Store it - myTexture = texture; - myTextureId = texture ? texture->myTexture : 0; - myTextureIsValid = true; - } -} - - -//////////////////////////////////////////////////////////// -void Renderer::SetShader(const Shader* shader) -{ - if ((shader != myShader) || !myShaderIsValid) - { - if (Shader::IsAvailable()) // to avoid calling possibly unsupported functions - { - // Apply the new shader - if (shader) - shader->Bind(); - else - GLCheck(glUseProgramObjectARB(0)); - - // Store it - myShader = shader; - myShaderIsValid = true; - } - } - else if (shader && myShaderIsValid) - { - // If the shader was already the current one, make sure that - // it is synchronized (in case it was modified since last use) - shader->Use(); - } -} - - -//////////////////////////////////////////////////////////// -void Renderer::Begin(PrimitiveType type) -{ - // Begin rendering - switch (type) - { - case TriangleList : glBegin(GL_TRIANGLES); break; - case TriangleStrip : glBegin(GL_TRIANGLE_STRIP); break; - case TriangleFan : glBegin(GL_TRIANGLE_FAN); break; - case QuadList : glBegin(GL_QUADS); break; - default: break; - } -} - - -//////////////////////////////////////////////////////////// -void Renderer::End() -{ - // End rendering - glEnd(); -} - - -//////////////////////////////////////////////////////////// -void Renderer::AddVertex(float x, float y) -{ - ProcessVertex(x, y, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f); -} - - -//////////////////////////////////////////////////////////// -void Renderer::AddVertex(float x, float y, float u, float v) -{ - ProcessVertex(x, y, u, v, 1.f, 1.f, 1.f, 1.f); -} - - -//////////////////////////////////////////////////////////// -void Renderer::AddVertex(float x, float y, const Color& color) -{ - ProcessVertex(x, y, 0.f, 0.f, color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f); -} - - -//////////////////////////////////////////////////////////// -void Renderer::AddVertex(float x, float y, float u, float v, const Color& color) -{ - ProcessVertex(x, y, u, v, color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f); -} - - -//////////////////////////////////////////////////////////// -void Renderer::ProcessVertex(float x, float y, float u, float v, float r, float g, float b, float a) -{ - // Transform the vertex position by the current model-view matrix - Vector2f position = myStates->modelView.Transform(Vector2f(x, y)); - - // Modulate the vertex color with the current global color - r *= myStates->r; - g *= myStates->g; - b *= myStates->b; - a *= myStates->a; - - // Render the vertex - glColor4f(r, g, b, a); - glTexCoord2f(u, v); - glVertex2f(position.x, position.y); -} - -} // namespace sf diff --git a/src/SFML/Graphics/Shader.cpp b/src/SFML/Graphics/Shader.cpp index e5c62fd6..7cdc3ea2 100644 --- a/src/SFML/Graphics/Shader.cpp +++ b/src/SFML/Graphics/Shader.cpp @@ -371,7 +371,7 @@ bool Shader::CompileProgram() static const char* vertexSrc = "void main()" "{" - " gl_TexCoord[0] = gl_MultiTexCoord0;" + " gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;" " gl_FrontColor = gl_Color;" " gl_Position = ftransform();" "}"; diff --git a/src/SFML/Graphics/Shape.cpp b/src/SFML/Graphics/Shape.cpp index ed80496f..9e17e439 100644 --- a/src/SFML/Graphics/Shape.cpp +++ b/src/SFML/Graphics/Shape.cpp @@ -26,365 +26,271 @@ // Headers //////////////////////////////////////////////////////////// #include -#include +#include +#include +#include #include +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + // Compute the normal of a segment + sf::Vector2f ComputeNormal(const sf::Vector2f& p1, const sf::Vector2f& p2) + { + sf::Vector2f normal(p1.y - p2.y, p2.x - p1.x); + float length = std::sqrt(normal.x * normal.x + normal.y * normal.y); + if (length != 0.f) + normal /= length; + return normal; + } +} + + namespace sf { //////////////////////////////////////////////////////////// -Shape::Shape() : -myOutline (0.f), -myIsFillEnabled (true), -myIsOutlineEnabled(true), -myIsCompiled (false) +Shape::~Shape() { - // Put a placeholder for the center of the shape - myPoints.push_back(Point()); } //////////////////////////////////////////////////////////// -void Shape::AddPoint(float x, float y, const Color& color, const Color& outlineColor) +void Shape::SetTexture(const Texture* texture, bool resetRect) { - AddPoint(Vector2f(x, y), color, outlineColor); + // Recompute the texture area if requested, or if there was no texture before + if (texture && (resetRect || !myTexture)) + SetTextureRect(IntRect(0, 0, texture->GetWidth(), texture->GetHeight())); + + // Assign the new texture + myTexture = texture; } //////////////////////////////////////////////////////////// -void Shape::AddPoint(const Vector2f& position, const Color& color, const Color& outlineColor) +const Texture* Shape::GetTexture() const { - myPoints.push_back(Point(position, color, outlineColor)); - myIsCompiled = false; + return myTexture; } //////////////////////////////////////////////////////////// -unsigned int Shape::GetPointsCount() const +void Shape::SetTextureRect(const IntRect& rect) { - return static_cast(myPoints.size() - 1); + myTextureRect = rect; + UpdateTexCoords(); } //////////////////////////////////////////////////////////// -void Shape::EnableFill(bool enable) +const IntRect& Shape::GetTextureRect() const { - myIsFillEnabled = enable; + return myTextureRect; } //////////////////////////////////////////////////////////// -void Shape::EnableOutline(bool enable) +void Shape::SetFillColor(const Color& color) { - myIsOutlineEnabled = enable; + myFillColor = color; + UpdateFillColors(); } //////////////////////////////////////////////////////////// -void Shape::SetPointPosition(unsigned int index, const Vector2f& position) +const Color& Shape::GetFillColor() const { - myPoints[index + 1].Position = position; - myIsCompiled = false; + return myFillColor; } //////////////////////////////////////////////////////////// -void Shape::SetPointPosition(unsigned int index, float x, float y) +void Shape::SetOutlineColor(const Color& color) { - SetPointPosition(index, Vector2f(x, y)); + myOutlineColor = color; + UpdateOutlineColors(); } //////////////////////////////////////////////////////////// -void Shape::SetPointColor(unsigned int index, const Color& color) +const Color& Shape::GetOutlineColor() const { - myPoints[index + 1].Col = color; - myIsCompiled = false; -} - - -//////////////////////////////////////////////////////////// -void Shape::SetPointOutlineColor(unsigned int index, const Color& color) -{ - myPoints[index + 1].OutlineCol = color; - myIsCompiled = false; + return myOutlineColor; } //////////////////////////////////////////////////////////// void Shape::SetOutlineThickness(float thickness) { - myOutline = thickness; -} - - -//////////////////////////////////////////////////////////// -const Vector2f& Shape::GetPointPosition(unsigned int index) const -{ - return myPoints[index + 1].Position; -} - - -//////////////////////////////////////////////////////////// -const Color& Shape::GetPointColor(unsigned int index) const -{ - return myPoints[index + 1].Col; -} - - -//////////////////////////////////////////////////////////// -const Color& Shape::GetPointOutlineColor(unsigned int index) const -{ - return myPoints[index + 1].OutlineCol; + myOutlineThickness = thickness; + Update(); // recompute everything because the whole shape must be offseted } //////////////////////////////////////////////////////////// float Shape::GetOutlineThickness() const { - return myOutline; + return myOutlineThickness; } //////////////////////////////////////////////////////////// -Shape Shape::Line(float p1x, float p1y, float p2x, float p2y, float thickness, const Color& color, float outline, const Color& outlineColor) +FloatRect Shape::GetLocalBounds() const { - Vector2f p1(p1x, p1y); - Vector2f p2(p2x, p2y); - - return Shape::Line(p1, p2, thickness, color, outline, outlineColor); + return myBounds; } //////////////////////////////////////////////////////////// -Shape Shape::Line(const Vector2f& p1, const Vector2f& p2, float thickness, const Color& color, float outline, const Color& outlineColor) +FloatRect Shape::GetGlobalBounds() const { - // Compute the extrusion direction - Vector2f normal; - ComputeNormal(p1, p2, normal); - normal *= thickness / 2; - - // Create the shape's points - Shape shape; - shape.AddPoint(p1 - normal, color, outlineColor); - shape.AddPoint(p2 - normal, color, outlineColor); - shape.AddPoint(p2 + normal, color, outlineColor); - shape.AddPoint(p1 + normal, color, outlineColor); - shape.SetOutlineThickness(outline); - - // Compile it - shape.Compile(); - - return shape; + return GetTransform().TransformRect(GetLocalBounds()); } //////////////////////////////////////////////////////////// -Shape Shape::Rectangle(float left, float top, float width, float height, const Color& color, float outline, const Color& outlineColor) +Shape::Shape() : +myTexture (NULL), +myTextureRect (), +myFillColor (255, 255, 255), +myOutlineColor (255, 255, 255), +myOutlineThickness(0), +myVertices (TrianglesFan), +myOutlineVertices (TrianglesStrip), +myInsideBounds (), +myBounds () { - // Create the shape's points - Shape shape; - shape.AddPoint(Vector2f(left, top), color, outlineColor); - shape.AddPoint(Vector2f(left + width, top), color, outlineColor); - shape.AddPoint(Vector2f(left + width, top + height), color, outlineColor); - shape.AddPoint(Vector2f(left, top + height), color, outlineColor); - shape.SetOutlineThickness(outline); - - // Compile it - shape.Compile(); - - return shape; } //////////////////////////////////////////////////////////// -Shape Shape::Rectangle(const FloatRect& rectangle, const Color& color, float outline, const Color& outlineColor) +void Shape::Update() { - return Shape::Rectangle(rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height, color, outline, outlineColor); -} - - -//////////////////////////////////////////////////////////// -Shape Shape::Circle(float x, float y, float radius, const Color& color, float outline, const Color& outlineColor) -{ - return Shape::Circle(Vector2f(x, y), radius, color, outline, outlineColor); -} - - -//////////////////////////////////////////////////////////// -Shape Shape::Circle(const Vector2f& center, float radius, const Color& color, float outline, const Color& outlineColor) -{ - static const int nbSegments = 40; - - // Create the points set - Shape shape; - for (int i = 0; i < nbSegments; ++i) + // Get the total number of points of the shape + unsigned int count = GetOutlinePointsCount(); + if (count < 3) { - float angle = i * 2 * 3.141592654f / nbSegments; - Vector2f offset(std::cos(angle), std::sin(angle)); - - shape.AddPoint(center + offset * radius, color, outlineColor); - } - - // Compile it - shape.SetOutlineThickness(outline); - shape.Compile(); - - return shape; -} - - -//////////////////////////////////////////////////////////// -void Shape::Render(RenderTarget&, Renderer& renderer) const -{ - // Make sure the shape has at least 3 points (4 if we count the center) - if (myPoints.size() < 4) + sf::Err() << "Invalid shape: it has less than 3 points" << std::endl; + myVertices.Resize(0); + myOutlineVertices.Resize(0); return; - - // Make sure the shape is compiled - if (!myIsCompiled) - const_cast(this)->Compile(); - - // Shapes only use color, no texture - renderer.SetTexture(NULL); - - // Draw the shape - if (myIsFillEnabled) - { - if (myPoints.size() == 4) - { - // Special case of a triangle - renderer.Begin(Renderer::TriangleList); - renderer.AddVertex(myPoints[1].Position.x, myPoints[1].Position.y, myPoints[1].Col); - renderer.AddVertex(myPoints[2].Position.x, myPoints[2].Position.y, myPoints[2].Col); - renderer.AddVertex(myPoints[3].Position.x, myPoints[3].Position.y, myPoints[3].Col); - renderer.End(); - } - else if (myPoints.size() == 5) - { - // Special case of a quad - renderer.Begin(Renderer::TriangleStrip); - renderer.AddVertex(myPoints[1].Position.x, myPoints[1].Position.y, myPoints[1].Col); - renderer.AddVertex(myPoints[2].Position.x, myPoints[2].Position.y, myPoints[2].Col); - renderer.AddVertex(myPoints[4].Position.x, myPoints[4].Position.y, myPoints[4].Col); - renderer.AddVertex(myPoints[3].Position.x, myPoints[3].Position.y, myPoints[3].Col); - renderer.End(); - } - else - { - renderer.Begin(Renderer::TriangleFan); - - // General case of a convex polygon - for (std::vector::const_iterator i = myPoints.begin(); i != myPoints.end(); ++i) - renderer.AddVertex(i->Position.x, i->Position.y, i->Col); - - // Close the shape by duplicating the first point at the end - const Point& first = myPoints[1]; - renderer.AddVertex(first.Position.x, first.Position.y, first.Col); - - renderer.End(); - } } - // Draw the outline - if (myIsOutlineEnabled && (myOutline != 0)) + myVertices.Resize(count + 2); // + 2 for center and repeated first point + + // Position + Vector2f offset(myOutlineThickness, myOutlineThickness); + for (unsigned int i = 0; i < count; ++i) + myVertices[i + 1].Position = GetOutlinePoint(i) + offset; + myVertices[count + 1].Position = myVertices[1].Position; + + // Update the bounding rectangle + myInsideBounds = myVertices.GetBounds(); + + // Compute the center and make it the first vertex + myVertices[0].Position.x = myInsideBounds.Left + myInsideBounds.Width / 2; + myVertices[0].Position.y = myInsideBounds.Top + myInsideBounds.Height / 2; + + // Color + UpdateFillColors(); + + // Texture coordinates + UpdateTexCoords(); + + // Outline + UpdateOutline(); +} + + +//////////////////////////////////////////////////////////// +void Shape::Draw(RenderTarget& target, RenderStates states) const +{ + states.Transform *= GetTransform(); + + // Render the inside + if (myFillColor.a > 0) { - renderer.Begin(Renderer::TriangleStrip); + states.Texture = myTexture; + target.Draw(myVertices, states); + } - for (std::vector::const_iterator i = myPoints.begin() + 1; i != myPoints.end(); ++i) - { - Vector2f point1 = i->Position; - Vector2f point2 = i->Position + i->Normal * myOutline; - renderer.AddVertex(point1.x, point1.y, i->OutlineCol); - renderer.AddVertex(point2.x, point2.y, i->OutlineCol); - } - - // Close the shape by duplicating the first point at the end - const Point& first = myPoints[1]; - Vector2f point1 = first.Position; - Vector2f point2 = first.Position + first.Normal * myOutline; - renderer.AddVertex(point1.x, point1.y, first.OutlineCol); - renderer.AddVertex(point2.x, point2.y, first.OutlineCol); - - renderer.End(); + // Render the outline + if ((myOutlineColor.a > 0) && (myOutlineThickness > 0)) + { + states.Texture = NULL; + target.Draw(myOutlineVertices, states); } } //////////////////////////////////////////////////////////// -void Shape::Compile() +void Shape::UpdateFillColors() { - // Compute the center - float nbPoints = static_cast(myPoints.size() - 1); - float r = 0, g = 0, b = 0, a = 0; - Point center(Vector2f(0, 0), Color(0, 0, 0, 0)); - for (std::size_t i = 1; i < myPoints.size(); ++i) - { - center.Position += myPoints[i].Position; - r += myPoints[i].Col.r; - g += myPoints[i].Col.g; - b += myPoints[i].Col.b; - a += myPoints[i].Col.a; - } - center.Position /= nbPoints; - center.Col.r = static_cast(r / nbPoints); - center.Col.g = static_cast(g / nbPoints); - center.Col.b = static_cast(b / nbPoints); - center.Col.a = static_cast(a / nbPoints); - myPoints[0] = center; + for (unsigned int i = 0; i < myVertices.GetVerticesCount(); ++i) + myVertices[i].Color = myFillColor; +} - // Compute the outline - for (std::size_t i = 1; i < myPoints.size(); ++i) + +//////////////////////////////////////////////////////////// +void Shape::UpdateTexCoords() +{ + for (unsigned int i = 0; i < myVertices.GetVerticesCount(); ++i) { + float xratio = (myVertices[i].Position.x - myInsideBounds.Left) / myInsideBounds.Width; + float yratio = (myVertices[i].Position.y - myInsideBounds.Top) / myInsideBounds.Height; + myVertices[i].TexCoords.x = static_cast(myTextureRect.Left + myTextureRect.Width * xratio); + myVertices[i].TexCoords.y = static_cast(myTextureRect.Top + myTextureRect.Height * yratio); + } +} + + +//////////////////////////////////////////////////////////// +void Shape::UpdateOutline() +{ + unsigned int count = myVertices.GetVerticesCount() - 2; + myOutlineVertices.Resize((count + 1) * 2); + + for (unsigned int i = 0; i < count; ++i) + { + unsigned int index = i + 1; + // Get the two segments shared by the current point - Point& p0 = (i == 1) ? myPoints[myPoints.size() - 1] : myPoints[i - 1]; - Point& p1 = myPoints[i]; - Point& p2 = (i == myPoints.size() - 1) ? myPoints[1] : myPoints[i + 1]; + Vector2f p0 = (i == 0) ? myVertices[count].Position : myVertices[index - 1].Position; + Vector2f p1 = myVertices[index].Position; + Vector2f p2 = myVertices[index + 1].Position; // Compute their normal - Vector2f normal1, normal2; - if (!ComputeNormal(p0.Position, p1.Position, normal1) || !ComputeNormal(p1.Position, p2.Position, normal2)) - continue; + Vector2f n1 = ComputeNormal(p0, p1); + Vector2f n2 = ComputeNormal(p1, p2); - // Add them to get the extrusion direction - float factor = 1.f + (normal1.x * normal2.x + normal1.y * normal2.y); - p1.Normal = (normal1 + normal2) / factor; + // Combine them to get the extrusion direction + float factor = 1.f + (n1.x * n2.x + n1.y * n2.y); + Vector2f normal = -(n1 + n2) / factor; - // Make sure it points towards the outside of the shape - float dot = (p1.Position.x - center.Position.x) * p1.Normal.x + (p1.Position.y - center.Position.y) * p1.Normal.y; - if (dot < 0) - p1.Normal = -p1.Normal; + // Update the outline points + myOutlineVertices[i * 2 + 0].Position = p1; + myOutlineVertices[i * 2 + 1].Position = p1 + normal * myOutlineThickness; } - myIsCompiled = true; + // Duplicate the first point at the end, to close the outline + myOutlineVertices[count * 2 + 0].Position = myOutlineVertices[0].Position; + myOutlineVertices[count * 2 + 1].Position = myOutlineVertices[1].Position; + + // Update outline colors + UpdateOutlineColors(); + + // Update the shape's bounds + myBounds = myOutlineVertices.GetBounds(); } //////////////////////////////////////////////////////////// -bool Shape::ComputeNormal(const Vector2f& p1, const Vector2f& p2, Vector2f& normal) +void Shape::UpdateOutlineColors() { - normal.x = p1.y - p2.y; - normal.y = p2.x - p1.x; - - float len = std::sqrt(normal.x * normal.x + normal.y * normal.y); - if (len == 0.f) - return false; - - normal.x /= len; - normal.y /= len; - - return true; -} - - -//////////////////////////////////////////////////////////// -Shape::Point::Point(const Vector2f& position, const Color& color, const Color& outlineColor) : -Position (position), -Normal (0.f, 0.f), -Col (color), -OutlineCol(outlineColor) -{ - + for (unsigned int i = 0; i < myOutlineVertices.GetVerticesCount(); ++i) + myOutlineVertices[i].Color = myOutlineColor; } } // namespace sf diff --git a/src/SFML/Graphics/Sprite.cpp b/src/SFML/Graphics/Sprite.cpp index 19405a7b..23ac67a8 100644 --- a/src/SFML/Graphics/Sprite.cpp +++ b/src/SFML/Graphics/Sprite.cpp @@ -27,46 +27,44 @@ //////////////////////////////////////////////////////////// #include #include -#include -#include +#include namespace sf { //////////////////////////////////////////////////////////// Sprite::Sprite() : -Drawable (), -mySubRect (0, 0, 1, 1), -myIsFlippedX(false), -myIsFlippedY(false) +myTexture (NULL), +myTextureRect(0, 0, 0, 0) { - } //////////////////////////////////////////////////////////// Sprite::Sprite(const Texture& texture) : -Drawable (), -mySubRect (0, 0, 1, 1), -myIsFlippedX(false), -myIsFlippedY(false) +myTexture (NULL), +myTextureRect(0, 0, 0, 0) { SetTexture(texture); } //////////////////////////////////////////////////////////// -void Sprite::SetTexture(const Texture& texture, bool adjustToNewSize) +Sprite::Sprite(const Texture& texture, const IntRect& rectangle) : +myTexture (NULL), +myTextureRect(0, 0, 0, 0) { - // If there was no valid texture before, force adjusting to the new texture size - if (!myTexture) - adjustToNewSize = true; + SetTexture(texture); + SetTextureRect(rectangle); +} - // If we want to adjust the size and the new texture is valid, we adjust the source rectangle - if (adjustToNewSize && (texture.GetWidth() > 0) && (texture.GetHeight() > 0)) - { - SetSubRect(IntRect(0, 0, texture.GetWidth(), texture.GetHeight())); - } + +//////////////////////////////////////////////////////////// +void Sprite::SetTexture(const Texture& texture, bool resetRect) +{ + // Recompute the texture area if requested, or if there was no valid texture before + if (resetRect || !myTexture) + SetTextureRect(IntRect(0, 0, texture.GetWidth(), texture.GetHeight())); // Assign the new texture myTexture = &texture; @@ -74,38 +72,25 @@ void Sprite::SetTexture(const Texture& texture, bool adjustToNewSize) //////////////////////////////////////////////////////////// -void Sprite::SetSubRect(const IntRect& rectangle) +void Sprite::SetTextureRect(const IntRect& rectangle) { - mySubRect = rectangle; + if (rectangle != myTextureRect) + { + myTextureRect = rectangle; + UpdatePositions(); + UpdateTexCoords(); + } } //////////////////////////////////////////////////////////// -void Sprite::Resize(float width, float height) +void Sprite::SetColor(const Color& color) { - if ((mySubRect.Width > 0) && (mySubRect.Height > 0)) - SetScale(width / mySubRect.Width, height / mySubRect.Height); -} - - -//////////////////////////////////////////////////////////// -void Sprite::Resize(const Vector2f& size) -{ - Resize(size.x, size.y); -} - - -//////////////////////////////////////////////////////////// -void Sprite::FlipX(bool flipped) -{ - myIsFlippedX = flipped; -} - - -//////////////////////////////////////////////////////////// -void Sprite::FlipY(bool flipped) -{ - myIsFlippedY = flipped; + // Update the vertices' color + myVertices[0].Color = color; + myVertices[1].Color = color; + myVertices[2].Color = color; + myVertices[3].Color = color; } @@ -117,49 +102,73 @@ const Texture* Sprite::GetTexture() const //////////////////////////////////////////////////////////// -const IntRect& Sprite::GetSubRect() const +const IntRect& Sprite::GetTextureRect() const { - return mySubRect; + return myTextureRect; } //////////////////////////////////////////////////////////// -Vector2f Sprite::GetSize() const +const Color& Sprite::GetColor() const { - return Vector2f(mySubRect.Width * GetScale().x, mySubRect.Height * GetScale().y); + return myVertices[0].Color; } //////////////////////////////////////////////////////////// -void Sprite::Render(RenderTarget&, Renderer& renderer) const +FloatRect Sprite::GetLocalBounds() const { - // Get the sprite size - float width = static_cast(mySubRect.Width); - float height = static_cast(mySubRect.Height); + float width = static_cast(myTextureRect.Width); + float height = static_cast(myTextureRect.Height); - // Check if the texture is valid, and calculate the texture coordinates - FloatRect coords; + return FloatRect(0.f, 0.f, width, height); +} + + +//////////////////////////////////////////////////////////// +FloatRect Sprite::GetGlobalBounds() const +{ + return GetTransform().TransformRect(GetLocalBounds()); +} + + +//////////////////////////////////////////////////////////// +void Sprite::Draw(RenderTarget& target, RenderStates states) const +{ if (myTexture) - coords = myTexture->GetTexCoords(mySubRect); + { + states.Transform *= GetTransform(); + states.Texture = myTexture; + target.Draw(myVertices, 4, Quads, states); + } +} - // Compute the texture coordinates - float left = coords.Left; - float top = coords.Top; - float right = coords.Left + coords.Width; - float bottom = coords.Top + coords.Height; - if (myIsFlippedX) std::swap(left, right); - if (myIsFlippedY) std::swap(top, bottom); - // Bind the texture - renderer.SetTexture(myTexture); +//////////////////////////////////////////////////////////// +void Sprite::UpdatePositions() +{ + float width = static_cast(myTextureRect.Width); + float height = static_cast(myTextureRect.Height); - // Draw the sprite's geometry - renderer.Begin(Renderer::TriangleStrip); - renderer.AddVertex(0, 0, left, top); - renderer.AddVertex(width, 0, right, top); - renderer.AddVertex(0, height, left, bottom); - renderer.AddVertex(width, height, right, bottom); - renderer.End(); + myVertices[0].Position = Vector2f(0, 0); + myVertices[1].Position = Vector2f(0, height); + myVertices[2].Position = Vector2f(width, height); + myVertices[3].Position = Vector2f(width, 0); +} + + +//////////////////////////////////////////////////////////// +void Sprite::UpdateTexCoords() +{ + int left = myTextureRect.Left; + int right = myTextureRect.Left + myTextureRect.Width; + int top = myTextureRect.Top; + int bottom = myTextureRect.Top + myTextureRect.Height; + + myVertices[0].TexCoords = Vector2i(left, top); + myVertices[1].TexCoords = Vector2i(left, bottom); + myVertices[2].TexCoords = Vector2i(right, bottom); + myVertices[3].TexCoords = Vector2i(right, top); } } // namespace sf diff --git a/src/SFML/Graphics/StarShape.cpp b/src/SFML/Graphics/StarShape.cpp new file mode 100644 index 00000000..0f5696ef --- /dev/null +++ b/src/SFML/Graphics/StarShape.cpp @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +StarShape::StarShape() : +myInnerRadius(10), +myOuterRadius(20), +myPointsCount(5) +{ +} + + +//////////////////////////////////////////////////////////// +void StarShape::SetInnerRadius(float radius) +{ + myInnerRadius = radius; + Update(); +} + + +//////////////////////////////////////////////////////////// +float StarShape::GetInnerRadius() const +{ + return myInnerRadius; +} + + +//////////////////////////////////////////////////////////// +void StarShape::SetOuterRadius(float radius) +{ + myOuterRadius = radius; + Update(); +} + + +//////////////////////////////////////////////////////////// +float StarShape::GetOuterRadius() const +{ + return myOuterRadius; +} + + +//////////////////////////////////////////////////////////// +void StarShape::SetPointsCount(unsigned int count) +{ + myPointsCount = count; + Update(); +} + + +//////////////////////////////////////////////////////////// +unsigned int StarShape::GetPointsCount() const +{ + return myPointsCount; +} + + +//////////////////////////////////////////////////////////// +unsigned int StarShape::GetOutlinePointsCount() const +{ + return myPointsCount * 2; +} + + +//////////////////////////////////////////////////////////// +Vector2f StarShape::GetOutlinePoint(unsigned int index) const +{ + static const float pi = 3.141592654f; + float angle = index * pi / myPointsCount - pi / 2; + float x = std::cos(angle); + float y = std::sin(angle); + float radius = (index % 2 == 0 ? myOuterRadius : myInnerRadius); + + return Vector2f(myOuterRadius + x * radius, myOuterRadius + y * radius); +} + +} // namespace sf diff --git a/src/SFML/Graphics/Text.cpp b/src/SFML/Graphics/Text.cpp index bed9c5e2..cec99882 100644 --- a/src/SFML/Graphics/Text.cpp +++ b/src/SFML/Graphics/Text.cpp @@ -27,17 +27,20 @@ //////////////////////////////////////////////////////////// #include #include -#include +#include +#include namespace sf { //////////////////////////////////////////////////////////// Text::Text() : +myString (), myFont (&Font::GetDefaultFont()), myCharacterSize(30), myStyle (Regular), -myRectUpdated (true) +myVertices (Quads), +myBounds () { } @@ -45,12 +48,14 @@ myRectUpdated (true) //////////////////////////////////////////////////////////// Text::Text(const String& string, const Font& font, unsigned int characterSize) : +myString (string), myFont (&font), myCharacterSize(characterSize), myStyle (Regular), -myRectUpdated (true) +myVertices (Quads), +myBounds () { - SetString(string); + UpdateGeometry(); } @@ -58,7 +63,7 @@ myRectUpdated (true) void Text::SetString(const String& string) { myString = string; - myRectUpdated = false; + UpdateGeometry(); } @@ -68,7 +73,7 @@ void Text::SetFont(const Font& font) if (myFont != &font) { myFont = &font; - myRectUpdated = false; + UpdateGeometry(); } } @@ -79,7 +84,7 @@ void Text::SetCharacterSize(unsigned int size) if (myCharacterSize != size) { myCharacterSize = size; - myRectUpdated = false; + UpdateGeometry(); } } @@ -90,7 +95,19 @@ void Text::SetStyle(unsigned long style) if (myStyle != style) { myStyle = style; - myRectUpdated = false; + UpdateGeometry(); + } +} + + +//////////////////////////////////////////////////////////// +void Text::SetColor(const Color& color) +{ + if (color != myColor) + { + myColor = color; + for (unsigned int i = 0; i < myVertices.GetVerticesCount(); ++i) + myVertices[i].Color = myColor; } } @@ -105,6 +122,7 @@ const String& Text::GetString() const //////////////////////////////////////////////////////////// const Font& Text::GetFont() const { + assert(myFont != NULL); // can never be NULL, always &Font::GetDefaultFont() by default return *myFont; } @@ -124,24 +142,29 @@ unsigned long Text::GetStyle() const //////////////////////////////////////////////////////////// -Vector2f Text::GetCharacterPos(std::size_t index) const +const Color& Text::GetColor() const { - // Make sure that we have a valid font - if (!myFont) - return Vector2f(0, 0); + return myColor; +} + + +//////////////////////////////////////////////////////////// +Vector2f Text::FindCharacterPos(std::size_t index) const +{ + assert(myFont != NULL); // Adjust the index if it's out of range if (index > myString.GetSize()) index = myString.GetSize(); - // We'll need this a lot - bool bold = (myStyle & Bold) != 0; - float space = static_cast(myFont->GetGlyph(L' ', myCharacterSize, bold).Advance); + // Precompute the variables needed by the algorithm + bool bold = (myStyle & Bold) != 0; + float hspace = static_cast(myFont->GetGlyph(L' ', myCharacterSize, bold).Advance); + float vspace = static_cast(myFont->GetLineSpacing(myCharacterSize)); // Compute the position Vector2f position; Uint32 prevChar = 0; - float lineSpacing = static_cast(myFont->GetLineSpacing(myCharacterSize)); for (std::size_t i = 0; i < index; ++i) { Uint32 curChar = myString[i]; @@ -153,69 +176,75 @@ Vector2f Text::GetCharacterPos(std::size_t index) const // Handle special characters switch (curChar) { - case L' ' : position.x += space; continue; - case L'\t' : position.x += space * 4; continue; - case L'\v' : position.y += lineSpacing * 4; continue; - case L'\n' : position.y += lineSpacing; position.x = 0; continue; + case L' ' : position.x += hspace; continue; + case L'\t' : position.x += hspace * 4; continue; + case L'\n' : position.y += vspace; position.x = 0; continue; + case L'\v' : position.y += vspace * 4; continue; } // For regular characters, add the advance offset of the glyph position.x += static_cast(myFont->GetGlyph(curChar, myCharacterSize, bold).Advance); } + // Transform the position to global coordinates + position = GetTransform().TransformPoint(position); + return position; } //////////////////////////////////////////////////////////// -FloatRect Text::GetRect() const +FloatRect Text::GetLocalBounds() const { - UpdateRect(); - - FloatRect rect; - rect.Left = (myBaseRect.Left - GetOrigin().x) * GetScale().x + GetPosition().x; - rect.Top = (myBaseRect.Top - GetOrigin().y) * GetScale().y + GetPosition().y; - rect.Width = myBaseRect.Width * GetScale().x; - rect.Height = myBaseRect.Height * GetScale().y; - - return rect; + return myBounds; } //////////////////////////////////////////////////////////// -void Text::Render(RenderTarget&, Renderer& renderer) const +FloatRect Text::GetGlobalBounds() const { - // No text or not font: nothing to render - if (!myFont || myString.IsEmpty()) + return GetTransform().TransformRect(GetLocalBounds()); +} + + +//////////////////////////////////////////////////////////// +void Text::Draw(RenderTarget& target, RenderStates states) const +{ + assert(myFont != NULL); + + states.Transform *= GetTransform(); + states.BlendMode = BlendAlpha; // alpha blending is mandatory for proper text rendering + states.Texture = &myFont->GetTexture(myCharacterSize); + target.Draw(myVertices, states); +} + + +//////////////////////////////////////////////////////////// +void Text::UpdateGeometry() +{ + assert(myFont != NULL); + + // Clear the previous geometry + myVertices.Clear(); + + // No text: nothing to draw + if (myString.IsEmpty()) return; - // Bind the font texture - const Texture& texture = myFont->GetTexture(myCharacterSize); - renderer.SetTexture(&texture); + // Compute values related to the text style + bool bold = (myStyle & Bold) != 0; + bool underlined = (myStyle & Underlined) != 0; + float italic = (myStyle & Italic) ? 0.208f : 0.f; // 12 degrees + float underlineOffset = myCharacterSize * 0.1f; + float underlineThickness = myCharacterSize * (bold ? 0.1f : 0.07f); - // Computes values related to the text style - bool bold = (myStyle & Bold) != 0; - bool underlined = (myStyle & Underlined) != 0; - float italicCoeff = (myStyle & Italic) ? 0.208f : 0.f; // 12 degrees - float underlineOffset = myCharacterSize * 0.1f; - float underlineThickness = myCharacterSize * (bold ? 0.1f : 0.07f); - FloatRect underlineCoords = texture.GetTexCoords(IntRect(1, 1, 1, 1)); - float underlineLeft = underlineCoords.Left; - float underlineTop = underlineCoords.Top; - float underlineRight = underlineCoords.Left + underlineCoords.Width; - float underlineBottom = underlineCoords.Top + underlineCoords.Height; + // Precompute the variables needed by the algorithm + float hspace = static_cast(myFont->GetGlyph(L' ', myCharacterSize, bold).Advance); + float vspace = static_cast(myFont->GetLineSpacing(myCharacterSize)); + float x = 0.f; + float y = static_cast(myCharacterSize); - // Initialize the rendering coordinates - float space = static_cast(myFont->GetGlyph(L' ', myCharacterSize, bold).Advance); - float lineSpacing = static_cast(myFont->GetLineSpacing(myCharacterSize)); - float x = 0.f; - float y = static_cast(myCharacterSize); - - // Note: - // Here we use a Begin/End pair for each quad because - // the font's texture may change in a call to GetGlyph - - // Draw one quad for each character + // Create one quad for each character Uint32 prevChar = 0; for (std::size_t i = 0; i < myString.GetSize(); ++i) { @@ -231,44 +260,42 @@ void Text::Render(RenderTarget&, Renderer& renderer) const float top = y + underlineOffset; float bottom = top + underlineThickness; - renderer.Begin(Renderer::QuadList); - renderer.AddVertex(0, top, underlineLeft, underlineTop); - renderer.AddVertex(x, top, underlineRight, underlineTop); - renderer.AddVertex(x, bottom, underlineRight, underlineBottom); - renderer.AddVertex(0, bottom, underlineLeft, underlineBottom); - renderer.End(); + myVertices.Append(Vertex(Vector2f(0, top), myColor, Vector2i(1, 1))); + myVertices.Append(Vertex(Vector2f(x, top), myColor, Vector2i(2, 1))); + myVertices.Append(Vertex(Vector2f(x, bottom), myColor, Vector2i(2, 2))); + myVertices.Append(Vertex(Vector2f(0, bottom), myColor, Vector2i(1, 2))); } // Handle special characters switch (curChar) { - case L' ' : x += space; continue; - case L'\t' : x += space * 4; continue; - case L'\n' : y += lineSpacing; x = 0; continue; - case L'\v' : y += lineSpacing * 4; continue; + case L' ' : x += hspace; continue; + case L'\t' : x += hspace * 4; continue; + case L'\n' : y += vspace; x = 0; continue; + case L'\v' : y += vspace * 4; continue; } // Extract the current glyph's description - const Glyph& glyph = myFont->GetGlyph(curChar, myCharacterSize, bold); - int advance = glyph.Advance; - const IntRect& bounds = glyph.Bounds; - FloatRect coords = texture.GetTexCoords(glyph.SubRect); + const Glyph& glyph = myFont->GetGlyph(curChar, myCharacterSize, bold); - int boundsRight = bounds.Left + bounds.Width; - int boundsBottom = bounds.Top + bounds.Height; - float coordsRight = coords.Left + coords.Width; - float coordsBottom = coords.Top + coords.Height; + int left = glyph.Bounds.Left; + int top = glyph.Bounds.Top; + int right = glyph.Bounds.Left + glyph.Bounds.Width; + int bottom = glyph.Bounds.Top + glyph.Bounds.Height; - // Draw a textured quad for the current character - renderer.Begin(Renderer::QuadList); - renderer.AddVertex(x + bounds.Left - italicCoeff * bounds.Top, y + bounds.Top, coords.Left, coords.Top); - renderer.AddVertex(x + boundsRight - italicCoeff * bounds.Top, y + bounds.Top, coordsRight, coords.Top); - renderer.AddVertex(x + boundsRight - italicCoeff * boundsBottom, y + boundsBottom, coordsRight, coordsBottom); - renderer.AddVertex(x + bounds.Left - italicCoeff * boundsBottom, y + boundsBottom, coords.Left, coordsBottom); - renderer.End(); + int u1 = glyph.TextureRect.Left; + int v1 = glyph.TextureRect.Top; + int u2 = glyph.TextureRect.Left + glyph.TextureRect.Width; + int v2 = glyph.TextureRect.Top + glyph.TextureRect.Height; + + // Add a quad for the current character + myVertices.Append(Vertex(Vector2f(x + left - italic * top, y + top), myColor, Vector2i(u1, v1))); + myVertices.Append(Vertex(Vector2f(x + right - italic * top, y + top), myColor, Vector2i(u2, v1))); + myVertices.Append(Vertex(Vector2f(x + right - italic * bottom, y + bottom), myColor, Vector2i(u2, v2))); + myVertices.Append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), myColor, Vector2i(u1, v2))); // Advance to the next character - x += advance; + x += glyph.Advance; } // If we're using the underlined style, add the last line @@ -277,113 +304,14 @@ void Text::Render(RenderTarget&, Renderer& renderer) const float top = y + underlineOffset; float bottom = top + underlineThickness; - renderer.Begin(Renderer::QuadList); - renderer.AddVertex(0, top, underlineLeft, underlineTop); - renderer.AddVertex(x, top, underlineRight, underlineTop); - renderer.AddVertex(x, bottom, underlineRight, underlineBottom); - renderer.AddVertex(0, bottom, underlineLeft, underlineBottom); - renderer.End(); - } -} - - -//////////////////////////////////////////////////////////// -void Text::UpdateRect() const -{ - if (myRectUpdated) - return; - - // Reset the previous states - myRectUpdated = true; - myBaseRect = FloatRect(0, 0, 0, 0); - - // No text or not font: empty box - if (!myFont || myString.IsEmpty()) - return; - - // Initial values - bool bold = (myStyle & Bold) != 0; - float charSize = static_cast(myCharacterSize); - float space = static_cast(myFont->GetGlyph(L' ', myCharacterSize, bold).Advance); - float lineSpacing = static_cast(myFont->GetLineSpacing(myCharacterSize)); - float curWidth = 0; - float curHeight = 0; - float width = 0; - float height = 0; - Uint32 prevChar = 0; - - // Go through each character - for (std::size_t i = 0; i < myString.GetSize(); ++i) - { - Uint32 curChar = myString[i]; - - // Apply the kerning offset - curWidth += static_cast(myFont->GetKerning(prevChar, curChar, myCharacterSize)); - prevChar = curChar; - - // Handle special characters - switch (curChar) - { - case L' ' : - curWidth += space; - continue; - - case L'\t' : - curWidth += space * 4; - continue; - - case L'\v' : - height += lineSpacing * 4; - curHeight = 0; - continue; - - case L'\n' : - height += lineSpacing; - curHeight = 0; - if (curWidth > width) - width = curWidth; - curWidth = 0; - continue; - } - - // Extract the current glyph's description - const Glyph& curGlyph = myFont->GetGlyph(curChar, myCharacterSize, bold); - - // Advance to the next character - curWidth += static_cast(curGlyph.Advance); - - // Update the maximum height - float charHeight = charSize + curGlyph.Bounds.Top + curGlyph.Bounds.Height; - if (charHeight > curHeight) - curHeight = charHeight; + myVertices.Append(Vertex(Vector2f(0, top), myColor, Vector2i(1, 1))); + myVertices.Append(Vertex(Vector2f(x, top), myColor, Vector2i(2, 1))); + myVertices.Append(Vertex(Vector2f(x, bottom), myColor, Vector2i(2, 2))); + myVertices.Append(Vertex(Vector2f(0, bottom), myColor, Vector2i(1, 2))); } - // Update the last line - if (curWidth > width) - width = curWidth; - height += curHeight; - - // Add a slight width if we're using the italic style - if (myStyle & Italic) - { - width += 0.208f * charSize; - } - - // Add a slight height if we're using the underlined style - if (myStyle & Underlined) - { - float underlineOffset = myCharacterSize * 0.1f; - float underlineThickness = myCharacterSize * (bold ? 0.1f : 0.07f); - - if (curHeight < charSize + underlineOffset + underlineThickness) - height += underlineOffset + underlineThickness; - } - - // Finally update the rectangle - myBaseRect.Left = 0; - myBaseRect.Top = 0; - myBaseRect.Width = width; - myBaseRect.Height = height; + // Recompute the bounding rectangle + myBounds = myVertices.GetBounds(); } } // namespace sf diff --git a/src/SFML/Graphics/Texture.cpp b/src/SFML/Graphics/Texture.cpp index e623e325..0c07cf34 100644 --- a/src/SFML/Graphics/Texture.cpp +++ b/src/SFML/Graphics/Texture.cpp @@ -44,6 +44,7 @@ myTextureWidth (0), myTextureHeight(0), myTexture (0), myIsSmooth (false), +myIsRepeated (false), myPixelsFlipped(false) { @@ -59,6 +60,7 @@ myTextureWidth (0), myTextureHeight(0), myTexture (0), myIsSmooth (copy.myIsSmooth), +myIsRepeated (copy.myIsRepeated), myPixelsFlipped(false) { LoadFromImage(copy.CopyToImage()); @@ -124,8 +126,8 @@ bool Texture::Create(unsigned int width, unsigned int height) // Initialize the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); - GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); + GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); @@ -347,9 +349,38 @@ void Texture::Update(const Window& window, unsigned int x, unsigned int y) //////////////////////////////////////////////////////////// -void Texture::Bind() const +void Texture::Bind(CoordinateType coordinateType) const { + // Bind the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); + + // Check if we need to define a special texture matrix + if ((coordinateType == Pixels) || myPixelsFlipped) + { + GLfloat matrix[16] = {1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f}; + + // If non-normalized coordinates (= pixels) are requested, we need to + // setup scale factors that convert the range [0 .. size] to [0 .. 1] + if (coordinateType == Pixels) + { + matrix[0] = 1.f / myTextureWidth; + matrix[5] = 1.f / myTextureHeight; + } + + // If pixels are flipped we must invert the Y axis + if (myPixelsFlipped) + { + matrix[5] = -matrix[5]; + matrix[13] = static_cast(myHeight / myTextureHeight); + } + + // Load the matrix + GLCheck(glMatrixMode(GL_TEXTURE)); + GLCheck(glLoadMatrixf(matrix)); + } } @@ -380,32 +411,28 @@ bool Texture::IsSmooth() const //////////////////////////////////////////////////////////// -FloatRect Texture::GetTexCoords(const IntRect& rect) const +void Texture::SetRepeated(bool repeated) { - if ((myTextureWidth != 0) && (myTextureHeight != 0)) + if (repeated != myIsRepeated) { - float width = static_cast(myTextureWidth); - float height = static_cast(myTextureHeight); + myIsRepeated = repeated; - if (myPixelsFlipped) + if (myTexture) { - return FloatRect( rect.Left / width, - (myHeight - rect.Top) / height, - rect.Width / width, - -rect.Height / height); - } - else - { - return FloatRect(rect.Left / width, - rect.Top / height, - rect.Width / width, - rect.Height / height); + EnsureGlContext(); + + GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); + GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); + GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); } } - else - { - return FloatRect(0, 0, 0, 0); - } +} + + +//////////////////////////////////////////////////////////// +bool Texture::IsRepeated() const +{ + return myIsRepeated; } @@ -432,6 +459,7 @@ Texture& Texture::operator =(const Texture& right) std::swap(myTextureHeight, temp.myTextureHeight); std::swap(myTexture, temp.myTexture); std::swap(myIsSmooth, temp.myIsSmooth); + std::swap(myIsRepeated, temp.myIsRepeated); std::swap(myPixelsFlipped, temp.myPixelsFlipped); return *this; diff --git a/src/SFML/Graphics/Transform.cpp b/src/SFML/Graphics/Transform.cpp new file mode 100644 index 00000000..181e1bbd --- /dev/null +++ b/src/SFML/Graphics/Transform.cpp @@ -0,0 +1,270 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +const Transform Transform::Identity; + + +//////////////////////////////////////////////////////////// +Transform::Transform() +{ + // Identity matrix + myMatrix[0] = 1.f; myMatrix[4] = 0.f; myMatrix[8] = 0.f; myMatrix[12] = 0.f; + myMatrix[1] = 0.f; myMatrix[5] = 1.f; myMatrix[9] = 0.f; myMatrix[13] = 0.f; + myMatrix[2] = 0.f; myMatrix[6] = 0.f; myMatrix[10] = 1.f; myMatrix[14] = 0.f; + myMatrix[3] = 0.f; myMatrix[7] = 0.f; myMatrix[11] = 0.f; myMatrix[15] = 1.f; +} + + +//////////////////////////////////////////////////////////// +Transform::Transform(float a00, float a01, float a02, + float a10, float a11, float a12, + float a20, float a21, float a22) +{ + myMatrix[0] = a00; myMatrix[4] = a01; myMatrix[8] = 0.f; myMatrix[12] = a02; + myMatrix[1] = a10; myMatrix[5] = a11; myMatrix[9] = 0.f; myMatrix[13] = a12; + myMatrix[2] = 0.f; myMatrix[6] = 0.f; myMatrix[10] = 1.f; myMatrix[14] = 0.f; + myMatrix[3] = a20; myMatrix[7] = a21; myMatrix[11] = 0.f; myMatrix[15] = a22; +} + + +//////////////////////////////////////////////////////////// +const float* Transform::GetMatrix() const +{ + return myMatrix; +} + + +//////////////////////////////////////////////////////////// +Transform Transform::GetInverse() const +{ + // Compute the determinant + float det = myMatrix[0] * (myMatrix[15] * myMatrix[5] - myMatrix[7] * myMatrix[13]) - + myMatrix[1] * (myMatrix[15] * myMatrix[4] - myMatrix[7] * myMatrix[12]) + + myMatrix[3] * (myMatrix[13] * myMatrix[4] - myMatrix[5] * myMatrix[12]); + + // Compute the inverse if the determinant is not zero + // (don't use an epsilon because the determinant may *really* be tiny) + if (det != 0.f) + { + return Transform( (myMatrix[15] * myMatrix[5] - myMatrix[7] * myMatrix[13]) / det, + -(myMatrix[15] * myMatrix[4] - myMatrix[7] * myMatrix[12]) / det, + (myMatrix[13] * myMatrix[4] - myMatrix[5] * myMatrix[12]) / det, + -(myMatrix[15] * myMatrix[1] - myMatrix[3] * myMatrix[13]) / det, + (myMatrix[15] * myMatrix[0] - myMatrix[3] * myMatrix[12]) / det, + -(myMatrix[13] * myMatrix[0] - myMatrix[1] * myMatrix[12]) / det, + (myMatrix[7] * myMatrix[1] - myMatrix[3] * myMatrix[5]) / det, + -(myMatrix[7] * myMatrix[0] - myMatrix[3] * myMatrix[4]) / det, + (myMatrix[5] * myMatrix[0] - myMatrix[1] * myMatrix[4]) / det); + } + else + { + return Transform(); + } +} + + +//////////////////////////////////////////////////////////// +Vector2f Transform::TransformPoint(float x, float y) const +{ + return Vector2f(myMatrix[0] * x + myMatrix[4] * y + myMatrix[12], + myMatrix[1] * x + myMatrix[5] * y + myMatrix[13]); +} + + +//////////////////////////////////////////////////////////// +Vector2f Transform::TransformPoint(const Vector2f& point) const +{ + return TransformPoint(point.x, point.y); +} + + +//////////////////////////////////////////////////////////// +FloatRect Transform::TransformRect(const FloatRect& rectangle) const +{ + // Transform the 4 corners of the rectangle + const Vector2f points[] = + { + TransformPoint(rectangle.Left, rectangle.Top), + TransformPoint(rectangle.Left, rectangle.Top + rectangle.Height), + TransformPoint(rectangle.Left + rectangle.Width, rectangle.Top), + TransformPoint(rectangle.Left + rectangle.Width, rectangle.Top + rectangle.Height) + }; + + // Compute the bounding rectangle of the transformed points + float left = points[0].x; + float top = points[0].y; + float right = points[0].x; + float bottom = points[0].y; + for (int i = 1; i < 4; ++i) + { + if (points[i].x < left) left = points[i].x; + else if (points[i].x > right) right = points[i].x; + if (points[i].y < top) top = points[i].y; + else if (points[i].y > bottom) bottom = points[i].y; + } + + return FloatRect(left, top, right - left, bottom - top); +} + + +//////////////////////////////////////////////////////////// +Transform Transform::Combine(const Transform& transform) const +{ + const float* a = myMatrix; + const float* b = transform.myMatrix; + + return Transform(a[0] * b[0] + a[4] * b[1] + a[12] * b[3], + a[0] * b[4] + a[4] * b[5] + a[12] * b[7], + a[0] * b[12] + a[4] * b[13] + a[12] * b[15], + a[1] * b[0] + a[5] * b[1] + a[13] * b[3], + a[1] * b[4] + a[5] * b[5] + a[13] * b[7], + a[1] * b[12] + a[5] * b[13] + a[13] * b[15], + a[3] * b[0] + a[7] * b[1] + a[15] * b[3], + a[3] * b[4] + a[7] * b[5] + a[15] * b[7], + a[3] * b[12] + a[7] * b[13] + a[15] * b[15]); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Translate(float x, float y) +{ + Transform translation(1, 0, x, + 0, 1, y, + 0, 0, 1); + + return *this = Combine(translation); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Translate(const Vector2f& offset) +{ + return Translate(offset.x, offset.y); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Rotate(float angle) +{ + float rad = angle * 3.141592654f / 180.f; + float cos = std::cos(rad); + float sin = std::sin(rad); + + Transform rotation(cos, -sin, 0, + sin, cos, 0, + 0, 0, 1); + + return *this = Combine(rotation); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Rotate(float angle, float centerX, float centerY) +{ + float rad = angle * 3.141592654f / 180.f; + float cos = std::cos(rad); + float sin = std::sin(rad); + + Transform rotation(cos, -sin, centerX * (1 - cos) + centerY * sin, + sin, cos, centerY * (1 - cos) - centerX * sin, + 0, 0, 1); + + return *this = Combine(rotation); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Rotate(float angle, const Vector2f& center) +{ + return Rotate(angle, center.x, center.y); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Scale(float scaleX, float scaleY) +{ + Transform scaling(scaleX, 0, 0, + 0, scaleY, 0, + 0, 0, 1); + + return *this = Combine(scaling); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Scale(float scaleX, float scaleY, float centerX, float centerY) +{ + Transform scaling(scaleX, 0, centerX * (1 - scaleX), + 0, scaleY, centerY * (1 - scaleY), + 0, 0, 1); + + return *this = Combine(scaling); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Scale(const Vector2f& factors) +{ + return Scale(factors.x, factors.y); +} + + +//////////////////////////////////////////////////////////// +Transform& Transform::Scale(const Vector2f& factors, const Vector2f& center) +{ + return Scale(factors.x, factors.y, center.x, center.y); +} + + +//////////////////////////////////////////////////////////// +Transform operator *(const Transform& left, const Transform& right) +{ + return left.Combine(right); +} + + +//////////////////////////////////////////////////////////// +Transform& operator *=(Transform& left, const Transform& right) +{ + return left = left.Combine(right); +} + + +//////////////////////////////////////////////////////////// +Vector2f operator *(const Transform& left, const Vector2f& right) +{ + return left.TransformPoint(right); +} + +} // namespace sf diff --git a/src/SFML/Graphics/Transformable.cpp b/src/SFML/Graphics/Transformable.cpp new file mode 100644 index 00000000..31130bac --- /dev/null +++ b/src/SFML/Graphics/Transformable.cpp @@ -0,0 +1,210 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +Transformable::Transformable() : +myOrigin (0, 0), +myPosition (0, 0), +myRotation (0), +myScale (1, 1), +myTransform (), +myTransformNeedUpdate (true), +myInverseTransform (), +myInverseTransformNeedUpdate(true) +{ +} + + +//////////////////////////////////////////////////////////// +void Transformable::SetPosition(float x, float y) +{ + myPosition.x = x; + myPosition.y = y; + myTransformNeedUpdate = true; + myInverseTransformNeedUpdate = true; +} + + +//////////////////////////////////////////////////////////// +void Transformable::SetPosition(const Vector2f& position) +{ + SetPosition(position.x, position.y); +} + + +//////////////////////////////////////////////////////////// +void Transformable::SetRotation(float angle) +{ + myRotation = angle; + myTransformNeedUpdate = true; + myInverseTransformNeedUpdate = true; +} + + +//////////////////////////////////////////////////////////// +void Transformable::SetScale(float factorX, float factorY) +{ + myScale.x = factorX; + myScale.y = factorY; + myTransformNeedUpdate = true; + myInverseTransformNeedUpdate = true; +} + + +//////////////////////////////////////////////////////////// +void Transformable::SetScale(const Vector2f& factors) +{ + SetScale(factors.x, factors.y); +} + + +//////////////////////////////////////////////////////////// +void Transformable::SetOrigin(float x, float y) +{ + myOrigin.x = x; + myOrigin.y = y; + myTransformNeedUpdate = true; + myInverseTransformNeedUpdate = true; +} + + +//////////////////////////////////////////////////////////// +void Transformable::SetOrigin(const Vector2f& origin) +{ + SetOrigin(origin.x, origin.y); +} + + +//////////////////////////////////////////////////////////// +const Vector2f& Transformable::GetPosition() const +{ + return myPosition; +} + + +//////////////////////////////////////////////////////////// +float Transformable::GetRotation() const +{ + return myRotation; +} + + +//////////////////////////////////////////////////////////// +const Vector2f& Transformable::GetScale() const +{ + return myScale; +} + + +//////////////////////////////////////////////////////////// +const Vector2f& Transformable::GetOrigin() const +{ + return myOrigin; +} + + +//////////////////////////////////////////////////////////// +void Transformable::Move(float offsetX, float offsetY) +{ + SetPosition(myPosition.x + offsetX, myPosition.y + offsetY); +} + + +//////////////////////////////////////////////////////////// +void Transformable::Move(const Vector2f& offset) +{ + SetPosition(myPosition.x + offset.x, myPosition.y + offset.y); +} + + +//////////////////////////////////////////////////////////// +void Transformable::Rotate(float angle) +{ + SetRotation(myRotation + angle); +} + + +//////////////////////////////////////////////////////////// +void Transformable::Scale(float factorX, float factorY) +{ + SetScale(myScale.x * factorX, myScale.y * factorY); +} + + +//////////////////////////////////////////////////////////// +void Transformable::Scale(const Vector2f& factor) +{ + SetScale(myScale.x * factor.x, myScale.y * factor.y); +} + + +//////////////////////////////////////////////////////////// +const Transform& Transformable::GetTransform() const +{ + // Recompute the combined transform if needed + if (myTransformNeedUpdate) + { + float angle = -myRotation * 3.141592654f / 180.f; + float cosine = static_cast(std::cos(angle)); + float sine = static_cast(std::sin(angle)); + float sxc = myScale.x * cosine; + float syc = myScale.y * cosine; + float sxs = myScale.x * sine; + float sys = myScale.y * sine; + float tx = -myOrigin.x * sxc - myOrigin.y * sys + myPosition.x; + float ty = myOrigin.x * sxs - myOrigin.y * syc + myPosition.y; + + myTransform = Transform( sxc, sys, tx, + -sxs, syc, ty, + 0.f, 0.f, 1.f); + myTransformNeedUpdate = false; + } + + return myTransform; +} + + +//////////////////////////////////////////////////////////// +const Transform& Transformable::GetInverseTransform() const +{ + // Recompute the inverse transform if needed + if (myInverseTransformNeedUpdate) + { + myInverseTransform = GetTransform().GetInverse(); + myInverseTransformNeedUpdate = false; + } + + return myInverseTransform; +} + +} // namespace sf diff --git a/src/SFML/Graphics/Vertex.cpp b/src/SFML/Graphics/Vertex.cpp new file mode 100644 index 00000000..43cf1905 --- /dev/null +++ b/src/SFML/Graphics/Vertex.cpp @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +Vertex::Vertex() : +Position (0, 0), +Color (255, 255, 255), +TexCoords(0, 0) +{ +} + + +//////////////////////////////////////////////////////////// +Vertex::Vertex(const Vector2f& position) : +Position (position), +Color (255, 255, 255), +TexCoords(0, 0) +{ +} + + +//////////////////////////////////////////////////////////// +Vertex::Vertex(const Vector2f& position, const sf::Color& color) : +Position (position), +Color (color), +TexCoords(0, 0) +{ +} + + +//////////////////////////////////////////////////////////// +Vertex::Vertex(const Vector2f& position, const Vector2i& texCoords) : +Position (position), +Color (255, 255, 255), +TexCoords(texCoords) +{ +} + + +//////////////////////////////////////////////////////////// +Vertex::Vertex(const Vector2f& position, const sf::Color& color, const Vector2i& texCoords) : +Position (position), +Color (color), +TexCoords(texCoords) +{ +} + +} // namespace sf diff --git a/src/SFML/Graphics/VertexArray.cpp b/src/SFML/Graphics/VertexArray.cpp new file mode 100644 index 00000000..eeb1d230 --- /dev/null +++ b/src/SFML/Graphics/VertexArray.cpp @@ -0,0 +1,150 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +VertexArray::VertexArray() : +myVertices (), +myPrimitiveType(Points) +{ +} + + +//////////////////////////////////////////////////////////// +VertexArray::VertexArray(PrimitiveType type, unsigned int verticesCount) : +myVertices (verticesCount), +myPrimitiveType(type) +{ +} + + +//////////////////////////////////////////////////////////// +unsigned int VertexArray::GetVerticesCount() const +{ + return myVertices.size(); +} + + +//////////////////////////////////////////////////////////// +Vertex& VertexArray::operator [](unsigned int index) +{ + return myVertices[index]; +} + + +//////////////////////////////////////////////////////////// +const Vertex& VertexArray::operator [](unsigned int index) const +{ + return myVertices[index]; +} + + +//////////////////////////////////////////////////////////// +void VertexArray::Clear() +{ + myVertices.clear(); +} + + +//////////////////////////////////////////////////////////// +void VertexArray::Resize(unsigned int verticesCount) +{ + myVertices.resize(verticesCount); +} + + +//////////////////////////////////////////////////////////// +void VertexArray::Append(const Vertex& vertex) +{ + myVertices.push_back(vertex); +} + + +//////////////////////////////////////////////////////////// +void VertexArray::SetPrimitiveType(PrimitiveType type) +{ + myPrimitiveType = type; +} + + +//////////////////////////////////////////////////////////// +PrimitiveType VertexArray::GetPrimitiveType() const +{ + return myPrimitiveType; +} + + +//////////////////////////////////////////////////////////// +FloatRect VertexArray::GetBounds() const +{ + if (!myVertices.empty()) + { + float left = myVertices[0].Position.x; + float top = myVertices[0].Position.y; + float right = myVertices[0].Position.x; + float bottom = myVertices[0].Position.y; + + for (std::size_t i = 0; i < myVertices.size(); ++i) + { + Vector2f position = myVertices[i].Position; + + // Update left and right + if (position.x < left) + left = position.x; + else if (position.x > right) + right = position.x; + + // Update top and bottom + if (position.y < top) + top = position.y; + else if (position.y > bottom) + bottom = position.y; + } + + return FloatRect(left, top, right - left, bottom - top); + } + else + { + // Array is empty + return FloatRect(); + } +} + + +//////////////////////////////////////////////////////////// +void VertexArray::Draw(RenderTarget& target, RenderStates states) const +{ + if (!myVertices.empty()) + target.Draw(&myVertices[0], myVertices.size(), myPrimitiveType, states); +} + +} // namespace sf diff --git a/src/SFML/Graphics/View.cpp b/src/SFML/Graphics/View.cpp index 7c012ed8..ec95ddba 100644 --- a/src/SFML/Graphics/View.cpp +++ b/src/SFML/Graphics/View.cpp @@ -26,18 +26,19 @@ // Headers //////////////////////////////////////////////////////////// #include +#include namespace sf { //////////////////////////////////////////////////////////// View::View() : -myCenter (), -mySize (), -myRotation (0), -myViewport (0, 0, 1, 1), -myMatrixUpdated (false), -myInvMatrixUpdated(false) +myCenter (), +mySize (), +myRotation (0), +myViewport (0, 0, 1, 1), +myTransformUpdated (false), +myInvTransformUpdated(false) { Reset(FloatRect(0, 0, 1000, 1000)); } @@ -45,12 +46,12 @@ myInvMatrixUpdated(false) //////////////////////////////////////////////////////////// View::View(const FloatRect& rectangle) : -myCenter (), -mySize (), -myRotation (0), -myViewport (0, 0, 1, 1), -myMatrixUpdated (false), -myInvMatrixUpdated(false) +myCenter (), +mySize (), +myRotation (0), +myViewport (0, 0, 1, 1), +myTransformUpdated (false), +myInvTransformUpdated(false) { Reset(rectangle); } @@ -58,12 +59,12 @@ myInvMatrixUpdated(false) //////////////////////////////////////////////////////////// View::View(const Vector2f& center, const Vector2f& size) : -myCenter (center), -mySize (size), -myRotation (0), -myViewport (0, 0, 1, 1), -myMatrixUpdated (false), -myInvMatrixUpdated(false) +myCenter (center), +mySize (size), +myRotation (0), +myViewport (0, 0, 1, 1), +myTransformUpdated (false), +myInvTransformUpdated(false) { } @@ -74,8 +75,8 @@ void View::SetCenter(float x, float y) myCenter.x = x; myCenter.y = y; - myMatrixUpdated = false; - myInvMatrixUpdated = false; + myTransformUpdated = false; + myInvTransformUpdated = false; } @@ -92,8 +93,8 @@ void View::SetSize(float width, float height) mySize.x = width; mySize.y = height; - myMatrixUpdated = false; - myInvMatrixUpdated = false; + myTransformUpdated = false; + myInvTransformUpdated = false; } @@ -111,8 +112,8 @@ void View::SetRotation(float angle) if (myRotation < 0) myRotation += 360.f; - myMatrixUpdated = false; - myInvMatrixUpdated = false; + myTransformUpdated = false; + myInvTransformUpdated = false; } @@ -132,8 +133,8 @@ void View::Reset(const FloatRect& rectangle) mySize.y = rectangle.Height; myRotation = 0; - myMatrixUpdated = false; - myInvMatrixUpdated = false; + myTransformUpdated = false; + myInvTransformUpdated = false; } @@ -194,30 +195,46 @@ void View::Zoom(float factor) //////////////////////////////////////////////////////////// -const Matrix3& View::GetMatrix() const +const Transform& View::GetTransform() const { // Recompute the matrix if needed - if (!myMatrixUpdated) + if (!myTransformUpdated) { - myMatrix = Matrix3::Projection(myCenter, mySize, myRotation); - myMatrixUpdated = true; + // Rotation components + float angle = myRotation * 3.141592654f / 180.f; + float cosine = static_cast(std::cos(angle)); + float sine = static_cast(std::sin(angle)); + float tx = -myCenter.x * cosine - myCenter.y * sine + myCenter.x; + float ty = myCenter.x * sine - myCenter.y * cosine + myCenter.y; + + // Projection components + float a = 2.f / mySize.x; + float b = -2.f / mySize.y; + float c = -a * myCenter.x; + float d = -b * myCenter.y; + + // Rebuild the projection matrix + myTransform = Transform( a * cosine, a * sine, a * tx + c, + -b * sine, b * cosine, b * ty + d, + 0.f, 0.f, 1.f); + myTransformUpdated = true; } - return myMatrix; + return myTransform; } //////////////////////////////////////////////////////////// -const Matrix3& View::GetInverseMatrix() const +const Transform& View::GetInverseTransform() const { // Recompute the matrix if needed - if (!myInvMatrixUpdated) + if (!myInvTransformUpdated) { - myInverseMatrix = GetMatrix().GetInverse(); - myInvMatrixUpdated = true; + myInverseTransform = GetTransform().GetInverse(); + myInvTransformUpdated = true; } - return myInverseMatrix; + return myInverseTransform; } } // namespace sf diff --git a/src/SFML/Window/Context.cpp b/src/SFML/Window/Context.cpp index 961c2508..354b4cdc 100644 --- a/src/SFML/Window/Context.cpp +++ b/src/SFML/Window/Context.cpp @@ -53,6 +53,13 @@ bool Context::SetActive(bool active) } +//////////////////////////////////////////////////////////// +ContextId Context::GetCurrentContextId() +{ + return priv::GlContext::GetCurrentContext(); +} + + //////////////////////////////////////////////////////////// Context::Context(const ContextSettings& settings, unsigned int width, unsigned int height) { diff --git a/src/SFML/Window/GlContext.cpp b/src/SFML/Window/GlContext.cpp index 196ed9c8..51f808ae 100644 --- a/src/SFML/Window/GlContext.cpp +++ b/src/SFML/Window/GlContext.cpp @@ -177,6 +177,13 @@ GlContext* GlContext::New(const ContextSettings& settings, unsigned int width, u } +//////////////////////////////////////////////////////////// +GlContext* GlContext::GetCurrentContext() +{ + return currentContext; +} + + //////////////////////////////////////////////////////////// GlContext::~GlContext() { diff --git a/src/SFML/Window/GlContext.hpp b/src/SFML/Window/GlContext.hpp index f6eb193a..015ef72a 100644 --- a/src/SFML/Window/GlContext.hpp +++ b/src/SFML/Window/GlContext.hpp @@ -118,6 +118,16 @@ public : //////////////////////////////////////////////////////////// static GlContext* New(const ContextSettings& settings, unsigned int width, unsigned int height); + //////////////////////////////////////////////////////////// + /// \brief Return the current active context + /// + /// This function returns 0 if no context is active. + /// + /// \return Context currently active in this thread + /// + //////////////////////////////////////////////////////////// + static GlContext* GetCurrentContext(); + public : //////////////////////////////////////////////////////////// From 6034b80ddfa2c90f40b4f9e5c1aa48798ce4eebf Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sat, 3 Dec 2011 14:24:26 +0100 Subject: [PATCH 02/21] sf::ConvexShape was not updated when SetPointsCount or SetPoint was called --- src/SFML/Graphics/ConvexShape.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SFML/Graphics/ConvexShape.cpp b/src/SFML/Graphics/ConvexShape.cpp index 222d5367..44693c24 100644 --- a/src/SFML/Graphics/ConvexShape.cpp +++ b/src/SFML/Graphics/ConvexShape.cpp @@ -45,6 +45,7 @@ ConvexShape::ConvexShape() void ConvexShape::SetPointsCount(unsigned int count) { myPoints.resize(count); + Update(); } @@ -59,6 +60,7 @@ unsigned int ConvexShape::GetPointsCount() const void ConvexShape::SetPoint(unsigned int index, const Vector2f& point) { myPoints[index] = point; + Update(); } From 44cc9bad8489e51463c2390115733fd1b67fb821 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 4 Dec 2011 10:53:14 +0100 Subject: [PATCH 03/21] Added constructors with parameters for shapes, and default-constructed shapes are now always empty --- include/SFML/Graphics/CircleShape.hpp | 6 ++---- include/SFML/Graphics/ConvexShape.hpp | 4 ---- include/SFML/Graphics/RectangleShape.hpp | 6 ++---- include/SFML/Graphics/StarShape.hpp | 19 ++++++++++--------- src/SFML/Graphics/CircleShape.cpp | 4 ++-- src/SFML/Graphics/ConvexShape.cpp | 9 +++------ src/SFML/Graphics/RectangleShape.cpp | 4 ++-- src/SFML/Graphics/StarShape.cpp | 17 ++++++++++++++--- 8 files changed, 35 insertions(+), 34 deletions(-) diff --git a/include/SFML/Graphics/CircleShape.hpp b/include/SFML/Graphics/CircleShape.hpp index fb181948..681e6610 100644 --- a/include/SFML/Graphics/CircleShape.hpp +++ b/include/SFML/Graphics/CircleShape.hpp @@ -44,16 +44,14 @@ public : //////////////////////////////////////////////////////////// /// \brief Default constructor /// - /// Creates a circle with a radius of 10. + /// \param radius Radius of the circle /// //////////////////////////////////////////////////////////// - CircleShape(); + CircleShape(float radius = 0); //////////////////////////////////////////////////////////// /// \brief Set the radius of the circle /// - /// The default radius of a circle is 10. - /// /// \param radius New radius of the circle /// /// \see GetRadius diff --git a/include/SFML/Graphics/ConvexShape.hpp b/include/SFML/Graphics/ConvexShape.hpp index ac0d5be8..d84bdfaf 100644 --- a/include/SFML/Graphics/ConvexShape.hpp +++ b/include/SFML/Graphics/ConvexShape.hpp @@ -45,16 +45,12 @@ public : //////////////////////////////////////////////////////////// /// \brief Default constructor /// - /// Creates a default triangle, just so that it's not empty. - /// //////////////////////////////////////////////////////////// ConvexShape(); //////////////////////////////////////////////////////////// /// \brief Set the number of points of the polygon /// - /// The default number of points of a polygon is 6. - /// /// \param count New number of points of the polygon /// /// \see GetPointsCount diff --git a/include/SFML/Graphics/RectangleShape.hpp b/include/SFML/Graphics/RectangleShape.hpp index 0e9393f9..907c3a7d 100644 --- a/include/SFML/Graphics/RectangleShape.hpp +++ b/include/SFML/Graphics/RectangleShape.hpp @@ -44,16 +44,14 @@ public : //////////////////////////////////////////////////////////// /// \brief Default constructor /// - /// Creates a 10x10 rectangle. + /// \param size Size of the rectangle /// //////////////////////////////////////////////////////////// - RectangleShape(); + RectangleShape(const Vector2f& size = Vector2f(0, 0)); //////////////////////////////////////////////////////////// /// \brief Set the size of the rectangle /// - /// The default size of a rectangle is 10x10. - /// /// \param size New size of the rectangle /// /// \see GetSize diff --git a/include/SFML/Graphics/StarShape.hpp b/include/SFML/Graphics/StarShape.hpp index c906a5b8..90ef0937 100644 --- a/include/SFML/Graphics/StarShape.hpp +++ b/include/SFML/Graphics/StarShape.hpp @@ -44,16 +44,21 @@ public : //////////////////////////////////////////////////////////// /// \brief Default constructor /// - /// Creates a star with 5 points, an inner radius of 10 and - /// an outer radius of 20. - /// //////////////////////////////////////////////////////////// StarShape(); //////////////////////////////////////////////////////////// - /// \brief Set the inner radius of the star + /// \brief Constructor /// - /// The default inner radius of a star is 10. + /// \param innerRadius Inner radius of the star + /// \param outerRadius Outer radius of the star + /// \param pointsCount Number of points of the star + /// + //////////////////////////////////////////////////////////// + StarShape(float innerRadius, float outerRadius, unsigned int pointsCount); + + //////////////////////////////////////////////////////////// + /// \brief Set the inner radius of the star /// /// \param radius New inner radius of the star /// @@ -75,8 +80,6 @@ public : //////////////////////////////////////////////////////////// /// \brief Set the outer radius of the star /// - /// The default outer radius of a star is 20. - /// /// \param radius New outer radius of the star /// /// \see GetOuterRadius @@ -97,8 +100,6 @@ public : //////////////////////////////////////////////////////////// /// \brief Set the number of points of the star /// - /// The default number of points of a star is 5. - /// /// \param count New number of points of the star /// /// \see GetPointsCount diff --git a/src/SFML/Graphics/CircleShape.cpp b/src/SFML/Graphics/CircleShape.cpp index 86a6a3cb..4830e71f 100644 --- a/src/SFML/Graphics/CircleShape.cpp +++ b/src/SFML/Graphics/CircleShape.cpp @@ -32,9 +32,9 @@ namespace sf { //////////////////////////////////////////////////////////// -CircleShape::CircleShape() : -myRadius(10) +CircleShape::CircleShape(float radius) { + SetRadius(radius); } diff --git a/src/SFML/Graphics/ConvexShape.cpp b/src/SFML/Graphics/ConvexShape.cpp index 44693c24..93fa5c0c 100644 --- a/src/SFML/Graphics/ConvexShape.cpp +++ b/src/SFML/Graphics/ConvexShape.cpp @@ -31,13 +31,10 @@ namespace sf { //////////////////////////////////////////////////////////// -ConvexShape::ConvexShape() +ConvexShape::ConvexShape() : +myPoints() { - // Let's define a triangle by default... just so that it's not empty - SetPointsCount(3); - SetPoint(0, Vector2f(5, 0)); - SetPoint(1, Vector2f(0, 10)); - SetPoint(2, Vector2f(10, 10)); + Update(); } diff --git a/src/SFML/Graphics/RectangleShape.cpp b/src/SFML/Graphics/RectangleShape.cpp index ba8d0ebf..ea80e69e 100644 --- a/src/SFML/Graphics/RectangleShape.cpp +++ b/src/SFML/Graphics/RectangleShape.cpp @@ -32,9 +32,9 @@ namespace sf { //////////////////////////////////////////////////////////// -RectangleShape::RectangleShape() : -mySize(10, 10) +RectangleShape::RectangleShape(const Vector2f& size) : { + SetSize(size); } diff --git a/src/SFML/Graphics/StarShape.cpp b/src/SFML/Graphics/StarShape.cpp index 0f5696ef..9cf5c625 100644 --- a/src/SFML/Graphics/StarShape.cpp +++ b/src/SFML/Graphics/StarShape.cpp @@ -33,10 +33,21 @@ namespace sf { //////////////////////////////////////////////////////////// StarShape::StarShape() : -myInnerRadius(10), -myOuterRadius(20), -myPointsCount(5) +myInnerRadius(0), +myOuterRadius(0), +myPointsCount(0) { + Update(); +} + + +//////////////////////////////////////////////////////////// +StarShape::StarShape(float innerRadius, float outerRadius, unsigned int pointsCount) : +myInnerRadius(innerRadius), +myOuterRadius(outerRadius), +myPointsCount(pointsCount) +{ + Update(); } From 7deec641ac5d015230b4739b8593b0dd0898bcdf Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 4 Dec 2011 11:22:46 +0100 Subject: [PATCH 04/21] Fixed compile error caused by previous commit --- src/SFML/Graphics/RectangleShape.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SFML/Graphics/RectangleShape.cpp b/src/SFML/Graphics/RectangleShape.cpp index ea80e69e..3b9ffd77 100644 --- a/src/SFML/Graphics/RectangleShape.cpp +++ b/src/SFML/Graphics/RectangleShape.cpp @@ -32,7 +32,7 @@ namespace sf { //////////////////////////////////////////////////////////// -RectangleShape::RectangleShape(const Vector2f& size) : +RectangleShape::RectangleShape(const Vector2f& size) { SetSize(size); } From 0548b226efa0f1a2bb5af3215a500040c8267338 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 4 Dec 2011 11:25:20 +0100 Subject: [PATCH 05/21] sf::Shape now outputs less warnings about the number of points --- src/SFML/Graphics/ConvexShape.cpp | 4 +--- src/SFML/Graphics/Shape.cpp | 1 - src/SFML/Graphics/StarShape.cpp | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/SFML/Graphics/ConvexShape.cpp b/src/SFML/Graphics/ConvexShape.cpp index 93fa5c0c..9367d4b1 100644 --- a/src/SFML/Graphics/ConvexShape.cpp +++ b/src/SFML/Graphics/ConvexShape.cpp @@ -31,10 +31,8 @@ namespace sf { //////////////////////////////////////////////////////////// -ConvexShape::ConvexShape() : -myPoints() +ConvexShape::ConvexShape() { - Update(); } diff --git a/src/SFML/Graphics/Shape.cpp b/src/SFML/Graphics/Shape.cpp index 9e17e439..14688c85 100644 --- a/src/SFML/Graphics/Shape.cpp +++ b/src/SFML/Graphics/Shape.cpp @@ -172,7 +172,6 @@ void Shape::Update() unsigned int count = GetOutlinePointsCount(); if (count < 3) { - sf::Err() << "Invalid shape: it has less than 3 points" << std::endl; myVertices.Resize(0); myOutlineVertices.Resize(0); return; diff --git a/src/SFML/Graphics/StarShape.cpp b/src/SFML/Graphics/StarShape.cpp index 9cf5c625..138b0944 100644 --- a/src/SFML/Graphics/StarShape.cpp +++ b/src/SFML/Graphics/StarShape.cpp @@ -37,7 +37,6 @@ myInnerRadius(0), myOuterRadius(0), myPointsCount(0) { - Update(); } From a2fcb758763a68c765f8ec6896abca303f32e1d5 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 4 Dec 2011 20:37:08 +0100 Subject: [PATCH 06/21] Fixed compile errors in sf::Vertex with LLVM GCC 4.2 on OS X Lion --- include/SFML/Graphics/Vertex.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SFML/Graphics/Vertex.hpp b/include/SFML/Graphics/Vertex.hpp index 21ff96ea..f8a09119 100644 --- a/include/SFML/Graphics/Vertex.hpp +++ b/include/SFML/Graphics/Vertex.hpp @@ -67,7 +67,7 @@ public : /// \param color Vertex color /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& position, const Color& color); + Vertex(const Vector2f& position, const sf::Color& color); //////////////////////////////////////////////////////////// /// \brief Construct the vertex from its position and texture coordinates @@ -88,7 +88,7 @@ public : /// \param texCoords Vertex texture coordinates /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& position, const Color& color, const Vector2i& texCoords); + Vertex(const Vector2f& position, const sf::Color& color, const Vector2i& texCoords); //////////////////////////////////////////////////////////// // Member data From 6381d10d4074b9e707a8973a9a8eb7bae2845edb Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 4 Dec 2011 20:43:28 +0100 Subject: [PATCH 07/21] Made one-arg constructors explicit in shapes classes --- include/SFML/Graphics/CircleShape.hpp | 2 +- include/SFML/Graphics/ConvexShape.hpp | 4 +++- include/SFML/Graphics/RectangleShape.hpp | 2 +- src/SFML/Graphics/ConvexShape.cpp | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/SFML/Graphics/CircleShape.hpp b/include/SFML/Graphics/CircleShape.hpp index 681e6610..cb0ffed0 100644 --- a/include/SFML/Graphics/CircleShape.hpp +++ b/include/SFML/Graphics/CircleShape.hpp @@ -47,7 +47,7 @@ public : /// \param radius Radius of the circle /// //////////////////////////////////////////////////////////// - CircleShape(float radius = 0); + explicit CircleShape(float radius = 0); //////////////////////////////////////////////////////////// /// \brief Set the radius of the circle diff --git a/include/SFML/Graphics/ConvexShape.hpp b/include/SFML/Graphics/ConvexShape.hpp index d84bdfaf..ed3ccd39 100644 --- a/include/SFML/Graphics/ConvexShape.hpp +++ b/include/SFML/Graphics/ConvexShape.hpp @@ -45,8 +45,10 @@ public : //////////////////////////////////////////////////////////// /// \brief Default constructor /// + /// \param pointsCount Number of points of the polygon + /// //////////////////////////////////////////////////////////// - ConvexShape(); + explicit ConvexShape(unsigned int pointsCount = 0); //////////////////////////////////////////////////////////// /// \brief Set the number of points of the polygon diff --git a/include/SFML/Graphics/RectangleShape.hpp b/include/SFML/Graphics/RectangleShape.hpp index 907c3a7d..48e9f5d4 100644 --- a/include/SFML/Graphics/RectangleShape.hpp +++ b/include/SFML/Graphics/RectangleShape.hpp @@ -47,7 +47,7 @@ public : /// \param size Size of the rectangle /// //////////////////////////////////////////////////////////// - RectangleShape(const Vector2f& size = Vector2f(0, 0)); + explicit RectangleShape(const Vector2f& size = Vector2f(0, 0)); //////////////////////////////////////////////////////////// /// \brief Set the size of the rectangle diff --git a/src/SFML/Graphics/ConvexShape.cpp b/src/SFML/Graphics/ConvexShape.cpp index 9367d4b1..6c6954f1 100644 --- a/src/SFML/Graphics/ConvexShape.cpp +++ b/src/SFML/Graphics/ConvexShape.cpp @@ -31,8 +31,9 @@ namespace sf { //////////////////////////////////////////////////////////// -ConvexShape::ConvexShape() +ConvexShape::ConvexShape(unsigned int pointsCount) { + SetPointsCount(pointsCount); } From 528961c9674a6040e5f5b3e0c30da8dc785b7c68 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 4 Dec 2011 20:59:26 +0100 Subject: [PATCH 08/21] Fixed bug in sf::Shape --- src/SFML/Graphics/Shape.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SFML/Graphics/Shape.cpp b/src/SFML/Graphics/Shape.cpp index 14688c85..a238cbfd 100644 --- a/src/SFML/Graphics/Shape.cpp +++ b/src/SFML/Graphics/Shape.cpp @@ -186,6 +186,7 @@ void Shape::Update() myVertices[count + 1].Position = myVertices[1].Position; // Update the bounding rectangle + myVertices[0] = myVertices[1]; // so that the result of GetBounds() is correct myInsideBounds = myVertices.GetBounds(); // Compute the center and make it the first vertex From f381260136691969ca6b530092ea269882326da2 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 4 Dec 2011 21:06:29 +0100 Subject: [PATCH 09/21] Removed references to the old sf::Renderer class --- include/SFML/Graphics/Shader.hpp | 4 ---- include/SFML/Graphics/Texture.hpp | 2 -- 2 files changed, 6 deletions(-) diff --git a/include/SFML/Graphics/Shader.hpp b/include/SFML/Graphics/Shader.hpp index 94ce1115..e91d538c 100644 --- a/include/SFML/Graphics/Shader.hpp +++ b/include/SFML/Graphics/Shader.hpp @@ -39,8 +39,6 @@ namespace sf { -class Renderer; - //////////////////////////////////////////////////////////// /// \brief Pixel/fragment shader class /// @@ -352,8 +350,6 @@ public : private : - friend class Renderer; - //////////////////////////////////////////////////////////// /// \brief Create the program and attach the shaders /// diff --git a/include/SFML/Graphics/Texture.hpp b/include/SFML/Graphics/Texture.hpp index 3ad0795d..c500ab5f 100644 --- a/include/SFML/Graphics/Texture.hpp +++ b/include/SFML/Graphics/Texture.hpp @@ -35,7 +35,6 @@ namespace sf { class Window; -class Renderer; class RenderTexture; class InputStream; @@ -467,7 +466,6 @@ public : private : - friend class Renderer; friend class RenderTexture; //////////////////////////////////////////////////////////// From 6ff27baf547e64c23ab3f8701bafe532d440cf90 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Mon, 5 Dec 2011 17:57:45 +0100 Subject: [PATCH 10/21] Removed wrong comment about scale factors in sf::Transformable --- include/SFML/Graphics/Transformable.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/SFML/Graphics/Transformable.hpp b/include/SFML/Graphics/Transformable.hpp index d20a16a2..91a0d498 100644 --- a/include/SFML/Graphics/Transformable.hpp +++ b/include/SFML/Graphics/Transformable.hpp @@ -93,8 +93,6 @@ public : //////////////////////////////////////////////////////////// /// \brief Set the scale factors of the object /// - /// \a factorX and \a factorY must be strictly positive, - /// otherwise they are ignored. /// This function completely overwrites the previous scale. /// See Scale to add a factor based on the previous scale instead. /// The default scale of a transformable object is (1, 1). @@ -110,8 +108,6 @@ public : //////////////////////////////////////////////////////////// /// \brief Set the scale factors of the object /// - /// \a scale.x and \a scale.y must be strictly positive, - /// otherwise they are ignored. /// This function completely overwrites the previous scale. /// See Scale to add a factor based on the previous scale instead. /// The default scale of a transformable object is (1, 1). From 4d0a6a299aef504189cc6b5faa508d75ae963abe Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Tue, 6 Dec 2011 18:08:37 +0100 Subject: [PATCH 11/21] Fixed typo --- src/SFML/Graphics/Shape.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SFML/Graphics/Shape.cpp b/src/SFML/Graphics/Shape.cpp index a238cbfd..3a0e87cb 100644 --- a/src/SFML/Graphics/Shape.cpp +++ b/src/SFML/Graphics/Shape.cpp @@ -125,7 +125,7 @@ const Color& Shape::GetOutlineColor() const void Shape::SetOutlineThickness(float thickness) { myOutlineThickness = thickness; - Update(); // recompute everything because the whole shape must be offseted + Update(); // recompute everything because the whole shape must be offset } From c9b87ec8a907023284d16a69956dcf7ef428b342 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sat, 10 Dec 2011 13:02:38 +0100 Subject: [PATCH 12/21] Added support for vertex shaders in sf::Shader Rewrote the Shader example --- examples/shader/CMakeLists.txt | 4 +- examples/shader/Effect.hpp | 74 +++ examples/shader/Shader.cpp | 489 +++++++++++------- examples/shader/resources/blink.frag | 9 + .../shader/resources/{blur.sfx => blur.frag} | 10 +- examples/shader/resources/colorize.sfx | 11 - examples/shader/resources/devices.png | Bin 0 -> 51410 bytes .../shader/resources/{edge.sfx => edge.frag} | 11 +- examples/shader/resources/fisheye.sfx | 13 - examples/shader/resources/nothing.sfx | 6 - .../resources/{pixelate.sfx => pixelate.frag} | 5 +- examples/shader/resources/sfml.png | Bin 0 -> 25973 bytes examples/shader/resources/sprite.png | Bin 6241 -> 0 bytes examples/shader/resources/storm.vert | 19 + examples/shader/resources/text-background.png | Bin 0 -> 745 bytes examples/shader/resources/wave.jpg | Bin 23249 -> 0 bytes examples/shader/resources/wave.sfx | 12 - examples/shader/resources/wave.vert | 15 + include/SFML/Graphics/Shader.hpp | 384 +++++++++----- src/SFML/Graphics/Shader.cpp | 317 ++++++++---- 20 files changed, 893 insertions(+), 486 deletions(-) create mode 100644 examples/shader/Effect.hpp create mode 100644 examples/shader/resources/blink.frag rename examples/shader/resources/{blur.sfx => blur.frag} (73%) delete mode 100644 examples/shader/resources/colorize.sfx create mode 100644 examples/shader/resources/devices.png rename examples/shader/resources/{edge.sfx => edge.frag} (81%) delete mode 100644 examples/shader/resources/fisheye.sfx delete mode 100644 examples/shader/resources/nothing.sfx rename examples/shader/resources/{pixelate.sfx => pixelate.frag} (63%) create mode 100644 examples/shader/resources/sfml.png delete mode 100644 examples/shader/resources/sprite.png create mode 100644 examples/shader/resources/storm.vert create mode 100644 examples/shader/resources/text-background.png delete mode 100644 examples/shader/resources/wave.jpg delete mode 100644 examples/shader/resources/wave.sfx create mode 100644 examples/shader/resources/wave.vert diff --git a/examples/shader/CMakeLists.txt b/examples/shader/CMakeLists.txt index 5b610d4d..acb1bac8 100644 --- a/examples/shader/CMakeLists.txt +++ b/examples/shader/CMakeLists.txt @@ -2,7 +2,9 @@ set(SRCROOT ${PROJECT_SOURCE_DIR}/examples/shader) # all source files -set(SRC ${SRCROOT}/Shader.cpp) +set(SRC + ${SRCROOT}/Effect.hpp + ${SRCROOT}/Shader.cpp) # define the shader target sfml_add_example(shader GUI_APP diff --git a/examples/shader/Effect.hpp b/examples/shader/Effect.hpp new file mode 100644 index 00000000..cb5b3668 --- /dev/null +++ b/examples/shader/Effect.hpp @@ -0,0 +1,74 @@ +#ifndef EFFECT_HPP +#define EFFECT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +//////////////////////////////////////////////////////////// +// Base class for effects +//////////////////////////////////////////////////////////// +class Effect : public sf::Drawable +{ +public : + + virtual ~Effect() + { + } + + const std::string& GetName() const + { + return myName; + } + + void Load() + { + myIsLoaded = sf::Shader::IsAvailable() && OnLoad(); + } + + void Update(float time, float x, float y) + { + if (myIsLoaded) + OnUpdate(time, x, y); + } + + void Draw(sf::RenderTarget& target, sf::RenderStates states) const + { + if (myIsLoaded) + { + OnDraw(target, states); + } + else + { + sf::Text error("Shader not\nsupported"); + error.SetPosition(320.f, 200.f); + error.SetCharacterSize(36); + target.Draw(error, states); + } + } + +protected : + + Effect(const std::string& name) : + myName(name), + myIsLoaded(false) + { + } + +private : + + // Virtual functions to be implemented in derived effects + virtual bool OnLoad() = 0; + virtual void OnUpdate(float time, float x, float y) = 0; + virtual void OnDraw(sf::RenderTarget& target, sf::RenderStates states) const = 0; + +private : + + std::string myName; + bool myIsLoaded; +}; + +#endif // EFFECT_HPP diff --git a/examples/shader/Shader.cpp b/examples/shader/Shader.cpp index e8456fd4..4451c301 100644 --- a/examples/shader/Shader.cpp +++ b/examples/shader/Shader.cpp @@ -2,71 +2,257 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include "Effect.hpp" #include -#include +#include #include -void DisplayError(); - //////////////////////////////////////////////////////////// -/// A class to simplify shader selection -/// +// "Pixelate" fragment shader //////////////////////////////////////////////////////////// -class ShaderSelector +class Pixelate : public Effect { public : - // Constructor - ShaderSelector(std::map& owner, const std::string& shader) : - myOwner (&owner), - myIterator(owner.find(shader)) + Pixelate() : + Effect("pixelate") { } - // Select the previous shader - void GotoPrevious() + bool OnLoad() { - if (myIterator == myOwner->begin()) - myIterator = myOwner->end(); - myIterator--; + // Load the texture and initialize the sprite + if (!myTexture.LoadFromFile("resources/background.jpg")) + return false; + mySprite.SetTexture(myTexture); + + // Load the shader + if (!myShader.LoadFromFile("resources/pixelate.frag", sf::Shader::Fragment)) + return false; + myShader.SetParameter("texture", sf::Shader::CurrentTexture); + + return true; } - // Select the next shader - void GotoNext() + void OnUpdate(float, float x, float y) { - myIterator++; - if (myIterator == myOwner->end()) - myIterator = myOwner->begin(); + myShader.SetParameter("pixel_threshold", (x + y) / 30); } - // Update the shader parameters - void Update(float x, float y) + void OnDraw(sf::RenderTarget& target, sf::RenderStates states) const { - if (myIterator->first == "blur") myIterator->second.SetParameter("offset", x * y * 0.03f); - else if (myIterator->first == "colorize") myIterator->second.SetParameter("color", 0.3f, x, y); - else if (myIterator->first == "edge") myIterator->second.SetParameter("threshold", x * y); - else if (myIterator->first == "fisheye") myIterator->second.SetParameter("mouse", x, y); - else if (myIterator->first == "wave") myIterator->second.SetParameter("offset", x, y); - else if (myIterator->first == "pixelate") myIterator->second.SetParameter("mouse", x, y); + states.Shader = &myShader; + target.Draw(mySprite, states); } - // Get the name of the current shader - const std::string& GetName() const +private: + + sf::Texture myTexture; + sf::Sprite mySprite; + sf::Shader myShader; +}; + + +//////////////////////////////////////////////////////////// +// "Wave" vertex shader + "blur" fragment shader +//////////////////////////////////////////////////////////// +class WaveBlur : public Effect +{ +public : + + WaveBlur() : + Effect("wave + blur") { - return myIterator->first; } - // Get the current shader - const sf::Shader* GetShader() const + bool OnLoad() { - return &myIterator->second; + // Create the text + myText.SetString("Praesent suscipit augue in velit pulvinar hendrerit varius purus aliquam.\n" + "Mauris mi odio, bibendum quis fringilla a, laoreet vel orci. Proin vitae vulputate tortor.\n" + "Praesent cursus ultrices justo, ut feugiat ante vehicula quis.\n" + "Donec fringilla scelerisque mauris et viverra.\n" + "Maecenas adipiscing ornare scelerisque. Nullam at libero elit.\n" + "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n" + "Nullam leo urna, tincidunt id semper eget, ultricies sed mi.\n" + "Morbi mauris massa, commodo id dignissim vel, lobortis et elit.\n" + "Fusce vel libero sed neque scelerisque venenatis.\n" + "Integer mattis tincidunt quam vitae iaculis.\n" + "Vivamus fringilla sem non velit venenatis fermentum.\n" + "Vivamus varius tincidunt nisi id vehicula.\n" + "Integer ullamcorper, enim vitae euismod rutrum, massa nisl semper ipsum,\n" + "vestibulum sodales sem ante in massa.\n" + "Vestibulum in augue non felis convallis viverra.\n" + "Mauris ultricies dolor sed massa convallis sed aliquet augue fringilla.\n" + "Duis erat eros, porta in accumsan in, blandit quis sem.\n" + "In hac habitasse platea dictumst. Etiam fringilla est id odio dapibus sit amet semper dui laoreet.\n"); + myText.SetCharacterSize(22); + myText.SetPosition(30, 20); + + // Load the shader + //if (!myShader.LoadFromFile("resources/wave.vert", sf::Shader::Vertex)) + if (!myShader.LoadFromFile("resources/wave.vert", "resources/blur.frag")) + return false; + + return true; } -private : + void OnUpdate(float time, float x, float y) + { + myShader.SetParameter("wave_phase", time); + myShader.SetParameter("wave_amplitude", x * 40, y * 40); + myShader.SetParameter("blur_radius", (x + y) * 0.008f); + } - std::map* myOwner; - std::map::iterator myIterator; + void OnDraw(sf::RenderTarget& target, sf::RenderStates states) const + { + states.Shader = &myShader; + target.Draw(myText, states); + } + +private: + + sf::Text myText; + sf::Shader myShader; +}; + + +//////////////////////////////////////////////////////////// +// "Storm" vertex shader + "blink" fragment shader +//////////////////////////////////////////////////////////// +class StormBlink : public Effect +{ +public : + + StormBlink() : + Effect("storm + blink") + { + } + + bool OnLoad() + { + // Create the points + myPoints.SetPrimitiveType(sf::Points); + for (int i = 0; i < 40000; ++i) + { + float x = static_cast(std::rand() % 800); + float y = static_cast(std::rand() % 600); + sf::Uint8 r = std::rand() % 255; + sf::Uint8 g = std::rand() % 255; + sf::Uint8 b = std::rand() % 255; + myPoints.Append(sf::Vertex(sf::Vector2f(x, y), sf::Color(r, g, b))); + } + + // Load the shader + if (!myShader.LoadFromFile("resources/storm.vert", "resources/blink.frag")) + return false; + + return true; + } + + void OnUpdate(float time, float x, float y) + { + float radius = 200 + std::cos(time) * 150; + myShader.SetParameter("storm_position", x * 800, y * 600); + myShader.SetParameter("storm_inner_radius", radius / 3); + myShader.SetParameter("storm_total_radius", radius); + myShader.SetParameter("blink_alpha", 0.5f + std::cos(time * 3) * 0.25f); + } + + void OnDraw(sf::RenderTarget& target, sf::RenderStates states) const + { + states.Shader = &myShader; + target.Draw(myPoints, states); + } + +private: + + sf::VertexArray myPoints; + sf::Shader myShader; +}; + + +//////////////////////////////////////////////////////////// +// "Edge" post-effect fragment shader +//////////////////////////////////////////////////////////// +class Edge : public Effect +{ +public : + + Edge() : + Effect("edge post-effect") + { + } + + bool OnLoad() + { + // Create the off-screen surface + if (!mySurface.Create(800, 600)) + return false; + mySurface.SetSmooth(true); + + // Load the textures + if (!myBackgroundTexture.LoadFromFile("resources/sfml.png")) + return false; + myBackgroundTexture.SetSmooth(true); + if (!myEntityTexture.LoadFromFile("resources/devices.png")) + return false; + myEntityTexture.SetSmooth(true); + + // Initialize the background sprite + myBackgroundSprite.SetTexture(myBackgroundTexture); + myBackgroundSprite.SetPosition(135, 100); + + // Load the moving entities + for (int i = 0; i < 6; ++i) + { + sf::Sprite entity(myEntityTexture, sf::IntRect(96 * i, 0, 96, 96)); + myEntities.push_back(entity); + } + + // Load the shader + if (!myShader.LoadFromFile("resources/edge.frag", sf::Shader::Fragment)) + return false; + myShader.SetParameter("texture", sf::Shader::CurrentTexture); + + return true; + } + + void OnUpdate(float time, float x, float y) + { + myShader.SetParameter("edge_threshold", 1 - (x + y) / 2); + + // Update the position of the moving entities + for (std::size_t i = 0; i < myEntities.size(); ++i) + { + float x = std::cos(0.25f * (time * i + (myEntities.size() - i))) * 300 + 350; + float y = std::sin(0.25f * (time * (myEntities.size() - i) + i)) * 200 + 250; + myEntities[i].SetPosition(x, y); + } + + // Render the updated scene to the off-screen surface + mySurface.Clear(sf::Color::White); + mySurface.Draw(myBackgroundSprite); + for (std::size_t i = 0; i < myEntities.size(); ++i) + mySurface.Draw(myEntities[i]); + mySurface.Display(); + } + + void OnDraw(sf::RenderTarget& target, sf::RenderStates states) const + { + states.Shader = &myShader; + target.Draw(sf::Sprite(mySurface.GetTexture()), states); + } + +private: + + sf::RenderTexture mySurface; + sf::Texture myBackgroundTexture; + sf::Texture myEntityTexture; + sf::Sprite myBackgroundSprite; + std::vector myEntities; + sf::Shader myShader; }; @@ -78,203 +264,110 @@ private : //////////////////////////////////////////////////////////// int main() { - // Check that the system can use shaders - if (sf::Shader::IsAvailable() == false) - { - DisplayError(); - return EXIT_SUCCESS; - } - // Create the main window sf::RenderWindow window(sf::VideoMode(800, 600), "SFML Shader"); + window.EnableVerticalSync(true); - // Create the render texture - sf::RenderTexture texture; - if (!texture.Create(window.GetWidth(), window.GetHeight())) + // Create the effects + std::vector effects; + effects.push_back(new Pixelate); + effects.push_back(new WaveBlur); + effects.push_back(new StormBlink); + effects.push_back(new Edge); + std::size_t current = 0; + + // Initialize them + for (std::size_t i = 0; i < effects.size(); ++i) + effects[i]->Load(); + + // Create the messages background + sf::Texture textBackgroundTexture; + if (!textBackgroundTexture.LoadFromFile("resources/text-background.png")) return EXIT_FAILURE; + sf::Sprite textBackground(textBackgroundTexture); + textBackground.SetPosition(0, 520); + textBackground.SetColor(sf::Color(255, 255, 255, 200)); - // Load a background texture to display - sf::Texture backgroundTexture; - if (!backgroundTexture.LoadFromFile("resources/background.jpg")) - return EXIT_FAILURE; - sf::Sprite background(backgroundTexture); - - // Load a sprite which we'll move into the scene - sf::Texture entityTexture; - if (!entityTexture.LoadFromFile("resources/sprite.png")) - return EXIT_FAILURE; - sf::Sprite entity(entityTexture); - - // Load the text font + // Load the messages font sf::Font font; if (!font.LoadFromFile("resources/sansation.ttf")) return EXIT_FAILURE; - // Load the texture needed for the wave shader - sf::Texture waveTexture; - if (!waveTexture.LoadFromFile("resources/wave.jpg")) - return EXIT_FAILURE; + // Create the description text + sf::Text description("Current effect: " + effects[current]->GetName(), font, 20); + description.SetPosition(10, 530); + description.SetColor(sf::Color(80, 80, 80)); - // Load all shaders - std::map shaders; - if (!shaders["nothing"].LoadFromFile("resources/nothing.sfx")) return EXIT_FAILURE; - if (!shaders["blur"].LoadFromFile("resources/blur.sfx")) return EXIT_FAILURE; - if (!shaders["colorize"].LoadFromFile("resources/colorize.sfx")) return EXIT_FAILURE; - if (!shaders["edge"].LoadFromFile("resources/edge.sfx")) return EXIT_FAILURE; - if (!shaders["fisheye"].LoadFromFile("resources/fisheye.sfx")) return EXIT_FAILURE; - if (!shaders["wave"].LoadFromFile("resources/wave.sfx")) return EXIT_FAILURE; - if (!shaders["pixelate"].LoadFromFile("resources/pixelate.sfx")) return EXIT_FAILURE; - ShaderSelector backgroundShader(shaders, "nothing"); - ShaderSelector entityShader(shaders, "nothing"); - ShaderSelector globalShader(shaders, "nothing"); - - // Do specific initializations - shaders["nothing"].SetCurrentTexture("texture"); - shaders["blur"].SetCurrentTexture("texture"); - shaders["blur"].SetParameter("offset", 0.f); - shaders["colorize"].SetCurrentTexture("texture"); - shaders["colorize"].SetParameter("color", 1.f, 1.f, 1.f); - shaders["edge"].SetCurrentTexture("texture"); - shaders["fisheye"].SetCurrentTexture("texture"); - shaders["wave"].SetCurrentTexture("texture"); - shaders["wave"].SetTexture("wave", waveTexture); - shaders["pixelate"].SetCurrentTexture("texture"); - - // Define a string for displaying the description of the current shader - sf::Text shaderStr; - shaderStr.SetFont(font); - shaderStr.SetCharacterSize(20); - shaderStr.SetColor(sf::Color(250, 100, 30)); - shaderStr.SetPosition(5.f, 0.f); - shaderStr.SetString("Background shader: \"" + backgroundShader.GetName() + "\"\n" - "Flower shader: \"" + entityShader.GetName() + "\"\n" - "Global shader: \"" + globalShader.GetName() + "\"\n"); - - // Define a string for displaying help - sf::Text infoStr; - infoStr.SetFont(font); - infoStr.SetCharacterSize(20); - infoStr.SetColor(sf::Color(250, 100, 30)); - infoStr.SetPosition(5.f, 500.f); - infoStr.SetString("Move your mouse to change the shaders' parameters\n" - "Press numpad 1/4 to change the background shader\n" - "Press numpad 2/5 to change the flower shader\n" - "Press numpad 3/6 to change the global shader"); - - // Create a clock to measure the total time elapsed - sf::Clock clock; + // Create the instructions text + sf::Text instructions("Press left and right arrows to change the current shader", font, 20); + instructions.SetPosition(280, 555); + instructions.SetColor(sf::Color(80, 80, 80)); // Start the game loop + sf::Clock clock; while (window.IsOpened()) { // Process events sf::Event event; while (window.PollEvent(event)) { - // Close window : exit + // Close window: exit if (event.Type == sf::Event::Closed) window.Close(); if (event.Type == sf::Event::KeyPressed) { - // Escape key : exit - if (event.Key.Code == sf::Keyboard::Escape) - window.Close(); - - // Numpad : switch effect switch (event.Key.Code) { - case sf::Keyboard::Numpad1 : backgroundShader.GotoPrevious(); break; - case sf::Keyboard::Numpad4 : backgroundShader.GotoNext(); break; - case sf::Keyboard::Numpad2 : entityShader.GotoPrevious(); break; - case sf::Keyboard::Numpad5 : entityShader.GotoNext(); break; - case sf::Keyboard::Numpad3 : globalShader.GotoPrevious(); break; - case sf::Keyboard::Numpad6 : globalShader.GotoNext(); break; - default : break; - } + // Escape key: exit + case sf::Keyboard::Escape: + window.Close(); + break; - // Update the text - shaderStr.SetString("Background shader: \"" + backgroundShader.GetName() + "\"\n" - "Entity shader: \"" + entityShader.GetName() + "\"\n" - "Global shader: \"" + globalShader.GetName() + "\"\n"); + // Left arrow key: previous shader + case sf::Keyboard::Left: + if (current == 0) + current = effects.size() - 1; + else + current--; + description.SetString("Current effect: " + effects[current]->GetName()); + break; + + // Right arrow key: next shader + case sf::Keyboard::Right: + if (current == effects.size() - 1) + current = 0; + else + current++; + description.SetString("Current effect: " + effects[current]->GetName()); + break; + } } } - // Get the mouse position in the range [0, 1] - float mouseX = sf::Mouse::GetPosition(window).x / static_cast(window.GetWidth()); - float mouseY = sf::Mouse::GetPosition(window).y / static_cast(window.GetHeight()); + // Update the current example + float x = static_cast(sf::Mouse::GetPosition(window).x) / window.GetWidth(); + float y = static_cast(sf::Mouse::GetPosition(window).y) / window.GetHeight(); + effects[current]->Update(clock.GetElapsedTime() / 1000.f, x, y); - // Update the shaders - backgroundShader.Update(mouseX, mouseY); - entityShader.Update(mouseX, mouseY); - globalShader.Update(mouseX, mouseY); + // Clear the window + window.Clear(sf::Color(255, 128, 0)); - // Animate the entity - float entityX = (std::cos(clock.GetElapsedTime() * 0.0013f) + 1.2f) * 300; - float entityY = (std::cos(clock.GetElapsedTime() * 0.0008f) + 1.2f) * 200; - entity.SetPosition(entityX, entityY); - entity.Rotate(window.GetFrameTime() * 0.1f); + // Draw the current example + window.Draw(*effects[current]); - // Draw the background and the moving entity to the render texture - texture.Clear(); - texture.Draw(background, backgroundShader.GetShader()); - texture.Draw(entity, entityShader.GetShader()); - texture.Display(); - - // Draw the contents of the render texture to the window - sf::Sprite screen(texture.GetTexture()); - window.Draw(screen, globalShader.GetShader()); - - // Draw the interface texts - window.Draw(shaderStr); - window.Draw(infoStr); + // Draw the text + window.Draw(textBackground); + window.Draw(instructions); + window.Draw(description); // Finally, display the rendered frame on screen window.Display(); } + // delete the effects + for (std::size_t i = 0; i < effects.size(); ++i) + delete effects[i]; + return EXIT_SUCCESS; } - - -//////////////////////////////////////////////////////////// -/// Fonction called when the post-effects are not supported ; -/// Display an error message and wait until the user exits -/// -//////////////////////////////////////////////////////////// -void DisplayError() -{ - // Create the main window - sf::RenderWindow window(sf::VideoMode(800, 600), "SFML Shader"); - - // Define a string for displaying the error message - sf::Text error("Sorry, your system doesn't support shaders"); - error.SetColor(sf::Color(200, 100, 150)); - error.SetPosition(100.f, 250.f); - - // Start the game loop - while (window.IsOpened()) - { - // Process events - sf::Event event; - while (window.PollEvent(event)) - { - // Close window : exit - if (event.Type == sf::Event::Closed) - window.Close(); - - // Escape key : exit - if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Keyboard::Escape)) - window.Close(); - } - - // Clear the window - window.Clear(); - - // Draw the error message - window.Draw(error); - - // Finally, display the rendered frame on screen - window.Display(); - } -} diff --git a/examples/shader/resources/blink.frag b/examples/shader/resources/blink.frag new file mode 100644 index 00000000..07c8ddb2 --- /dev/null +++ b/examples/shader/resources/blink.frag @@ -0,0 +1,9 @@ +uniform sampler2D texture; +uniform float blink_alpha; + +void main() +{ + vec4 pixel = gl_Color; + pixel.a = blink_alpha; + gl_FragColor = pixel; +} diff --git a/examples/shader/resources/blur.sfx b/examples/shader/resources/blur.frag similarity index 73% rename from examples/shader/resources/blur.sfx rename to examples/shader/resources/blur.frag index bde2fa65..c40e5b3f 100644 --- a/examples/shader/resources/blur.sfx +++ b/examples/shader/resources/blur.frag @@ -1,12 +1,12 @@ uniform sampler2D texture; -uniform float offset; +uniform float blur_radius; void main() { - vec2 offx = vec2(offset, 0.0); - vec2 offy = vec2(0.0, offset); + vec2 offx = vec2(blur_radius, 0.0); + vec2 offy = vec2(0.0, blur_radius); - vec4 pixel = texture2D(texture, gl_TexCoord[0].xy) * 1.0 + + vec4 pixel = texture2D(texture, gl_TexCoord[0].xy) * 4.0 + texture2D(texture, gl_TexCoord[0].xy - offx) * 2.0 + texture2D(texture, gl_TexCoord[0].xy + offx) * 2.0 + texture2D(texture, gl_TexCoord[0].xy - offy) * 2.0 + @@ -16,5 +16,5 @@ void main() texture2D(texture, gl_TexCoord[0].xy + offx - offy) * 1.0 + texture2D(texture, gl_TexCoord[0].xy + offx + offy) * 1.0; - gl_FragColor = gl_Color * (pixel / 13.0); + gl_FragColor = gl_Color * (pixel / 16.0); } diff --git a/examples/shader/resources/colorize.sfx b/examples/shader/resources/colorize.sfx deleted file mode 100644 index eeb407c8..00000000 --- a/examples/shader/resources/colorize.sfx +++ /dev/null @@ -1,11 +0,0 @@ -uniform sampler2D texture; -uniform vec3 color; - -void main() -{ - vec4 pixel = texture2D(texture, gl_TexCoord[0].xy) * gl_Color; - float gray = pixel.r * 0.39 + pixel.g * 0.50 + pixel.b * 0.11; - - gl_FragColor = vec4(gray * color, 1.0) * 0.6 + pixel * 0.4; - gl_FragColor.a = pixel.a; -} diff --git a/examples/shader/resources/devices.png b/examples/shader/resources/devices.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1cbc85b8f59a269c58f1fc031c19464693b06d GIT binary patch literal 51410 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�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 threshold) - gl_FragColor.rgb = vec3(0, 0, 0); + vec4 pixel = gl_Color * texture2D(texture, gl_TexCoord[0].xy); + if (edge > edge_threshold * 8) + pixel.rgb = vec3(0.0, 0.0, 0.0); else - gl_FragColor.rgb = vec3(1, 1, 1); - gl_FragColor.a = gl_Color.a * texture2D(texture, gl_TexCoord[0].xy).a; + pixel.a = edge_threshold; + gl_FragColor = pixel; } diff --git a/examples/shader/resources/fisheye.sfx b/examples/shader/resources/fisheye.sfx deleted file mode 100644 index 97e3f8e4..00000000 --- a/examples/shader/resources/fisheye.sfx +++ /dev/null @@ -1,13 +0,0 @@ -uniform sampler2D texture; -uniform vec2 mouse; - -void main() -{ - float len = distance(gl_TexCoord[0].xy, mouse) * 7.0; - - vec2 coords = gl_TexCoord[0].xy; - if (len < 1.0) - coords += (gl_TexCoord[0].xy - mouse) * len; - - gl_FragColor = texture2D(texture, coords) * gl_Color; -} diff --git a/examples/shader/resources/nothing.sfx b/examples/shader/resources/nothing.sfx deleted file mode 100644 index cde0473a..00000000 --- a/examples/shader/resources/nothing.sfx +++ /dev/null @@ -1,6 +0,0 @@ -uniform sampler2D texture; - -void main() -{ - gl_FragColor = texture2D(texture, gl_TexCoord[0].xy) * gl_Color; -} diff --git a/examples/shader/resources/pixelate.sfx b/examples/shader/resources/pixelate.frag similarity index 63% rename from examples/shader/resources/pixelate.sfx rename to examples/shader/resources/pixelate.frag index 12a334e7..3c8eb0e0 100644 --- a/examples/shader/resources/pixelate.sfx +++ b/examples/shader/resources/pixelate.frag @@ -1,10 +1,9 @@ uniform sampler2D texture; -uniform vec2 mouse; +uniform float pixel_threshold; void main() { - float factor = 5.0 + 100.0 * length(mouse); + float factor = 1.0 / (pixel_threshold + 0.001); vec2 pos = floor(gl_TexCoord[0].xy * factor + 0.5) / factor; - gl_FragColor = texture2D(texture, pos) * gl_Color; } diff --git a/examples/shader/resources/sfml.png b/examples/shader/resources/sfml.png new file mode 100644 index 0000000000000000000000000000000000000000..1da719ff0afbd9549533bf992b5fc13ad957d9f0 GIT binary patch literal 25973 zcmXteWmFu`)Ai!+F2UX1A-F?ucXxM(;JUa34Fq?0cY?cXaChg~-~T=Dhut}+XQsMu zch#-iH65j-D1`)%4-WtUkYuFARR92RbN~QM7#14Ta*~|71-f9lNocu=xtJTfSvxwA zs9D>a17Ow>;SngQKE~01gC2l!lTncPUlR{1?l)LXP!pV!w6-e%0Dt}84@|U@BLe_1 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
A(GOC&9E< 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 LNkvXXu0mjfpAzUi diff --git a/examples/shader/resources/storm.vert b/examples/shader/resources/storm.vert new file mode 100644 index 00000000..442f80a9 --- /dev/null +++ b/examples/shader/resources/storm.vert @@ -0,0 +1,19 @@ +uniform vec2 storm_position; +uniform float storm_total_radius; +uniform float storm_inner_radius; + +void main() +{ + vec4 vertex = gl_ModelViewMatrix * gl_Vertex; + vec2 offset = vertex.xy - storm_position; + float len = length(offset); + if (len < storm_total_radius) + { + float push_distance = storm_inner_radius + len / storm_total_radius * (storm_total_radius - storm_inner_radius); + vertex.xy = storm_position + normalize(offset) * push_distance; + } + + gl_Position = gl_ProjectionMatrix * vertex; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_FrontColor = gl_Color; +} diff --git a/examples/shader/resources/text-background.png b/examples/shader/resources/text-background.png new file mode 100644 index 0000000000000000000000000000000000000000..c86e9b6c0ec49ab9c6a0208b22d5b811f0681443 GIT binary patch literal 745 zcmeAS@N?(olHy`uVBq!ia0y~yU{(OK12~v~WZsRoWFRG4;u=xnT$Gwvl9`{U5R#dj z%D{e`hm%jw;NKKsTc85=5>H=Ou#h$L;t%EiKq1ZokH}&M20djEW~^9hU&g?|lAydb|aAM`} zzx%$MGcfQh-Eys}o|)l5K-pe<28IL^HZcZ=8I7zA0%-}13@wro3=D~63w`~*eCxOW z4}-eaLBP8SvEn)m4I6I#E6IHQ3Ih0UmL6nc;3z(L=_&;9uHI|6Zpxz75MZ5FetX-S zZU%k zZQ1Vg&y9a8F&uajv!SFE0xsV@{&-@oAVWftUc@dac=_&c-FXZv%64N|QN1^Avpy4p zPu}(4%nS@}kHcFKMm#M#iD5*IT{_s6^-uU11i~Jh8-kq&4C$Sq;6@{^o_~J%w=@Go zThRUYSM*5 nzq||#&MTK+UI|VqPq}B`Jmh literal 0 HcmV?d00001 diff --git a/examples/shader/resources/wave.jpg b/examples/shader/resources/wave.jpg deleted file mode 100644 index cd125b8c82fe5b51d1d15f75b95cba2b5ca4a516..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23249 zcmb4qgntrF551BC%fNH`RvCc-4X z`+Tq8_Ye4;ZSQ;SJ?A>t-go!&ocp<-=iDRjw*ilV8rm8F92^{g7WM_W-vFor@E$z) z?~V5W4<8Q?pWqQ80k)A6J$m?vob>Tya#C_K3MzVP3Q9UkGIAPb8af6BCMKrG)GTZ) zjBNCbOpO0cf`f;TPk>KALP$u$NI_1)`2XGRKLIEn;sS614{)9Wa4B#eP~hB;0+;~U z@BcNx|26Cne1J=c1Hg82JqF<5VY>kj{&&!SBXAzzvEx#R;yY4`y`Z9gs$xWg9mk9V z!2RD@{%;Ndb`Cs3TpYkdY%d1|02deA|KFJZHvl^eE(JcjC?(YkY9q&|_v-+X|N1E& zPymzxe+aGUw46JQKeBE2Uwx}S#|td+JA3N_1Iw$V3S6eGiGEbEGWz%;#g*7J^*ciB zm$~!Rp~e#ZQZwLSvw%M-QX_p_5rmmAd`0Ag*Su7<#1&!6UgAS8>e>e^mi31vm$P#c zeFIe|{1ve2cw-+k4O;Ven@)ZDaj22Z2md+%pqpgLSS25h=hSI92}NvXZ>9cal1H#E z1ZeQ5W`!j-Ft}&3aM=u@EbpkHC1Jr4mrqN=mWp;}+A@qm1!$2Pj(%Nbr(MK&LFONm zOGXdZb|CnL=A)N=o6T7HjKTl?s9wBJu$B+z7{B(*tG>>i;kHa-q_Ix#f?>oFt? zzw)>YrBtjT-x=R$7*3w%fvD^Qn)EZoODm5%j;u_>U@jCa%?q$)71~ z;Q1>0uinrMRZ#FJTWPGGG9MgSfJ?nKeo1r+<*WiVt8Zwg-8-$)^vcHcjtaqFcoiiF z3YQs=sX9tg5#7!UR7ST?HN2u91N~}DS1{}as~VoyrGpO)=2L&XMREC%v3P!_;)=U+ zM|1`JQQjxM2aJZME92e+#!ip*wFcTHe95b9OBOTH-AhnN{%h$pfq-{vAh)?xi zh>VEOYm&SW4$G!X@j&TM+|V1Ii|kVh(&d#VR0D9aj7eBGB7k)kKvy zX0x9wR{blOH@caxu!F-Z_z8HvM-`9q$fG8CK9O-Lq3xg$XVYLz_bl|x_--I)7yJA3 zz|5JR(+`cqU3A0czGOjl_v4OKI}YKfkEz^XxvYD@!{Q+0rbt@*KThaV;$^g1`|lAZ z{}-(Kf1GJ6x6tIzd~nYfRBZz#b-Mu@wO75SGgO67ADYR`{;604@`Pl>;h#8256aAD8r)8bl zfV}5ecXfm>tRqX-=n&)d7{jT}=T3b%8e<&aZk5Xp(D%Sfatvs!k)k%pB7N1#HU_GKuo&>vNl ze@}yKB6$S#Egq}kqq$55ckb9<(Ung=?NjGe1!g{ZzEADHeV*tgmGS*RUtyUOtcSQ>egX_ zFpo#Rpy%9B%+5L)mh-Y+law6*n`|5kDwAb=2|^x^1>6JBAD-3jc(NBJ1GRh6s=RU% z7L#AEzeIzwRs2$8h9A6S%Ph~G3yf8r@)C0xceF_>9+|rw zOyK$=uC`w9NQ^O{PD%|~=|z;DfHWlp|VhaWcQF9qG0 zX{69CG4VTg(td3g)-bC5We#l09*gju{l;hIEO!skssKd@?=0+RV?PysREsN3vBZn} zZv;%KTXxSQB&_=29h83?pX@AbHigsraHcP>EY>J&3BWxK7Wb6J)QqJjb&tD3fX z?g4`A-<8onxW`iY59f`&V2`M}0`LZIi4K(Bn$+zZBf;JuY=no1*VcQ5(J*s!Rm{1v z7aJz<7eD)LxLIN5XUAiz9e0rJo5l0R>)&Zl zP==<0yj=^iO3Kwkziq|_O5IY4e_VXW^yyb+al^i60nOzW==B2TDe!}^v#u+FP%15hhU-Wcy zUMlqv8C-ydAKJ;90D%z>h^*>kyv7KP2<_&){32kDU)}99MRL?HlTJC~5s?1H2NMOC z!w7e7LB2QlfP5g^1m2>trSe`ZqZ70yn|2;IwiZ=Nmqb$i^i zO)Gpdl`jPp&(6m-9&-o`3XiyUhL^$9jeo%kJQU#h#$HvjPiSDh%REaC%VqnkYExVv zg31~WtzN7Rf4B#<9a8anYfIS)mQ5D)IBBasE4I4hpk4ay`6idaBzCUKBMp> z$F20=k4(I(weel;;rSIE|8APn&}HlOdWQ#%?fTIT*}%C`eOU*r%P}9{Y-IF^ zwM)a$@sKj@A-oGBHJ91`c5H9HT4RZm)XG_VWS0ab*(ZUMr&q+0avdKbaU%gLMqTGx zL<&1H63$6H;7DWqZ7NXk-9v|>cFmnWpGI%Gpa@;IGSN{{({8Yw`_FR3gaB#Y|EfLo z^DBXL^yZ=J&uIfpJNI(oKg%->?#1JYVfbQ2eg&(Qy?9h0kZEspQw-N>v;Y-hh3jKEvG|*==~M@K*U@L z9;Ha2+y|MtFNl9S)zYHIc;HEYj5nofCsaFYDHHY-`PQD_k)9G6*c9%9kXM#$w?Nn( zHIyf7m(2M&E1Kt41$41mKXb3W)SLD5tO;I;U@sVJCVh3GwS09a?-8t{MJ# z%k+9Kq;bWVWi%ip-ipTnKb_!;As5d)9pTv`4x82~VqQAcZ)WEAl$@3x;mkfQ0UFKc zJ3u0&Af(cu$GO8+*-;A9>Q&xd?HaiZ>kxwjjrAI0c8yMG$8>twBIVch=T?v@YE}is zJBbMLOe};$$5Jr}RE&hdO2$1P7vB7aGc{Y`on$7heH51V4%pgpNth31!+_!&V5w;` zE-Ff?F4ZK4m5g0+*o%BUloeR7qt6NPryK2;C8zA1mDO$J=UNt^b=6qSe1TXAN0I&r zPInL6ZI80n@cY=DTC24rm;lU$*>Km*ED0t)N$j@CtP<8Rekzzd(6qHa&I-GYF;|P& zOg?swW}HJw+rb3?)Wxi*MFcf_%dengy`J_9t9o`TBO147>|NiK9d>tfBuJQFlB!u6zOEXlel$5s~lD!Ifa~@cFTaiqRSZS)ih*q-N7gc28v(o+%5nsk0OdD({baVTWZhdl$SO4TeHnQK2%8S6462+I7 z&+;q2Xjor?zFfTz$?4x)FX3V`@jfuim+gYX(UbiU=Hhf*ABN$@Ajv36q`&CAs=B;62lh?v3wa|B4a; zM{#gcp4SjTzyQ5F)Aogw(u2K;;RCU}{TGtk`8-=BVCkVv-6(O(djL_vr)!zxyF?<| zE6Q0fAeq)Z0M{zpL$gG0ku^s8Mh+XAq@D0d?y7|8S{qsHch!vI``RbYD1;2EyJxcf zsb1_0p}GTQoShn63-@JsQ7P>OMWkRAUL$2;YS`@`}~`)f!U%Hu2CIY`>VlmQ^LPOyx5Pub;R^@ zy$4BbTtnraU$}bEPB{R5bG_vki(ob!{n~?F8J)k5p}K-t$oG@Rn7_c>Wgj~m*azMN zzT_zN?cW0$*?KVp_>d1pM|M9k%3Zme3AYVMi#k!Wz;@%{<_2ugrr)lO7tnuK$*H^E zoK{g3Et1RKEsW*v)y#&^vfBr+tMcrAw5XNx^T5c%w;$|`XxjU**Z$<9f81tQJvv$b z{vo8A|6}VvhG&(vbJG}ZO+AsY{n3=b?lo;zw?ctPf|x>qRM$JpP3CewOQkR>b={+m zWrabFrI|{OB`JB>4v&Cwad;(;ljhaLA1pubhvx&bqegiNUS&#gQ9Z?$l^2nb|wruL|alJC9(3CCw zMh4W}@fdoz8h-lnb8Iqd&fjfD84#$06g1z!8Rb&aNb#;xH7#NyJ~=jBg-K! z#nI6BlV66SQu?8@CYK^+Z=5Eraz3iH3PYXv8Jz&A^!9JT47B}Z~rB!~48{z>NoalQ1tXM+` zS-E#$f`&3IMLHV8LZ+@4(4zK2V^b$lgX$Fp6>AJXryMZwdjRzEQWFz?;_Sndhfoi+ zj>+5XOT?o4$*p@~EWd!Fnb}QBr3sD0WDF$Dn&`#k$)y%XzvXjr@%Lk(fhK;tn;Wrn zziB6P+U}&`T{KRR$i!I}PQU|r4fMREL!m>)HhYbs-j!pu)IGN3;#^jl6}5X<&ZQZ) z=i-sZDqfUhVuhUN2glXsxH2)R!0JKu&)%vt6PnQow4QI7BIpQd)@fpVm(S<~zrO`o zS8DZNJZrYH!!?F-=EQ{PsO!?vX>rI&2rEJTwSqMJL^qw(ob$c)O=;Cu! zkQ`3*H}9f{mz2#MYop2koJz#x-2)v~j`a96y#+}$Qb%Xjm6e~ad&owLJCB*&;ltreCnO68YMQnigo(QOFZYt`T0rFQYR~3u8DYoNHd#|PsjlhK$VfUfQmF;zykg^f;p+pf6@4}$@@e(P zeS9TO^m&M0k;u>!+w}+GZ(1xD#gRsv3~d?KYJ0utPJM&ybpcmtpn-vJfw;9MJPo!b z__!jS1*(H^uQ`EjX`SehBmL?s&>-7atq<$x4$Tw90uJ?1&Naq?7N))}mzj)(SZ65* zCeOo@>n6=hJUay?sUfBz7nQtq?F(%26;#nHq-FD(adBc2S*vC)o@?XM8s} zUZa(iDn_^AcPcqht_6kGMudLgynSf%jkJ(UOWJ%~ufhv8ZUeRV_VLZuL_fJq^*H^g znrZVesGh^+8CarGWz`yCA;i_A61wXZEIP&qa!aON%vPWwDZ(ku{c|H?uJcR6&Zv#; z)4wbHdqDEelIZELV>Sgwys-{%MxtTmPvGx2Pp8$)=RiZ?F!7AB8WuQ(XA;6OFpY9H z56^NzYn_pEw7bkx91Yz9Zbq z&1YVVcp6BrFzWTg*f)` z#`vnHV#)^co1bYEtwrS{(0EZ>W3|5+O=e~GBSHZl_|4p+QWzG|Q!WiQ4?q9gsL(ox z4#!G&$)%{k(V0Ta_e$I~!})i~@>0^bn7;gp7?!sapPN3UoY2i@$ivGYd#n#M`kHa578F=Rv}#wh-BGI( zp$*iC@c2g?lZMXxql}g|z9pj;I&7Id(dgFIwJ|PT`mNSE*2U6r<)QHE+b_7ynsvNu zZsVGDoNJCB{^CFLc|_}8w7Q6H)-p$T(!-i{X?1z^4!p@@F%D372hyAYzx^Pw_rQ1K z?b8{ST=!b#jJq!9-oQVN&#Yn{5M|-kU*frvU-BE(rcau5HL{JOc?B*eJ6<)isF%+G z7A)Lir-qglaTV??BZGQwpE~PrHhHmHw=@w6`q1A4L}|NCpY`7Z45sg}9yJN8k_wZ2 z0qS$6W(B4mm;R_2Dw}X5%32uwzIA#gnexV&q?EdEer$=uFWA_XDfUySli!uqun&z$ zJlABsf6oW*y6}JBSks@9{Z+B5i23^>?Xq)R&R?;5Mv+{;6Nq8;1O2JL^8*#473LS0 zjelU27F#e1wSJW~){m#+n-PaWH=77~;rT8|OMib>S3`G-wBrr_G&b5gZH-;%s?x=- z7jH5In(@u_Q(ONivFwn>UK!R^Mm_YJ+kE$nUmGjEsQvRdi+wYD{c~_QF`L)o9>BqK zr}5_=z{x8F!-FSJDYMO6LuL&mz3wy-eKE0-Q0!t&U}gUUH%)l>0Qerj#J6p**>vt8jM&sj$s+VfH zHf<(dW;cd&OUF=PRnv{F_}( z)>E0rDi;L13dV}dLM^AUkxlKDg&K^x2m1DrpUQEWu~>`~%TZNY!v`kOB(z|{CR#ad z9xb$xr}1LSbi>_8yseDmbUST~!Kw8#{r2E4`j?-TAKmA>9e8YCX$S z1GBnec@*PBet+zg{%T3rt%gxw`L8>zz5Z4t-z@J_PwDLb*vaqGnG4sv->;cgZ?e8$ z-P{9mB)%qNcx^s`GrwQlX`J=>#!gujD|N#WO}#QDtQ}z$VTsN}FIPuP_Y?jM5F8|8(-g{EZtvn# zq&^gtW@*TJrO8)jqB9&}yq;E}zMkdM)1Ph!a!PF`2?#i$*&gKh%~!i75ttbz7|msC z7O2rI^R8NJfzNX+OzIu;IDaSqWWHw0fci5AzBI7ub^A(FT5_i;Z`8hB74BybD6DHU zTlKtF_-4&#f7;=?WA3I8obJcZiPbN-&|Yo$#f*x?3wd3x0@B^7F7BmyhmYRzeR%P7 zg-A36w9Zo;VD?xXA2}dqOky*N!1Qh?co-pHup&X!WjBi->G|T|>Zw2=U)Qw&v_7;=@@S%nEg;b~2oR5GiCu}|A%f54FSe{yA+j5kc zmb^WAha*i`%&-{WDNmgTt&29hGi%Hjo*=5Zef;s1Le)wCrFYeewTU0YpIBBnS=?kh zU3)m3vyf4mkwb@pkdU!%p0y(jHf)M7!}=n)j2TYE7r&8wO3YaYj{wCa$XsLYSYjcq zJ8HR9wn_pHiGF)+&pzL!&O)L@r`d+{Wd?CBGEegvkYt`rw-iAlHC&3!9>DrOV`s!& zhM9x&s|Xz~bxhdZsC!+uPOP4s2F7wzwT659P&LEK3n~^v3)>t2V6Hy`B@*;=&h$nn zdww-uO_(`aUnI_m;$eMBE^`lfAUaKH>o%yOP`yPKyLu%vtvpisXMompf+1jVyA``g z0YJw)p-FgNzz1tZ(1cEjK9*}A|E}}W zTkQogWuA_6?VcgJw+lQCajQI9J(W&A4Ii9knvWlYW}cr} z!x+W3@ZSd=5f?^_XILvWGTjlpT&ug9_El|qs%5z67l$>=4?Gv1uPa@JQg;h$Y2ryK z@*OWz{WR!6XxR)daB8-OGDz7x6kc1Y$Le*cx`N6@b9k6%YyRP=yES4u6t6K_Dx1sx zERP@+EayvO^>Y}0>tzm=C0)<-ckfg*oq(8!l?bty?6~p2iFOekdy|!2pW3<|txMKz zed8|UL_5!Ai9IN=FFa-5rB@qXgk79&Cviz7QsZ^vu0`W`k0DGBc7{jh#(45BViQBl zi}3zsBe-tt3;jiRRB0qq^oJJWujc)Y+A3!6e)CGnxWBP{_r52lw0i0uu&8g@pO){E zPja)k%Z+1p52&@8!~|Bm+Pc_Cy&45X&zF7VM)hAS>jaDse>vAHnGO(87aeV@H~#1( zoA=SN;Y{>HsU2W;j?PJ&f$Ou7Z3VLYnrI;7u?AW~Jx}W&xZ87ur{Td}uo=pP4B9|@ zJ_b`)c&l2R32mSjc?*23GAETNfDgo7^LYf`XH#+IiKa{jl-tN>v?a+43MZKd&wAkiU_!OeMPNRJ6W*=(p#)Jf` zn%9u@MexMpR4{jDvcfF)60+J!OW6ooq}>>#C?`aQ8;U!)l-LGWuUMPea-j~B{y(26 zaNH2PDW0wp#4~s$SWQ~YLnYE#m(u7dj(HqtTBsk}Y_pD*<9eHElijRHHV7z;hv{F> zoi<;kOxW<@mx8U<60?ij(c%( zrTBqMj=i9zH%7~?7c}+)_16I!??DuMdMtbA59EGQI;IPZ$ss>ja}~Tr2k7yt^G+KF zEzTE+TPbFA{;*MttxrC}T5QZ%Yh97nmbQ;3&<2GF`?`!^mnSx&F7p90d@~}+dbeeV z2BGsjh?z&n0v-j(IlZGC%QW2XbiHCTq3UPr3~m28#1=5h3qvGv9oN&U$!>Rwr+;P} znn^f{>U<}cbkIp$$x_7v%j3WKD+z5~2u)C+>w;F0WLY+QDyA@lb*N{;49+KV_l&Jr z$n?9M!JHQAsXH5DE&9`A?WIJ@ddlk<_F;$Rnj$pow)iiLyqK)OA~c(9th($hhGcI6 zR=i5k_$wxpwPb^KvUCFw;qGn073w`m^KxHjYEY#{|2U;q|2Tf8a)bLIf~9RgqI!(U z10`ho8(DL>ig0PwSQ*pD&>`aZ`bh)Ut$EH$x=>Lbpa!t|4$478)P3FRYhgv&ihvJm zB7w14p;A;rqry7got4$WJP;deg$P)cdZR()HCCDwdr;zqw}q|}vSo0MS>cx%u%lMT z&r1Z`-$51Gf)Zes1la2H@X^bF>&F{6DFOK@5ALR8(Yw67_kblDs;ASYaUDS@@vJKG zyqI`*?n7t#5*uDP)-FDGbAk^pX)mDfWl!8v$rx#5?lilQP+yxhS+PrH@JD2v1KP2r z(<@36{Rf$59Bh22$PBzobGS?1IYnM=@IQOaJjy1ZVoNxsLZ2n-_EsG6M^u@w(ny}`u|BC-yG)BL!HYyRQnO1J{-5t|-QYjIEOFV0s|9}0P?Kcfp1 zu%2)t1DIiRwtUX>6{czl2(cbW3 zNGt4?4c1zBaygJPxPghf2Lv6`ZR74wHe(c~Efzzue>lJJVS3(O`7X}asG^ue?R*_4 zhJ~e4Wj^p|hl^hbRUC^bH2Z1yN9qXu6PM)bN+9`*-`JqUEgHT$|plmap#ULd2ZYixSUlqx0<@K*X|=YjH5-n#ZqK65vSK3LB;V=(gr73o zSiDDR&^|3tVYyafwqz`hjyHFp+fVPH-oKXYSyRnut=qaBNIi@B5PBC|BRrh3vc=JZ znvhj~QJ#+`=N^ccJL3|?U48QRu;curQ>yVl_cij6(Pg&dgq=h}k8^Ub4u+0Mm#>H4 zI>MJCT}H2ULPWo`!tavVf4EP@BsQm&qy4I=xU7*S*kp~^hy`Scc*DM+H$obb;Q(Bk z)OFudb&ilTZMWj{q15&Q{dve*Sa(Yk<5$;gwPzTu=yjKtt-P`_h_dU^iLFvt_P$h> zU*0biiRRN$h@Jm)ja3Rv$sH=u+&0jAKvj$}>W}4Snpwu%inoV9j1=vD-6b7iA`Or> z;8NXs&T`tylW5s#za?ijO^@Z8%E3dEs}J}yp$_ohK{4?^#8IWZgSvqw=u*b*qxaG!i0cHy68&(VBu0GKEEIdFLoMJg+{^x=!wberlzDhUpn-I76`F zBWZos84uj|IZ{?EAF+o7_$Z=L53#iuqdQ9{e(t7yPI2=Mf4A7iDm`G9w`sC{ltrKmp%L?rESnP9Q`t|pSE*%*==`IGgcAWs(qDnRq#uL`mhFD$2SWSd5ioY7m6^G)AzmXH>;$}P7f|AG<=YF{O?{%cg{C9-~VI-7zo(ts-z{Br)lBQL(U zE)3ct#rkQ_9r&!IQe%m>KvU8~#b0Q|%v&)BXl`!fQ@Ka!O@KMJKNV2Ytawg zmA#y#_dFt46laWMbUv8DyZbyK=S{~|*_Y^kX*b_O0oDA=R|(~@I=}H2P0pp#c`A~+ zT=Ccptg+db`Qnlq)c0q9)O839jKsRb#lI+E^B~wBXf0vu0!AT;4p!gD`!XU5?(~J` z_JQkyW~<7do>mN-1{$k}wGh@VV0hUvOkdKvc{wpSR`BY@w`i$yqH*74SXB7OFB#T_ zgD5(=by{y5uS>(&p25R2ms@IQj@aeFi^nl6I6>HQ47hU|;LeWZ707`-f;N-7_qH#m z-Q131W~SQ1Dp*rkD5+|6O-6xDrNk=OfI}#w#ucG@{U4a^IcW3nf}VJ{O9s3+Hw5z_ z`1?7&W3ik?V=$=$T?v%PgirD|y*_>9@vuYXc!QjJJm7r$yO1rFraqa|?kC%E=jpAm z6ZnyCuVQ);o{{MGZ$5t<*_^<>^b`2ks4Gz~rANhgB#edvDYJfd;nK(vLQ}ROwsU16O=V6x4k-(gj?lvjt74g*O_}l?4kd+So?4U75QFy!g`Nn9 zWU`SiP7jFpk9zHrlVCsD-+33C#~Fpr68b_jV-VZbe@@zzg5F$Sw{YsBf6i(Y!CrlK zf79&V%5q^G3o4<1U7`u54Hj!f-qQFQ=pcIjIT+M)ql#`T>ntZRp;syf1-}pnluAir zzdt`;{&$zLE8C^gmy03{0xv}u7zM6Z-BJ3f9Wf#F9uPjfiQj_sc zZFXMJkrC61i3u6xV()WUNr7JKb(8a_l|>?s#{z5N;VPx zDz^X8P1&a$H1qrfqF>1K8P>c)dbV8ZVXT6)(C4>lgE~sN0~N7tTRLon1Ip152xtSO_5P92fvl!bgS;^6@vyi*N@N-mdaj5yx!QjT@>6p(_{|mVfk`~$3&udD@ zIHW5F;#{x}kJ}DIn*YYznGw>ygst`!Z6SA1f49?NwYEG0ows1^XKp+ALg>HhnSgd( zhrGa%uJZn+g%h~DTBBK>g*LxN#=r(kl8MQ&&(A^(QF^qJ%ruX32Y=H%pBq3JW zEN#42OZm&+Y8YoDwP34N_W)J1>LaH*DsS~e#U<(G&>C{sJwTgW{dbn>?rH4q4%Ruo z?=m7OYu3oOU4K{jr;F+3gjRSnlNQBT2fm5ztQoGBaGKnhH)m559rZ)Mx)SESq=5GA zJF<|38<$SD7dp?)5*ME?V5_c*9^C`vg(x=tPTMX_;xdPX{vI{PbtdAu!nn6N*;Q#| zcr4MHT_{9O6OI+}5Dr87dBnrNXba8=v2gXkz(Y4q{wo+41Fybk=pJm2ArAAn_ z3-Qrk=87lxmiFT<{iKai(BR@%jg9fv>*pm54UBu>D77^o(H=sWe{pH|<^u91{j!bB zj3BijFi)v{9xahE!80$<%&eY0O{gQ3v0B`j=LUB^Pc|yvXTV-#)^xV6Vm>c+m-O-} zpKvb_^XLFut-O;a7@~CV;OZA%c0Uq9bn{l9J6A&+BWXLZv1yDFUnPz1`GV$@_XWMZ z=GS(SxjE*8ZH`v25WOs>hw6I!7`a!+#4v2OU(8bfQ|3d>C}f=*hKeD6^Sg(=+z<;@e6;rwW;28Be1{z5Ky1J+!R1(0jg~k?8v+KkrEgQnG})QB>nZ=SPa!a))D%x3wQ3*Y#7@6 zYjCPl_H^_Z$)I3qkEbq1m8o1nZ+#?!zHJ3fGS0-L?iB&ec#pE$6KXO!;u2?ue73wH zNSojZr^&)`FPM~VQ4d|~1KmJ6NS2JsLc&$0tXly4`EI<^b{M~o3m?=LvqWeTqdY== zA5Q6^v{sR27VMEDU&1Cd&yjjCA2joPUbgyI3kd&5r_~5)@=rwgU3N?V2HhZsO-Iod zh96r>@;FT}i~1@V{6Q|CyPK{lz28&aC1OLyk+)CWtA$xd={L^zY>BTe$}qM?45g^ebM)-v~$Z zZZ6cCT>ISBBv*W6mp+ih6y8Oe#_7%e6W=)T$$~m+NxL&Xr_Wp>weq$o6&hoJ`@D5Z z1|}76N6oNMUz&|`A(gzI8@SN_B;FJ1w#@VvHLspa-+SMTO!Ml8!}>5!N-I?*PuZ^K zc@I`qtWJVU%D%TubOdfrU4t4L#LdSz%y(s^mQXK*z2FiVQVoV$UXgV2Y_UUe0!wU- z{+Yr=f33_2rj>`|Wj@}~Hz{IuLHgMos{m4AJbtE~GY;W<0O8=_jq)?$gvxT7{ar?T zqSN$K!Qwth#p3}2IAMWm`J2Ff>A070Cu_qy5`Cjf0VW`uYU1<*%QpAEEMbBZoq)~q;)nwN?+eVR}DB%$X|b~UoT zpPap?pNL%uZKN_0frE^Ad~L5&hy2cD%c|}bhYa(N#!{pF1Lvs0C+B``-Hh9jJ|&`w zPkiZdV3V`W^A}T|RsYIKVkH(oAs{PuF1 zA`j-$kEWUmf@rm^!OqIuS3Zd5U_%!(_Pe#Fbib2ll$jX8ce}DRCZ_dNc(LI-Ze-Ib z>Ql-v>b96mG+m4@iT5Lbxz3UXP%|{4b3b!ZEx))f49~m2#&SYW` zc0a~o5XQa!l$Gy3$!cynpk;~ZblSWmAB`4bu^A+>TyvWQXjdv}@Rv}p(_X}m9ngT3 zb}LT0ZLt9u49S_tYB8JLkCO$nqWl=pt7>}-hst7?MspLeo7o7FRkwv<|EXWl5PNxK zg=!=cqt8OCusL$NU+umdW}1i6_{BspFLe)yZ(0*G$EBZvOtW%R_fa-EBrE@nViAZ< zKKV01dFQnJeaW6kdHE_dLpUCme!B6n^p5!>Bi`C>o;}e<{3%v6gEWc^knBF+d{@s| zt+?+gU_v`~ajn{$6aE76GB8=RD)9C4x@e4(pf{y}qGvM2u9qndD{U^%%g=VPfI#G0 z?m4|;TS&=DT)>DHMLMHWV+2p%C68YGS~8i1Aj`p9f*?WGn)VNw6^^`j$TXJ<(b|-g`KOr(-fdwbVPzqjn21x0?oRiw zDhQ9REf+Ap%Cn{FcVHBHU-b8_m?K&nU;^Dm%#d~*Vtp$Eu&UfMp z8w?ywcg*`Nu~*Mw56$0gQ1or72m0IaI%M>ylo)|g{nROoGSl*2P-|m8Z*Be%li92u z6(VoJ#gGAyQZ~{F?v~VR?1qT20L~h?^Bh{1N@HmJY(@tX^!oj7+s&!S4DOvQ`76}3 zKEPZD$r=N{o9CL`!VBf?<}lv5?AI+CR|-@4B*-g+j2hW@Tz%Yj>8CiTZ_pAXwlCdXOpBu=LPgNhZ=RrRI=E#e;`2gtti>Cl-xPwuG2F7^pNMlbRq`f{9_mf zA=s}#HVd*0lEWRB4Ci`ZeY$F^=W+Tfx7{I_H9eEHajMn!>vgO8F%5%i3$8L%r+Q-6 zu;kU|4Tr173hISqrf+&lK&0B&t8Z0bJi3(vq5}1TRrS3(^>zJfDyznO)J6}QK^~12 zb3F-Fd3IEkBB=gxjBMVwckjPGICDj`7`+4YLqhb*_#MNsGy%M#%KG>jc8kQ==^=x( zE~)>g>x1Yrehqq2tS8>MW9(c(i)OP$K%4SV4j<#ROD2;04L1c?er7_|H{V5*(f)a! zdIXxjF6bPBe%dGF__R-`7C+5A@n#w@2O5=4xhtg+Cv^DK=XfY1?@Krmi*9_~*K}NB z_p_C2YCj#e6QALAWh_h9i+f||o7Gn7G+Iz`!?o$B@;BdxZ7REY5vmF-ttzmpRjF4- zoij8CmzAxr1;`15-j>sKDy71JBTs^lxYWj@`CR$@H zz{W&y~6%b0Ro;hB0p z|MjLqFR_8yHFbS5T_&GSW|55`psYXJY;RKQ0OdRtPXiuF*3cZ3Rx9tx^q)W~izv^aCzIGBdd2;TKG27A`dhVs z7ZL6~y>rr7rlr;df?s^cI`e5KICS^{FHHPjzG7P5;_@ki2(dTvdYwIXWw?rMu^n^`b=X!uj&>SJ@Yq^J@Vzl#Re4*Qs-!83PjS+H9F-AWFb zwmU%yqGD!JGTmBkjC5ndO@_xr+nqs-SInB&u^$yck@@rW)17{Dh-4QKf1x7L>pXb; zG4#aLSOp7tx=O4KG~n4Ac6dfJXbM+cdXdrbykaHiS!V_USte4aY2DjgZW3bk(o5rQ zC9(U#dKG**{Si)41Rq&a^OrF$BW-uYE~>}0LZipTF4)4@QF9zC>}Es?N@z$z9`skR z?M(7I#dPvI>CnpyR_Pmsq+or7xEwbK&4BozYmm9l@^e0>rdnMw_{B`>Iu+I{Sj}Xv zQw=h%Bw4znywfxF!gPL69+OcD5B&~kO^W>$^^kY|;(NY5v32G&!&r6=P{LRF(*eUu z%CjPn!~4-@rT8aZH7a%UesNkpr#0BDB<@P2c#A>mUugE6Ux8)#>}kn`$d2oQG$yr3 z96i?(5HT}^CXiL9*1Ca&R;)hK@?J75ches#KEuSYbTIbPWSFkv7QYmyRl?~gITFTK zPpOnC)yGjL26dXavi3egds-_EhZK)lOQuqYk%Qi%pBN)hOW%zVvM9Bhs&8J3S*D!V zpbK&h+8(vCtG8y=%Zf6UT@;%IcD~7sA=1^rUGJ6@UT+Um)djTB0UEm_fRo}D+-+a9X$2n-G{(ZIemXW64K&1z zN;lsFAe-r%nN%6=;FPt)8Y7azrFVZ3GeFqa9zjj(84%)d!}@uPtDAC3Y0FyjwKsnj z3!!LtCtFeo4otd^tAN{a|NE=)lK&_L^6zlw~+uTXpj6kmnIC5sY=*M?$MIz7r3P1L+R9vV2~`PuNFI$kFVa^s_68HkE(egrZp zHFv#xa&z`S0dyIQ=Cd!vzOx8A$)gNf3uU`QoN)+gEaNPJc4?-(4~dSI3MdI^AdO0c z_&IP9W5ExS{Ikw{$r{y`9&Daim2x9B?bjjDKsg50jGnpVsc~X;Jj#Q($Z&H(9i?pGN zxORIT#fv3X=Q(w^`)M3=gPFQ$I6v>*tc>XfVQsvBh$Q>5q}cn%ggmV+sBfh^JVXp z`hU%c7vpPUUCF+Sm~BWRX0Y5M59EN@^u+Qen~8RwN4 zBi~Llc8co`%DGCBl#HoFlU9^vA=*(a*J(m9W7)#8v1IS2y&}C|{T1Gmw4`UM=j=Ap z#q<`be@%8q`kly;p0=e=sh#9V&(EXXHPSRrH==s)mwCfK>AZ7M}Alb(arwjCx$(QHLI-J*o+#ij~(^!+D&S2waB*30%RJr&>N zNBVegW0fS9i}krCpQKNwtN#E*obByPFGZc~mYl-vXm_cS(C15$$?LLWO0Lvjo=S>1 zl*-)--D!8hjwYHM>s!ku>5eWg!(+W@1Ndo8nWaKZl4viCxinKik)=Yv%9VK80}nka z6+UCyTtHUh+A7VKT0Kj~m-^Z09z?I!&n5CDYw@!8$d~%rpYjlTE!oYUr{>9|vYI3N zL_GV=*nJw5zEh}wFU;$##vneNYA48 zb2pKgz1IU#Fp&OBSw9ixX zXMVO;`SYnP@~=D4<(1xlq01AlRWj~NJaKIFxRWU?Zp^w%AmLA>vE+HwmMcwLOhmJ3 zV#>T^*G_YjT@m3o>Gvs>(vRTm1uf*iVbUbmM6)Dz&(F)Hu{^n5vh*-^49foiet>!} z_Q?MLQSOf?$lmOe^j_Y^nsI2iTKjo=PI_*1lAoQaMd-P`lC!et>r)*X2N`v}b|seO zsHNvDI$m8AKb?%w<_ z=lV9q(@b0zV*dcsTwnS?;|a%Y4DoQ=2Sr%F^xGHyk!2Y^R#QW^p4aqe8cwxj!*YIy zGb>-xoRI7Tc68G%VN2Gmqk%hGv(F;U(xs1~PvwB@R@s(w5_Hc>gFlu(i$0RiH-$fz zP{f_RShHfOjB>Ly*>u$1r%k`w+H{$ZFKCt?+xtK^aD1~owL93s*S0H_mi80*Dxrh8 zZ4~PSv0!H|O^7Dp`D~h>9anT&$dA3ynp{2ZgH`2Nm+Q%v&pn?Ld!lM|CAvK#Yz^*- z*}i{hX02rp9CVLp^OFeKZ(?=m9y>iV%$NGvSq5Dto^j^M<&}^m^2B>^b|c!!;>(Egwuxc*Nv@Ld zWYSp4xfRiNNP7ZhKA7Jr70^edcEOeE=qs;%Il?Z_v9f-U{{YcUi5;^^4ydX_t6?}} z2*Y6U*?6sprT+j*sgcz;MC|5OHfZ(pOw_ph-#YA^la{tBdChEVGZjsUq~)biiWRd! zsmS!ZGtJ{1pZW~*x>WcNo&!U`v>p#eu@@P(~tJPlKvCyxir6H%w+vH zZdcNIC4Dz;SJRxJ2G7c^dR51lX=BJ!Z7hh7Czd;hA!@;sz_cdv9J15w^e;YlEUfeA z^3OGaC2Go;5|*r1w_O=uDax+d%gOEfH+pAmRVr5ZVD3uVbOJ<;?#mHDrd3y-Ptysn?{-bk zcY7JidwUSf&wCEmk3$6+UTp~H<-LN7q*dM&T{t$?Nt#Nk2_7mGnZA{+*=1q;3Mz6RXIi$zoiR*3%qJ$O{W{t7nnMpekd@ocDy?I6*o7eZK{!Luns)TwMsnKZ(` zIxuxtM(KqdX4>eiw3%Huv19wOT!c~E%&5KQ7U;beNWB(q5Hq3D(J$!4*68%f&}nFt z^r3c!dX{@M8j`$EGW28XbW6^`Q)i)ikoEad8|g;Y=yRtk$ZxG>=#i4T*sa%esyUj{ zqNs{SDvF~#I%q1bWRZ?<>-uD}KA5_yieR}e!Yc$*mi9~bAB*?Ure0NEif~ofbbC}I z&lHfoYm>_r=<+j)x*tNxXrG6N3OFtB4P!_}GWfTQkDd6O^PC##kEG#%HT4>EV z9!<9UC}FusJC5$OB8!f+He$3b%##tU-fj9fZROWZf6M`fMxU5D;n*wrFD7rB-$34U z{hNS~InrD(3RQ154I$2nrDwg2Bxlh?tojv)6G`GLyqXltUF<_GdODStL~`=zdb-iI z@bw;-LOE&CETrh}SM*CK`Y=FgHS7H1Cz1UZJ(jjIk^L1v{S3v~%xaN#9dq>xo zpl)Un!ySv$&hcSC?0r6@pQ7xXIsX7R(qG08cUpdpQcu&Qlk`#kW3+hj#7T|!{f;q{ z^xe51M7TS!wsTb>$}~&-r^LgUFz z{vjpAPT*l+O?ue7!=+NJbbd3bN}EIjshiA2|u)nZ!t%*h`3})>4RnaLbCul)BB`p^c zW75!qbb49}*O~0O41X;1-$o~Blk{V2bb248$7qi9+Cpmel@KE{{X7H;By&Pfp)NI0JPHW2635Pq0=#T zychjLyN6Y z@-rGlp<=|lGs}~<*hF^ph%svuCR)&5IOzbgWwtHJn{4YEKC03CS~$%}JtT(*lQ?rA zC$s6hxd1(%OEKa*ag|<80?5yzi7s?HMtu}X&!JdxHhvS2-nsyjpG2~<=*CWc6v@k> zsBt#_Dv!!t3nZ^a5S`M^U=l&Et@tT!f+9$#!=g} zD#?-!SBbHVJa|)i*j^$yZCVumb9aaa)_n)!tF%@57+xj+0F8(0MEN*=rT34I>LPL( zdPhAzye>S9D-zqz!jO;hfAU)S+R_=_=i3#nRY{aFFr*tZg$rVF2F&pVNt@UPNK!Qa z038^(-35OPoZ(qM$~oYWrb~S%=iy3HO5&6A)|4wXj1vv+7WRdw6dSge$v5Kqv)Y-Rgy0}%)e60&C=+D@}A*s<+;DaOS_9( z5O3nLY_Wlk(Fz#2QWM^GTSO^gtst3aX^Hd1wlHa`2u5i9%54Jhzzv>nWd{lbRz4YJ zAK=gYXDZShDH+e=M7*}?Xq)s|OQNb*6rP@)4c5t~T#7}q7t~|+A^m9o0JR>A>EXSR zo}*j*hdMGfp{kZ?=<|>%F*3eJ&M zpXi!T^fL-KE*Fo|T?=Gq(NpSJ98d$_(R<0gj2!}Z9fac!xVjd- zhW0Ocy^Y+%V2(Aggn4KYX1>I4Gz9BE2d*)&M+u}7-bvb za~Vb1c=x{r2*Iul{1|_&@Y4w24xFS|PWw8r2k}kYKAt{{TTy z+O#P={4E6$TGofKvDSc=GuX)$JtC*eO@1ZMla{{{8_vsGEPS_hp*r&3%Jyq%9);y+ z`j&okl!u>rZ~mo^oge6S8Yq4f_#gcthstBESo%5OSdrJj2hU8R)1bjwQyT7#l_E&U_ZyqRCEmtIVsS>)bK z-`2~&B5&(rJ^Jsk6`4MdRP!I&C6!Q}+9BtjRLaK74?a>VC5TrcX^Yaevcf)lL=SCI zL~&o*C5;+)V%)ei%1xm3KWNJr4*vimu85vUl9QiAQ*!9aL$sn8&0?4R^k zq>rVJ_Ek^IdnPB|#;TnS+>y(ds0gLDIhY{dw zR56VQlr&}+zzo*5WxR!Lwkm2KzkwLU*YI0>vV3Mm-h2gUL101(H?Mp3v*-OK5_mH)uV-D;WjepA0;K1=u4b$hB-y} zJjsuSYaihr)SpXf{eufx1!d>a1%ukh(3{5|}{IPs}J1^7q!7}Glb1Y*a-d>YXX zQfwy;6_4oYhV`MeRMMaecxX9_&^}kNimZ|!LC;z$Jm>YRBcnO351~lbtOaI2pf%X_ zi#-R;m91G@=gZ}uZ314kWlRaYu}a;2nRhJKJ3SU0x=S3fE|SP-b7?Gf9%n38p1)>S ze#*tNs~J_(ha}fT*v#5=`JAfhNkQ13V)9?GiBf57ob)78*i@N4vVMGuTMAZ(ur7DD z{So@F)>c7#5B}Cw`j2n&vK!WOe~{}!J!qB-n`D2i?c_%K4%(GIu3xboJy&XI6tP-E zg&Pg%Qv0?Jk6%yH7Ss`Pv5O2=hf4Eg(AqqmDp>jiy((EE)0$DVNA#&>S>jCv6pf?N z{JGz)mp(-Q0M^eX^QGxh%TFeDzZ(>9R^H0ewTdtoOiGs zn%az>O{$nC78=7XBUR&U?IRYL+Kpny+NBvV)u8GdJfghP=?t04^@vnBoT2REPsYsNBDH6JdT>#JmgP+FGTGW?nd|q>~DpfCm89~Nuo+bPSBoI zPSI&1FKA6JFK8NBm?8jUA>MH~bI3iRaNG zfg_A@(Y_RLh;)iZ`#jGLAm(${hLN7ugl*&+!@AK@Og1X=Jz~npQgy2-Aj{>D(I3*K zkKst=fULu_6z8S+*?kIUEO#1ZC6Mss^2d>+^2NE}Fs@eT%VlN~_MJM)Rdhz&is`Dq zwCFX%*`C?F_vloK6t*Lp_R&IKd$uW-=Fv-#N2Uw@V{g$Xq~_5`-%)q@6;txw$xlf) zv8I~VgLLJ2G)8)9waAg5g{f!g;k}a6o3$NkWur;sF?nd1k%g!hSXkmiR-jq6@LrIlW6ohn%y8FZ*nKyz9GTER3|nzKms zk0xj9WzUs2^|Q--x78IaxALTZwj^GGb}d_t;(B+TE|SW4a;Yry?>0v)s)YR|i1wlE gRk54Iu4Y9ghG72y(FAw7Vc0$N1)N=g -#include +#include +#include #include +#include #include #include #include @@ -39,30 +41,45 @@ namespace sf { +class InputStream; +class Texture; + //////////////////////////////////////////////////////////// -/// \brief Pixel/fragment shader class +/// \brief Shader class (vertex and fragment) /// //////////////////////////////////////////////////////////// -class SFML_API Shader : GlResource +class SFML_API Shader : GlResource, NonCopyable { +public : + + //////////////////////////////////////////////////////////// + /// \brief Types of shaders + /// + //////////////////////////////////////////////////////////// + enum Type + { + Vertex, ///< Vertex shader + Fragment ///< Fragment (pixel) shader + }; + + //////////////////////////////////////////////////////////// + /// \brief Special type/value that can be passed to SetParameter, + /// and that represents the texture of the object being drawn + /// + //////////////////////////////////////////////////////////// + struct CurrentTextureType {}; + static CurrentTextureType CurrentTexture; + public : //////////////////////////////////////////////////////////// /// \brief Default constructor /// - /// This constructor creates an invalid shader + /// This constructor creates an invalid shader. /// //////////////////////////////////////////////////////////// Shader(); - //////////////////////////////////////////////////////////// - /// \brief Copy constructor - /// - /// \param copy Instance to copy - /// - //////////////////////////////////////////////////////////// - Shader(const Shader& copy); - //////////////////////////////////////////////////////////// /// \brief Destructor /// @@ -70,64 +87,139 @@ public : ~Shader(); //////////////////////////////////////////////////////////// - /// \brief Load the shader from a file + /// \brief Load either the vertex or fragment shader from a file /// + /// This function loads a single shader, either vertex or + /// fragment, identified by the second argument. /// The source must be a text file containing a valid - /// fragment shader in GLSL language. GLSL is a C-like - /// language dedicated to OpenGL shaders; you'll probably - /// need to read a good documentation for it before writing - /// your own shaders. + /// shader in GLSL language. GLSL is a C-like language + /// dedicated to OpenGL shaders; you'll probably need to + /// read a good documentation for it before writing your + /// own shaders. /// - /// \param filename Path of the shader file to load + /// \param filename Path of the vertex or fragment shader file to load + /// \param type Type of shader (vertex or fragment) /// /// \return True if loading succeeded, false if it failed /// /// \see LoadFromMemory, LoadFromStream /// //////////////////////////////////////////////////////////// - bool LoadFromFile(const std::string& filename); + bool LoadFromFile(const std::string& filename, Type type); //////////////////////////////////////////////////////////// - /// \brief Load the shader from a source code in memory + /// \brief Load both the vertex and fragment shaders from files /// - /// The source code must be a valid fragment shader in - /// GLSL language. GLSL is a C-like language dedicated - /// to OpenGL shaders; you'll probably need to read a - /// good documentation for it before writing your own shaders. + /// This function loads both the vertex and the fragment + /// shaders. If one of them fails to load, the shader is left + /// empty (the valid shader is unloaded). + /// The sources must be text files containing valid shaders + /// in GLSL language. GLSL is a C-like language dedicated to + /// OpenGL shaders; you'll probably need to read a good documentation + /// for it before writing your own shaders. + /// + /// \param vertexShaderFilename Path of the vertex shader file to load + /// \param fragmentShaderFilename Path of the fragment shader file to load + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see LoadFromMemory, LoadFromStream + /// + //////////////////////////////////////////////////////////// + bool LoadFromFile(const std::string& vertexShaderFilename, const std::string& fragmentShaderFilename); + + //////////////////////////////////////////////////////////// + /// \brief Load either the vertex or fragment shader from a source code in memory + /// + /// This function loads a single shader, either vertex or + /// fragment, identified by the second argument. + /// The source code must be a valid shader in GLSL language. + /// GLSL is a C-like language dedicated to OpenGL shaders; + /// you'll probably need to read a good documentation for + /// it before writing your own shaders. /// /// \param shader String containing the source code of the shader + /// \param type Type of shader (vertex or fragment) /// /// \return True if loading succeeded, false if it failed /// /// \see LoadFromFile, LoadFromStream /// //////////////////////////////////////////////////////////// - bool LoadFromMemory(const std::string& shader); + bool LoadFromMemory(const std::string& shader, Type type); //////////////////////////////////////////////////////////// - /// \brief Load the shader from a custom stream + /// \brief Load both the vertex and fragment shaders from source codes in memory /// - /// The source code must be a valid fragment shader in - /// GLSL language. GLSL is a C-like language dedicated - /// to OpenGL shaders; you'll probably need to read a - /// good documentation for it before writing your own shaders. + /// This function loads both the vertex and the fragment + /// shaders. If one of them fails to load, the shader is left + /// empty (the valid shader is unloaded). + /// The sources must be valid shaders in GLSL language. GLSL is + /// a C-like language dedicated to OpenGL shaders; you'll + /// probably need to read a good documentation for it before + /// writing your own shaders. + /// + /// \param vertexShader String containing the source code of the vertex shader + /// \param fragmentShader String containing the source code of the fragment shader + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see LoadFromFile, LoadFromStream + /// + //////////////////////////////////////////////////////////// + bool LoadFromMemory(const std::string& vertexShader, const std::string& fragmentShader); + + //////////////////////////////////////////////////////////// + /// \brief Load either the vertex or fragment shader from a custom stream + /// + /// This function loads a single shader, either vertex or + /// fragment, identified by the second argument. + /// The source code must be a valid shader in GLSL language. + /// GLSL is a C-like language dedicated to OpenGL shaders; + /// you'll probably need to read a good documentation for it + /// before writing your own shaders. /// /// \param stream Source stream to read from + /// \param type Type of shader (vertex or fragment) /// /// \return True if loading succeeded, false if it failed /// /// \see LoadFromFile, LoadFromMemory /// //////////////////////////////////////////////////////////// - bool LoadFromStream(InputStream& stream); + bool LoadFromStream(InputStream& stream, Type type); + + //////////////////////////////////////////////////////////// + /// \brief Load both the vertex and fragment shaders from custom streams + /// + /// This function loads both the vertex and the fragment + /// shaders. If one of them fails to load, the shader is left + /// empty (the valid shader is unloaded). + /// The source codes must be valid shaders in GLSL language. + /// GLSL is a C-like language dedicated to OpenGL shaders; + /// you'll probably need to read a good documentation for + /// it before writing your own shaders. + /// + /// \param vertexShaderStream Source stream to read the vertex shader from + /// \param fragmentShaderStream Source stream to read the fragment shader from + /// + /// \return True if loading succeeded, false if it failed + /// + /// \see LoadFromFile, LoadFromMemory + /// + //////////////////////////////////////////////////////////// + bool LoadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream); //////////////////////////////////////////////////////////// /// \brief Change a float parameter of the shader /// /// \a name is the name of the variable to change in the shader. - /// For example: + /// The corresponding parameter in the shader must be a float + /// (float GLSL type). + /// + /// Example: /// \code - /// uniform float myparam; // this is the variable in the pixel shader + /// uniform float myparam; // this is the variable in the shader /// \endcode /// \code /// shader.SetParameter("myparam", 5.2f); @@ -136,8 +228,6 @@ public : /// \param name Name of the parameter in the shader /// \param x Value to assign /// - /// \see SetTexture, SetCurrentTexture - /// //////////////////////////////////////////////////////////// void SetParameter(const std::string& name, float x); @@ -145,9 +235,12 @@ public : /// \brief Change a 2-components vector parameter of the shader /// /// \a name is the name of the variable to change in the shader. - /// For example: + /// The corresponding parameter in the shader must be a 2x1 vector + /// (vec2 GLSL type). + /// + /// Example: /// \code - /// uniform vec2 myparam; // this is the variable in the pixel shader + /// uniform vec2 myparam; // this is the variable in the shader /// \endcode /// \code /// shader.SetParameter("myparam", 5.2f, 6.0f); @@ -157,8 +250,6 @@ public : /// \param x First component of the value to assign /// \param y Second component of the value to assign /// - /// \see SetTexture, SetCurrentTexture - /// //////////////////////////////////////////////////////////// void SetParameter(const std::string& name, float x, float y); @@ -166,9 +257,12 @@ public : /// \brief Change a 3-components vector parameter of the shader /// /// \a name is the name of the variable to change in the shader. - /// For example: + /// The corresponding parameter in the shader must be a 3x1 vector + /// (vec3 GLSL type). + /// + /// Example: /// \code - /// uniform vec3 myparam; // this is the variable in the pixel shader + /// uniform vec3 myparam; // this is the variable in the shader /// \endcode /// \code /// shader.SetParameter("myparam", 5.2f, 6.0f, -8.1f); @@ -179,8 +273,6 @@ public : /// \param y Second component of the value to assign /// \param z Third component of the value to assign /// - /// \see SetTexture, SetCurrentTexture - /// //////////////////////////////////////////////////////////// void SetParameter(const std::string& name, float x, float y, float z); @@ -188,9 +280,12 @@ public : /// \brief Change a 4-components vector parameter of the shader /// /// \a name is the name of the variable to change in the shader. - /// For example: + /// The corresponding parameter in the shader must be a 4x1 vector + /// (vec4 GLSL type). + /// + /// Example: /// \code - /// uniform vec4 myparam; // this is the variable in the pixel shader + /// uniform vec4 myparam; // this is the variable in the shader /// \endcode /// \code /// shader.SetParameter("myparam", 5.2f, 6.0f, -8.1f, 0.4f); @@ -202,8 +297,6 @@ public : /// \param z Third component of the value to assign /// \param w Fourth component of the value to assign /// - /// \see SetTexture, SetCurrentTexture - /// //////////////////////////////////////////////////////////// void SetParameter(const std::string& name, float x, float y, float z, float w); @@ -211,9 +304,12 @@ public : /// \brief Change a 2-components vector parameter of the shader /// /// \a name is the name of the variable to change in the shader. - /// For example: + /// The corresponding parameter in the shader must be a 2x1 vector + /// (vec2 GLSL type). + /// + /// Example: /// \code - /// uniform vec2 myparam; // this is the variable in the pixel shader + /// uniform vec2 myparam; // this is the variable in the shader /// \endcode /// \code /// shader.SetParameter("myparam", sf::Vector2f(5.2f, 6.0f)); @@ -222,8 +318,6 @@ public : /// \param name Name of the parameter in the shader /// \param vector Vector to assign /// - /// \see SetTexture, SetCurrentTexture - /// //////////////////////////////////////////////////////////// void SetParameter(const std::string& name, const Vector2f& vector); @@ -231,9 +325,12 @@ public : /// \brief Change a 2-components vector parameter of the shader /// /// \a name is the name of the variable to change in the shader. - /// For example: + /// The corresponding parameter in the shader must be a 3x1 vector + /// (vec3 GLSL type). + /// + /// Example: /// \code - /// uniform vec3 myparam; // this is the variable in the pixel shader + /// uniform vec3 myparam; // this is the variable in the shader /// \endcode /// \code /// shader.SetParameter("myparam", sf::Vector3f(5.2f, 6.0f, -8.1f)); @@ -242,59 +339,113 @@ public : /// \param name Name of the parameter in the shader /// \param vector Vector to assign /// - /// \see SetTexture, SetCurrentTexture - /// //////////////////////////////////////////////////////////// void SetParameter(const std::string& name, const Vector3f& vector); //////////////////////////////////////////////////////////// - /// \brief Change a texture parameter of the shader + /// \brief Change a color parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 4x1 vector + /// (vec4 GLSL type). + /// + /// It is important to note that the components of the color are + /// normalized before being passed to the shader. Therefore, + /// they are converted from range [0 .. 255] to range [0 .. 1]. + /// For example, a sf::Color(255, 125, 0, 255) will be transformed + /// to a vec4(1.0, 0.5, 0.0, 1.0) in the shader. /// - /// \a name is the name of the texture to change in the shader. - /// This function maps an external texture to the given shader - /// variable; to use the current texture of the object being drawn, - /// use SetCurrentTexture instead. /// Example: /// \code - /// // These are the variables in the pixel shader - /// uniform sampler2D the_texture; + /// uniform vec4 color; // this is the variable in the shader + /// \endcode + /// \code + /// shader.SetParameter("color", sf::Color(255, 128, 0, 255)); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param color Color to assign + /// + //////////////////////////////////////////////////////////// + void SetParameter(const std::string& name, const Color& color); + + //////////////////////////////////////////////////////////// + /// \brief Change a matrix parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 4x4 matrix + /// (mat4 GLSL type). + /// + /// Example: + /// \code + /// uniform mat4 matrix; // this is the variable in the shader + /// \endcode + /// \code + /// sf::Transform transform; + /// transform.Translate(5, 10); + /// shader.SetParameter("matrix", transform); + /// \endcode + /// + /// \param name Name of the parameter in the shader + /// \param transform Transform to assign + /// + //////////////////////////////////////////////////////////// + void SetParameter(const std::string& name, const sf::Transform& transform); + + //////////////////////////////////////////////////////////// + /// \brief Change a texture parameter of the shader + /// + /// \a name is the name of the variable to change in the shader. + /// The corresponding parameter in the shader must be a 2D texture + /// (sampler2D GLSL type). + /// + /// Example: + /// \code + /// uniform sampler2D the_texture; // this is the variable in the shader /// \endcode /// \code /// sf::Texture texture; /// ... - /// shader.SetTexture("the_texture", texture); + /// shader.SetParameter("the_texture", texture); /// \endcode /// It is important to note that \a texture must remain alive as long /// as the shader uses it, no copy is made internally. /// + /// To use the texture of the object being draw, which cannot be + /// known in advance, you can pass the special value + /// sf::Shader::CurrentTexture: + /// \code + /// shader.SetParameter("the_texture", sf::Shader::CurrentTexture). + /// \endcode + /// /// \param name Name of the texture in the shader /// \param texture Texture to assign /// - /// \see SetParameter, SetCurrentTexture - /// //////////////////////////////////////////////////////////// - void SetTexture(const std::string& name, const Texture& texture); + void SetParameter(const std::string& name, const Texture& texture); //////////////////////////////////////////////////////////// - /// \brief Set the current object texture in the shader + /// \brief Change a texture parameter of the shader + /// + /// This overload maps a shader texture variable to the + /// texture of the object being drawn, which cannot be + /// known in advance. The second argument must be + /// sf::Shader::CurrentTexture. + /// The corresponding parameter in the shader must be a 2D texture + /// (sampler2D GLSL type). /// - /// This function maps a shader texture variable to the - /// texture of the object being drawn. /// Example: /// \code - /// // This is the variable in the pixel shader - /// uniform sampler2D current; + /// uniform sampler2D current; // this is the variable in the shader /// \endcode /// \code - /// shader.SetCurrentTexture("current"); + /// shader.SetParameter("current", sf::Shader::CurrentTexture); /// \endcode /// /// \param name Name of the texture in the shader /// - /// \see SetParameter, SetTexture - /// //////////////////////////////////////////////////////////// - void SetCurrentTexture(const std::string& name); + void SetParameter(const std::string& name, CurrentTextureType); //////////////////////////////////////////////////////////// /// \brief Bind the shader for rendering (activate it) @@ -326,16 +477,6 @@ public : //////////////////////////////////////////////////////////// void Unbind() const; - //////////////////////////////////////////////////////////// - /// \brief Overload of assignment operator - /// - /// \param right Instance to assign - /// - /// \return Reference to self - /// - //////////////////////////////////////////////////////////// - Shader& operator =(const Shader& right); - //////////////////////////////////////////////////////////// /// \brief Tell whether or not the system supports shaders /// @@ -351,12 +492,18 @@ public : private : //////////////////////////////////////////////////////////// - /// \brief Create the program and attach the shaders + /// \brief Compile the shader(s) and create the program + /// + /// If one of the arguments is NULL, the corresponding shader + /// is not created. + /// + /// \param vertexShaderCode Source code of the vertex shader + /// \param fragmentShaderCode Source code of the fragment shader /// /// \return True on success, false if any error happened /// //////////////////////////////////////////////////////////// - bool CompileProgram(); + bool CompileProgram(const char* vertexShaderCode, const char* fragmentShaderCode); //////////////////////////////////////////////////////////// /// \brief Bind all the textures used by the shader @@ -367,16 +514,6 @@ private : //////////////////////////////////////////////////////////// void BindTextures() const; - //////////////////////////////////////////////////////////// - /// \brief Make sure that the shader is ready to be used - /// - /// This function is called by the Renderer class, to make - /// sure that the shader's parameters are properly applied - /// even when Use() is not called due to internal optimizations. - /// - //////////////////////////////////////////////////////////// - void Use() const; - //////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////// @@ -388,7 +525,6 @@ private : unsigned int myShaderProgram; ///< OpenGL identifier for the program int myCurrentTexture; ///< Location of the current texture in the shader TextureTable myTextures; ///< Texture variables in the shader, mapped to their location - std::string myFragmentShader; ///< Fragment shader source code }; } // namespace sf @@ -401,34 +537,44 @@ private : /// \class sf::Shader /// \ingroup graphics /// -/// Pixel shaders (or fragment shaders) are programs written -/// using a specific language, executed directly by the -/// graphics card and allowing to apply per-pixel real-time -/// operations to the rendered entities. +/// Shaders are programs written using a specific language, +/// executed directly by the graphics card and allowing +/// to apply real-time operations to the rendered entities. /// -/// Pixel shaders are written in GLSL, which is a C-like +/// There are two kinds of shaders: +/// \li Vertex shaders, that process vertices +/// \li Fragment (pixel) shaders, that process pixels +/// +/// A sf::Shader can be composed of either a vertex shader +/// alone, a fragment shader alone, or both combined +/// (see the variants of the Load functions). +/// +/// Shaders are written in GLSL, which is a C-like /// language dedicated to OpenGL shaders. You'll probably /// need to learn its basics before writing your own shaders /// for SFML. /// /// Like any C/C++ program, a shader has its own variables /// that you can set from your C++ application. sf::Shader -/// handles 3 different types of variables: +/// handles 4 different types of variables: /// \li floats /// \li vectors (2, 3 or 4 components) /// \li textures +/// \li transforms (matrices) /// /// The value of the variables can be changed at any time -/// with either Shader::SetParameter or Shader::SetTexture: +/// with either the various overloads of the SetParameter function: /// \code /// shader.SetParameter("offset", 2.f); /// shader.SetParameter("color", 0.5f, 0.8f, 0.3f); -/// shader.SetTexture("overlay", texture); // texture is a sf::Texture -/// shader.SetCurrentTexture("texture"); +/// shader.SetParameter("matrix", transform); // transform is a sf::Transform +/// shader.SetParameter("overlay", texture); // texture is a sf::Texture +/// shader.SetParameter("texture", sf::Shader::CurrentTexture); /// \endcode /// -/// Shader::SetCurrentTexture maps the given texture variable -/// to the current texture of the object being drawn. +/// The special Shader::CurrentTexture argument maps the +/// given texture variable to the current texture of the +/// object being drawn (which cannot be known in advance). /// /// To apply a shader to a drawable, you must pass it as an /// additional parameter to the Draw function: @@ -443,13 +589,15 @@ private : /// window.Draw(sprite, states); /// \endcode /// -/// Shaders can be used on any drawable, but they are mainly -/// made for sprites and shapes. Using a shader on a sf::String -/// is more limited, because the texture of the text is not the -/// actual text that you see on screen, it is a big texture -/// containing all the characters of the font in an arbitrary -/// order. Thus, texture lookups on pixels other than the current -/// one may not give you the expected result. +/// Shaders can be used on any drawable, but some combinations are +/// not interesting. For example, using a vertex shader on a sf::Sprite +/// is limited because there are only 4 vertices, the sprite would +/// have to be subdivided in order to apply wave effects. +/// Another bad example is a fragment shader with sf::Text: the texture +/// of the text is not the actual text that you see on screen, it is +/// a big texture containing all the characters of the font in an +/// arbitrary order; thus, texture lookups on pixels other than the +/// current one may not give you the expected result. /// /// Shaders can also be used to apply global post-effects to the /// current contents of the target (like the old sf::PostFx class @@ -466,8 +614,8 @@ private : /// easily inserted anywhere without impacting all the code. /// /// Like sf::Texture that can be used as a raw OpenGL texture, -/// sf::Shader can also be used directly as a raw fragment -/// shader for custom OpenGL geometry. +/// sf::Shader can also be used directly as a raw shader for +/// custom OpenGL geometry. /// \code /// window.SetActive(); /// shader.Bind(); diff --git a/src/SFML/Graphics/Shader.cpp b/src/SFML/Graphics/Shader.cpp index ea0243d3..8847b0fa 100644 --- a/src/SFML/Graphics/Shader.cpp +++ b/src/SFML/Graphics/Shader.cpp @@ -27,6 +27,7 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include #include #include @@ -46,30 +47,50 @@ namespace GLCheck(glGetIntegerv(GL_MAX_TEXTURE_COORDS_ARB, &maxUnits)); return maxUnits; } + + // Read the contents of a file into an array of char + bool GetFileContents(const std::string& filename, std::vector& buffer) + { + std::ifstream file(filename.c_str(), std::ios_base::binary); + if (file) + { + file.seekg(0, std::ios_base::end); + std::streamsize size = file.tellg(); + file.seekg(0, std::ios_base::beg); + buffer.resize(size); + file.read(&buffer[0], size); + buffer.push_back('\0'); + return true; + } + else + { + return false; + } + } + + // Read the contents of a stream into an array of char + bool GetStreamContents(sf::InputStream& stream, std::vector& buffer) + { + sf::Int64 size = stream.GetSize(); + buffer.resize(static_cast(size)); + sf::Int64 read = stream.Read(&buffer[0], size); + buffer.push_back('\0'); + return read == size; + } } namespace sf { +//////////////////////////////////////////////////////////// +Shader::CurrentTextureType Shader::CurrentTexture; + + //////////////////////////////////////////////////////////// Shader::Shader() : myShaderProgram (0), myCurrentTexture(-1) { - -} - - -//////////////////////////////////////////////////////////// -Shader::Shader(const Shader& copy) : -myShaderProgram (0), -myCurrentTexture(copy.myCurrentTexture), -myTextures (copy.myTextures), -myFragmentShader(copy.myFragmentShader) -{ - // Create the shaders and the program - if (copy.myShaderProgram) - CompileProgram(); } @@ -85,48 +106,107 @@ Shader::~Shader() //////////////////////////////////////////////////////////// -bool Shader::LoadFromFile(const std::string& filename) +bool Shader::LoadFromFile(const std::string& filename, Type type) { - // Open the file - std::ifstream file(filename.c_str()); - if (!file) + // Read the file + std::vector shader; + if (!GetFileContents(filename, shader)) { Err() << "Failed to open shader file \"" << filename << "\"" << std::endl; return false; } - // Read the shader code from the file - myFragmentShader.clear(); - std::string line; - while (std::getline(file, line)) - myFragmentShader += line + "\n"; - - // Create the shaders and the program - return CompileProgram(); + // Compile the shader program + if (type == Vertex) + return CompileProgram(&shader[0], NULL); + else + return CompileProgram(NULL, &shader[0]); } //////////////////////////////////////////////////////////// -bool Shader::LoadFromMemory(const std::string& shader) +bool Shader::LoadFromFile(const std::string& vertexShaderFilename, const std::string& fragmentShaderFilename) { - // Save the shader code - myFragmentShader = shader; + // Read the vertex shader file + std::vector vertexShader; + if (!GetFileContents(vertexShaderFilename, vertexShader)) + { + Err() << "Failed to open vertex shader file \"" << vertexShaderFilename << "\"" << std::endl; + return false; + } - // Create the shaders and the program - return CompileProgram(); + // Read the fragment shader file + std::vector fragmentShader; + if (!GetFileContents(fragmentShaderFilename, fragmentShader)) + { + Err() << "Failed to open fragment shader file \"" << fragmentShaderFilename << "\"" << std::endl; + return false; + } + + // Compile the shader program + return CompileProgram(&vertexShader[0], &fragmentShader[0]); } //////////////////////////////////////////////////////////// -bool Shader::LoadFromStream(InputStream& stream) +bool Shader::LoadFromMemory(const std::string& shader, Type type) +{ + // Compile the shader program + if (type == Vertex) + return CompileProgram(shader.c_str(), NULL); + else + return CompileProgram(NULL, shader.c_str()); +} + + +//////////////////////////////////////////////////////////// +bool Shader::LoadFromMemory(const std::string& vertexShader, const std::string& fragmentShader) +{ + // Compile the shader program + return CompileProgram(vertexShader.c_str(), fragmentShader.c_str()); +} + + +//////////////////////////////////////////////////////////// +bool Shader::LoadFromStream(InputStream& stream, Type type) { // Read the shader code from the stream - std::vector buffer(static_cast(stream.GetSize())); - Int64 read = stream.Read(&buffer[0], buffer.size()); - myFragmentShader.assign(&buffer[0], &buffer[0] + read); + std::vector shader; + if (!GetStreamContents(stream, shader)) + { + Err() << "Failed to read shader from stream" << std::endl; + return false; + } - // Create the shaders and the program - return CompileProgram(); + // Compile the shader program + if (type == Vertex) + return CompileProgram(&shader[0], NULL); + else + return CompileProgram(NULL, &shader[0]); +} + + +//////////////////////////////////////////////////////////// +bool Shader::LoadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream) +{ + // Read the vertex shader code from the stream + std::vector vertexShader; + if (!GetStreamContents(vertexShaderStream, vertexShader)) + { + Err() << "Failed to read vertex shader from stream" << std::endl; + return false; + } + + // Read the fragment shader code from the stream + std::vector fragmentShader; + if (!GetStreamContents(fragmentShaderStream, fragmentShader)) + { + Err() << "Failed to read fragment shader from stream" << std::endl; + return false; + } + + // Compile the shader program + return CompileProgram(&vertexShader[0], &fragmentShader[0]); } @@ -241,7 +321,38 @@ void Shader::SetParameter(const std::string& name, const Vector3f& v) //////////////////////////////////////////////////////////// -void Shader::SetTexture(const std::string& name, const Texture& texture) +void Shader::SetParameter(const std::string& name, const Color& color) +{ + SetParameter(name, color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f); +} + + +//////////////////////////////////////////////////////////// +void Shader::SetParameter(const std::string& name, const sf::Transform& transform) +{ + if (myShaderProgram) + { + EnsureGlContext(); + + // Enable program + GLhandleARB program = glGetHandleARB(GL_PROGRAM_OBJECT_ARB); + GLCheck(glUseProgramObjectARB(myShaderProgram)); + + // Get parameter location and assign it new values + GLint location = glGetUniformLocationARB(myShaderProgram, name.c_str()); + if (location != -1) + GLCheck(glUniformMatrix4fvARB(location, 1, GL_FALSE, transform.GetMatrix())); + else + Err() << "Parameter \"" << name << "\" not found in shader" << std::endl; + + // Disable program + GLCheck(glUseProgramObjectARB(program)); + } +} + + +//////////////////////////////////////////////////////////// +void Shader::SetParameter(const std::string& name, const Texture& texture) { if (myShaderProgram) { @@ -279,7 +390,7 @@ void Shader::SetTexture(const std::string& name, const Texture& texture) //////////////////////////////////////////////////////////// -void Shader::SetCurrentTexture(const std::string& name) +void Shader::SetParameter(const std::string& name, CurrentTextureType) { if (myShaderProgram) { @@ -322,20 +433,6 @@ void Shader::Unbind() const } -//////////////////////////////////////////////////////////// -Shader& Shader::operator =(const Shader& right) -{ - Shader temp(right); - - std::swap(myShaderProgram, temp.myShaderProgram); - std::swap(myCurrentTexture, temp.myCurrentTexture); - std::swap(myTextures, temp.myTextures); - std::swap(myFragmentShader, temp.myFragmentShader); - - return *this; -} - - //////////////////////////////////////////////////////////// bool Shader::IsAvailable() { @@ -352,7 +449,7 @@ bool Shader::IsAvailable() //////////////////////////////////////////////////////////// -bool Shader::CompileProgram() +bool Shader::CompileProgram(const char* vertexShaderCode, const char* fragmentShaderCode) { EnsureGlContext(); @@ -368,74 +465,73 @@ bool Shader::CompileProgram() if (myShaderProgram) GLCheck(glDeleteObjectARB(myShaderProgram)); - // Define the vertex shader source (we provide it directly as it doesn't have to change) - static const char* vertexSrc = - "void main()" - "{" - " gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;" - " gl_FrontColor = gl_Color;" - " gl_Position = ftransform();" - "}"; - // Create the program myShaderProgram = glCreateProgramObjectARB(); - // Create the shaders - GLhandleARB vertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); - GLhandleARB fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); - - // Compile them - const char* fragmentSrc = myFragmentShader.c_str(); - GLCheck(glShaderSourceARB(vertexShader, 1, &vertexSrc, NULL)); - GLCheck(glShaderSourceARB(fragmentShader, 1, &fragmentSrc, NULL)); - GLCheck(glCompileShaderARB(vertexShader)); - GLCheck(glCompileShaderARB(fragmentShader)); - - // Check the compile logs - GLint success; - GLCheck(glGetObjectParameterivARB(vertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); - if (success == GL_FALSE) + // Create the vertex shader if needed + if (vertexShaderCode) { - char log[1024]; - GLCheck(glGetInfoLogARB(vertexShader, sizeof(log), 0, log)); - Err() << "Failed to compile shader:" << std::endl - << log << std::endl; + // Create and compile the shader + GLhandleARB vertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + GLCheck(glShaderSourceARB(vertexShader, 1, &vertexShaderCode, NULL)); + GLCheck(glCompileShaderARB(vertexShader)); + + // Check the compile log + GLint success; + GLCheck(glGetObjectParameterivARB(vertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); + if (success == GL_FALSE) + { + char log[1024]; + GLCheck(glGetInfoLogARB(vertexShader, sizeof(log), 0, log)); + Err() << "Failed to compile vertex shader:" << std::endl + << log << std::endl; + GLCheck(glDeleteObjectARB(vertexShader)); + GLCheck(glDeleteObjectARB(myShaderProgram)); + myShaderProgram = 0; + return false; + } + + // Attach the shader to the program, and delete it (not needed anymore) + GLCheck(glAttachObjectARB(myShaderProgram, vertexShader)); GLCheck(glDeleteObjectARB(vertexShader)); - GLCheck(glDeleteObjectARB(fragmentShader)); - GLCheck(glDeleteObjectARB(myShaderProgram)); - myShaderProgram = 0; - return false; - } - GLCheck(glGetObjectParameterivARB(fragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); - if (success == GL_FALSE) - { - char log[1024]; - GLCheck(glGetInfoLogARB(fragmentShader, sizeof(log), 0, log)); - Err() << "Failed to compile shader:" << std::endl - << log << std::endl; - GLCheck(glDeleteObjectARB(vertexShader)); - GLCheck(glDeleteObjectARB(fragmentShader)); - GLCheck(glDeleteObjectARB(myShaderProgram)); - myShaderProgram = 0; - return false; } - // Attach the shaders to the program - GLCheck(glAttachObjectARB(myShaderProgram, vertexShader)); - GLCheck(glAttachObjectARB(myShaderProgram, fragmentShader)); + // Create the fragment shader if needed + if (fragmentShaderCode) + { + // Create and compile the shader + GLhandleARB fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + GLCheck(glShaderSourceARB(fragmentShader, 1, &fragmentShaderCode, NULL)); + GLCheck(glCompileShaderARB(fragmentShader)); - // We can now delete the shaders - GLCheck(glDeleteObjectARB(vertexShader)); - GLCheck(glDeleteObjectARB(fragmentShader)); + // Check the compile log + GLint success; + GLCheck(glGetObjectParameterivARB(fragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &success)); + if (success == GL_FALSE) + { + char log[1024]; + GLCheck(glGetInfoLogARB(fragmentShader, sizeof(log), 0, log)); + Err() << "Failed to compile fragment shader:" << std::endl + << log << std::endl; + GLCheck(glDeleteObjectARB(fragmentShader)); + GLCheck(glDeleteObjectARB(myShaderProgram)); + myShaderProgram = 0; + return false; + } + + // Attach the shader to the program, and delete it (not needed anymore) + GLCheck(glAttachObjectARB(myShaderProgram, fragmentShader)); + GLCheck(glDeleteObjectARB(fragmentShader)); + } // Link the program GLCheck(glLinkProgramARB(myShaderProgram)); - // Get link log + // Check the link log + GLint success; GLCheck(glGetObjectParameterivARB(myShaderProgram, GL_OBJECT_LINK_STATUS_ARB, &success)); if (success == GL_FALSE) { - // Oops... link errors char log[1024]; GLCheck(glGetInfoLogARB(myShaderProgram, sizeof(log), 0, log)); Err() << "Failed to link shader:" << std::endl @@ -466,11 +562,4 @@ void Shader::BindTextures() const GLCheck(glActiveTextureARB(GL_TEXTURE0_ARB)); } - -//////////////////////////////////////////////////////////// -void Shader::Use() const -{ - BindTextures(); -} - } // namespace sf From e6956d8e4df8efa6817d9a5e778f895a8259388b Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sat, 10 Dec 2011 14:51:03 +0100 Subject: [PATCH 13/21] Removed the sf::StarShape class --- include/SFML/Graphics.hpp | 1 - include/SFML/Graphics/StarShape.hpp | 179 ---------------------------- src/SFML/Graphics/CMakeLists.txt | 2 - src/SFML/Graphics/StarShape.cpp | 117 ------------------ 4 files changed, 299 deletions(-) delete mode 100644 include/SFML/Graphics/StarShape.hpp delete mode 100644 src/SFML/Graphics/StarShape.cpp diff --git a/include/SFML/Graphics.hpp b/include/SFML/Graphics.hpp index f20aedd4..31e18c92 100644 --- a/include/SFML/Graphics.hpp +++ b/include/SFML/Graphics.hpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include diff --git a/include/SFML/Graphics/StarShape.hpp b/include/SFML/Graphics/StarShape.hpp deleted file mode 100644 index 90ef0937..00000000 --- a/include/SFML/Graphics/StarShape.hpp +++ /dev/null @@ -1,179 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -#ifndef SFML_STARSHAPE_HPP -#define SFML_STARSHAPE_HPP - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include - - -namespace sf -{ -//////////////////////////////////////////////////////////// -/// \brief Specialized shape representing a star -/// -//////////////////////////////////////////////////////////// -class SFML_API StarShape : public Shape -{ -public : - - //////////////////////////////////////////////////////////// - /// \brief Default constructor - /// - //////////////////////////////////////////////////////////// - StarShape(); - - //////////////////////////////////////////////////////////// - /// \brief Constructor - /// - /// \param innerRadius Inner radius of the star - /// \param outerRadius Outer radius of the star - /// \param pointsCount Number of points of the star - /// - //////////////////////////////////////////////////////////// - StarShape(float innerRadius, float outerRadius, unsigned int pointsCount); - - //////////////////////////////////////////////////////////// - /// \brief Set the inner radius of the star - /// - /// \param radius New inner radius of the star - /// - /// \see GetInnerRadius - /// - //////////////////////////////////////////////////////////// - void SetInnerRadius(float radius); - - //////////////////////////////////////////////////////////// - /// \brief Get the inner radius of the star - /// - /// \return Inner radius of the star - /// - /// \see SetInnerRadius - /// - //////////////////////////////////////////////////////////// - float GetInnerRadius() const; - - //////////////////////////////////////////////////////////// - /// \brief Set the outer radius of the star - /// - /// \param radius New outer radius of the star - /// - /// \see GetOuterRadius - /// - //////////////////////////////////////////////////////////// - void SetOuterRadius(float radius); - - //////////////////////////////////////////////////////////// - /// \brief Get the outer radius of the star - /// - /// \return Outer radius of the star - /// - /// \see SetOuterRadius - /// - //////////////////////////////////////////////////////////// - float GetOuterRadius() const; - - //////////////////////////////////////////////////////////// - /// \brief Set the number of points of the star - /// - /// \param count New number of points of the star - /// - /// \see GetPointsCount - /// - //////////////////////////////////////////////////////////// - void SetPointsCount(unsigned int count); - - //////////////////////////////////////////////////////////// - /// \brief Get the number of points of the star - /// - /// \return Number of points of the star - /// - /// \see SetPointsCount - /// - //////////////////////////////////////////////////////////// - unsigned int GetPointsCount() const; - -private : - - //////////////////////////////////////////////////////////// - /// \brief Get the number of points defining the shape - /// - /// \return Number of points of the shape - /// - //////////////////////////////////////////////////////////// - virtual unsigned int GetOutlinePointsCount() const; - - //////////////////////////////////////////////////////////// - /// \brief Get a point of the shape - /// - /// \param index Index of the point to get - /// - /// \return Index-th point of the shape - /// - //////////////////////////////////////////////////////////// - virtual Vector2f GetOutlinePoint(unsigned int index) const; - -private : - - //////////////////////////////////////////////////////////// - // Member data - //////////////////////////////////////////////////////////// - float myInnerRadius; ///< Inner radius of the star - float myOuterRadius; ///< Outer radius of the star - unsigned int myPointsCount; ///< Number of points of the star -}; - -} // namespace sf - - -#endif // SFML_STARSHAPE_HPP - - -//////////////////////////////////////////////////////////// -/// \class sf::StarShape -/// \ingroup graphics -/// -/// This class inherits all the functions of sf::Transformable -/// (position, rotation, scale, bounds, ...) as well as the -/// functions of sf::Shape (outline, color, texture, ...). -/// -/// Usage example: -/// \code -/// sf::StarShape star; -/// star.SetInnerRadius(25); -/// star.SetInnerRadius(40); -/// star.SetPointsCount(6); -/// star.SetOutlineColor(sf::Color::Red); -/// star.SetOutlineThickness(5); -/// star.SetPosition(10, 20); -/// ... -/// window.Draw(star); -/// \endcode -/// -/// \see sf::Shape, sf::CircleShape, sf::RectangleShape, sf::ConvexShape -/// -//////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/CMakeLists.txt b/src/SFML/Graphics/CMakeLists.txt index edb75454..1adabaed 100644 --- a/src/SFML/Graphics/CMakeLists.txt +++ b/src/SFML/Graphics/CMakeLists.txt @@ -43,8 +43,6 @@ set(SRC ${INCROOT}/CircleShape.hpp ${SRCROOT}/RectangleShape.cpp ${INCROOT}/RectangleShape.hpp - ${SRCROOT}/StarShape.cpp - ${INCROOT}/StarShape.hpp ${SRCROOT}/ConvexShape.cpp ${INCROOT}/ConvexShape.hpp ${SRCROOT}/Sprite.cpp diff --git a/src/SFML/Graphics/StarShape.cpp b/src/SFML/Graphics/StarShape.cpp deleted file mode 100644 index 138b0944..00000000 --- a/src/SFML/Graphics/StarShape.cpp +++ /dev/null @@ -1,117 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include - - -namespace sf -{ -//////////////////////////////////////////////////////////// -StarShape::StarShape() : -myInnerRadius(0), -myOuterRadius(0), -myPointsCount(0) -{ -} - - -//////////////////////////////////////////////////////////// -StarShape::StarShape(float innerRadius, float outerRadius, unsigned int pointsCount) : -myInnerRadius(innerRadius), -myOuterRadius(outerRadius), -myPointsCount(pointsCount) -{ - Update(); -} - - -//////////////////////////////////////////////////////////// -void StarShape::SetInnerRadius(float radius) -{ - myInnerRadius = radius; - Update(); -} - - -//////////////////////////////////////////////////////////// -float StarShape::GetInnerRadius() const -{ - return myInnerRadius; -} - - -//////////////////////////////////////////////////////////// -void StarShape::SetOuterRadius(float radius) -{ - myOuterRadius = radius; - Update(); -} - - -//////////////////////////////////////////////////////////// -float StarShape::GetOuterRadius() const -{ - return myOuterRadius; -} - - -//////////////////////////////////////////////////////////// -void StarShape::SetPointsCount(unsigned int count) -{ - myPointsCount = count; - Update(); -} - - -//////////////////////////////////////////////////////////// -unsigned int StarShape::GetPointsCount() const -{ - return myPointsCount; -} - - -//////////////////////////////////////////////////////////// -unsigned int StarShape::GetOutlinePointsCount() const -{ - return myPointsCount * 2; -} - - -//////////////////////////////////////////////////////////// -Vector2f StarShape::GetOutlinePoint(unsigned int index) const -{ - static const float pi = 3.141592654f; - float angle = index * pi / myPointsCount - pi / 2; - float x = std::cos(angle); - float y = std::sin(angle); - float radius = (index % 2 == 0 ? myOuterRadius : myInnerRadius); - - return Vector2f(myOuterRadius + x * radius, myOuterRadius + y * radius); -} - -} // namespace sf From 048abbf46f1654edd40bf2628d44474d5c8e1730 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sat, 10 Dec 2011 14:51:40 +0100 Subject: [PATCH 14/21] Renamed GetOutlinePoint/GetOutlinePointsCount to GetPoint/GetPointsCount in sf::Shape, and made them public --- include/SFML/Graphics/CircleShape.hpp | 10 +++--- include/SFML/Graphics/ConvexShape.hpp | 26 ++------------- include/SFML/Graphics/RectangleShape.hpp | 8 ++--- include/SFML/Graphics/Shape.hpp | 42 ++++++++++-------------- src/SFML/Graphics/CircleShape.cpp | 6 ++-- src/SFML/Graphics/ConvexShape.cpp | 14 -------- src/SFML/Graphics/RectangleShape.cpp | 4 +-- src/SFML/Graphics/Shape.cpp | 5 ++- 8 files changed, 35 insertions(+), 80 deletions(-) diff --git a/include/SFML/Graphics/CircleShape.hpp b/include/SFML/Graphics/CircleShape.hpp index cb0ffed0..0c7b3f21 100644 --- a/include/SFML/Graphics/CircleShape.hpp +++ b/include/SFML/Graphics/CircleShape.hpp @@ -69,15 +69,13 @@ public : //////////////////////////////////////////////////////////// float GetRadius() const; -private : - //////////////////////////////////////////////////////////// - /// \brief Get the number of points defining the shape + /// \brief Get the number of points of the shape /// /// \return Number of points of the shape /// //////////////////////////////////////////////////////////// - virtual unsigned int GetOutlinePointsCount() const; + virtual unsigned int GetPointsCount() const; //////////////////////////////////////////////////////////// /// \brief Get a point of the shape @@ -87,7 +85,7 @@ private : /// \return Index-th point of the shape /// //////////////////////////////////////////////////////////// - virtual Vector2f GetOutlinePoint(unsigned int index) const; + virtual Vector2f GetPoint(unsigned int index) const; private : @@ -122,6 +120,6 @@ private : /// window.Draw(circle); /// \endcode /// -/// \see sf::Shape, sf::StarShape, sf::RectangleShape, sf::ConvexShape +/// \see sf::Shape, sf::RectangleShape, sf::ConvexShape /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/ConvexShape.hpp b/include/SFML/Graphics/ConvexShape.hpp index ed3ccd39..771f1521 100644 --- a/include/SFML/Graphics/ConvexShape.hpp +++ b/include/SFML/Graphics/ConvexShape.hpp @@ -68,7 +68,7 @@ public : /// \see SetPointsCount /// //////////////////////////////////////////////////////////// - unsigned int GetPointsCount() const; + virtual unsigned int GetPointsCount() const; //////////////////////////////////////////////////////////// /// \brief Set the position of a point @@ -94,27 +94,7 @@ public : /// \see SetPoint /// //////////////////////////////////////////////////////////// - Vector2f GetPoint(unsigned int index) const; - -private : - - //////////////////////////////////////////////////////////// - /// \brief Get the number of points defining the shape - /// - /// \return Number of points of the shape - /// - //////////////////////////////////////////////////////////// - virtual unsigned int GetOutlinePointsCount() const; - - //////////////////////////////////////////////////////////// - /// \brief Get a point of the shape - /// - /// \param index Index of the point to get - /// - /// \return Index-th point of the shape - /// - //////////////////////////////////////////////////////////// - virtual Vector2f GetOutlinePoint(unsigned int index) const; + virtual Vector2f GetPoint(unsigned int index) const; private : @@ -157,6 +137,6 @@ private : /// window.Draw(polygon); /// \endcode /// -/// \see sf::Shape, sf::StarShape, sf::RectangleShape, sf::CircleShape +/// \see sf::Shape, sf::RectangleShape, sf::CircleShape /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/RectangleShape.hpp b/include/SFML/Graphics/RectangleShape.hpp index 48e9f5d4..6d3b4c04 100644 --- a/include/SFML/Graphics/RectangleShape.hpp +++ b/include/SFML/Graphics/RectangleShape.hpp @@ -69,15 +69,13 @@ public : //////////////////////////////////////////////////////////// const Vector2f& GetSize() const; -private : - //////////////////////////////////////////////////////////// /// \brief Get the number of points defining the shape /// /// \return Number of points of the shape /// //////////////////////////////////////////////////////////// - virtual unsigned int GetOutlinePointsCount() const; + virtual unsigned int GetPointsCount() const; //////////////////////////////////////////////////////////// /// \brief Get a point of the shape @@ -87,7 +85,7 @@ private : /// \return Index-th point of the shape /// //////////////////////////////////////////////////////////// - virtual Vector2f GetOutlinePoint(unsigned int index) const; + virtual Vector2f GetPoint(unsigned int index) const; private : @@ -122,6 +120,6 @@ private : /// window.Draw(rectangle); /// \endcode /// -/// \see sf::Shape, sf::StarShape, sf::CircleShape, sf::ConvexShape +/// \see sf::Shape, sf::CircleShape, sf::ConvexShape /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/Graphics/Shape.hpp b/include/SFML/Graphics/Shape.hpp index e38c1784..1b545b41 100644 --- a/include/SFML/Graphics/Shape.hpp +++ b/include/SFML/Graphics/Shape.hpp @@ -184,6 +184,24 @@ public : //////////////////////////////////////////////////////////// float GetOutlineThickness() const; + //////////////////////////////////////////////////////////// + /// \brief Get the total number of points of the shape + /// + /// \return Number of points of the shape + /// + //////////////////////////////////////////////////////////// + virtual unsigned int GetPointsCount() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get a point of the shape + /// + /// \param index Index of the point to get, in range [0 .. GetPointsCount() - 1] + /// + /// \return Index-th point of the shape + /// + //////////////////////////////////////////////////////////// + virtual Vector2f GetPoint(unsigned int index) const = 0; + //////////////////////////////////////////////////////////// /// \brief Get the local bounding rectangle of the entity /// @@ -230,30 +248,6 @@ protected : //////////////////////////////////////////////////////////// void Update(); -private : - - //////////////////////////////////////////////////////////// - /// \brief Get the number of points defining the shape - /// - /// This function must be implemented in derived classes. - /// - /// \return Number of points of the shape - /// - //////////////////////////////////////////////////////////// - virtual unsigned int GetOutlinePointsCount() const = 0; - - //////////////////////////////////////////////////////////// - /// \brief Get a point of the shape - /// - /// This function must be implemented in derived classes. - /// - /// \param index Index of the point to get - /// - /// \return Index-th point of the shape - /// - //////////////////////////////////////////////////////////// - virtual Vector2f GetOutlinePoint(unsigned int index) const = 0; - private : //////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/CircleShape.cpp b/src/SFML/Graphics/CircleShape.cpp index 4830e71f..cbf692dd 100644 --- a/src/SFML/Graphics/CircleShape.cpp +++ b/src/SFML/Graphics/CircleShape.cpp @@ -54,16 +54,16 @@ float CircleShape::GetRadius() const //////////////////////////////////////////////////////////// -unsigned int CircleShape::GetOutlinePointsCount() const +unsigned int CircleShape::GetPointsCount() const { return 30; } //////////////////////////////////////////////////////////// -Vector2f CircleShape::GetOutlinePoint(unsigned int index) const +Vector2f CircleShape::GetPoint(unsigned int index) const { - float angle = index * 2 * 3.141592654f / GetOutlinePointsCount(); + float angle = index * 2 * 3.141592654f / GetPointsCount(); float x = std::cos(angle) * myRadius; float y = std::sin(angle) * myRadius; diff --git a/src/SFML/Graphics/ConvexShape.cpp b/src/SFML/Graphics/ConvexShape.cpp index 6c6954f1..d60b583b 100644 --- a/src/SFML/Graphics/ConvexShape.cpp +++ b/src/SFML/Graphics/ConvexShape.cpp @@ -66,18 +66,4 @@ Vector2f ConvexShape::GetPoint(unsigned int index) const return myPoints[index]; } - -//////////////////////////////////////////////////////////// -unsigned int ConvexShape::GetOutlinePointsCount() const -{ - return GetPointsCount(); -} - - -//////////////////////////////////////////////////////////// -Vector2f ConvexShape::GetOutlinePoint(unsigned int index) const -{ - return GetPoint(index); -} - } // namespace sf diff --git a/src/SFML/Graphics/RectangleShape.cpp b/src/SFML/Graphics/RectangleShape.cpp index 3b9ffd77..0d49192d 100644 --- a/src/SFML/Graphics/RectangleShape.cpp +++ b/src/SFML/Graphics/RectangleShape.cpp @@ -54,14 +54,14 @@ const Vector2f& RectangleShape::GetSize() const //////////////////////////////////////////////////////////// -unsigned int RectangleShape::GetOutlinePointsCount() const +unsigned int RectangleShape::GetPointsCount() const { return 4; } //////////////////////////////////////////////////////////// -Vector2f RectangleShape::GetOutlinePoint(unsigned int index) const +Vector2f RectangleShape::GetPoint(unsigned int index) const { switch (index) { diff --git a/src/SFML/Graphics/Shape.cpp b/src/SFML/Graphics/Shape.cpp index 3a0e87cb..656cc370 100644 --- a/src/SFML/Graphics/Shape.cpp +++ b/src/SFML/Graphics/Shape.cpp @@ -169,7 +169,7 @@ myBounds () void Shape::Update() { // Get the total number of points of the shape - unsigned int count = GetOutlinePointsCount(); + unsigned int count = GetPointsCount(); if (count < 3) { myVertices.Resize(0); @@ -180,9 +180,8 @@ void Shape::Update() myVertices.Resize(count + 2); // + 2 for center and repeated first point // Position - Vector2f offset(myOutlineThickness, myOutlineThickness); for (unsigned int i = 0; i < count; ++i) - myVertices[i + 1].Position = GetOutlinePoint(i) + offset; + myVertices[i + 1].Position = GetPoint(i); myVertices[count + 1].Position = myVertices[1].Position; // Update the bounding rectangle From c33fa1d2908aed1ce71aece82c6d03923c3e0386 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sat, 10 Dec 2011 15:22:21 +0100 Subject: [PATCH 15/21] Added SetPointsCount in sf::CircleShape --- include/SFML/Graphics/CircleShape.hpp | 29 ++++++++++++++++++++++++--- src/SFML/Graphics/CircleShape.cpp | 19 ++++++++++++++---- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/include/SFML/Graphics/CircleShape.hpp b/include/SFML/Graphics/CircleShape.hpp index 0c7b3f21..446dcc36 100644 --- a/include/SFML/Graphics/CircleShape.hpp +++ b/include/SFML/Graphics/CircleShape.hpp @@ -44,10 +44,11 @@ public : //////////////////////////////////////////////////////////// /// \brief Default constructor /// - /// \param radius Radius of the circle + /// \param radius Radius of the circle + /// \param pointsCount Number of points composing the circle /// //////////////////////////////////////////////////////////// - explicit CircleShape(float radius = 0); + explicit CircleShape(float radius = 0, unsigned int pointsCount = 30); //////////////////////////////////////////////////////////// /// \brief Set the radius of the circle @@ -69,11 +70,23 @@ public : //////////////////////////////////////////////////////////// float GetRadius() const; + //////////////////////////////////////////////////////////// + /// \brief Set the number of points of the circle + /// + /// \param count New number of points of the circle + /// + /// \see GetPointsCount + /// + //////////////////////////////////////////////////////////// + void SetPointsCount(unsigned int count); + //////////////////////////////////////////////////////////// /// \brief Get the number of points of the shape /// /// \return Number of points of the shape /// + /// \see SetPointsCount + /// //////////////////////////////////////////////////////////// virtual unsigned int GetPointsCount() const; @@ -92,7 +105,8 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - float myRadius; ///< Radius of the circle + float myRadius; ///< Radius of the circle + unsigned int myPointsCount; ///< Number of points composing the circle }; } // namespace sf @@ -120,6 +134,15 @@ private : /// window.Draw(circle); /// \endcode /// +/// Since the graphics card can't draw perfect circles, we have to +/// fake them with multiple triangles connected to each other. The +/// "points count" property of sf::CircleShape defines how many of these +/// triangles to use, and therefore defines the quality of the circle. +/// +/// The number of points can also be used for another purpose; with +/// small numbers you can create any regular polygon shape: +/// equilateral triangle, square, pentagon, hexagon, ... +/// /// \see sf::Shape, sf::RectangleShape, sf::ConvexShape /// //////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/CircleShape.cpp b/src/SFML/Graphics/CircleShape.cpp index cbf692dd..4d6b0e89 100644 --- a/src/SFML/Graphics/CircleShape.cpp +++ b/src/SFML/Graphics/CircleShape.cpp @@ -32,9 +32,11 @@ namespace sf { //////////////////////////////////////////////////////////// -CircleShape::CircleShape(float radius) +CircleShape::CircleShape(float radius, unsigned int pointsCount) : +myRadius (radius), +myPointsCount(pointsCount) { - SetRadius(radius); + Update(); } @@ -53,17 +55,26 @@ float CircleShape::GetRadius() const } +//////////////////////////////////////////////////////////// +void CircleShape::SetPointsCount(unsigned int count) +{ + myPointsCount = count; + Update(); +} + //////////////////////////////////////////////////////////// unsigned int CircleShape::GetPointsCount() const { - return 30; + return myPointsCount; } //////////////////////////////////////////////////////////// Vector2f CircleShape::GetPoint(unsigned int index) const { - float angle = index * 2 * 3.141592654f / GetPointsCount(); + static const float pi = 3.141592654f; + + float angle = index * 2 * pi / myPointsCount - pi / 2; float x = std::cos(angle) * myRadius; float y = std::sin(angle) * myRadius; From 78e1e8732b084ab74ef104575fa2604957e19274 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Wed, 14 Dec 2011 07:34:47 +0100 Subject: [PATCH 16/21] Fixed (?) a fragment shader in the "Shader" example --- examples/shader/resources/edge.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shader/resources/edge.frag b/examples/shader/resources/edge.frag index 173a1e71..14551f98 100644 --- a/examples/shader/resources/edge.frag +++ b/examples/shader/resources/edge.frag @@ -24,7 +24,7 @@ void main() vec3 result = sqrt(hEdge.rgb * hEdge.rgb + vEdge.rgb * vEdge.rgb); float edge = length(result); vec4 pixel = gl_Color * texture2D(texture, gl_TexCoord[0].xy); - if (edge > edge_threshold * 8) + if (edge > (edge_threshold * 8.0)) pixel.rgb = vec3(0.0, 0.0, 0.0); else pixel.a = edge_threshold; From bc95d855b38b159f4921570fbdb728f925956b00 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Mon, 19 Dec 2011 17:50:15 +0100 Subject: [PATCH 17/21] Added a virtual destructor to sf::Transformable --- include/SFML/Graphics/Transformable.hpp | 6 ++++++ src/SFML/Graphics/Transformable.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/include/SFML/Graphics/Transformable.hpp b/include/SFML/Graphics/Transformable.hpp index 91a0d498..e42af36c 100644 --- a/include/SFML/Graphics/Transformable.hpp +++ b/include/SFML/Graphics/Transformable.hpp @@ -47,6 +47,12 @@ public : //////////////////////////////////////////////////////////// Transformable(); + //////////////////////////////////////////////////////////// + /// \brief Virtual destructor + /// + //////////////////////////////////////////////////////////// + virtual ~Transformable(); + //////////////////////////////////////////////////////////// /// \brief Set the position of the object /// diff --git a/src/SFML/Graphics/Transformable.cpp b/src/SFML/Graphics/Transformable.cpp index 31130bac..004a22b5 100644 --- a/src/SFML/Graphics/Transformable.cpp +++ b/src/SFML/Graphics/Transformable.cpp @@ -45,6 +45,12 @@ myInverseTransformNeedUpdate(true) } +//////////////////////////////////////////////////////////// +Transformable::~Transformable() +{ +} + + //////////////////////////////////////////////////////////// void Transformable::SetPosition(float x, float y) { From 78910b555e13d0798751c321f6b3ac447d3cf347 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Wed, 21 Dec 2011 22:27:19 +0100 Subject: [PATCH 18/21] Removed unused Context::GetCurrentContextId() --- include/SFML/Window/Context.hpp | 14 -------------- src/SFML/Window/Context.cpp | 7 ------- src/SFML/Window/GlContext.cpp | 7 ------- src/SFML/Window/GlContext.hpp | 10 ---------- 4 files changed, 38 deletions(-) diff --git a/include/SFML/Window/Context.hpp b/include/SFML/Window/Context.hpp index 48b21491..def5acf9 100644 --- a/include/SFML/Window/Context.hpp +++ b/include/SFML/Window/Context.hpp @@ -41,8 +41,6 @@ namespace priv class GlContext; } -typedef void* ContextId; - //////////////////////////////////////////////////////////// /// \brief Class holding a valid drawing context /// @@ -77,18 +75,6 @@ public : //////////////////////////////////////////////////////////// bool SetActive(bool active); - //////////////////////////////////////////////////////////// - /// \brief Return the identifier of the current active context - /// - /// The returned id has no special meaning, it should only be - /// used as a key to map external stuff to internal contexts. - /// This function returns 0 if no context is active. - /// - /// \return Identifier of the current context - /// - //////////////////////////////////////////////////////////// - static ContextId GetCurrentContextId(); - public : //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/Context.cpp b/src/SFML/Window/Context.cpp index 354b4cdc..961c2508 100644 --- a/src/SFML/Window/Context.cpp +++ b/src/SFML/Window/Context.cpp @@ -53,13 +53,6 @@ bool Context::SetActive(bool active) } -//////////////////////////////////////////////////////////// -ContextId Context::GetCurrentContextId() -{ - return priv::GlContext::GetCurrentContext(); -} - - //////////////////////////////////////////////////////////// Context::Context(const ContextSettings& settings, unsigned int width, unsigned int height) { diff --git a/src/SFML/Window/GlContext.cpp b/src/SFML/Window/GlContext.cpp index 51f808ae..196ed9c8 100644 --- a/src/SFML/Window/GlContext.cpp +++ b/src/SFML/Window/GlContext.cpp @@ -177,13 +177,6 @@ GlContext* GlContext::New(const ContextSettings& settings, unsigned int width, u } -//////////////////////////////////////////////////////////// -GlContext* GlContext::GetCurrentContext() -{ - return currentContext; -} - - //////////////////////////////////////////////////////////// GlContext::~GlContext() { diff --git a/src/SFML/Window/GlContext.hpp b/src/SFML/Window/GlContext.hpp index 015ef72a..f6eb193a 100644 --- a/src/SFML/Window/GlContext.hpp +++ b/src/SFML/Window/GlContext.hpp @@ -118,16 +118,6 @@ public : //////////////////////////////////////////////////////////// static GlContext* New(const ContextSettings& settings, unsigned int width, unsigned int height); - //////////////////////////////////////////////////////////// - /// \brief Return the current active context - /// - /// This function returns 0 if no context is active. - /// - /// \return Context currently active in this thread - /// - //////////////////////////////////////////////////////////// - static GlContext* GetCurrentContext(); - public : //////////////////////////////////////////////////////////// From 5a4e8d58af1794978e3ab37097280d360f839447 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Wed, 21 Dec 2011 22:31:42 +0100 Subject: [PATCH 19/21] Minor modifications --- include/SFML/Graphics/Transform.hpp | 2 +- src/SFML/Graphics/GLCheck.hpp | 2 +- src/SFML/Graphics/Transform.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/SFML/Graphics/Transform.hpp b/include/SFML/Graphics/Transform.hpp index 17459989..026fe43e 100644 --- a/include/SFML/Graphics/Transform.hpp +++ b/include/SFML/Graphics/Transform.hpp @@ -358,7 +358,7 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - float myMatrix[16]; /// 4x4 matrix defining the transformation + float myMatrix[16]; ///< 4x4 matrix defining the transformation }; //////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/GLCheck.hpp b/src/SFML/Graphics/GLCheck.hpp index 3be57e1a..560210c6 100644 --- a/src/SFML/Graphics/GLCheck.hpp +++ b/src/SFML/Graphics/GLCheck.hpp @@ -44,7 +44,7 @@ namespace priv #ifdef SFML_DEBUG // In debug mode, perform a test on every OpenGL call -#define GLCheck(call) ((call), sf::priv::GLCheckError(__FILE__, __LINE__)) + #define GLCheck(call) ((call), sf::priv::GLCheckError(__FILE__, __LINE__)) #else diff --git a/src/SFML/Graphics/Transform.cpp b/src/SFML/Graphics/Transform.cpp index 181e1bbd..3d459ca6 100644 --- a/src/SFML/Graphics/Transform.cpp +++ b/src/SFML/Graphics/Transform.cpp @@ -89,7 +89,7 @@ Transform Transform::GetInverse() const } else { - return Transform(); + return Identity; } } From b65b19343abf9e1becd13b3fcd87b003f67eb2a3 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Wed, 21 Dec 2011 22:44:21 +0100 Subject: [PATCH 20/21] Changed the type of Vertex::TexCoords from integers to floats, to make it compatible with buggy ATI drivers --- include/SFML/Graphics/Vertex.hpp | 18 ++++++++++------- src/SFML/Graphics/RenderTarget.cpp | 2 +- src/SFML/Graphics/Shape.cpp | 4 ++-- src/SFML/Graphics/Sprite.cpp | 16 +++++++-------- src/SFML/Graphics/Text.cpp | 32 +++++++++++++++--------------- src/SFML/Graphics/Vertex.cpp | 4 ++-- 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/include/SFML/Graphics/Vertex.hpp b/include/SFML/Graphics/Vertex.hpp index f8a09119..f7e9ae60 100644 --- a/include/SFML/Graphics/Vertex.hpp +++ b/include/SFML/Graphics/Vertex.hpp @@ -78,7 +78,7 @@ public : /// \param texCoords Vertex texture coordinates /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& position, const Vector2i& texCoords); + Vertex(const Vector2f& position, const Vector2f& texCoords); //////////////////////////////////////////////////////////// /// \brief Construct the vertex from its position, color and texture coordinates @@ -88,14 +88,14 @@ public : /// \param texCoords Vertex texture coordinates /// //////////////////////////////////////////////////////////// - Vertex(const Vector2f& position, const sf::Color& color, const Vector2i& texCoords); + Vertex(const Vector2f& position, const sf::Color& color, const Vector2f& texCoords); //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// Vector2f Position; ///< 2D position of the vertex sf::Color Color; ///< Color of the vertex - Vector2i TexCoords; ///< Coordinates of the texture's pixel to map to the vertex + Vector2f TexCoords; ///< Coordinates of the texture's pixel to map to the vertex }; } // namespace sf @@ -128,16 +128,20 @@ public : /// // define a 100x100 square, red, with a 10x10 texture mapped on it /// sf::Vertex vertices[] = /// { -/// sf::Vertex(sf::Vector2f( 0, 0), sf::Color::Red, sf::Vector2i( 0, 0)), -/// sf::Vertex(sf::Vector2f( 0, 100), sf::Color::Red, sf::Vector2i( 0, 10)), -/// sf::Vertex(sf::Vector2f(100, 100), sf::Color::Red, sf::Vector2i(10, 10)), -/// sf::Vertex(sf::Vector2f(100, 0), sf::Color::Red, sf::Vector2i(10, 0)) +/// sf::Vertex(sf::Vector2f( 0, 0), sf::Color::Red, sf::Vector2f( 0, 0)), +/// sf::Vertex(sf::Vector2f( 0, 100), sf::Color::Red, sf::Vector2f( 0, 10)), +/// sf::Vertex(sf::Vector2f(100, 100), sf::Color::Red, sf::Vector2f(10, 10)), +/// sf::Vertex(sf::Vector2f(100, 0), sf::Color::Red, sf::Vector2f(10, 0)) /// }; /// /// // draw it /// window.Draw(vertices, 4, sf::Quads); /// \endcode /// +/// Note: although texture coordinates are supposed to be an integer +/// amount of pixels, their type is float because of some buggy graphics +/// drivers that are not able to process integer coordinates correctly. +/// /// \see sf::VertexArray /// //////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index 4d8d593f..3fbf730c 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -201,7 +201,7 @@ void RenderTarget::Draw(const Vertex* vertices, unsigned int verticesCount, const char* data = reinterpret_cast(vertices); GLCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); GLCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); - GLCheck(glTexCoordPointer(2, GL_INT, sizeof(Vertex), data + 12)); + GLCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); // Find the OpenGL primitive type static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, diff --git a/src/SFML/Graphics/Shape.cpp b/src/SFML/Graphics/Shape.cpp index 656cc370..de585e92 100644 --- a/src/SFML/Graphics/Shape.cpp +++ b/src/SFML/Graphics/Shape.cpp @@ -239,8 +239,8 @@ void Shape::UpdateTexCoords() { float xratio = (myVertices[i].Position.x - myInsideBounds.Left) / myInsideBounds.Width; float yratio = (myVertices[i].Position.y - myInsideBounds.Top) / myInsideBounds.Height; - myVertices[i].TexCoords.x = static_cast(myTextureRect.Left + myTextureRect.Width * xratio); - myVertices[i].TexCoords.y = static_cast(myTextureRect.Top + myTextureRect.Height * yratio); + myVertices[i].TexCoords.x = myTextureRect.Left + myTextureRect.Width * xratio; + myVertices[i].TexCoords.y = myTextureRect.Top + myTextureRect.Height * yratio; } } diff --git a/src/SFML/Graphics/Sprite.cpp b/src/SFML/Graphics/Sprite.cpp index 23ac67a8..cb8b1db0 100644 --- a/src/SFML/Graphics/Sprite.cpp +++ b/src/SFML/Graphics/Sprite.cpp @@ -160,15 +160,15 @@ void Sprite::UpdatePositions() //////////////////////////////////////////////////////////// void Sprite::UpdateTexCoords() { - int left = myTextureRect.Left; - int right = myTextureRect.Left + myTextureRect.Width; - int top = myTextureRect.Top; - int bottom = myTextureRect.Top + myTextureRect.Height; + float left = static_cast(myTextureRect.Left); + float right = left + myTextureRect.Width; + float top = static_cast(myTextureRect.Top); + float bottom = top + myTextureRect.Height; - myVertices[0].TexCoords = Vector2i(left, top); - myVertices[1].TexCoords = Vector2i(left, bottom); - myVertices[2].TexCoords = Vector2i(right, bottom); - myVertices[3].TexCoords = Vector2i(right, top); + myVertices[0].TexCoords = Vector2f(left, top); + myVertices[1].TexCoords = Vector2f(left, bottom); + myVertices[2].TexCoords = Vector2f(right, bottom); + myVertices[3].TexCoords = Vector2f(right, top); } } // namespace sf diff --git a/src/SFML/Graphics/Text.cpp b/src/SFML/Graphics/Text.cpp index 62b4a812..a602818b 100644 --- a/src/SFML/Graphics/Text.cpp +++ b/src/SFML/Graphics/Text.cpp @@ -260,10 +260,10 @@ void Text::UpdateGeometry() float top = y + underlineOffset; float bottom = top + underlineThickness; - myVertices.Append(Vertex(Vector2f(0, top), myColor, Vector2i(1, 1))); - myVertices.Append(Vertex(Vector2f(x, top), myColor, Vector2i(2, 1))); - myVertices.Append(Vertex(Vector2f(x, bottom), myColor, Vector2i(2, 2))); - myVertices.Append(Vertex(Vector2f(0, bottom), myColor, Vector2i(1, 2))); + myVertices.Append(Vertex(Vector2f(0, top), myColor, Vector2f(1, 1))); + myVertices.Append(Vertex(Vector2f(x, top), myColor, Vector2f(2, 1))); + myVertices.Append(Vertex(Vector2f(x, bottom), myColor, Vector2f(2, 2))); + myVertices.Append(Vertex(Vector2f(0, bottom), myColor, Vector2f(1, 2))); } // Handle special characters @@ -283,16 +283,16 @@ void Text::UpdateGeometry() int right = glyph.Bounds.Left + glyph.Bounds.Width; int bottom = glyph.Bounds.Top + glyph.Bounds.Height; - int u1 = glyph.TextureRect.Left; - int v1 = glyph.TextureRect.Top; - int u2 = glyph.TextureRect.Left + glyph.TextureRect.Width; - int v2 = glyph.TextureRect.Top + glyph.TextureRect.Height; + float u1 = static_cast(glyph.TextureRect.Left); + float v1 = static_cast(glyph.TextureRect.Top); + float u2 = static_cast(glyph.TextureRect.Left + glyph.TextureRect.Width); + float v2 = static_cast(glyph.TextureRect.Top + glyph.TextureRect.Height); // Add a quad for the current character - myVertices.Append(Vertex(Vector2f(x + left - italic * top, y + top), myColor, Vector2i(u1, v1))); - myVertices.Append(Vertex(Vector2f(x + right - italic * top, y + top), myColor, Vector2i(u2, v1))); - myVertices.Append(Vertex(Vector2f(x + right - italic * bottom, y + bottom), myColor, Vector2i(u2, v2))); - myVertices.Append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), myColor, Vector2i(u1, v2))); + myVertices.Append(Vertex(Vector2f(x + left - italic * top, y + top), myColor, Vector2f(u1, v1))); + myVertices.Append(Vertex(Vector2f(x + right - italic * top, y + top), myColor, Vector2f(u2, v1))); + myVertices.Append(Vertex(Vector2f(x + right - italic * bottom, y + bottom), myColor, Vector2f(u2, v2))); + myVertices.Append(Vertex(Vector2f(x + left - italic * bottom, y + bottom), myColor, Vector2f(u1, v2))); // Advance to the next character x += glyph.Advance; @@ -304,10 +304,10 @@ void Text::UpdateGeometry() float top = y + underlineOffset; float bottom = top + underlineThickness; - myVertices.Append(Vertex(Vector2f(0, top), myColor, Vector2i(1, 1))); - myVertices.Append(Vertex(Vector2f(x, top), myColor, Vector2i(2, 1))); - myVertices.Append(Vertex(Vector2f(x, bottom), myColor, Vector2i(2, 2))); - myVertices.Append(Vertex(Vector2f(0, bottom), myColor, Vector2i(1, 2))); + myVertices.Append(Vertex(Vector2f(0, top), myColor, Vector2f(1, 1))); + myVertices.Append(Vertex(Vector2f(x, top), myColor, Vector2f(2, 1))); + myVertices.Append(Vertex(Vector2f(x, bottom), myColor, Vector2f(2, 2))); + myVertices.Append(Vertex(Vector2f(0, bottom), myColor, Vector2f(1, 2))); } // Recompute the bounding rectangle diff --git a/src/SFML/Graphics/Vertex.cpp b/src/SFML/Graphics/Vertex.cpp index 43cf1905..abdbedcd 100644 --- a/src/SFML/Graphics/Vertex.cpp +++ b/src/SFML/Graphics/Vertex.cpp @@ -58,7 +58,7 @@ TexCoords(0, 0) //////////////////////////////////////////////////////////// -Vertex::Vertex(const Vector2f& position, const Vector2i& texCoords) : +Vertex::Vertex(const Vector2f& position, const Vector2f& texCoords) : Position (position), Color (255, 255, 255), TexCoords(texCoords) @@ -67,7 +67,7 @@ TexCoords(texCoords) //////////////////////////////////////////////////////////// -Vertex::Vertex(const Vector2f& position, const sf::Color& color, const Vector2i& texCoords) : +Vertex::Vertex(const Vector2f& position, const sf::Color& color, const Vector2f& texCoords) : Position (position), Color (color), TexCoords(texCoords) From 191730ac0d639cfdef8fae560cdbc7d7e8c67db1 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 25 Dec 2011 22:30:38 +0100 Subject: [PATCH 21/21] Added a render states cache to improve performances --- include/SFML/Graphics/RenderTarget.hpp | 61 ++++- include/SFML/Graphics/Texture.hpp | 3 + src/SFML/Graphics/CMakeLists.txt | 2 + src/SFML/Graphics/RenderTarget.cpp | 250 +++++++++++++----- .../Graphics/RenderTextureImplDefault.cpp | 4 + src/SFML/Graphics/RenderTextureImplFBO.cpp | 2 +- src/SFML/Graphics/Texture.cpp | 55 +++- src/SFML/Graphics/TextureSaver.cpp | 50 ++++ src/SFML/Graphics/TextureSaver.hpp | 75 ++++++ 9 files changed, 435 insertions(+), 67 deletions(-) create mode 100644 src/SFML/Graphics/TextureSaver.cpp create mode 100644 src/SFML/Graphics/TextureSaver.hpp diff --git a/include/SFML/Graphics/RenderTarget.hpp b/include/SFML/Graphics/RenderTarget.hpp index 548c09df..e87ffc3b 100644 --- a/include/SFML/Graphics/RenderTarget.hpp +++ b/include/SFML/Graphics/RenderTarget.hpp @@ -36,12 +36,12 @@ #include #include #include +#include namespace sf { class Drawable; -class Vertex; //////////////////////////////////////////////////////////// /// \brief Base class for all render targets (window, texture, ...) @@ -303,6 +303,44 @@ protected : //////////////////////////////////////////////////////////// void Initialize(); + //////////////////////////////////////////////////////////// + /// \brief Apply the current view + /// + //////////////////////////////////////////////////////////// + void ApplyCurrentView(); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new blending mode + /// + /// \param mode Blending mode to apply + /// + //////////////////////////////////////////////////////////// + void ApplyBlendMode(BlendMode mode); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new transform + /// + /// \param transform Transform to apply + /// + //////////////////////////////////////////////////////////// + void ApplyTransform(const Transform& transform); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new texture + /// + /// \param texture Texture to apply + /// + //////////////////////////////////////////////////////////// + void ApplyTexture(const Texture* texture); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new shader + /// + /// \param shader Shader to apply + /// + //////////////////////////////////////////////////////////// + void ApplyShader(const Shader* shader); + private : //////////////////////////////////////////////////////////// @@ -319,12 +357,27 @@ private : //////////////////////////////////////////////////////////// virtual bool Activate(bool active) = 0; + //////////////////////////////////////////////////////////// + /// \brief Render states cache + /// + //////////////////////////////////////////////////////////// + struct StatesCache + { + enum {VertexCacheSize = 4}; + + bool ViewChanged; ///< Has the current view changed since last draw? + BlendMode LastBlendMode; ///< Cached blending mode + Uint64 LastTextureId; ///< Cached texture + bool UseVertexCache; ///< Did we previously use the vertex cache? + Vertex VertexCache[VertexCacheSize]; ///< Pre-transformed vertices cache + }; + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - View myDefaultView; ///< Default view - View myView; ///< Current view - bool myViewChanged; ///< Has the current view changed since last Draw? + View myDefaultView; ///< Default view + View myView; ///< Current view + StatesCache myCache; ///< Render states cache }; } // namespace sf diff --git a/include/SFML/Graphics/Texture.hpp b/include/SFML/Graphics/Texture.hpp index c500ab5f..7a2ee5e4 100644 --- a/include/SFML/Graphics/Texture.hpp +++ b/include/SFML/Graphics/Texture.hpp @@ -35,6 +35,7 @@ namespace sf { class Window; +class RenderTarget; class RenderTexture; class InputStream; @@ -467,6 +468,7 @@ public : private : friend class RenderTexture; + friend class RenderTarget; //////////////////////////////////////////////////////////// /// \brief Get a valid image size according to hardware support @@ -494,6 +496,7 @@ private : bool myIsSmooth; ///< Status of the smooth filter bool myIsRepeated; ///< Is the texture in repeat mode? mutable bool myPixelsFlipped; ///< To work around the inconsistency in Y orientation + Uint64 myCacheId; ///< Unique number that identifies the texture to the render target's cache }; } // namespace sf diff --git a/src/SFML/Graphics/CMakeLists.txt b/src/SFML/Graphics/CMakeLists.txt index 1adabaed..8856fb12 100644 --- a/src/SFML/Graphics/CMakeLists.txt +++ b/src/SFML/Graphics/CMakeLists.txt @@ -51,6 +51,8 @@ set(SRC ${INCROOT}/Text.hpp ${SRCROOT}/Texture.cpp ${INCROOT}/Texture.hpp + ${SRCROOT}/TextureSaver.cpp + ${SRCROOT}/TextureSaver.hpp ${SRCROOT}/Transform.cpp ${INCROOT}/Transform.hpp ${SRCROOT}/Transformable.cpp diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index 3fbf730c..cea7386b 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -40,7 +40,7 @@ namespace sf RenderTarget::RenderTarget() : myDefaultView(), myView (), -myViewChanged(false) +myCache () { } @@ -66,7 +66,7 @@ void RenderTarget::Clear(const Color& color) void RenderTarget::SetView(const View& view) { myView = view; - myViewChanged = true; + myCache.ViewChanged = true; } @@ -136,81 +136,78 @@ void RenderTarget::Draw(const Vertex* vertices, unsigned int verticesCount, if (Activate(true)) { - // Apply the new view if needed - if (myViewChanged) + // Check if the vertex count is low enough so that we can pre-transform them + bool useVertexCache = (verticesCount <= StatesCache::VertexCacheSize); + if (useVertexCache) { - // Set the viewport - IntRect viewport = GetViewport(myView); - int top = GetHeight() - (viewport.Top + viewport.Height); - GLCheck(glViewport(viewport.Left, top, viewport.Width, viewport.Height)); + // Pre-transform the vertices and store them into the vertex cache + for (unsigned int i = 0; i < verticesCount; ++i) + { + Vertex& vertex = myCache.VertexCache[i]; + vertex.Position = states.Transform * vertices[i].Position; + vertex.Color = vertices[i].Color; + vertex.TexCoords = vertices[i].TexCoords; + } - // Set the projection matrix - GLCheck(glMatrixMode(GL_PROJECTION)); - GLCheck(glLoadMatrixf(myView.GetTransform().GetMatrix())); - - myViewChanged = false; + // Since vertices are transformed, we must use an identity transform to render them + if (!myCache.UseVertexCache) + ApplyTransform(Transform::Identity); + } + else + { + ApplyTransform(states.Transform); } - // Apply the transform - GLCheck(glMatrixMode(GL_MODELVIEW)); - GLCheck(glLoadMatrixf(states.Transform.GetMatrix())); + // Apply the view + if (myCache.ViewChanged) + ApplyCurrentView(); // Apply the blend mode - switch (states.BlendMode) - { - // Alpha blending - // glBlendFuncSeparateEXT is used when available to avoid an incorrect alpha value when the target - // is a RenderTexture -- in this case the alpha value must be written directly to the target buffer - default : - case BlendAlpha : - if (GLEW_EXT_blend_func_separate) - GLCheck(glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - else - GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - break; - - // Additive blending - case BlendAdd : - GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE)); - break; - - // Multiplicative blending - case BlendMultiply : - GLCheck(glBlendFunc(GL_DST_COLOR, GL_ZERO)); - break; - - // No blending - case BlendNone : - GLCheck(glBlendFunc(GL_ONE, GL_ZERO)); - break; - } + if (states.BlendMode != myCache.LastBlendMode) + ApplyBlendMode(states.BlendMode); // Apply the texture - if (states.Texture) - states.Texture->Bind(Texture::Pixels); - else - GLCheck(glBindTexture(GL_TEXTURE_2D, 0)); + Uint64 textureId = states.Texture ? states.Texture->myCacheId : 0; + if (textureId != myCache.LastTextureId) + ApplyTexture(states.Texture); // Apply the shader if (states.Shader) - states.Shader->Bind(); - else - GLCheck(glUseProgramObjectARB(0)); + ApplyShader(states.Shader); + + // If we pre-transform the vertices, we must use our internal vertex cache + if (useVertexCache) + { + // ... and if we already used it previously, we don't need to set the pointers again + if (!myCache.UseVertexCache) + vertices = myCache.VertexCache; + else + vertices = NULL; + } // Setup the pointers to the vertices' components - const char* data = reinterpret_cast(vertices); - GLCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); - GLCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); - GLCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + if (vertices) + { + const char* data = reinterpret_cast(vertices); + GLCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); + GLCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); + GLCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + } // Find the OpenGL primitive type - static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, - GL_TRIANGLES, GL_TRIANGLE_STRIP, - GL_TRIANGLE_FAN, GL_QUADS}; + static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, + GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS}; GLenum mode = modes[type]; // Draw the primitives GLCheck(glDrawArrays(mode, 0, verticesCount)); + + // Unbind the shader, if any + if (states.Shader) + ApplyShader(NULL); + + // Update the cache + myCache.UseVertexCache = useVertexCache; } } @@ -257,16 +254,26 @@ void RenderTarget::ResetGLStates() // Make sure that GLEW is initialized priv::EnsureGlewInit(); + // Define the default OpenGL states GLCheck(glDisable(GL_LIGHTING)); GLCheck(glDisable(GL_DEPTH_TEST)); GLCheck(glEnable(GL_TEXTURE_2D)); GLCheck(glEnable(GL_ALPHA_TEST)); GLCheck(glEnable(GL_BLEND)); GLCheck(glAlphaFunc(GL_GREATER, 0)); + GLCheck(glMatrixMode(GL_MODELVIEW)); GLCheck(glEnableClientState(GL_VERTEX_ARRAY)); GLCheck(glEnableClientState(GL_COLOR_ARRAY)); GLCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + // Apply the default SFML states + ApplyBlendMode(BlendAlpha); + ApplyTransform(Transform::Identity); + ApplyTexture(NULL); + ApplyShader(NULL); + myCache.UseVertexCache = false; + + // Set the default view SetView(GetView()); } } @@ -275,12 +282,135 @@ void RenderTarget::ResetGLStates() //////////////////////////////////////////////////////////// void RenderTarget::Initialize() { - // Setup the default view + // Setup the default and current views myDefaultView.Reset(FloatRect(0, 0, static_cast(GetWidth()), static_cast(GetHeight()))); - SetView(myDefaultView); + myView = myDefaultView; // Initialize the default OpenGL render-states ResetGLStates(); } + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyCurrentView() +{ + // Set the viewport + IntRect viewport = GetViewport(myView); + int top = GetHeight() - (viewport.Top + viewport.Height); + GLCheck(glViewport(viewport.Left, top, viewport.Width, viewport.Height)); + + // Set the projection matrix + GLCheck(glMatrixMode(GL_PROJECTION)); + GLCheck(glLoadMatrixf(myView.GetTransform().GetMatrix())); + + // Go back to model-view mode + GLCheck(glMatrixMode(GL_MODELVIEW)); + + myCache.ViewChanged = false; +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyBlendMode(BlendMode mode) +{ + switch (mode) + { + // Alpha blending + // glBlendFuncSeparateEXT is used when available to avoid an incorrect alpha value when the target + // is a RenderTexture -- in this case the alpha value must be written directly to the target buffer + default : + case BlendAlpha : + if (GLEW_EXT_blend_func_separate) + GLCheck(glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + else + GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + break; + + // Additive blending + case BlendAdd : + GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE)); + break; + + // Multiplicative blending + case BlendMultiply : + GLCheck(glBlendFunc(GL_DST_COLOR, GL_ZERO)); + break; + + // No blending + case BlendNone : + GLCheck(glBlendFunc(GL_ONE, GL_ZERO)); + break; + } + + myCache.LastBlendMode = mode; +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyTransform(const Transform& transform) +{ + // No need to call glMatrixMode(GL_MODELVIEW), it is always the + // current mode (for optimization purpose, since it's the most used) + GLCheck(glLoadMatrixf(transform.GetMatrix())); +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyTexture(const Texture* texture) +{ + if (texture) + texture->Bind(Texture::Pixels); + else + GLCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + myCache.LastTextureId = texture ? texture->myCacheId : 0; +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyShader(const Shader* shader) +{ + if (shader) + shader->Bind(); + else + GLCheck(glUseProgramObjectARB(0)); +} + } // namespace sf + + +//////////////////////////////////////////////////////////// +// Render states caching strategies +// +// * View +// If SetView was called since last draw, the projection +// matrix is updated. We don't need more, the view doesn't +// change frequently. +// +// * Transform +// The transform matrix is usually expensive because each +// entity will most likely use a different transform. This can +// lead, in worst case, to changing it every 4 vertices. +// To avoid that, when the vertex count is low enough, we +// pre-transform them and therefore use an identity transform +// to render them. +// +// * Blending mode +// It's a simple integral value, so we can easily check +// whether the value to apply is the same as before or not. +// +// * Texture +// Storing the pointer or OpenGL ID of the last used texture +// is not enough; if the sf::Texture instance is destroyed, +// both the pointer and the OpenGL ID might be recycled in +// a new texture instance. We need to use our own unique +// identifier system to ensure consistent caching. +// +// * Shader +// Shaders are very hard to optimize, because they have +// parameters that can be hard (if not impossible) to track, +// like matrices or textures. The only optimization that we +// do is that we avoid setting a null shader if there was +// already none for the previous draw. +// +//////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/RenderTextureImplDefault.cpp b/src/SFML/Graphics/RenderTextureImplDefault.cpp index 7cffc63c..481eed64 100644 --- a/src/SFML/Graphics/RenderTextureImplDefault.cpp +++ b/src/SFML/Graphics/RenderTextureImplDefault.cpp @@ -27,6 +27,7 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include @@ -77,6 +78,9 @@ bool RenderTextureImplDefault::Activate(bool active) //////////////////////////////////////////////////////////// void RenderTextureImplDefault::UpdateTexture(unsigned int textureId) { + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy the rendered pixels to the texture GLCheck(glBindTexture(GL_TEXTURE_2D, textureId)); GLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, myWidth, myHeight)); diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/RenderTextureImplFBO.cpp index 326452ae..d7ded0de 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.cpp @@ -83,7 +83,7 @@ bool RenderTextureImplFBO::IsAvailable() //////////////////////////////////////////////////////////// bool RenderTextureImplFBO::Create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer) { - //Create the context + // Create the context myContext = new Context; // Create the framebuffer object diff --git a/src/SFML/Graphics/Texture.cpp b/src/SFML/Graphics/Texture.cpp index 197baf33..22207fe7 100644 --- a/src/SFML/Graphics/Texture.cpp +++ b/src/SFML/Graphics/Texture.cpp @@ -28,12 +28,33 @@ #include #include #include +#include #include +#include +#include #include #include #include +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + // Thread-safe unique identifier generator, + // is used for states cache (see RenderTarget) + sf::Uint64 GetUniqueId() + { + static sf::Uint64 id = 1; // start at 1, zero is "no texture" + static sf::Mutex mutex; + + sf::Lock lock(mutex); + return id++; + } +} + + namespace sf { //////////////////////////////////////////////////////////// @@ -45,7 +66,8 @@ myTextureHeight(0), myTexture (0), myIsSmooth (false), myIsRepeated (false), -myPixelsFlipped(false) +myPixelsFlipped(false), +myCacheId (GetUniqueId()) { } @@ -60,7 +82,8 @@ myTextureHeight(0), myTexture (0), myIsSmooth (copy.myIsSmooth), myIsRepeated (copy.myIsRepeated), -myPixelsFlipped(false) +myPixelsFlipped(false), +myCacheId (GetUniqueId()) { if (copy.myTexture) LoadFromImage(copy.CopyToImage()); @@ -123,6 +146,9 @@ bool Texture::Create(unsigned int width, unsigned int height) myTexture = static_cast(texture); } + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Initialize the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); @@ -130,6 +156,7 @@ bool Texture::Create(unsigned int width, unsigned int height) GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); + myCacheId = GetUniqueId(); return true; } @@ -195,6 +222,9 @@ bool Texture::LoadFromImage(const Image& image, const IntRect& area) // Create the texture and upload the pixels if (Create(rectangle.Width, rectangle.Height)) { + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy the pixels to the texture, row by row const Uint8* pixels = image.GetPixelsPtr() + 4 * (rectangle.Left + (width * rectangle.Top)); GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); @@ -237,6 +267,9 @@ Image Texture::CopyToImage() const EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Create an array of pixels std::vector pixels(myWidth * myHeight * 4); @@ -302,10 +335,14 @@ void Texture::Update(const Uint8* pixels, unsigned int width, unsigned int heigh { EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy pixels from the given array to the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); myPixelsFlipped = false; + myCacheId = GetUniqueId(); } } @@ -340,10 +377,14 @@ void Texture::Update(const Window& window, unsigned int x, unsigned int y) if (myTexture && window.SetActive(true)) { + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy pixels from the back-buffer to the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 0, 0, window.GetWidth(), window.GetHeight())); myPixelsFlipped = true; + myCacheId = GetUniqueId(); } } @@ -380,6 +421,9 @@ void Texture::Bind(CoordinateType coordinateType) const // Load the matrix GLCheck(glMatrixMode(GL_TEXTURE)); GLCheck(glLoadMatrixf(matrix)); + + // Go back to model-view mode (sf::RenderTarget relies on it) + GLCheck(glMatrixMode(GL_MODELVIEW)); } } @@ -395,6 +439,9 @@ void Texture::SetSmooth(bool smooth) { EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); @@ -421,6 +468,9 @@ void Texture::SetRepeated(bool repeated) { EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); @@ -461,6 +511,7 @@ Texture& Texture::operator =(const Texture& right) std::swap(myIsSmooth, temp.myIsSmooth); std::swap(myIsRepeated, temp.myIsRepeated); std::swap(myPixelsFlipped, temp.myPixelsFlipped); + myCacheId = GetUniqueId(); return *this; } diff --git a/src/SFML/Graphics/TextureSaver.cpp b/src/SFML/Graphics/TextureSaver.cpp new file mode 100644 index 00000000..e4bbecd3 --- /dev/null +++ b/src/SFML/Graphics/TextureSaver.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +TextureSaver::TextureSaver() +{ + GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &myTextureBinding)); +} + + +//////////////////////////////////////////////////////////// +TextureSaver::~TextureSaver() +{ + GLCheck(glBindTexture(GL_TEXTURE_2D, myTextureBinding)); +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/TextureSaver.hpp b/src/SFML/Graphics/TextureSaver.hpp new file mode 100644 index 00000000..845cffb7 --- /dev/null +++ b/src/SFML/Graphics/TextureSaver.hpp @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_TEXTURESAVER_HPP +#define SFML_TEXTURESAVER_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Automatic wrapper for saving and restoring the current texture binding +/// +//////////////////////////////////////////////////////////// +class TextureSaver +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// The current texture binding is saved. + /// + //////////////////////////////////////////////////////////// + TextureSaver(); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + /// The previous texture binding is restored. + /// + //////////////////////////////////////////////////////////// + ~TextureSaver(); + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + GLint myTextureBinding; ///< Texture binding to restore +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_TEXTURESAVER_HPP