updating the journey:
shader to colorize:
uniform sampler2D texture;
uniform vec4 color_id;
void main(){
vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);
if(pixel.a != 0.0){
gl_FragColor = color_id;
}
else{
discard;
}
}
aparently gl_FragColor and gl_TexCoord are deprecated in GLSL, but I wasn't able to do it using "out vec4" and alikes
an aplication that can draw it directly to the screen, so we can see it is working:
#include <string>
#include <fstream>
#include <iostream>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(512, 256), "Tests");
sf::Vector2u tile_size(32, 32);
sf::Vector2u map_size(16, 8);
sf::Texture tileset;
tileset.loadFromFile("terrain-6.png");
sf::VertexArray vertices;
vertices.setPrimitiveType(sf::Quads);
vertices.resize(map_size.x*map_size.y*4);
const int level[] ={
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
};
for (unsigned int i = 0; i < map_size.x; ++i){
for (unsigned int j = 0; j < map_size.y; ++j){
int tileNumber = level[i+j*map_size.x];
int tu = tileNumber % (tileset.getSize().x / tile_size.x);
int tv = tileNumber / (tileset.getSize().x / tile_size.x);
sf::Vertex* quad = &vertices[(i+j*map_size.x)*4];
quad[0].position = sf::Vector2f(i*tile_size.x, (j)*tile_size.y);
quad[1].position = sf::Vector2f((i+1)*tile_size.x, (j)*tile_size.y);
quad[2].position = sf::Vector2f((i+1)*tile_size.x, (j+1)*tile_size.y);
quad[3].position = sf::Vector2f(i*tile_size.x, (j+1)*tile_size.y);
quad[0].texCoords = sf::Vector2f(tu*tile_size.x, tv*tile_size.y);
quad[1].texCoords = sf::Vector2f((tu+1)*tile_size.x, tv*tile_size.y);
quad[2].texCoords = sf::Vector2f((tu+1)*tile_size.x, (tv+1)*tile_size.y);
quad[3].texCoords = sf::Vector2f(tu*tile_size.x, (tv+1)*tile_size.y);
}
}
sf::Shader shader;
shader.loadFromFile("shaders/test.frag", sf::Shader::Fragment);
shader.setUniform("texture", tileset);
shader.setUniform("color_id", sf::Glsl::Vec4(255, 0, 0, 255));
sf::RenderStates rs;
rs.texture = &tileset;
rs.shader = &shader;
while (window.isOpen()){
sf::Event event;
while (window.pollEvent(event)){
if(event.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color::White);
window.draw(vertices, rs);
window.display();
sf::sleep(sf::milliseconds(10));
}
return 0;
}
if we draw it without the shader:
and after enabling it:
next step would be to draw it to a buffer. problem is, as I said, the shader is applied to the whole vertexArray at once, so every tile would have the same id. so my idea was to draw sprites around the mouse (as you said, like 1/4 of the screen), each one with the shader applied with a different color value. so i tried this (my idea would be to iterate a single sprite sized as a tile trough the screen, drawing it to a RenderTexture):
#include <string>
#include <fstream>
#include <iostream>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(512, 256), "Tests");
sf::Vector2u tile_size(32, 32);
sf::Vector2u map_size(16, 8);
sf::Texture tileset;
tileset.loadFromFile("terrain-6.png");
sf::VertexArray vertices;
vertices.setPrimitiveType(sf::Quads);
vertices.resize(map_size.x*map_size.y*4);
const int level[] ={
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
};
for (unsigned int i = 0; i < map_size.x; ++i){
for (unsigned int j = 0; j < map_size.y; ++j){
int tileNumber = level[i+j*map_size.x];
int tu = tileNumber % (tileset.getSize().x / tile_size.x);
int tv = tileNumber / (tileset.getSize().x / tile_size.x);
sf::Vertex* quad = &vertices[(i+j*map_size.x)*4];
quad[0].position = sf::Vector2f(i*tile_size.x, (j)*tile_size.y);
quad[1].position = sf::Vector2f((i+1)*tile_size.x, (j)*tile_size.y);
quad[2].position = sf::Vector2f((i+1)*tile_size.x, (j+1)*tile_size.y);
quad[3].position = sf::Vector2f(i*tile_size.x, (j+1)*tile_size.y);
quad[0].texCoords = sf::Vector2f(tu*tile_size.x, tv*tile_size.y);
quad[1].texCoords = sf::Vector2f((tu+1)*tile_size.x, tv*tile_size.y);
quad[2].texCoords = sf::Vector2f((tu+1)*tile_size.x, (tv+1)*tile_size.y);
quad[3].texCoords = sf::Vector2f(tu*tile_size.x, (tv+1)*tile_size.y);
}
}
sf::Shader shader;
shader.loadFromFile("shaders/test.frag", sf::Shader::Fragment);
shader.setUniform("texture", tileset);
sf::RenderTexture buffer;
buffer.create(window.getSize().x, window.getSize().y);
sf::Texture buffer_tex;
sf::Sprite buffer_spr(buffer_tex);
sf::RenderStates rs;
rs.texture = &tileset;
rs.shader = &shader;
while (window.isOpen()){
sf::Event event;
while (window.pollEvent(event)){
if(event.type == sf::Event::Closed)
window.close();
}
buffer.clear();
for (size_t y=0; y<map_size.y; y++){
for (size_t x=0; x<map_size.x; x++){
shader.setUniform("color_id", sf::Glsl::Vec4(x, y, 0, 255));
int tileNumber = level[x+y*map_size.x];
int tu = tileNumber % (tileset.getSize().x / tile_size.x);
int tv = tileNumber / (tileset.getSize().x / tile_size.x);
buffer_spr.setTextureRect(sf::IntRect(tu*tile_size.x, tv*tile_size.y, tile_size.x, tile_size.y));
buffer_spr.setPosition(x*tile_size.x, y*tile_size.y);
buffer.draw(buffer_spr, rs);
}
}
buffer.display();
window.clear(sf::Color::White);
window.draw(vertices, &tileset);
sf::Sprite test_sprite(buffer.getTexture());
window.draw(test_sprite);
window.display();
sf::sleep(sf::milliseconds(10));
}
return 0;
}
the test_sprite is just for testing purposes, obviously. I set it to use the render texture, so we could see what is being draw. but the results are weird:
maybe i'm not calculating the sprite and its texture positions correctly?
or maybe a vertex shader would be better?
EDIT: watch out, my brain is probably melting after 10 days trying to solve this. i simply forgot to load the texture in the buffer_tex. and by the way, that was nonsense; I just had to use the original tileset texture in the buffer_spr sprite. fixing it:
#include <string>
#include <fstream>
#include <iostream>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(512, 256), "Tests");
sf::Vector2u tile_size(32, 32);
sf::Vector2u map_size(16, 8);
sf::Texture tileset;
tileset.loadFromFile("terrain-6.png");
sf::VertexArray vertices;
vertices.setPrimitiveType(sf::Quads);
vertices.resize(map_size.x*map_size.y*4);
const int level[] ={
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
};
for (unsigned int i = 0; i < map_size.x; ++i){
for (unsigned int j = 0; j < map_size.y; ++j){
int tileNumber = level[i+j*map_size.x];
int tu = tileNumber % (tileset.getSize().x / tile_size.x);
int tv = tileNumber / (tileset.getSize().x / tile_size.x);
sf::Vertex* quad = &vertices[(i+j*map_size.x)*4];
quad[0].position = sf::Vector2f(i*tile_size.x, (j)*tile_size.y);
quad[1].position = sf::Vector2f((i+1)*tile_size.x, (j)*tile_size.y);
quad[2].position = sf::Vector2f((i+1)*tile_size.x, (j+1)*tile_size.y);
quad[3].position = sf::Vector2f(i*tile_size.x, (j+1)*tile_size.y);
quad[0].texCoords = sf::Vector2f(tu*tile_size.x, tv*tile_size.y);
quad[1].texCoords = sf::Vector2f((tu+1)*tile_size.x, tv*tile_size.y);
quad[2].texCoords = sf::Vector2f((tu+1)*tile_size.x, (tv+1)*tile_size.y);
quad[3].texCoords = sf::Vector2f(tu*tile_size.x, (tv+1)*tile_size.y);
}
}
sf::Shader shader;
shader.loadFromFile("shaders/test.frag", sf::Shader::Fragment);
shader.setUniform("texture", tileset);
sf::RenderTexture buffer;
buffer.create(window.getSize().x, window.getSize().y);
sf::Sprite buffer_spr(tileset);
sf::RenderStates rs;
rs.texture = &tileset;
rs.shader = &shader;
while (window.isOpen()){
sf::Event event;
while (window.pollEvent(event)){
if(event.type == sf::Event::Closed)
window.close();
}
buffer.clear();
for (size_t y=0; y<map_size.y; y++){
for (size_t x=0; x<map_size.x; x++){
shader.setUniform("color_id", sf::Glsl::Vec4(x, y, 0, 255));
int tileNumber = level[y*map_size.x+x];
int tu = tileNumber % (tileset.getSize().x / tile_size.x);
int tv = tileNumber / (tileset.getSize().x / tile_size.x);
buffer_spr.setTextureRect(sf::IntRect(tu*tile_size.x, tv*tile_size.y, tile_size.x, tile_size.y));
buffer_spr.setPosition(x*tile_size.x, y*tile_size.y);
buffer.draw(buffer_spr, rs);
}
}
buffer.display();
window.clear(sf::Color::White);
window.draw(vertices, &tileset);
sf::Sprite test_sprite(buffer.getTexture());
window.draw(test_sprite);
window.display();
sf::sleep(sf::milliseconds(10));
}
return 0;
}
works now:
not bad for a prototype. last part is identify the color under the mouse cursor and return the id.
EDIT 2: tried this right before clearing the buffer, but it does not update the color (with or without the first line)
sf::Texture::bind(&buffer.getTexture(), sf::Texture::CoordinateType::Pixels);
sf::Color color(10, 10, 10, 255);
glReadPixels(sf::Mouse::getPosition().x, sf::Mouse::getPosition().y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color);
std::cout << size_t(color.r) << " ";