-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mesh compression #129
base: master
Are you sure you want to change the base?
Mesh compression #129
Conversation
Thank you! Happy to see this code. I'll hopefully have time to look into this during the weekend, need to refresh my memory first on what we wanted to do here :) And the issues you mention could very well be stupid bugs on my side, I tried my best to prepare MeshData for this but might not be handling everything correctly yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @melikebatihan!
I gave this a first pass, mainly looking at coding style, formatting and such. @mosra will have a closer look at the actual implementation.
Generally, try to use Containers::Array
instead of std::vector
where possible, on the one hand, this will require you to write code with better allocation behavior (from which standpoint your code looks pretty good either way already, though), and it might allow avoiding the <vector>
header for improved compile times.
The UnsignedByte
etc types exist to avoid potential differences between platforms/compilers. While rare, we use them consistently, so that accidental uses of the standard types stand out in the code.
@@ -38,7 +39,7 @@ | |||
#include <Magnum/Trade/ArrayAllocator.h> | |||
#include <Magnum/Trade/MeshData.h> | |||
#include <meshoptimizer.h> | |||
|
|||
#include <Corrade/Containers/StringView.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit-pick: there should be a space below this line
@@ -223,6 +224,36 @@ bool convertInPlaceInternal(const char* prefix, MeshData& mesh, const SceneConve | |||
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ | |||
} | |||
|
|||
if((configuration.value<bool>("encodeVertex") || configuration.value<bool>("decodeVertex")) && (mesh.vertexData().size() > 256)) { | |||
Error{} << prefix << "Compression and decompression is not possible with vertex data bigger than 256 bytes"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't sound quite right, but maybe I just don't fully understand how the mesh compression works in magnum:
I would suppose that even compressed meshes can get quite a lot bigger than just 256 bytes, maybe you meant that mesh compression is not possible with "more" than 256 bytes? 🤔 Feel free to point out in case I'm missing something.
} | ||
|
||
if(configuration.value<bool>("encodeVertex")) { | ||
unsigned int encoded_attribute_count = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsigned int encoded_attribute_count = 0; | |
UnsignedInt encodedAttributeCount = 0; |
To adhere to corrade coding style :)
if(configuration.value<bool>("encodeVertex")) { | ||
unsigned int encoded_attribute_count = 0; | ||
for(UnsignedInt i = 0; i < mesh.attributeCount(); ++i) { | ||
if(isVertexFormatImplementationSpecific(mesh.attributeFormat(i))) encoded_attribute_count++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(isVertexFormatImplementationSpecific(mesh.attributeFormat(i))) encoded_attribute_count++; | |
if(isVertexFormatImplementationSpecific(mesh.attributeFormat(i))) ++encoded_attribute_count; |
nit-pick: it doesn't make a difference, but x++
stands out to me to be there for some specific reason. (@mosra might think differently)
@@ -319,6 +350,64 @@ template<std::size_t(*F)(UnsignedInt*, const UnsignedInt*, std::size_t, const Fl | |||
|
|||
} | |||
|
|||
std::vector<unsigned char> encodeVertex(const MeshData& mesh) { | |||
auto owned_mesh = MeshTools::owned(mesh); | |||
std::vector<unsigned char> buffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a Containers::Array<UnsignedByte>
(UnsignedByte is unsigned char
)
You then need to use Containers::arrayReserve()
and Containers::arrayResize()
functions.
for(unsigned int i=0; i < owned_mesh.attributeCount(); ++i) { | ||
Containers::ArrayView<const unsigned char> data = Containers::arrayCast<const unsigned char>(owned_mesh.attribute(i).asContiguous()); | ||
const std::size_t vertexSize = owned_mesh.attributeStride(i); | ||
int result = meshopt_decodeVertexBuffer(decoded.data(), owned_mesh.vertexCount(), vertexSize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result
is not being used yet... probably needs to be checked for errors?
Vector4 color[8]; | ||
} vertices; | ||
|
||
const Vector2 positions[]{{ 0.5f, -0.5f}, { 0.5f, 0.5f}, {-0.5f, -0.5f}, {-0.5f, 0.5f}, {-1.0f, -0.5f}, {-1.0f, 0.5f}, {-1.0f, 0.5f}, {-1.0f, 0.5f}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's some extra spaces here that could be cleaned up :)
Trade::DataFlags{}, indices, Trade::MeshIndexData{indices}, | ||
Trade::DataFlag::Mutable, vertexData, { | ||
Trade::MeshAttributeData{Trade::MeshAttribute::Position, | ||
Containers::StridedArrayView1D<const Vector2>{ | ||
Containers::arrayView(vertexData), vertices.position, | ||
Containers::arraySize(positions), sizeof(Vector2)}}, | ||
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, | ||
Containers::StridedArrayView1D<const Vector2>{ | ||
Containers::arrayView(vertexData), vertices.textureCoordinate, | ||
Containers::arraySize(textureCoordinates), sizeof(Vector2)}}, | ||
Trade::MeshAttributeData{Trade::MeshAttribute::Color, | ||
Containers::StridedArrayView1D<const Vector4>{ | ||
Containers::arrayView(vertexData), vertices.color, | ||
Containers::arraySize(colors), sizeof(Vector4)}}, | ||
}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to reduce the indentation here, to make it easier to read
const Vector2 textureCoordinates[]{{1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 0.0f}, {0.0f, 1.0f}, {0.8f, 0.0f}, {1.0f, 0.8f}, {1.0f, 0.8f}, {1.0f, 0.8f}}; | ||
const Vector4 colors[]{{255,0,0,0}, {0,255,0,0}, {0,0,255,0}, {150,70,30,0}, {30,70,150,0}, {30,150,70,0}, {30,150,70,0}, {30,150,70,0}}; | ||
|
||
for(UnsignedInt i = 0; i < 8; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for(UnsignedInt i = 0; i < 8; i++) { | |
for(UnsignedInt i = 0; i < 8; ++i) { |
Ultra nit-picky, as above, sorry! :)
Containers::Pointer<AbstractSceneConverter> converter = _manager.instantiate("MeshOptimizerSceneConverter"); | ||
converter->configuration().setValue("encodeVertex", true); | ||
|
||
struct QuadVertex { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What makes this vertex a "Quad" rather than just a vertex? Possibly just Vertex
is sufficient :)
Hey @mosra, @Squareys,
here is the first outlook on mesh compression.
I have used “TexturedQuadExample” from magnum examples to figure out how mesh compression of meshoptimizer library works and created interleaved and non-interleaved mesh data using the position, texture, and color data in the example. Later on, I adapted my work there to the plugin, but got the following errors when I started testing:
Index Compression
When wrapped in a MeshData instance as the following, the encoded index data can’t be decoded because the encoded indexData.size() gets assigned to the indexCount(), hence the requirement ‘index_count % 3 == 0' fails:
out = Trade::MeshData{primitive, {}, encoded_buffer, Trade::MeshIndexData{encoded_buffer}, {}, vertexData, attributeData, vertexCount};
So, in the case above out.indexCount() yields out.indexData.size() instead of the number of indices.
Vertex Compression
1- With interleaved mesh, encoded data can be put in a MeshData instance only when all attribute spans are smaller than the encoded data size, which can be achieved only with short vertex buffers as in the test example
MeshOptimizerSceneConverterTest::encodeDecodeInterleavedMesh()
. However, an encoded vertex data successfully wrapped in a MeshData instance can’t be used as an argument in doConvert() for decoding because the attribute information doesn’t match the vertex data information.When the number of vertices is increased as in the example
MeshOptimizerSceneConverterTest::encodeInterleavedLongMesh()
, an error about an attribute span incompatible with the given encoded vertex data occurs, hence no MeshData instance with the encoded data in it can be created.2- With non-interleaved mesh, I’ve done encoding attribute by attribute and merged encoded buffers into one to wrap it with a MeshData instance, which makes decoding problematic because decoding also needs to be performed separately on each attribute but encoded MeshData can’t give any information on where each attribute data starts and ends. I tried putting this info in the MeshData instance by changing the offset of the encoded data in accordance with the size of each encoded attribute data but it returned with an attribute span error.