Using hasField() + fieldId() was a bad usage pattern leading to a double
linear lookup, so there's now findFieldId() returning an Optional which
covers both. Similarly for finding an object offset in an field, there's
a findFieldObjectOffset() returning an Optional, fieldObjectOffset()
asserting if an object is not found (for convenience to avoid explicit
error handling on user side) and hasFieldObject().
The internal helpers were also renamed and the offset argument moved to
be last for consistency.
To become the central piece of an upcoming SceneTools library, now I
need it just to implement duplication of objects that have more than one
mesh/light/camera/... assignment.
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.
Support utilities needed for SceneTools that will operate on arbitrary
SceneData (adding/removing fields, objects...) without having to know
what each field means and how it needs to be treated.
Honestly the same would make sense for the VertexFormat enum as well.
But not now, later.
This moves the transformation type consistency checks directly into the
constructor and makes the discovered dimension count available through a
getter. As a result, a lot of duplicate assertions (and
corresponding tests) could be removed while tightening the behavior --
before it was possible to have a 2D transformation matrix complemented
with a 3D rotation quaternion and all asserts would pass, yet the user
would be required to call transformations2DAsArray() *but*
translationsRotationsScalings3DAsArray().
Since scenes can exist without any transformation field, there's both
is2D() and is3D() and they both return false in that case.
For a lack of better API right now, this will be used to check if an
object belongs to a certain scene. In the future, when BitArray is a
thing, there will be a dedicated field with a mask telling which objects
belong to the scene and which not, with a fallback that goes through all
field object mappings and collects objects that have at least one field.
Useful for cases when it's desirable to extract just some elements, for
example just for a particular object ID. Or when allocating an
arbitrarily large array isn't wanted for perf/memory reasons.
These need to query field size for allocating the output array which
means looking up the field by name, but the same lookup was then done
again in the fooInto() implementation.
Reason is that Assimp custom material attribute names are also prefixed
with $ and other weird characters, which could lead to them appearing
before $LayerName, causing a layer to falsely appear unnamed. A space,
instead, is before all printable characters so it's guaranteed to be
always first.
Some things you just don't realize at first. Fortunately the binary
layout isn't pinned yet for the serialization format so this change is
mostly fine.
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*.
Hah, so many overloads. Not providing mutable access to keys or layer
offsets as that would break the invariant of the internal array always
being sorted.
Because otherwise we don't properly test all cases. Case in point --
attributeData(UnsignedInt) wasn't correctly propagating the array size,
causing the new tests to fail. Fix in the next commit.
The multi-level APIs still don't check anything regarding level sizes
and order since for example *.ico has no restrictions at all. But the
rest (like non-zero size) is a restriction for all file formats I'm
aware of.
TgaImageConverter test had to be adapted, I expect a lot of breakages in
plugins tests as well. But user code should be fine I think. Also
reduced the rather excessive dimensions in AbstractImageConverterTest,
since the allocation requirement now made the default Emscripten heap
OOM.
To avoid combinatorial explosion of functions that have to be
implemented on the plugin side, the single-image variants proxy to the
multi-level variants, if available.
THe plugin interface string had to be bumped because otherwise there was
a *really nasty* silent ABI break where instead of doSetFlags() it was
suddenly calling doConvert().
The "funniest" of all is that those get printed in a totally random
fashion. Some in Debug, some in Release, some not at all (like, half of
the MaterialDataTest doesn'ŧ initialize `state` and yet it doesn't
complain). Fuck these half-assed features that achieve nothing except
bullying people WHO KNOW WHAT THEY ARE DOING FFS.
Because::This::Is::Colon::Cancer. Also rename Cube to CubeMap and get
rid of the comment that said cube map images are six consecutive 2D
images. Now it's one 3D image instead because that makes more sense, in
the very rare case we'd need to have six different images again we could
probably add a CubeFace value or some such.