idx_t get_next_tileid( idx_t const previd, idx_t const * const tile_dims, idx_t const nmodes, idx_t const iter_mode, idx_t const mode_idx) { idx_t maxid = 1; idx_t coords[MAX_NMODES]; for(idx_t m=0; m < nmodes; ++m) { coords[m] = 0; maxid *= tile_dims[m]; } if(previd == TILE_BEGIN) { coords[iter_mode] = mode_idx; return get_tile_id(tile_dims, nmodes, coords); } /* check for out of bounds */ if(previd >= maxid) { return TILE_ERR; } /* convert previd to coords */ fill_tile_coords(tile_dims, nmodes, previd, coords); /* overflowing this mode means TILE_END */ idx_t const overmode = (iter_mode == 0) ? 1 : 0; /* increment least significant mode (unless we're iterating over it) and * propagate overflows */ idx_t pmode = (iter_mode == nmodes-1) ? nmodes-2 : nmodes-1; ++coords[pmode]; while(coords[pmode] == tile_dims[pmode]) { if(pmode == overmode) { return TILE_END; } /* overflow this one too and move on */ coords[pmode] = 0; --pmode; /* we don't alter the mode we are iterating over */ if(pmode == iter_mode) { /* XXX: checking for overmode should catch this */ assert(pmode > 0); /* if we aren't at the end just skip over it */ --pmode; } /* we're now at a valid mode, carry over previous overflow */ ++coords[pmode]; } return get_tile_id(tile_dims, nmodes, coords); }
void EditorInputCenter::update_tile_selection() { Rectf select = tile_drag_rect(); auto tiles = Editor::current()->tileselect.tiles.get(); auto tilemap = dynamic_cast<TileMap*>(Editor::current()->layerselect.selected_tilemap); if ( !tilemap ) { return; } tiles->tiles.clear(); tiles->width = select.get_width() + 1; tiles->height = select.get_height() + 1; int w = tilemap->get_width(); int h = tilemap->get_height(); for (int y = select.p1.y; y <= select.p2.y; y++) { for (int x = select.p1.x; x <= select.p2.x; x++) { if ( x < 0 || y < 0 || x >= w || y >= h) { tiles->tiles.push_back(0); } else { tiles->tiles.push_back(tilemap->get_tile_id(x, y)); } } } }
/* * Use only 1 tile on a tensor with 'MAX_NMODES' modes. id should be 0. */ CTEST2(tile_traverse, get_tile_id_zero) { __fill_arr(data->dims, MAX_NMODES, 1); __fill_arr(data->coords, MAX_NMODES, 0); /* one tile_traverse, id should always be 0 */ ASSERT_EQUAL(0, get_tile_id(data->dims, MAX_NMODES, data->coords)); }
/* Our tiles art are stored as spritemaps. Drawing * the tiles means drawing one of the frames in * one of the spritemaps we use for each layer. */ static void draw_tile(struct game* game, struct sprite* tilemap, enum map_layer layer_name, struct tile_pos pos) { draw_sprite_frame(tilemap, TILE_SIZE * (pos.col) - game->camera.x, TILE_SIZE * (pos.row) - game->camera.y, get_tile_id(game->map_spec.data, game->layers[layer_name], pos) - 1); }
tilemap_component::tile_id_t tilemap_component::try_get_tile_id(unsigned x, unsigned y) const { if(x < p_num_tiles.x() && y < p_num_tiles.y()) { return get_tile_id(x, y); } else { return -1; } }
void TileMap::change_all(uint32_t oldtile, uint32_t newtile) { for (size_t x = 0; x < get_width(); x++) { for (size_t y = 0; y < get_height(); y++) { if (get_tile_id(x,y) != oldtile) continue; change(x,y,newtile); } } }
/* * Test get_tile_id on out of bounds values. */ CTEST2(tile_traverse, get_tile_id_oob) { idx_t const nmodes = MAX_NMODES; for(idx_t m=0; m < nmodes; ++m) { data->dims[m] = m+1; data->coords[m] = m+1; } /* check out of bounds */ ASSERT_EQUAL(TILE_ERR, get_tile_id(data->dims, nmodes, data->coords)); for(idx_t m=0; m < nmodes; ++m) { data->coords[m] = m; } ASSERT_NOT_EQUAL(TILE_ERR, get_tile_id(data->dims, nmodes, data->coords)); /* check each mode individually */ for(idx_t m=0; m < nmodes; ++m) { ++data->coords[m]; ASSERT_EQUAL(TILE_ERR, get_tile_id(data->dims, nmodes, data->coords)); --data->coords[m]; } }
CTEST2(tile_traverse, fill_tile_coords) { idx_t const nmodes = 4; idx_t const nthreads = 4; idx_t ntiles = 1; for(idx_t m=0; m < nmodes; ++m) { data->dims[m] = nthreads; ntiles *= nthreads; } for(idx_t t=0; t < ntiles; ++t) { fill_tile_coords(data->dims, nmodes, t, data->coords); ASSERT_EQUAL(t, get_tile_id(data->dims, nmodes, data->coords)); } }
/* * Test get_tile_id on a 3D problem with a prime number of threads. */ CTEST2(tile_traverse, get_tile_id_3d) { idx_t const nmodes = 3; idx_t const nthreads = 7; __fill_arr(data->dims, nmodes, nthreads); idx_t id = 0; for(idx_t m1=0; m1 < nthreads; ++m1) { data->coords[0] = m1; for(idx_t m2=0; m2 < nthreads; ++m2) { data->coords[1] = m2; for(idx_t m3=0; m3 < nthreads; ++m3) { data->coords[2] = m3; ASSERT_EQUAL(id, get_tile_id(data->dims, nmodes, data->coords)); ++id; } } } }
/* * Do a traversal of the space with non-uniform tile dimensions. */ CTEST2(tile_traverse, tile_weird_dim) { idx_t const nmodes = 5; for(idx_t m=0; m < nmodes; ++m) { data->dims[m] = m+1; } for(idx_t m=0; m < nmodes; ++m) { /* empty tiles */ __fill_arr(data->coords, nmodes, 0); /* the number of tiles that the traversal should go through */ idx_t ntiles = 1; for(idx_t m2=0; m2 < nmodes; ++m2) { if(m2 != m) { ntiles *= data->dims[m2]; } } /* now ensure every idx in the mode sees that it is the end */ for(idx_t d=0; d < data->dims[m]; ++d) { idx_t startid = get_next_tileid(TILE_BEGIN, data->dims, nmodes, m, d); /* compute start id manually */ data->coords[m] = d; idx_t const manual = get_tile_id(data->dims, nmodes, data->coords); ASSERT_EQUAL(manual, startid); /* Iterate over all tiles and also check last+1. Start from one because * TILE_BEGIN has already happened. */ idx_t id = startid; for(idx_t t=1; t < ntiles; ++t) { id = get_next_tileid(id, data->dims, nmodes, m, d); ASSERT_NOT_EQUAL(startid, id); ASSERT_NOT_EQUAL(TILE_END, id); } id = get_next_tileid(id, data->dims, nmodes, m, d); ASSERT_EQUAL(TILE_END, id); } } }
/* * Test TILE_BEGIN functionality. */ CTEST2(tile_traverse, get_tile_id_begin) { idx_t const nmodes = 6; idx_t const nthreads = 3; __fill_arr(data->dims, nmodes, nthreads); for(idx_t m=0; m < nmodes; ++m) { for(idx_t d=0; d < data->dims[m]; ++d) { /* get starting id */ idx_t const b_id = get_next_tileid(TILE_BEGIN, data->dims, nmodes, m, d); /* now do it ourselves */ __fill_arr(data->coords, nmodes, 0); data->coords[m] = d; idx_t const c_id = get_tile_id(data->dims, nmodes, data->coords); ASSERT_EQUAL(c_id, b_id); } } }
/* * Test TILE_END functionality. */ CTEST2(tile_traverse, get_tile_id_end) { idx_t const nmodes = MAX_NMODES; idx_t const nthreads = 3; __fill_arr(data->dims, nmodes, nthreads); for(idx_t m=0; m < nmodes; ++m) { /* very last tile */ __fill_arr(data->coords, nmodes, nthreads-1); /* now ensure every idx in the mode sees that it is the end */ for(idx_t d=0; d < data->dims[m]; ++d) { /* now do it ourselves */ data->coords[m] = d; idx_t const t_id = get_tile_id(data->dims, nmodes, data->coords); idx_t const b_id = get_next_tileid(t_id, data->dims, nmodes, m, d); ASSERT_EQUAL(TILE_END, b_id); } } }
/* * Test get_tile_id on a 3D problem with weird dimensions. */ CTEST2(tile_traverse, get_tile_id_weird_dim) { idx_t const nmodes = 3; for(idx_t m=0; m < nmodes; ++m) { data->dims[m] = m+1; } idx_t id = 0; for(idx_t m1=0; m1 < data->dims[0]; ++m1) { data->coords[0] = m1; for(idx_t m2=0; m2 < data->dims[1]; ++m2) { data->coords[1] = m2; for(idx_t m3=0; m3 < data->dims[2]; ++m3) { data->coords[2] = m3; ASSERT_EQUAL(id, get_tile_id(data->dims, nmodes, data->coords)); ++id; } } } }
void SectorParser::parse_old_format(const ReaderMapping& reader) { m_sector.name = "main"; reader.get("gravity", m_sector.gravity); std::string backgroundimage; if (reader.get("background", backgroundimage) && (backgroundimage != "")) { if (backgroundimage == "arctis.png") backgroundimage = "arctis.jpg"; if (backgroundimage == "arctis2.jpg") backgroundimage = "arctis.jpg"; if (backgroundimage == "ocean.png") backgroundimage = "ocean.jpg"; backgroundimage = "images/background/" + backgroundimage; if (!PHYSFS_exists(backgroundimage.c_str())) { log_warning << "Background image \"" << backgroundimage << "\" not found. Ignoring." << std::endl; backgroundimage = ""; } } float bgspeed = .5; reader.get("bkgd_speed", bgspeed); bgspeed /= 100; Color bkgd_top, bkgd_bottom; int r = 0, g = 0, b = 128; reader.get("bkgd_red_top", r); reader.get("bkgd_green_top", g); reader.get("bkgd_blue_top", b); bkgd_top.red = static_cast<float> (r) / 255.0f; bkgd_top.green = static_cast<float> (g) / 255.0f; bkgd_top.blue = static_cast<float> (b) / 255.0f; reader.get("bkgd_red_bottom", r); reader.get("bkgd_green_bottom", g); reader.get("bkgd_blue_bottom", b); bkgd_bottom.red = static_cast<float> (r) / 255.0f; bkgd_bottom.green = static_cast<float> (g) / 255.0f; bkgd_bottom.blue = static_cast<float> (b) / 255.0f; if(backgroundimage != "") { auto background = std::make_shared<Background>(); background->set_image(backgroundimage, bgspeed); m_sector.add_object(background); } else { auto gradient = std::make_shared<Gradient>(); gradient->set_gradient(bkgd_top, bkgd_bottom); m_sector.add_object(gradient); } std::string particlesystem; reader.get("particle_system", particlesystem); if(particlesystem == "clouds") m_sector.add_object(std::make_shared<CloudParticleSystem>()); else if(particlesystem == "snow") m_sector.add_object(std::make_shared<SnowParticleSystem>()); else if(particlesystem == "rain") m_sector.add_object(std::make_shared<RainParticleSystem>()); Vector startpos(100, 170); reader.get("start_pos_x", startpos.x); reader.get("start_pos_y", startpos.y); auto spawn = std::make_shared<SpawnPoint>(); spawn->pos = startpos; spawn->name = "main"; m_sector.spawnpoints.push_back(spawn); m_sector.music = "chipdisko.ogg"; // skip reading music filename. It's all .ogg now, anyway /* reader.get("music", music); */ m_sector.music = "music/" + m_sector.music; int width = 30, height = 15; reader.get("width", width); reader.get("height", height); std::vector<unsigned int> tiles; if(reader.get("interactive-tm", tiles) || reader.get("tilemap", tiles)) { auto tileset = TileManager::current()->get_tileset(m_sector.level->get_tileset()); auto tilemap = std::make_shared<TileMap>(tileset); tilemap->set(width, height, tiles, LAYER_TILES, true); // replace tile id 112 (old invisible tile) with 1311 (new invisible tile) for(size_t x=0; x < tilemap->get_width(); ++x) { for(size_t y=0; y < tilemap->get_height(); ++y) { uint32_t id = tilemap->get_tile_id(x, y); if(id == 112) tilemap->change(x, y, 1311); } } if (height < 19) tilemap->resize(width, 19); m_sector.add_object(tilemap); } if(reader.get("background-tm", tiles)) { auto tileset = TileManager::current()->get_tileset(m_sector.level->get_tileset()); auto tilemap = std::make_shared<TileMap>(tileset); tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false); if (height < 19) tilemap->resize(width, 19); m_sector.add_object(tilemap); } if(reader.get("foreground-tm", tiles)) { auto tileset = TileManager::current()->get_tileset(m_sector.level->get_tileset()); auto tilemap = std::make_shared<TileMap>(tileset); tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false); // fill additional space in foreground with tiles of ID 2035 (lightmap/black) if (height < 19) tilemap->resize(width, 19, 2035); m_sector.add_object(tilemap); } // read reset-points (now spawn-points) ReaderMapping resetpoints; if(reader.get("reset-points", resetpoints)) { auto iter = resetpoints.get_iter(); while(iter.next()) { if(iter.get_key() == "point") { Vector sp_pos; if(reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y)) { auto sp = std::make_shared<SpawnPoint>(); sp->name = "main"; sp->pos = sp_pos; m_sector.spawnpoints.push_back(sp); } } else { log_warning << "Unknown token '" << iter.get_key() << "' in reset-points." << std::endl; } } } // read objects ReaderCollection objects; if(reader.get("objects", objects)) { for(auto const& obj : objects.get_objects()) { auto object = parse_object(obj.get_name(), obj.get_mapping()); if(object) { m_sector.add_object(object); } else { log_warning << "Unknown object '" << obj.get_name() << "' in level." << std::endl; } } } // add a camera auto camera_ = std::make_shared<Camera>(&m_sector, "Camera"); m_sector.add_object(camera_); m_sector.update_game_objects(); if (m_sector.solid_tilemaps.empty()) { log_warning << "sector '" << m_sector.name << "' does not contain a solid tile layer." << std::endl; } fix_old_tiles(); m_sector.update_game_objects(); }
uint32_t TileMap::get_tile_id_at(const Vector& pos) const { Vector xy = (pos - offset) / 32; return get_tile_id(int(xy.x), int(xy.y)); }
const Tile* TileMap::get_tile(int x, int y) const { uint32_t id = get_tile_id(x, y); return tileset->get(id); }
void EditorInputCenter::fill() { auto tilemap = dynamic_cast<TileMap*>(Editor::current()->layerselect.selected_tilemap); if (! tilemap) { return; } // The tile that is going to be replaced: Uint32 replace_tile = tilemap->get_tile_id(hovered_tile.x, hovered_tile.y); if (Editor::current()->tileselect.tiles->pos(0, 0) == tilemap->get_tile_id(hovered_tile.x, hovered_tile.y)) { // Replacing by the same tiles shouldn't do anything. return; } std::vector<Vector> pos_stack; pos_stack.clear(); pos_stack.push_back(hovered_tile); auto tiles = Editor::current()->tileselect.tiles.get(); // Passing recursively trough all tiles to be replaced... while (pos_stack.size()) { if (pos_stack.size() > 1000000) { log_warning << "More than 1'000'000 tiles in stack to fill, STOP" << std::endl; return; } Vector pos = pos_stack[pos_stack.size() - 1]; Vector tpos = pos - hovered_tile; // Tests for being inside tilemap: if ( pos.x < 0 || pos.y < 0 || pos.x >= tilemap->get_width() || pos.y >= tilemap->get_height()) { pos_stack.pop_back(); continue; } input_tile(pos, tiles->pos(tpos.x, tpos.y)); Vector pos_; // Going left... pos_ = pos + Vector(-1, 0); if (pos_.x >= 0) { if (replace_tile == tilemap->get_tile_id(pos_.x, pos_.y) && replace_tile != tiles->pos(tpos.x - 1, tpos.y)) { pos_stack.push_back( pos_ ); continue; } } // Going right... pos_ = pos + Vector(1, 0); if (pos_.x < tilemap->get_width()) { if (replace_tile == tilemap->get_tile_id(pos_.x, pos_.y) && replace_tile != tiles->pos(tpos.x + 1, tpos.y)) { pos_stack.push_back( pos_ ); continue; } } // Going up... pos_ = pos + Vector(0, -1); if (pos_.y >= 0) { if (replace_tile == tilemap->get_tile_id(pos_.x, pos_.y) && replace_tile != tiles->pos(tpos.x, tpos.y - 1)) { pos_stack.push_back( pos_ ); continue; } } // Going down... pos_ = pos + Vector(0, 1); if (pos_.y < tilemap->get_height()) { if (replace_tile == tilemap->get_tile_id(pos_.x, pos_.y) && replace_tile != tiles->pos(tpos.x, tpos.y + 1)) { pos_stack.push_back( pos_ ); continue; } } // When tiles on each side are already filled or occupied by another tiles, it ends. pos_stack.pop_back(); } }
idx_t * tt_densetile( sptensor_t * const tt, idx_t const * const tile_dims) { timer_start(&timers[TIMER_TILE]); idx_t const nmodes = tt->nmodes; /* * Count tiles and compute their dimensions. */ idx_t ntiles = 1; for(idx_t m=0; m < nmodes; ++m) { ntiles *= tile_dims[m]; } /* the actual number of indices to place in each tile */ idx_t tsizes[MAX_NMODES]; for(idx_t m=0; m < nmodes; ++m) { tsizes[m] = SS_MAX(tt->dims[m] / tile_dims[m], 1); } /* We'll copy the newly tiled non-zeros into this one, then copy back */ sptensor_t * newtt = tt_alloc(tt->nnz, tt->nmodes); /* * Count of non-zeros per tile. We use +1 because after a prefix sum, this * becomes a pointer into the non-zeros for each tile (e.g., csr->row_ptr). */ idx_t * tcounts_global = splatt_malloc((ntiles+1) * sizeof(*tcounts_global)); for(idx_t t=0; t < ntiles+1; ++t) { tcounts_global[t] = 0; } /* * A matrix of thread-local counters. */ int const nthreads = splatt_omp_get_max_threads(); idx_t * * tcounts_thread = splatt_malloc( (nthreads+1) * sizeof(*tcounts_thread)); /* After the prefix sum, the global counter will have the sum of all nnz in * each tile (across threads), and thus can be returned. */ tcounts_thread[nthreads] = tcounts_global; /* partition the non-zeros */ idx_t * thread_parts = partition_simple(tt->nnz, nthreads); #pragma omp parallel { int const tid = splatt_omp_get_thread_num(); idx_t const nnz_start = thread_parts[tid]; idx_t const nnz_end = thread_parts[tid+1]; /* allocate / initialize thread-local counters */ tcounts_thread[tid] = splatt_malloc(ntiles * sizeof(**tcounts_thread)); for(idx_t tile=0; tile < ntiles; ++tile) { tcounts_thread[tid][tile] = 0; } #pragma omp barrier /* offset by 1 to make prefix sum easy */ idx_t * tcounts_local = tcounts_thread[tid+1]; /* count tile sizes (in nnz) */ idx_t coord[MAX_NMODES]; for(idx_t x=nnz_start; x < nnz_end; ++x) { for(idx_t m=0; m < nmodes; ++m) { /* capping at dims-1 fixes overflow when dims don't divide evenly */ coord[m] = SS_MIN(tt->ind[m][x] / tsizes[m], tile_dims[m]-1); } idx_t const id = get_tile_id(tile_dims, nmodes, coord); assert(id < ntiles); ++tcounts_local[id]; } #pragma omp barrier #pragma omp single { /* prefix sum for each tile */ for(idx_t tile=0; tile < ntiles; ++tile) { for(int thread=0; thread < nthreads; ++thread) { tcounts_thread[thread+1][tile] += tcounts_thread[thread][tile]; } /* carry over to next tile */ if(tile < (ntiles-1)) { tcounts_thread[0][tile+1] += tcounts_thread[nthreads][tile]; } } } /* implied barrier */ /* grab my starting indices now */ tcounts_local = tcounts_thread[tid]; /* * Rearrange old tensor into new tiled one. */ for(idx_t x=nnz_start; x < nnz_end; ++x) { for(idx_t m=0; m < nmodes; ++m) { coord[m] = SS_MIN(tt->ind[m][x] / tsizes[m], tile_dims[m]-1); } /* offset by 1 to make prefix sum easy */ idx_t const id = get_tile_id(tile_dims, nmodes, coord); assert(id < ntiles); idx_t const newidx = tcounts_local[id]++; newtt->vals[newidx] = tt->vals[x]; for(idx_t m=0; m < nmodes; ++m) { newtt->ind[m][newidx] = tt->ind[m][x]; } } splatt_free(tcounts_local); } /* end omp parallel */ /* copy tiled data into old struct */ par_memcpy(tt->vals, newtt->vals, tt->nnz * sizeof(*tt->vals)); for(idx_t m=0; m < nmodes; ++m) { par_memcpy(tt->ind[m], newtt->ind[m], tt->nnz * sizeof(**tt->ind)); } /* shift counts to the right by 1 to make proper pointer */ memmove(tcounts_global+1, tcounts_global, ntiles * sizeof(*tcounts_global)); tcounts_global[0] = 0; assert(tcounts_global[ntiles] == tt->nnz); tt_free(newtt); splatt_free(tcounts_thread); splatt_free(thread_parts); timer_stop(&timers[TIMER_TILE]); return tcounts_global; }