%{
#include "StdAfx.h"
#include "ParsingSymbols.h"

#include <Engine/Templates/DynamicStackArray.cpp>
#include <Engine/Ska/StringTable.h>
#include <Engine/Ska/Render.h>

extern CTFileName strCurentFileName;
MeshLOD *pMeshLOD;
SkeletonLOD *pSkeletonLOD;

static INDEX _ctVertices  = 0;
static INDEX _ctNormals   = 0;
static INDEX _ctUVMaps    = 0;
static INDEX _ctTexCoords = 0;
static INDEX _ctSurfaces  = 0;
static INDEX _ctTriangles = 0;
static INDEX _ctWeights   = 0;
static INDEX _ctVertexWeights = 0;
static INDEX _ctMorphs    = 0;
static INDEX _ctVertexMorphs = 0;
static INDEX _ctBones     = 0;
static INDEX _ctBoneEnvelopes = 0;
static INDEX _ctMorphEnvelopes = 0;
static INDEX _ctFrames = 0;

static INDEX _iBone       = 0;
static INDEX _iVertex     = 0;
static INDEX _iNormal     = 0;
static INDEX _iUVMap      = 0;
static INDEX _iTexCoord   = 0;
static INDEX _iSurface    = 0;
static INDEX _iTriangle   = 0;
static INDEX _iWeight     = 0;
static INDEX _iVertexWeight  = 0;
static INDEX _iMorph      = 0;
static INDEX _iVertexMorph = 0;
static INDEX _iBoneEnvelope = 0;
static INDEX _iMorphEnvelope = 0;
static INDEX _iFrame = 0;

/*
** Shader
*/
struct SurfaceShader
{
  CTString fnShaderName;
  INDEX ss_iSurfaceID;
  ShaderParams ss_spShaderParams;
};
INDEX _ishParamIndex; // current index of shader param
INDEX _ctshParamsMax; // size of array allocated for shader params
CStaticArray<struct SurfaceShader> _assSurfaceShaders;
INDEX _iShaderSurfIndex = 0;
INDEX _ctShaderSurfaces = 0;

/*
** 
*/
float _fCurentMaxDistance;// max distance for next lod
float _fTreshold = 0;// treshold for next animation
float _fAnimSpeed = -1;
BOOL bCompresion = FALSE;// is animation is using compresions
%}

%{
#define YYERROR_VERBOSE 1                  

// if error occurs in parsing
void yyerror(const char *strFormat, ...)
{
  va_list arg;
  va_start(arg, strFormat);
  CTString strError;
  strError.VPrintF(strFormat, arg);
  // throw the string
  ThrowF_t("File '%s' (line %d)\n%s",(const char*)strCurentFileName, _yy_iLine, (const char*)strError);
};

%}

/* BISON Declarations */

%union {
  INDEX i;
  float f;
  const char *str;
  float v2[2];
  float v3[3];
  int   i3[3];
  float f12[12];
}

%token <f> c_float
%token <i> c_int
%token <str> c_string

%token k_SE_MESH
%token k_SE_SKELETON
%token k_PARENT
%token k_BONES
%token k_VERTICES
%token k_NORMALS
%token k_UVMAPS
%token k_NAME
%token k_TEXCOORDS
%token k_SURFACES
%token k_TRIANGLE_SET
%token k_WEIGHTS
%token k_WEIGHT_SET
%token k_MORPHS
%token k_RELATIVE
%token k_TRUE
%token k_FALSE
%token k_MORPH_SET
%token k_SE_MESH_END
%token k_SE_SKELETON_END
%token k_SE_ANIM
%token k_SEC_PER_FRAME
%token k_FRAMES
%token k_BONEENVELOPES
%token k_MORPHENVELOPES
%token k_DEFAULT_POSE
%token k_SE_ANIM_END
%token k_ANIM_SET_LIST
%token k_ANIM_ID
%token k_MAX_DISTANCE
%token k_MESHLODLIST
%token k_SKELETONLODLIST
%token k_TRESHOLD
%token k_COMPRESION
%token k_LENGTH
%token k_ANIMSPEED
%token k_SHADER_PARAMS
%token k_SHADER_PARAMS_END
%token k_SHADER_SURFACES
%token k_SHADER_SURFACE
%token k_SHADER_NAME
%token k_SHADER_TEXTURES
%token k_SHADER_UVMAPS
%token k_SHADER_COLORS
%token k_SHADER_FLOATS
%token k_SHADER_FLAGS
%token k_FULL_FACE_FORWARD
%token k_HALF_FACE_FORWARD


%type <v2>  uv2
%type <v3>  vert3
%type <v3>  norm3
%type <f>   float_const
%type <i>   int_const
%type <i3>  triangle
%type <f12> matrix
%type <i>   boolean


%start program

%%

/*
 * Global structure of the source file.
 */

program
: meshlod
| skeletonlod_list
| animset_array 
| animset_list
| meshlod_list
;

/*
 * Mesh lod list
 */

meshlod_list
: k_MESHLODLIST '{' meshlod_array_opt '}'
;
// optional array of mesh lods
meshlod_array_opt
: /*null*/
| meshlod_array
;
// array of mesh lods
meshlod_array
: meshlod
| meshlod_array meshlod
;
// lod max distance
max_distance
: /*null*/
{
  // fill with default values
}
| k_MAX_DISTANCE float_const ';'
{
  _fCurentMaxDistance = $2;
}
;

meshlod
: max_distance shader_params_opt mesh_begin is_full_faceforward_opt is_half_faceforward_opt vertices normals uvmaps surfaces weights morphs mesh_end
;


/* 
 * AnimSet importer
 */

animset_list
: k_ANIM_SET_LIST '{' animset_array_opt '}' 
;

animset_array_opt
: /*null*/
| animset_array
;

animset_array
: animset
| animset_array animset
;

animset
: treshold_opt compresion_opt animspeed_opt animset_begin animation_header bone_envelopes morph_envelopes animset_end
;

compresion_opt
:/*null*/
{ bCompresion = FALSE; }
| k_COMPRESION boolean
{ bCompresion  = $2; }

treshold_opt 
: /*null*/
{ _fTreshold = 0.0f; }
| k_TRESHOLD float_const ';'
{ _fTreshold = $2; }
;

animspeed_opt
: /*null*/
{
  _fAnimSpeed = -1;
}
 | k_ANIMSPEED float_const ';'
{
  _fAnimSpeed = $2;
}
;

animset_begin
: k_SE_ANIM c_float ';'
{
  // get curent animation count
  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  // increase curent animation count
  _yy_pAnimSet->as_Anims.Expand(ctan+1);
  // get ref to new created animation
  Animation &an = _yy_pAnimSet->as_Anims[ctan];
  // set animation allready read treshold
  an.an_fTreshold = _fTreshold;
  // set animation allready read compresion flag
  an.an_bCompresed =  bCompresion;
}
;

animation_header
: k_SEC_PER_FRAME c_float ';' k_FRAMES c_int ';' k_ANIM_ID c_string ';'
{
  _ctFrames = $5;
  if(_ctFrames<0) {
    yyerror("Negative frame count found %d",_ctFrames);
  }

  // get current animset count
  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  // get ref to last animation
  Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
  // set animations seconds per frame
  an.an_fSecPerFrame = $2;
  // set animation frame count
  an.an_iFrames = _ctFrames;
  // set animation ID
  an.an_iID = ska_GetIDFromStringTable($8);
  // set animation source file (current file)
  an.an_fnSourceFile = strCurentFileName;
  // set animation custom speed (if any)
  an.an_bCustomSpeed = FALSE;
  if(_fAnimSpeed>=0)
  {
    an.an_bCustomSpeed = TRUE;
    an.an_fSecPerFrame = _fAnimSpeed;
  }
}
;

bone_envelopes
: k_BONEENVELOPES c_int
{
  // set bone envelopes count
  _ctBoneEnvelopes = $2;

  if(_ctBoneEnvelopes<0) {
    yyerror("Negative bone envelope count found %d",_ctBoneEnvelopes);
  }

  // count animations
  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
  // add new bone envelope array
  an.an_abeBones.New(_ctBoneEnvelopes);
  // set current bone envelope index
  _iBoneEnvelope = 0;
}
 '{' bone_env_header_opt '}'
{
  if(_iBoneEnvelope!=_ctBoneEnvelopes) {
    yyerror("Incorect number of bone envelopes.\nExpecting %d but found only %d",_ctBoneEnvelopes,_iBoneEnvelope);
  }
}
;

bone_env_header_opt
: /*null*/
| bone_env_header
;

bone_env_header
: bone_envelope
| bone_env_header_opt bone_envelope
;

bone_envelope
: k_NAME c_string k_DEFAULT_POSE '{' matrix ';'
{
  if(_iBoneEnvelope>=_ctBoneEnvelopes) {
    yyerror("Incorect number of bone envelopes - %d",_ctBoneEnvelopes);
  }
  
  // get last animation
  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
  BoneEnvelope &be = an.an_abeBones[_iBoneEnvelope];

  // create array of bone posiotions and rotations
  be.be_apPos.New(_ctFrames);
  be.be_arRot.New(_ctFrames);
  // for each frame
  for(INDEX ifn=0;ifn<_ctFrames;ifn++)
  {
    // set bone position and rotation frame number
    be.be_apPos[ifn].ap_iFrameNum = ifn;
    be.be_arRot[ifn].ar_iFrameNum = ifn;
  }
  // get id of this bone envelope
  be.be_iBoneID = ska_GetIDFromStringTable($2);
  // fill default pos matrix
  memcpy(&be.be_mDefaultPos,$5,sizeof(float)*12);
  
  _iFrame = 0;
}
 '}' '{' bone_env_array '}'
{
  _iBoneEnvelope++;
  if(_iFrame!=_ctFrames) {
    INDEX ctan = _yy_pAnimSet->as_Anims.Count();
    Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
    BoneEnvelope &be = an.an_abeBones[_iBoneEnvelope-1];
    CTString strBoneEnvelope = ska_GetStringFromTable(be.be_iBoneID);

     yyerror("Incorect number of bone envelope frames count for bone envelope '%s'.\nExpecting %d but found only %d",
             (const char*)strBoneEnvelope,_ctFrames,_iFrame);
  }
}
;


bone_env_array
: bone_env_m
| bone_env_array bone_env_m
; 

bone_env_m
: matrix ';'
{
  if(_iFrame>=_ctFrames) {
    yyerror("Incorect number of bone envelope frames - %d",_ctFrames);
  }

  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  QVect qvPlacement;
  Matrix12ToQVect(qvPlacement,$1);
  Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
  BoneEnvelope &be = an.an_abeBones[_iBoneEnvelope];

  be.be_apPos[_iFrame].ap_vPos = qvPlacement.vPos;
  be.be_arRot[_iFrame].ar_qRot = qvPlacement.qRot;
  _iFrame++;
}
;

morph_envelopes
: k_MORPHENVELOPES c_int
{
  // set morph envelopes count
  _ctMorphEnvelopes = $2;

  if(_ctMorphEnvelopes<0) {
    yyerror("Negative morph envelope count found %d",_ctMorphEnvelopes);
  }

  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  _yy_pAnimSet->as_Anims[ctan-1].an_ameMorphs.New(_ctMorphEnvelopes);
  _iMorphEnvelope = 0;
}
 '{' morph_env_header '}'
{
  if(_iMorphEnvelope!=_ctMorphEnvelopes) {
    yyerror("Incorect number of morph envelopes.\nExpecting %d but found only %d",_ctMorphEnvelopes,_iMorphEnvelope);
  }
}
;

morph_env_header
: /*null*/
| morph_env_header_notnull
;

morph_env_header_notnull
: morph_env
| morph_env_header morph_env
;

morph_env
: k_NAME c_string
{
  if(_iMorphEnvelope>=_ctMorphEnvelopes) {
    yyerror("Incorect number of morph envelopes - %d",_ctMorphEnvelopes);
  }
    INDEX ctan = _yy_pAnimSet->as_Anims.Count();
    // get ref to animation and morph envelope
    Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
    MorphEnvelope &me = an.an_ameMorphs[_iMorphEnvelope];
    // create array for morphs factors
    me.me_aFactors.New(_ctFrames);
    me.me_iMorphMapID = ska_GetIDFromStringTable($2);
    _iFrame = 0;
}
 '{' morph_env_array '}'
{
  _iMorphEnvelope++;
  if(_iFrame!=_ctFrames) {
    INDEX ctan = _yy_pAnimSet->as_Anims.Count();
    Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
    MorphEnvelope &me = an.an_ameMorphs[_iMorphEnvelope-1];
    CTString strMorphEnvelope = ska_GetStringFromTable(me.me_iMorphMapID);

     yyerror("Incorect number of morph envelope frames count for morph envelope '%s'.\nExpecting %d but found only %d",
             (const char*)strMorphEnvelope,_ctFrames,_iFrame);
  }
}
;

morph_env_array
: morph_env_i
| morph_env_array morph_env_i
;

morph_env_i
: c_float ';'
{
  if(_iFrame>=_ctFrames) {
    yyerror("Incorect number of morph envelope frames - %d",_ctFrames);
  }

  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
  MorphEnvelope &me = an.an_ameMorphs[_iMorphEnvelope];
  me.me_aFactors[_iFrame] = $1;

  _iFrame++;
}
 | c_int ';'
{
  if(_iFrame>=_ctFrames) {
    yyerror("Incorect number of morph envelope frames - %d",_ctFrames);
  }

  INDEX ctan = _yy_pAnimSet->as_Anims.Count();
  Animation &an = _yy_pAnimSet->as_Anims[ctan-1];
  MorphEnvelope &me = an.an_ameMorphs[_iMorphEnvelope];
  me.me_aFactors[_iFrame] = $1;

  _iFrame++;
}
;

animset_end
: k_SE_ANIM_END ';'
;


/*
 * Skeleton importer
 */

skeletonlod_list
: k_SKELETONLODLIST '{' opt_skeletonlod_array '}'
;

opt_skeletonlod_array
: /*null*/
| skeletonlod_array
;

skeletonlod_array
: skeletonlod
| skeletonlod_array skeletonlod
;

skeletonlod
: max_distance skeleton_begin bones skeleton_end
;

skeleton_begin
: k_SE_SKELETON c_float ';'
{
  // check for version of skeleton ascii file
}
;

bones
: k_BONES c_int '{' 
{
  _ctBones = $2;
  if(_ctBones<0) {
    yyerror("Negative bone count found %d",_ctBones);
  }

  // set bone index to 0
  _iBone = 0;
  // get skeleton lods count
  INDEX ctskllod = _yy_pSkeleton->skl_aSkeletonLODs.Count();
  // increase skeleton lods count
  _yy_pSkeleton->skl_aSkeletonLODs.Expand(ctskllod+1);
  // get ref to new skeleton created
  pSkeletonLOD = &_yy_pSkeleton->skl_aSkeletonLODs[ctskllod];
  // read source file name of skeleton ascii file
  pSkeletonLOD->slod_fnSourceFile = strCurentFileName;
  // get allready remembered max distance
  pSkeletonLOD->slod_fMaxDistance = _fCurentMaxDistance;
  // create new array for bones
  pSkeletonLOD->slod_aBones.New(_ctBones);
}
bone_headers '}'
{
  if(_iBone!=_ctBones) {
    yyerror("Incorect number of bones.\nExpecting %d but found only %d",_ctBones,_iBone);
  }
}
;

bone_headers
: /*null*/
| bone_headers_notnull
;

bone_headers_notnull
: bone_header
| bone_headers bone_header
;

bone_header
: k_NAME c_string ';' k_PARENT c_string ';' k_LENGTH float_const 
{
  if(_iBone>=_ctBones) {
    yyerror("Incorect number of bones - %d",_ctBones);
  }

  SkeletonBone &sb = pSkeletonLOD->slod_aBones[_iBone];
  // get bone ID
  sb.sb_iID = ska_GetIDFromStringTable($2);
  // get parent bone ID
  sb.sb_iParentID = ska_GetIDFromStringTable($5);
  // get bone length
  sb.sb_fBoneLength = $8;
}
';' bone
;

bone
: '{' matrix  ';' '}'
{
  SkeletonBone &sb = pSkeletonLOD->slod_aBones[_iBone];
  // convert matrix to qvect struct
  Matrix12ToQVect(sb.sb_qvRelPlacement,$2);
  // copy matrix to bone as its absolute placement
  memcpy(&sb.sb_mAbsPlacement,$2,sizeof(float)*12);
  // calculate bone offset length
  FLOAT3D vOffset = FLOAT3D($2[3],$2[7],$2[11]);
  sb.sb_fOffSetLen = vOffset.Length();
  // increase bone count
  _iBone++;
}
;

skeleton_end
: k_SE_SKELETON_END ';'
{
  // end of skeleton parsing
}

/*
** Shader params
*/
shader_params_opt
:/*null*/
{
  // no shader for next surface
  _assSurfaceShaders.Clear();
  _ctShaderSurfaces=0;
  _iShaderSurfIndex=0;
}
|shader_params
;

shader_params
: k_SHADER_PARAMS float_const ';' k_SHADER_SURFACES c_int '{'
{
  _assSurfaceShaders.Clear();
  _ctShaderSurfaces = $5;
  // create array of shader params for each surface in mesh file
  _assSurfaceShaders.New(_ctShaderSurfaces);
  // reset surface index
  _iShaderSurfIndex=0;
} surface_shader_params_array_opt '}' ';'
{
  // check if surfaces count match count of read surfaces
  if(_iShaderSurfIndex!=_ctShaderSurfaces) yyerror("Incorect number of surfaces");
} k_SHADER_PARAMS_END ; 

surface_shader_params_array_opt
: /*null*/
| surface_shader_params_array
;

surface_shader_params_array
: surface_shader_params
| surface_shader_params_array surface_shader_params;

surface_shader_params
: shader_name shader_textures shader_uvmaps shader_colors shader_floats shader_flag_opt'}' ';' 
{
  _iShaderSurfIndex++;
}
;

shader_name
: k_SHADER_SURFACE c_string ';' '{' k_SHADER_NAME c_string ';'
{
  if(_iShaderSurfIndex>=_ctShaderSurfaces) yyerror("Incorect number of surfaces");
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  SurfShader.ss_iSurfaceID = ska_GetIDFromStringTable($2);
  // set shader fn
  SurfShader.fnShaderName = (CTString)$6
};

shader_textures
: k_SHADER_TEXTURES c_int
{
  _ctshParamsMax = $2;
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  SurfShader.ss_spShaderParams.sp_aiTextureIDs.New(_ctshParamsMax);
  _ishParamIndex = 0;
} '{' shader_texture_array_opt '}' ';'
{
  // incorect params count
  if(_ishParamIndex!=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect texture params count\nExpecting %d but found %d",_ctshParamsMax,_ishParamIndex);
    yyerror((char*)(const char*)strErr);
  }
};

shader_texture_array_opt
: /*null*/
| shader_texture_array;

shader_texture_array
: shader_texture
| shader_texture_array shader_texture
;

shader_texture
: c_string ';'
{
  if(_ishParamIndex>=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect texture params count %d",_ctshParamsMax);
    yyerror((char*)(const char*)strErr);
  }
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  // set ID of current texture name
  SurfShader.ss_spShaderParams.sp_aiTextureIDs[_ishParamIndex] = ska_GetIDFromStringTable($1);
  // increase shading parametar index
  _ishParamIndex++;
};

shader_uvmaps
:k_SHADER_UVMAPS c_int 
{
  _ctshParamsMax = $2;
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  SurfShader.ss_spShaderParams.sp_aiTexCoordsIndex.New(_ctshParamsMax);
  _ishParamIndex = 0;
} '{' shader_uvmaps_array_opt '}' ';'
{
  // incorect params count
  if(_ishParamIndex!=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect uvmap params count\nExpecting %d but found %d",_ctshParamsMax,_ishParamIndex);
    yyerror((char*)(const char*)strErr);
  }
};

shader_uvmaps_array_opt
: /*null*/
| shader_uvmaps_array;

shader_uvmaps_array
: shader_uvmap
| shader_uvmaps_array shader_uvmap;

shader_uvmap
: c_int ';'
{
  if(_ishParamIndex>=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect uvmap params count %d",_ctshParamsMax);
    yyerror((char*)(const char*)strErr);
  }
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  // set index of current uvmap name
  SurfShader.ss_spShaderParams.sp_aiTexCoordsIndex[_ishParamIndex] = $1;
  // increase shading parametar index
  _ishParamIndex++;
};

shader_colors
: k_SHADER_COLORS c_int
{
  _ctshParamsMax = $2;
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  SurfShader.ss_spShaderParams.sp_acolColors.New(_ctshParamsMax);
  _ishParamIndex = 0;
} '{' shader_colors_array_opt '}' ';'
{
  // incorect params count
  if(_ishParamIndex!=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect color params count\nExpecting %d but found %d",_ctshParamsMax,_ishParamIndex);
    yyerror((char*)(const char*)strErr);
  }
};

shader_colors_array_opt
: /*null*/
| shader_colors_array;

shader_colors_array
: shader_color
| shader_colors_array shader_color;

shader_color
: c_int ';'
{
  if(_ishParamIndex>=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect colors params count %d",_ctshParamsMax);
    yyerror((char*)(const char*)strErr);
  }
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  // set color
  SurfShader.ss_spShaderParams.sp_acolColors[_ishParamIndex] = $1;
  // increase color parametar index
  _ishParamIndex++;
};

shader_floats
: k_SHADER_FLOATS c_int
{
  _ctshParamsMax = $2;
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  SurfShader.ss_spShaderParams.sp_afFloats.New(_ctshParamsMax);
  _ishParamIndex = 0;

} '{' shader_floats_array_opt '}' ';'
{
  // incorect params count
  if(_ishParamIndex!=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect floats params count\nExpecting %d but found %d",_ctshParamsMax,_ishParamIndex);
    yyerror((char*)(const char*)strErr);
  }
};

shader_floats_array_opt
: /*null*/
| shader_floats_array;

shader_floats_array
: shader_float
| shader_floats_array shader_float;

shader_float
: float_const ';'
{
  if(_ishParamIndex>=_ctshParamsMax)
  {
    CTString strErr = CTString(0,"Incorect floats params count %d",_ctshParamsMax);
    yyerror((char*)(const char*)strErr);
  }
  // set color
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  SurfShader.ss_spShaderParams.sp_afFloats[_ishParamIndex] = $1;
  // increase floats parametar index
  _ishParamIndex++;
};

shader_flag_opt
: /*null*/
| shader_flags;

shader_flags
: k_SHADER_FLAGS c_int ';'
{
  SurfaceShader &SurfShader = _assSurfaceShaders[_iShaderSurfIndex];
  SurfShader.ss_spShaderParams.sp_ulFlags = $2;
}

// Mesh importer
mesh_begin
: k_SE_MESH c_float ';'
{
  if ($2!=0.1) NOTHING; //parseMesh->error
  INDEX ctmshlod = _yy_pMesh->msh_aMeshLODs.Count();
  _yy_pMesh->msh_aMeshLODs.Expand(ctmshlod+1);
  pMeshLOD = &_yy_pMesh->msh_aMeshLODs[ctmshlod];
  pMeshLOD->mlod_fnSourceFile = strCurentFileName;
  pMeshLOD->mlod_fMaxDistance = _fCurentMaxDistance;
  _yy_iWIndex=0;
  _yy_iMIndex=0;
}
;

is_full_faceforward_opt
: /*null*/
{
  pMeshLOD->mlod_ulFlags &= ~ML_FULL_FACE_FORWARD;
}
| k_FULL_FACE_FORWARD boolean
{
  if($2 == TRUE) {
    pMeshLOD->mlod_ulFlags|=ML_FULL_FACE_FORWARD;
  } else {
    pMeshLOD->mlod_ulFlags &=~ML_FULL_FACE_FORWARD;
  }
}

is_half_faceforward_opt
: /*null*/
{
  pMeshLOD->mlod_ulFlags &= ~ML_HALF_FACE_FORWARD;
}
| k_HALF_FACE_FORWARD boolean
{
  if($2 == TRUE) {
    pMeshLOD->mlod_ulFlags|=ML_HALF_FACE_FORWARD;
  } else {
    pMeshLOD->mlod_ulFlags &=~ML_HALF_FACE_FORWARD;
  }
}


vertices
: k_VERTICES c_int '{' {
  _ctVertices = $2;
  if(_ctVertices<0) {
    yyerror("Negative vertex count found %d",_ctVertices);
  }

  pMeshLOD->mlod_aVertices.New(_ctVertices);
  _iVertex = 0;
}
 vert3_array '}'
{
  if(_iVertex!=_ctVertices) {
    yyerror("Incorect number of vertices.\nExpecting %d but found only %d",_ctVertices,_iVertex);
  }
}
;

vert3_array 
: vert3_array_value 
| vert3_array vert3_array_value
;

vert3_array_value
: vert3 ';' {
  if(_iVertex>=_ctVertices) {
    yyerror("Incorect number of vertices - %d",_ctVertices);
  }

  pMeshLOD->mlod_aVertices[_iVertex].x = $1[0];
  pMeshLOD->mlod_aVertices[_iVertex].y = $1[1];
  pMeshLOD->mlod_aVertices[_iVertex].z = $1[2];
  _iVertex++;
}
;


normals
: k_NORMALS c_int '{' {
  _ctNormals = $2;
  if(_ctNormals != _ctVertices) {
    yyerror("Number of normals differs from the number of vertices!");
  }
  if(_ctNormals <0) {
    yyerror("Negative normal count found %d",_ctNormals);
  }

  _iNormal = 0;

  pMeshLOD->mlod_aNormals.New(_ctNormals);
}
 norm3_array '}'
{
  if(_iNormal!=_ctNormals) {
    yyerror("Incorect number of normals.\nExpecting %d but found only %d",_ctNormals,_iNormal);
  }
}
;

norm3_array
: norm3_array_value
| norm3_array norm3_array_value
;

norm3_array_value
: norm3 ';'
{
  if(_iNormal>=_ctNormals) {
    yyerror("Incorect number of normals - %d",_ctNormals);
  }

  pMeshLOD->mlod_aNormals[_iNormal].nx = $1[0];
  pMeshLOD->mlod_aNormals[_iNormal].ny = $1[1];
  pMeshLOD->mlod_aNormals[_iNormal].nz = $1[2];
  _iNormal++;
}
;

uvmaps
: k_UVMAPS c_int '{' {

  _ctUVMaps = $2;
  if(_ctUVMaps <0) {
    yyerror("Negative UVMaps count found %d",_ctUVMaps);
  }
  _iUVMap = 0;

  pMeshLOD->mlod_aUVMaps.New(_ctUVMaps);
}
uvmaps_opt_array '}'
{
  if(_iUVMap!=_ctUVMaps) {
    yyerror("Incorect number of UVMaps.\nExpecting %d but found only %d",_ctUVMaps,_iUVMap);
  }
}
;

uvmaps_opt_array
: /*null*/
| uvmaps_array
;

uvmaps_array
: uvmap 
| uvmaps_array uvmap
;

uvmap
: '{' k_NAME c_string ';' k_TEXCOORDS c_int '{'
{
  if(_iUVMap>=_ctUVMaps) {
    yyerror("Incorect number of UVMaps - %d",_ctUVMaps);
  }

  _ctTexCoords = $6;
  if(_ctTexCoords != _ctVertices) {
    yyerror("Number of texcords differs from the number of vertices!");
  }
  if(_ctTexCoords <0) {
    yyerror("Negative texcoords count found %d",_ctTexCoords);
  }
  _iTexCoord = 0;

  MeshUVMap &uvmap = pMeshLOD->mlod_aUVMaps[_iUVMap];
  uvmap.muv_aTexCoords.New(_ctTexCoords);
  uvmap.muv_iID = ska_GetIDFromStringTable($3);
}
uv2_array '}' '}'
{
  _iUVMap++;
  if(_iTexCoord!=_ctTexCoords) {
    yyerror("Incorect number of TexCoords for UVMap %d.\nExpecting %d but found only %d",_iUVMap,_ctTexCoords,_iTexCoord);
  }
}
;

uv2_array
: uv2_array_value
| uv2_array uv2_array_value
;

uv2_array_value
: uv2 ';'
{
  if(_iTexCoord>=_ctTexCoords) {
    yyerror("Incorect number of TexCoords - %d",_ctTexCoords);
  }

  MeshUVMap &uvmap = pMeshLOD->mlod_aUVMaps[_iUVMap];
  MeshTexCoord &mtc = uvmap.muv_aTexCoords[_iTexCoord];

  mtc.u = $1[0];
  mtc.v = $1[1];
  _iTexCoord++;
}
;

surfaces
: k_SURFACES c_int '{'
{
  _ctSurfaces = $2;
  if(_ctSurfaces <0) {
    yyerror("Negative surface count found %d",_ctSurfaces);
  }

  _iSurface = 0;

  pMeshLOD->mlod_aSurfaces.New(_ctSurfaces);

  // reset shader for each surface
  for(INDEX isrf=0;isrf<_ctSurfaces;isrf++) {
    MeshSurface &msrf = pMeshLOD->mlod_aSurfaces[isrf];
    msrf.msrf_pShader = NULL;
  }
}
 surfaces_array '}'
{
  if(_iSurface!=_ctSurfaces) {
    yyerror("Incorect number of surfaces.\nExpecting %d but found only %d",_ctSurfaces,_iSurface);
  }
}
;

surfaces_array
: triangleset 
| surfaces_array triangleset
;

triangleset
: '{' k_NAME c_string ';' k_TRIANGLE_SET c_int '{'
{
  _ctTriangles = $6;
  if(_ctTriangles <0) {
    yyerror("Negative triangle count found %d",_ctTriangles);
  }

  if(_iSurface>=_ctSurfaces) {
    yyerror("Incorect number of surfaces - %d",_ctSurfaces);
  }

  MeshSurface &msrf = pMeshLOD->mlod_aSurfaces[_iSurface];
  msrf.msrf_aTriangles.New(_ctTriangles);
  msrf.msrf_iSurfaceID = ska_GetIDFromStringTable($3);
  msrf.msrf_pShader = NULL;

  _iTriangle = 0;

  INDEX ctSrfSha = _assSurfaceShaders.Count();
  // find this surface in array of shader surfaces that was parsed before mesh
  for(INDEX iSrfSha=0;iSrfSha<ctSrfSha;iSrfSha++)
  {
    // reset shader
    SurfaceShader &ssSurfShader = _assSurfaceShaders[iSrfSha];
    if(ssSurfShader.ss_iSurfaceID == msrf.msrf_iSurfaceID)
    {
      // copy shader texture ID's to mesh
      msrf.msrf_ShadingParams.sp_aiTextureIDs = ssSurfShader.ss_spShaderParams.sp_aiTextureIDs;
      msrf.msrf_ShadingParams.sp_aiTexCoordsIndex = ssSurfShader.ss_spShaderParams.sp_aiTexCoordsIndex;
      msrf.msrf_ShadingParams.sp_acolColors = ssSurfShader.ss_spShaderParams.sp_acolColors;
      msrf.msrf_ShadingParams.sp_afFloats = ssSurfShader.ss_spShaderParams.sp_afFloats;
      msrf.msrf_ShadingParams.sp_ulFlags  = ssSurfShader.ss_spShaderParams.sp_ulFlags;
      // set shader for this surface
      if(ssSurfShader.fnShaderName.Length()>0) {
        ChangeSurfaceShader_t(msrf,ssSurfShader.fnShaderName);
      } else {
        msrf.msrf_pShader=NULL;
      }
      break;
    }
  }
}
 triangle_array '}' '}'
{
  _iSurface++;
  if(_iTriangle!=_ctTriangles) {
    yyerror("Incorect number of triangles for surface %d.\nExpecting %d but found only %d",_iSurface,_ctTriangles,_iTriangle);
  }
}
;

triangle_array
: triangle_array_value
| triangle_array triangle_array_value
;

triangle_array_value
: triangle ';'
{
  if(_iTriangle>=_ctTriangles) {
    yyerror("Incorect number of triangles - %d",_ctTriangles);
  }

  MeshSurface &msrf = pMeshLOD->mlod_aSurfaces[_iSurface];
  MeshTriangle &mt = msrf.msrf_aTriangles[_iTriangle];
  mt.iVertex[0]=$1[0];
  mt.iVertex[1]=$1[1];
  mt.iVertex[2]=$1[2];
  _iTriangle++;
}
;


weights
: k_WEIGHTS c_int '{'
{
  _ctWeights = $2;
  if(_ctWeights <0) {
    yyerror("Negative weights count found %d",_ctWeights);
  }

  _iWeight = 0;
  pMeshLOD->mlod_aWeightMaps.New(_ctWeights);
}
 weights_array '}'
{
  if(_iWeight!=_ctWeights) {
    yyerror("Incorect number of weights.\nExpecting %d but found only %d",_ctWeights,_iWeight);
  }
}
;

weights_array
:/*null*/
|weights_array_notnull
;

weights_array_notnull
: weightset
| weights_array weightset
;

weightset
: '{' k_NAME c_string ';' k_WEIGHT_SET c_int '{'
{
  _ctVertexWeights = $6;
  if(_ctVertexWeights <0) {
    yyerror("Negative vertex weights count found %d",_ctVertexWeights);
  }

  if(_iWeight>=_ctWeights) {
    yyerror("Incorect number of weights - %d",_ctWeights);
  }

  MeshWeightMap &mwm = pMeshLOD->mlod_aWeightMaps[_iWeight];

  mwm.mwm_iID = ska_GetIDFromStringTable($3);
  mwm.mwm_aVertexWeight.New(_ctVertexWeights);
  _iVertexWeight = 0;
}
  weight_map_array '}' '}'
{
  _iWeight++;
  if(_iVertexWeight!=_ctVertexWeights) {
    yyerror("Incorect number of vertex weights.\nExpecting %d but found only %d",_ctVertexWeights,_iVertexWeight);
  }
}
;

weight_map_array
: weight_map_array_value
| weight_map_array weight_map_array_value
;

weight_map_array_value
: '{' c_int ';' float_const ';' '}'
{
  if(_iVertexWeight>=_ctVertexWeights) {
    yyerror("Incorect number of vertex weights - %d",_ctVertexWeights);
  }

  MeshWeightMap &mwm = pMeshLOD->mlod_aWeightMaps[_iWeight];
  MeshVertexWeight &mvw = mwm.mwm_aVertexWeight[_iVertexWeight];
  
  FLOAT fWeight = $4;
  if(fWeight<0) {
    yyerror("Weight value is negative");
  }

  mvw.mww_iVertex = $2;
  mvw.mww_fWeight = fWeight;
  _iVertexWeight++;
}
;


morphs
: k_MORPHS c_int '{'
{
  _ctMorphs = $2;

  if(_ctMorphs<0) {
    yyerror("Negative morph count found %d",_ctMorphs);
  }

  pMeshLOD->mlod_aMorphMaps.New($2);
  _iMorph = 0;
}
 morphs_array '}'
{
  if(_iMorph!=_ctMorphs) {
    yyerror("Incorect number of morphs.\nExpecting %d but found only %d",_ctMorphs,_iMorph);
  }
}
;

morphs_array
: /*null*/
| morphs_array_notnull
;
morphs_array_notnull
: morphset
| morphs_array morphset
;

morphset
: '{' k_NAME c_string ';' k_RELATIVE boolean k_MORPH_SET c_int
{
  _ctVertexMorphs = $8;

  if(_ctVertexMorphs <0) {
    yyerror("Negative vertex morphs count found %d",_ctVertexMorphs);
  }

  if(_iMorph>=_ctMorphs) {
    yyerror("Incorect number of morphs - %d",_ctMorphs);
  }

  MeshMorphMap &mmm = pMeshLOD->mlod_aMorphMaps[_iMorph];
  mmm.mmp_aMorphMap.New($8);
  mmm.mmp_iID = ska_GetIDFromStringTable($3);
  mmm.mmp_bRelative = $6;
  _iVertexMorph = 0;
}
 '{' morph_set_array '}' '}'
{
  _iMorph++;
  if(_iVertexMorph!=_ctVertexMorphs) {
    yyerror("Incorect number of vertex morphs.\nExpecting %d but found only %d",_ctVertexMorphs,_iVertexMorph);
  }
}
;

morph_set_array
: morph_set_array_value
| morph_set_array morph_set_array_value
;

morph_set_array_value
: '{' c_int ';' float_const ',' float_const ',' float_const ';'
                float_const ',' float_const ',' float_const ';' '}'
{
  if(_iVertexMorph>=_ctVertexMorphs) {
    yyerror("Incorect number of vertex morphs - %d",_ctVertexMorphs);
  }

  // add mesh vertex morph to morph array
  MeshMorphMap &mmm = pMeshLOD->mlod_aMorphMaps[_iMorph];
  MeshVertexMorph &mwm = mmm.mmp_aMorphMap[_iVertexMorph];
  mwm.mwm_iVxIndex = $2;
  mwm.mwm_x = $4;
  mwm.mwm_y = $6;
  mwm.mwm_z = $8;
  mwm.mwm_nx = $10;
  mwm.mwm_ny = $12;
  mwm.mwm_nz = $14;
   _iVertexMorph++;
}
;


mesh_end
: k_SE_MESH_END ';'
;


float_const
: c_float
{
  $$ = $1;
}
| c_int {
  $$ = (float)$1;
}
;
  
int_const
: c_int {
  $$ = $1;
}
;

uv2
: float_const ',' float_const {
  $$[0] = $1;
  $$[1] = $3;
}
;

vert3
: float_const ',' float_const ',' float_const {
  $$[0] = $1;
  $$[1] = $3;
  $$[2] = $5;
}
;

norm3
: float_const ',' float_const ',' float_const {
  $$[0] = $1;
  $$[1] = $3;
  $$[2] = $5;
}
;

triangle
: int_const ',' int_const ',' int_const {
  $$[0] = $1;
  $$[1] = $3;
  $$[2] = $5;
}
;

matrix
: float_const ',' float_const ',' float_const ',' float_const ',' float_const ',' float_const ',' float_const ',' float_const ',' float_const ',' float_const ',' float_const ',' float_const {
  $$[0] = $1;
  $$[1] = $3;
  $$[2] = $5;
  $$[3] = $7;
  $$[4] = $9;
  $$[5] = $11;
  $$[6] = $13;
  $$[7] = $15;
  $$[8] = $17;
  $$[9] = $19;
  $$[10] = $21;
  $$[11] = $23;
}
;

boolean
: k_TRUE ';'
{
  $$ = TRUE;
}
| k_FALSE ';'
{
  $$ = FALSE;
}
;
%%