I hope this is complete enough to review. It's not elegant of course:
#include <math.h>
#include <cmath>
#include <string>
#include <SFML/Graphics.hpp>
#include <SFML/Window/VideoMode.hpp>
#include <SFML/Audio.hpp>
// from headers --
sf::Font titleFont, headingFont, textFont;
bool FontInit() {
bool loadFontOkay = true;
if ( !titleFont.loadFromFile("image/fontTitle.ttf") ||
!headingFont.loadFromFile("image/fontHeading.ttf") ||
!textFont.loadFromFile("image/fontText.ttf") ) {
// error
loadFontOkay = false;
}
return loadFontOkay;
}
sf::IntRect texSize = sf::IntRect(0, 0, 32, 32);
class TextureManager {
public:
sf::Texture texMaskNoise;
TextureManager() { MaskTextureInit(); }
private:
void MaskTextureInit()
{
texMaskNoise.loadFromFile("image/Mask_Noise.png", texSize);
}
};
static TextureManager texMgr;
// -- end from headers
// C++, SFML, OpenGL
int main()
{
// rand seed
srand(time(NULL));
// main game setup //
// window
sf::RenderWindow rWin( sf::VideoMode( 1024, 576, 256 ), "X" );
sf::VideoMode vm( sf::VideoMode::getDesktopMode() );
rWin.setPosition( sf::Vector2i( (( vm.width / 2 ) - 512 ) , (( vm.height / 2 ) - 320 ) ) );
// time and space
sf::Clock frameTimer;
frameTimer.restart();
float timeDelta = 0.f;
float globalTime = 1.f;
float globalScale = 2.f;
// view port
sf::View vw;
vw.setSize(1024.f, 576.f);
vw.setViewport(sf::FloatRect(0.f, 0.f, 1.f, 1.f));
vw.setCenter(320.f, 320.f);
// terrain
// TODO: implement terrain manager
// holds all terrain textures + noise mask
// uses terrain layer offsets from custom Scene class
// handles render texture + tile blend shader
// scrolls with offset to draw tiles in view, based on player position, with updated render texture center
// 10x10 tiles = 320x320 pixels
// 32x32 tiles = 1024x1024 pixels
sf::Texture tTexture;
if ( !tTexture.loadFromFile("image/Terrain_Soil_Base.png", sf::IntRect(0,0,32,32) ) )
{
// error
}
tTexture.setRepeated(true);
tTexture.setSmooth(false);
sf::Sprite terrain;
terrain.setTexture(tTexture);
terrain.setTextureRect(sf::IntRect(0, 0, 32 * 10, 32 * 10));
terrain.setOrigin(160.f, 160.f);
terrain.setScale(globalScale, globalScale);
terrain.setPosition(320.f, 320.f);
// terrain layer prototype
sf::RenderTexture terrainGrassLayer;
sf::ContextSettings settings;
settings.depthBits = 8;
terrainGrassLayer.create( 32 * 10 * globalScale, 32 * 10 * globalScale, settings );
sf::Texture lTexture;
if ( !lTexture.loadFromFile("image/Terrain_Soil_Grass.png", sf::IntRect(0,0,32,32) ) )
{
// error
}
tTexture.setRepeated(true);
tTexture.setSmooth(false);
sf::Sprite terrainLayer;
terrainLayer.setTexture(lTexture);
terrainLayer.setTextureRect(sf::IntRect(0,0,32,32));
terrainLayer.setOrigin(160.f, 160.f);
terrainLayer.setScale(globalScale, globalScale);
terrainLayer.setPosition(320.f, 320.f);
terrainGrassLayer.draw( terrain );
// find all 1024 alpha values of the noise mask texture pixels, use them to set alpha of each layer 32x32 tile
// TODO: store these values instead of continually reading from image file
// attempt to load shader
// FIXME: shaders are available, but failing to load, either from src/ or image/
sf::Shader tileBlend;
//bool shaderLoaded = tileBlend.loadFromFile( "src/TerrainTest.glsl", sf::Shader::Fragment );
//bool shaderLoaded = tileBlend.loadFromFile( "src/TerrainTileBlend.glsl", sf::Shader::Fragment );
const std::string vertexShader = \
"void main()" \
"{" \
" // transform the vertex position" \
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;" \
"" \
" // transform the texture coordinates" \
" gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;" \
"" \
" // forward the vertex color" \
" gl_FrontColor = gl_Color;" \
"}";
const std::string fragmentShader = \
"uniform sampler2D tSample;" \
"uniform float tileAlphaCenter;" \
"void main()" \
"{" \
" vec4 pixel = texture2D( tSample, gl_TexCoord[0].xy );" \
" pixel.a = tileAlphaCenter;" \
" gl_FragColor = gl_Color * pixel;" \
"}";
//bool shaderLoaded = tileBlend.loadFromMemory( vertexShader, fragmentShader );
bool shaderLoaded = tileBlend.loadFromMemory( fragmentShader, sf::Shader::Fragment );
std::string debugText = "";
if ( sf::Shader::isAvailable() )
debugText += "Shaders are available on this system";
else
debugText = "No shaders, sorry";
/*
// TEST FORCE COMPILATION ERROR MSG
sf::Shader dummy;
if ( !dummy.loadFromMemory("", sf::Shader::Fragment) )
{
debugText += "\n... dummy fails ...";
}
*/
for ( int t=0; t<5; t++ )
{
int offsetX, offsetY;
offsetX = 0;
offsetY = 0;
switch (t)
{
case 0:
lTexture.loadFromFile("image/Terrain_Soil_Rock.png", sf::IntRect(0,0,32,32) );
break;
case 1:
lTexture.loadFromFile("image/Terrain_Soil_Root.png", sf::IntRect(0,0,32,32) );
break;
case 2:
lTexture.loadFromFile("image/Terrain_Soil_Pebble.png", sf::IntRect(0,0,32,32) );
break;
case 3:
lTexture.loadFromFile("image/Terrain_Soil_Grass.png", sf::IntRect(0,0,32,32) );
break;
case 4:
lTexture.loadFromFile("image/Terrain_Soil_Flower.png", sf::IntRect(0,0,32,32) );
break;
}
offsetX = (rand() % 32);
offsetY = (rand() % 32);
for ( int i=0; i<32; i++ )
{
for ( int n=0; n<32; n++ )
{
// TODO: migrate to function
// TODO: allow function to return alpha value from pixel 'wrapped around' texture edge
terrainLayer.setPosition( 320.f + (i * 32 * globalScale), 320.f + (n * 32 * globalScale) );
sf::Color pixelAlpha = sf::Color::White;
unsigned int getX, getY;
getX = i + offsetX;
getY = n + offsetY;
if ( getX > 31 )
getX -= 31;
if ( getY > 31 )
getY -= 31;
pixelAlpha.a = texMgr.texMaskNoise.copyToImage().getPixel(getX, getY).a;
if ( sf::Shader::isAvailable() && shaderLoaded )
{
// blending across tile to surrounding eight mask pixel alpha values
// 1 2 3
// 4 X 5
// 6 7 8
// configure and utilize tileBlend shader
tileBlend.setUniform("tSample", lTexture);
pixelAlpha.a = texMgr.texMaskNoise.copyToImage().getPixel(getX, getY).a;
float centerA = (float)(pixelAlpha.a/255);
tileBlend.setUniform("tileAlphaCenter", centerA);
float topleftA = 0.f;
if ( getX > 0 && getY > 0 )
{
topleftA = (float)(texMgr.texMaskNoise.copyToImage().getPixel((getX-1),(getY-1)).a / 255);
topleftA += centerA;
topleftA /= 2.f;
}
else
topleftA = centerA;
float topA = 0.f;
if ( getY > 0 )
{
topA = (float)(texMgr.texMaskNoise.copyToImage().getPixel(getX,(getY-1)).a / 255);
topA += centerA;
topA /= 2.f;
}
else
topA = centerA;
float toprightA = 0.f;
if ( getX < 31 && getY > 0 )
{
toprightA = (float)(texMgr.texMaskNoise.copyToImage().getPixel((getX+1),(getY-1)).a / 255);
toprightA += centerA;
toprightA /= 2.f;
}
else
toprightA = centerA;
float leftA = 0.f;
if ( getX > 0 )
{
leftA = (float)(texMgr.texMaskNoise.copyToImage().getPixel((getX-1),getY).a / 255);
leftA += centerA;
leftA /= 2.f;
}
else
leftA = centerA;
float rightA = 0.f;
if ( getX < 31 )
{
rightA = (float)(texMgr.texMaskNoise.copyToImage().getPixel((getX+1),getY).a / 255);
rightA += centerA;
rightA /= 2.f;
}
else
rightA = centerA;
float bottomleftA = 0.f;
if ( getX > 0 && getY < 31 )
{
bottomleftA = (float)(texMgr.texMaskNoise.copyToImage().getPixel((getX-1),(getY+1)).a / 255);
bottomleftA += centerA;
bottomleftA /= 2.f;
}
else
bottomleftA = centerA;
float bottomA = 0.f;
if ( getY < 31 )
{
bottomA = (float)(texMgr.texMaskNoise.copyToImage().getPixel(getX,(getY+1)).a / 255);
bottomA += centerA;
bottomA /= 2.f;
}
else
bottomA = centerA;
float bottomrightA = 0.f;
if ( getX < 31 && getY < 31 )
{
bottomrightA = (float)(texMgr.texMaskNoise.copyToImage().getPixel((getX+1),(getY+1)).a / 255);
bottomrightA += centerA;
bottomrightA /= 2.f;
}
else
bottomrightA = centerA;
tileBlend.setUniform("tileAlphaTopLeft", topleftA);
tileBlend.setUniform("tileAlphaTop", topA);
tileBlend.setUniform("tileAlphaTopRight", toprightA);
tileBlend.setUniform("tileAlphaLeft", leftA);
tileBlend.setUniform("tileAlphaRight", rightA);
tileBlend.setUniform("tileAlphaBottomLeft", bottomleftA);
tileBlend.setUniform("tileAlphaBottom", bottomA);
tileBlend.setUniform("tileAlphaBottomRight", bottomrightA);
terrainGrassLayer.draw( terrainLayer, &tileBlend );
}
else
{
// tile blend shader not found, just draw tile with tile center alpha value
terrainLayer.setColor( pixelAlpha );
terrainGrassLayer.draw( terrainLayer );
}
}
}
}
if ( shaderLoaded )
debugText += "\n... and shader loaded!";
else
debugText += "\n... and shader not loaded";
terrainGrassLayer.display();
sf::Sprite terrainLayerSprite;
terrainLayerSprite.setTexture( terrainGrassLayer.getTexture() );
// debug feedback
sf::Text debugLine;
debugLine.setFont(textFont);
debugLine.setScale(.381f,.381f);
debugLine.setPosition(32.f,48.f);
// main loop
rWin.setActive();
rWin.setFramerateLimit(60);
while ( rWin.isOpen() )
{
// time delta
timeDelta = frameTimer.restart().asSeconds() * globalTime;
// window events
sf::Event event;
while (rWin.pollEvent(event))
{
if (event.type == sf::Event::Closed)
rWin.close();
if (event.type == sf::Event::Resized) {
rWin.setSize( sf::Vector2u( 1024, 576 ) );
rWin.setPosition( sf::Vector2i( (( vm.width / 2 ) - 512 ) , (( vm.height / 2 ) - 320 ) ) );
}
}
// draw calls
rWin.clear();
rWin.setView( vw );
rWin.draw( terrainLayerSprite );
// debug
debugLine.setString(debugText);
if ( debugLine.getString() != "" )
rWin.draw(debugLine);
rWin.display();
}
}