Files

267 lines
7.9 KiB
C++

//
//
#include "AnimationElement.h"
// Our Assert conflicts with the definitions inside libRocket
#pragma push_macro("Assert")
#undef Assert
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <Rocket/Core.h>
#include <Rocket/Core/ElementDocument.h>
#include <Rocket/Core/GeometryUtilities.h>
#include <Rocket/Core/String.h>
#include <scpui/RocketRenderingInterface.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#pragma pop_macro("Assert")
namespace scpui {
namespace elements {
using namespace Rocket;
using namespace Rocket::Core;
// This is basically a copy of ElementImage with a few changes
AnimationElement::AnimationElement(const String& tag_in) : Element(tag_in), dimensions(-1, -1), geometry(this)
{
ResetCoords();
geometry_dirty = false;
texture_dirty = true;
}
AnimationElement::~AnimationElement() = default;
// Sizes the box to the element's inherent size.
bool AnimationElement::GetIntrinsicDimensions(Vector2f& _dimensions)
{
// Check if we need to reload the texture.
if (texture_dirty) {
LoadTexture();
}
// Calculate the x dimension.
if (HasAttribute("width")) {
dimensions.x = GetAttribute<float>("width", -1);
} else if (using_coords) {
dimensions.x = (float)(coords[2] - coords[0]);
} else {
dimensions.x = (float)texture.GetDimensions(GetRenderInterface()).x;
}
// Calculate the y dimension.
if (HasAttribute("height")) {
dimensions.y = GetAttribute<float>("height", -1);
} else if (using_coords) {
dimensions.y = (float)(coords[3] - coords[1]);
} else {
dimensions.y = (float)texture.GetDimensions(GetRenderInterface()).y;
}
// Return the calculated dimensions. If this changes the size of the element, it will result in
// a 'resize' event which is caught below and will regenerate the geometry.
_dimensions = dimensions;
return true;
}
// Renders the element.
void AnimationElement::OnRender()
{
// Regenerate the geometry if required (this will be set if 'coords' changes but does not
// result in a resize).
if (geometry_dirty) {
GenerateGeometry();
}
// Render the geometry beginning at this element's content region.
geometry.Render(GetAbsoluteOffset(Rocket::Core::Box::CONTENT));
}
// Called when attributes on the element are changed.
void AnimationElement::OnAttributeChange(const Rocket::Core::AttributeNameList& changed_attributes)
{
// Call through to the base element's OnAttributeChange().
Rocket::Core::Element::OnAttributeChange(changed_attributes);
float dirty_layout = false;
// Check for a changed 'src' attribute. If this changes, the old texture handle is released,
// forcing a reload when the layout is regenerated.
if (changed_attributes.find("src") != changed_attributes.end()) {
texture_dirty = true;
dirty_layout = true;
// Reset the animation timestamps to make sure the animation starts at the beginning
animation_last_update_time = -1.0f;
}
// Check for a changed 'width' attribute. If this changes, a layout is forced which will
// recalculate the dimensions.
if (changed_attributes.find("width") != changed_attributes.end() ||
changed_attributes.find("height") != changed_attributes.end()) {
dirty_layout = true;
}
// Check for a change to the 'coords' attribute. If this changes, the coordinates are
// recomputed and a layout forced.
if (changed_attributes.find("coords") != changed_attributes.end()) {
if (HasAttribute("coords")) {
StringList coords_list;
StringUtilities::ExpandString(coords_list, GetAttribute<String>("coords", ""));
if (coords_list.size() != 4) {
Rocket::Core::Log::Message(
Log::LT_WARNING,
"Element '%s' has an invalid 'coords' attribute; coords requires 4 values, found " SIZE_T_ARG ".",
GetAddress().CString(), coords_list.size());
ResetCoords();
} else {
for (size_t i = 0; i < 4; ++i) {
coords[i] = atoi(coords_list[i].CString());
}
// Check for the validity of the coordinates.
if (coords[0] < 0 || coords[2] < coords[0] || coords[1] < 0 || coords[3] < coords[1]) {
Rocket::Core::Log::Message(
Log::LT_WARNING,
"Element '%s' has an invalid 'coords' attribute; invalid coordinate values specified.",
GetAddress().CString());
ResetCoords();
} else {
// We have new, valid coordinates; force the geometry to be regenerated.
geometry_dirty = true;
using_coords = true;
}
}
} else {
ResetCoords();
}
// Coordinates have changes; this will most likely result in a size change, so we need to force a layout.
dirty_layout = true;
}
if (dirty_layout) {
DirtyLayout();
}
}
// Regenerates the element's geometry.
void AnimationElement::ProcessEvent(Rocket::Core::Event& event)
{
Element::ProcessEvent(event);
if (event.GetTargetElement() == this && event == "resize") {
GenerateGeometry();
}
}
void AnimationElement::GenerateGeometry()
{
// Release the old geometry before specifying the new vertices.
geometry.Release(true);
std::vector<Rocket::Core::Vertex>& vertices = geometry.GetVertices();
std::vector<int>& indices = geometry.GetIndices();
vertices.resize(4);
indices.resize(6);
// Generate the texture coordinates.
Vector2f texcoords[2];
if (using_coords) {
Vector2f texture_dimensions((float)texture.GetDimensions(GetRenderInterface()).x,
(float)texture.GetDimensions(GetRenderInterface()).y);
if (texture_dimensions.x == 0) {
texture_dimensions.x = 1;
}
if (texture_dimensions.y == 0) {
texture_dimensions.y = 1;
}
texcoords[0].x = (float)coords[0] / texture_dimensions.x;
texcoords[0].y = (float)coords[1] / texture_dimensions.y;
texcoords[1].x = (float)coords[2] / texture_dimensions.x;
texcoords[1].y = (float)coords[3] / texture_dimensions.y;
} else {
texcoords[0] = Vector2f(0, 0);
texcoords[1] = Vector2f(1, 1);
}
Rocket::Core::GeometryUtilities::GenerateQuad(&vertices[0], // vertices to write to
&indices[0], // indices to write to
Vector2f(0, 0), // origin of the quad
GetBox().GetSize(Rocket::Core::Box::CONTENT), // size of the quad
Colourb(255, 255, 255, 255), // colour of the vertices
texcoords[0], // top-left texture coordinate
texcoords[1]); // top-right texture coordinate
geometry_dirty = false;
}
bool AnimationElement::LoadTexture()
{
texture_dirty = false;
// Get the source URL for the image.
String image_source = GetAttribute<String>("src", "");
if (image_source.Empty()) {
return false;
}
geometry_dirty = true;
Rocket::Core::ElementDocument* document = GetOwnerDocument();
URL source_url(document == nullptr ? "" : document->GetSourceURL());
if (!texture.Load(image_source, source_url.GetPath())) {
geometry.SetTexture(nullptr);
return false;
}
// Set the texture onto our geometry object.
geometry.SetTexture(&texture);
return true;
}
void AnimationElement::ResetCoords()
{
using_coords = false;
for (int & coord : coords) {
coord = -1;
}
}
void AnimationElement::OnUpdate()
{
Element::OnUpdate();
if (texture.GetHandle(GetRenderInterface()) == 0) {
// Make sure that the handle is valid
return;
}
if (animation_last_update_time > 0.0f) {
const auto animation_frame_time =
Rocket::Core::GetSystemInterface()->GetElapsedTime() - animation_last_update_time;
// Set the advance time for the render call so that the generic anim inside the handle gets rendered correctly
scpui::RocketRenderingInterface::advanceAnimation(texture.GetHandle(GetRenderInterface()),
animation_frame_time);
}
animation_last_update_time = Rocket::Core::GetSystemInterface()->GetElapsedTime();
}
} // namespace elements
} // namespace scpui