Browse Source

MeshTools: deprecate mesh/buffer-modifying vertex/index tools.

The point of this change is to allow greater flexibility and reduce
confusion.

When instanced meshes are implemented, MeshTools::interleave() can be
used for creating interleaved buffers with per-instance data and then
the call to Mesh::setCount() will be harmful and/or confusing, becuase
the user would in fact want to call Mesh::setInstanceCount() instead.
Similarly, MeshTools::compressIndices() can be used to create index
buffer for more than one mesh.

GL 4.4 has ARB_buffer_storage, which (in relatively distant future) will
mean that the current way of Buffer::setData() will be deprecated in
favor of Buffer::setStorage(), similarly as Texture::setStorage()
replaced Texture::setImage(). Thus any function which calls
Buffer::setData() internally is not future-proof.

The old MeshTools::compressIndices() and MeshTools::interleave()
overloads are marked as deprecated and will be removed in future
release.
pull/51/head
Vladimír Vondruš 12 years ago
parent
commit
51a14b9e0b
  1. 22
      src/Magnum/DebugTools/Implementation/AbstractShapeRenderer.cpp
  2. 3
      src/Magnum/DebugTools/ObjectRenderer.cpp
  3. 89
      src/Magnum/Mesh.h
  4. 43
      src/Magnum/MeshTools/Compile.cpp
  5. 47
      src/Magnum/MeshTools/CompressIndices.cpp
  6. 35
      src/Magnum/MeshTools/CompressIndices.h
  7. 84
      src/Magnum/MeshTools/Interleave.h
  8. 33
      src/Magnum/MeshTools/Test/CompressIndicesTest.cpp
  9. 23
      src/Magnum/MeshTools/Test/InterleaveTest.cpp

22
src/Magnum/DebugTools/Implementation/AbstractShapeRenderer.cpp

@ -59,8 +59,17 @@ template<> void create<2>(Trade::MeshData2D& data, Resource<Mesh>& meshResource,
/* Index buffer, if needed, if not, resource key doesn't have to be set */ /* Index buffer, if needed, if not, resource key doesn't have to be set */
if(data.isIndexed()) { if(data.isIndexed()) {
CORRADE_INTERNAL_ASSERT(indexBufferResource.key() != ResourceKey()); CORRADE_INTERNAL_ASSERT(indexBufferResource.key() != ResourceKey());
Containers::Array<char> indexData;
Mesh::IndexType indexType;
UnsignedInt indexStart, indexEnd;
std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(data.indices());
Buffer* indexBuffer = new Buffer(Buffer::Target::ElementArray); Buffer* indexBuffer = new Buffer(Buffer::Target::ElementArray);
MeshTools::compressIndices(*mesh, *indexBuffer, BufferUsage::StaticDraw, data.indices()); indexBuffer->setData(indexData, BufferUsage::StaticDraw);
mesh->setCount(data.indices().size())
.setIndexBuffer(*indexBuffer, 0, indexType, indexStart, indexEnd);
ResourceManager::instance().set(indexBufferResource.key(), indexBuffer, ResourceDataState::Final, ResourcePolicy::Manual); ResourceManager::instance().set(indexBufferResource.key(), indexBuffer, ResourceDataState::Final, ResourcePolicy::Manual);
/* The mesh is not indexed, set proper vertex count */ /* The mesh is not indexed, set proper vertex count */
@ -82,8 +91,17 @@ template<> void create<3>(Trade::MeshData3D& data, Resource<Mesh>& meshResource,
/* Index buffer, if needed, if not, resource key doesn't have to be set */ /* Index buffer, if needed, if not, resource key doesn't have to be set */
if(data.isIndexed()) { if(data.isIndexed()) {
CORRADE_INTERNAL_ASSERT(indexBufferResource.key() != ResourceKey()); CORRADE_INTERNAL_ASSERT(indexBufferResource.key() != ResourceKey());
Containers::Array<char> indexData;
Mesh::IndexType indexType;
UnsignedInt indexStart, indexEnd;
std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(data.indices());
Buffer* indexBuffer = new Buffer(Buffer::Target::ElementArray); Buffer* indexBuffer = new Buffer(Buffer::Target::ElementArray);
MeshTools::compressIndices(*mesh, *indexBuffer, BufferUsage::StaticDraw, data.indices()); indexBuffer->setData(indexData, BufferUsage::StaticDraw);
mesh->setCount(data.indices().size())
.setIndexBuffer(*indexBuffer, 0, indexType, indexStart, indexEnd);
ResourceManager::instance().set(indexBufferResource.key(), indexBuffer, ResourceDataState::Final, ResourcePolicy::Manual); ResourceManager::instance().set(indexBufferResource.key(), indexBuffer, ResourceDataState::Final, ResourcePolicy::Manual);
/* The mesh is not indexed, set proper vertex count */ /* The mesh is not indexed, set proper vertex count */

3
src/Magnum/DebugTools/ObjectRenderer.cpp

@ -26,6 +26,7 @@
#include "ObjectRenderer.h" #include "ObjectRenderer.h"
#include "Magnum/Buffer.h" #include "Magnum/Buffer.h"
#include "Magnum/Mesh.h"
#include "Magnum/DebugTools/ResourceManager.h" #include "Magnum/DebugTools/ResourceManager.h"
#include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/Interleave.h"
#include "Magnum/SceneGraph/AbstractCamera.h" #include "Magnum/SceneGraph/AbstractCamera.h"
@ -159,7 +160,7 @@ template<UnsignedInt dimensions> ObjectRenderer<dimensions>::ObjectRenderer(Scen
Buffer* indexBuffer = new Buffer(Buffer::Target::ElementArray); Buffer* indexBuffer = new Buffer(Buffer::Target::ElementArray);
Mesh* mesh = new Mesh; Mesh* mesh = new Mesh;
MeshTools::interleave(*mesh, *vertexBuffer, BufferUsage::StaticDraw, Renderer<dimensions>::positions, Renderer<dimensions>::colors); vertexBuffer->setData(MeshTools::interleave(Renderer<dimensions>::positions, Renderer<dimensions>::colors), BufferUsage::StaticDraw);
ResourceManager::instance().set(this->vertexBuffer.key(), vertexBuffer, ResourceDataState::Final, ResourcePolicy::Manual); ResourceManager::instance().set(this->vertexBuffer.key(), vertexBuffer, ResourceDataState::Final, ResourcePolicy::Manual);
indexBuffer->setData(Renderer<dimensions>::indices, BufferUsage::StaticDraw); indexBuffer->setData(Renderer<dimensions>::indices, BufferUsage::StaticDraw);

89
src/Magnum/Mesh.h

@ -127,16 +127,15 @@ You have to specify at least primitive and vertex/index count using
data, add them to the mesh and specify data, add them to the mesh and specify
@ref AbstractShaderProgram::Attribute "shader attribute" layout inside the @ref AbstractShaderProgram::Attribute "shader attribute" layout inside the
buffers using @ref addVertexBuffer(). You can also use buffers using @ref addVertexBuffer(). You can also use
@ref MeshTools::interleave() conveniently fill interleaved vertex buffer. @ref MeshTools::interleave() to conveniently interleave vertex data.
The function itself calls @ref setCount(), so you don't have to, but you still
have to specify the primitive using @ref setPrimitive() and the layout using
@ref addVertexBuffer().
If you want indexed mesh, fill your index buffer with data and specify its If you want indexed mesh, fill your index buffer with data and specify its
layout using @ref setIndexBuffer(). You can also use @ref MeshTools::compressIndices() layout using @ref setIndexBuffer(). You can also use @ref MeshTools::compressIndices()
to conveniently compress the indices, fill the index buffer and configure the to conveniently compress the indices based on the range used.
mesh. It will call @ref setCount() and @ref setIndexBuffer(), so you don't have
to do anything else. There is also @ref MeshTools::compile() function which operates directly on
@ref Trade::MeshData2D / @ref Trade::MeshData3D and returns fully configured
mesh and vertex/index buffers for use with stock shaders.
Note that neither vertex buffers nor index buffer is managed (e.g. deleted on Note that neither vertex buffers nor index buffer is managed (e.g. deleted on
destruction) by the mesh, so you have to manage them on your own and ensure destruction) by the mesh, so you have to manage them on your own and ensure
@ -159,19 +158,19 @@ class MyShader: public AbstractShaderProgram {
// ... // ...
}; };
Buffer vertexBuffer;
Mesh mesh;
// Fill vertex buffer with position data // Fill vertex buffer with position data
static constexpr Vector3 positions[30] = { static constexpr Vector3 positions[30] = {
// ... // ...
}; };
Buffer vertexBuffer;
vertexBuffer.setData(positions, BufferUsage::StaticDraw); vertexBuffer.setData(positions, BufferUsage::StaticDraw);
// Set primitive and vertex count, add the buffer and specify its layout // Configure the mesh, add vertex buffer
Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Triangles) mesh.setPrimitive(MeshPrimitive::Triangles)
.setCount(30) .setCount(30)
.addVertexBuffer(vertexBuffer, 0, MyShader::Position()); .addVertexBuffer(vertexBuffer, 0, MyShader::Position{});
@endcode @endcode
@subsubsection Mesh-configuration-examples-nonindexed-phong Interleaved vertex data @subsubsection Mesh-configuration-examples-nonindexed-phong Interleaved vertex data
@ -179,17 +178,16 @@ mesh.setPrimitive(MeshPrimitive::Triangles)
@code @code
// Non-indexed primitive with positions and normals // Non-indexed primitive with positions and normals
Trade::MeshData3D plane = Primitives::Plane::solid(); Trade::MeshData3D plane = Primitives::Plane::solid();
Buffer vertexBuffer;
Mesh mesh;
// Fill vertex buffer with interleaved position and normal data // Fill vertex buffer with interleaved position and normal data
MeshTools::interleave(mesh, buffer, BufferUsage::StaticDraw, Buffer vertexBuffer;
plane.positions(0), plane.normals(0)); vertexBuffer.setData(MeshTools::interleave(plane.positions(0), plane.normals(0)), BufferUsage::StaticDraw);
// Set primitive and specify layout of interleaved vertex buffer, vertex count // Configure the mesh, add vertex buffer
// has been already set by MeshTools::interleave() Mesh mesh;
mesh.setPrimitive(plane.primitive()) mesh.setPrimitive(plane.primitive())
.addVertexBuffer(buffer, 0, Shaders::Phong::Position(), Shaders::Phong::Normal()); .setCount(plane.positions(0).size())
.addVertexBuffer(buffer, 0, Shaders::Phong::Position{}, Shaders::Phong::Normal{});
@endcode @endcode
@subsubsection Mesh-configuration-examples-indexed-phong Indexed mesh @subsubsection Mesh-configuration-examples-indexed-phong Indexed mesh
@ -202,48 +200,60 @@ class MyShader: public AbstractShaderProgram {
// ... // ...
}; };
Buffer vertexBuffer, indexBuffer;
Mesh mesh;
// Fill vertex buffer with position data // Fill vertex buffer with position data
static constexpr Vector3 positions[300] = { static constexpr Vector3 positions[300] = {
// ... // ...
}; };
Buffer vertexBuffer;
vertexBuffer.setData(positions, BufferUsage::StaticDraw); vertexBuffer.setData(positions, BufferUsage::StaticDraw);
// Fill index buffer with index data // Fill index buffer with index data
static constexpr GLubyte indices[75] = { static constexpr GLubyte indices[75] = {
// ... // ...
}; };
Buffer indexBuffer;
indexBuffer.setData(indices, BufferUsage::StaticDraw); indexBuffer.setData(indices, BufferUsage::StaticDraw);
// Set primitive, index count, specify the buffers // Configure the mesh, add both vertex and index buffer
Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Triangles) mesh.setPrimitive(MeshPrimitive::Triangles)
.setCount(75) .setCount(75)
.addVertexBuffer(vertexBuffer, 0, MyShader::Position()) .addVertexBuffer(vertexBuffer, 0, MyShader::Position{})
.setIndexBuffer(indexBuffer, 0, Mesh::IndexType::UnsignedByte, 176, 229); .setIndexBuffer(indexBuffer, 0, Mesh::IndexType::UnsignedByte, 176, 229);
@endcode @endcode
Or using @ref MeshTools::interleave() and @ref MeshTools::compressIndices():
@code @code
// Indexed primitive // Indexed primitive
Trade::MeshData3D cube = Primitives::Cube::solid(); Trade::MeshData3D cube = Primitives::Cube::solid();
Buffer vertexBuffer, indexBuffer;
Mesh mesh;
// Fill vertex buffer with interleaved position and normal data // Fill vertex buffer with interleaved position and normal data
MeshTools::interleave(mesh, vertexBuffer, BufferUsage::StaticDraw, Buffer vertexBuffer;
cube.positions(0), cube.normals(0)); vertexBuffer.setData(MeshTools::interleave(cube.positions(0), cube.normals(0)), BufferUsage::StaticDraw);
// Fill index buffer with compressed index data // Compress index data
MeshTools::compressIndices(mesh, indexBuffer, BufferUsage::StaticDraw, Containers::Array<char> indexData;
cube.indices()); Mesh::IndexType indexType;
UnsignedInt indexStart, indexEnd;
std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(cube.indices());
// Set primitive and specify layout of interleaved vertex buffer. Index count // Fill index buffer
// and index buffer has been already specified by MeshTools::compressIndices(). Buffer indexBuffer;
indexBuffer.setData(data);
// Configure the mesh, add both vertex and index buffer
Mesh mesh;
mesh.setPrimitive(plane.primitive()) mesh.setPrimitive(plane.primitive())
.addVertexBuffer(vertexBuffer, 0, Shaders::Phong::Position(), Shaders::Phong::Normal()); .setCount(cube.indices().size())
.addVertexBuffer(vertexBuffer, 0, Shaders::Phong::Position{}, Shaders::Phong::Normal{})
.setIndexBuffer(indexBuffer, 0, indexType, indexStart, indexEnd);
@endcode @endcode
Or, if you plan to use the mesh with stock shaders, you can just use
@ref MeshTools::compile().
@subsubsection Mesh-configuration-examples-data-options Specific formats of vertex data @subsubsection Mesh-configuration-examples-data-options Specific formats of vertex data
@code @code
@ -255,34 +265,39 @@ class MyShader: public AbstractShaderProgram {
// ... // ...
}; };
// Initial mesh configuration
Mesh mesh; Mesh mesh;
mesh.setPrimitive(...)
.setCount(30);
// Fill position buffer with positions specified as two-component XY (i.e., // Fill position buffer with positions specified as two-component XY (i.e.,
// no Z component, which is meant to be always 0) // no Z component, which is meant to be always 0)
Buffer positionBuffer;
Vector2 positions[30] = { Vector2 positions[30] = {
// ... // ...
}; };
Buffer positionBuffer;
positionBuffer.setData(positions, BufferUsage::StaticDraw);
// Specify layout of positions buffer -- only two components, unspecified Z // Specify layout of positions buffer -- only two components, unspecified Z
// component will be automatically set to 0 // component will be automatically set to 0
mesh.addVertexBuffer(positionBuffer, 0, mesh.addVertexBuffer(positionBuffer, 0,
MyShader::Position(MyShader::Position::Components::Two)); MyShader::Position{MyShader::Position::Components::Two});
// Fill color buffer with colors specified as four-byte BGRA (e.g. directly // Fill color buffer with colors specified as four-byte BGRA (e.g. directly
// from TGA file) // from TGA file)
Buffer colorBuffer;
GLubyte colors[4*30] = { GLubyte colors[4*30] = {
// ... // ...
}; };
Buffer colorBuffer;
colorBuffer.setData(colors, BufferUsage::StaticDraw); colorBuffer.setData(colors, BufferUsage::StaticDraw);
// Specify layout of color buffer -- BGRA, each component unsigned byte and we // Specify layout of color buffer -- BGRA, each component unsigned byte and we
// want to normalize them from [0, 255] to [0.0f, 1.0f] // want to normalize them from [0, 255] to [0.0f, 1.0f]
mesh.addVertexBuffer(colorBuffer, 0, MyShader::Color( mesh.addVertexBuffer(colorBuffer, 0, MyShader::Color{
MyShader::Color::Components::BGRA, MyShader::Color::Components::BGRA,
MyShader::Color::DataType::UnsignedByte, MyShader::Color::DataType::UnsignedByte,
MyShader::Color::DataOption::Normalized)); MyShader::Color::DataOption::Normalized});
@endcode @endcode
@section Mesh-drawing Rendering meshes @section Mesh-drawing Rendering meshes

43
src/Magnum/MeshTools/Compile.cpp

@ -25,6 +25,7 @@
#include "Compile.h" #include "Compile.h"
#include "Magnum/Buffer.h"
#include "Magnum/Math/Vector3.h" #include "Magnum/Math/Vector3.h"
#include "Magnum/MeshTools/CompressIndices.h" #include "Magnum/MeshTools/CompressIndices.h"
#include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/Interleave.h"
@ -51,9 +52,7 @@ std::tuple<Mesh, std::unique_ptr<Buffer>, std::unique_ptr<Buffer>> compile(const
std::unique_ptr<Buffer> vertexBuffer{new Buffer{Buffer::Target::Array}}; std::unique_ptr<Buffer> vertexBuffer{new Buffer{Buffer::Target::Array}};
/* Interleave positions */ /* Interleave positions */
std::size_t vertexCount; Containers::Array<char> data = MeshTools::interleave(
Containers::Array<char> data;
std::tie(vertexCount, std::ignore, data) = MeshTools::interleave(
meshData.positions(0), meshData.positions(0),
stride - sizeof(Shaders::Generic2D::Position::Type)); stride - sizeof(Shaders::Generic2D::Position::Type));
mesh.addVertexBuffer(*vertexBuffer, 0, mesh.addVertexBuffer(*vertexBuffer, 0,
@ -73,16 +72,23 @@ std::tuple<Mesh, std::unique_ptr<Buffer>, std::unique_ptr<Buffer>> compile(const
} }
/* Fill vertex buffer with interleaved data */ /* Fill vertex buffer with interleaved data */
vertexBuffer->setData(data, BufferUsage::StaticDraw); vertexBuffer->setData(data, usage);
/* Fill index buffer */ /* If indexed, fill index buffer and configure indexed mesh */
std::unique_ptr<Buffer> indexBuffer; std::unique_ptr<Buffer> indexBuffer;
if(meshData.isIndexed()) { if(meshData.isIndexed()) {
Containers::Array<char> indexData;
Mesh::IndexType indexType;
UnsignedInt indexStart, indexEnd;
std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(meshData.indices());
indexBuffer.reset(new Buffer{Buffer::Target::ElementArray}); indexBuffer.reset(new Buffer{Buffer::Target::ElementArray});
MeshTools::compressIndices(mesh, *indexBuffer, usage, meshData.indices()); indexBuffer->setData(data, usage);
mesh.setCount(meshData.indices().size())
.setIndexBuffer(*indexBuffer, 0, indexType, indexStart, indexEnd);
/* Else set proper vertex count */ /* Else set vertex count */
} else mesh.setCount(vertexCount); } else mesh.setCount(meshData.positions(0).size());
return std::make_tuple(std::move(mesh), std::move(vertexBuffer), std::move(indexBuffer)); return std::make_tuple(std::move(mesh), std::move(vertexBuffer), std::move(indexBuffer));
} }
@ -106,9 +112,7 @@ std::tuple<Mesh, std::unique_ptr<Buffer>, std::unique_ptr<Buffer>> compile(const
std::unique_ptr<Buffer> vertexBuffer{new Buffer{Buffer::Target::Array}}; std::unique_ptr<Buffer> vertexBuffer{new Buffer{Buffer::Target::Array}};
/* Interleave positions */ /* Interleave positions */
std::size_t vertexCount; Containers::Array<char> data = MeshTools::interleave(
Containers::Array<char> data;
std::tie(vertexCount, std::ignore, data) = MeshTools::interleave(
meshData.positions(0), meshData.positions(0),
stride - sizeof(Shaders::Generic3D::Position::Type)); stride - sizeof(Shaders::Generic3D::Position::Type));
mesh.addVertexBuffer(*vertexBuffer, 0, mesh.addVertexBuffer(*vertexBuffer, 0,
@ -140,16 +144,23 @@ std::tuple<Mesh, std::unique_ptr<Buffer>, std::unique_ptr<Buffer>> compile(const
} }
/* Fill vertex buffer with interleaved data */ /* Fill vertex buffer with interleaved data */
vertexBuffer->setData(data, BufferUsage::StaticDraw); vertexBuffer->setData(data, usage);
/* Fill index buffer */ /* If indexed, fill index buffer and configure indexed mesh */
std::unique_ptr<Buffer> indexBuffer; std::unique_ptr<Buffer> indexBuffer;
if(meshData.isIndexed()) { if(meshData.isIndexed()) {
Containers::Array<char> indexData;
Mesh::IndexType indexType;
UnsignedInt indexStart, indexEnd;
std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(meshData.indices());
indexBuffer.reset(new Buffer{Buffer::Target::ElementArray}); indexBuffer.reset(new Buffer{Buffer::Target::ElementArray});
MeshTools::compressIndices(mesh, *indexBuffer, usage, meshData.indices()); indexBuffer->setData(data, usage);
mesh.setCount(meshData.indices().size())
.setIndexBuffer(*indexBuffer, 0, indexType, indexStart, indexEnd);
/* Else set proper vertex count */ /* Else set vertex count */
} mesh.setCount(vertexCount); } mesh.setCount(meshData.positions(0).size());
return std::make_tuple(std::move(mesh), std::move(vertexBuffer), std::move(indexBuffer)); return std::make_tuple(std::move(mesh), std::move(vertexBuffer), std::move(indexBuffer));
} }

47
src/Magnum/MeshTools/CompressIndices.cpp

@ -41,50 +41,53 @@ template<> constexpr Mesh::IndexType indexType<UnsignedByte>() { return Mesh::In
template<> constexpr Mesh::IndexType indexType<UnsignedShort>() { return Mesh::IndexType::UnsignedShort; } template<> constexpr Mesh::IndexType indexType<UnsignedShort>() { return Mesh::IndexType::UnsignedShort; }
template<> constexpr Mesh::IndexType indexType<UnsignedInt>() { return Mesh::IndexType::UnsignedInt; } template<> constexpr Mesh::IndexType indexType<UnsignedInt>() { return Mesh::IndexType::UnsignedInt; }
template<class T> inline std::tuple<std::size_t, Mesh::IndexType, Containers::Array<char>> compress(const std::vector<UnsignedInt>& indices) { template<class T> inline std::pair<Containers::Array<char>, Mesh::IndexType> compress(const std::vector<UnsignedInt>& indices) {
Containers::Array<char> buffer(indices.size()*sizeof(T)); Containers::Array<char> buffer(indices.size()*sizeof(T));
for(std::size_t i = 0; i != indices.size(); ++i) { for(std::size_t i = 0; i != indices.size(); ++i) {
T index = static_cast<T>(indices[i]); T index = static_cast<T>(indices[i]);
std::memcpy(buffer.begin()+i*sizeof(T), &index, sizeof(T)); std::memcpy(buffer.begin()+i*sizeof(T), &index, sizeof(T));
} }
return std::make_tuple(indices.size(), indexType<T>(), std::move(buffer)); return {std::move(buffer), indexType<T>()};
} }
std::tuple<std::size_t, Mesh::IndexType, Containers::Array<char>> compressIndicesInternal(const std::vector<UnsignedInt>& indices, UnsignedInt max) { }
switch(Math::log(256, max)) {
std::tuple<Containers::Array<char>, Mesh::IndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector<UnsignedInt>& indices) {
/** @todo Performance hint when range can be represented by smaller value? */
auto minmax = std::minmax_element(indices.begin(), indices.end());
std::pair<Containers::Array<char>, Mesh::IndexType> typeData;
switch(Math::log(256, *minmax.second)) {
case 0: case 0:
return compress<UnsignedByte>(indices); typeData = compress<UnsignedByte>(indices);
break;
case 1: case 1:
return compress<UnsignedShort>(indices); typeData = compress<UnsignedShort>(indices);
break;
case 2: case 2:
case 3: case 3:
return compress<UnsignedInt>(indices); typeData = compress<UnsignedInt>(indices);
break;
default: default:
CORRADE_ASSERT(false, "MeshTools::compressIndices(): no type able to index" << max << "elements.", {}); CORRADE_ASSERT(false, "MeshTools::compressIndices(): no type able to index" << *minmax.second << "elements.", {});
} }
}
}
std::tuple<std::size_t, Mesh::IndexType, Containers::Array<char>> compressIndices(const std::vector<UnsignedInt>& indices) { return std::make_tuple(std::move(typeData.first), typeData.second, *minmax.first, *minmax.second);
return compressIndicesInternal(indices, *std::max_element(indices.begin(), indices.end()));
} }
#ifdef MAGNUM_BUILD_DEPRECATED
void compressIndices(Mesh& mesh, Buffer& buffer, BufferUsage usage, const std::vector<UnsignedInt>& indices) { void compressIndices(Mesh& mesh, Buffer& buffer, BufferUsage usage, const std::vector<UnsignedInt>& indices) {
auto minmax = std::minmax_element(indices.begin(), indices.end());
/** @todo Performance hint when range can be represented by smaller value? */
std::size_t indexCount;
Mesh::IndexType indexType;
Containers::Array<char> data; Containers::Array<char> data;
std::tie(indexCount, indexType, data) = compressIndicesInternal(indices, *minmax.second); Mesh::IndexType type;
UnsignedInt start, end;
std::tie(data, type, start, end) = compressIndices(indices);
mesh.setCount(indices.size())
.setIndexBuffer(buffer, 0, indexType, *minmax.first, *minmax.second);
buffer.setData(data, usage); buffer.setData(data, usage);
mesh.setCount(indices.size())
.setIndexBuffer(buffer, 0, type, start, end);
} }
#endif
}} }}

35
src/Magnum/MeshTools/CompressIndices.h

@ -39,24 +39,34 @@ namespace Magnum { namespace MeshTools {
/** /**
@brief Compress vertex indices @brief Compress vertex indices
@param indices Index array @param indices Index array
@return Index count, type and compressed index array @return Index range, type and compressed index array
This function takes index array and outputs them compressed to smallest This function takes index array and outputs them compressed to smallest
possible size. For example when your indices have maximum number 463, it's possible size. For example when your indices have maximum number 463, it's
wasteful to store them in array of 32bit integers, array of 16bit integers is wasteful to store them in array of 32bit integers, array of 16bit integers is
sufficient. Example usage: sufficient.
Example usage:
@code @code
std::size_t indexCount; std::vector<UnsignedInt> indices;
Containers::Array<char> indexData;
Mesh::IndexType indexType; Mesh::IndexType indexType;
Containers::Array<char> data; UnsignedInt indexStart, indexEnd;
std::tie(indexCount, indexType, data) = MeshTools::compressIndices(indices); std::tie(indexData, indexType, indexStart, indexEnd) = MeshTools::compressIndices(indices);
@endcode
Buffer indexBuffer;
indexBuffer.setData(indexData, BufferUsage::StaticDraw);
See also @ref compressIndices(Mesh&, Buffer&, BufferUsage, const std::vector<UnsignedInt>&), Mesh mesh;
which writes the compressed data directly into index buffer of given mesh. mesh.setCount(indices.size())
.setIndexBuffer(indexBuffer, 0, indexType, indexStart, indexEnd);
@endcode
@todo Extract IndexType out of Mesh class
*/ */
std::tuple<std::size_t, Mesh::IndexType, Containers::Array<char>> MAGNUM_MESHTOOLS_EXPORT compressIndices(const std::vector<UnsignedInt>& indices); std::tuple<Containers::Array<char>, Mesh::IndexType, UnsignedInt, UnsignedInt> MAGNUM_MESHTOOLS_EXPORT compressIndices(const std::vector<UnsignedInt>& indices);
#ifdef MAGNUM_BUILD_DEPRECATED
/** /**
@brief Compress vertex indices and write them to index buffer @brief Compress vertex indices and write them to index buffer
@param mesh Output mesh @param mesh Output mesh
@ -64,14 +74,17 @@ std::tuple<std::size_t, Mesh::IndexType, Containers::Array<char>> MAGNUM_MESHTOO
@param usage Index buffer usage @param usage Index buffer usage
@param indices Index array @param indices Index array
@deprecated Use general-purpose
@ref Magnum::MeshTools::compressIndices(const std::vector<UnsignedInt>&) "compressIndices(const std::vector<UnsignedInt>&)"
instead.
The same as @ref compressIndices(const std::vector<UnsignedInt>&), but this The same as @ref compressIndices(const std::vector<UnsignedInt>&), but this
function writes the output to given buffer and calls @ref Mesh::setCount() and function writes the output to given buffer and calls @ref Mesh::setCount() and
@ref Mesh::setIndexBuffer(), thus you don't need to do anything else for mesh @ref Mesh::setIndexBuffer(), thus you don't need to do anything else for mesh
index configuration. index configuration.
@see @ref MeshTools::interleave(), @ref MeshTools::compile()
*/ */
void MAGNUM_MESHTOOLS_EXPORT compressIndices(Mesh& mesh, Buffer& buffer, BufferUsage usage, const std::vector<UnsignedInt>& indices); void MAGNUM_MESHTOOLS_EXPORT compressIndices(Mesh& mesh, Buffer& buffer, BufferUsage usage, const std::vector<UnsignedInt>& indices);
#endif
}} }}

84
src/Magnum/MeshTools/Interleave.h

@ -30,12 +30,16 @@
*/ */
#include <cstring> #include <cstring>
#include <vector> #include <Corrade/Containers/Array.h>
#include <limits> #include <Corrade/Utility/Assert.h>
#include <tuple>
#include "Magnum/Mesh.h" #include "Magnum/Magnum.h"
#ifdef MAGNUM_BUILD_DEPRECATED
#include <tuple>
#include "Magnum/Buffer.h" #include "Magnum/Buffer.h"
#include "Magnum/Mesh.h"
#endif
namespace Magnum { namespace MeshTools { namespace Magnum { namespace MeshTools {
@ -94,20 +98,21 @@ template<class T, class ...U> void writeInterleaved(std::size_t stride, char* st
@brief %Interleave vertex attributes @brief %Interleave vertex attributes
This function takes list of attribute arrays and returns them interleaved, so This function takes list of attribute arrays and returns them interleaved, so
data for each attribute are in continuous place in memory. Returned tuple data for each attribute are in continuous place in memory.
contains attribute count, stride and data array. Deleting the data array is up
to the user.
Size of the data buffer can be computed from attribute count and stride, as Example usage:
shown below. Example usage:
@code @code
std::vector<Vector4> positions; MeshPrimitive primitive;
std::vector<Vector3> positions;
std::vector<Vector2> textureCoordinates; std::vector<Vector2> textureCoordinates;
std::size_t attributeCount;
std::size_t stride; Buffer vertexBuffer;
Containers::Array<char> data; vertexBuffer.setData(MeshTools::interleave(positions, textureCoordinates), BufferUsage::StaticDraw);
std::tie(attributeCount, stride, data) = MeshTools::interleave(positions, textureCoordinates);
// ... Mesh mesh;
mesh.setPrimitive(primitive)
.setCount(positions.count())
.addVertexBuffer(vertexBuffer, 0, MyShader::Position{}, MyShader::TextureCoordinates{});
@endcode @endcode
It's often desirable to align data for one vertex on 32bit boundaries. To It's often desirable to align data for one vertex on 32bit boundaries. To
@ -116,10 +121,8 @@ achieve that, you can specify gaps between the attributes:
std::vector<Vector4> positions; std::vector<Vector4> positions;
std::vector<UnsignedShort> weights; std::vector<UnsignedShort> weights;
std::vector<Color3ub> vertexColors; std::vector<Color3ub> vertexColors;
std::size_t attributeCount;
std::size_t stride; auto data = MeshTools::interleave(positions, weights, 2, textureCoordinates, 1);
Containers::Array<char> data;
std::tie(attributeCount, stride, data) = MeshTools::interleave(positions, weights, 2, textureCoordinates, 1);
@endcode @endcode
All gap bytes are set zero. This way vertex stride is 24 bytes, without gaps it All gap bytes are set zero. This way vertex stride is 24 bytes, without gaps it
would be 21 bytes, causing possible performance loss. would be 21 bytes, causing possible performance loss.
@ -131,13 +134,13 @@ would be 21 bytes, causing possible performance loss.
for) and function `size()` returning count of elements. In most cases it for) and function `size()` returning count of elements. In most cases it
will be `std::vector` or `std::array`. will be `std::vector` or `std::array`.
See also @ref interleave(Mesh&, Buffer&, BufferUsage, const T&...), @see @ref interleaveInto()
which writes the interleaved array directly into buffer of given mesh or @todo remove `std::enable_if` when deprecated overloads are removed
@ref interleaveInto() which writes the data into existing buffer instead of
creating new one.
*/ */
/* enable_if to avoid clash with overloaded function below */ /* enable_if to avoid clash with overloaded function below */
template<class T, class ...U> typename std::enable_if<!std::is_same<T, Mesh>::value, std::tuple<std::size_t, std::size_t, Containers::Array<char>>>::type interleave(const T& first, const U&... next) { template<class T, class ...U> typename std::enable_if<!std::is_same<T, Mesh>::value, Containers::Array<char>>::type
interleave(const T& first, const U&... next)
{
/* Compute buffer size and stride */ /* Compute buffer size and stride */
const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...); const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...);
const std::size_t stride = Implementation::Stride{}(first, next...); const std::size_t stride = Implementation::Stride{}(first, next...);
@ -147,10 +150,10 @@ template<class T, class ...U> typename std::enable_if<!std::is_same<T, Mesh>::va
Containers::Array<char> data = Containers::Array<char>::zeroInitialized(attributeCount*stride); Containers::Array<char> data = Containers::Array<char>::zeroInitialized(attributeCount*stride);
Implementation::writeInterleaved(stride, data.begin(), first, next...); Implementation::writeInterleaved(stride, data.begin(), first, next...);
return std::make_tuple(attributeCount, stride, std::move(data)); return data;
/* Otherwise return nullptr */ /* Otherwise return nullptr */
} else return std::make_tuple(0, stride, nullptr); } else return nullptr;
} }
/** /**
@ -165,18 +168,17 @@ parameters.
arrays have the same size. The passed buffer must also be large enough to arrays have the same size. The passed buffer must also be large enough to
contain the interleaved data. contain the interleaved data.
*/ */
template<class T, class ...U> std::tuple<std::size_t, std::size_t> interleaveInto(Containers::ArrayReference<char> buffer, const T& first, const U&... next) { template<class T, class ...U> void interleaveInto(Containers::ArrayReference<char> buffer, const T& first, const U&... next) {
/* Verify expected buffer size */ /* Verify expected buffer size */
const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...); const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...);
const std::size_t stride = Implementation::Stride{}(first, next...); const std::size_t stride = Implementation::Stride{}(first, next...);
CORRADE_ASSERT(attributeCount*stride <= buffer.size(), "MeshTools::interleaveInto(): the data buffer is too small, expected" << attributeCount*stride << "but got" << buffer.size(), {}); CORRADE_ASSERT(attributeCount*stride <= buffer.size(), "MeshTools::interleaveInto(): the data buffer is too small, expected" << attributeCount*stride << "but got" << buffer.size(), );
/* Write data */ /* Write data */
Implementation::writeInterleaved(stride, buffer.begin(), first, next...); Implementation::writeInterleaved(stride, buffer.begin(), first, next...);
return std::make_tuple(attributeCount, stride);
} }
#ifdef MAGNUM_BUILD_DEPRECATED
/** /**
@brief %Interleave vertex attributes, write them to array buffer and configure the mesh @brief %Interleave vertex attributes, write them to array buffer and configure the mesh
@param mesh Output mesh @param mesh Output mesh
@ -184,6 +186,10 @@ template<class T, class ...U> std::tuple<std::size_t, std::size_t> interleaveInt
@param usage Vertex buffer usage @param usage Vertex buffer usage
@param attributes Attribute arrays and gaps @param attributes Attribute arrays and gaps
@deprecated Use general-purpose
@ref Magnum::MeshTools::interleave(const T&...) "interleave(const T&...)"
instead.
The same as @ref interleave(const T&, const U&...), but this function also The same as @ref interleave(const T&, const U&...), but this function also
writes the output to given array buffer. If given mesh is not indexed, it also writes the output to given array buffer. If given mesh is not indexed, it also
updates vertex count in the mesh accordingly, so you don't have to call updates vertex count in the mesh accordingly, so you don't have to call
@ -193,20 +199,19 @@ updates vertex count in the mesh accordingly, so you don't have to call
@ref Mesh::addVertexBuffer() on the mesh afterwards. @ref Mesh::addVertexBuffer() on the mesh afterwards.
@see @ref compressIndices(), @ref compile() @see @ref compressIndices(), @ref compile()
@todo rework so Mesh & Buffer doesn't need to be included in header
*/ */
template<class ...T> void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { template<class ...T> CORRADE_DEPRECATED("Use interleave(const T&...) instead") void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) {
Containers::Array<char> data; if(!mesh.isIndexed()) mesh.setCount(Implementation::AttributeCount{}(attributes...));
std::size_t attributeCount; buffer.setData(interleave(attributes...), usage);
std::tie(attributeCount, std::ignore, data) = interleave(attributes...);
if(!mesh.isIndexed()) mesh.setCount(attributeCount);
buffer.setData(data, usage);
} }
/** /**
@brief Write vertex attribute to array buffer and configure the mesh @brief Write vertex attribute to array buffer and configure the mesh
@deprecated Use general-purpose
@ref Magnum::MeshTools::interleave(const T&...) "interleave(const T&...)"
instead.
Simplified specialization of the above function for only one attribute array, Simplified specialization of the above function for only one attribute array,
equivalent to the following: equivalent to the following:
@code @code
@ -214,10 +219,11 @@ buffer.setData(attribute, usage);
if(!mesh.isIndexed()) mesh.setVertexCount(attribute.size()); if(!mesh.isIndexed()) mesh.setVertexCount(attribute.size());
@endcode @endcode
*/ */
template<class T> typename std::enable_if<!std::is_convertible<T, std::size_t>::value, void>::type interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) { template<class T> CORRADE_DEPRECATED("Use interleave(const T&...) instead") typename std::enable_if<!std::is_convertible<T, std::size_t>::value, void>::type interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) {
if(!mesh.isIndexed()) mesh.setCount(attribute.size()); if(!mesh.isIndexed()) mesh.setCount(attribute.size());
buffer.setData(attribute, usage); buffer.setData(attribute, usage);
} }
#endif
}} }}

33
src/Magnum/MeshTools/Test/CompressIndicesTest.cpp

@ -47,27 +47,29 @@ CompressIndicesTest::CompressIndicesTest() {
} }
void CompressIndicesTest::compressChar() { void CompressIndicesTest::compressChar() {
std::size_t indexCount;
Mesh::IndexType indexType;
Containers::Array<char> data; Containers::Array<char> data;
std::tie(indexCount, indexType, data) = MeshTools::compressIndices( Mesh::IndexType type;
UnsignedInt start, end;
std::tie(data, type, start, end) = MeshTools::compressIndices(
std::vector<UnsignedInt>{1, 2, 3, 0, 4}); std::vector<UnsignedInt>{1, 2, 3, 0, 4});
CORRADE_COMPARE(indexCount, 5); CORRADE_COMPARE(start, 0);
CORRADE_VERIFY(indexType == Mesh::IndexType::UnsignedByte); CORRADE_COMPARE(end, 4);
CORRADE_COMPARE(type, Mesh::IndexType::UnsignedByte);
CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()), CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()),
(std::vector<char>{ 0x01, 0x02, 0x03, 0x00, 0x04 })); (std::vector<char>{ 0x01, 0x02, 0x03, 0x00, 0x04 }));
} }
void CompressIndicesTest::compressShort() { void CompressIndicesTest::compressShort() {
std::size_t indexCount;
Mesh::IndexType indexType;
Containers::Array<char> data; Containers::Array<char> data;
std::tie(indexCount, indexType, data) = MeshTools::compressIndices( Mesh::IndexType type;
UnsignedInt start, end;
std::tie(data, type, start, end) = MeshTools::compressIndices(
std::vector<UnsignedInt>{1, 256, 0, 5}); std::vector<UnsignedInt>{1, 256, 0, 5});
CORRADE_COMPARE(indexCount, 4); CORRADE_COMPARE(start, 0);
CORRADE_VERIFY(indexType == Mesh::IndexType::UnsignedShort); CORRADE_COMPARE(end, 256);
CORRADE_COMPARE(type, Mesh::IndexType::UnsignedShort);
if(!Utility::Endianness::isBigEndian()) { if(!Utility::Endianness::isBigEndian()) {
CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()), CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()),
(std::vector<char>{ 0x01, 0x00, (std::vector<char>{ 0x01, 0x00,
@ -84,14 +86,15 @@ void CompressIndicesTest::compressShort() {
} }
void CompressIndicesTest::compressInt() { void CompressIndicesTest::compressInt() {
std::size_t indexCount;
Mesh::IndexType indexType;
Containers::Array<char> data; Containers::Array<char> data;
std::tie(indexCount, indexType, data) = MeshTools::compressIndices( Mesh::IndexType type;
UnsignedInt start, end;
std::tie(data, type, start, end) = MeshTools::compressIndices(
std::vector<UnsignedInt>{65536, 3, 2}); std::vector<UnsignedInt>{65536, 3, 2});
CORRADE_COMPARE(indexCount, 3); CORRADE_COMPARE(start, 2);
CORRADE_VERIFY(indexType == Mesh::IndexType::UnsignedInt); CORRADE_COMPARE(end, 65536);
CORRADE_COMPARE(type, Mesh::IndexType::UnsignedInt);
if(!Utility::Endianness::isBigEndian()) { if(!Utility::Endianness::isBigEndian()) {
CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()), CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()),

23
src/Magnum/MeshTools/Test/InterleaveTest.cpp

@ -87,16 +87,11 @@ void InterleaveTest::strideGaps() {
} }
void InterleaveTest::write() { void InterleaveTest::write() {
std::size_t attributeCount; const Containers::Array<char> data = MeshTools::interleave(
std::size_t stride;
Containers::Array<char> data;
std::tie(attributeCount, stride, data) = MeshTools::interleave(
std::vector<Byte>{0, 1, 2}, std::vector<Byte>{0, 1, 2},
std::vector<Int>{3, 4, 5}, std::vector<Int>{3, 4, 5},
std::vector<Short>{6, 7, 8}); std::vector<Short>{6, 7, 8});
CORRADE_COMPARE(attributeCount, std::size_t(3));
CORRADE_COMPARE(stride, std::size_t(7));
if(!Utility::Endianness::isBigEndian()) { if(!Utility::Endianness::isBigEndian()) {
CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()), (std::vector<char>{ CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()), (std::vector<char>{
0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00,
@ -113,17 +108,11 @@ void InterleaveTest::write() {
} }
void InterleaveTest::writeGaps() { void InterleaveTest::writeGaps() {
std::size_t attributeCount; const Containers::Array<char> data = MeshTools::interleave(
std::size_t stride;
Containers::Array<char> data;
std::tie(attributeCount, stride, data) = MeshTools::interleave(
std::vector<Byte>{0, 1, 2}, 3, std::vector<Byte>{0, 1, 2}, 3,
std::vector<Int>{3, 4, 5}, std::vector<Int>{3, 4, 5},
std::vector<Short>{6, 7, 8}, 2); std::vector<Short>{6, 7, 8}, 2);
CORRADE_COMPARE(attributeCount, std::size_t(3));
CORRADE_COMPARE(stride, std::size_t(12));
if(!Utility::Endianness::isBigEndian()) { if(!Utility::Endianness::isBigEndian()) {
/* byte, _____________gap, int___________________, short_____, _______gap */ /* byte, _____________gap, int___________________, short_____, _______gap */
CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()), (std::vector<char>{ CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()), (std::vector<char>{
@ -142,8 +131,6 @@ void InterleaveTest::writeGaps() {
} }
void InterleaveTest::interleaveInto() { void InterleaveTest::interleaveInto() {
std::size_t attributeCount;
std::size_t stride;
auto data = Containers::Array<char>::from( auto data = Containers::Array<char>::from(
0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77,
0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77,
@ -151,11 +138,7 @@ void InterleaveTest::interleaveInto() {
0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77
); );
std::tie(attributeCount, stride) = MeshTools::interleaveInto(data, MeshTools::interleaveInto(data, 2, std::vector<Int>{4, 5, 6, 7}, 1, std::vector<Short>{0, 1, 2, 3}, 3);
2, std::vector<Int>{4, 5, 6, 7}, 1, std::vector<Short>{0, 1, 2, 3}, 3);
CORRADE_COMPARE(attributeCount, std::size_t{4});
CORRADE_COMPARE(stride, std::size_t{12});
if(!Utility::Endianness::isBigEndian()) { if(!Utility::Endianness::isBigEndian()) {
/* _______gap, int___________________, _gap, short_____, _____________gap */ /* _______gap, int___________________, _gap, short_____, _____________gap */

Loading…
Cancel
Save