| Specific Topics Updated 02 Mar 2001 | Upper levels: - QuArK Information Base - 4. The Source Code

4.5. Specific Topics | [ Prev - Up - Next ]

Here are discussions of various specific topics.

Index

- - Map Saving (02 Mar 2001)
- - Texture Scales, Threepoints, etc. (02 Mar 2001)

Map Saving | tiglari - 02 Mar 2001 | [ Top ]

Map saving is done by the calling TTMapBrush.SaveAsText in QkMapObjects. This is triggered in slightly different ways by the regular build menu (qSaveFile), and the 'File -> SaveAs -> *.map' menu item/dialog (TQForm1.Save), both in QkMapObjects. there is duplication of the code involved in reading the map saving config options; the build menu code does it in Python (quarkpy.mapquakemenu.writemapfile), the file-menu code in delphi (QMap/QMapFile.SaveFile). The relevant object hierarchy is:
    TTreeMap
      TTreeMapSpec (can have writeable specifics)
         TTreeMapGroup (can have sub-polys/beziers)
            TTreeMapBrush (brush entity)
         TTreeMapEntity
            TDuplicator (hmm, also can have sub-stuff)
      TTexturedTreeMap
         TFace
         TBezier
TTreeMapBrush writes the worldspawn specifics, contained polys, and beziers, and then, at the end, invokes TTMapGroup.SaveAsText to write out the brush and point entities in the map structure (outside of the scope of the worldspawn entities). To keep things confusing, several of the routines pass a Flags parameter, co-named with one of the fields of QObject, which keeps track of some parameters controlling map-writing. Groups are for example perused for beziers to write only if soIgnoreToBuild is set in Flags, and vfIgnoreToBuild is set in ViewFlags, which is the TTreeMapGroup property encoding the view/build flags in groups in the tree-structure. Originally, the beziers were collected with ListeEntites with the Cat set at [ecBezier], but this caused the ignoretobuild flags to be ignored, so currently a special ListeBeziers function is used. Perhaps ListeEntites should be reinstated with a Flags argument.

Texture Scales, Threepoints, etc. | tiglari - 02 Mar 2001 | [ Top ]

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 vace 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 methord `(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), respctively, 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 threeponts 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 coordiante 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.

GNU General Public License by The QuArK (Quake Army Knife) Community - http://www.planetquake.com/quark | [ Prev - Top - Next ]