The views made it look like one cannot use Trade::ImageData etc. as
inputs, and the reason for using them was because there was a
load-bearing const in the view type that allowed slice() to work. The
slice() API is fixed now so this doesn't need to be silly anymore.
Now it's less code, and it also no longer results in random edge
artifacts because the padding area wasn't correctly uploaded. I'm going
to do the same change in the FreeTypeFont and StbTrueTypeFont next.
Because otherwise the users likely have to do something similar on their
end to perform a texture upload etc., which means they'll either take
the easy path and upload everything including the unused area, or they
introduce various bugs in the process, leading to random artifacts,
especially when it comes to padding.
Which is exactly what I think is causing random test failures in the Ui
library text rendering, because the glyph cache filling process in
plugins is calculating the rectangle too tight, without considering
padding. Gonna fix that now.
Back in 2020 when I wrote this I didn't really expect the MeshData to be
directly used for much more than putting them on a GPU, mostly because
that used to be the primary use case with the old MeshData2D /
MeshData3D. So the documentation was focusing mainly on populating a GPU
mesh, and any docs for CPU-side access were added rather hastily.
As now the asset processing use case is much larger, the original docs no
longer made sense. Let's hope this is better.
Not too great yet, but at least the most common operations have an
example snippet that shows real use, instead of jumping off a cliff
right into the most detailed description.
I attempted to make it private only to discover it was used by Magnum
Player to make the workflow with opt-in tweakable constants more
efficient. So let's document that.
Because this overrides the base pointer*Event() implementations, it
additionally has to call into the parent implementation in order to fall
back to the deprecated mouse event if the new pointer event isn't
handled.
Pointer events are an unified abstraction over mouse, touch, pen and
potential other yet-to-be-invented pointer-like input methods. Their goal
is to expose all such input methods under a single interface so the
application side doesn't need to explicitly make sure that it's
touch-aware or pen-aware. This abstraction is already present in HTML5,
in Qt6 and in WINAPI as well, and is also what I adopted for the new UI
library because it *just makes sense*.
Unfortunately not even SDL3 took the opportunity to introduce that and
instead added a *third* separate event type for pen input in SDL3. At
first I thought that I wouldn't introduce any extra abstractions in the
Application classes (because that's what they are designed to be, as
lightweight as possible), but midway through introducing TouchEvent
classes and fighting SDL's touch->mouse and mouse->touch compatibility
translation (yes, it's both ways, depending on the platform) I realized
that a much simpler solution that doesn't require any event translation
or the users duplicating their event handling logic for several possible
input types is to introduce a single new event type that covers all.
Which is what this commit does -- it doesn't introduce anything
touch-related so far, just creates a new PointerEvent and
PointerMoveEvent class and corresponding virtual functions. Additionally,
I took this as an opportunity to make the position floating-point, since
that's what SDL3 does now as well, and GLFW did so since ever.
Plus, the Pointer and Pointers enums are directly on the Sdl2Application
class, to allow me to *finally* introduce pointer state queries. Which
weren't possible until now, because there were mutually incompatible
MouseEvent::Button and MouseMoveEvent::Button enums and putting them on
the base class would mean one would have to be translated and the other
not. With Pointer it's translated always, because there isn't any similar
enumeration in SDL that would cover mouse, touch and pen at the same
time.
Like with TextureTools::DistanceField, to make room for non-GL
implementations. Old names are deprecated aliases, same for headers. The
only remaining bit in the Text library is the Renderer class, but for
that one I first need to invent a non-shitty API so I can just deprecate
the old thing as a whole and create something reasonable from scratch in
RendererGL.
The internal GL texture format (especially the R8 vs Luminance mess on
ES2) is now considered an implementation detail and shouldn't affect
common use in any way.
The format is now required always, in order to prepare for use cases
where colored glyphs are a thing as well. Additionally, to match the
recent change in AbstractGlyphCache, the processed format is specified
separately, allowing the input and processed formats to be decoupled.
Which ultimately fixes the regression on ES2 and WebGL 1 where it was no
longer possible to call font.fillyGlyphCache() on a
DistanceFieldGlyphCache.
Also, as there's now a generic format on input, another ES2-specific
issue is now fixed as well, in particular a case where a GL error
would be emitted on drivers with EXT_texture_storage because an unsized
format is passed to setStorage(). This was a problem since a long time
ago, but I ignored it because it didn't affect WebGL 1 and all drivers
that exposed EXT_texture_storage exposed EXT_texture_rg, effectively
circumventing this issue. Or so I think, at least.
The constructors taking either a GL::TextureFormat or no format at all
are now deprecated aliases to the new functionality.
Essential for text selection and editing in cases where mapping from the
input text to the actual shaped glyphs is nontrivial. I.e., in case of
HarfBuzzFont; both FreeTypeFont and StbTrueTypeFont perform a 1:1
translation from input (UTF-8) characters to glyphs so there it isn't
as important.
This is going to get called from fillGlyphCache() that takes a string,
and thus should be better than one virtual call per character. The
single-character variant still stays, as it's a nice convenience API.
Eventually glyphSize() will get a similar treatment as well.
Like the Deg / Rad classes, these are for strongly-typed representation
of time. Because the current way, either with untyped and imprecise
Float, or the insanely-hard-to-use and bloated std::chrono::nanoseconds,
was just too crappy.
This is just the types alone, corresponding typedefs in the root
namespace, and conversion from std::chrono. Using these in the Animation
library, in Timeline, in DebugTools::FrameProfiler, GL::TimeQuery etc.,
will eventually and gradually follow.
To allow people to cherry-pick just a subset of them if other code
defines literals that may conflict. I first did that the same way as
STL (so both namespaces inline), only to subsequently discover the
horror that all literals are implicitly available in the enclosing
Math namespace, thus preventing no conflicts at all. So the Literals
namespace isn't getting inline, only the inner ones.
This is also in preparation for introduction of
Literals::ConstexprColorLiterals that would provide a constexpr variant
of the _srgbf literals at the expense of having a large LUT in a header
file.
To be used with the recently added MaterialTools::removeDuplicates() for
example, or internally by other upcoming scene tools such as importer
filtering.
Especially given that nullptr causes an assert. All call sites basically
ended up passing a &font and all that extra annoyance just doesn't make
sense.
Given this API is still relatively recent, I'm not bothering with
backwards compatibility.
In basically all cases it's two independent operations so forcing them
to be done together doesn't really bring any potential efficiency
advantages. On the other hand, splitting them allows allows the caller
to better make use of available memory, as the new
renderGlyphQuadsInto() allows the input and output arrays to be aliased.
Bumping AbstractFont plugin interface versions as this is a breaking
change.
Replaces the previous, grossly inefficient AbstractLayouter which was
performing one virtual call per glyph (!). It's now also reusable,
meaning it doesn't need to be allocated anew for every new shaped text,
and it no longer requires each and every font plugin to implement the
same redundant glyph data fetching from the glyph cache, scaling etc. --
all that is meant to be done by the users of AbstractShaper, i.e.
Renderer. The independency on a glyph cache theorerically also means it
can be used for a completely different, non-texture-based way to render
text (such as direct path drawing directly on the GPU), although I won't
be exploring that path now.
It also exposes an interface for specifying script, language,
direction and typographic features. Such interface will be currently
only implemented in HarfBuzz, but that's the intent -- to provide a
flexible enough interface to support all possible use cases that a font
or a font plugin may support, instead of exposing a least common
denominator and then having no easy way to shape a text in a non-Latin
script or use a fancy OpenType feature the chosen font has.
The old public interface is preserved for backwards compatibility,
marked as deprecated, however the virtual APIs are not, as supporting
that would be too nasty. I don't think any user code ever implemented a
font plugin so this should be okay.
To ensure smooth transition with no regressions, the Renderer class and
MagnumFont tests still use the old API in this commit, and their test
pass the same way as they did before (except for two removed MagnumFont
test cases which tested errors that are now an assertion in the
deprecated layout() API and thus cannot be tested from the plugin
anymore). Porting them away from the deprecated API will be done in
separate commits.
The class now supports incremental filling, multiple fonts, texture
arrays, removes all reliance on STL containers and is finally properly
documented.
To avoid complete breakage of every use, as much as possible was kept as
deprecated APIs -- in particular the reserve() with the nasty
std::vectors, the insert() that assumes a 2D cache and a single font
and textureSize() that returns a 2D vector. Those behave the same as
before, but will assert if the cache is an array or contains more than
one font.
On the other hand, begin() / end() access with std::unordered_map iterators
(ew!) was removed as the internals simply aren't a hashmap anymore. The
image() that returned an Image2D is now used to fill the glyph cache
instead of querying its potentially processed contents, and returns a
MutableImageView3D. I considered keeping it and adding sourceImage()
instead, but such naming turned out to be too inconsistent. For querying
processed image data (such as with the distance field cache) there's a
new processedImage() query, guarded by new GlyphCacheFeature bits -- if
both ImageProcessing and ProcessedImageDownload is set, it can be used
to retrieve the processed image (so, similar as ImageDownload was
before), and if neither is set, the cache contents are queryable
directly through image(), without needing any special support from
the GPU API.
Existing code is updated only in the minimal way possible to ensure that
no serious breakage was introduced by reimplementing the deprecated APIs
on top of the new backend. Porting away from deprecated APIs will be
done in next commits. The GlyphCache and DistanceFieldGlyphCache have
their public API kept intact for now, as a similar rework will be needed
for them as well.
Additionally, the MagnumFont and MagnumFontConverter plugins aren't
compiling yet as they require substantial changes to deal with the new
glyph cache features. That is not the case with other plugins in the
magnum-plugins repository tho, for those the backwards compatibility
"just works". On the other hand, since layout of the AbstractGlyphChange
changed, I'm bumping the AbstractFont plugin interface version to
force-trigger a rebuild of dependent projects. Because I ran a stale
magnum-player binary, it worked without crashing or GL errors but just
didn't show ANY text whatsoever due to ABI differences, and I wasted
some precious minutes before realizing that a simple rebuild would fix
it.
The overhead of maintaining two classes with only very slight
differences in the API and the internals being basically identical is
not worth it. Too much potential for inconsistencies and doc errors.
Additionally, when I attempted to use it for the reworked Text glyph
cache, I realized I'd need to wrap them both under a common interface,
allowing easy use for both 2D and 2D array textures. And then it's
easier to just have the Atlas class done that way directly instead of
papering over that in a downstream API.
Instead of piling up a mountain if the other end is a ditch. Results in
better packing in most cases, but in one doesn't, so also adding an
option to disable this.
The docs image is now slightly more leveled, one pixel lower.
Just the dumbest possible idea I had, and it compares surprisingly well
in both efficiency (~comparable to stb_rect_pack) and speed
(significantly faster than stb_rect_pack with tons of tiny images,
slower with larger ones -- would probably need to SIMD Math::max() and
such, haha). It's the very first implementation without any additional
improvements I have in mind, so it'll likely improve further.
Includes a benchmark with a bunch of "datasets" extracted from both
fonts and large glTF models. The stb_rect_pack file isn't commited as
it's not useful apart from this single benchmark, put it to
AtlasTestFiles/ and recompile.
All std::string arguments are now a StringView, what returned a
std::pair is now a Pair. STL compatibility headers are included on
deprecated builds to ease porting, as usual.
The only *really* breaking changes are in the internals, where an
ArrayView<const char32_t> is used instead of std::u32string, which is in
line with the change done in Utility::Unicode::utf32(); and a Triple is
returned instead of a std::tuple. Behaviorally nothing changed except
that fillGlyphCache() now asserts if the input string contains invalid
UTF-8 (which is also in line with the cahnge done in Utility::Unicode).