After a little reading, I found that any AOT Query object can be exposed as an OData Source. My only issue was that the data that I needed came from a complex collection of tables (read unrolling dimensions again). As a result, I decided to create an in-memory table and populate it using a stored procedure from the database. That part was simple enough (just remember that the database calling method must be marked as server.
In general, I prefer to work with in-memory temp tables. This is because tempDB is one of the busiest places in SQL Server. From what I've gathered, every query that is executed against more that 1 table is resolved there. So, the general advice is, if you don't have to use tempDB, don't.
The only issue that I had was how to use a query on using an empty temp table as a data source. It turns out that AOT Query objects are based on QueryRun. Literally, the class declaration reads:
public class QueryRun extends ObjectRun
So, to populate the query, all that you need is to call setCursor() on the init method.
public void init()
{
super();
this.setCursor(MyInMeomryTempTable::populate());
}
From there, exposing the Query was a simple matter of going to Organization Administration -> Setup -> Document Management and opening Document data sources. Once there, simple add a new document data source. The module is arbitrary and is just used for organizing the list of data sources. Data Source Type needs to be Query Reference. Then, select the name of the Query Object that you created and check Activated. Description is optional.
Links:
https://blogs.msdn.microsoft.com/aif/2011/08/23/odata-query-service/
http://immerhier.com/connect-microsoft-power-bi-to-ax-2012-odata-query-service/
http://www.uxceclipse.com/odata-powerquery-and-microsoft-dynamics-ax-2012-data-sources/