wip: backport vertex shader to arb assembly

This commit is contained in:
Eric Wasylishen 2014-10-17 17:09:55 -04:00
parent f4cd8e8e5f
commit f87c677fd0
7 changed files with 332 additions and 7 deletions

View file

@ -0,0 +1,10 @@
#!/bin/sh
cgc -o vpalias.vp -profile arbvp1 -oglsl vsalias.glsl
rm vpalias.h
while read line
do
echo "\"$line\\\\n\"" >> vpalias.h
done < vpalias.vp

View file

@ -127,6 +127,31 @@ QS_PFNGLUNIFORM1FPROC GL_Uniform1fFunc = NULL; //ericw
QS_PFNGLUNIFORM3FPROC GL_Uniform3fFunc = NULL; //ericw
QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc = NULL; //ericw
// vertex/fragment program
PFNGLBINDPROGRAMARBPROC qglBindProgramARB = NULL;
PFNGLDELETEPROGRAMSARBPROC qglDeleteProgramsARB = NULL;
PFNGLGENPROGRAMSARBPROC qglGenProgramsARB = NULL;
PFNGLGETPROGRAMENVPARAMETERDVARBPROC qglGetProgramEnvParameterdvARB = NULL;
PFNGLGETPROGRAMENVPARAMETERFVARBPROC qglGetProgramEnvParameterfvARB = NULL;
PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC qglGetProgramLocalParameterdvARB = NULL;
PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC qglGetProgramLocalParameterfvARB = NULL;
PFNGLGETPROGRAMSTRINGARBPROC qglGetProgramStringARB = NULL;
PFNGLGETPROGRAMIVARBPROC qglGetProgramivARB = NULL;
PFNGLISPROGRAMARBPROC qglIsProgramARB = NULL;
PFNGLPROGRAMENVPARAMETER4DARBPROC qglProgramEnvParameter4dARB = NULL;
PFNGLPROGRAMENVPARAMETER4DVARBPROC qglProgramEnvParameter4dvARB = NULL;
PFNGLPROGRAMENVPARAMETER4FARBPROC qglProgramEnvParameter4fARB = NULL;
PFNGLPROGRAMENVPARAMETER4FVARBPROC qglProgramEnvParameter4fvARB = NULL;
PFNGLPROGRAMLOCALPARAMETER4DARBPROC qglProgramLocalParameter4dARB = NULL;
PFNGLPROGRAMLOCALPARAMETER4DVARBPROC qglProgramLocalParameter4dvARB = NULL;
PFNGLPROGRAMLOCALPARAMETER4FARBPROC qglProgramLocalParameter4fARB = NULL;
PFNGLPROGRAMLOCALPARAMETER4FVARBPROC qglProgramLocalParameter4fvARB = NULL;
PFNGLPROGRAMSTRINGARBPROC qglProgramStringARB = NULL;
PFNGLVERTEXATTRIBPOINTERARBPROC qglVertexAttribPointerARB = NULL;
PFNGLENABLEVERTEXATTRIBARRAYARBPROC qglEnableVertexAttribArrayARB = NULL;
PFNGLDISABLEVERTEXATTRIBARRAYARBPROC qglDisableVertexAttribArrayARB = NULL;
//====================================
//johnfitz -- new cvars
@ -1068,11 +1093,34 @@ static void GL_CheckExtensions (void)
Con_Warning ("GLSL not available\n");
}
}
if (!(qglBindProgramARB = (PFNGLBINDPROGRAMARBPROC) SDL_GL_GetProcAddress ("glBindProgramARB"))) return;
if (!(qglDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) SDL_GL_GetProcAddress ("glDeleteProgramsARB"))) return;
if (!(qglGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) SDL_GL_GetProcAddress ("glGenProgramsARB"))) return;
if (!(qglGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) SDL_GL_GetProcAddress ("glGetProgramEnvParameterdvARB"))) return;
if (!(qglGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) SDL_GL_GetProcAddress ("glGetProgramEnvParameterfvARB"))) return;
if (!(qglGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) SDL_GL_GetProcAddress ("glGetProgramLocalParameterdvARB"))) return;
if (!(qglGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) SDL_GL_GetProcAddress ("glGetProgramLocalParameterfvARB"))) return;
if (!(qglGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) SDL_GL_GetProcAddress ("glGetProgramStringARB"))) return;
if (!(qglGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) SDL_GL_GetProcAddress ("glGetProgramivARB"))) return;
if (!(qglIsProgramARB = (PFNGLISPROGRAMARBPROC) SDL_GL_GetProcAddress ("glIsProgramARB"))) return;
if (!(qglProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) SDL_GL_GetProcAddress ("glProgramEnvParameter4dARB"))) return;
if (!(qglProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) SDL_GL_GetProcAddress ("glProgramEnvParameter4dvARB"))) return;
if (!(qglProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) SDL_GL_GetProcAddress ("glProgramEnvParameter4fARB"))) return;
if (!(qglProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) SDL_GL_GetProcAddress ("glProgramEnvParameter4fvARB"))) return;
if (!(qglProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) SDL_GL_GetProcAddress ("glProgramLocalParameter4dARB"))) return;
if (!(qglProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) SDL_GL_GetProcAddress ("glProgramLocalParameter4dvARB"))) return;
if (!(qglProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) SDL_GL_GetProcAddress ("glProgramLocalParameter4fARB"))) return;
if (!(qglProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) SDL_GL_GetProcAddress ("glProgramLocalParameter4fvARB"))) return;
if (!(qglProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) SDL_GL_GetProcAddress ("glProgramStringARB"))) return;
if (!(qglVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) SDL_GL_GetProcAddress ("glVertexAttribPointerARB"))) return;
if (!(qglEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress ("glEnableVertexAttribArrayARB"))) return;
if (!(qglDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress ("glDisableVertexAttribArrayARB"))) return;
}
/*
===============
GL_SetupState -- johnfitz
===============
GL_SetupState -- johnfitz
does all the stuff from GL_Init that needs to be done every time a new GL render context is created
===============

View file

@ -218,6 +218,32 @@ extern QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc;
extern qboolean gl_glsl_able;
// ericw --
// vertex/fragment program
extern PFNGLBINDPROGRAMARBPROC qglBindProgramARB;
extern PFNGLDELETEPROGRAMSARBPROC qglDeleteProgramsARB;
extern PFNGLGENPROGRAMSARBPROC qglGenProgramsARB;
extern PFNGLGETPROGRAMENVPARAMETERDVARBPROC qglGetProgramEnvParameterdvARB;
extern PFNGLGETPROGRAMENVPARAMETERFVARBPROC qglGetProgramEnvParameterfvARB;
extern PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC qglGetProgramLocalParameterdvARB;
extern PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC qglGetProgramLocalParameterfvARB;
extern PFNGLGETPROGRAMSTRINGARBPROC qglGetProgramStringARB;
extern PFNGLGETPROGRAMIVARBPROC qglGetProgramivARB;
extern PFNGLISPROGRAMARBPROC qglIsProgramARB;
extern PFNGLPROGRAMENVPARAMETER4DARBPROC qglProgramEnvParameter4dARB;
extern PFNGLPROGRAMENVPARAMETER4DVARBPROC qglProgramEnvParameter4dvARB;
extern PFNGLPROGRAMENVPARAMETER4FARBPROC qglProgramEnvParameter4fARB;
extern PFNGLPROGRAMENVPARAMETER4FVARBPROC qglProgramEnvParameter4fvARB;
extern PFNGLPROGRAMLOCALPARAMETER4DARBPROC qglProgramLocalParameter4dARB;
extern PFNGLPROGRAMLOCALPARAMETER4DVARBPROC qglProgramLocalParameter4dvARB;
extern PFNGLPROGRAMLOCALPARAMETER4FARBPROC qglProgramLocalParameter4fARB;
extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC qglProgramLocalParameter4fvARB;
extern PFNGLPROGRAMSTRINGARBPROC qglProgramStringARB;
extern PFNGLVERTEXATTRIBPOINTERARBPROC qglVertexAttribPointerARB;
extern PFNGLENABLEVERTEXATTRIBARRAYARBPROC qglEnableVertexAttribArrayARB;
extern PFNGLDISABLEVERTEXATTRIBARRAYARBPROC qglDisableVertexAttribArrayARB;
//ericw -- NPOT texture support
extern qboolean gl_texture_NPOT;

View file

@ -84,6 +84,47 @@ void *GLARB_GetNormalOffset (aliashdr_t *hdr, int pose)
return (void *)(currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + normaloffs);
}
// from RMQEngine
GLuint GL_CreateProgram (const GLchar *source)
{
GLuint progid;
GLint errPos;
const GLubyte *errString;
GLenum errGLErr;
qglGenProgramsARB (1, &progid);
qglBindProgramARB (GL_VERTEX_PROGRAM_ARB, progid);
errGLErr = glGetError ();
qglProgramStringARB (GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen (source), source);
errGLErr = glGetError ();
// Find the error position
glGetIntegerv (GL_PROGRAM_ERROR_POSITION_ARB, &errPos);
errString = glGetString (GL_PROGRAM_ERROR_STRING_ARB);
if (errGLErr != GL_NO_ERROR) Con_Printf ("Generic OpenGL Error\n");
if (errPos != -1) Con_Printf ("Program error at position: %d\n", errPos);
if (errString && errString[0]) Con_Printf ("Program error: %s\n", errString);
if ((errPos != -1) || (errString && errString[0]) || (errGLErr != GL_NO_ERROR))
{
Con_Printf ("Program:\n%s\n", source);
qglDeleteProgramsARB (1, &progid);
qglBindProgramARB (GL_VERTEX_PROGRAM_ARB, 0);
return 0;
}
else
{
// gl_arb_programs[gl_num_arb_programs] = progid;
// gl_num_arb_programs++;
qglBindProgramARB (GL_VERTEX_PROGRAM_ARB, 0);
return progid;
}
}
/*
=============
GL_DrawAliasFrame_GLSL -- ericw
@ -104,8 +145,21 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata)
blend = 0;
}
// ericw -- ARB shader
static GLuint shader;
if (shader == 0)
{
const GLchar *source =
#include "vpalias.h"
;
shader = GL_CreateProgram(source);
}
//
// ericw -- shader
#if 0
static GLuint shader;
static GLuint program;
static GLuint blendLoc;
@ -210,7 +264,7 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata)
}
GL_UseProgramFunc(program);
// ericw --
// ericw -- bind it and stuff
@ -244,13 +298,13 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata)
GL_Uniform1fFunc(blendLoc, blend);
GL_Uniform3fFunc(shadevectorLoc, shadevector[0], shadevector[1], shadevector[2]);
GL_Uniform4fFunc(lightColorLoc, lightcolor[0], lightcolor[1], lightcolor[2], entalpha);
#endif
// draw
glDrawElements(GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (void *)(intptr_t)currententity->model->vboindexofs);
// clean up
#if 0
GL_DisableVertexAttribArrayFunc(pose1VertexAttrIndex);
GL_DisableVertexAttribArrayFunc(pose2VertexAttrIndex);
@ -267,7 +321,7 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata)
GL_DisableVertexAttribArrayFunc(pose2NormalAttrIndex);
GL_UseProgramFunc(0);
#endif
rs_aliaspasses += paliashdr->numtris;
}

View file

@ -0,0 +1,78 @@
"!!ARBvp1.0\n"
"# cgc version 3.1.0013, build date Apr 18 2012\n"
"# command line args: -profile arbvp1 -oglsl\n"
"# source file: vsalias.glsl\n"
"#vendor NVIDIA Corporation\n"
"#version 3.1.0.13\n"
"#profile arbvp1\n"
"#program main\n"
"#semantic gl_ModelViewProjectionMatrix : state.matrix.mvp.transpose\n"
"#semantic gl_ModelViewMatrix : state.matrix.modelview.transpose\n"
"#semantic Blend\n"
"#semantic ShadeVector\n"
"#semantic LightColor\n"
"#var float4 gl_TexCoord[0] : $vout.TEX0 : TEX0 : -1 : 1\n"
"#var float4 gl_TexCoord[1] : $vout.TEX1 : TEX1 : -1 : 1\n"
"#var float4 gl_TexCoord[2] : : : -1 : 0\n"
"#var float4 gl_TexCoord[3] : : : -1 : 0\n"
"#var float4 gl_TexCoord[4] : : : -1 : 0\n"
"#var float4 gl_TexCoord[5] : : : -1 : 0\n"
"#var float4 gl_TexCoord[6] : : : -1 : 0\n"
"#var float4 gl_TexCoord[7] : : : -1 : 0\n"
"#var float4 gl_MultiTexCoord0 : $vin.TEXCOORD0 : TEXCOORD0 : -1 : 1\n"
"#var float4 gl_Position : $vout.POSITION : HPOS : -1 : 1\n"
"#var float4x4 gl_ModelViewProjectionMatrix : state.matrix.mvp.transpose : c[1], 4 : -1 : 1\n"
"#var float4 gl_FrontColor : $vout.COLOR0 : COL0 : -1 : 1\n"
"#var float4x4 gl_ModelViewMatrix : state.matrix.modelview.transpose : c[5], 4 : -1 : 1\n"
"#var float gl_FogFragCoord : $vout.FOGC : FOGC : -1 : 1\n"
"#var float Blend : : c[9] : -1 : 1\n"
"#var float3 ShadeVector : : c[10] : -1 : 1\n"
"#var float4 LightColor : : c[11] : -1 : 1\n"
"#var float4 Pose1Vert : $vin.ATTR0 : ATTR0 : -1 : 1\n"
"#var float3 Pose1Normal : $vin.ATTR1 : ATTR1 : -1 : 1\n"
"#var float4 Pose2Vert : $vin.ATTR2 : ATTR2 : -1 : 1\n"
"#var float3 Pose2Normal : $vin.ATTR3 : ATTR3 : -1 : 1\n"
"#const c[0] = 0.29545453 1 0\n"
"PARAM c[12] = { { 0.29545453, 1, 0 },\n"
"state.matrix.mvp.transpose.row[0..3],\n"
"state.matrix.modelview.transpose.row[0..3],\n"
"program.local[9..11] };\n"
"TEMP R0;\n"
"TEMP R1;\n"
"TEMP R2;\n"
"MOV R0, vertex.attrib[0];\n"
"ADD R0, vertex.attrib[2], -R0;\n"
"MAD R0, R0, c[9].x, vertex.attrib[0];\n"
"MUL R1, R0.y, c[2];\n"
"MAD R1, R0.x, c[1], R1;\n"
"MAD R1, R0.z, c[3], R1;\n"
"MUL R2.x, R0.y, c[6].z;\n"
"MAD R0.y, R0.x, c[5].z, R2.x;\n"
"MAD R0.x, R0.z, c[7].z, R0.y;\n"
"MAD R0.x, R0.w, c[8].z, R0;\n"
"ABS result.fogcoord.x, R0;\n"
"DP3 R0.x, vertex.attrib[1], c[10];\n"
"SLT R0.y, R0.x, c[0].z;\n"
"MAD R0.z, R0.x, c[0].x, c[0].y;\n"
"ADD R0.x, R0, -R0.z;\n"
"ABS R0.y, R0;\n"
"MAD result.position, R0.w, c[4], R1;\n"
"SGE R0.w, c[0].z, R0.y;\n"
"ADD R0.x, R0, c[0].y;\n"
"MAD R0.x, R0, R0.w, R0.z;\n"
"DP3 R0.y, vertex.attrib[3], c[10];\n"
"SLT R0.w, R0.y, c[0].z;\n"
"MAD R0.z, R0.y, c[0].x, c[0].y;\n"
"ADD R0.y, R0, -R0.z;\n"
"ABS R0.w, R0;\n"
"SGE R0.w, c[0].z, R0;\n"
"ADD R0.y, R0, c[0];\n"
"MAD R0.y, R0, R0.w, R0.z;\n"
"ADD R0.y, R0, -R0.x;\n"
"MOV R0.w, c[0].y;\n"
"MAD R0.xyz, R0.y, c[9].x, R0.x;\n"
"MOV result.texcoord[0], vertex.texcoord[0];\n"
"MOV result.texcoord[1], vertex.texcoord[0];\n"
"MUL result.color, R0, c[11];\n"
"END\n"
"# 34 instructions, 3 R-regs\n"

View file

@ -0,0 +1,78 @@
!!ARBvp1.0
# cgc version 3.1.0013, build date Apr 18 2012
# command line args: -profile arbvp1 -oglsl
# source file: vsalias.glsl
#vendor NVIDIA Corporation
#version 3.1.0.13
#profile arbvp1
#program main
#semantic gl_ModelViewProjectionMatrix : state.matrix.mvp.transpose
#semantic gl_ModelViewMatrix : state.matrix.modelview.transpose
#semantic Blend
#semantic ShadeVector
#semantic LightColor
#var float4 gl_TexCoord[0] : $vout.TEX0 : TEX0 : -1 : 1
#var float4 gl_TexCoord[1] : $vout.TEX1 : TEX1 : -1 : 1
#var float4 gl_TexCoord[2] : : : -1 : 0
#var float4 gl_TexCoord[3] : : : -1 : 0
#var float4 gl_TexCoord[4] : : : -1 : 0
#var float4 gl_TexCoord[5] : : : -1 : 0
#var float4 gl_TexCoord[6] : : : -1 : 0
#var float4 gl_TexCoord[7] : : : -1 : 0
#var float4 gl_MultiTexCoord0 : $vin.TEXCOORD0 : TEXCOORD0 : -1 : 1
#var float4 gl_Position : $vout.POSITION : HPOS : -1 : 1
#var float4x4 gl_ModelViewProjectionMatrix : state.matrix.mvp.transpose : c[1], 4 : -1 : 1
#var float4 gl_FrontColor : $vout.COLOR0 : COL0 : -1 : 1
#var float4x4 gl_ModelViewMatrix : state.matrix.modelview.transpose : c[5], 4 : -1 : 1
#var float gl_FogFragCoord : $vout.FOGC : FOGC : -1 : 1
#var float Blend : : c[9] : -1 : 1
#var float3 ShadeVector : : c[10] : -1 : 1
#var float4 LightColor : : c[11] : -1 : 1
#var float4 Pose1Vert : $vin.ATTR0 : ATTR0 : -1 : 1
#var float3 Pose1Normal : $vin.ATTR1 : ATTR1 : -1 : 1
#var float4 Pose2Vert : $vin.ATTR2 : ATTR2 : -1 : 1
#var float3 Pose2Normal : $vin.ATTR3 : ATTR3 : -1 : 1
#const c[0] = 0.29545453 1 0
PARAM c[12] = { { 0.29545453, 1, 0 },
state.matrix.mvp.transpose.row[0..3],
state.matrix.modelview.transpose.row[0..3],
program.local[9..11] };
TEMP R0;
TEMP R1;
TEMP R2;
MOV R0, vertex.attrib[0];
ADD R0, vertex.attrib[2], -R0;
MAD R0, R0, c[9].x, vertex.attrib[0];
MUL R1, R0.y, c[2];
MAD R1, R0.x, c[1], R1;
MAD R1, R0.z, c[3], R1;
MUL R2.x, R0.y, c[6].z;
MAD R0.y, R0.x, c[5].z, R2.x;
MAD R0.x, R0.z, c[7].z, R0.y;
MAD R0.x, R0.w, c[8].z, R0;
ABS result.fogcoord.x, R0;
DP3 R0.x, vertex.attrib[1], c[10];
SLT R0.y, R0.x, c[0].z;
MAD R0.z, R0.x, c[0].x, c[0].y;
ADD R0.x, R0, -R0.z;
ABS R0.y, R0;
MAD result.position, R0.w, c[4], R1;
SGE R0.w, c[0].z, R0.y;
ADD R0.x, R0, c[0].y;
MAD R0.x, R0, R0.w, R0.z;
DP3 R0.y, vertex.attrib[3], c[10];
SLT R0.w, R0.y, c[0].z;
MAD R0.z, R0.y, c[0].x, c[0].y;
ADD R0.y, R0, -R0.z;
ABS R0.w, R0;
SGE R0.w, c[0].z, R0;
ADD R0.y, R0, c[0];
MAD R0.y, R0, R0.w, R0.z;
ADD R0.y, R0, -R0.x;
MOV R0.w, c[0].y;
MAD R0.xyz, R0.y, c[9].x, R0.x;
MOV result.texcoord[0], vertex.texcoord[0];
MOV result.texcoord[1], vertex.texcoord[0];
MUL result.color, R0, c[11];
END
# 34 instructions, 3 R-regs

View file

@ -0,0 +1,31 @@
#version 110
uniform float Blend;
uniform vec3 ShadeVector;
uniform vec4 LightColor;
attribute vec4 Pose1Vert;
attribute vec3 Pose1Normal;
attribute vec4 Pose2Vert;
attribute vec3 Pose2Normal;
float r_avertexnormal_dot(vec3 vertexnormal) // from MH
{
float dot = dot(vertexnormal, ShadeVector);
// wtf - this reproduces anorm_dots within as reasonable a degree of tolerance as the >= 0 case
if (dot < 0.0)
return 1.0 + dot * (13.0 / 44.0);
else
return 1.0 + dot;
}
void main()
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord0;
vec4 lerpedVert = mix(Pose1Vert, Pose2Vert, Blend);
gl_Position = gl_ModelViewProjectionMatrix * lerpedVert;
float dot1 = r_avertexnormal_dot(Pose1Normal);
float dot2 = r_avertexnormal_dot(Pose2Normal);
gl_FrontColor = LightColor * vec4(vec3(mix(dot1, dot2, Blend)), 1.0);
// fog
vec3 ecPosition = vec3(gl_ModelViewMatrix * lerpedVert);
gl_FogFragCoord = abs(ecPosition.z);
};