int Grid::pixelIntersect(const int2 &screen_pos, bool (*pixelTest)(const ObjectDef&, const int2&), int flags) const { IRect grid_box(0, 0, m_size.x, m_size.y); int best = -1; FBox best_box; for(int y = grid_box.min.y; y < grid_box.max.y; y++) { const int2 &row_rect = m_row_rects[y]; if(row_rect.x >= screen_pos.y || row_rect.y <= screen_pos.y) continue; for(int x = grid_box.min.x; x < grid_box.max.x; x++) { int node_id = nodeAt(int2(x, y)); const Node &node = m_nodes[node_id]; if(!flagTest(node.obj_flags, flags) || !node.rect.isInside(screen_pos)) continue; if(node.is_dirty) updateNode(node_id); const Object *objects[node.size]; int count = extractObjects(node_id, objects, -1, flags); for(int n = 0; n < count; n++) if(objects[n]->rect().isInside(screen_pos) && pixelTest(*objects[n], screen_pos)) { if(best == -1 || drawingOrder(objects[n]->bbox, best_box) == 1) { best = objects[n] - &m_objects[0]; best_box = objects[n]->bbox; } } } } return best; }
Intersection WorldViewer::pixelIntersect(const int2 &screen_pos, const FindFilter &filter) const { Intersection out; FBox out_bbox; if(filter.flags() & Flags::tile) { const TileMap &tile_map = m_world->tileMap(); vector<int> inds; tile_map.findAll(inds, IRect(screen_pos, screen_pos + int2(1, 1)), filter.flags() | Flags::visible); for(int i = 0; i < (int)inds.size(); i++) { const auto &desc = tile_map[inds[i]]; FBox bbox = desc.bbox; if(out.empty() || drawingOrder(bbox, out_bbox) == 1) if(desc.ptr->testPixel(screen_pos - worldToScreen((int3)bbox.min))) { out = ObjectRef(inds[i], false); out_bbox = bbox; } } } if(filter.flags() & Flags::entity) { int ignore_index = m_world->filterIgnoreIndex(filter); for(int n = 0; n < (int)m_entities.size(); n++) { const Entity *entity = refEntity(n); if(!entity || !m_occluder_config.isVisible(m_entities[n].occluder_id) || !Flags::test(entity->flags(), filter.flags()) || n == ignore_index) continue; if(!entity->testPixel(screen_pos)) continue; FBox bbox = entity->boundingBox(); //TODO: check this if(out.empty() || drawingOrder(bbox, out_bbox) == 1) { out = ObjectRef(n, true); out_bbox = bbox; } } } if(out.empty()) return Intersection(); return Intersection(out, intersection(screenRay(screen_pos), out_bbox)); }
void EntitiesEditor::findVisible(vector<int> &out, const IRect &rect) const { out.clear(); std::set<int> indices; vector<int> temp; temp.reserve(100); enum { block_size = 64 }; for(int gy = m_selection.min.y; gy <= m_selection.max.y; gy += block_size) for(int gx = m_selection.min.x; gx <= m_selection.max.x; gx += block_size) { temp.clear(); IRect block_rect(int2(gx, gy), min(int2(gx + block_size, gy + block_size), m_selection.max)); m_entity_map.findAll(temp, block_rect, Flags::all | Flags::visible); for(int n = 0; n < (int)temp.size(); n++) if(indices.find(temp[n]) != indices.end()) { temp[n--] = temp.back(); temp.pop_back(); } for(int y = gy, endy = min(m_selection.max.y, gy + block_size); y <= endy; y += 2) for(int x = m_selection.min.x, endx = min(gx + block_size, m_selection.max.x); x <= endx; x += 2) { int2 point(x, y); if(point.x + 1 <= m_selection.max.x && (y & 1)) point.x++; bool tile_isected = false; int tile_idx = -1; for(int i = 0; i < (int)temp.size(); i++) { const auto &object = m_entity_map[temp[i]]; bool is_trigger = object.ptr->typeId() == EntityId::trigger; if(!is_trigger && !object.ptr->testPixel(point)) continue; if(is_trigger && !object.ptr->currentScreenRect().isInside(point)) continue; if(!tile_isected) { tile_idx = m_tile_map.pixelIntersect(int2(x, y), Flags::walkable_tile | Flags::visible); tile_isected = true; } if(tile_idx != -1) { if(drawingOrder(m_tile_map[tile_idx].bbox, m_entity_map[temp[i]].bbox) == 1) continue; } indices.insert(temp[i]); temp[i--] = temp.back(); temp.pop_back(); } } } out.insert(out.begin(), indices.begin(), indices.end()); }
bool OccluderConfig::update(const FBox &bbox) { //TODO: hiding when close to a door/window FBox test_box(bbox.min.x, bbox.min.y + 1.0f, bbox.min.z, bbox.max.x, 256, bbox.max.z); float3 mid_point = asXZY(test_box.center().xz(), bbox.min.y + 2.0f); bool vis_changed = update(); vector<int> temp; temp.reserve(256); IRect test_rect = (IRect)worldToScreen(bbox); const Grid &grid = m_map.m_grid; grid.findAll(temp, test_rect); vector<int> temp2; temp2.reserve(256); PodArray<int> overlaps(m_map.size()); memset(overlaps.data(), 0, m_map.size() * sizeof(int)); for(int i = 0; i < (int)temp.size(); i++) { const auto &object = grid[temp[i]]; if(object.occluder_id == -1) continue; const OccluderMap::Occluder &occluder = m_map[object.occluder_id]; int order = drawingOrder(object.bbox, bbox); if(order == 1) overlaps[object.occluder_id] = order; } for(int n = 0; n < (int)m_states.size(); n++) { bool is_overlapping = false; const OccluderMap::Occluder &occluder = m_map[n]; if(overlaps[n] == 1) { FBox bbox_around(bbox.min - float3(16, 0, 16), bbox.max + float3(16, 0, 16)); bbox_around.min.y = 0; bbox_around.max.y = Grid::max_height; temp2.clear(); grid.findAll(temp2, bbox_around); FBox local_box = FBox(); for(int i = 0; i < (int)temp2.size(); i++) { const auto &object = grid[temp2[i]]; if(object.occluder_id == n) local_box = local_box.empty()? object.bbox : sum(local_box, object.bbox); } is_overlapping = local_box.min.y > mid_point.y; } if(is_overlapping != m_states[n].is_overlapping) { m_states[n].is_overlapping = is_overlapping; vis_changed = true; } } if(!vis_changed) return false; for(int n= 0; n < (int)m_states.size(); n++) m_states[n].is_visible = !m_states[n].is_overlapping; //TODO: isUnder can be precomputed for(int n = 0; n < (int)m_states.size(); n++) { if(!m_states[n].is_visible) continue; for(int i = 0; i < (int)m_states.size(); i++) if(!m_states[i].is_visible && m_map.isUnder(i, n)) { m_states[n].is_visible = false; break; } } return true; }