Again, similarly to what's done for custom MeshAttribute and SceneField
values already. I'm bumping the importer interface version as adding new
virtual functions is a silent ABI breakage, but it's good to do in any
case as the AnimationTrackTarget enum was extended to 16 bits and the
values got shifted.
The `Type` was suggesting it'd be some C++ type, definitely not values
like Scaling3D or Translation2D, resulting in a significant "brain
autocompletion error" every time I was using that type.
Unfortunately on AnimationData the trackTargetType() couldn't similarly
get renamed to trackTarget() as there's already trackTarget() that
contains the node ID the target points to, so it's trackTargetName()
instead. Renaming trackTarget() to trackTargetId() wasn't an option as
that would be inconsistent with everything else (TextureTools::image(),
MaterialAttribute::BaseColorTexture, SceneField::Mesh are all IDs but
they don't have an `Id` suffix); renaming to AnimationTrackTargetName
would keep it insanely long and wouldn't make it consistent either
(MeshAttribute, SceneFIeld, MaterialAttribute are all referred to as
"names" yet they don't have a `Name` suffix).
Such an unnecessary footgun -- I was already checking the other case,
having attribute data too short, but this I thought is fine because it's
not leading to any crash. Well it's leading to needless pain and
suffering, that's what it is doing!
And of course already found FIVE such bugs in just Magnum tests alone.
With the intention that those will eventually contain also things like
YUp / YDown, PremultipliedAlpha and such.
This commit is mostly just busywork, wiring this into [Compressed]Image,
[Compressed]ImageView and Trade::ImageData and ensuring the flags get
correctly propagated during moves and conversions. Unfortunately in case
of Trade::ImageData it meant deprecating the current set of constructor
in order to insert an ImageFlags parameter before the importer state
pointer.
The only non-trivial piece of logic is when a 2D
[Compressed]ImageView gets converted to a 3D one, then the Array bit is
implicitly dropped, as 2D arrays of 1D images are not really a thing.
Instead, it's now possible to add new flags when doing the conversion --
for example to turn a 2D image to a (single-layer) 2D array image.
Until now, these were only transitively tested in concrete plugin
implementations, meaning that if the base implementation would have an
error (such as accessing a null optional), it would only get discovered
when building a plugin, worst case a plugin in a completely different
repo.
Except for file callbacks, for these I have another change planned for
zero-copy import and it would be unwise to break stuff twice, providing
two sets of backwards compatibility wrappers. The image / scene
converter plugins went through a similar change earlier already and the
shader converters were made sane since the very beginning. OTOH audio
importers and text stuff are scheduled for merging with Trade or a
larger rework anyway, so I didn't see any point in updating those.
It's mostly a trivial change, except that returned String instances are
now also checked for non-default deleters same as Arrays because yes,
wow such flexibility compared to STL strings. Same was done for
ShaderTools::AbstractConverter already anyway, so nothing unheard-of
either.
The importer plugin interface version is bumped as this likely breaks
ABI in a nasty way that would lead to crashes.
For file opening there's no longer an unatomic pair of exists() +
read(), but since Path::read() now returns an Optional, it means we can
reliably distinguish between empty files and failures.
While at it, also added TODOs for removal of the StringStl.h header
that's needed in various places for compatibility with APIs still using
STL strings.
It was a clever harmless trick. Well, it was way more harmless than it
was clever, but even then it caused UBSan to complain. And that's Not A
Good Thing for various reasons, so let's just comply.
The main bad effect of this change is a *slightly* larger list of
exported symbols but until we actually get rid of the major bloats like
<iostream>, <string> and the like, this is not going to have any
measurable impact.
Now it's a field and its corresponding object mapping, instead of
field and "objects":
- Goes better with the concept that there's not really any materialized
"object" anywhere, just fields mapped to them.
- No more weird singular/plural difference between field() and
objects(), it's field() and mapping() now.
- The objectCount() that actually wasn't really object count is now a
mappingBound(), an upper bound for object IDs contained in the object
mapping views. Which is quite self-explanatory without having to
mention every time that the range may be sparse.
This got originally added as some sort of a kludge to make it easy to go
to the parent transformation, assuming Parent and Transformation share
the same object mapping:
parentTransformation = transformations[parents[i]]
But after some ACTUAL REAL WORLD use, I realized that there's often a
set of objects that have a Parent defined, and then another, completely
disjoint, set of objects that have a transformation (for example certain
nodes having no transformation at all because it's an identity). And so
this parent indirection is not only useless, but in fact an additional
complication. Let's say we make a map of the transformations, where
transformationMap[i] is a transformation for object i:
transformationMap = {}
for j in range(len(transformations)):
transformationMap[transformationObject[j]] = transformation[j]
Then, with *no* assumptions about shared object mapping, the indirection
would cause parent transformation retrieval to look like this:
parentTransformation = transformationMap[parentObjects[parents[i]]
While *without* the indirection, it'd be just
parentTransformation = transformationMap[parents[i]]
Honestly I don't care much, this is just that the original
PrimitiveImporter tests started to fail because they expected
object3DForName() to return -1 for a 2D name, but that's no longer the
case. It would be possible to fix this, but I doubt anyone ever relied
on such behavior for 2D scenes, so just add a test that acknowledges
this new behavior.
As the comment says -- before the user code expected that if the scene
hierarchy is broken, particular objects will fail to import. Now the
whole scene fails to import, so we don't even get to know the actual
(expanded) deprecated 2D/3D object count. To reduce the suffering,
return at least the dimension-less object count there. It won't include
the duplicates from the single-function-object conversion but better
than reporting 0.
Same as with MeshData2D/3D, the original ObjectData API and plugin
interfaces are preserved to keep existing code as well as existing
importer implementations working. As Magnum's own importers will get
updated to the new SceneData workflow, a backward compatibility layer
provided that translates it to the subset that the legacy ObjectData
understands.
With this commit, both existing plugin code can build (and test against)
the new workflow, and any ports to the new workflow can test against the
legacy interfaces. Except that for now the compatibility layer doesn't
deal with objects that have more than one mesh or for example a light
and a camera attached, this will be done in a separate step.
For example if we're looking for a 2D image named "cubemap", the
importer will tell us there's 0 2D images, making a potential mistake
(2D vs 3D) more obvious.
Using openMemory() instead of openData() allows the implementation to
assume the data will stay in scope for as long as needed, which can
prevent unnecessary copies in some plugin implementations.
It warranted a new flag, DataFlag::ExternallyOwned, to describe this
kind of memory. I couldn't reuse Owned as that's used for allocations
owned by the instance, which is too little for certain future use cases.
For example returning *Data instances referencing an Owned memory would
mean the user has to assume the memory is gone when the importer
instance is gone, and that's generally not true for memory passed to
openMemory().
Originally I thought I would do this later, but then realized the
existing plugin implementations would need to get all updated again to
be aware of the new flag, with some being forgotten, and it's just
easier to do the whole thing in a single step.
This allows to better describe memory ownership and transfer it instead
of forcing the plugins to allocate their own local copy if the import
happens in-place on the imported data. Right now that's mainly for the
openFile() use case, which implicitly allocated an Array with file
contents only to pass it to openData() which then made a copy because it
could not make any assumption about data scope.
In other words, certain plugins (TgaImporter, KtxImporter, DdsImporter,
CgltfImporter and possibly others) will now have their peak memory usage
*halved*.
There will be Flag::FlipY for images at some point, enabled by default
for compatibility with existing GL code, and so it makes sense to start
discouraging setFlags() as early as possible to avoid people resetting
the default by accident.
Also update the imageconverter, sceneconverter and shaderconverter utils
to use these instead of setFlags().
Makes more sense as the function isn't expected to fail (and thus any
kind of lazy population is not possible as it would be too late for
error checks anyway).
*Not* updating interface strings even though this is an ABI break
because we're doing that right after the skin import interface bump.
The plugin interface version got bumped to avoid ABI issues when loading
plugins that weren't updated for the change, but apart from that this
shouldn't be a breaking change, as the API returns a type that can be
both an Optional and a Pointer.
Until now, except for an attribute-less index-less mesh, the vertex
count was only implicitly taken from passed attributes, but it was
severely limiting:
- There was no way to set vertex count for an attribute-less
indexed mesh, which didn't make sense
- All code that made non-owning MeshData instances referencing another
MeshData had to explicitly handle the attribute-less corner case to
avoid vertex count getting lost
- Offset-only attributes couldn't be used to specify static layout of
meshes with dynamic vertex count, causing unnecessary extra
allocations especially in the Primitives library.
Similar to image mip level import, but this is largely left to be
importer-specific. For example PLY defines per-face data and sometimes
one might want to import them as-is, without them being turned into a
per-vertex property.