P3D File Format - ODOLV4x: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
mNo edit summary
m (→‎ODOLv40Lod: reorganised vertex table)
Line 255: Line 255:
     LodUsedBone                  LodUsedBones[nUsedBones];
     LodUsedBone                  LodUsedBones[nUsedBones];
     LodPointFlags                LodPointFlags;              // Potentially compressed
     LodPointFlags                LodPointFlags;              // Potentially compressed
     byte                          UnknownBytes[8];
     byte                          UnknownBytes[8];
     float                        UnknownFloats[10];
     float                        UnknownFloats[10];
 
     ulong                        NoOfTextures;
     ulong                        NoOfTextures;
     asciiz                        LodPaaTextureNames[NoOfTextures];  //"ca\characters\hhl\hhl_01_co.paa"
     asciiz                        LodPaaTextureNames[NoOfTextures];  //"ca\characters\hhl\hhl_01_co.paa"
Line 277: Line 275:
     ulong                        nFrames;
     ulong                        nFrames;
     LodKeyFrame                  LodKeyFrames[nFrames];
     LodKeyFrame                  LodKeyFrames[nFrames];
     byte                          Unknown[17];
     byte                          Unknown[17];
  if (V40 or V43)
     VertexTable                  VertexTable;
     ulong                        nBytes;
  {
     byte                          UnknownBytes[nBytes][32];   // Potentially compressed
     LodUV                        LodUV1;                     // Potentially compressed
  }
     ulong                        nUVs;
====VertexTable====
     if (nUVs==2)
 
    LodUV                        LodUV2;                    // Potentially compressed
all arrays are subject to compression
    ulong                        NoOfVertices;
    XYZTriplet                    LodXZY[NoOfVertices];       // Potentially compressed
    ulong                        nNormals;
    LodNormals                    LodNormals[nNormals];      // Potentially compressed
    ulong                        nMinMax;
    XYZTriplet                    LodMinMaxXYZ[nMinMax][2];  // Potentially compressed


  }else // V47 & V48
struct
   
  {
  {
     (A2)LodUV                    LodUV1;  
     A2LodUV                      A2LodUV1;                     // Potentially compressed
     ulong                        nUVs;                       // 1 or 2 (see note)
     ulong                        nUVs;
     if (nUVs==2)
     if (nUVs==2)
     {
     {
     float                       A2Floats[4];
     if (V47 & V48)
     A2LodUV                      A2LodUV2;                     // Potentially compressed
      float                       A2Floats[4];
     (A2)LodUV                    LodUV2;  
     }
     }
     ulong                        NoOfVertices;
     ulong                        NoOfPoints;
     XYZTriplet                    LodXZY[NoOfVertices];       // Potentially compressed, same as arma1
     XYZTriplet                    LodPoints[NoOfPoints];
     ulong                        nNormals;
     ulong                        nNormals;
     A2LodNormals                  A2LodNormals[nNormals];       // Potentially compressed
     (A2)LodNormals                LodNormals[nNormals];  
     ulong                        nMinMax;
     ulong                        nMinMax;
     float                        LodMinMaxXYZ[nMinMax][2];   // Potentially compressed
     (A2)LodMinMax                MinMax[nMinMax];            //optional
  }
    ulong                        nProperties;
      // Note that nUV1's== nNormals == NoOfVertices
    VertProperty                  VertProperties[nProperties];//optional related to skeleton
    
 
    ulong                        Count;
*in error, V47 sometimes sets nUV's as 0 but means 1
    float                         UnknownUV[Count][2];   // Potentially compressed// probably a vertices something
 
    ulong                         nBytes;
*There are always Points,PointFlags, Normals and UV1 arrays. (count!=0).
     byte                          UnknownBytes[nBytes][32];  // Potentially compressed
*Their counts are always identical.
   }
*MinMax and VertProperties are optional in the sense that their counts can be zero
These counts (when present) are also identical to the others.
*In Odol7 PointFlags are part of this stucture, in Arma, they are a separated table.
 
====CompressedFill Arrays====
 
LodUV's and LodNormals arrays are not only subject to the standard 1024 rule compression, but also have a fill byte.
 
struct
{
   ulong                        Count;
  tbool                        DefaultFill;
  if (DefaultFill)
  type                        Array;          // default fill for all Counts
  else
  type                         Array[Count];   // potentially compressed
}
 
The structure either contains a single set of type variables, or, an array of type variables. If a full array is declared (DefaultFill =false) then that array is subject to the 1024 rule as per normal.
 
 
====LodUV====
CompressedFill type = UVPair // eg float U,V;
====A2LodUV====
CompressedFill type = float // eg float UV;
====LodNormals====
  CompressedFill type = XYZTriplet
====A2LodNormals====
  CompressedFill type = float
====LodPointFlags====
CompressedFill type = ulong bits
 
This table is the equivalent of Oxygen's points->properties dialog box. It specifically stores the user values and other flags for that point.
 
In ODOl7 it was part of the vertex table. In Arma, it is separate.
 
See [[P3D Point and Face Flags]]
 
====LodMinMax====
Array
{
  XYZTriplet     MinMax[Count][2]; // 2 == min vs max
}
 
====A2LodMinMax====
Array
{
   float        MinMax[Count][2]; // 2 == min vs max
}
 
====VertProperty====
struct
{
  ulong  index;
   UVPair Unknown;
}


====LodModelProxy====
====LodModelProxy====
Line 341: Line 387:
This struct seems to assign unique iterative IDs (starting from zero) to some bones or their anims. Quite weird way of doing that, so it is probably not the whole truth.
This struct seems to assign unique iterative IDs (starting from zero) to some bones or their anims. Quite weird way of doing that, so it is probably not the whole truth.


====LodPointFlags====
struct
{
  ulong  NoOfPts;
  tbool  UseDefault;
  if (UseDefault)
  ulong DefaultValue;
  else // =0
  ulong PropertyValues[NoOfPts]; // potentially compressed
}
This table is the equivalent of Oxygen's points->properties dialog box. It specifically stores the user values and other flags for that point.
In ODOl7 it was part of the vertex table. In Arma, it is separate.
See [[P3D Point and Face Flags]]
PropertyValues for NoOfPts are either all the same (UseDefault), or, they are individually declared.
On the other hand, and similar to CompressedStructs of OdolV7, if the amount of data in the array exceeds 1023 bytes, that array is compressed.
The use of a) potential compression and b) a default fill, is endemic to many ODOLV40 type packets.


====LodMaterial====
====LodMaterial====
Line 470: Line 494:
   }
   }
  }
  }
====LodUV====
For V40 & V3
LodUV
{
  ulong                        nVertices;
  tbool                        DefaultFill;
  if (DefaultFill)
  float                        UV[2];              // default fill for all nVertices
  else
  float                        UV[nVertices][2];  // potentially compressed
}
The structure either contains a single UV pair of floats. Or, pairs of UV floats for all positions (nVertices)
If a full array is declared (DefaultFill != 0) then that array is compressed if 2 * sizeof(float) * nVertices > 1023
For V47 & 8
A2LodUV
{
  ulong                        nVertices;
  tbool                        DefaultFill;
  if (DefaultFill)
  float                        UV;              // default fill for all nVertices
  else
  float                        UV[nVertices];  // potentially compressed
}
As above, except only a single float value per vertex is used.
====LodNormals====
A2LodNormals
LodNormals
{
    tbool                        DefaultFill;
    if (DefaultFill)
    type XZY;
    else
    XYZTriplet                  XZY[nNormals];        // Potentially compressed
}
V40 and V43 type == XYZTriplet
V47 and V48 type == float               


== Decompression ==
== Decompression ==

Revision as of 03:24, 19 May 2010

Template:unsupported-doc

Introduction

Acknowledgements

This body of work is due to Synide's sweat and tears. To whom, all honour and glory. Ably assisted by T_D and Mikero that further detailed the data and gave this article a more general and correct structure.

General

The general format of an ArmA ODOLV4x p3d model is similar to the ODOLV7 format. The major differences are that ArmA models have

  • an optional model.cfg, and
  • Lods occur in the file from highest to lowest LodType value.

Legend

see Generic FileFormat Data Types

File Paths

The PrefixRoot\ folder.

Life for modellers would be far less tedious if filenames could also be relative to the p3d they are encountered in. Altering or moving or renaming the pbo (and specifically it's prefix) would not alter the relative location of the paa's it contains.

BI choose to use hard-wired Pbo-Prefix-addressing ONLY.

All hardwired addressing is relative to a built-in-situ (ie virtual) PrefixRoot\ folder

Each and every pbo in Arma contains a unique identity name, a prefix. Irrespective of the name of the pbo, the prefixname is THE name of the pbo from the perspective of the engine. In most cases, the prefixname is, conveniently, the filename. One huge advantage of this mechanism, sorely sorely missed in OFP. is that self-documenting increasing revisions of an addonV123.pbo can be supplied to Arma, with no changes to the mission sqms and other pbos that refer to it.

The PrefixRoot\ folder contains the prefix names of all pbos encountered (almost) ANYWHERE.

Thus the pbos in the Official Addons folder, the Oem Mods\Addons folder(s), the Dta core and bin pbo's, are all examined for their unique prefix names. These prefix names become the dictionary index of where the pbo really is, AND, what filename it actually is.

Thus all filename references in a p3d, *unconditionally* contain a prefixname\someFile\SomeWhere.

In most cases they refer to the very same pbo as the containing p3d and a great pity that the extraneous information could not have been removed by (optional) relative addressing as it requires a great deal of fiddling about when modifying models.

Note also that there is some inconsistency in filename paths. Most do not have a leading \. Some, require it. Both are indeed \hardwired

An Example:

P3dProxyName ="\ca\a10\agm65";

The immediate (and unfortunate) impression is that there is an A10 folder inside the official CA.pbo addon. In fact, the prefix of the A10.pbo = "ca\A10". Thus this reference is to the A10.pbo within which, is a agm65.p3d in it's root folder. (and again, this reference is in fact an extraneous reference to itself since the referring p3d (A10.p3d) is in the same pbo)

Versions

This Document covers ODOL versions:

V40

  • Original Arma1 binarised p3d

V43

ModelInfo now has a 24 byte thermal profile appended, making it same as vbs2.

• An extra byte at end of Skeleton structure: always 0

  • LZSS compression is still used at this level

V47

As per V43 plus:

• all compressed blocks are LZO compressed

• CompressedMinMax block is now nMinMax*8 in size

• CompressedNormals block is now nNormals*4 in size

  • LodKeyFrame has 4 extra floats

• UVSet structure changed to:

LodUV
{
 float  uvScaling[4];
 ulong  nVertices;
 tbool  DefaultFill;
 if (DefaultFill)
  float                        UV;              // default fill for all nVertices
 else
  float                        UV[nVertices];   // potentially compressed
}


V48

As per V47 plus:

ModelInfo has a 4 byte appendix



File Format

ODOLv4x
{
 StandardP3DHeader Header;
 ModelInfo         ModelInfo; // see P3D Model Info
 Animations        Animations;
 ulong             StartAdressOfLods[Header.NoOfLods];// offset relative to start of file.
 ulong             EndAdressOfLods  [Header.NoOfLods];
 LODFaceDefaults   LODFaceDefaults  [Header.NoOfLods];
 ODOLv40Lod        ODOLv40Lods[Header.NoOfLods];  
 }//EndOfFile


Structures

StandardP3DHeader

struct
{
  char[4]  Filetype; // "ODOL"
  ulong    Version;  // 40
  ulong    NoOfLods; // alias NoOfResolutions;
}


common header structure for all P3D file formats


Animations

Animations
{
 tbool             AnimsExist;
 if (AnimsExist)
 {
  ulong            nAnimationClasses; // eg NoOfAnimSelections;
  AnimationClass   AnimationClasses[nAnimationClasses];

  ulong            NoOfResolutions;// same value as Header.NoOfLods
  Bones2Anims      Bones2Anims[NoOfResolutions];
  Anims2Bones      Anims2Bones[NoOfResolutions];
  //For every bone there is a list of Animations for each resolution
  //And, a reversed table of every Animation gets a Bone.
  //The reversed table optionally appends axis info dependent on the AnimTransformType
 }
}

AnimationClass

AnimationClass { ulong AnimTransformType; asciiz AnimClassName; // "RightDoor" asciiz AnimSource; // "rotor" float MinMaxValue[2]; float MinMaxPhase[2]; ulong sourceAddress; switch(AnimTransformType) case 0://rotaton case 1://rotationX case 2://rotationY case 3://rotationZ float angle[2]; break; case 4://translation case 5://translationX case 6://translationY case 7://translationZ float offset[2]; break; case 8: //"direct" float axisPos[3]; float axisDir[3]; float angle; //in radians whereas the model.cfg entry is in degrees float axisOffset; break; case 9: //"hide" float hideValue; break; }

corresponds to model.cfg
class CfgModels
{
 ....

 class whateverModel: Default
 {
  ...
  class Animations
  {
   class RightDoor //AnimClassName
   {
     type = "translation";//AnimTransformType
     source = "rotor";    //AnimSource
     etc



Bones2Anims

Bones2Anims
{
 ulong        NoOfBones;
 Bone2AnimClassList   Bone2AnimClassLists[NoOfBones];
}

Bone2AnimClassList

Bone2AnimClassList
{
 ulong NoOfAnimClasses;
 ulong AnimationClassIndex[NoOfAnimClasses]; // a (sometimes repeating) list of zero based indexes into above animation classes
}

Anims2Bones

Anims2Bones
{
 AnimBones AnimBones[Animations.nAnimationClasses];
}

AnimBones

every lod contains an identical list of animation entries that declare the position and axis of the each animation classes

AnimBones
{
 long SkeletonBoneNameIndex; // zero based index to the SkeletonBoneName name & parentname
 // equivalent to selection = "LeftDoor"; eg in the model.cfg
 /*
 ** SkeletonBoneNameIndex== -1 when no skeleton bone is for this Anim and (obviously?) no axis information follows.
 */
 if (SkeletonBoneNameIndex!= -1) && (AnimationClass.AnimTransformType != 8 || 9)
 {
 /*
 ** AnimationClass.AnimTransformType 8 (direct) and 9 (hide) never require axis information. 
 ** This because the "direct" (type 8) already has axis info in it's AnimationClass structure, 
 ** and "hidden" (type 9) clearly doesn't need it.
 */
    XYZTriplet axisPos; //describes the position of the axis used for this anim
    XYZTriplet axisDir;
 }
}

LODFaceDefaults

 tbool             UseDefault[Header.NoOfLods];
 FaceData
 {
  ulong   HeaderFaceCount;
  bytes   Unknown[13];
 }[Number of false UseDefault's];

A face data struct only exists for those lods who's UseDefault is zero

ODOLv40Lod

 ODOLv40Lod
 {
   ulong                         nProxies;
   LodModelProxy                 LodModelProxies[nProxies];
   ulong                         nLodItems;
   ulong                         LodItems[nLodItems];        // potentially compressed
   ulong                         nUsedBones;
   LodUsedBone                   LodUsedBones[nUsedBones];
   LodPointFlags                 LodPointFlags;              // Potentially compressed
   byte                          UnknownBytes[8];
   float                         UnknownFloats[10];
   ulong                         NoOfTextures;
   asciiz                        LodPaaTextureNames[NoOfTextures];  //"ca\characters\hhl\hhl_01_co.paa"
   ulong                         NoOfMaterials;
   LodMaterial                   LodMaterials[NoOfMaterials];
   LodEdge                       LodEdge1;                          // potentially compressed
   LodEdge                       LodEdge2;                          // potentially compressed
   ulong                         NoOfFaces;
   ulong                         OffsetToSectionsStruct;            // see below
   ushort                        AlwaysZero;
   LodFace                       LodFace[NoOfFaces];                // see P3D Lod Faces
   ulong                         nSections;
   LodSection                    LodSections[nSections];            // see P3D Lod Sections
   ulong                         nNamedSelections;
   LodNamedSelection             LodNamedSelections[nNamedSelections]; //See P3D Named Selections potentially compressed
   ulong                         nTokens;
   LodTokenPair                  LodTokenPairs[nTokens];
   ulong                         nFrames;
   LodKeyFrame                   LodKeyFrames[nFrames];
   byte                          Unknown[17];
   VertexTable                   VertexTable;
   ulong                         nBytes;
   byte                          UnknownBytes[nBytes][32];   // Potentially compressed
 }

VertexTable

all arrays are subject to compression

struct
{
   (A2)LodUV                     LodUV1; 
   ulong                         nUVs;                       // 1 or 2 (see note)
   if (nUVs==2)
   {
    if (V47 & V48)
     float                       A2Floats[4];
    (A2)LodUV                    LodUV2; 
   }
   ulong                         NoOfPoints;
   XYZTriplet                    LodPoints[NoOfPoints];
   ulong                         nNormals;
   (A2)LodNormals                LodNormals[nNormals]; 
   ulong                         nMinMax;
   (A2)LodMinMax                 MinMax[nMinMax];            //optional
   ulong                         nProperties;
   VertProperty                  VertProperties[nProperties];//optional related to skeleton
  • in error, V47 sometimes sets nUV's as 0 but means 1
  • There are always Points,PointFlags, Normals and UV1 arrays. (count!=0).
  • Their counts are always identical.
  • MinMax and VertProperties are optional in the sense that their counts can be zero

These counts (when present) are also identical to the others.

  • In Odol7 PointFlags are part of this stucture, in Arma, they are a separated table.

CompressedFill Arrays

LodUV's and LodNormals arrays are not only subject to the standard 1024 rule compression, but also have a fill byte.

struct
{
 ulong                         Count;
 tbool                         DefaultFill;
 if (DefaultFill)
  type                         Array;          // default fill for all Counts
 else
  type                         Array[Count];   // potentially compressed
}

The structure either contains a single set of type variables, or, an array of type variables. If a full array is declared (DefaultFill =false) then that array is subject to the 1024 rule as per normal.


LodUV

CompressedFill type = UVPair // eg float U,V;

A2LodUV

CompressedFill type = float // eg float UV;

LodNormals

 CompressedFill type = XYZTriplet

A2LodNormals

 CompressedFill type = float

LodPointFlags

CompressedFill type = ulong bits

This table is the equivalent of Oxygen's points->properties dialog box. It specifically stores the user values and other flags for that point.

In ODOl7 it was part of the vertex table. In Arma, it is separate.

See P3D Point and Face Flags

LodMinMax

Array
{
 XYZTriplet     MinMax[Count][2]; // 2 == min vs max
}

A2LodMinMax

Array
{
 float         MinMax[Count][2]; // 2 == min vs max
}

VertProperty

struct
{
 ulong  index;
 UVPair Unknown;
}

LodModelProxy

 LodModelProxy
 {
   asciiz      P3dProxyName;        //"\ca\a10\agm65" (.p3d is implied) <<note the leading filename backslash
   XYZTriplet  RotationMatrix[3];
   XYZTriplet  Translation;
   ulong       FaceIndex;
   ulong       NamedSelectionIndex;
   ulong       Unknown[2];
 }

This structure is (almost) identical to ODOL7 except it has 4 indices rather than 2.

LodUsedBone

LodUsedBone
{
  ulong nIDs;         //range 0..3
  ulong BoneID[nIDs];
}

This struct seems to assign unique iterative IDs (starting from zero) to some bones or their anims. Quite weird way of doing that, so it is probably not the whole truth.


LodMaterial

   //Basically... A direct replication of the information in the given .rvmat file
 LodMaterial
 {
   asciiz            RvMatName;     // "ca\characters\data\soldier_captive_hhl.rvmat"
   ulong             Type;          // 9 == Arma, 10==VBS2
   D3DCOLORVALUE     Emissive;
   D3DCOLORVALUE     Ambient;
   D3DCOLORVALUE     Diffuse;
   D3DCOLORVALUE     forcedDiffuse;
   D3DCOLORVALUE     Specular;
   D3DCOLORVALUE     Unknown;       //Usually same as Specular
   float             SpecularPower;
   ulong             PixelShaderId; //See enumPixelShaderId
   ulong             VertexShaderId;//See enumVertexShaderId
   ulong             BoolFlag;      //mostly 1 otherwise 0
   ulong             AnIndex;       //0,1 or 2
   asciiz            BiSurfaceName; // "ca\data\Penetration\plastic.bisurf"
   ulong             Always0x01;
   ulong             aCount;        //Generally 0
   ulong             nTextures;
   ulong             nTransforms;   // always same as nTextures
   LodStageTexture   StageTextures  [nTextures];
   LodStageTransform StageTransforms[nTransforms]; 
 }  
There is always one default Texture and Transform as the first entry.
It is the only entry if a SurfaceName exists.
D3DCOLORVALUE
D3DCOLORVALUE
{
  float r,g,b,a;
}
LodStageTexture
LodStageTexture
{
 ulong  TextureFilter; // see below
 asciiz PaaTexture;    // "ca\characters\data\civil_tvreport_body_as.paa
                       // alternatively "#(argb,8,8,3)color(0,0,0,1,CO)" (eg)
 ulong  StageID;       // zero based, see below
};
The StageID is iterative (linear sequential). 1st entry is 0, 2nd 1, 3rd 2, etc.
TextureFilter maybe 1 of the following values.
  • 0: Point // sometimes
  • 1: Linear // rarely
  • 2: TriLinear // not seen
  • 3: Anisotropic (default)
LodStageTransform
  LodStageTransform
  {
   ulong UVSource;
   float Transform[4][3];//a DirectX texture space transform matrix
  };

LodEdge

LodEdge
{
 ulong   nEdges;
 ushort  Edges[nEdges]; // potentially compressed
};

LodFace

see P3D Lod Faces

Because of the variable amount of vertices in this struct (3 or 4), OffsetToSectionsStruct is used to skip the block. It's value is relative to the first PolygonVertice and is computed as follows
   OffsetToSectionsStruct= NoOfPolygons * (SizeofEach (PolygonVertice)); 

Each PolygonVertice is

nOfVertices *sizeof(ushort) + sizeof(ushort); // always 8 or 10

This, is in fact, an ERROR because the type size of the NoOfVertices is byte, NOT ushort. Hence the REAL offset is

RealOffsetToSectionsStruct = OffsetToSectionsStruct - NoOfPolygons *(sizeof(ushort)- sizeof(byte) );

or, to put it more simply

RealOffsetToSectionsStruct = OffsetToSectionsStruct - NoOfPolygons;

NOTE: See discussions for actual nature of this value


LodTokenPair

 LodTokenPair
 {
    asciiz Property;// "noshadow" = "1" eg
    asciiz Value;
 }

LodKeyFrame

LodKeyFrame
{
 float      FrameTime;
 ulong      NoOfFramePoints;
 XYZTriplet LodFramePoints[NoOfFramePoints];
 if V47 or V48
 {

float Arma2[4];

 }
}

Decompression

see Compressed LZSS File Format

see Compressed LZO File Format


In ODOL v40 and v43 format files, some of the data structures present in the file are compressed by using LZSS compression. ODOL v47 and v48 use LZO compression.

Unlike pbo compression, in ArmA model files, one only knows the number of items to decompress, the expected output size (in bytes) and the expected checksum. With this information and the size of a given data item one has the necessary information to expand the data to it's original format and size.

Note:- Data structures that are identified as being compressible will only be compressed if the 'expectedSize' is >= 1024 bytes.

As an example if one was expanding the array of vertices positions...

  • A vertex is described by it's x,y,z coordinates which are floats. A float is a 32bit (4 byte) number.
  • If we were processing 1968 vertices then our expected output size would be 1968 * (3 * 4) = 23,616 bytes.

This 'expectedSize' is the only necessary information one would need to pass to a processing sub-routine or function.


Reference Tables

Material Stages

The number of material stages is dependant on the type of Shader that is used to process the material by the ArmA game engine. A reference table is used when processing materials where depending on the shader specified the given number of stages should be processed.

refShaderStages { int PixelShaderId; int NoOfStages; };

ID (Hex/Decimal) Name Description NoOfStages
0x00, 0 Normal diffuse color modulate, alpha replicate 0
0x01, 1 NormalDXTA diffuse color modulate, alpha replicate, DXT alpha correction 0
0x02, 2 NormalMap normal map shader 3
0x03, 3 NormalMapThrough normal map shader - through lighting 3
0x04, 4 NormalMapSpecularDIMap ? 2
0x05, 5 NormalMapDiffuse ? 2
0x06, 6 Detail ? 1
0x07, 7 ? ? ?
0x08, 8 Water sea water 2
0x09, 9 ? ? ?
0x0A, 10 White ? 0
0x0B, 11 ? ? ?
0x0C, 12 AlphaShadow shadow alpha write 0
0x0D, 13 AlphaNoShadow shadow alpha (no shadow) write 0
0x0E, 14 ? ? ?
0x0F, 15 DetailMacroAS ? 3
0x10, 16 ? ? ?
0x11, 17 ? ? ?
0x12, 18 NormalMapSpecularMap ? 2
0x13, 19 NormalMapDetailSpecularMap Similar to NormalMapDiffuse 3
0x14, 20 NormalMapMacroASSpecularMap ? 4
0x15, 21 NormalMapDetailMacroASSpecularMap ? 5
0x16, 22 NormalMapSpecularDIMap Same as NormalMapSpecularMap, but uses _SMDI texture 2
0x17, 23 NormalMapDetailSpecularDIMap ? 3
0x18, 24 NormalMapMacroASSpecularDIMap ? 4
0x19, 25 NormalMapDetailMacroASSpecularDIMap ? 5
0x38, 56 Glass ? 2
0x3A, 58 NormalMapSpecularThrough ? 3
0x3B, 59 Grass Special shader to allow volumetric shadows to be cast on grass clutter 0
0x3C, 60 NormalMapThroughSimple ? 0

Enums

int enum PixelShaderId { Normal = 0x00, NormalMap = 0x02, NormalMapDiffuse = 0x05, NormalMapMacroASSpecularMap = 0x14, NormalMapSpecularDIMap = 0x16, NormalMapMacroASSpecularDIMap = 0x18, AlphaShadow = 0x0C, AlphaNoShadow = 0x0D, Glass = 0x38, Detail = 0x06, NormalMapSpecularMap = 0x12 }

int enum VertexShaderId { Basic = 0x00, NormalMap = 0x01, NormalMapAS = 0x0F }


Links

Article Author - Sy (Synide) -- Sy 17:16, 11 August 2007 (CEST)

Original ODOLv40 Article detailed by Bxbx (Biki'd by Mikero)