-
Notifications
You must be signed in to change notification settings - Fork 28
Sprite 3D
A replacement for the standard SFML sprite, which includes the same functionality, with the added ability to rotate around all three axes for a full 3D effect.
This class is designed to be able to replace a standard SFML sprite with no changes to the code. This means that you can change from sf::Sprite to sw::Sprite3d and continue as normal.
Sprite 3D has stuck with degrees-only for rotation (at least for now) as it is more common in 3D graphics to use degrees as well as it would involve having to provide some way to provide 3 sf::Angles at once for the rotation. e.g. sf::Vector3sf::Angle but this may be messy.
The base usage based on sf::Sprite is not listed here. For more information on using sf::Sprite, please read the official tutorial and the official documentation.
-
sw::Sprite3d sprite;
sprite with no texture and size of 0x0 -
sw::Sprite3d sprite(texture);
sprite with texture -
sw::Sprite3d sprite(texture, textureRect);
sprite with texture and texture rectangle -
sw::Sprite3d sprite(frontTexture, backTexture);
sprite with a separate front texture and back texture -
sw::Sprite3d sprite(frontTexture, textureRect, backTexture);
sprite with a separate front texture and back texture, a texture rectangle and a default back offset of (0, 0) - see next declaration -
sw::Sprite3d sprite(frontTexture, textureRect, backTexture, backOffset);
sprite with a separate front texture and back texture, a texture rectangle and a separate texture offset for the back -
sw::Sprite3d sprite(standardSprite);
sprite copied from a standard SFML sprite
Note: the texture rectangle defines the part of the texture to use and also base size of the sprite. The 'position' (left, right) of the texture rectangle defines the offset from the top-left corner. The back offset is used in the same way but cannot be a separate size, which is the reason why the back doesn't have its own texture rectangle.
As with the standard SFML sprite, Sprite 3D can be drawn using the standard drawing method:
window.draw(sprite);
where window is an sf::RenderWindow.
Sprite 3D also falls victim to some of the same pitfalls as the standard sprite, such as the White Square Problem.
Sprite 3D has all the standard SFML transformations (scale, move, setPosition, setColor etc.) and all the standard SFML sprite methods (setTexture(), setTextureRect(), getGlobalBounds() etc.).
setTexture() can also take an additional parameter: bool resetBackOffset
. This is similar to resetRect but only affects the back texture.
setTexture() can also be called without parameters. This clears/nullifies the texture and solid colours will be used instead.
getGlobalBounds() return the actual axis-aligned bounding rectangle of the transformed sprite, rather than than the bounding rectangle of the transformed local bounding rectangle.
In addition, a large number of new methods are available to manipulate Sprite 3D:
Texture
-
setBackTexture(const sf::Texture&, bool "resetOffset")
sets the back texture to use for the back face. resetOffset can be omitted; if it is provided and is true, the back texture offset will be automatically reset to (0, 0) -
getBackTexture()
returns a const pointer to the sf::Texture that is used to display the back face. If no back texture has set, this returns a nullptr -
setTextureOffset(sf::Vector2i)
sets the texture offset position of the main texture (either both sides or only front if separate back is in used) -
getTextureOffset()
returns an sf::Vector2i of only the offset position. This is equivalent to the position of the texture rectangle -
setBackTextureOffset(sf::Vector2i)
set the texture offset position for the back texture -
getBackTextureOffset()
return an sf::Vector2i of the texture offset of the back texture -
setFlipBack(bool)
sets whether or not to correct (flip) the back face. If the bool is omitted, it's assumed to be true -
getFlipBack()
returns a bool that is true if back face corrections are enabled (see above)
Rotation
-
setPitch(float)
sets the amount of rotation around the x axis ("pitch") -
setYaw(float)
sets the amount of rotation around the y axis ("yaw") -
setRoll(float)
sets the amount of rotation around the z axis ("roll"). This is the usual 2D rotation and can also be set by using setRotation(float) -
setRotation(sf::Vector3f)
sets the amount of rotation around all three axes (x, y and z). Effectively sets pitch, yaw and roll at once -
setRotation3d(sf::Vector3f)
also sets the amount of rotation around all three axes (identical tosetRotation(sf::Vector3f)
) -
getRotation3d()
returns an sf::Vector3f of the current rotation around all three axes. -
setOriginZ(float)
sets the origin's z position -
setOrigin(sf::Vector2f)
sets the origin's 2D position. This is the standard sf::Transformable origin setting method. However, the origin's z position is reset to zero when this method is used -
setOrigin(sf::Vector3f)
sets the origin's 3D position (includes a z position) -
setOrigin3d(sf::Vector3f)
also sets the origin's 3D position (identical tosetOrigin(sf::Vector3f)
) -
getOrigin3d()
returns an sf::Vector3f that represents the origin's 3D position. -
getMostExtremeAngle()
returns a float that represents the most extreme angle. That is, the pitch or yaw angle that is closest to 90°, which represents how 'turned away from the viewer' the sprite is.
Mesh
-
setMeshDensity(unsigned int)
sets how dense the mesh is. This value is the number of points along each side, not including the corners.
e.g. A mesh that uses 5x5 points has a density of 3 -
getMeshDensity()
returns an unsigned int of the current mesh density -
setSubdivision(unsigned int)
sets the level of subdivision. This value is how many times each quad of the base mesh (specified by mesh density) is split into four quads. -
getSubdivision()
return an unsigned int of the current subdivision level -
setDynamicSubdivision(bool)
enables dynamic subdivision if the bool is true or omitted. If the bool is false, dynamic subdivision is disabled -
getDynamicSubdivision()
returns a bool that is true if dynamic subdivision is enabled -
setDynamicRange(unsigned int maximum, unsigned int minimum)
sets the range used for dynamic subdivision. maximum is the subdivision level when the Most Extreme Angle (see above) is 90° whereas minimum is the subdivision level for the first section of the range between to the two. If minimum is omitted, the minimum will be zero -
getSubdividedMeshDensity()
returns an unsigned int of the mesh density of the subdivided mesh (not the base mesh), based on the current mesh density and the current subdivision level -
reserveMeshDensity(unsigned int)
reserves memory for the expected maximum mesh density, as increases of the mesh density (and subdivision) may require relocation of the entire mesh. Since subdivision is a factor for relocation, reservations should be made to encompass any possible subdivision. e.g. with a mesh density of 1 and a subdivision range of 0 - 2, the maximum number of points per edge (when subdivision is at 2) is 9, so the mesh density reserved should be 7 (mesh density doesn't include the corner points) -
setNumberOfPoints(unsigned int)
calculates and sets the mesh density based on a specified number of points in the mesh. e.g. 25 points (5x5 points) would be a mesh density of 3. Provided for convenience only -
setNumberOfQuads(unsigned int)
calculates and sets the mesh density based on a specified number of quads in the mesh. e.g. 25 quads (5x5 quads) would be a mesh density of 2. Provided for convenience only -
minimalMesh()
resets the mesh to its default two triangles. Mesh density is set to zero and (attempts to) set subdivision level to zero. Note: this does not disable dynamic subdivision
Other
-
getSprite()
returns a standard SFML sprite formed similarly to itself (ignores all extra features - back textures, 3D rotation etc.). Note that a texture must have been assigned before getting an sf::Sprite. In debug mode, an assert will be hit but otherwise, it's undefined behaviour to do so. -
setDepth(float)
sets the apparent depth of the 3D effect. Initial value is 10 -
getDepth()
returns a float of the current depth (see above)
#include <SFML/Graphics.hpp>
#include <SelbaWard/Sprite3d.hpp>
int main()
{
sf::Texture frontTexture, backTexture;
if (!frontTexture.loadFromFile("resources/Card Face - SFML.png") ||
!backTexture.loadFromFile("resources/Card Back - SFML.png"))
return EXIT_FAILURE;
sf::RenderWindow window(sf::VideoMode({ 200u, 200u }), "Selba Ward - Sprite 3D: simple example");
sw::Sprite3d sprite(frontTexture, backTexture);
sprite.setOrigin(sprite.getLocalBounds().size / 2.f);
sprite.setPosition(sf::Vector2f(window.getSize() / 2u));
sf::Clock clock;
while (window.isOpen())
{
while (const auto event{ window.pollEvent() }) if (event->is<sf::Event::Closed>()) window.close();
sprite.setRotation({ clock.getElapsedTime().asSeconds() * 46.5f, clock.getElapsedTime().asSeconds() * 35.4f, clock.getElapsedTime().asSeconds() * 24.3f });
window.clear();
window.draw(sprite);
window.display();
}
return EXIT_SUCCESS;
}
The code above displays a playing card (with an SFML design) rotating around all three axes:
Note: the textures for this example are available, along with more examples, in the examples folder, although you can use your own images. It works better if both images are of equal size and you may have to adjust the window size and/or the sprite's scale if you use larger images.
(Sprite 3D v1.3)