155 lines
6.2 KiB
C++
155 lines
6.2 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// scene/delta en FlatBuffers (contrat contract/, ADR 0013, lot B) : aller-retour exact,
|
|
// valeurs refusées par les contrôles générés.
|
|
#include "sequa/3d/wire_contract.hpp"
|
|
|
|
#include <doctest/doctest.h>
|
|
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <span>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace sequa::three_d;
|
|
using namespace sequa::three_d::wire;
|
|
namespace cr = sequa::core_render;
|
|
namespace common = sequa::common;
|
|
|
|
namespace {
|
|
|
|
std::span<const std::uint8_t> bytes_of(const std::string& frame)
|
|
{
|
|
return {reinterpret_cast<const std::uint8_t*>(frame.data()), frame.size()};
|
|
}
|
|
|
|
std::string frame_bytes(const flatbuffers::FlatBufferBuilder& builder)
|
|
{
|
|
return {reinterpret_cast<const char*>(builder.GetBufferPointer()), builder.GetSize()};
|
|
}
|
|
|
|
SceneDeltaMessage every_command()
|
|
{
|
|
Transform transform;
|
|
transform.position = {1.5f, -2.25f, 1e-7f};
|
|
transform.rotation = {0, 0.70710677f, 0, 0.70710677f};
|
|
transform.scale = {2, 1, 0.5f};
|
|
Visual visual;
|
|
visual.primitive = Primitive::capsule;
|
|
visual.size = {0.5f, 1.8f, 0.5f};
|
|
visual.color = {0.1f, 0.2f, 0.3f, 0.4f};
|
|
visual.semantic_class = 0xFFFFFFFFU;
|
|
Camera camera;
|
|
camera.transform = transform;
|
|
camera.vertical_fov_deg = 55;
|
|
camera.near_plane = 0.1f;
|
|
camera.far_plane = 500;
|
|
|
|
SceneDeltaMessage message;
|
|
message.delta.world_version = (1ULL << 53) + 7; // au-delà de ce qu'un double JS garde exactement
|
|
message.tick = 0xFFFFFFFFFFFFFFFFULL;
|
|
message.full = true;
|
|
message.sent_at_us = 1'000'000'123ULL;
|
|
message.delta.commands = {CreateObject{0x10, transform, visual}, SetTransform{0x10, transform},
|
|
SetVisual{0x10, visual}, SetCamera{camera},
|
|
DestroyObject{0xFFFFFFFFFFFFFFFFULL}};
|
|
return message;
|
|
}
|
|
|
|
// Trame scene/delta d'une seule commande SetTransform, écrite à la main.
|
|
std::string set_transform_frame(std::uint64_t id, float x)
|
|
{
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
const common::Transform transform(common::Vec3(x, 0, 0), common::Quat(0, 0, 0, 1), common::Vec3(1, 1, 1));
|
|
const std::vector<cr::SceneCommand> types{cr::SceneCommand::SetTransform};
|
|
const std::vector<flatbuffers::Offset<void>> tables{cr::CreateSetTransform(builder, id, &transform).Union()};
|
|
const auto types_vector = builder.CreateVector(types);
|
|
const auto tables_vector = builder.CreateVector(tables);
|
|
cr::contract::finish_scene_delta(builder, 0,
|
|
cr::CreateSceneDelta(builder, 1, 1, false, 1, types_vector, tables_vector));
|
|
return frame_bytes(builder);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("scene/delta FlatBuffers : aller-retour exact de toutes les commandes")
|
|
{
|
|
const SceneDeltaMessage message = every_command();
|
|
const std::string frame = encode_scene_delta_frame(message);
|
|
std::string error;
|
|
const auto back = decode_scene_delta_frame(bytes_of(frame), &error);
|
|
REQUIRE_MESSAGE(back.has_value(), error);
|
|
CHECK(back->delta == message.delta);
|
|
CHECK(back->tick == message.tick);
|
|
CHECK(back->full == message.full);
|
|
CHECK(back->sent_at_us == message.sent_at_us);
|
|
|
|
// Identifiant de la liaison core-render dans les octets 4 à 7.
|
|
REQUIRE(frame.size() > 8);
|
|
CHECK(frame.substr(4, 4) == "SQCR");
|
|
}
|
|
|
|
TEST_CASE("scene/delta FlatBuffers : delta vide, full absent vaut faux")
|
|
{
|
|
SceneDeltaMessage message;
|
|
message.delta.world_version = 3;
|
|
message.tick = 2;
|
|
message.sent_at_us = 1;
|
|
const auto back = decode_scene_delta_frame(bytes_of(encode_scene_delta_frame(message)));
|
|
REQUIRE(back.has_value());
|
|
CHECK(back->delta.commands.empty());
|
|
CHECK_FALSE(back->full);
|
|
CHECK(back->delta.world_version == 3);
|
|
}
|
|
|
|
TEST_CASE("scene/delta FlatBuffers : valeurs refusees par les controles generes")
|
|
{
|
|
std::string error;
|
|
CHECK(decode_scene_delta_frame(bytes_of(set_transform_frame(5, 1.0f))).has_value());
|
|
|
|
SUBCASE("id 0 réservé")
|
|
{
|
|
CHECK_FALSE(decode_scene_delta_frame(bytes_of(set_transform_frame(0, 1.0f)), &error).has_value());
|
|
CHECK(error.find("id") != std::string::npos);
|
|
}
|
|
SUBCASE("nombre non fini")
|
|
{
|
|
CHECK_FALSE(decode_scene_delta_frame(bytes_of(set_transform_frame(5, std::numeric_limits<float>::infinity())), &error)
|
|
.has_value());
|
|
CHECK_FALSE(decode_scene_delta_frame(bytes_of(set_transform_frame(5, std::numeric_limits<float>::quiet_NaN())))
|
|
.has_value());
|
|
}
|
|
SUBCASE("primitive inconnue")
|
|
{
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
const common::Vec3 size(1, 1, 1);
|
|
const common::Color color(1, 1, 1, 1);
|
|
const auto visual = cr::CreateVisual(builder, static_cast<cr::Primitive>(9), &size, &color, 0U);
|
|
const std::vector<cr::SceneCommand> types{cr::SceneCommand::SetVisual};
|
|
const std::vector<flatbuffers::Offset<void>> tables{cr::CreateSetVisual(builder, 4, visual).Union()};
|
|
const auto types_vector = builder.CreateVector(types);
|
|
const auto tables_vector = builder.CreateVector(tables);
|
|
cr::contract::finish_scene_delta(builder, 0,
|
|
cr::CreateSceneDelta(builder, 1, 1, false, 1, types_vector, tables_vector));
|
|
CHECK_FALSE(decode_scene_delta_frame(bytes_of(frame_bytes(builder)), &error).has_value());
|
|
CHECK(error.find("primitive") != std::string::npos);
|
|
}
|
|
SUBCASE("champ obligatoire absent (tick)")
|
|
{
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
const auto types_vector = builder.CreateVector(std::vector<cr::SceneCommand>{});
|
|
const auto tables_vector = builder.CreateVector(std::vector<flatbuffers::Offset<void>>{});
|
|
cr::contract::finish_scene_delta(
|
|
builder, 0, cr::CreateSceneDelta(builder, 1, flatbuffers::nullopt, false, 1, types_vector, tables_vector));
|
|
CHECK_FALSE(decode_scene_delta_frame(bytes_of(frame_bytes(builder)), &error).has_value());
|
|
CHECK(error.find("tick") != std::string::npos);
|
|
}
|
|
SUBCASE("trame tronquée")
|
|
{
|
|
const std::string frame = encode_scene_delta_frame(every_command());
|
|
for (std::size_t size = 0; size < frame.size(); size += 7) {
|
|
CHECK_FALSE(decode_scene_delta_frame(bytes_of(frame.substr(0, size))).has_value());
|
|
}
|
|
}
|
|
}
|