2014-09-30 22:07:25 +08:00
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// Headers
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
#include <SFML/Graphics.hpp>
|
2022-07-05 00:20:58 +08:00
|
|
|
|
2022-06-13 00:41:53 +08:00
|
|
|
#include <array>
|
2024-02-08 21:28:57 +08:00
|
|
|
#include <filesystem>
|
2022-07-05 00:20:58 +08:00
|
|
|
#include <iostream>
|
2019-04-13 19:16:32 +08:00
|
|
|
|
2023-04-24 20:13:52 +08:00
|
|
|
#include <cstdlib>
|
|
|
|
|
2019-04-13 19:16:32 +08:00
|
|
|
#define GLAD_GL_IMPLEMENTATION
|
2021-04-20 21:59:24 +08:00
|
|
|
#include <gl.h>
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2018-04-12 03:45:36 +08:00
|
|
|
#ifdef SFML_SYSTEM_IOS
|
|
|
|
#include <SFML/Main.hpp>
|
|
|
|
#endif
|
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
#ifndef GL_SRGB8_ALPHA8
|
|
|
|
#define GL_SRGB8_ALPHA8 0x8C43
|
|
|
|
#endif
|
|
|
|
|
2022-01-10 06:47:04 +08:00
|
|
|
std::filesystem::path resourcesDir()
|
2018-04-13 02:39:55 +08:00
|
|
|
{
|
|
|
|
#ifdef SFML_SYSTEM_IOS
|
|
|
|
return "";
|
|
|
|
#else
|
2022-03-15 09:32:26 +08:00
|
|
|
return "resources";
|
2018-04-13 02:39:55 +08:00
|
|
|
#endif
|
|
|
|
}
|
2014-09-30 22:07:25 +08:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
/// Entry point of application
|
|
|
|
///
|
|
|
|
/// \return Application exit code
|
|
|
|
///
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
int main()
|
|
|
|
{
|
2015-10-01 16:13:16 +08:00
|
|
|
bool exit = false;
|
|
|
|
bool sRgb = false;
|
|
|
|
|
2015-09-23 23:05:39 +08:00
|
|
|
while (!exit)
|
2014-09-30 22:07:25 +08:00
|
|
|
{
|
2015-10-01 16:13:16 +08:00
|
|
|
// Request a 24-bits depth buffer when creating the window
|
|
|
|
sf::ContextSettings contextSettings;
|
2022-07-05 00:20:58 +08:00
|
|
|
contextSettings.depthBits = 24;
|
2015-10-01 16:13:16 +08:00
|
|
|
contextSettings.sRgbCapable = sRgb;
|
|
|
|
|
|
|
|
// Create the main window
|
2024-01-02 06:38:14 +08:00
|
|
|
sf::RenderWindow window(sf::VideoMode({800, 600}),
|
|
|
|
"SFML graphics with OpenGL",
|
|
|
|
sf::Style::Default,
|
|
|
|
sf::State::Windowed,
|
|
|
|
contextSettings);
|
2015-10-01 16:13:16 +08:00
|
|
|
window.setVerticalSyncEnabled(true);
|
2023-05-01 08:00:04 +08:00
|
|
|
window.setMinimumSize(sf::Vector2u(400, 300));
|
|
|
|
window.setMaximumSize(sf::Vector2u(1200, 900));
|
2015-10-01 16:13:16 +08:00
|
|
|
|
|
|
|
// Create a sprite for the background
|
|
|
|
sf::Texture backgroundTexture;
|
|
|
|
backgroundTexture.setSrgb(sRgb);
|
2022-01-10 06:47:04 +08:00
|
|
|
if (!backgroundTexture.loadFromFile(resourcesDir() / "background.jpg"))
|
2014-09-30 22:07:25 +08:00
|
|
|
return EXIT_FAILURE;
|
2023-02-15 11:42:12 +08:00
|
|
|
const sf::Sprite background(backgroundTexture);
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Create some text to draw on top of our OpenGL object
|
|
|
|
sf::Font font;
|
2022-01-10 06:47:04 +08:00
|
|
|
if (!font.loadFromFile(resourcesDir() / "tuffy.ttf"))
|
2015-10-01 16:13:16 +08:00
|
|
|
return EXIT_FAILURE;
|
2021-12-10 03:55:33 +08:00
|
|
|
|
2022-09-06 13:18:29 +08:00
|
|
|
sf::Text text(font, "SFML / OpenGL demo");
|
|
|
|
sf::Text sRgbInstructions(font, "Press space to toggle sRGB conversion");
|
|
|
|
sf::Text mipmapInstructions(font, "Press return to toggle mipmapping");
|
2015-11-22 01:44:38 +08:00
|
|
|
text.setFillColor(sf::Color(255, 255, 255, 170));
|
2015-09-23 23:05:39 +08:00
|
|
|
sRgbInstructions.setFillColor(sf::Color(255, 255, 255, 170));
|
|
|
|
mipmapInstructions.setFillColor(sf::Color(255, 255, 255, 170));
|
2021-12-15 17:29:34 +08:00
|
|
|
text.setPosition({280.f, 450.f});
|
|
|
|
sRgbInstructions.setPosition({175.f, 500.f});
|
|
|
|
mipmapInstructions.setPosition({200.f, 550.f});
|
2015-10-01 16:13:16 +08:00
|
|
|
|
2015-09-23 23:05:39 +08:00
|
|
|
// Load a texture to apply to our 3D cube
|
|
|
|
sf::Texture texture;
|
2022-01-10 06:47:04 +08:00
|
|
|
if (!texture.loadFromFile(resourcesDir() / "logo.png"))
|
2015-09-23 23:05:39 +08:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
// Attempt to generate a mipmap for our cube texture
|
|
|
|
// We don't check the return value here since
|
|
|
|
// mipmapping is purely optional in this example
|
2022-07-05 00:20:58 +08:00
|
|
|
(void)texture.generateMipmap();
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2016-10-07 05:44:56 +08:00
|
|
|
// Make the window the active window for OpenGL calls
|
2021-12-10 03:55:33 +08:00
|
|
|
if (!window.setActive(true))
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to set window to active" << std::endl;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2016-10-07 05:44:56 +08:00
|
|
|
|
2019-04-13 19:16:32 +08:00
|
|
|
// Load OpenGL or OpenGL ES entry points using glad
|
|
|
|
#ifdef SFML_OPENGL_ES
|
2023-11-27 06:06:37 +08:00
|
|
|
gladLoadGLES1(sf::Context::getFunction);
|
2019-04-13 19:16:32 +08:00
|
|
|
#else
|
2023-11-27 06:06:37 +08:00
|
|
|
gladLoadGL(sf::Context::getFunction);
|
2019-04-13 19:16:32 +08:00
|
|
|
#endif
|
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Enable Z-buffer read and write
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_TRUE);
|
2019-04-13 19:16:32 +08:00
|
|
|
#ifdef SFML_OPENGL_ES
|
|
|
|
glClearDepthf(1.f);
|
|
|
|
#else
|
2015-10-01 16:13:16 +08:00
|
|
|
glClearDepth(1.f);
|
2019-04-13 19:16:32 +08:00
|
|
|
#endif
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Disable lighting
|
|
|
|
glDisable(GL_LIGHTING);
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Configure the viewport (the same size as the window)
|
2021-04-20 21:59:24 +08:00
|
|
|
glViewport(0, 0, static_cast<GLsizei>(window.getSize().x), static_cast<GLsizei>(window.getSize().y));
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Setup a perspective projection
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
2014-09-30 22:07:25 +08:00
|
|
|
glLoadIdentity();
|
2023-02-15 11:42:12 +08:00
|
|
|
const GLfloat ratio = static_cast<float>(window.getSize().x) / static_cast<float>(window.getSize().y);
|
2019-04-13 19:16:32 +08:00
|
|
|
#ifdef SFML_OPENGL_ES
|
|
|
|
glFrustumf(-ratio, ratio, -1.f, 1.f, 1.f, 500.f);
|
|
|
|
#else
|
2015-10-01 16:13:16 +08:00
|
|
|
glFrustum(-ratio, ratio, -1.f, 1.f, 1.f, 500.f);
|
2019-04-13 19:16:32 +08:00
|
|
|
#endif
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Bind the texture
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
2015-09-23 23:05:39 +08:00
|
|
|
sf::Texture::bind(&texture);
|
2014-09-30 22:07:25 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Define a 3D cube (6 faces made of 2 triangles composed by 3 vertices)
|
2022-02-18 03:32:28 +08:00
|
|
|
// clang-format off
|
2022-06-13 00:41:53 +08:00
|
|
|
constexpr std::array<GLfloat, 180> cube =
|
2015-10-01 16:13:16 +08:00
|
|
|
{
|
|
|
|
// positions // texture coordinates
|
|
|
|
-20, -20, -20, 0, 0,
|
|
|
|
-20, 20, -20, 1, 0,
|
|
|
|
-20, -20, 20, 0, 1,
|
|
|
|
-20, -20, 20, 0, 1,
|
|
|
|
-20, 20, -20, 1, 0,
|
|
|
|
-20, 20, 20, 1, 1,
|
|
|
|
|
|
|
|
20, -20, -20, 0, 0,
|
|
|
|
20, 20, -20, 1, 0,
|
|
|
|
20, -20, 20, 0, 1,
|
|
|
|
20, -20, 20, 0, 1,
|
|
|
|
20, 20, -20, 1, 0,
|
|
|
|
20, 20, 20, 1, 1,
|
|
|
|
|
|
|
|
-20, -20, -20, 0, 0,
|
|
|
|
20, -20, -20, 1, 0,
|
|
|
|
-20, -20, 20, 0, 1,
|
|
|
|
-20, -20, 20, 0, 1,
|
|
|
|
20, -20, -20, 1, 0,
|
|
|
|
20, -20, 20, 1, 1,
|
|
|
|
|
|
|
|
-20, 20, -20, 0, 0,
|
|
|
|
20, 20, -20, 1, 0,
|
|
|
|
-20, 20, 20, 0, 1,
|
|
|
|
-20, 20, 20, 0, 1,
|
|
|
|
20, 20, -20, 1, 0,
|
|
|
|
20, 20, 20, 1, 1,
|
|
|
|
|
|
|
|
-20, -20, -20, 0, 0,
|
|
|
|
20, -20, -20, 1, 0,
|
|
|
|
-20, 20, -20, 0, 1,
|
|
|
|
-20, 20, -20, 0, 1,
|
|
|
|
20, -20, -20, 1, 0,
|
|
|
|
20, 20, -20, 1, 1,
|
|
|
|
|
|
|
|
-20, -20, 20, 0, 0,
|
|
|
|
20, -20, 20, 1, 0,
|
|
|
|
-20, 20, 20, 0, 1,
|
|
|
|
-20, 20, 20, 0, 1,
|
|
|
|
20, -20, 20, 1, 0,
|
|
|
|
20, 20, 20, 1, 1
|
|
|
|
};
|
2022-02-18 03:32:28 +08:00
|
|
|
// clang-format on
|
2015-10-01 16:13:16 +08:00
|
|
|
|
|
|
|
// Enable position and texture coordinates vertex components
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
2022-06-13 00:41:53 +08:00
|
|
|
glVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), cube.data());
|
|
|
|
glTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), cube.data() + 3);
|
2015-10-01 16:13:16 +08:00
|
|
|
|
|
|
|
// Disable normal and color vertex components
|
|
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
|
2016-10-07 05:44:56 +08:00
|
|
|
// Make the window no longer the active window for OpenGL calls
|
2021-12-10 03:55:33 +08:00
|
|
|
if (!window.setActive(false))
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to set window to inactive" << std::endl;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2016-10-07 05:44:56 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Create a clock for measuring the time elapsed
|
2023-02-15 11:42:12 +08:00
|
|
|
const sf::Clock clock;
|
2015-10-01 16:13:16 +08:00
|
|
|
|
2015-09-23 23:05:39 +08:00
|
|
|
// Flag to track whether mipmapping is currently enabled
|
|
|
|
bool mipmapEnabled = true;
|
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Start game loop
|
|
|
|
while (window.isOpen())
|
|
|
|
{
|
|
|
|
// Process events
|
2024-05-02 09:43:24 +08:00
|
|
|
while (const auto event = window.pollEvent())
|
2015-10-01 16:13:16 +08:00
|
|
|
{
|
|
|
|
// Close window: exit
|
2023-11-04 11:47:14 +08:00
|
|
|
if (event.is<sf::Event::Closed>())
|
2015-10-01 16:13:16 +08:00
|
|
|
{
|
|
|
|
exit = true;
|
|
|
|
window.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Escape key: exit
|
2023-11-04 11:47:14 +08:00
|
|
|
if (const auto* keyPressed = event.getIf<sf::Event::KeyPressed>();
|
|
|
|
keyPressed && keyPressed->code == sf::Keyboard::Key::Escape)
|
2015-10-01 16:13:16 +08:00
|
|
|
{
|
|
|
|
exit = true;
|
|
|
|
window.close();
|
|
|
|
}
|
|
|
|
|
2015-09-23 23:05:39 +08:00
|
|
|
// Return key: toggle mipmapping
|
2023-11-04 11:47:14 +08:00
|
|
|
if (const auto* keyPressed = event.getIf<sf::Event::KeyPressed>();
|
|
|
|
keyPressed && keyPressed->code == sf::Keyboard::Key::Enter)
|
2015-09-23 23:05:39 +08:00
|
|
|
{
|
|
|
|
if (mipmapEnabled)
|
|
|
|
{
|
|
|
|
// We simply reload the texture to disable mipmapping
|
2022-01-10 06:47:04 +08:00
|
|
|
if (!texture.loadFromFile(resourcesDir() / "logo.png"))
|
2015-09-23 23:05:39 +08:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
mipmapEnabled = false;
|
|
|
|
}
|
2021-12-10 03:55:33 +08:00
|
|
|
else if (texture.generateMipmap())
|
2015-09-23 23:05:39 +08:00
|
|
|
{
|
|
|
|
mipmapEnabled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Space key: toggle sRGB conversion
|
2023-11-04 11:47:14 +08:00
|
|
|
if (const auto* keyPressed = event.getIf<sf::Event::KeyPressed>();
|
|
|
|
keyPressed && keyPressed->code == sf::Keyboard::Key::Space)
|
2015-10-01 16:13:16 +08:00
|
|
|
{
|
|
|
|
sRgb = !sRgb;
|
|
|
|
window.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust the viewport when the window is resized
|
2023-11-04 11:47:14 +08:00
|
|
|
if (const auto* resized = event.getIf<sf::Event::Resized>())
|
2016-10-07 05:44:56 +08:00
|
|
|
{
|
2023-02-15 11:42:12 +08:00
|
|
|
const sf::Vector2u textureSize = backgroundTexture.getSize();
|
2019-04-13 19:16:32 +08:00
|
|
|
|
2016-10-07 05:44:56 +08:00
|
|
|
// Make the window the active window for OpenGL calls
|
2021-12-10 03:55:33 +08:00
|
|
|
if (!window.setActive(true))
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to set window to active" << std::endl;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2016-10-07 05:44:56 +08:00
|
|
|
|
2023-11-04 11:47:14 +08:00
|
|
|
const auto [width, height] = resized->size;
|
|
|
|
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
|
2019-01-18 23:04:03 +08:00
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadIdentity();
|
2023-11-04 11:47:14 +08:00
|
|
|
const GLfloat newRatio = static_cast<float>(width) / static_cast<float>(height);
|
2019-04-13 19:16:32 +08:00
|
|
|
#ifdef SFML_OPENGL_ES
|
2021-04-20 21:59:24 +08:00
|
|
|
glFrustumf(-newRatio, newRatio, -1.f, 1.f, 1.f, 500.f);
|
2019-04-13 19:16:32 +08:00
|
|
|
#else
|
2021-04-20 21:59:24 +08:00
|
|
|
glFrustum(-newRatio, newRatio, -1.f, 1.f, 1.f, 500.f);
|
2019-04-13 19:16:32 +08:00
|
|
|
#endif
|
2016-10-07 05:44:56 +08:00
|
|
|
|
|
|
|
// Make the window no longer the active window for OpenGL calls
|
2021-12-10 03:55:33 +08:00
|
|
|
if (!window.setActive(false))
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to set window to inactive" << std::endl;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2019-04-13 19:16:32 +08:00
|
|
|
|
2019-01-18 23:04:03 +08:00
|
|
|
sf::View view;
|
2021-04-20 21:59:24 +08:00
|
|
|
view.setSize(sf::Vector2f(textureSize));
|
|
|
|
view.setCenter(sf::Vector2f(textureSize) / 2.f);
|
2019-01-18 23:04:03 +08:00
|
|
|
window.setView(view);
|
2016-10-07 05:44:56 +08:00
|
|
|
}
|
2015-10-01 16:13:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the background
|
|
|
|
window.pushGLStates();
|
|
|
|
window.draw(background);
|
|
|
|
window.popGLStates();
|
|
|
|
|
2016-10-07 05:44:56 +08:00
|
|
|
// Make the window the active window for OpenGL calls
|
2021-12-10 03:55:33 +08:00
|
|
|
if (!window.setActive(true))
|
|
|
|
{
|
2022-06-24 07:09:22 +08:00
|
|
|
// On failure, try re-creating the window, as it is intentionally
|
2021-12-22 02:48:27 +08:00
|
|
|
// closed when changing color space.
|
|
|
|
continue;
|
2021-12-10 03:55:33 +08:00
|
|
|
}
|
2016-10-07 05:44:56 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Clear the depth buffer
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
2019-01-18 23:04:03 +08:00
|
|
|
// We get the position of the mouse cursor (or touch), so that we can move the box accordingly
|
|
|
|
sf::Vector2i pos;
|
2019-04-13 19:16:32 +08:00
|
|
|
|
2022-07-05 00:20:58 +08:00
|
|
|
#ifdef SFML_SYSTEM_IOS
|
2019-01-18 23:04:03 +08:00
|
|
|
pos = sf::Touch::getPosition(0);
|
2022-07-05 00:20:58 +08:00
|
|
|
#else
|
2020-12-13 04:40:52 +08:00
|
|
|
pos = sf::Mouse::getPosition(window);
|
2022-07-05 00:20:58 +08:00
|
|
|
#endif
|
2019-04-13 19:16:32 +08:00
|
|
|
|
2023-02-15 11:42:12 +08:00
|
|
|
const float x = static_cast<float>(pos.x) * 200.f / static_cast<float>(window.getSize().x) - 100.f;
|
|
|
|
const float y = -static_cast<float>(pos.y) * 200.f / static_cast<float>(window.getSize().y) + 100.f;
|
2015-10-01 16:13:16 +08:00
|
|
|
|
|
|
|
// Apply some transformations
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glLoadIdentity();
|
|
|
|
glTranslatef(x, y, -100.f);
|
|
|
|
glRotatef(clock.getElapsedTime().asSeconds() * 50.f, 1.f, 0.f, 0.f);
|
|
|
|
glRotatef(clock.getElapsedTime().asSeconds() * 30.f, 0.f, 1.f, 0.f);
|
|
|
|
glRotatef(clock.getElapsedTime().asSeconds() * 90.f, 0.f, 0.f, 1.f);
|
|
|
|
|
|
|
|
// Draw the cube
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 36);
|
|
|
|
|
2016-10-07 05:44:56 +08:00
|
|
|
// Make the window no longer the active window for OpenGL calls
|
2021-12-10 03:55:33 +08:00
|
|
|
if (!window.setActive(false))
|
|
|
|
{
|
|
|
|
std::cerr << "Failed to set window to inactive" << std::endl;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2016-10-07 05:44:56 +08:00
|
|
|
|
2015-10-01 16:13:16 +08:00
|
|
|
// Draw some text on top of our OpenGL object
|
|
|
|
window.pushGLStates();
|
|
|
|
window.draw(text);
|
2015-09-23 23:05:39 +08:00
|
|
|
window.draw(sRgbInstructions);
|
|
|
|
window.draw(mipmapInstructions);
|
2015-10-01 16:13:16 +08:00
|
|
|
window.popGLStates();
|
|
|
|
|
|
|
|
// Finally, display the rendered frame on screen
|
|
|
|
window.display();
|
|
|
|
}
|
2014-09-30 22:07:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|