From feca5c5471ba71423aa0870b1e1aff88d693baf5 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 7 Oct 2014 14:15:14 +0300 Subject: [PATCH] atlas: Should work now --- src/impl/atlas.cpp | 136 +++++++++++++++++++++++++++++------------- src/interface/atlas.h | 9 +-- 2 files changed, 100 insertions(+), 45 deletions(-) diff --git a/src/impl/atlas.cpp b/src/impl/atlas.cpp index a240163..eb781a1 100644 --- a/src/impl/atlas.cpp +++ b/src/impl/atlas.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace interface { @@ -20,18 +22,19 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry const AtlasSegmentReference add_segment( const AtlasSegmentDefinition &segment_def) { - throw Exception("Not implemented"); // Get Texture2D resource magic::ResourceCache *magic_cache = m_context->GetSubsystem(); - magic::Texture2D *texture = magic_cache->GetResource( - segment_def.resource_name); + magic::Image *seg_img = magic_cache->GetResource( + segment_def.resource_name.c_str()); + if(seg_img == nullptr) + throw Exception("Couldn't find image: "+segment_def.resource_name); // Get resolution of texture - magic::IntVector2 texture_size(texture->GetWidth(), texture->GetHeight()); + magic::IntVector2 seg_img_size(seg_img->GetWidth(), seg_img->GetHeight()); // Try to find a texture atlas for this texture size - TextureAtlasDefinition *atlas_def = NULL; + TextureAtlasDefinition *atlas_def = nullptr; for(TextureAtlasDefinition &def0 : m_defs){ - if(def0.segment_resolution == texture_size){ + if(def0.segment_resolution == seg_img_size){ size_t max = def0.total_segments.x_ * def0.total_segments.y_; if(atlas_def->segments.size() >= max) continue; // Full @@ -47,26 +50,56 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry atlas_def->id = m_defs.size()-1; // Calculate segment resolution atlas_def->segment_resolution = magic::IntVector2( - texture_size.x_ / segment_def.total_segments.x_, - texture_size.y_ / segment_def.total_segments.y_ + seg_img_size.x_ / segment_def.total_segments.x_, + seg_img_size.y_ / segment_def.total_segments.y_ ); // Calculate total segments based on segment resolution - int max_res = 2048; + const int max_res = 2048; atlas_def->total_segments = magic::IntVector2( max_res / atlas_def->segment_resolution.x_, - max_res / atlas_def->segment_resolution.y_, + max_res / atlas_def->segment_resolution.y_ ); + magic::IntVector2 atlas_resolution( + atlas_def->total_segments.x_ * atlas_def->segment_resolution.x_, + atlas_def->total_segments.y_ * atlas_def->segment_resolution.y_ + ); + // Create image for new atlas + magic::Image *atlas_img = new magic::Image(m_context); + atlas_img->SetSize(atlas_resolution.x_, atlas_resolution.y_, 4); // Create texture for new atlas - // TODO: Extend cache to fit this atlas if it doesn't yet - // TODO: Set texture of cached atlas + magic::Texture2D *atlas_tex = new magic::Texture2D(m_context); + // TODO: Use TEXTURE_STATIC or TEXTURE_DYNAMIC? + atlas_tex->SetSize(atlas_resolution.x_, atlas_resolution.y_, + magic::Graphics::GetRGBAFormat(), magic::TEXTURE_STATIC); + // Add new atlas to cache + const auto &id = atlas_def->id; + m_cache.resize(id+1); + TextureAtlasCache *cache = &m_cache[id]; + cache->image = atlas_img; + cache->texture = atlas_tex; + cache->segment_resolution = atlas_def->segment_resolution; + cache->total_segments = atlas_def->total_segments; } - // TODO: Add this definition to the atlas + // Add this segment to the atlas definition + uint seg_id = atlas_def->segments.size(); + atlas_def->segments.resize(seg_id + 1); + atlas_def->segments[seg_id] = segment_def; + // Update this segment in cache + TextureAtlasCache &atlas_cache = m_cache[atlas_def->id]; + atlas_cache.segments.resize(seg_id + 1); + AtlasSegmentCache &seg_cache = atlas_cache.segments[seg_id]; + update_segment_cache(seg_id, seg_img, seg_cache, segment_def, atlas_cache); + // Return reference to new segment + AtlasSegmentReference ref; + ref.atlas_id = atlas_def->id; + ref.segment_id = seg_id; + return ref; } const TextureAtlasDefinition* get_atlas_definition(uint atlas_id) { if(atlas_id >= m_defs.size()) - return NULL; + return nullptr; return &m_defs[atlas_id]; } @@ -75,47 +108,68 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry { const TextureAtlasDefinition *atlas = get_atlas_definition(ref.atlas_id); if(!atlas) - return NULL; + return nullptr; if(ref.segment_id >= atlas->segments.size()) - return NULL; + return nullptr; return &atlas->segments[ref.segment_id]; } - void update_segment_cache(AtlasSegmentCache &cache, - const AtlasSegmentDefinition &def, const TextureAtlasDefinition &atlas) + void update_segment_cache(uint seg_id, magic::Image *seg_img, + AtlasSegmentCache &cache, const AtlasSegmentDefinition &def, + const TextureAtlasCache &atlas) { - // TODO - // TODO: Get Texture2D resource - // TODO: Check that resolution of texture matches with atlas - // TODO: + // Check if atlas is full + size_t max_segments = atlas.total_segments.x_ * atlas.total_segments.y_; + if(atlas.segments.size() >= max_segments) + throw Exception("Atlas is full"); + // Set segment texture + cache.texture = atlas.texture; + // Calculate segment's position in atlas texture + uint seg_iy = seg_id / atlas.total_segments.x_; + uint seg_ix = seg_id - seg_iy; + magic::IntVector2 seg_size = atlas.segment_resolution; + magic::IntVector2 dst_p0(seg_ix * seg_size.x_, seg_iy * seg_size.y_); + magic::IntVector2 dst_p1 = dst_p0 + seg_size; + // Set coordinates in cache + cache.coord0 = magic::Vector2( + (float)dst_p0.x_ / (float)(atlas.total_segments.x_ * seg_size.x_), + (float)dst_p0.y_ / (float)(atlas.total_segments.y_ * seg_size.y_) + ); + cache.coord0 = magic::Vector2( + (float)dst_p1.x_ / (float)(atlas.total_segments.x_ * seg_size.x_), + (float)dst_p1.y_ / (float)(atlas.total_segments.y_ * seg_size.y_) + ); + // Draw segment into atlas image + magic::IntVector2 seg_img_size(seg_img->GetWidth(), seg_img->GetHeight()); + magic::IntVector2 src_off( + seg_img_size.x_ / def.total_segments.x_ * def.select_segment.x_, + seg_img_size.y_ / def.total_segments.y_ * def.select_segment.y_ + ); + for(int y=0; yGetPixel(src_p.x_, src_p.y_); + atlas.image->SetPixel(dst_p.x_, dst_p.y_, c); + } + } + // Update atlas texture from atlas image + // TODO: Does this require something more? + atlas.texture->SetData(atlas.image); } const AtlasSegmentCache* get_texture(const AtlasSegmentReference &ref) { if(ref.atlas_id >= m_cache.size()){ - if(ref.atlas_id >= m_defs.size()) - return NULL; - // New atlases are left with valid=false - m_cache.resize(m_defs.size()); + // Cache is always up-to-date + return nullptr; } TextureAtlasCache &cache = m_cache[ref.atlas_id]; - if(!cache.valid){ - const auto &id = ref.atlas_id; - cache.segment_resolution = m_defs[id].segment_resolution; - cache.total_segments = m_defs[id].total_segments; - // New segments are left with valid=false - cache.segments.resize(m_defs[id].segments.size()); - cache.valid = true; + if(ref.segment_id >= cache.segments.size()){ + // Cache is always up-to-date + return nullptr; } - if(ref.segment_id >= cache.segments.size()) - return NULL; AtlasSegmentCache &seg_cache = cache.segments[ref.segment_id]; - if(seg_cache.valid) - return &seg_cache; - TextureAtlasDefinition &atlas_def = m_defs[ref.atlas_id]; - const AtlasSegmentDefinition &seg_def = atlas_def.segments[ref.segment_id]; - update_segment_cache(seg_cache, seg_def, atlas_def); - seg_cache.valid = true; return &seg_cache; } }; diff --git a/src/interface/atlas.h b/src/interface/atlas.h index 9cf67e4..578aaeb 100644 --- a/src/interface/atlas.h +++ b/src/interface/atlas.h @@ -3,11 +3,13 @@ #pragma once #include "core/types.h" #include +#include namespace Urho3D { class Context; class Texture2D; + class Image; } namespace interface @@ -30,8 +32,7 @@ namespace interface struct AtlasSegmentCache { - bool valid = false; - magic::Texture2D *texture = NULL; + magic::Texture2D *texture = nullptr; magic::Vector2 coord0; magic::Vector2 coord1; }; @@ -46,8 +47,8 @@ namespace interface struct TextureAtlasCache { - bool valid = false; - magic::Texture2D *texture = NULL; + magic::SharedPtr image; + magic::Texture2D *texture = nullptr; magic::IntVector2 segment_resolution; magic::IntVector2 total_segments; sv_ segments;