From 53cb0401515667b234b9bcfb6ef908e30c62ad4f Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 20 Mar 2019 04:47:03 +0700 Subject: [PATCH] add shape class (2d analog of mesh) --- .gitattributes | 1 + headers/enduro2d/utils/_all.hpp | 1 + headers/enduro2d/utils/_utils.hpp | 1 + headers/enduro2d/utils/shape.hpp | 89 +++++++ sources/enduro2d/utils/mesh.cpp | 3 + sources/enduro2d/utils/shape.cpp | 240 ++++++++++++++++++ .../enduro2d/utils/shape_impl/shape_impl.hpp | 15 ++ .../utils/shape_impl/shape_reader_e2d.cpp | 110 ++++++++ untests/bin/gnome/gnome.obj.gnome.e2d_shape | 3 + untests/bin/gnome/gnome.obj.yad.e2d_shape | 3 + untests/sources/untests_utils/shape.cpp | 47 ++++ 11 files changed, 513 insertions(+) create mode 100644 headers/enduro2d/utils/shape.hpp create mode 100644 sources/enduro2d/utils/shape.cpp create mode 100644 sources/enduro2d/utils/shape_impl/shape_impl.hpp create mode 100644 sources/enduro2d/utils/shape_impl/shape_reader_e2d.cpp create mode 100644 untests/bin/gnome/gnome.obj.gnome.e2d_shape create mode 100644 untests/bin/gnome/gnome.obj.yad.e2d_shape create mode 100644 untests/sources/untests_utils/shape.cpp diff --git a/.gitattributes b/.gitattributes index 0a2dbfac..d18d83d0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,4 @@ *.png filter=lfs diff=lfs merge=lfs -text *.obj filter=lfs diff=lfs merge=lfs -text *.e2d_mesh filter=lfs diff=lfs merge=lfs -text +*.e2d_shape filter=lfs diff=lfs merge=lfs -text diff --git a/headers/enduro2d/utils/_all.hpp b/headers/enduro2d/utils/_all.hpp index 31340224..543401b2 100644 --- a/headers/enduro2d/utils/_all.hpp +++ b/headers/enduro2d/utils/_all.hpp @@ -19,6 +19,7 @@ #include "mesh.hpp" #include "module.hpp" #include "path.hpp" +#include "shape.hpp" #include "streams.hpp" #include "strfmts.hpp" #include "strings.hpp" diff --git a/headers/enduro2d/utils/_utils.hpp b/headers/enduro2d/utils/_utils.hpp index 4d40a9e3..714d8e8b 100644 --- a/headers/enduro2d/utils/_utils.hpp +++ b/headers/enduro2d/utils/_utils.hpp @@ -16,6 +16,7 @@ namespace e2d class color32; class image; class mesh; + class shape; class input_stream; class output_stream; class input_sequence; diff --git a/headers/enduro2d/utils/shape.hpp b/headers/enduro2d/utils/shape.hpp new file mode 100644 index 00000000..525a704a --- /dev/null +++ b/headers/enduro2d/utils/shape.hpp @@ -0,0 +1,89 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019 Matvey Cherevko + ******************************************************************************/ + +#pragma once + +#include "_utils.hpp" + +#include "buffer.hpp" +#include "color32.hpp" +#include "streams.hpp" + +namespace e2d +{ + class bad_shape_access final : public exception { + public: + const char* what() const noexcept final { + return "bad shape access"; + } + }; + + class shape final { + public: + shape() = default; + + shape(shape&& other) noexcept; + shape& operator=(shape&& other) noexcept; + + shape(const shape& other); + shape& operator=(const shape& other); + + shape& assign(shape&& other) noexcept; + shape& assign(const shape& other); + + void swap(shape& other) noexcept; + void clear() noexcept; + bool empty() const noexcept; + + shape& set_uvs(std::size_t channel, vector&& uvs); + shape& set_uvs(std::size_t channel, const vector& uvs); + shape& set_uvs(std::size_t channel, const v2f* uvs, std::size_t count); + + shape& set_colors(std::size_t channel, vector&& colors); + shape& set_colors(std::size_t channel, const vector& colors); + shape& set_colors(std::size_t channel, const color32* colors, std::size_t count); + + shape& set_vertices(vector&& vertices) noexcept; + shape& set_vertices(const vector& vertices); + shape& set_vertices(const v2f* vertices, std::size_t count); + + shape& set_indices(std::size_t subshape, vector&& indices); + shape& set_indices(std::size_t subshape, const vector& indices); + shape& set_indices(std::size_t subshape, const u32* indices, std::size_t count); + + const vector& uvs(std::size_t channel) const; + std::size_t uvs_channel_count() const noexcept; + + const vector& colors(std::size_t channel) const; + std::size_t colors_channel_count() const noexcept; + + const vector& vertices() const noexcept; + + const vector& indices(std::size_t subshape) const; + std::size_t indices_subshape_count() const noexcept; + private: + vector> uvs_channels_; + vector> colors_channels_; + + vector vertices_; + vector> indices_subshapees_; + }; + + void swap(shape& l, shape& r) noexcept; + bool operator==(const shape& l, const shape& r) noexcept; + bool operator!=(const shape& l, const shape& r) noexcept; +} + +namespace e2d { namespace shapes +{ + bool try_load_shape( + shape& dst, + const buffer& src) noexcept; + + bool try_load_shape( + shape& dst, + const input_stream_uptr& src) noexcept; +}} diff --git a/sources/enduro2d/utils/mesh.cpp b/sources/enduro2d/utils/mesh.cpp index 2d009a83..4d4f1fe7 100644 --- a/sources/enduro2d/utils/mesh.cpp +++ b/sources/enduro2d/utils/mesh.cpp @@ -257,6 +257,9 @@ namespace e2d } bool operator==(const mesh& l, const mesh& r) noexcept { + if ( l.vertices().size() != r.vertices().size() ) { + return false; + } if ( l.uvs_channel_count() != r.uvs_channel_count() ) { return false; } diff --git a/sources/enduro2d/utils/shape.cpp b/sources/enduro2d/utils/shape.cpp new file mode 100644 index 00000000..fe81aec1 --- /dev/null +++ b/sources/enduro2d/utils/shape.cpp @@ -0,0 +1,240 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019 Matvey Cherevko + ******************************************************************************/ + +#include "shape_impl/shape_impl.hpp" + +namespace e2d +{ + shape::shape(shape&& other) noexcept { + assign(std::move(other)); + } + + shape& shape::operator=(shape&& other) noexcept { + return assign(std::move(other)); + } + + shape::shape(const shape& other) { + assign(other); + } + + shape& shape::operator=(const shape& other) { + return assign(other); + } + + shape& shape::assign(shape&& other) noexcept { + if ( this != &other ) { + swap(other); + other.clear(); + } + return *this; + } + + shape& shape::assign(const shape& other) { + if ( this != &other ) { + shape m; + m.uvs_channels_ = other.uvs_channels_; + m.colors_channels_ = other.colors_channels_; + m.vertices_ = other.vertices_; + m.indices_subshapees_ = other.indices_subshapees_; + swap(m); + } + return *this; + } + + void shape::swap(shape& other) noexcept { + using std::swap; + swap(uvs_channels_, other.uvs_channels_); + swap(colors_channels_, other.colors_channels_); + swap(vertices_, other.vertices_); + swap(indices_subshapees_, other.indices_subshapees_); + } + + void shape::clear() noexcept { + uvs_channels_.clear(); + colors_channels_.clear(); + vertices_.clear(); + indices_subshapees_.clear(); + } + + bool shape::empty() const noexcept { + return vertices_.empty(); + } + + shape& shape::set_uvs(std::size_t channel, vector&& uvs) { + if ( uvs_channels_.size() <= channel ) { + uvs_channels_.resize(channel + 1); + } + uvs_channels_[channel] = std::move(uvs); + return *this; + } + + shape& shape::set_uvs(std::size_t channel, const vector& uvs) { + return set_uvs(channel, uvs.data(), uvs.size()); + } + + shape& shape::set_uvs(std::size_t channel, const v2f* uvs, std::size_t count) { + return count + ? set_uvs(channel, uvs + ? vector(uvs, uvs + count) + : vector(count)) + : set_uvs(channel, vector()); + } + + shape& shape::set_colors(std::size_t channel, vector&& colors) { + if ( colors_channels_.size() <= channel ) { + colors_channels_.resize(channel + 1); + } + colors_channels_[channel] = std::move(colors); + return *this; + } + + shape& shape::set_colors(std::size_t channel, const vector& colors) { + return set_colors(channel, colors.data(), colors.size()); + } + + shape& shape::set_colors(std::size_t channel, const color32* colors, std::size_t count) { + return count + ? set_colors(channel, colors + ? vector(colors, colors + count) + : vector(count)) + : set_colors(channel, vector()); + } + + shape& shape::set_vertices(vector&& vertices) noexcept { + vertices_ = std::move(vertices); + return *this; + } + + shape& shape::set_vertices(const vector& vertices) { + return set_vertices(vertices.data(), vertices.size()); + } + + shape& shape::set_vertices(const v2f* vertices, std::size_t count) { + return count + ? set_vertices(vertices + ? vector(vertices, vertices + count) + : vector(count)) + : set_vertices(vector()); + } + + shape& shape::set_indices(std::size_t subshape, vector&& indices) { + if ( indices_subshapees_.size() <= subshape ) { + indices_subshapees_.resize(subshape + 1); + } + indices_subshapees_[subshape] = std::move(indices); + return *this; + } + + shape& shape::set_indices(std::size_t subshape, const vector& indices) { + return set_indices(subshape, indices.data(), indices.size()); + } + + shape& shape::set_indices(std::size_t subshape, const u32* indices, std::size_t count) { + return count + ? set_indices(subshape, indices + ? vector(indices, indices + count) + : vector(count)) + : set_indices(subshape, vector()); + } + + const vector& shape::uvs(std::size_t channel) const { + if ( channel < uvs_channels_.size() ) { + return uvs_channels_[channel]; + } + throw bad_shape_access(); + } + + std::size_t shape::uvs_channel_count() const noexcept { + return uvs_channels_.size(); + } + + const vector& shape::colors(std::size_t channel) const { + if ( channel < colors_channels_.size() ) { + return colors_channels_[channel]; + } + throw bad_shape_access(); + } + + std::size_t shape::colors_channel_count() const noexcept { + return colors_channels_.size(); + } + + const vector& shape::vertices() const noexcept { + return vertices_; + } + + const vector& shape::indices(std::size_t subshape) const { + if ( subshape < indices_subshapees_.size() ) { + return indices_subshapees_[subshape]; + } + throw bad_shape_access(); + } + + std::size_t shape::indices_subshape_count() const noexcept { + return indices_subshapees_.size(); + } +} + +namespace e2d +{ + void swap(shape& l, shape& r) noexcept { + l.swap(r); + } + + bool operator==(const shape& l, const shape& r) noexcept { + if ( l.vertices().size() != r.vertices().size() ) { + return false; + } + if ( l.uvs_channel_count() != r.uvs_channel_count() ) { + return false; + } + if ( l.colors_channel_count() != r.colors_channel_count() ) { + return false; + } + if ( l.indices_subshape_count() != r.indices_subshape_count() ) { + return false; + } + for ( std::size_t i = 0; i < l.uvs_channel_count(); ++i ) { + if ( l.uvs(i) != r.uvs(i) ) { + return false; + } + } + for ( std::size_t i = 0; i < l.colors_channel_count(); ++i ) { + if ( l.colors(i) != r.colors(i) ) { + return false; + } + } + for ( std::size_t i = 0; i < l.indices_subshape_count(); ++i ) { + if ( l.indices(i) != r.indices(i) ) { + return false; + } + } + return l.vertices() == r.vertices(); + } + + bool operator!=(const shape& l, const shape& r) noexcept { + return !(l == r); + } +} + +namespace e2d { namespace shapes +{ + bool try_load_shape( + shape& dst, + const buffer& src) noexcept + { + return impl::try_load_shape_e2d(dst, src); + } + + bool try_load_shape( + shape& dst, + const input_stream_uptr& src) noexcept + { + buffer file_data; + return streams::try_read_tail(file_data, src) + && try_load_shape(dst, file_data); + } +}} diff --git a/sources/enduro2d/utils/shape_impl/shape_impl.hpp b/sources/enduro2d/utils/shape_impl/shape_impl.hpp new file mode 100644 index 00000000..83866c5f --- /dev/null +++ b/sources/enduro2d/utils/shape_impl/shape_impl.hpp @@ -0,0 +1,15 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019 Matvey Cherevko + ******************************************************************************/ + +#pragma once + +#include +#include + +namespace e2d { namespace shapes { namespace impl +{ + bool try_load_shape_e2d(shape& dst, const buffer& src) noexcept; +}}} diff --git a/sources/enduro2d/utils/shape_impl/shape_reader_e2d.cpp b/sources/enduro2d/utils/shape_impl/shape_reader_e2d.cpp new file mode 100644 index 00000000..b344cda1 --- /dev/null +++ b/sources/enduro2d/utils/shape_impl/shape_reader_e2d.cpp @@ -0,0 +1,110 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019 Matvey Cherevko + ******************************************************************************/ + +#include "shape_impl.hpp" + +namespace +{ + using namespace e2d; + + const u32 mesh_file_version = 1u; + const str_view shape_file_signature = "e2d_shape"; + + template < typename T > + input_sequence& iseq_read_vector_pods(input_sequence& iseq, std::vector& v) { + return iseq.read(v.data(), v.size() * sizeof(T)); + } + + bool check_signature(const input_stream_uptr& stream) { + input_sequence iseq{*stream}; + + u32 file_version = 0; + char* file_signature = static_cast(E2D_CLEAR_ALLOCA( + shape_file_signature.length() + 1)); + + iseq.read(file_signature, shape_file_signature.length()) + .read(file_version); + + return iseq.success() + && shape_file_signature == file_signature + && mesh_file_version == file_version; + } + + bool load_shape(shape& dst, const input_stream_uptr& stream) { + input_sequence iseq{*stream}; + + u32 vertices = 0; + u32 indices = 0; + u32 uvs_channels = 0; + u32 colors_channels = 0; + + iseq.read(vertices) + .read(indices) + .read(uvs_channels) + .read(colors_channels); + + if ( !iseq.success() ) { + return false; + } + + vector vertices_data(vertices); + vector indices_data(indices); + + vector> uvs_channels_data( + uvs_channels, + vector(vertices)); + + vector> colors_channels_data( + colors_channels, + vector(vertices)); + + iseq_read_vector_pods(iseq, vertices_data); + iseq_read_vector_pods(iseq, indices_data); + + for ( auto& uvs : uvs_channels_data ) { + iseq_read_vector_pods(iseq, uvs); + } + + for ( auto& colors : colors_channels_data ) { + iseq_read_vector_pods(iseq, colors); + } + + if ( !iseq.success() || stream->tell() != stream->length() ) { + return false; + } + + shape m; + + m.set_vertices(std::move(vertices_data)); + m.set_indices(0, std::move(indices_data)); + + for ( std::size_t i = 0; i < uvs_channels_data.size(); ++i ) { + m.set_uvs(i, std::move(uvs_channels_data[i])); + } + + for ( std::size_t i = 0; i < colors_channels_data.size(); ++i ) { + m.set_colors(i, std::move(colors_channels_data[i])); + } + + dst = std::move(m); + return true; + } +} + +namespace e2d { namespace shapes { namespace impl +{ + bool try_load_shape_e2d(shape& dst, const buffer& src) noexcept { + try { + auto stream = make_memory_stream(src); + return stream + && check_signature(stream) + && load_shape(dst, stream); + } catch (...) { + // nothing + } + return false; + } +}}} diff --git a/untests/bin/gnome/gnome.obj.gnome.e2d_shape b/untests/bin/gnome/gnome.obj.gnome.e2d_shape new file mode 100644 index 00000000..a9673361 --- /dev/null +++ b/untests/bin/gnome/gnome.obj.gnome.e2d_shape @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79ec01d82de7c22ba872c7fb36a5c4ef64a566208afbb35c48a277d13a5c33a6 +size 14493 diff --git a/untests/bin/gnome/gnome.obj.yad.e2d_shape b/untests/bin/gnome/gnome.obj.yad.e2d_shape new file mode 100644 index 00000000..652eaa04 --- /dev/null +++ b/untests/bin/gnome/gnome.obj.yad.e2d_shape @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cec1a445997e22a375d8164f1b186962f3d3d13cc161dfd47489f51f39574f3 +size 1821 diff --git a/untests/sources/untests_utils/shape.cpp b/untests/sources/untests_utils/shape.cpp new file mode 100644 index 00000000..edf5a1b4 --- /dev/null +++ b/untests/sources/untests_utils/shape.cpp @@ -0,0 +1,47 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019 Matvey Cherevko + ******************************************************************************/ + +#include "_utils.hpp" +using namespace e2d; + +TEST_CASE("shape") { + str resources; + REQUIRE(filesystem::extract_predef_path( + resources, + filesystem::predef_path::resources)); + { + shape s; + REQUIRE(shapes::try_load_shape( + s, + make_read_file(path::combine(resources, "bin/gnome/gnome.obj.gnome.e2d_shape")))); + + REQUIRE(s.vertices().size() == 397); + + REQUIRE(s.indices_subshape_count() == 1); + REQUIRE(s.indices(0).size() == 2028); + + REQUIRE(s.uvs_channel_count() == 1); + REQUIRE(s.uvs(0).size() == 397); + + REQUIRE(s.colors_channel_count() == 0); + } + { + shape s; + REQUIRE(shapes::try_load_shape( + s, + make_read_file(path::combine(resources, "bin/gnome/gnome.obj.yad.e2d_shape")))); + + REQUIRE(s.vertices().size() == 46); + + REQUIRE(s.indices_subshape_count() == 1); + REQUIRE(s.indices(0).size() == 264); + + REQUIRE(s.uvs_channel_count() == 1); + REQUIRE(s.uvs(0).size() == 46); + + REQUIRE(s.colors_channel_count() == 0); + } +}