PEW File Format
PEW files are Visitor's project files. Visitor is Bohemia Interactive's tool for creating Islands.
Visitor is a GUI tool interface that interacts with Bulldozer, an in-built 'world' viewer inside any Armed Assault engine.
The contents of the PEW project file are not directly related to the ultimate output, a WRP file. However, that data contains all similar elements, such as road networks, elevations, cell matrices, models (P3D files) and textures (RVMAT files).
Exporting a PEW file into WRP format involves the use of Bulldozer, since it is Bulldozer that decides how it wants that data presented rather than anything Visitor might like to decide. It is Bulldozer that determines the format and version of the resulting WRP file (in 8WVR format), so the WRP version is based on the version of the Real Virtuality engine used to create it. This is not the same thing as saying the VERSION of the engine. It is the engine, that decides, the VERSION of wrp it needs to generate.
Furthermore, the resulting Wrp file is (for want of a better expression) 'unbinarised' 8WVR format. It is nothing more or less, than a (modified) copy of texture, elevations and models. Period. Roadnetworks and other ancilliary data is only processed into a 'binarised' OPRW format wrp, using Bi's binarizer tool.
Note that it is possible to generate 8WVR format without involving bulldozer (or arma) using [party tools (dead link)].
While there have been several PEW versions, the ones listed here are:
- POSEW59
- POSEW60
There are only a few subtle differences to their makeup.
Legend
See Generic FileFormat Data Types.
File Format
POSEW59
POSEW60
{
PoseHeader Header;
ulong nOFPTextures; // none in Arma
OFPTexture OFPTextures[nOFPTextures];
ulong nObjectTemplates;
ObjectTemplate ObjectTemplates[nObjectTemplates];
shortBool NoOfpTerrains;
if (!NoOfpTerrains)
{
ulong nOFPTerrains; // none in Arma
OFPTerrain OFPTerrains[nOFPTerrains];
}
shortBool NoOFPForests; // none in Arma
if (!NoOFPForests)
{
ulong nOFPForests;
OFPForest OFPForests[nOFPForests];
}
RoadNetworkTemplate RoadNetworkTemplates[...];
Elevation Elevation[...];
ulong NoOfObjects;
Object Objects[NoOfObjects];
ulong NoOfLayers;
String CurrentLayerName; // "Base"
Layer Layers[NoOfLayers] ;
ulong nNamedZones;// almost never present on pose60
NamedZone NamedZones[nNamedZones];
ulong nRoadNetworks;
if (nRoadNetworks > 0)
RoadNetwork RoadNetworks[nRoadNetworks];
ulong nKeyPoints;
KeyPoint KeyPoints[nKeyPoints];
ulong NoOfBackgrounds;
Background Backgrounds[NoOfBackgrounds]; // not checked for 59
ulong TotalSizeOfSelections; // from here, to end of file
ulong nSelections;
Selection Selections[nSelections];
}
PoseHeader
Header
{
char Signature[7]; // "POSEW59" or "POSEW60" note: NOT null terminated
ulong Length; // of PEW file;
ulong UnknownLong; // typically 0
String IslandRvMatPath; // "SomePboPrefix\SomeIsland\data\0" see below
String IslandP3dPath; // "" (objects)
float TerrainGridSize; // 10.0 metres
float SegmentSize; // 400.0 (==SegmentSize - SegmentOverlap)
String IslandClassname; // ""
};
Texture And Terrain Sizes
- Pew files deal with terrain. There is no 'texture' size as such. Therefore the gridsize specified in the header pertains to terrain cells.
- In contrast, the gridsize specified in a WRP pertains to TEXTURE(rvmat) cells.
"Texture", 'Material', 'Surface', 'Layer' are synonomous to rvmat files. They are the island's unique surface as generated by importing from a sat mask image. There can be multipler layers of rvmat files.
Terrain, are the cell grids of the map and as often as not specified at a different (larger) dimension.
Unlike Wrp files, Textures and Terrain sizes are not declared in a pew directly. At least, not, in the header.
The Terrain (Map) Size is declared in the elevations structure.
TextureSizeS (plural) are derived, not declared, in each surface 'Layer' of pew. Since, ultimately, each layer must 'fit' onto the map. Each layer contains a shift value of how much SMALLER the surface is, relative to the map. For OFP, and OFP converted-to-arma, islands, this is 0 (there is no difference betweem a 256x256 cell Terrain and a 256x256 cell Texture.
For Arma, it is *generally* x 4 (shift value=2).
TerrainGridSize
A wrp file's GridSize is the OPPOSITE of a PewFile's GridSize.
In a wrp, it defines the size (in meters) of each cell of the TEXTURE (rvmats).
In a pew, there is no direct TEXTURE dimension since there can be multiple layers, each with different (derived) dimensions.
So this value, in a pew, is the TERRAIN dimension.
IslandRvMatPath
RVMAT file references in the Layers structure are listed as relative addresses to the IslandRvMatPath. This is in contradistinction to the general Bis way-of-doing-things where all references are hard references.
Traditionally, an RVMAT(Island) data path is declared as "Layers\some.rvmat". It is expected, therefore, that you do indeed have a "P:\SomePboPrefix\SomeIsland\...\Layers" folder. Put another way, the prefix of the PBO must be "SomePboPrefix\SomeIsland\...".
The SubFolder 'Layers' is hard wired by Visitor. It is generated automatically when importing a satmask image. Thus, all relative reverences to the rvmats that Visitor produces, are prefaced by 'Layers\'.
Using 3rd party tools, you can, should you wish to, alter this prefixing and location of the rvmats to anywhere at all, including no path at all (Visitor, the engine, and the binarizer don't actually care). The purpose in doing so is somewhat moot, since, unlike p3d's, rvmat's for an island are unique to an island. The chances or need of referencing them by something else is zilch. Hence, unlike p3d's, they may just as well stay in the wrp's pbo. Point being, they don't have to.
IslandObjectPath
IslandObjectPath uses the same mechanics as above. However, since P3D files are almost inevitable declared from multiple locations (any PBO, including the base 'CA' PBO included in the game) its use is moot. Be that as it may, the mechanism exists and works, identically to IslandRvMatPath.
PEW files are not known to contain anything in IslandObjectPath or IslandClassname.
OFPTexture
OFPTexture
{
ulong TextureID;
String TextureFileName; // "snih3.paa"
String TextureName; // "snih3"
String RvmatName; // "snih3.rvmat"
byte UnknownBytes[14]; // (typically 0)
RGBAColor Colour;
}
ObjectTemplate
ObjectTemplate
{
String ModelFilename; // "SomePrefixRoot\data\jablon.p3d\0"
String ModelName; // "jablon"
ulong ObjectType; // 0..5 (so far...)
// 0 Undefined - This type should never be encountered.
// 1 Natural
// 2 Artificial
// 3 Road (RoadFlag will be true)
// 4 Forest
// 5 Road2 (RoadFlag will be false)
RGBAColor OutlineColour;
RGBAColor ObjectColour;
double GeometryBounds[2]; // 50.0
ulong ModelID; // uniqueID, used by objects to lookup this model(p3d)
XYZTriplet GeometryAutocenterPos;
double ResolutionBounds[2]; // 50.0
XYZTriplet ResolutionAutoCenterPos;
shortBool Generally0x01;
shortBool RandomScaleFlag;
double RandomScaleMinMax[2]; // 50.0
shortBool RandomRotateFlag;
double RandomRotateMinMax[2]; // 20.0
shortBool RandomOrientationFlag;
double RanmdomOrientationMinMax[2]; // 180.0
shortBool RoadFlag;
if (RoadFlag)
{
TransformMatrix RoadNamedSelections; // (LB, PB, LE, PE)
TransformMatrix XRoadNamedSelections; // (LD, LH, PD, PH)
};
ulong nNamedVectors; // Usually 0 (zero)
NamedVector NamedVectors[nNamedVectors];
ulong MarkerType; // Rectangular = 0, Elliptical = 1
}
As opposed to layers (material RVMAT surfaces), 'objects' are instances of models placed onto the map. Irrespective of the number of objects there is only one reference to the model used, which is then referenced as often as needed.
ModelFilename, ModelName, and ModelID are unique unduplicated entries.
The ModelID is not zero based; its value is incremental according to the number of times any model has been added while editing, even if previous models have been deleted; model IDs do not rearrange themselves and deleted IDs are not recycled for re-use. The ObjectID illustrated on maps is, in contrast, the unique InstanceID of each object in the objects structure below. 'Road2' type cannot be defined with Visitor3 Personal Edition. It's a type specific to VBS2 V3 edition.
NamedVector
NamedVector
{
String Name; // "pohrada"
LongBool Unknown;
ulong nTriplets; // generally 2 sometimes 1
XYZTriplet StartEndPos[nTriplets];
}
As of v60 a rarely used sub structure with ObjTemplates.
OFPTerrain
OFPTerrain
{
String TerrainName;
byte UnknownBytes[11];
ulong nSurfaces;
OFPSurface OFPSurfaces[nSurfaces];
}
OFPSurface
OFPSurface
{
String SurfaceName;
float Surfacefloat[4];
byte UnknownBytes[16];
}
OFPForest
OFPForest
{
String ForestName;
RGBAColor OutlineColour;
RGBAColor ObjectColour;
ulong SquareFillModelID;
ulong SquareModelID;
ulong TriangleModelID;
ulong Unknown1; // (typically 0)
ulong Unknown2; // (typically 0)
}
NamedZone
NamedZone
{
ushort IsPresent;
ushort Moveable;
String Name; // Les_new
RGBAColor AreaColor;
RGBAColor OutlineColor;
ulong DisplayStyle;
ulong nFloats;
float Floats[nFloats];
ulong ID;
String Name2;
}
AltNamedZone
AltNamedZone
{
ushort IsPresent;
ushort Moveable;
String Name; // Les_new
RGBAColor AreaColor;
RGBAColor OutlineColor;
ulong DisplayStyle;
ulong nFloats;
float Floats[nFloats];
ulong ID;
ulong Visible;
}
RoadNetworkTemplates
RoadNetworkTemplates
{
shortBool RoadNetworkTemplatesDefined;
if (!RoadNetworkTemplatesDefined)
{
//RoadNetworkTemplates
ulong NoOfRoadNetworkTemplates;
if (NoOfRoadNetworkTemplates > 0)
{
RoadNetTemplate RoadNetTemplates[NoOfRoadNetworkTemplates];
}
//CrossroadTemplates
ulong NoOfCrossroadTemplates;
if (NoOfCrossroadTemplates > 0)
{
CrossroadTemplate CrossroadTemplates[NoOfCrossroadTemplates];
}
}
}
RoadNetTemplate
RoadNetTemplate
{
String TemplateName;
RGBAColor KeyPartsColour;
RGBAColor NormalPartsColour;
shortBool FilledLine;
double MaxAngle;
double MaxBankAngle;
ulong NoOfStraightParts;
if (NoOfStraightParts > 0)
StraightPart StraightParts[NoOfStraightParts];
ulong NoOfCurveParts;
if (NoOfCurveParts > 0)
CurvePart CurveParts[NoOfCurveParts];
ulong NoOfSpecialParts;
if (NoOfSpecialParts > 0)
SpecialPart SpecialParts[NoOfSpecialParts];
ulong NoOfTerminatorParts;
if (NoOfTerminatorParts > 0)
TerminatorPart TerminatorParts[NoOfTerminatorParts];
}
StraightPart
StraightPart
{
String StraightPartName;
ulong ObjectTemplateID;
StraightPartEnum StraightPartType;
shortBool CanChangeBankAngle;
}
CurvePart
CurvePart
{
String CurvePartName;
ulong ObjectTemplateID;
CurvePartEnum CurvePartType;
}
SpecialPart
SpecialPart
{
String SpecialPartName;
ulong ObjectTemplateID;
SpecialPartEnum SpecialPartType;
}
TerminatorPart
TerminatorPart
{
String TerminatorPartName;
ulong ObjectTemplateID;
}
CrossroadTemplate
CrossroadTemplate
{
String TemplateName;
CrossroadTypeEnum Shape;
RGBAColor Color;
shortBool FilledLine;
ulong ObjectTemplateID;
String Net_A;
String Net_B;
String Net_C;
String Net_D;
}
Type | Straights | Curves |
---|---|---|
0 | 6 m | 25 m |
1 | 12 m | 50 m |
2 | 25 m | 75 m |
3 | 100 m |
Broadly speaking, there are a few basic road types:
- asfalt: Bitumen
- silnice: Paved
- cesta: Dirt
Each RoadType describes the general characteristics of the corresponding road, and can have multiple curved, straight, special, and termination P3D models associated with it.
Generally speaking, there are:
- 3 'straight' models, approximately 6, 12, and 25 meters long respectively.
- 4 'curved' models, approximately 25, 50, 75, and 100 meters long.
- 1 'termination' type.
The termination type is a road like any other but tends to be a fixed 6 meter fade out of the general road texture.
Although "CrossRoads" could conceivably have any number of intersections, only two types are handled.
- Type 1: A T_Junction (3 intersections)
- Type 3: A genuine crossroad (4 way intersection)
For T_Junctions, there is obviously no 4th intersection and this is null filled.
Without taking too literal an interpretation, there are some major types of road.
- asfaltka: Bitumen (sealed)
- silnice: Paved
- cesta: Dirt
Thus the names of each intersection reflect the road type of that intersection, sometimes resulting in (up to) four identical names.
The overall name of the crossroad itself, tries to reflect the nature of it is makeup thus
- kr_asfaltka_asfaltka_t: an bitumen T_Junction
- kr_silince_x_cesta: a crossroad of paved and dirt roads.
Elevation
Elevation
{
XYPair TerrainSize; // 256 x 256 eg
float Heights[TerrainSize];
float BlueEdgeTerrainHeights[NoOfBlueFloats]; // Always zero values
// NoOfBlueFloats = (TerrainSize_Y * TerrainSize_X) / 16;
ulong Always0;
}
Object
Object
{
ShortBool IsPresent;
if (IsPresent)
{
ShortBool Moveable; // always 0
ulong ObjectID; // See Below
TransformMatrix ColumnFormat;
double ObjectRelativeSize; // decimal percentage
String InstanceName; // "" mostly. "minimalStrelPos" eg for artifical objects
float RelativeSurfaceElevation;
RGBA OutlineColour;
RGBA ObjectColour;
ulong ModelID; // See below
};
};
TransformMatrix
Note that a pew TransformMatrix is the only instance in the entire repertoire of bis file formats where the transform is specified in columns. All other TransformMatrices of BI (notably models) are in Row format. It makes diddly squat difference to the values in the matrix, only the positions of those values are different.
The XYZ positional component is the absolute position. If a relative elevation has been specified in the pew, that value has been added to the component at this storage time.
ObjectID
Every object entry has a zero-based, linear sequential ObjectID. ObjectID is unique for every object and is the value displayed on a map for all models placed on it (including roads, grass, etc.). Thus, every tree and every road section has its own unique ObjectID.
The first Object in the PEW file is ObjectID==0, the next 1,2,3,4 etc.
If an object has been deleted, that entry retains its unique object ID, even though the ObjectID is no longer stored. If an object is not present (IsPresent is false), the ObjectID is calculated based on the last used object ID. The next real object (IsPresent is true) will always be the Nth ObjectID because the number always increases by the number of object entries, present or not. The reason for this is to maintain consistent IDs for objects so that missions can rely on a bush having the same NearestObject ID as it always had, irrespective of map upgrades.
When you add a new object, it is thus always is added to end of table with a new object ID. When you remove an object, the object ID remains reserved even though the object no longer exists.
This mechanism works well within the ArmA engine, but fails to help at all for conversion of OFP islands unless you methodically and manually enter all of the entries in the correct order. With upwards of 100,000 entries per island, this is practically impossible.
ModelID
Note that this is the unique ID of the ModelID in the object Template. It is not an index into the template structures. Unusual, and slow as a result.
Layer
Layer
{
ulong SizeType; // 0..2 - see below
String Name; // "Base"
shortBool DefaultIndicator; // 0 ... 1
if (DefaultIndicator == 0)
{
ulong SurfaceTable[TableSize.x*y];
ulong TextureTable[TableSize.x*y];
byte UnknownTable[(TableSize.x*y/4)];
ushort UnknownShort;
};
if (DefaultIndicator == 1)
{
ulong NoOfTerrainMaterials;
TerrainMaterial[NoOfTerrainMaterials]
{
ulong BitFlags; // 1 = label, 0x40 = rvmatfile,,,,
String MaterialName; // "---sea---", "Layers\P_000-000_L00.rvmat"
ulong TypeID; // 0..3
};
ulong RvmatIndexTable[TableSize.x*y];
};
};
TableSize = Elevations.TerrainSize >> SizeType;
RvmatIndexTable
The texture table contains 1-based indexes into the rvmats. This is a traditional bis way-of-doing-things in that the 0th 'rvmat' is, by default 'sea' texture and does not have an associated file, nor is it referenced in this table.
The organisation of 'cells' differs from its equivalent wrp. They are the reversed, mirror image of each other. Thus for a pew file with a 'poetic' 4x4 island:
|
|
Unlike a wrp there is no Texture Size (256x256) eg. It is a derived value from the Elevations size (ergo the TerrainSize) (1024x1024 eg)
The size of this table, the 'Texture Size' is defined as a ratio via LayerSizeType.
- 0 same
- 1 half
- 2 quarter
Rvmat Conventions
Naming of rvmat files (generally) follows a simple convention largely because the files are auto_generated when importing the satmask. The resolved Texture size (dependent on the pixels and resoltion wanted) are divided into x and y numbered co-ordinates thus
p_(x)00..(x)nn_(y)00..nn_L00..zz
where:
(x) and (y) are the cellgrid position (and not part of the label)
Lzz is the layer
For a mythical single layer island with an 8x8 TEXTURE , files names will range from
p_00_00_L00 to p_07_07_L00
PxxPyy
RoadNetworks
A 'RoadNetwork' essentially is comprised of a 'KeyPart' and 1-4 Directions. A Direction comprsises 0 to 'N' number of 'Parts'. When the Visitor3 user starts placing a 'RoadNetwork' on a map they are 'forced' to place a 'KeyPart'. Each 'RoadNetwork' has at least 1 'Direction' defined. A 'Direction' may have zero 'RoadParts' placed on the map.
RoadNetworks
{
shortBool NetworkDefined;
if (NetworkDefined)
{
shortBool Always0x01;
shortBool Movable;
ulong NetworkID;
float Network_XZY[3];
float Direction;
shortBool KeyPartDefined; // Should always be 1=Yes 'cause you cant start a road without placing a KeyPart
float KeyPart_XZY[3];
float KeyPart_Direction;
ulong KeyPart_TemplateObjectID;
KeyPartTypeEnum KeyPart_Type;
ushort KeyPart_FamilyType;
String KeyPart_RoadNetworkTemplateName;
String KeyPart_ModelName;
RGBAColor KeyPart_Color;
shortBool DirectionsDefined; // Should always be 0x0100=yes because every RoadNetwork has at least 1 direction.
ulong NoOfDirections;
ulong Direction_A_Something; //Always 0x0000 0000
ulong Direction_B_Something; //Always 0x0000 0000
ulong Direction_C_Something; //Always 0x0000 0000
ulong Direction_D_Something; //Always 0x0000 0000
DirectionHeader DirectionHeaders[NoOfDirections];
byte Unknown[92];
ulong NoOfDirections;
Direction Directions[NoOfDirections];
}
}
DirectionHeader
DirectionHeader
{
String RoadNetworkTemplateName;
float DirectionOffsetXZY[3];
float DirectionOffsetDirection; // 180.0 degrees.
}
Direction
Direction
{
ulong NoOfParts;
RoadPart RoadParts[NoOfParts];
String RoadNetworkTemplateName;
float DirectionOffsetXZY[3];
float DirectionOffsetDirection; // 180.0 degrees.
RGBAColor KeyPartColor;
RGBAColor NormalPartColor;
shortBool FilledLine;
double MaxAngle;
double MaxBankAngle;
}
RoadPart
RoadPart
{
shortBool Defined;
shortBool Enabled; // Possibly incorrect.
float RoadPartXZY[3];
float RoadPartDirection;
ulong RoadPart_ObjectTemplateID;
ushort PartTypeUIListboxIndex;
String ModelName;
byte Unknown[102];
}
KeyPoint
KeyPoint
{
ShortBool IsPresent;
if (IsPresent)
{
ShortBool Always0;
String ClassName; // "Noe_Lany"
RGBA AreaColor,OutlineColor;
ulong DisplayStyle; // 0 solid
// 1..6 hatch-horz,vert,cross,skew right,skew left,skewcross
// 7 standard
ShortBool Visible;
float Offset[2]; // map relative
float Size[2]; // 250 x 250.0 eg (width and height)
ulong ID; // 0,1,2,3,4,5,6....
BisString TownName; // "Lipany", "Hill"
BisString LocaleType; // "NameCity" NameCityCapital, NameVillage, NameLocal, VegetationBroadLeaf, Hill, Marine, ViewPoint
BisString ClassText; // "canOcclude=1; BumbleButt='FlowControl2"; ++=)#4555Semi"
}
}
There is always a classname associated with this Keypoint,
- Forest_Owls
- Abel_LaTrinite
- MyPinkElephant
and in most cases a text name that is 'seen' on the map
"Le Refuge Des Chasseurs"
Viewpoint and Marine types normally don't have names associated with them. There is no "place" in the sea.
Each of these Keypoints have a Locale Type. Some of which are:
- NameCity
- NameCityCapital
- NameVillage
- VegetationBroadLeaf (Forest)
Background
Background // this structure has not been seen
{
String BackgroundFilename; // "sat_lco.bmp"
String BackgroundName; // "overlay1"
float OffsetXY[2];
float SizeXY[2];
ulong Transparency;
shortBool Visible; // or ulong?
}
Selection
Selection
{
ushort strlen;
char[] SelectionName[strlen]; // NOT null terminated
ulong nObjects;
SelectionObject SelectionObjects[nObjects]
}
SelectionObject
SelectionObject
{
ulong ObjectInstanceID;
byte Unknown[8];
}
Enums
ObjectTemplateTypeEnum
enum ObjectTemplateTypeEnum
{
Undefined = 0,
Natural = 1,
Artificial = 2,
Road = 3,
Forest = 4,
Road2 = 5
}
MarkerTypeEnum
enum MarkerTypeEnum
{
Rectangular = 0,
Elliptical = 1
}
StraightPartEnum
enum StraightPartEnum : ushort
{
SixBy25, // 6,25m
TwelveBy5, // 12,5m
TwentyFive // 25m
}
CurvePartEnum
enum CurvePartEnum : ushort
{
Radius25m,
Radius50m,
Radius75m,
Radius100m
}
SpecialPartEnum
enum SpecialPartEnum : ushort
{
SixBy25, // 6,25m
TwelveBy5, // 12,5m
TwentyFive // 25m
}
KeyPartTypeEnum
enum KeyPartTypeEnum
{
Crossroad,
Straight,
Special,
Terminator
}