Go to QuArK Web Site
Texture Scales, Threepoints, etc.
Updated 05 Apr 2018
Upper levels:
QuArK Information Base
4. The Source Code
4.4. Specific Topics

 4.4.3. Texture Scales, Threepoints, etc.

 [ Prev - Up - Next ] 

Enhanced Texture Positioning

Q1/2/3-engine games represent faces by specifying three points that lie on the plane of the face (the initial three three-tuples in the representation of a face in the map-format). QuArK's 'enhanced texture positioning' also uses these points to represent the texture positioning information, but there are several twists and turns in the way.

The basic idea is that the first point is the location in map space of the origin of the texture space, while the second two are the locations of the (1,0) and (0,-1) points (1 being the width/height of a texture tile; 128 is often used instead of 1)). Hunh? how did -1 get into it??

That's the first twist. Presumably due to the unfortunate identification of the upper-left corner of a texture as the origin, things seem to work (for faces) as if the vertical texture coordinate were negative. This requires sign-flips at various points in the code, for example in projecting textures between faces and patches, since the patch texture coordinates don't have this property. Confusing.

The next twist is due to the fact that the threepoints are supposed to express the orientation of the face by having the cross-product (p1-p0)^(p2-p0) stick outward from the poly(s) that the face is in. This makes it impossible to express 'mirror image' texture scales directly, since the cross-product would be reversed and point into the poly. The solution to this is that faces have a boolean attribute 'Mirror'. In the Delphi, the method  Face.GetThreePoints  fills in the values of three var parameters V1, V2, V3; if Mirror is set, then V2 is the position of texture (1,0) and V3 the position of texture (0,-1), otherwise the reverse (this is backwards from how I would have expected it to be, but there it is). In the enhanced texture positioning output (written into a comment at the end of each face), TX1 means that Mirror is false, TX2 that it's true.

In the Python, the numerical (second) arguments to the face method  (set)threepoints  specify whether this three-point swap is to take place; if this argument is 0 or 2, it doesn't; if it's 1 or 3, it does.

And finally for the third twist. In the map format, the threepoints don't represent the texture size, but rather the texture scale: if the distances (p1-p0), (p2-p0) are 128, then the texture scale is 1:1 (tho it may be sheared). So these points will only represent that actual locations of the texture size is 128x128. If we want to the threepoints to be located at the actual texture points, we need to use  GetThreePointsUserTex  in Delphi, or set the numerical argument of  (set)threepoints  to 1 or 3 in Python.

So wrapping all this up, the threepoints info as gotten by  GetThreePointsUserTex  will have V3, V2 giving the map space locations of texture (1,0) and (0,-1), respectively, if Mirror is false, vice versa if it's true. So what we've got is a pretty direct specification of a linear mapping from the 'texture plane' (an infinite plane tiled by the texture) into map space.

Brush Primitives

'Brush primitives' is the new brush format used by Q3Radiant. Other than a bit of extra wrapping around the brush, its main new feature (for now at any rate) is an improved methods of expressing the texture-mapping over the 5 'shift scale rotate' numbers that come after the texture name in the old representation.

In BP format, you get something that looks like this coming after the threepoints info, and before the texture name:

( ( 0.007813 0 0 ) ( 0 0.007813 0 ) )
What these are is the top two rows of the 'homogeneous matrix' mapping the plane of the face, under a special coordinate system we will get to in a moment, into the texture plane (so it's sort of the reverse of how QuArK etp works). Erm, what's a homogeneous matrix you might ask.

It is 3x3 matrix whose bottom row is (0 0 1). If you multiply a homogeneous matrix by a column vector whose z coordinate is 1, you'll see that the equations are exactly those for a linear mapping followed by a translation: the first two numbers of the top two rows give a 2x2 matrix describing the linear transformation, and the last number give a column vector to be added (to the a column 2-vector) to give the translation. So what about this coordinate system?

It is one whose axis directions are gotten by rotating the Y and Z coordinates of map space so that they lie in the plane. These direction vectors are computed by function GetAxisBase in QkMapPoly.pas. It's essential that q3map and the editor compute them in the same way; hopefully the Q3Radiant/Q3map developers won't change it without telling us. Then of course for a coordinate system you need an origin, this is gotten by scaling the face's normal vector by its Dist (-ance from the origin).

So what goes on in GetPXPY is that the (texture-scaled, etc.) threepoints are converted to the Axis Base coordinate system, and then these equations are solved for the coefficients aij making up the matrix (I used Maple V to get the solution):

 a11*p0x+a12*p0y+a13 =  0
 a21*p0x+a22*p0y+a23 =  0

 a11*p1x+a12*p1y+a13 =  1
 a21*p1x+a22*p1y+a23 =  0

 a11*p2x+a12*p2y+a13 =  0
 a21*p2x+a22*p2y+a23 = -1

Bezier Texture Scaling

The routines for Face-Bezier texture projections are in quarkpy\b2utils, texPlaneFromCph and texcpFromFace. The former is rather complicated; I think it might be possible to clean it up using the Axis Base idea, but anway the code as some comments.


Copyright (c) 2022, GNU General Public License by The QuArK (Quake Army Knife) Community - https://quark.sourceforge.io/

 [ Prev - Top - Next ]