New to game development with SFML and really like it.
So far I'm trying to render a circle on a sprite, but somehow I only get a quarter of the circle meaning that the window is taken as surface when drawing instead of sprite. If I make the sprie size as the window, I'll see the full circle..
Here's my code:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
const char *glsl = R"(
#version 330 core
uniform vec2 u_resolution;
void main() {
vec2 pos = gl_FragCoord.xy / u_resolution;
float radius = 0.4;
float d = smoothstep(radius + 0.01, radius, distance(pos, vec2(0.5)));
gl_FragColor = vec4(d*0.0, d*0.5, d*1.0, 1.0);
}
)";
int main() {
sf::RenderWindow window(sf::VideoMode(800, 600), "GLSL", sf::Style::Default);
sf::Texture tex;
tex.create(800 / 2, 600 /2); // or tex.create(800, 600); for full window surface
sf::Sprite spr(tex);
sf::Shader shader;
shader.loadFromMemory(glsl, sf::Shader::Fragment);
shader.setUniform("u_resolution", sf::Vector2f(800, 600));
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) if(event.type == sf::Event::Closed) window.close();
window.clear(sf::Color(244,244,244));
window.draw(spr, &shader);
window.display();
}
}
Any help would be appreciate. What I'm looking to achieve is to render the full circle in the sprite range. Thanks!
You take a quarter of the screen because you do tex.create(800 / 2, 600 /2); and your window size is sf::RenderWindow window(sf::VideoMode(800, 600) ...);. So it devide by the width and the height. Create a texture with the same size that your window and I think you'll be good (tex.create(800, 600);).
Related
In this code I have one view (white, zoom 2) and the original window, also one sprite for a car and one sprite for a wall...
#include <SFML/Graphics.hpp>
#include "collision.hpp"
#define windowWidth 1000
#define windowHeight 600
int main()
{
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "SFML Views");
sf::View view(sf::FloatRect(0,0, windowWidth, windowHeight));
view.zoom(2);
// car
sf::Texture imgCar;
Collision::CreateTextureAndBitmask(imgCar, "carro.png" );
sf::Sprite car;
car.setTexture(imgCar);
car.setPosition(0, (windowHeight - imgCar.getSize().y) / 2);
// wall
sf::Texture imgWall;
Collision::CreateTextureAndBitmask( imgWall, "barreira.png" );
sf::Sprite wall;
wall.setTexture(imgWall);
wall.setPosition(windowWidth - imgWall.getSize().x, 0);
sf::RectangleShape background (sf::Vector2f(windowWidth, windowHeight));
background.setFillColor(sf::Color::White);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
if (!Collision::PixelPerfectTest(car, wall))
car.move(0.1, 0);
window.clear();
window.setView(view);
window.draw(background);
window.setView(window.getDefaultView());
window.draw(car);
window.draw(wall);
window.display();
}
return 0;
}
... if both car and wall are related with the main window, I get a perfect car collision with the wall. (see image).
Now if I put the car inside the view ...
window.clear();
window.setView(view);
window.draw(background);
window.draw(car);
window.setView(window.getDefaultView());
window.draw(wall);
window.display();
... the collision is detected as if the wall were within the boundaries of the view (which is zoomed in) (see image).
How can I make the collision detection independent of the view?
I have a shader I'm attempting to use and I've come across an issue that i can't solve since my knowledge of glsl is limited.
I'm using a texture as a mask and to debug this issue I simply use this textures pixel color as the gl_FragColor, I'll post some images to show what it looks like and what it should look like.
Image link;
https://imgur.com/EBt2vbL
It seems related to the coordinates from gl_TexCoord[0].xy not getting the proper coordinates of the dissolve texture
main.cpp
#include "Engine.h"
#include <stdio.h>
#include <iostream>
#include <windows.h>
int main(int argc, char *argv[])
{
try
{
Engine game;
game.Run();
}
catch (std::exception& err)
{
std::cout << "\nException: " << err.what() << std::endl;
}
return 0;
}
Engine.h
#pragma once
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>
#include <vector>
#include <iostream>
class Engine
{
public:
Engine();
void Run();
void HandleEvents(sf::Time deltaTime);
void Update(sf::Time deltaTime);
void BuildVertices();
void Draw();
private:
bool running;
bool hasFocus;
bool fullScreen;
sf::RenderWindow mainWindow;
sf::Time deltaTime;
sf::Event event;
sf::Vector2i screenResolution;
sf::Vector2i mousePosition;
sf::VertexArray vertices;
sf::Vertex vertex;
sf::Shader dissolveShader;
sf::Texture dissolveTexture;
sf::RenderStates renderState;
float dissolveValue;
sf::Texture objectSpriteSheetTexture;
};
Engine.cpp
#include "Engine.h"
static const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
Engine::Engine()
: hasFocus(true)
, fullScreen(fullScreen)
, running(false)
, dissolveValue(1.0f)
, vertices(sf::Quads)
{
mainWindow.create(sf::VideoMode(640, 480), "Test", sf::Style::Titlebar);
mainWindow.setPosition(sf::Vector2i(0, 0));
screenResolution.x = 640;
screenResolution.y = 480;
// 512x512 sheet, each sprite is 128x128
if (!objectSpriteSheetTexture.loadFromFile("ObjectSheet.png"))
std::cout << "failed to load ObjectSheet.png" << std::endl;
if (!dissolveTexture.loadFromFile("DissolveTexture.png"))
std::cout << "failed to load DissolveTexture.png" << std::endl;
if (!dissolveShader.loadFromFile("DissolveShader.frag", sf::Shader::Fragment))
{
std::cout << "failed to load DissolveShader.frag" << std::endl;
}
dissolveShader.setUniform("sourceTexture", sf::Shader::CurrentTexture);
dissolveShader.setUniform("dissolveTexture", dissolveTexture);
renderState.shader = &dissolveShader;
renderState.texture = &objectSpriteSheetTexture;
}
void Engine::Run()
{
// main loop
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
sf::Time elapsedTime;
running = true;
while(running)
{
elapsedTime = clock.restart();
timeSinceLastUpdate += elapsedTime;
HandleEvents(TimePerFrame);
while(timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
Update(TimePerFrame);
}
BuildVertices();
Draw();
}
}
void Engine::HandleEvents(sf::Time deltaTime)
{
mousePosition = sf::Mouse::getPosition(mainWindow);
while(mainWindow.pollEvent(event))
{
if(event.type == sf::Event::Closed)
mainWindow.close();
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Escape)
{
running = false;
}
}
}
}
void Engine::Update(sf::Time deltaTime)
{
}
void Engine::BuildVertices()
{
vertices.clear();
int frameSize = 128;
sf::Vector2i objectPosition(100, 100);
sf::Vector2i spriteSheetTextureCoordinates(0, 128);
vertex.position.x = objectPosition.x;
vertex.position.y = objectPosition.y;
vertex.texCoords.x = spriteSheetTextureCoordinates.x;
vertex.texCoords.y = spriteSheetTextureCoordinates.y;
vertices.append(vertex);
vertex.position.x = objectPosition.x + frameSize;
vertex.position.y = objectPosition.y;
vertex.texCoords.x = spriteSheetTextureCoordinates.x + frameSize;
vertex.texCoords.y = spriteSheetTextureCoordinates.y;
vertices.append(vertex);
vertex.position.x = objectPosition.x + frameSize;
vertex.position.y = objectPosition.y + frameSize;
vertex.texCoords.x = spriteSheetTextureCoordinates.x + frameSize;
vertex.texCoords.y = spriteSheetTextureCoordinates.y + frameSize;
vertices.append(vertex);
vertex.position.x = objectPosition.x;
vertex.position.y = objectPosition.y + frameSize;
vertex.texCoords.x = spriteSheetTextureCoordinates.x;
vertex.texCoords.y = spriteSheetTextureCoordinates.y + frameSize;
vertices.append(vertex);
}
void Engine::Draw()
{
mainWindow.clear(sf::Color::Black);
dissolveShader.setUniform("dissolveValue", dissolveValue);
mainWindow.draw(vertices, renderState);
mainWindow.display();
}
the vertex shader is a standard pass through handled by sfml.
the fragment shader;
#version 130
// used as the mask to determine if a pixel of the source texture should be drawn, 128x128
uniform sampler2D dissolveTexture;
// the texture of the object i'm drawing, a 128x128 part of a 512x512 sprite sheet
uniform sampler2D sourceTexture;
// set to 1.0 for debug
uniform float dissolveValue;
void main( void )
{
vec4 sourceColor = texture2D(sourceTexture, gl_TexCoord[0].xy);
vec4 maskColor = texture2D(dissolveTexture, gl_TexCoord[0].xy);
if(maskColor.r <= dissolveValue)
{
// it would return the source pixel color here one the issue is solved
// gl_FragColor = sourceColor;
// debuging, so returning the mask textures pixel color
gl_FragColor = maskColor;
}
else
{
gl_FragColor = sourceColor;
}
}
I'm probably overlooking something simple, so if someone can point me in the right direction i'd appreciate it, thanks!
The texture coordinates, for the GLSL function texture (formerly texture2D) range from 0.0 to 1.0 where (0.0, 0.0) is in general the bottom-left corner and (1.0, 1.0) is the top-right corner of the texture image.
But, the SFML library scales the texture cooridnates by the size of the curren t texture (sf::Shader::CurrentTexture). This means the texture coordinates have to be set up in the range of the current texture size:
This means you have to set up the texture coordinates like this:
void Engine::BuildVertices()
{
vertices.clear();
int frameSize = 128;
sf::Vector2i objectPosition(100, 100);
sf::Vector2i texSize(512, 512);
vertex.position = sf::Vector2f(objectPosition.x, objectPosition.y);
vertex.texCoords = sf::Vector2f(0.0f, 0.0f);
vertices.append(vertex);
vertex.position = sf::Vector2f(objectPosition.x + frameSize, objectPosition.y);
vertex.texCoords = sf::Vector2f(texSize.x, 0.0f);
vertices.append(vertex);
vertex.position = sf::Vector2f(objectPosition.x + frameSize, objectPosition.y + frameSize);
vertex.texCoords = sf::Vector2f(texSize.x, texSize.y);
vertices.append(vertex);
vertex.position = sf::Vector2f(objectPosition.x, objectPosition.y + frameSize);
vertex.texCoords = sf::Vector2f(0.0f, texSize.y);
vertices.append(vertex);
}
You have a mask texture with the size of 128*128, and you have tiled sprite (4*4 tiles) with the size of 512*512. I recommend to add a texture coordinate offset uniform (texOffset) and a texture scale uniform (texScale) to the fragment shader, with allows to select a tile of the texture:
#version 130
uniform sampler2D dissolveTexture;
uniform sampler2D sourceTexture;
uniform float dissolveValue;
uniform vec2 texScale;
uniform vec2 texOffset;
void main( void )
{
vec4 sourceColor = texture2D(sourceTexture, texOffset+texScale*gl_TexCoord[0].xy);
vec4 maskColor = texture2D(dissolveTexture, gl_TexCoord[0].xy);
gl_FragColor = mix( sourceColor, maskColor, step(maskColor.r, dissolveValue) );
}
You have to set the uniforms in the function Draw. The scale is given by the reciprocal of the number of tile rows and colums. The offset is the index of the tile multiplied by the scale factor:
void Engine::Draw()
{
mainWindow.clear(sf::Color::Black);
dissolveValue = 0.5f;
dissolveShader.setUniform("dissolveValue", dissolveValue);
float scale_x = 1.0f/4.0f;
float scale_y = 1.0f/4.0f;
int i_x = 1; // column of tile (form 0 to 3)
int i_y = 2; // row of tile (form 0 to 3)
dissolveShader.setUniform("texScale", sf::Glsl::Vec2(scale_x, scale_y));
dissolveShader.setUniform("texOffset", sf::Glsl::Vec2(i_x*scale_x, i_y*scale_y));
mainWindow.draw(vertices, renderState);
mainWindow.display();
}
I'm working on a sort of ascii canvas for a game. I assumed it would be more efficient to to use a spritesheet of ascii glyphs in cp437 style to draw the ascii art. I needed a way to color the background and foreground of the glyphs so I'm using a fragment shader. Using the shader drops me to 7 fps. Not using the shader, I get about 131.
Am I doing something incorrectly? Is it just too expensive to loop through a vector of strings (and each character in the string), calculate the position of the glyph on the sheet, set the texture position, and then draw the sprite with a shader for each character?
int main() {
sf::RenderWindow rt(sf::VideoMode(1280, 720), "Demo Game");
sf::Texture texture;
texture.loadFromFile("Resources/courier_8x16.png");
texture.setSmooth(false);
sf::Sprite sprite(texture);
sf::Shader shader;
shader.loadFromFile("cycle.frag", sf::Shader::Fragment);
shader.setUniform("texture", sf::Shader::CurrentTexture);
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
std::vector<std::string> chars = std::vector<std::string>(50, std::string(100, 'X'));
while (rt.isOpen())
{
processEvents();
timeSinceLastUpdate += clock.restart();
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
processEvents();
update(TimePerFrame);
}
//render();
rt.clear();
for (int y = 0; y < chars.size(); y++)
{
for (int x = 0; x < chars[y].size(); x++)
{
//bg and fg colors will be in a 2D vector and used here
shader.setUniform("foreground", sf::Glsl::Vec4(std::Color.White));
shader.setUniform("background", sf::Glsl::Vec4(std::Color.Black));
//uses decimal value of char and dimensions
//to find location of appropriate glyph in sprite sheet
sprite.setTextureRect(sf::IntRect((chars[y][x] % 16) * 8, (chars[y][x] / 16) * 16, 8, 16));
sprite.setPosition(x * 8, y * 16);
rt.draw(sprite, &shader);
}
}
rt.display();
}
}
Here's the shader code:
//////cycle.frag
uniform vec4 foreground;
uniform vec4 background;
uniform sampler2D texture;
void main()
{
vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);
if (pixel.r < .1)
pixel = background;
else
pixel = foreground;
gl_FragColor = pixel;
}
Here's the sprite sheet: (all the paragraph symbols are just placeholders)
Just started learning SFML (so please bear with me).
I have made a RenderWindow object, and I have a image I want to fit perfectly to that window.
By searching through the documentation, I found the sf::Sprite::SetScale function. Is this the right function to do it? But how do I set the scale of the sprite to the scale of the RenderWindow, when the RenderWindow object size is set in pixels? Do I have to get the scale of the RenderWindow, and then assign the background sprite to that scale or?
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(600, 300), "Simple Game");
sf::Texture BackgroundTexture;
sf::Sprite background;
//background.setScale(0.2, 0.2); <--- how?
if(!BackgroundTexture.loadFromFile("media/background.png"))
{
return -1;
}
else
{
background.setTexture(BackgroundTexture);
}
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
switch(event.type)
{
case sf::Event::Closed:
window.close();
}
}
window.clear();
window.draw(background);
window.display();
}
}
The scale factor you need is simply the ratio between the window size and the texture size. sf::Texture has a getSize function, as has sf::RenderWindow. Simply get the sizes of both, calculate the ratio and use it to set the scale of the sprite, like this:
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(600, 300), "Simple Game");
sf::Texture BackgroundTexture;
sf::Sprite background;
sf::Vector2u TextureSize; //Added to store texture size.
sf::Vector2u WindowSize; //Added to store window size.
if(!BackgroundTexture.loadFromFile("background.png"))
{
return -1;
}
else
{
TextureSize = BackgroundTexture.getSize(); //Get size of texture.
WindowSize = window.getSize(); //Get size of window.
float ScaleX = (float) WindowSize.x / TextureSize.x;
float ScaleY = (float) WindowSize.y / TextureSize.y; //Calculate scale.
background.setTexture(BackgroundTexture);
background.setScale(ScaleX, ScaleY); //Set scale.
}
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
switch(event.type)
{
case sf::Event::Closed:
window.close();
}
}
window.clear();
window.draw(background);
window.display();
}
}
I have a simple program where I set the texture of a sprite and change its color, but it won't display. The window is entirely black. Is there something I did not do?
#include <SFML/Graphics.hpp>
int main()
{
sf::Texture texture;
sf::Sprite sprite;
texture.create(200, 200);
sprite.setTexture(texture);
sprite.setColor(sf::Color(209, 59, 59));
while (window.isOpen())
{
window.clear();
window.draw(sprite);
window.display();
}
return 0;
}
Setting a color to a sprite while its texture's pixels have no color will not work, because of how sf::Sprite::setColor() method works.
From SFML documentation:
void sf::Sprite::setColor ( const Color & color )
This color is modulated (multiplied) with the sprite's texture. It can be used to colorize the sprite, or change its global opacity. By default, the sprite's color is opaque white.
Multiplying any color against a transparent texture will not change anything in how the Sprite is rendered.
One solution would be to create an image of size 200,200 and setting the image to the desired color, using sf::Image::create (width, height, color) constructor, then loading the texture from that image.
Example code:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(500,500), "TEST");
sf::Image image;
image.create(200,200,(sf::Color(209, 59, 59)));
sf::Texture texture;
texture.loadFromImage(image);
sf::Sprite sprite;
sprite.setTexture(texture);
while (window.isOpen())
{
window.clear();
window.draw(sprite);
window.display();
}
return 0;
}