blob: 05327091adf9cfe2bd998bc9809999dd6b48d1d9 [file] [log] [blame] [edit]
/*
* Copyright 2022 Rive
*/
#include "bicubic.hpp"
#include <vector>
template <typename T> T lerp(const T& a, const T& b, float t)
{
return a * (1 - t) + b * t;
}
using namespace rivegm;
rive::Vec2D BicubicPatch::eval(float u, float v) const
{
float weights[16];
this->evalWeights(u, v, weights);
rive::Vec2D result{0, 0};
for (int i = 0; i < 16; ++i)
{
result += m_Pts[i] * weights[i];
}
return result;
}
void BicubicPatch::evalWeights(float u, float v, float weights[16]) const
{
auto compute_bezier_coeff = [](float c[4], float t) {
float s = 1 - t;
c[0] = s * s * s;
c[1] = 3 * t * s * s;
c[2] = 3 * t * t * s;
c[3] = t * t * t;
};
float cu[4], cv[4];
compute_bezier_coeff(cu, u);
compute_bezier_coeff(cv, v);
int i = 0;
for (int y = 0; y < 4; ++y)
{
for (int x = 0; x < 4; ++x)
{
weights[i++] = cu[x] * cv[y];
}
}
}
BicubicPatch::Rec BicubicPatch::buffers(const rive::AABB* uvBounds) const
{
constexpr int N = 16;
constexpr int QUADS = N * N;
constexpr int VERTS = (N + 1) * (N + 1);
constexpr int TRIS = QUADS * 2;
constexpr int INDXS = TRIS * 3;
const rive::AABB unit = {0, 0, 1, 1};
if (!uvBounds)
{
uvBounds = &unit;
}
Rec rec;
rec.pts.resize(VERTS);
rec.uvs.resize(VERTS);
auto pos = rec.pts.data();
auto tex = rec.uvs.data();
const float normalize = 1.0f / N;
for (int y = 0; y <= N; ++y)
{
const float v = y * normalize;
const float tex_v = lerp(uvBounds->top(), uvBounds->bottom(), v);
for (int x = 0; x <= N; ++x)
{
const float u = x * normalize;
*pos++ = this->eval(u, v);
*tex++ = {
lerp(uvBounds->left(), uvBounds->right(), u),
tex_v,
};
}
}
assert(pos - rec.pts.data() == VERTS);
assert(tex - rec.uvs.data() == VERTS);
rec.indices.resize(INDXS);
uint16_t* ndx = rec.indices.data();
constexpr int W = N + 1;
int index = 0;
for (int y = 0; y < N; ++y)
{
for (int x = 0; x < N; ++x)
{
ndx[0] = index;
ndx[1] = index + 1;
ndx[2] = index + 1 + W;
ndx[3] = index;
ndx[4] = index + 1 + W;
ndx[5] = index + W;
ndx += 6;
index += 1;
}
index += 1; // skip the last column
}
assert(ndx - rec.indices.data() == INDXS);
for (int i = 0; i < INDXS; ++i)
{
assert(rec.indices[i] < VERTS);
}
return rec;
}
BicubicPatch::Mesh BicubicPatch::mesh(rive::Factory* factory,
const rive::AABB* uvBounds) const
{
auto rec = BicubicPatch::buffers(uvBounds);
auto ptsBuffer =
factory->makeRenderBuffer(rive::RenderBufferType::vertex,
rive::RenderBufferFlags::none,
rec.pts.size() * sizeof(rive::Vec2D));
if (ptsBuffer)
{
void* ptsData = ptsBuffer->map();
memcpy(ptsData, rec.pts.data(), ptsBuffer->sizeInBytes());
ptsBuffer->unmap();
}
auto uvsBuffer = factory->makeRenderBuffer(
rive::RenderBufferType::vertex,
rive::RenderBufferFlags::mappedOnceAtInitialization,
rec.uvs.size() * sizeof(rive::Vec2D));
if (uvsBuffer)
{
void* uvsData = uvsBuffer->map();
memcpy(uvsData, rec.uvs.data(), uvsBuffer->sizeInBytes());
uvsBuffer->unmap();
}
auto idxBuffer = factory->makeRenderBuffer(
rive::RenderBufferType::index,
rive::RenderBufferFlags::mappedOnceAtInitialization,
rec.indices.size() * sizeof(uint16_t));
if (idxBuffer)
{
void* idxData = idxBuffer->map();
memcpy(idxData, rec.indices.data(), idxBuffer->sizeInBytes());
idxBuffer->unmap();
}
return {std::move(ptsBuffer), std::move(uvsBuffer), std::move(idxBuffer)};
}