Skip to content

Source: Triangles Extractor

Hapaxia edited this page Nov 24, 2023 · 1 revision

Introduction

The different primitive types are very important for individual shapes and reduce the number of vertices required but must be used as a single object.

The Triangles Extractor takes these vertices used with the different primitive types are returns the vertices of independent triangles that would reproduce the same result.

This can be useful if you want to have multiple (separate) shapes using the same vertex array.

It's very simple to use and has the ability to extract between sf::VertexArray and std::vector<sf::Vertex>. You can extract from either and to either.

It's a very small free function in the trianglesExtractor namespace. There are few different versions for different types (mentioned above) but these all use the same small function.

Included are some functions to "convert" types. That is, they create one from another. They are in the convert namespace (within the trianglesExtractor namespace). You can get vertices (std::vector<sf::Vertex>) from a vertex array, a vertex array from vertices as well as the ability to create a vertex buffer from either of those types (although the vertex buffer is not used in triangles extraction itself, you can still use it; it's tiny!)

e.g. extracting triangles from one vertex array into another:

sf::VertexArray original;
// set up original vertex array however you like!
sf::VertexArray triangles{ trianglesExtractor::vertexArrayFrom(original) };

Yes, just that one thing.
It sets up the triangles vertex array's primitive type automatically to be sf::Triangles (independent triangles)

Code

Here's the entire header file (including the different types and converters):

#ifndef HAPAXIA_SFML_SNIPPETS_TRIANGLES_EXTRACTOR_HPP
#define HAPAXIA_SFML_SNIPPETS_TRIANGLES_EXTRACTOR_HPP

#include <SFML/Graphics.hpp>

namespace trianglesExtractor
{
	namespace convert
	{

sf::VertexArray vertexArrayFromVertices(const std::vector<sf::Vertex>& vertices, const sf::PrimitiveType primitiveType = sf::PrimitiveType::Triangles)
{
	const std::size_t size{ vertices.size() };
	sf::VertexArray vertexArray(primitiveType, size);
	for (std::size_t i{ 0u }; i < size; ++i)
		vertexArray[i] = vertices[i];
	return vertexArray;
}

std::vector<sf::Vertex> verticesFromVertexArray(const sf::VertexArray& vertexArray)
{
	const std::size_t size{ vertexArray.getVertexCount() };
	std::vector<sf::Vertex> vertices(size);
	for (std::size_t i{ 0u }; i < size; ++i)
		vertices[i] = vertexArray[i];
	return vertices;
}

sf::VertexBuffer vertexBufferFromVertices(const std::vector<sf::Vertex>& vertices, const sf::PrimitiveType primitiveType = sf::PrimitiveType::Triangles)
{
	sf::VertexBuffer v(primitiveType);
	v.create(vertices.size());
	v.update(vertices.data());
	return v;
}

sf::VertexBuffer vertexBufferFromVertexArray(const sf::VertexArray& vertexArray)
{
	return vertexBufferFromVertices(verticesFromVertexArray(vertexArray), vertexArray.getPrimitiveType());
}

	} // namespace convert



std::vector<sf::Vertex> verticesFrom(const std::vector<sf::Vertex>& vertices, sf::PrimitiveType primitiveType)
{
	if (primitiveType == sf::PrimitiveType::Triangles)
		return vertices;

	std::vector<sf::Vertex> result;

	if (vertices.size() < 3u)
		return result;

	std::size_t numberOfVerticesPerPrimitiveOrig{ 1u };
	std::size_t numberOfVerticesPerPrimitiveNew{ 3u };
	std::size_t baseSizeRemoval{ 2u };
	if (primitiveType == sf::PrimitiveType::Quads)
	{
		numberOfVerticesPerPrimitiveOrig = 4u;
		numberOfVerticesPerPrimitiveNew = 6u;
		baseSizeRemoval = 0u;
	}
	const std::size_t numberOfPrimitives{ (vertices.size() - baseSizeRemoval) / numberOfVerticesPerPrimitiveOrig };
	result.resize(numberOfPrimitives * numberOfVerticesPerPrimitiveNew);
	for (std::size_t p{ 0u }; p < numberOfPrimitives; ++p)
	{
		const std::size_t startVertexOrig{ p * numberOfVerticesPerPrimitiveOrig };
		const std::size_t startVertexNew{ p * numberOfVerticesPerPrimitiveNew };
		result[startVertexNew + 0u] = (primitiveType == sf::TriangleFan) ? vertices[0u] : vertices[startVertexOrig + 0u];
		result[startVertexNew + 1u] = vertices[startVertexOrig + 1u];
		result[startVertexNew + 2u] = vertices[startVertexOrig + 2u];
		if (primitiveType == sf::PrimitiveType::Quads)
		{
			result[startVertexNew + 3u] = vertices[startVertexOrig + 0u];
			result[startVertexNew + 4u] = vertices[startVertexOrig + 2u];
			result[startVertexNew + 5u] = vertices[startVertexOrig + 3u];
		}
	}
	return result;
}

std::vector<sf::Vertex> verticesFrom(const sf::VertexArray& vertexArray)
{
	return verticesFrom(convert::verticesFromVertexArray(vertexArray), vertexArray.getPrimitiveType());
}

sf::VertexArray vertexArrayFrom(const std::vector<sf::Vertex>& vertices, sf::PrimitiveType primitiveType)
{
	return convert::vertexArrayFromVertices(verticesFrom(vertices, primitiveType), sf::PrimitiveType::Triangles);
}

sf::VertexArray vertexArrayFrom(const sf::VertexArray& vertexArray)
{
	return vertexArrayFrom(convert::verticesFromVertexArray(vertexArray), vertexArray.getPrimitiveType());
}

} // namespace trianglesExtractor
#endif // HAPAXIA_SFML_SNIPPETS_TRIANGLES_EXTRACTOR_HPP

Usage

Header

To use the code above, you can simply save it as a header file and include it as necessary; it has include guards.

If you would prefer to have the code directly included in another file (header or source), you can remove the include guards (lines starting with #ifndef, #define and #endif)

Extracting

There are 2 different ways to extract, based on the type you wish to receive (a vertex array or just vertices). Both are similar. Both can also be extracted from either a vertex array or vertices.

To extract triangles into a vertex array:
extractTriangles::vertexArrayFrom(x)
a vertex array is returned and can be assigned to another.

To extract triangles into just vertices:
extractTriangles::verticesFrom(x)
a vector of vertices is returned (std::vector<sf::Vertex>) and can be assigned to another.

"x" - in both cases - is either a vertex array or both an std::vector<sf::Vertex> (vertices) and an sf::PrimitiveType (original primitive type to extract from)

e.g. extracting into a vertex array from just vertices:

std::vector<sf::Vertex> vertices;
// .. set up vertices however you like!
sf::VertexArray vertexArray{ extractTriangles::vertexArrayFrom(vertices, sf::PrimitiveType::TriangleFan) };

Here, we're presuming that we would normally draw "vertices" with a triangle fan primitive.

Converting

This is pretty obvious but we'll see what is available.

We'll presume that "vertices" is of type std::vector<sf::Vertex>, "vertexArray" is of type sf::VertexArray and "vertexBuffer" is of type sf::VertexBuffer.

vertexArray = extractTriangles::convert::vertexArrayFromVertices(vertices, sf::PrimitiveType::TriangleFan);
triangle fan presumed here

vertices = extractTriangles::convert::verticesFromVertexArray(vertexArray);
primitive type is lost

vertexBuffer = extractTriangles::convert::vertexBufferFrom(vertexArray);
vertex buffer is created from a vertex array

vertexBuffer = extractTriangles::convert::vertexBufferFrom(vertices, sf::PrimitiveType::TriangleFan);
vertex buffer is created from vertices and triangles fan is presumed here

Remember that converting does not extract any triangles, it simply converts from one type to the other.

License

This code is from https://github.com/Hapaxia/SfmlSnippets/tree/master/TrianglesExtractor and provided under the zlib license.

It is from the GitHub repository SfmlSnippets by Hapaxia.

Example

Here is a full example that shows two shapes, both vertex arrays. One is created to show a triangle fan and the other is independent triangles only.

The first vertex array (left) can have its primitive type switched using the 1, 2, 3 or 4 keys. After switching its primitive type, the second vertex array (right) is updated by extracting the triangles from the first one. They should both look identical.

In addition, you can use Q, W, E and R to temporarily change the primitive types of one of the vertex arrays to a "non-solid" primitive type i.e. points, lines, line strip.

See the comment documentation near the top of the example code to see more information.

Here's the example's code:

////////////////////////////////////////////////////////////////
//
// The MIT License (MIT)
//
// Copyright (c) 2023 M.J.Silk
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////
//
//
//       ------------
//       INTRODUCTION
//       ------------
//
//   Creates a base vertex array with 9 vertices to show a triangle fan.
//   That base vertex array then has its triangles extracted into another vertex array.
//   The two vertex arrays are shown next to each other (and should look identical).
//   You can switch the primitive of the base vertex array and the extracted triangles vertex array is automatically updated (with extracted triangles)
//   You can change the primitive type of each of the base vertex array to a line strip to see its underlying structure (it never changes).
//   You can also change the primitive type of the extracted triangles vertex array to either a line strip, lines or points to see its underlying structure.
//   Note that this does affect the other vertex array and the primitive is (obviously) lost when a new primitive type is chosen.
//
//
//       --------
//       CONTROLS
//       --------
//
//   1			            change base vertex array primitive to triangle fan (also re-extracts triangles)
//   2			            change base vertex array primitive to triangle fan (also re-extracts triangles)
//   3			            change base vertex array primitive to triangle fan (also re-extracts triangles)
//   4			            change base vertex array primitive to triangle fan (also re-extracts triangles)
// 
//   Q			            change base vertex array primitive to line strip (does not affect extracted triangles vertex array)
//   W                      change extracted triangles vertex array primitive to line strip (does not affect base vertex array)
//   E                      change extracted triangles vertex array primitive to lines (does not affect base vertex array)
//   R                      change extracted triangles vertex array primitive to points (does not affect base vertex array)
// 
//   ESC                    quit
// 
// 
//        ----
//        NOTE
//        ----
//
//    You may also need to adjust the path of the included header ("TrianglesExtractor.hpp") depending on your approach.
//
//
////////////////////////////////////////////////////////////////



#include <SFML/Graphics.hpp>

#include "../TrianglesExtractor/TrianglesExtractor.hpp"



int main()
{
	// create a base vertex array
	sf::VertexArray vertexArray;
	vertexArray.setPrimitiveType(sf::PrimitiveType::TriangleFan);
	vertexArray.resize(9u);

	// fill base vertex array with vertices to show off a triangle fan (but works with other primitives)
	const sf::Vector2f vertexArrayCenter{ 150.f, 100.f };
	const sf::Vector2f vertexArrayRadius{ 100.f, 100.f };
	vertexArray[0u].position = vertexArrayCenter;
	for (std::size_t i{ 1u }; i < vertexArray.getVertexCount(); ++i)
		vertexArray[i].position = vertexArrayCenter + sf::Vector2f(vertexArrayRadius.x * std::cos((i - 1u) * 0.5f), vertexArrayRadius.y * std::sin((i - 1u) * 0.5f));

	// create a vertex array with extracted triangles
	sf::VertexArray vertexArrayTriangles;
	vertexArrayTriangles = trianglesExtractor::vertexArrayFrom(vertexArray);

	// move the extracted triangles vertex array
	sf::Transform vertexArrayTrianglesTransform;
	vertexArrayTrianglesTransform.translate({ 250.f, 0.f });

	sf::RenderWindow window(sf::VideoMode(550u, 250u), "Triangles Extractor - example");
	while (window.isOpen())
	{
		// render
		window.clear();
		window.draw(vertexArray); // show base vertex array
		window.draw(vertexArrayTriangles, vertexArrayTrianglesTransform); // show extracted triangles (should look the same as base vertex array)
		window.display();

		// events
		sf::Event event;
		while (window.pollEvent(event))
		{
			if ((event.type == sf::Event::Closed) || ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
				window.close();
			switch (event.type)
			{
			case sf::Event::KeyPressed:
				switch (event.key.scancode)
				{
				case sf::Keyboard::Scancode::Q: // set primitive of base vertex array to line strip
					vertexArray.setPrimitiveType(sf::LineStrip);
					break;
				case sf::Keyboard::Scancode::W: // set primitive of extracted triangles vertex array to line strip
					vertexArrayTriangles.setPrimitiveType(sf::LineStrip);
					break;
				case sf::Keyboard::Scancode::E: // set primitive of extracted triangles vertex array to lines
					vertexArrayTriangles.setPrimitiveType(sf::Lines);
					break;
				case sf::Keyboard::Scancode::R: // set primitive of extracted triangles vertex array to points
					vertexArrayTriangles.setPrimitiveType(sf::Points);
					break;
				case sf::Keyboard::Scancode::Num1: // switch base vertex to a triangle fan and re-extract triangles
					vertexArray.setPrimitiveType(sf::PrimitiveType::TriangleFan);
					vertexArrayTriangles = trianglesExtractor::vertexArrayFrom(vertexArray);
					break;
				case sf::Keyboard::Scancode::Num2: // switch base vertex to a triangle strip and re-extract triangles
					vertexArray.setPrimitiveType(sf::PrimitiveType::TriangleStrip);
					vertexArrayTriangles = trianglesExtractor::vertexArrayFrom(vertexArray);
					break;
				case sf::Keyboard::Scancode::Num3: // switch base vertex to independent quads and re-extract triangles
					vertexArray.setPrimitiveType(sf::PrimitiveType::Quads);
					vertexArrayTriangles = trianglesExtractor::vertexArrayFrom(vertexArray);
					break;
				case sf::Keyboard::Scancode::Num4: // switch base vertex to independent triangle and re-extract triangles
					vertexArray.setPrimitiveType(sf::PrimitiveType::Triangles);
					vertexArrayTriangles = trianglesExtractor::vertexArrayFrom(vertexArray);
					break;
				}
				break;
			}
		}
	}
}

This example is also available on the GitHub repository, along with other useful SFML snippets (also listed on SFML's wiki).

As mentioned above, the instructions for this example are contained in comments near the top of the example code.

As visible in the code, the example is licensed using the MIT license.


Written by Hapaxia (Github | Mastodon | Twitter | SFML forum | All Links)

Clone this wiki locally