| /* |
| * 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)}; |
| } |