| #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 "rive/assets/image_asset.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()) | |
| { | |
| buffer->push_back(reader.readVarUintAs<uint16_t>()); | |
| } | |
| 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); } | |
| Core* Mesh::clone() const | |
| { | |
| auto factory = artboard()->factory(); | |
| auto clone = static_cast<Mesh*>(MeshBase::clone()); | |
| clone->m_VertexRenderBufferDirty = true; | |
| clone->m_VertexRenderBuffer = factory->makeRenderBuffer(RenderBufferType::vertex, | |
| RenderBufferFlags::none, | |
| m_Vertices.size() * sizeof(Vec2D)); | |
| clone->m_UVRenderBuffer = m_UVRenderBuffer; | |
| clone->m_IndexRenderBuffer = m_IndexRenderBuffer; | |
| return clone; | |
| } | |
| void Mesh::initializeSharedBuffers(RenderImage* renderImage) | |
| { | |
| Mat2D uvTransform = renderImage != nullptr ? renderImage->uvTransform() : Mat2D(); | |
| auto factory = artboard()->factory(); | |
| m_VertexRenderBufferDirty = true; | |
| m_VertexRenderBuffer = factory->makeRenderBuffer(RenderBufferType::vertex, | |
| RenderBufferFlags::none, | |
| m_Vertices.size() * sizeof(Vec2D)); | |
| m_UVRenderBuffer = factory->makeRenderBuffer(RenderBufferType::vertex, | |
| RenderBufferFlags::mappedOnceAtInitialization, | |
| m_Vertices.size() * sizeof(Vec2D)); | |
| if (m_UVRenderBuffer) | |
| { | |
| float* uv = static_cast<float*>(m_UVRenderBuffer->map()); | |
| for (auto vertex : m_Vertices) | |
| { | |
| Vec2D xformedUV = uvTransform * Vec2D(vertex->u(), vertex->v()); | |
| *uv++ = xformedUV.x; | |
| *uv++ = xformedUV.y; | |
| } | |
| m_UVRenderBuffer->unmap(); | |
| } | |
| m_IndexRenderBuffer = factory->makeRenderBuffer(RenderBufferType::index, | |
| RenderBufferFlags::mappedOnceAtInitialization, | |
| m_IndexBuffer->size() * sizeof(uint16_t)); | |
| if (m_IndexRenderBuffer) | |
| { | |
| void* indexData = m_IndexRenderBuffer->map(); | |
| memcpy(indexData, m_IndexBuffer->data(), m_IndexRenderBuffer->sizeInBytes()); | |
| m_IndexRenderBuffer->unmap(); | |
| } | |
| } | |
| void Mesh::buildDependencies() | |
| { | |
| Super::buildDependencies(); | |
| if (skin() != nullptr) | |
| { | |
| skin()->addDependent(this); | |
| } | |
| parent()->addDependent(this); | |
| } | |
| void Mesh::update(ComponentDirt value) | |
| { | |
| if (hasDirt(value, ComponentDirt::Vertices)) | |
| { | |
| if (skin() != nullptr) | |
| { | |
| skin()->deform({(Vertex**)m_Vertices.data(), m_Vertices.size()}); | |
| } | |
| m_VertexRenderBufferDirty = true; | |
| } | |
| Super::update(value); | |
| } | |
| void Mesh::draw(Renderer* renderer, const RenderImage* image, BlendMode blendMode, float opacity) | |
| { | |
| if (m_VertexRenderBufferDirty && m_VertexRenderBuffer != nullptr) | |
| { | |
| Vec2D* mappedVertices = reinterpret_cast<Vec2D*>(m_VertexRenderBuffer->map()); | |
| for (auto vertex : m_Vertices) | |
| { | |
| *mappedVertices++ = vertex->renderTranslation(); | |
| } | |
| m_VertexRenderBuffer->unmap(); | |
| m_VertexRenderBufferDirty = false; | |
| } | |
| if (skin() == nullptr) | |
| { | |
| renderer->transform(parent()->as<WorldTransformComponent>()->worldTransform()); | |
| } | |
| renderer->drawImageMesh(image, | |
| m_VertexRenderBuffer, | |
| m_UVRenderBuffer, | |
| m_IndexRenderBuffer, | |
| static_cast<uint32_t>(m_Vertices.size()), | |
| static_cast<uint32_t>(m_IndexBuffer->size()), | |
| blendMode, | |
| opacity); | |
| } |