Yeah, my bad, I knew about sf::View::setViewport, but I thought it would be difficult to provide specific coordinates to it since the viewport must be expressed as a ratio... But fooling around with it, trying it out, it looks like it's pretty precise after all.
So now my code looks like this:
#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>
void update(sf::Window&);
void draw_scene_on(sf::RenderTarget&);
class View {
public:
View(sf::FloatRect, sf::FloatRect);
void set_view(sf::RenderTarget*);
virtual void draw(sf::RenderTarget*);
protected:
sf::FloatRect pos;
private:
void set_viewport();
sf::View view;
sf::Vector2u target_size;
sf::FloatRect screen_pos;
};
int main() {
// Create a window.
sf::RenderWindow window(sf::VideoMode(200, 200), "My window", sf::Style::Close);
// Create a canvas to draw the scene in.
// I made the scene and the window the same size, but that may not
// alwas be the case.
sf::RenderTexture canvas;
canvas.create(200, 200);
// Link the canvas with a sprite to be able to draw it on the window.
sf::Sprite spr;
spr.setTexture(canvas.getTexture());
// Make a vector of arbitrary test Views.
std::vector<View*> views;
views.push_back(new View(sf::FloatRect(0, 0, 50, 100), sf::FloatRect(50, 0, 50, 100)));
views.push_back(new View(sf::FloatRect(50, 0, 100, 100), sf::FloatRect(100, 50, 100, 100)));
views.push_back(new View(sf::FloatRect(10, 10, 20, 20), sf::FloatRect(10, 10, 20, 20)));
views.push_back(new View(sf::FloatRect(10, 10, 40, 20), sf::FloatRect(10, 31, 40, 20)));
views.push_back(new View(sf::FloatRect(10, 10, 50, 20), sf::FloatRect(10, 52, 50, 20)));
while (window.isOpen()) {
// This just handles events; to be able to close the window.
update(window);
// Clear the canvas and draw the scene on it.
canvas.clear(sf::Color::White);
draw_scene_on(canvas);
canvas.display();
// Clear the window...
window.clear(sf::Color::Black);
// And draw the scene on each view.
for (unsigned i = 0; i != views.size(); ++i) {
// The view is set by calling its set_view method. This is so
// it can adjust its viewport to the window size.
views[i]->set_view(&window);
window.draw(spr);
// Each view has a draw method which is useful for
// drawng HUDs and stuff.
views[i]->draw(&window);
}
// Set the default view back.
window.setView(window.getDefaultView());
window.display();
}
return 0;
}
void update(sf::Window& window) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape) {
window.close();
}
break;
default:
break;
}
}
}
void draw_scene_on(sf::RenderTarget& target) {
sf::CircleShape circle;
circle.setRadius(10);
for (unsigned int i = 0; i < target.getSize().y; i += 10) {
for (unsigned int j = 0; j < target.getSize().x; j += 10) {
circle.setPosition(j + 1, i + 1);
circle.setFillColor(sf::Color(i, j, 0));
target.draw(circle);
}
}
}
View::View(sf::FloatRect pos, sf::FloatRect screen_pos) : pos(pos), screen_pos(screen_pos) {
view = sf::View(pos);
target_size = sf::Vector2u(1, 1);
set_viewport();
}
void View::set_view(sf::RenderTarget* target) {
sf::Vector2u ts = target->getSize();
if (ts != target_size) {
target_size = ts;
set_viewport();
}
target->setView(view);
}
void View::set_viewport() {
view.setViewport(sf::FloatRect(
screen_pos.left / target_size.x,
screen_pos.top / target_size.y,
screen_pos.width / target_size.x,
screen_pos.height / target_size.y));
}
void View::draw(sf::RenderTarget* target) {
sf::CircleShape circle;
circle.setRadius(5);
circle.setFillColor(sf::Color::Red);
circle.setPosition(10, 10);
target->draw(circle);
}
It's a lot faster now, but the result isn't exactly the same... since I pass View::draw the whole window instead of just the portion of the scene the view represents.
I'd appreciate it if anyone can come up with a workaround for that...
Thanks.