Hi, I've finished to implement my own depthtest, alpha test and blending in a fragment shader and the result is very much better.
I thing I'll add the possibility to use the 3D even to draw 2D stuff. (It'll avoid me to do a z sorting)
It draws each semi-transparent textures without any problem.
I had just to correct the source code in my shader :
#ifndef TILEMAP
#define TILEMAP
#include "renderTexture.h"
#include "tile.h"
#include "shader.h"
namespace odfaeg {
namespace g3d {
class TileMap : public Drawable, public Transformable
{
public:
TileMap (View* view, int zOrder = 0) :
Transformable (Vec3f (view->getPosition().x, view->getPosition().y, zOrder), Vec3f(view->getSize().x, view->getSize().y, 0), Vec3f(view->getSize().x * 0.5f, view->getSize().y * 0.5f, 0)) {
this->zOrder = zOrder;
this->view = view;
}
static void genBuffers(int screenWidth, int screenHeight) {
resolution = Vec2f(screenWidth, screenHeight);
depthBuffer.create(resolution.x, resolution.y);
frameBuffer.create(resolution.x, resolution.y);
const std::string vertexShader = \
"void main () {" \
"gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;" \
"gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;" \
"gl_FrontColor = gl_Color;" \
"}";
const std::string depthGenFragShader = \
"uniform sampler2D depthBuffer;" \
"uniform vec2 resolution;" \
"void main () {" \
"vec2 position = ( gl_FragCoord.xy / resolution.xy );" \
"vec4 pixel = texture2D(depthBuffer, position);" \
"if (gl_FragCoord.z > pixel.z) {" \
"gl_FragColor = vec4(0, 0, gl_FragCoord.z, 1);" \
"} else {" \
"gl_FragColor = gl_Color * pixel;" \
"}" \
"}";
const std::string frameBufferGenFragShader = \
"uniform sampler2D depthBuffer;" \
"uniform sampler2D frameBuffer;" \
"uniform sampler2D texture;" \
"uniform vec2 resolution;" \
"void main () { " \
"vec2 position = ( gl_FragCoord.xy / resolution.xy );" \
"vec4 depth = texture2D(depthBuffer, position);" \
"vec4 color = texture2D(frameBuffer, position);" \
"vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);" \
"vec4 finalColor = pixel;" \
"if (gl_FragCoord.z >= depth.z) {" \
"float delta = color.a - finalColor.a;" \
"gl_FragColor = color + vec4(finalColor.r * delta, finalColor.g * delta, finalColor.b * delta, delta);" \
"gl_FragColor = finalColor;" \
"} else if (color.a < finalColor.a) {" \
"float delta = finalColor.a - color.a;" \
"gl_FragColor = color + vec4(finalColor.r * delta, finalColor.g * delta, finalColor.b * delta, delta);" \
"}" \
"}";
if (!depthBufferGenerator->loadFromMemory(vertexShader, depthGenFragShader))
throw Erreur(50, "Failed to load depth buffer generator shader", 0);
if (!frameBufferGenerator->loadFromMemory(vertexShader, frameBufferGenFragShader))
throw Erreur(51, "Failed to load frame buffer generator shader", 0);
depthBufferGenerator->setParameter("resolution",resolution.x, resolution.y);
frameBufferGenerator->setParameter("resolution",resolution.x, resolution.y);
}
static void clearBufferBits(sf::Color color) {
frameBuffer.clear(sf::Color(color.r, color.g, color.b, 0));
}
static void clearDepthBits() {
depthBuffer.clear(sf::Color(0, 0, 0, 255));
}
bool load(const Texture& tileset, std::vector<Tile*> tiles)
{
// load the tileset texture
m_tileset = tileset;
// resize the vertex array to fit the level size
m_vertices.setPrimitiveType(sf::Quads);
m_vertices.resize(tiles.size() * 4);
// populate the vertex array, with one quad per tile
for (unsigned int i = 0; i < tiles.size(); i++) {
// get a pointer to the current tile's quad
Vertex* quad = &m_vertices[i * 4];
Vec2f position (tiles[i]->getPosition().x, tiles[i]->getPosition().y);
float zOrder = tiles[i]->getPosition().z;
Vec2f size = Vec2f(tiles[i]->getSize().x, tiles[i]->getSize().y);
sf::IntRect subRect = tiles[i]->getTextureRect();
// define its 4 corners
quad[i].position = sf::Vector3f(position.x, position.y, zOrder);
quad[i+1].position = sf::Vector3f(position.x + size.x, position.y, zOrder);
quad[i+2].position = sf::Vector3f(position.x + size.x, position.y + size.y, zOrder);
quad[i+3].position = sf::Vector3f(position.x, position.y + size.y, zOrder);
// define its 4 texture coordinates
quad[i].texCoords = sf::Vector2f(subRect.left, subRect.top);
quad[i+1].texCoords = sf::Vector2f(subRect.left + subRect.width, subRect.top);
quad[i+2].texCoords = sf::Vector2f(subRect.left + subRect.width, subRect.top + subRect.height);
quad[i+3].texCoords = sf::Vector2f(subRect.left, subRect.top + subRect.height);
}
return true;
}
static Tile& getFrameBufferTile () {
sf::IntRect subRect(0, 0, frameBuffer.getView()->getSize().x, frameBuffer.getView()->getSize().y);
Tile* tile = new Tile (&frameBuffer.getTexture(), frameBuffer.getView()->getPosition(), frameBuffer.getView()->getSize(), subRect, 0);
//tile->setCenter(Vec3f(frameBuffer.getView()->getPosition().x, frameBuffer.getView()->getPosition().y, 0));
return *tile;
}
static void update () {
sf::IntRect subRect(0, 0, depthBuffer.getView()->getSize().x, depthBuffer.getView()->getSize().y);
Tile tile (&depthBuffer.getTexture(), depthBuffer.getView()->getPosition(), depthBuffer.getView()->getSize(), subRect, 0);
}
static Tile& getDepthBufferTile() {
sf::IntRect subRect(0, 0, depthBuffer.getView()->getSize().x, depthBuffer.getView()->getSize().y);
Tile *tile = new Tile (&depthBuffer.getTexture(), depthBuffer.getView()->getPosition(), depthBuffer.getView()->getSize(), subRect, 0);
return *tile;
}
private:
virtual void draw(RenderTarget& target, RenderStates states) const
{
// apply the transform
states.transform = getTransform();
states.texture = &m_tileset;
states.shader = frameBufferGenerator;
//Update the depth buffer and the frame buffer of the current frame.
frameBufferGenerator->setParameter("depthBuffer", depthBuffer.getTexture());
frameBufferGenerator->setParameter("frameBuffer", frameBuffer.getTexture());
frameBufferGenerator->setParameter("texture", Shader::CurrentTexture);
frameBuffer.setView(view);
frameBuffer.draw(m_vertices, states);
frameBuffer.display();
sf::IntRect subRect(0, 0, view->getSize().x, view->getSize().y);
//Update the depth buffer.
depthBuffer.setView(view);
depthBufferGenerator->setParameter("depthBuffer", depthBuffer.getTexture());
states.shader = depthBufferGenerator;
depthBuffer.draw(m_vertices, states);
depthBuffer.display();
states.shader = nullptr;
Tile& tile = getFrameBufferTile();
//tile.setCenter(Vec3f(view->getPosition().x, view->getPosition().y, 0));
target.draw(tile, states);
}
VertexArray m_vertices;
Texture m_tileset;
static RenderTexture depthBuffer;
static RenderTexture frameBuffer;
static Shader *depthBufferGenerator;
static Shader *frameBufferGenerator;
int zOrder;
static Vec2f resolution;
View* view;
};
}
}
#endif // TILEMAP
So I've a depthTexture (the blue component is the z value of my pixels) and a frameBufferTexture which contains all pixels which are previously drawn on the screen.
So, when I want to draw the textures I just have to do that :
#include "ODFAEG/Graphics/3D/renderWindow.h"
#include "ODFAEG/Graphics/3D/tile.h"
#include "ODFAEG/Graphics/3D/tileMap.h"
#include "ODFAEG/Graphics/3D/cube.h"
#include "ODFAEG/Math/projMatrix.h"
using namespace odfaeg;
using namespace odfaeg::g3d;
int main() {
g3d::RenderWindow window(sf::VideoMode(800, 600), "TestODFAEG", sf::Style::Default, sf::ContextSettings(32));
View view (800, 600, 10, Vec3f(0, -1, 0));
window.setView(&view);
view.scale(-1, 1, 1);
TileMap::genBuffers(view.getSize().x, view.getSize().y);
Texture text;
std::vector<Tile*> tiles1, tiles2;
text.loadFromFile("tilesets/herbe.png");
TileMap tm1(&view), tm2(&view);
Tile tile1 (&text, Vec2f(0, 0), Vec2f(120, 60), sf::IntRect(0, 0, 100, 50),0);
tiles1.push_back(&tile1);
tm1.load(text, tiles1);
Texture text2;
text2.loadFromFile("tilesets/dalles.png");
//std::cout<<text2.m_cacheId<<std::endl;
//std::cout<<text2->m_cacheId<<std::endl;
Tile tile2 (&text2, Vec2f(50, 25), Vec2f(120, 60), sf::IntRect(0, 0, 100, 50),-1);
tiles2.push_back(&tile2);
tm2.load(text2, tiles2);
BoundingRectangle rect (0, 0, 1000, 1000);
//tiles = generate_floor(tiles,rect);
Cube cube(Vec3f(-1, 1, 1), 2, 2, 2, sf::Color(255, 0, 0));
while(window.isOpen()) {
//}
sf::Event event;
while(window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color(0, 0, 0));
TileMap::clearBufferBits(sf::Color(0, 0, 0));
TileMap::clearDepthBits();
window.draw(tm1);
window.draw(tm2);
TileMap::update();
window.display();
}
return 0;
}
The view : the two first parameters are the view's width and height, the thirst is the max Z value, and the last the direction of the head of the view.
So -1 as y gives me a view which point at the bottom.
I do a scale(-1, 1, 1) on the view them because otherwise the x coordinates are inverted.
Then I gen the buffers of the TileMap. (And I pass it the resolution)
After I create the tileMaps and I pass them the current view and the Tiles.
Finally I clear the depth and framebuffer textures before drawing the tilemaps at render Time.
I've to call the TileMap::Update() function otherwise the first tilemap doesn't display. (I don't know why)
PS : this is -1 and not 1 for the z value of the second tile because the zAxis point to us with opengl.