mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-16 14:08:59 +07:00
add sprite making using atlas
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "_high.hpp"
|
||||
|
||||
#include "assets/atlas_asset.hpp"
|
||||
#include "assets/texture_asset.hpp"
|
||||
#include "assets/material_asset.hpp"
|
||||
|
||||
@@ -30,13 +31,12 @@ namespace e2d
|
||||
sprite& assign(sprite&& other) noexcept;
|
||||
sprite& assign(const sprite& other);
|
||||
|
||||
sprite& set_size(const v2f& size) noexcept;
|
||||
sprite& set_pivot(const v2f& pivot) noexcept;
|
||||
sprite& set_texrect(const b2f& texrect) noexcept;
|
||||
sprite& set_region(const atlas::region& region) noexcept;
|
||||
sprite& set_texture(const texture_asset::ptr& texture) noexcept;
|
||||
sprite& set_material(const material_asset::ptr& material) noexcept;
|
||||
|
||||
const v2f& size() const noexcept;
|
||||
const v2f& pivot() const noexcept;
|
||||
const b2f& texrect() const noexcept;
|
||||
const texture_asset::ptr& texture() const noexcept;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"size" : { "x" : 66, "y" : 113 },
|
||||
"pivot" : { "x" : 0.5, "y" : 0.5 },
|
||||
"texrect" : { "w" : 1, "h" : 1 },
|
||||
"texture" : "ship.png",
|
||||
"atlas" : {
|
||||
"atlas" : "ships_atlas.json",
|
||||
"region" : "ship (2).png"
|
||||
},
|
||||
"material" : "sprite_material.json"
|
||||
}
|
||||
|
||||
3
samples/bin/library/ships.png
Executable file
3
samples/bin/library/ships.png
Executable file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5a4d7dc7537adc92535d339b502b209d275af933451cb6004d36e8671f2b6120
|
||||
size 156863
|
||||
12
samples/bin/library/ships_atlas.json
Normal file
12
samples/bin/library/ships_atlas.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"texture" : "ships.png",
|
||||
"regions" : [{
|
||||
"name" : "ship (1).png",
|
||||
"pivot" : { "x" : 441, "y" : 455 },
|
||||
"texrect" : { "x" : 408, "y" : 399, "w" : 66, "h" : 113 }
|
||||
},{
|
||||
"name" : "ship (2).png",
|
||||
"pivot" : { "x" : 441, "y" : 340 },
|
||||
"texrect" : { "x" : 408, "y" : 284, "w" : 66, "h" : 113 }
|
||||
}]
|
||||
}
|
||||
@@ -155,7 +155,6 @@ namespace
|
||||
|
||||
vector<atlas::region> regions;
|
||||
if ( root.HasMember("regions") ) {
|
||||
E2D_ASSERT(root["regions"].IsArray());
|
||||
const auto& regions_json = root["regions"];
|
||||
if ( !parse_regions(regions_json, regions) ) {
|
||||
return stdex::make_rejected_promise<atlas>(
|
||||
@@ -165,7 +164,6 @@ namespace
|
||||
|
||||
vector<atlas::shape_region> shape_regions;
|
||||
if ( root.HasMember("shape_regions") ) {
|
||||
E2D_ASSERT(root["shape_regions"].IsArray());
|
||||
const auto& shape_regions_json = root["shape_regions"];
|
||||
if ( !parse_shape_regions(shape_regions_json, shape_regions) ) {
|
||||
return stdex::make_rejected_promise<atlas>(
|
||||
|
||||
@@ -18,18 +18,44 @@ namespace
|
||||
}
|
||||
};
|
||||
|
||||
const char* sprite_asset_schema_source = R"json(
|
||||
{
|
||||
const char* sprite_asset_schema_source = R"json({
|
||||
"type" : "object",
|
||||
"required" : [ "size", "pivot", "texrect" ],
|
||||
"oneOf" : [{
|
||||
"required" : [ "atlas", "material" ],
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"size" : { "$ref": "#/common_definitions/v2" },
|
||||
"pivot" : { "$ref": "#/common_definitions/v2" },
|
||||
"texrect" : { "$ref": "#/common_definitions/b2" },
|
||||
"texture" : { "$ref": "#/common_definitions/address" },
|
||||
"atlas" : { "$ref": "#/definitions/atlas" },
|
||||
"material" : { "$ref": "#/common_definitions/address" }
|
||||
}
|
||||
}, {
|
||||
"required" : [ "texture", "material" ],
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"texture" : { "$ref": "#/definitions/texture" },
|
||||
"material" : { "$ref": "#/common_definitions/address" }
|
||||
}
|
||||
}],
|
||||
"definitions" : {
|
||||
"atlas" : {
|
||||
"type" : "object",
|
||||
"required" : [ "atlas", "region" ],
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"atlas" : { "$ref": "#/common_definitions/address" },
|
||||
"region" : { "$ref": "#/common_definitions/name" }
|
||||
}
|
||||
},
|
||||
"texture" : {
|
||||
"type" : "object",
|
||||
"required" : [ "texture", "pivot", "texrect" ],
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
"texture" : { "$ref": "#/common_definitions/address" },
|
||||
"pivot" : { "$ref": "#/common_definitions/v2" },
|
||||
"texrect" : { "$ref": "#/common_definitions/b2" }
|
||||
}
|
||||
}
|
||||
}
|
||||
})json";
|
||||
|
||||
const rapidjson::SchemaDocument& sprite_asset_schema() {
|
||||
@@ -50,62 +76,126 @@ namespace
|
||||
return *schema;
|
||||
}
|
||||
|
||||
stdex::promise<sprite> parse_sprite(
|
||||
stdex::promise<sprite> parse_sprite_with_atlas(
|
||||
library& library,
|
||||
str_view parent_address,
|
||||
const rapidjson::Value& root)
|
||||
{
|
||||
v2f size;
|
||||
E2D_ASSERT(root.HasMember("size"));
|
||||
if ( !json_utils::try_parse_value(root["size"], size) ) {
|
||||
E2D_ASSERT(root.HasMember("material") && root["material"].IsString());
|
||||
auto material_p = library.load_asset_async<material_asset>(
|
||||
path::combine(parent_address, root["material"].GetString()));
|
||||
|
||||
E2D_ASSERT(root.HasMember("atlas") && root["atlas"].IsObject());
|
||||
const auto& atlas_root_json = root["atlas"];
|
||||
|
||||
E2D_ASSERT(atlas_root_json.HasMember("atlas") && atlas_root_json["atlas"].IsString());
|
||||
auto atlas_p = library.load_asset_async<atlas_asset>(
|
||||
path::combine(parent_address, atlas_root_json["atlas"].GetString()));
|
||||
|
||||
str_hash region_hash;
|
||||
if ( !json_utils::try_parse_value(atlas_root_json["region"], region_hash) ) {
|
||||
return stdex::make_rejected_promise<sprite>(
|
||||
sprite_asset_loading_exception());
|
||||
}
|
||||
|
||||
return stdex::make_tuple_promise(std::make_tuple(
|
||||
std::move(atlas_p),
|
||||
std::move(material_p)
|
||||
)).then([region_hash](const std::tuple<
|
||||
atlas_asset::load_result,
|
||||
material_asset::load_result
|
||||
>& results){
|
||||
const atlas_asset::load_result& atlas = std::get<0>(results);
|
||||
const material_asset::load_result& material = std::get<1>(results);
|
||||
|
||||
if ( !atlas || !material ) {
|
||||
throw sprite_asset_loading_exception();
|
||||
}
|
||||
|
||||
const texture_asset::ptr& texture = atlas->content().texture();
|
||||
const atlas::region* region = atlas->content().find_region(region_hash);
|
||||
|
||||
if ( !texture || !region ) {
|
||||
throw sprite_asset_loading_exception();
|
||||
}
|
||||
|
||||
sprite content;
|
||||
content.set_region(*region);
|
||||
content.set_texture(texture);
|
||||
content.set_material(material);
|
||||
return content;
|
||||
});
|
||||
}
|
||||
|
||||
stdex::promise<sprite> parse_sprite_with_texture(
|
||||
library& library,
|
||||
str_view parent_address,
|
||||
const rapidjson::Value& root)
|
||||
{
|
||||
E2D_ASSERT(root.HasMember("material") && root["material"].IsString());
|
||||
auto material_p = library.load_asset_async<material_asset>(
|
||||
path::combine(parent_address, root["material"].GetString()));
|
||||
|
||||
E2D_ASSERT(root.HasMember("texture") && root["texture"].IsObject());
|
||||
const auto& texture_root_json = root["texture"];
|
||||
|
||||
E2D_ASSERT(texture_root_json.HasMember("texture") && texture_root_json["texture"].IsString());
|
||||
auto texture_p = library.load_asset_async<texture_asset>(
|
||||
path::combine(parent_address, texture_root_json["texture"].GetString()));
|
||||
|
||||
v2f pivot;
|
||||
E2D_ASSERT(root.HasMember("pivot"));
|
||||
if ( !json_utils::try_parse_value(root["pivot"], pivot) ) {
|
||||
E2D_ASSERT(texture_root_json.HasMember("pivot"));
|
||||
if ( !json_utils::try_parse_value(texture_root_json["pivot"], pivot) ) {
|
||||
return stdex::make_rejected_promise<sprite>(
|
||||
sprite_asset_loading_exception());
|
||||
}
|
||||
|
||||
b2f texrect;
|
||||
E2D_ASSERT(root.HasMember("texrect"));
|
||||
if ( !json_utils::try_parse_value(root["texrect"], texrect) ) {
|
||||
E2D_ASSERT(texture_root_json.HasMember("texrect"));
|
||||
if ( !json_utils::try_parse_value(texture_root_json["texrect"], texrect) ) {
|
||||
return stdex::make_rejected_promise<sprite>(
|
||||
sprite_asset_loading_exception());
|
||||
}
|
||||
|
||||
E2D_ASSERT(!root.HasMember("texture") || root["texture"].IsString());
|
||||
auto texture_p = root.HasMember("texture")
|
||||
? library.load_asset_async<texture_asset>(path::combine(
|
||||
parent_address,
|
||||
root["texture"].GetString()))
|
||||
: stdex::make_resolved_promise(texture_asset::ptr());
|
||||
|
||||
E2D_ASSERT(!root.HasMember("material") || root["material"].IsString());
|
||||
auto material_p = root.HasMember("material")
|
||||
? library.load_asset_async<material_asset>(path::combine(
|
||||
parent_address,
|
||||
root["material"].GetString()))
|
||||
: stdex::make_resolved_promise(material_asset::ptr());
|
||||
|
||||
return stdex::make_tuple_promise(std::make_tuple(
|
||||
std::move(texture_p),
|
||||
std::move(material_p)
|
||||
)).then([size, pivot, texrect](const std::tuple<
|
||||
)).then([pivot, texrect](const std::tuple<
|
||||
texture_asset::load_result,
|
||||
material_asset::load_result
|
||||
>& results){
|
||||
const texture_asset::load_result& texture = std::get<0>(results);
|
||||
const material_asset::load_result& material = std::get<1>(results);
|
||||
|
||||
if ( !texture || !material ) {
|
||||
throw sprite_asset_loading_exception();
|
||||
}
|
||||
|
||||
sprite content;
|
||||
content.set_size(size);
|
||||
content.set_pivot(pivot);
|
||||
content.set_texrect(texrect);
|
||||
content.set_texture(std::get<0>(results));
|
||||
content.set_material(std::get<1>(results));
|
||||
content.set_texture(texture);
|
||||
content.set_material(material);
|
||||
return content;
|
||||
});
|
||||
}
|
||||
|
||||
stdex::promise<sprite> parse_sprite(
|
||||
library& library,
|
||||
str_view parent_address,
|
||||
const rapidjson::Value& root)
|
||||
{
|
||||
if ( root.HasMember("atlas") ) {
|
||||
return parse_sprite_with_atlas(library, parent_address, root);
|
||||
}
|
||||
|
||||
if ( root.HasMember("texture") ) {
|
||||
return parse_sprite_with_texture(library, parent_address, root);
|
||||
}
|
||||
|
||||
return stdex::make_rejected_promise<sprite>(
|
||||
sprite_asset_loading_exception());
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
|
||||
@@ -65,11 +65,6 @@ namespace e2d
|
||||
return *this;
|
||||
}
|
||||
|
||||
sprite& sprite::set_size(const v2f& size) noexcept {
|
||||
size_ = size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
sprite& sprite::set_pivot(const v2f& pivot) noexcept {
|
||||
pivot_ = pivot;
|
||||
return *this;
|
||||
@@ -80,6 +75,12 @@ namespace e2d
|
||||
return *this;
|
||||
}
|
||||
|
||||
sprite& sprite::set_region(const atlas::region& region) noexcept {
|
||||
pivot_ = region.pivot;
|
||||
texrect_ = region.texrect;
|
||||
return *this;
|
||||
}
|
||||
|
||||
sprite& sprite::set_texture(const texture_asset::ptr& texture) noexcept {
|
||||
texture_ = texture;
|
||||
return *this;
|
||||
@@ -90,10 +91,6 @@ namespace e2d
|
||||
return *this;
|
||||
}
|
||||
|
||||
const v2f& sprite::size() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
|
||||
const v2f& sprite::pivot() const noexcept {
|
||||
return pivot_;
|
||||
}
|
||||
@@ -118,8 +115,7 @@ namespace e2d
|
||||
}
|
||||
|
||||
bool operator==(const sprite& l, const sprite& r) noexcept {
|
||||
return l.size() == r.size()
|
||||
&& l.pivot() == r.pivot()
|
||||
return l.pivot() == r.pivot()
|
||||
&& l.texrect() == r.texrect()
|
||||
&& l.texture() == r.texture()
|
||||
&& l.material() == r.material();
|
||||
|
||||
@@ -125,31 +125,36 @@ namespace e2d { namespace render_system_impl
|
||||
const renderer& node_r,
|
||||
const sprite_renderer& spr_r)
|
||||
{
|
||||
if ( !node || !node_r.enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !spr_r.sprite() || !spr_r.sprite()->content().material()) {
|
||||
if ( !node || !node_r.enabled() || !spr_r.sprite() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sprite& spr = spr_r.sprite()->content();
|
||||
const texture_asset::ptr& tex_a = spr.texture();
|
||||
const material_asset::ptr& mat_a = spr.material();
|
||||
|
||||
const f32 sw = spr.size().x;
|
||||
const f32 sh = spr.size().y;
|
||||
if ( !tex_a || !tex_a->content() || !mat_a ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const f32 hw = -sw * spr.pivot().x;
|
||||
const f32 hh = -sh * spr.pivot().y;
|
||||
const b2f& tex_r = spr.texrect();
|
||||
const v2f& tex_s = tex_a->content()->size().cast_to<f32>();
|
||||
|
||||
const v4f p1{hw + 0.f, hh + 0.f, 0.f, 1.f};
|
||||
const v4f p2{hw + sw, hh + 0.f, 0.f, 1.f};
|
||||
const v4f p3{hw + sw, hh + sh, 0.f, 1.f};
|
||||
const v4f p4{hw + 0.f, hh + sh, 0.f, 1.f};
|
||||
const f32 sw = tex_r.size.x;
|
||||
const f32 sh = tex_r.size.y;
|
||||
|
||||
const f32 tx = spr.texrect().position.x;
|
||||
const f32 ty = spr.texrect().position.y;
|
||||
const f32 tw = spr.texrect().size.x;
|
||||
const f32 th = spr.texrect().size.y;
|
||||
const f32 px = tex_r.position.x - spr.pivot().x;
|
||||
const f32 py = tex_r.position.y - spr.pivot().y;
|
||||
|
||||
const v4f p1{px + 0.f, py + 0.f, 0.f, 1.f};
|
||||
const v4f p2{px + sw, py + 0.f, 0.f, 1.f};
|
||||
const v4f p3{px + sw, py + sh, 0.f, 1.f};
|
||||
const v4f p4{px + 0.f, py + sh, 0.f, 1.f};
|
||||
|
||||
const f32 tx = tex_r.position.x / tex_s.x;
|
||||
const f32 ty = tex_r.position.y / tex_s.y;
|
||||
const f32 tw = tex_r.size.x / tex_s.x;
|
||||
const f32 th = tex_r.size.y / tex_s.y;
|
||||
|
||||
const m4f& sm = node->world_matrix();
|
||||
const color& tn = spr_r.tint();
|
||||
@@ -163,10 +168,6 @@ namespace e2d { namespace render_system_impl
|
||||
{ v3f(p3 * sm), {tx + tw, ty + th }, color32(tn) },
|
||||
{ v3f(p4 * sm), {tx + 0.f, ty + th }, color32(tn) }};
|
||||
|
||||
const texture_ptr texture = spr.texture()
|
||||
? spr.texture()->content()
|
||||
: nullptr;
|
||||
|
||||
const render::sampler_min_filter min_filter = spr_r.filtering()
|
||||
? render::sampler_min_filter::linear
|
||||
: render::sampler_min_filter::nearest;
|
||||
@@ -180,7 +181,7 @@ namespace e2d { namespace render_system_impl
|
||||
spr.material(),
|
||||
property_cache_
|
||||
.sampler(sprite_texture_sampler_hash, render::sampler_state()
|
||||
.texture(texture)
|
||||
.texture(tex_a->content())
|
||||
.min_filter(min_filter)
|
||||
.mag_filter(mag_filter))
|
||||
.merge(node_r.properties())
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"size" : { "x" : 10, "y" : 20 },
|
||||
"pivot" : { "x" : 0.5, "y" : 0.7 },
|
||||
"texrect" : { "w" : 0.5, "h" : 1 },
|
||||
"texture" : "image.png",
|
||||
"material" : "material.json"
|
||||
}
|
||||
7
untests/bin/library/sprite_a.json
Normal file
7
untests/bin/library/sprite_a.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"atlas" : {
|
||||
"atlas" : "atlas.json",
|
||||
"region" : "sprite"
|
||||
},
|
||||
"material" : "material.json"
|
||||
}
|
||||
8
untests/bin/library/sprite_t.json
Normal file
8
untests/bin/library/sprite_t.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"texture" : {
|
||||
"texture" : "image.png",
|
||||
"pivot" : { "x" : 1, "y" : 2 },
|
||||
"texrect" : { "x" : 5, "y" : 6, "w" : 7, "h" : 8 }
|
||||
},
|
||||
"material" : "material.json"
|
||||
}
|
||||
@@ -96,6 +96,9 @@ TEST_CASE("library"){
|
||||
REQUIRE(texture_res);
|
||||
REQUIRE(texture_res->content());
|
||||
|
||||
auto material_res = l.load_asset<material_asset>("material.json");
|
||||
REQUIRE(material_res);
|
||||
|
||||
auto atlas_res = l.load_asset<atlas_asset>("atlas.json");
|
||||
REQUIRE(atlas_res);
|
||||
REQUIRE(atlas_res->content().texture() == texture_res);
|
||||
@@ -116,13 +119,23 @@ TEST_CASE("library"){
|
||||
});
|
||||
REQUIRE_FALSE(atlas_res->content().find_shape_region("shape_sprite2"));
|
||||
|
||||
auto sprite_res = l.load_asset<sprite_asset>("sprite.json");
|
||||
{
|
||||
auto sprite_res = l.load_asset<sprite_asset>("sprite_a.json");
|
||||
REQUIRE(sprite_res);
|
||||
REQUIRE(sprite_res->content().size() == v2f(10.f, 20.f));
|
||||
REQUIRE(sprite_res->content().pivot() == v2f(0.5f, 0.7f));
|
||||
REQUIRE(sprite_res->content().texrect() == b2f(0.5f, 1.f));
|
||||
REQUIRE(sprite_res->content().texture());
|
||||
REQUIRE(sprite_res->content().material());
|
||||
REQUIRE(sprite_res->content().pivot() == v2f(1.f, 2.f));
|
||||
REQUIRE(sprite_res->content().texrect() == b2f(5.f, 6.f, 7.f, 8.f));
|
||||
REQUIRE(sprite_res->content().texture() == texture_res);
|
||||
REQUIRE(sprite_res->content().material() == material_res);
|
||||
}
|
||||
|
||||
{
|
||||
auto sprite_res = l.load_asset<sprite_asset>("sprite_t.json");
|
||||
REQUIRE(sprite_res);
|
||||
REQUIRE(sprite_res->content().pivot() == v2f(1.f, 2.f));
|
||||
REQUIRE(sprite_res->content().texrect() == b2f(5.f, 6.f, 7.f, 8.f));
|
||||
REQUIRE(sprite_res->content().texture() == texture_res);
|
||||
REQUIRE(sprite_res->content().material() == material_res);
|
||||
}
|
||||
|
||||
auto model_res = l.load_asset<model_asset>("model.json");
|
||||
REQUIRE(model_res);
|
||||
|
||||
Reference in New Issue
Block a user