I don't know if the approach I discovered is the same as the one Graham describes above, but the way I solved this recently was to begin as above by choosing one edge to be horizontal and normalising it and calling the result a right vector.
I then reversed the face normal and called the result the look vector.
I then crossed them to get an up vector.
Armed with a look, right and up vector, I generated a view matrix which I applied to each point with D3DXVec3TransformCoord(), storing the results in a seperate buffer.
By ignoring the Z components of the result, the result was a collection of X,Y points which defined the face as if the camera was looking straight at it, with the X,Y's in a normalized (0-1) range.
Since I was working on a system where there was a 1-1 correspondance between world units and texture sizes, these X,Y's were almost the U,V's - I just had to find the lowest X and translate all the U's by it and the same for the Y and V to align the texture top left on the shape.
This doesn't address the skewed square issue you mention above, but it worked really well otherwise.
// heavily drawn from Game Programming with Direct3D 9 (Frank Luna)D3DXMATRIX MakeViewMatrix(D3DXVECTOR3 Look,D3DXVECTOR3 Right,D3DXVECTOR3 Up,D3DXVECTOR3 Pos){ D3DXVec3Normalize(&Look,&Look); D3DXVec3Cross(&Up,&Look,&Right); D3DXVec3Normalize(&Up,&Up); D3DXVec3Cross(&Right,&Up,&Look); D3DXVec3Normalize(&Right,&Right); D3DXMATRIX M; M(0,0)=Right.x; M(0,1)=Up.x; M(0,2)=Look.x; M(0,3)=0.0f; M(1,0)=Right.y; M(1,1)=Up.y; M(1,2)=Look.y; M(1,3)=0.0f; M(2,0)=Right.z; M(2,1)=Up.z; M(2,2)=Look.z; M(2,3)=0.0f; M(3,0)=-D3DXVec3Dot(&Right,&Pos); M(3,1)=-D3DXVec3Dot(&Up,&Pos); M(3,2)=-D3DXVec3Dot(&Look,&Pos); M(3,3)=1.0f; return M;}void snipped_from_my_code(){ D3DXVECTOR3 Pos=Shapes[Select.Index].GetFaceCenter(Select.Face); D3DXVECTOR3 Norm=Shapes[Select.Index].GetFaceNormal(Select.Face,Select.Vertex); if(D3DXVec3Length(&Norm)==0) { InfoText="Normal from vertex is ambiguous"; Mode=ModeInformation; return; } D3DXVECTOR3 Right=Shapes[Select.Index].GetFaceLateral(Select.Face,Select.Vertex); if(D3DXVec3Length(&Right)==0) { InfoText="Lateral from vertex is ambiguous"; Mode=ModeInformation; return; } D3DXVECTOR3 Look=Norm*-1.0f; D3DXVECTOR3 Up; D3DXVec3Cross(&Up,&Look,&Right); D3DXMATRIX M=MakeViewMatrix(Look,Right,Up,Pos); std::vector<D3DXVECTOR3> Output; Shapes[Select.Index].TransformFace(Select.Face,M,Output); float MinX=FLT_MAX,MinY=FLT_MAX; for(WORD I=0;I<Output.size();++I) { if(Output.x<MinX) MinX=Output.x; if(Output.y<MinY) MinY=Output.y; } for(WORD I=0;I<Output.size();++I) { Shapes[Select.Index].SetUV(Select.Face,I,Output.x-MinX,(Output.y*-1.0f)-MinY); } }}