Browse Source

GL: allow to construct DynamicAttribute from VertexFormat.

pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
13c071a1aa
  1. 18
      doc/snippets/MagnumTrade.cpp
  2. 58
      src/Magnum/GL/Attribute.cpp
  3. 33
      src/Magnum/GL/Attribute.h
  4. 2
      src/Magnum/GL/CMakeLists.txt
  5. 149
      src/Magnum/GL/Test/AttributeTest.cpp
  6. 2
      src/Magnum/GL/Test/CMakeLists.txt
  7. 6
      src/Magnum/Trade/MeshData.h

18
doc/snippets/MagnumTrade.cpp

@ -277,17 +277,17 @@ GL::Mesh mesh{data.primitive()};
GL::Buffer vertices;
vertices.setData(data.vertexData());
/* Set up the position attribute */
Shaders::Phong::Position position;
auto positionFormat = data.attributeFormat(Trade::MeshAttribute::Position);
if(positionFormat == VertexFormat::Vector2)
position = {Shaders::Phong::Position::Components::Two};
else if(positionFormat == VertexFormat::Vector3)
position = {Shaders::Phong::Position::Components::Three};
else Fatal{} << "Huh?";
/* Set up the position and normal attributes */
mesh.addVertexBuffer(vertices,
data.attributeOffset(Trade::MeshAttribute::Position),
data.attributeStride(Trade::MeshAttribute::Position), position);
data.attributeStride(Trade::MeshAttribute::Position),
GL::DynamicAttribute{Shaders::Phong::Position{},
data.attributeFormat(Trade::MeshAttribute::Position)});
mesh.addVertexBuffer(vertices,
data.attributeOffset(Trade::MeshAttribute::Normal),
data.attributeStride(Trade::MeshAttribute::Normal),
GL::DynamicAttribute{Shaders::Phong::Normal{},
data.attributeFormat(Trade::MeshAttribute::Normal)});
// Set up other attributes ...

58
src/Magnum/GL/Attribute.cpp

@ -28,6 +28,8 @@
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
#include "Magnum/VertexFormat.h"
namespace Magnum { namespace GL {
Debug& operator<<(Debug& debug, const DynamicAttribute::Kind value) {
@ -467,4 +469,60 @@ Debug& operator<<(Debug& debug, const Attribute<Math::Vector<4, Float>>::DataTyp
}
DynamicAttribute::DynamicAttribute(const Kind kind, UnsignedInt location, const VertexFormat format, GLint maxComponents): _kind{kind}, _location{location}, _components{Components(vertexFormatComponentCount(format))} {
/* Translate component type to a GL-specific value */
switch(vertexFormatComponentFormat(format)) {
#define _c(format) \
case VertexFormat::format: \
_dataType = DataType::format; \
break;
_c(UnsignedByte)
_c(Byte)
_c(UnsignedShort)
_c(Short)
_c(UnsignedInt)
_c(Int)
_c(Float)
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
_c(Half)
#endif
#ifndef MAGNUM_TARGET_GLES
_c(Double)
#endif
#undef _c
/* Nothing else expected to be returned from
vertexFormatComponentFormat() */
default: CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* If the type is normalized, switch the type to GenericNormalized (if not
already), and check that the attribute isn't expected to be integral or
long */
if(isVertexFormatNormalized(format)) {
CORRADE_ASSERT(kind == Kind::Generic || kind == Kind::GenericNormalized,
"GL::DynamicAttribute: can't use" << format << "for a" << kind << "attribute", );
_kind = Kind::GenericNormalized;
/* Otherwise check that non-normalized types aren't used for attributes
that are expected to be normalized. Float is an exception. */
} else if(_dataType != DataType::Float) {
CORRADE_ASSERT(kind != Kind::GenericNormalized,
"GL::DynamicAttribute: can't use" << format << "for a normalized attribute", );
/* Finally, float data types can't be used for integer attributes */
} else {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(kind != Kind::Integral,
"GL::DynamicAttribute: can't use" << format << "for an integral attribute", );
#endif
}
#ifndef CORRADE_NO_DEBUG
/* Should pass also if maxComponents is GL_BGRA */
CORRADE_ASSERT(GLint(_components) <= maxComponents,
"GL::DynamicAttribute: can't use" << format << "for a" << maxComponents << Debug::nospace << "-component attribute", );
#else
static_cast<void>(maxComponents);
#endif
}
}}

33
src/Magnum/GL/Attribute.h

@ -346,7 +346,7 @@ location and base type. Note that unlike the compile-time specification, this
class doesn't do any sanity verification and leaves most of the responsibility
on the user.
*/
class DynamicAttribute {
class MAGNUM_GL_EXPORT DynamicAttribute {
public:
/**
* @brief Attribute kind
@ -543,6 +543,31 @@ class DynamicAttribute {
*/
template<UnsignedInt location_, class T> constexpr /*implicit*/ DynamicAttribute(const Attribute<location_, T>& attribute);
/**
* @brief Construct from a generic mesh attribute type
* @m_since_latest
*
* The @p type is expected to be compatible with @p kind --- i.e.,
* normalized or floating-point for @ref Kind::GenericNormalized,
* non-normalized for @ref Kind::Integral / @ref Kind::Long and
* integral for @ref Kind::Integral.
*/
explicit DynamicAttribute(Kind kind, UnsignedInt location, VertexFormat format): DynamicAttribute{kind, location, format, 4} {}
/**
* @brief Construct from a compile-time attribute with a generic mesh attribute type override
* @m_since_latest
*
* Extracts kind and location from passed @ref Attribute type and calls
* @ref DynamicAttribute(Kind, UnsignedInt, VertexFormat). Expects that
* @p type's component count is not larger than the component count
* defined in the @p Attribute type. Note that only the
* compile-time-defined properties of the @p Attribute type are used,
* the instance-specific data type, options and component count is
* ignored.
*/
template<UnsignedInt location_, class T> explicit DynamicAttribute(const Attribute<location_, T>&, VertexFormat format);
/** @brief Attribute kind */
constexpr Kind kind() const { return _kind; }
@ -556,6 +581,10 @@ class DynamicAttribute {
constexpr DataType dataType() const { return _dataType; }
private:
/* Used by the constructor taking Attribute, defined in cpp to avoid
a dependency on <Magnum/Mesh.h> for the assertion */
explicit DynamicAttribute(Kind kind, UnsignedInt location, VertexFormat format, GLint maxComponents);
Kind _kind;
UnsignedInt _location;
Components _components;
@ -892,6 +921,8 @@ template<class T> struct Attribute<Math::Matrix4<T>>: Attribute<Math::Matrix<4,
template<UnsignedInt location_, class T> constexpr DynamicAttribute::DynamicAttribute(const Attribute<location_, T>& attribute): _kind{Implementation::kindFor<location_, T>(attribute.dataOptions())}, _location{location_}, _components{Components(GLint(attribute.components()))}, _dataType{DataType(GLenum(attribute.dataType()))} {}
template<UnsignedInt location_, class T> DynamicAttribute::DynamicAttribute(const Attribute<location_, T>& attribute, const VertexFormat format): DynamicAttribute{Implementation::kindFor<location_, T>(attribute.dataOptions()), location_, format, GLint(Implementation::Attribute<T>::DefaultComponents)} {}
}}
#endif

2
src/Magnum/GL/CMakeLists.txt

@ -27,7 +27,6 @@ set(MagnumGL_SRCS
AbstractObject.cpp
AbstractQuery.cpp
AbstractShaderProgram.cpp
Attribute.cpp
Buffer.cpp
Context.cpp
DefaultFramebuffer.cpp
@ -56,6 +55,7 @@ set(MagnumGL_SRCS
set(MagnumGL_GracefulAssert_SRCS
AbstractFramebuffer.cpp
AbstractTexture.cpp
Attribute.cpp
CubeMapTexture.cpp
Mesh.cpp
MeshView.cpp

149
src/Magnum/GL/Test/AttributeTest.cpp

@ -27,6 +27,7 @@
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/VertexFormat.h"
#include "Magnum/GL/Attribute.h"
namespace Magnum { namespace GL { namespace Test { namespace {
@ -53,6 +54,23 @@ struct AttributeTest: TestSuite::Tester {
void attributeMatrixNxNd();
void attributeMatrixMxNd();
void attributeFromGenericFormat();
#ifndef MAGNUM_TARGET_GLES2
void attributeFromGenericFormatIntegral();
#endif
#ifndef MAGNUM_TARGET_GLES
void attributeFromGenericFormatLong();
#endif
void attributeFromGenericFormatEnableNormalized();
void attributeFromGenericFormatUnexpectedForNormalizedKind();
#ifndef MAGNUM_TARGET_GLES2
void attributeFromGenericFormatUnexpectedForIntegralKind();
#endif
#ifndef MAGNUM_TARGET_GLES
void attributeFromGenericFormatUnexpectedForLongKind();
#endif
void attributeFromGenericFormatTooManyComponents();
void debugComponents1();
void debugComponents2();
void debugComponents3();
@ -98,6 +116,23 @@ AttributeTest::AttributeTest() {
&AttributeTest::attributeMatrixNxNd,
&AttributeTest::attributeMatrixMxNd,
&AttributeTest::attributeFromGenericFormat,
#ifndef MAGNUM_TARGET_GLES2
&AttributeTest::attributeFromGenericFormatIntegral,
#endif
#ifndef MAGNUM_TARGET_GLES
&AttributeTest::attributeFromGenericFormatLong,
#endif
&AttributeTest::attributeFromGenericFormatEnableNormalized,
&AttributeTest::attributeFromGenericFormatUnexpectedForNormalizedKind,
#ifndef MAGNUM_TARGET_GLES2
&AttributeTest::attributeFromGenericFormatUnexpectedForIntegralKind,
#endif
#ifndef MAGNUM_TARGET_GLES
&AttributeTest::attributeFromGenericFormatUnexpectedForLongKind,
#endif
&AttributeTest::attributeFromGenericFormatTooManyComponents,
&AttributeTest::debugComponents1,
&AttributeTest::debugComponents2,
&AttributeTest::debugComponents3,
@ -491,6 +526,120 @@ void AttributeTest::attributeMatrixMxNd() {
#endif
}
void AttributeTest::attributeFromGenericFormat() {
DynamicAttribute a{DynamicAttribute::Kind::Generic, 3,
VertexFormat::UnsignedShort};
CORRADE_COMPARE(a.kind(), DynamicAttribute::Kind::Generic);
CORRADE_COMPARE(a.location(), 3);
CORRADE_COMPARE(a.components(), DynamicAttribute::Components::One);
CORRADE_COMPARE(a.dataType(), DynamicAttribute::DataType::UnsignedShort);
/* Check that compile-time attribs work too */
DynamicAttribute a2{Attribute<7, Vector3>{},
VertexFormat::UnsignedShort};
CORRADE_COMPARE(a2.kind(), DynamicAttribute::Kind::Generic);
CORRADE_COMPARE(a2.location(), 7);
CORRADE_COMPARE(a2.components(), DynamicAttribute::Components::One);
CORRADE_COMPARE(a2.dataType(), DynamicAttribute::DataType::UnsignedShort);
DynamicAttribute b{DynamicAttribute::Kind::GenericNormalized, 3,
VertexFormat::Vector2bNormalized};
CORRADE_COMPARE(b.kind(), DynamicAttribute::Kind::GenericNormalized);
CORRADE_COMPARE(b.location(), 3);
CORRADE_COMPARE(b.components(), DynamicAttribute::Components::Two);
CORRADE_COMPARE(b.dataType(), DynamicAttribute::DataType::Byte);
DynamicAttribute c{DynamicAttribute::Kind::Generic, 3,
VertexFormat::Vector4ui};
CORRADE_COMPARE(c.kind(), DynamicAttribute::Kind::Generic);
CORRADE_COMPARE(c.location(), 3);
CORRADE_COMPARE(c.components(), DynamicAttribute::Components::Four);
CORRADE_COMPARE(c.dataType(), DynamicAttribute::DataType::UnsignedInt);
/* This one shouldn't fail even though the normalization is (probably?)
ignored. Not exactly sure. */
DynamicAttribute d{DynamicAttribute::Kind::GenericNormalized, 3,
VertexFormat::Float};
CORRADE_COMPARE(d.kind(), DynamicAttribute::Kind::GenericNormalized);
CORRADE_COMPARE(d.location(), 3);
CORRADE_COMPARE(d.components(), DynamicAttribute::Components::One);
CORRADE_COMPARE(d.dataType(), DynamicAttribute::DataType::Float);
}
#ifndef MAGNUM_TARGET_GLES2
void AttributeTest::attributeFromGenericFormatIntegral() {
DynamicAttribute a{DynamicAttribute::Kind::Integral, 3,
VertexFormat::Vector3s};
CORRADE_COMPARE(a.kind(), DynamicAttribute::Kind::Integral);
CORRADE_COMPARE(a.location(), 3);
CORRADE_COMPARE(a.components(), DynamicAttribute::Components::Three);
CORRADE_COMPARE(a.dataType(), DynamicAttribute::DataType::Short);
}
#endif
#ifndef MAGNUM_TARGET_GLES
void AttributeTest::attributeFromGenericFormatLong() {
DynamicAttribute a{DynamicAttribute::Kind::Long, 15,
VertexFormat::Vector2d};
CORRADE_COMPARE(a.kind(), DynamicAttribute::Kind::Long);
CORRADE_COMPARE(a.location(), 15);
CORRADE_COMPARE(a.components(), DynamicAttribute::Components::Two);
CORRADE_COMPARE(a.dataType(), DynamicAttribute::DataType::Double);
}
#endif
void AttributeTest::attributeFromGenericFormatEnableNormalized() {
DynamicAttribute a{DynamicAttribute::Kind::Generic, 3,
VertexFormat::Vector3ubNormalized};
/* Generic is automatically switched to GenericNormalized */
CORRADE_COMPARE(a.kind(), DynamicAttribute::Kind::GenericNormalized);
CORRADE_COMPARE(a.location(), 3);
CORRADE_COMPARE(a.components(), DynamicAttribute::Components::Three);
CORRADE_COMPARE(a.dataType(), DynamicAttribute::DataType::UnsignedByte);
}
void AttributeTest::attributeFromGenericFormatUnexpectedForNormalizedKind() {
std::ostringstream out;
Error redirectError{&out};
DynamicAttribute{DynamicAttribute::Kind::GenericNormalized, 3,
VertexFormat::Int};
CORRADE_COMPARE(out.str(),
"GL::DynamicAttribute: can't use VertexFormat::Int for a normalized attribute\n");
}
#ifndef MAGNUM_TARGET_GLES2
void AttributeTest::attributeFromGenericFormatUnexpectedForIntegralKind() {
std::ostringstream out;
Error redirectError{&out};
DynamicAttribute{DynamicAttribute::Kind::Integral, 3,
VertexFormat::Vector2bNormalized};
DynamicAttribute{DynamicAttribute::Kind::Integral, 3,
VertexFormat::Vector3};
CORRADE_COMPARE(out.str(),
"GL::DynamicAttribute: can't use VertexFormat::Vector2bNormalized for a GL::DynamicAttribute::Kind::Integral attribute\n"
"GL::DynamicAttribute: can't use VertexFormat::Vector3 for an integral attribute\n");
}
#endif
#ifndef MAGNUM_TARGET_GLES
void AttributeTest::attributeFromGenericFormatUnexpectedForLongKind() {
std::ostringstream out;
Error redirectError{&out};
DynamicAttribute{DynamicAttribute::Kind::Long, 3,
VertexFormat::UnsignedShortNormalized};
CORRADE_COMPARE(out.str(),
"GL::DynamicAttribute: can't use VertexFormat::UnsignedShortNormalized for a GL::DynamicAttribute::Kind::Long attribute\n");
}
#endif
void AttributeTest::attributeFromGenericFormatTooManyComponents() {
std::ostringstream out;
Error redirectError{&out};
DynamicAttribute{Attribute<7, Vector2>{}, VertexFormat::Vector3};
CORRADE_COMPARE(out.str(),
"GL::DynamicAttribute: can't use VertexFormat::Vector3 for a 2-component attribute\n");
}
void AttributeTest::debugComponents1() {
typedef Attribute<3, Float> Attribute;

2
src/Magnum/GL/Test/CMakeLists.txt

@ -23,7 +23,7 @@
# DEALINGS IN THE SOFTWARE.
#
corrade_add_test(GLAttributeTest AttributeTest.cpp LIBRARIES MagnumGL)
corrade_add_test(GLAttributeTest AttributeTest.cpp LIBRARIES MagnumGLTestLib)
corrade_add_test(GLAbstractShaderProgramTest AbstractShaderProgramTest.cpp LIBRARIES MagnumGL)
corrade_add_test(GLBufferTest BufferTest.cpp LIBRARIES MagnumGL)
corrade_add_test(GLContextTest ContextTest.cpp LIBRARIES MagnumGL)

6
src/Magnum/Trade/MeshData.h

@ -373,7 +373,11 @@ cases, sometimes you may want to minimize the import time of a large model or
the imported data may be already in a well-optimized layout and format that you
want to preserve. The @ref MeshData class internally stores a contiguous blob
of data, which you can directly upload, and then use provided metadata to let
the GPU know of the format and layout:
the GPU know of the format and layout. Because there's a lot of possible types
of each attribute (floats, packed integers, ...), the @ref GL::DynamicAttribute
can accept a pair of @ref GL::Attribute defined by the shader and the actual
@ref VertexFormat, figuring out all properties such as component count and
element data type without having to explicitly handle all relevant types:
@snippet MagnumTrade.cpp MeshData-usage-advanced

Loading…
Cancel
Save