| #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); | |
| } |