Here's how to figure it out from the Satoshi client code:
The IMPLEMENT_SERIALIZE macro is used to both store transactions on disk and to serialize them into a byte-array that can be hashed.
For class CTransaction, that looks like:
nVersion = this->nVersion;
READWRITE is a wrapper that is overloaded to Do The Right Thing for all the types bitcoin deals with; for complex types like CTxOut, IMPLEMENT_SERIALIZE is (essentially) called recursively.
Expand out all of the types and, assuming I didn't screw up (always an iffy assumption), it looks like a CTransaction is serialized as:
vin.size (vectors are serialized as a compressed count immediately followed by their contents)
vin.prevout (vin->prevout->hash followed immediately by vin->prevout->n, as 36 bytes)
vin.scriptSig (CScripts are serialized as a vector of bytes)
... repeated for each vin
... repeated for each vout
String all those bytes together, SHA256 them twice, and you should get the transaction hash for the merkle chain.