First of all, the playerMesh.material does not get set when it is called where it is (after the co-routine. This only works when I put the assignment to .material after .enabled in ShowPlayerCoroutine().
This makes me wonder how coroutines work. I know that in coroutines, they will only return when a yield expression or end of function is encountered. However, since I'm not doing "yield StartCoroutine( ShowPlayerCoroutine() )", what does it do? I am told that StartCoroutine( ShowPlayerCoroutine() ) returns immediately, but does this mean that both Start() and ShowPlayerCoroutine() are running concurrently? I just can't visualize what the instruction pointer is doing in this case. Note that I am mainly a C++ programmer, and there is no similar construct in C++ for this behavior that I am aware of.
When you call StartCoroutine, you trigger what is almost like a process on a separate thread. It's not a separate thread however, so there's no concerns about thread-safety and locking access to variables. In addition, the code executing in your coroutine blocks execution of everything else completely (just as code in an Update() function would), except when it is told to pause and release control with a yield function.
So, when you start a coroutine, it starts running immediately, blocking the current execution of the function which started it, until it hits the first yield statement. The yield statement then causes execution to pause and the current position within the coroutine is stored. Execution then returns to the calling function. The value returned by yield specifies when the coroutine should start back up again - the smallest amount of time is after all the current frame's scripts have elapsed.
This means when you call
StartCoroutine( ShowPlayerCoroutine() );
your coroutine starts executing (blocking the Start() function), however on its first line, it reaches the yield function, and so returns - allowing the Start() function to resume and complete.
When it yielded - because of the value returned, it queued itself up to continue executing after "playerSpawnTime" seconds have elapsed. So the game continues playing for until that number of seconds have elapsed, at which point the coroutine will start back up again from the point at which it stopped.
Because Unity is not multithreaded (except for certain streaming operations), none of your code is ever executing at the same time. It is executed in a certain pre-determined order based on the queue of Update() events, FixedUpdate() events, coroutines which are now due to resume, and any other events which may be due to execute.
They are not executed concurrently. They go into a Stack/Queue and get executed in regular intervals.
The execution is halted in every yield and resumes from there when the yield that halted it ends.
WaitForSeconds in this case is like telling it to only execute the rest of the code when WaitForSeconds has ended its own execution.
Is that the actual code that's not working? That's probably because you're assingning the material while the Mesh is disabled. Not sure about that. :-/