Yes, you can still have trees with an object store. The object identifier can be wide... for example, the NVMe standard I believe uses 128-bit 'keys'. Sigh. Slashdot really needs to fix its broken lameness filter, I can't even use brackets to represent bit spaces.
So a filesystem can be organized using keys like this for the inode:
parent_object_key, object_key
An this for the file content:
object_key, file_offset|extent_size
For example, a file block could easily be encoded as a 64-bit integer byte offset, with a 63 bit positive offset space, and an extent size encoded in the low 6 bits (radix 1 to radix 63, allowing extents up to (1 63) bytes. Since the low 6 bits are used for the extent, the minimum extent size would be 64 bytes. The negative key space could be used for auxillary records associated with the file or directory. HAMMER2 uses this very method to encode its radix trees, allowing each recursion to use a variable-sized extent and to represent any 64-bit sub-range within the hash space (but H2 runs on top of a normal block device, it doesn't extent the encoding down to the device).
A set of directory entries could be encoded as follows, where [object_key] is the inode number of the directory.
object_key, filename_hash_key
Though doing so would almost certainly not be optimal since directory entries are very small.
Inode numbers wind up just being object keys.
This is readily doable... actually, this sort of methodology has been used many times before. I did a turnkey system 20 years ago that used this method to create a simple-stupid filesystem for a NOR flash filesystem.
The problem with this methodology is that if done at the kernel/filesystem-level, it requires the underlying storage to directly implement the key-store, as well as to support the key width required by the filesystem.... which seriously restricts what the filesystem can be built on top of.
-Matt