Starting to render something for meshes.
diff --git a/include/rive/bones/skinnable.hpp b/include/rive/bones/skinnable.hpp
index 0cbce5a..d71aa38 100644
--- a/include/rive/bones/skinnable.hpp
+++ b/include/rive/bones/skinnable.hpp
@@ -1,6 +1,8 @@
#ifndef _RIVE_SKINNABLE_HPP_
#define _RIVE_SKINNABLE_HPP_
+#include "rive/rive_types.hpp"
+
namespace rive {
class Skin;
class Component;
diff --git a/include/rive/shapes/mesh.hpp b/include/rive/shapes/mesh.hpp
index 69ea246..5bef62d 100644
--- a/include/rive/shapes/mesh.hpp
+++ b/include/rive/shapes/mesh.hpp
@@ -1,19 +1,25 @@
#ifndef _RIVE_MESH_HPP_
#define _RIVE_MESH_HPP_
#include "rive/generated/shapes/mesh_base.hpp"
+#include "rive/bones/skinnable.hpp"
#include "rive/span.hpp"
#include "rive/refcnt.hpp"
+#include "rive/renderer.hpp"
namespace rive {
class MeshVertex;
- class Mesh : public MeshBase {
+ class Mesh : public MeshBase, public Skinnable {
protected:
class IndexBuffer : public std::vector<uint16_t>, public RefCnt {};
std::vector<MeshVertex*> m_Vertices;
rcp<IndexBuffer> m_IndexBuffer;
+ rcp<RenderBuffer> m_IndexRenderBuffer;
+ rcp<RenderBuffer> m_VertexRenderBuffer;
+ rcp<RenderBuffer> m_UVRenderBuffer;
+
public:
StatusCode onAddedDirty(CoreContext* context) override;
StatusCode onAddedClean(CoreContext* context) override;
@@ -21,6 +27,14 @@
void addVertex(MeshVertex* vertex);
void decodeTriangleIndexBytes(Span<const uint8_t> value) override;
void copyTriangleIndexBytes(const MeshBase& object) override;
+ void buildDependencies() override;
+ void draw(Renderer* renderer,
+ const RenderImage* image,
+ BlendMode blendMode,
+ float opacity);
+
+ void updateVertexRenderBuffer(Renderer* renderer);
+ void markSkinDirty() override;
#ifdef TESTING
std::vector<MeshVertex*>& vertices() { return m_Vertices; }
rcp<IndexBuffer> indices() { return m_IndexBuffer; }
diff --git a/skia/renderer/src/skia_renderer.cpp b/skia/renderer/src/skia_renderer.cpp
index d1d60ab..96a9c51 100644
--- a/skia/renderer/src/skia_renderer.cpp
+++ b/skia/renderer/src/skia_renderer.cpp
@@ -11,19 +11,16 @@
class SkiaBuffer : public RenderBuffer {
const size_t m_ElemSize;
void* m_Buffer;
+
public:
- SkiaBuffer(const void* src, size_t count, size_t elemSize)
- : RenderBuffer(count)
- , m_ElemSize(elemSize)
- {
+ SkiaBuffer(const void* src, size_t count, size_t elemSize) :
+ RenderBuffer(count), m_ElemSize(elemSize) {
size_t bytes = count * elemSize;
m_Buffer = malloc(bytes);
memcpy(m_Buffer, src, bytes);
}
-
- ~SkiaBuffer() {
- free(m_Buffer);
- }
+
+ ~SkiaBuffer() { free(m_Buffer); }
const float* f32s() const {
assert(m_ElemSize == sizeof(float));
@@ -34,7 +31,7 @@
assert(m_ElemSize == sizeof(uint16_t));
return static_cast<const uint16_t*>(m_Buffer);
}
-
+
const SkPoint* points() const {
return reinterpret_cast<const SkPoint*>(this->f32s());
}
@@ -45,7 +42,8 @@
};
template <typename T> rcp<RenderBuffer> make_buffer(Span<T> span) {
- return rcp<RenderBuffer>(new SkiaBuffer(span.data(), span.size(), sizeof(T)));
+ return rcp<RenderBuffer>(
+ new SkiaBuffer(span.data(), span.size(), sizeof(T)));
}
class SkiaRenderShader : public RenderShader {
@@ -143,20 +141,21 @@
float opacity) {
// need our vertices and uvs to agree
assert(vertices->count() == uvCoords->count());
- // vertices and uvs are arrays of floats, so we need their counts to be even,
- // since we treat them as arrays of points
+ // vertices and uvs are arrays of floats, so we need their counts to be
+ // even, since we treat them as arrays of points
assert((vertices->count() & 1) == 0);
// We do this because our UVs are normalized, but Skia expects them to be
// sized to the shader (i.e. 0..width, 0..height).
// To accomdate this, we effectively scaling the image down to 0..1 to
// match the scale of the UVs.
- const auto scale = SkMatrix::Scale(1.0f/image->width(), 1.0f/image->height());
+ const auto scale =
+ SkMatrix::Scale(1.0f / image->width(), 1.0f / image->height());
auto skiaImage = reinterpret_cast<const SkiaRenderImage*>(image)->skImage();
const SkSamplingOptions sampling(SkFilterMode::kLinear);
- auto shader = skiaImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
- sampling, &scale);
+ auto shader = skiaImage->makeShader(
+ SkTileMode::kClamp, SkTileMode::kClamp, sampling, &scale);
SkPaint paint;
paint.setAlphaf(opacity);
@@ -166,6 +165,7 @@
const SkColor* no_colors = nullptr;
auto vertexMode = SkVertices::kTriangles_VertexMode;
const int vertexCount = vertices->count() >> 1;
+ // clang-format off
auto vt = SkVertices::MakeCopy(vertexMode,
vertexCount,
SkiaBuffer::Cast(vertices.get())->points(),
@@ -173,6 +173,7 @@
no_colors,
indices->count(),
SkiaBuffer::Cast(indices.get())->u16s());
+ // clang-format on
// The blend mode is ignored if we don't have colors && uvs
m_Canvas->drawVertices(vt, SkBlendMode::kModulate, paint);
@@ -180,19 +181,22 @@
bool SkiaRenderImage::decode(Span<const uint8_t> encodedData) {
- sk_sp<SkData> data = SkData::MakeWithoutCopy(encodedData.data(),
- encodedData.size());
+ sk_sp<SkData> data =
+ SkData::MakeWithoutCopy(encodedData.data(), encodedData.size());
m_SkImage = SkImage::MakeFromEncoded(data);
m_Width = m_SkImage->width();
m_Height = m_SkImage->height();
return true;
}
-rcp<RenderShader> SkiaRenderImage::makeShader(RenderTileMode tx, RenderTileMode ty,
+rcp<RenderShader> SkiaRenderImage::makeShader(RenderTileMode tx,
+ RenderTileMode ty,
const Mat2D* localMatrix) const {
- const SkMatrix lm = localMatrix ? ToSkia::convert(*localMatrix) : SkMatrix();
+ const SkMatrix lm =
+ localMatrix ? ToSkia::convert(*localMatrix) : SkMatrix();
const SkSamplingOptions options(SkFilterMode::kLinear);
- auto sh = m_SkImage->makeShader(ToSkia::convert(tx), ToSkia::convert(ty), options, &lm);
+ auto sh = m_SkImage->makeShader(
+ ToSkia::convert(tx), ToSkia::convert(ty), options, &lm);
return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
}
@@ -211,38 +215,57 @@
RenderPaint* makeRenderPaint() { return new SkiaRenderPaint(); }
RenderImage* makeRenderImage() { return new SkiaRenderImage(); }
- rcp<RenderShader> makeLinearGradient(float sx, float sy, float ex, float ey,
- const ColorInt colors[], const float stops[],
- int count, RenderTileMode mode,
- const Mat2D* localm)
- {
- const SkPoint pts[] = { {sx, sy}, {ex, ey} };
+ rcp<RenderShader> makeLinearGradient(float sx,
+ float sy,
+ float ex,
+ float ey,
+ const ColorInt colors[],
+ const float stops[],
+ int count,
+ RenderTileMode mode,
+ const Mat2D* localm) {
+ const SkPoint pts[] = {{sx, sy}, {ex, ey}};
const SkMatrix lm = localm ? ToSkia::convert(*localm) : SkMatrix();
- auto sh = SkGradientShader::MakeLinear(pts, (const SkColor*)colors, stops, count,
- ToSkia::convert(mode), 0, &lm);
+ auto sh = SkGradientShader::MakeLinear(pts,
+ (const SkColor*)colors,
+ stops,
+ count,
+ ToSkia::convert(mode),
+ 0,
+ &lm);
return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
}
- rcp<RenderShader> makeRadialGradient(float cx, float cy, float radius,
- const ColorInt colors[], const float stops[],
- int count, RenderTileMode mode,
- const Mat2D* localm)
- {
+ rcp<RenderShader> makeRadialGradient(float cx,
+ float cy,
+ float radius,
+ const ColorInt colors[],
+ const float stops[],
+ int count,
+ RenderTileMode mode,
+ const Mat2D* localm) {
const SkMatrix lm = localm ? ToSkia::convert(*localm) : SkMatrix();
- auto sh = SkGradientShader::MakeRadial({cx, cy}, radius,
- (const SkColor*)colors, stops, count,
- ToSkia::convert(mode), 0, &lm);
+ auto sh = SkGradientShader::MakeRadial({cx, cy},
+ radius,
+ (const SkColor*)colors,
+ stops,
+ count,
+ ToSkia::convert(mode),
+ 0,
+ &lm);
return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
}
- rcp<RenderShader> makeSweepGradient(float cx, float cy,
- const ColorInt colors[], const float stops[],
- int count, RenderTileMode mode,
- const Mat2D* localm)
- {
- const SkMatrix lm = localm ? ToSkia::convert(*localm) : SkMatrix();
- auto sh = SkGradientShader::MakeSweep(cx, cy, (const SkColor*)colors, stops, count,
- 0, &lm);
- return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
- }
+ rcp<RenderShader> makeSweepGradient(float cx,
+ float cy,
+ const ColorInt colors[],
+ const float stops[],
+ int count,
+ RenderTileMode mode,
+ const Mat2D* localm) {
+ const SkMatrix lm = localm ? ToSkia::convert(*localm) : SkMatrix();
+ auto sh = SkGradientShader::MakeSweep(
+ cx, cy, (const SkColor*)colors, stops, count, 0, &lm);
+ return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
+ }
} // namespace rive
diff --git a/src/bones/skinnable.cpp b/src/bones/skinnable.cpp
index 8dabc6f..781267b 100644
--- a/src/bones/skinnable.cpp
+++ b/src/bones/skinnable.cpp
@@ -1,5 +1,6 @@
#include "rive/bones/skinnable.hpp"
#include "rive/shapes/points_path.hpp"
+#include "rive/shapes/mesh.hpp"
using namespace rive;
@@ -8,6 +9,9 @@
case PointsPath::typeKey:
return component->as<PointsPath>();
break;
+ case Mesh::typeKey:
+ return component->as<Mesh>();
+ break;
}
return nullptr;
}
diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp
index d1a01e3..7684707 100644
--- a/src/shapes/image.cpp
+++ b/src/shapes/image.cpp
@@ -3,6 +3,7 @@
#include "rive/importers/backboard_importer.hpp"
#include "rive/assets/file_asset.hpp"
#include "rive/assets/image_asset.hpp"
+#include "rive/shapes/mesh.hpp"
using namespace rive;
@@ -24,7 +25,11 @@
renderer->transform(worldTransform());
renderer->translate(-width / 2.0f, -height / 2.0f);
- renderer->drawImage(renderImage, blendMode(), renderOpacity());
+ if (m_Mesh != nullptr) {
+ m_Mesh->draw(renderer, renderImage, blendMode(), renderOpacity());
+ } else {
+ renderer->drawImage(renderImage, blendMode(), renderOpacity());
+ }
renderer->restore();
}
diff --git a/src/shapes/mesh.cpp b/src/shapes/mesh.cpp
index 9dbb9b7..8dd26a1 100644
--- a/src/shapes/mesh.cpp
+++ b/src/shapes/mesh.cpp
@@ -1,13 +1,11 @@
#include "rive/shapes/mesh.hpp"
#include "rive/shapes/image.hpp"
+#include "rive/shapes/mesh_vertex.hpp"
#include <limits>
-#include <cassert>
using namespace rive;
-void Mesh::markDrawableDirty() {
- // TODO: add dirty for rebuilding vertex buffer (including deform).
-}
+void Mesh::markDrawableDirty() { m_VertexRenderBuffer = nullptr; }
void Mesh::addVertex(MeshVertex* vertex) { m_Vertices.push_back(vertex); }
@@ -28,6 +26,11 @@
}
StatusCode Mesh::onAddedClean(CoreContext* context) {
+ // Make sure Core found indices in the file for this Mesh.
+ if (m_IndexBuffer == nullptr) {
+ return StatusCode::InvalidObject;
+ }
+
// Check the indices are all in range. We should consider having a better
// error reporting system to the implementor.
for (auto index : *m_IndexBuffer) {
@@ -53,4 +56,50 @@
void Mesh::copyTriangleIndexBytes(const MeshBase& object) {
m_IndexBuffer = object.as<Mesh>()->m_IndexBuffer;
+}
+
+void Mesh::markSkinDirty() {}
+
+void Mesh::buildDependencies() {
+ Super::buildDependencies();
+
+ // TODO: This logic really needs to move to a "intializeGraphics/Renderer"
+ // method that is passed a reference to the Renderer.
+
+ // TODO: if this is an instance, share the index and uv buffer from the
+ // source. Consider storing an m_SourceMesh.
+
+ std::vector<float> uv = std::vector<float>(m_Vertices.size() * 2);
+ std::size_t index = 0;
+
+ for (auto vertex : m_Vertices) {
+ uv[index++] = vertex->u();
+ uv[index++] = vertex->v();
+ }
+ m_UVRenderBuffer = makeBufferF32(uv.data(), uv.size());
+ m_IndexRenderBuffer =
+ makeBufferU16(m_IndexBuffer->data(), m_IndexBuffer->size());
+}
+
+void Mesh::draw(Renderer* renderer,
+ const RenderImage* image,
+ BlendMode blendMode,
+ float opacity) {
+ if (m_VertexRenderBuffer == nullptr) {
+
+ std::vector<float> vertices = std::vector<float>(m_Vertices.size() * 2);
+ std::size_t index = 0;
+ for (auto vertex : m_Vertices) {
+ vertices[index++] = vertex->x();
+ vertices[index++] = vertex->y();
+ }
+ m_VertexRenderBuffer = makeBufferF32(vertices.data(), vertices.size());
+ }
+
+ renderer->drawImageMesh(image,
+ m_VertexRenderBuffer,
+ m_UVRenderBuffer,
+ m_IndexRenderBuffer,
+ blendMode,
+ opacity);
}
\ No newline at end of file
diff --git a/test/no_op_renderer.cpp b/test/no_op_renderer.cpp
index a07b17e..a85f778 100644
--- a/test/no_op_renderer.cpp
+++ b/test/no_op_renderer.cpp
@@ -6,8 +6,10 @@
RenderPath* makeRenderPath() { return new NoOpRenderPath(); }
RenderImage* makeRenderImage() { return new NoOpRenderImage(); }
- rcp<RenderShader> makeLinearGradient(float sx, float sy,
- float ex, float ey,
+ rcp<RenderShader> makeLinearGradient(float sx,
+ float sy,
+ float ex,
+ float ey,
const ColorInt colors[],
const float stops[],
int count,
@@ -16,7 +18,9 @@
return nullptr;
}
- rcp<RenderShader> makeRadialGradient(float cx, float cy, float radius,
+ rcp<RenderShader> makeRadialGradient(float cx,
+ float cy,
+ float radius,
const ColorInt colors[],
const float stops[],
int count,
@@ -25,7 +29,8 @@
return nullptr;
}
- rcp<RenderShader> makeSweepGradient(float cx, float cy,
+ rcp<RenderShader> makeSweepGradient(float cx,
+ float cy,
const ColorInt colors[],
const float stops[],
int count,
@@ -33,4 +38,13 @@
return nullptr;
}
+ rcp<RenderBuffer> makeBufferU16(const uint16_t[], size_t count) {
+ return nullptr;
+ }
+ rcp<RenderBuffer> makeBufferU32(const uint32_t[], size_t count) {
+ return nullptr;
+ }
+ rcp<RenderBuffer> makeBufferF32(const float[], size_t count) {
+ return nullptr;
+ }
} // namespace rive