| #include "rive/shapes/mesh.hpp" |
| #include "rive/shapes/image.hpp" |
| #include "rive/shapes/vertex.hpp" |
| #include "rive/shapes/mesh_vertex.hpp" |
| #include "rive/bones/skin.hpp" |
| #include "rive/artboard.hpp" |
| #include "rive/factory.hpp" |
| #include "rive/span.hpp" |
| #include <limits> |
| |
| using namespace rive; |
| |
| /// Called whenever a vertex moves (x/y change). |
| void Mesh::markDrawableDirty() { |
| if (skin() != nullptr) { |
| skin()->addDirt(ComponentDirt::Skin); |
| } |
| addDirt(ComponentDirt::Vertices); |
| } |
| |
| void Mesh::addVertex(MeshVertex* vertex) { m_Vertices.push_back(vertex); } |
| |
| StatusCode Mesh::onAddedDirty(CoreContext* context) { |
| StatusCode result = Super::onAddedDirty(context); |
| if (result != StatusCode::Ok) { |
| return result; |
| } |
| |
| if (!parent()->is<Image>()) { |
| return StatusCode::MissingObject; |
| } |
| |
| // All good, tell the image it has a mesh. |
| parent()->as<Image>()->setMesh(this); |
| |
| return StatusCode::Ok; |
| } |
| |
| 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) { |
| if (index >= m_Vertices.size()) { |
| return StatusCode::InvalidObject; |
| } |
| } |
| return Super::onAddedClean(context); |
| } |
| |
| void Mesh::decodeTriangleIndexBytes(Span<const uint8_t> value) { |
| // decode the triangle index bytes |
| rcp<IndexBuffer> buffer = rcp<IndexBuffer>(new IndexBuffer()); |
| |
| BinaryReader reader(value); |
| while (!reader.reachedEnd()) { |
| uint64_t index = reader.readVarUint64(); |
| assert(index < std::numeric_limits<uint16_t>::max()); |
| buffer->push_back(index); |
| } |
| m_IndexBuffer = buffer; |
| } |
| |
| void Mesh::copyTriangleIndexBytes(const MeshBase& object) { |
| m_IndexBuffer = object.as<Mesh>()->m_IndexBuffer; |
| } |
| |
| /// Called whenever a bone moves that is connected to the skin. |
| void Mesh::markSkinDirty() { addDirt(ComponentDirt::Vertices); } |
| |
| void Mesh::buildDependencies() { |
| Super::buildDependencies(); |
| if (skin() != nullptr) { |
| skin()->addDependent(this); |
| } |
| parent()->addDependent(this); |
| |
| // 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(); |
| } |
| |
| auto factory = artboard()->factory(); |
| m_UVRenderBuffer = factory->makeBufferF32(toSpan(uv)); |
| m_IndexRenderBuffer = factory->makeBufferU16(toSpan(*m_IndexBuffer)); |
| } |
| |
| void Mesh::update(ComponentDirt value) { |
| if (hasDirt(value, ComponentDirt::Vertices)) { |
| if (skin() != nullptr) { |
| skin()->deform({(Vertex**)m_Vertices.data(), m_Vertices.size()}); |
| } |
| m_VertexRenderBuffer = nullptr; |
| } |
| Super::update(value); |
| } |
| |
| void Mesh::draw(Renderer* renderer, const RenderImage* image, BlendMode blendMode, float opacity) { |
| if (m_VertexRenderBuffer == nullptr) { |
| |
| std::vector<float> vertices(m_Vertices.size() * 2); |
| std::size_t index = 0; |
| for (auto vertex : m_Vertices) { |
| auto translation = vertex->renderTranslation(); |
| vertices[index++] = translation[0]; |
| vertices[index++] = translation[1]; |
| } |
| |
| auto factory = artboard()->factory(); |
| m_VertexRenderBuffer = factory->makeBufferF32(toSpan(vertices)); |
| } |
| |
| if (skin() == nullptr) { |
| renderer->transform(parent()->as<WorldTransformComponent>()->worldTransform()); |
| } |
| renderer->drawImageMesh( |
| image, m_VertexRenderBuffer, m_UVRenderBuffer, m_IndexRenderBuffer, blendMode, opacity); |
| } |