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