I have a very simple rule-of-thumb: If you need to stop and think more than ~15 seconds when coding some piece of code (say, a function or complex loop, etc.), then that piece of code needs a comment.
It works really good for me. Next time you, or someone at your level of understanding of the overall codebase, runs into that piece of code, (s)he will immediately see why was it done the way it's done.