This is pretty disappointing. So outside of the elaborate HashMap scheme you outlined above, we are forced to flatten any custom object data we'd like to inject into emails into contact-keyed structures?
Not any custom object data: 2nd-level relationships via Company/Account are fine (as are Oppties of course). 2nd-level via Lead/Contact, yes, the data needs to be flattened to the 1st level in order to be read from Velocity.
I've run into this before and we were able to overcome the issue using Apex and sending the email out of Salesforce, rather than Marketo. Flattening the object into a custom object in Marketo might work as well, but if you have a good Salesforce Developer you've got another potential option.
Hey Stephen, do you have any more information on how you handled this via Apex? I would be interested in learning more, in case this could help us out with a similar situation.