using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Text; using allegro; using alleggl; public class exext : AllegGL { /* This examples demonstrates how to use the AllegroGL extension mechanism. */ const float M_PI = 3.1415926535897932384626433832795f; const int WINDOW_W = 640; const int WINDOW_H = 480; const int MESH_SIZE = 64; static float[, ,] mesh = new float[MESH_SIZE, MESH_SIZE, 3]; static float wave_movement = 0.0f; /* Define our vertex program. * It basically does: * pos = vertex.position; * pos.y = (sin(wave.x + pos.x / 5) + sin(wave.x + pos.z / 4)) * 2.5; * result.position = modelview * projection * pos; */ /* Plain ARBvp doesn't have a SIN opcode, so we provide one, built on a taylor * expansion, with some fugding. * * First, we convert the operand to the [-pi..+pi] period by: * - Dividing by 2pi, adding 1/2 * - Taking the fraction of the result * - Multiplying by 2pi, then subtracting pi. * x' = frac((x / 2pi) + 0.5) * 2pi - pi * * Then, we compute the sine using a 7th order Taylor series centered at 0: * x' = x - x^3/3! + x^5/5! - x^7/7! * * Note that we begin by multiplying x by 0.98 as a fugding factor to * compensate for the fact that our Taylor series is just an approximation. * The error is then reduced to < 0.5% from the ideal sine function. */ static string SIN(string d, string s, string t) { /* Convert to [-pi..+pi] period */ return string.Format( "MAD \"{0}\", \"{1}\", one_over_pi, 0.5;\n" + "FRC \"{0}\",\"{0}\";\n" + "MAD \"{0}\",\"{0}\", two_pi, -pi;\n" + "MUL \"{0}\",\"{0}\", 0.98;\n" /* Scale input to compensate for prec error */+ /* Compute SIN(d), using a Taylor series */ "MUL \"{2}\".x, \"{0}\", \"{0}\";\n" /* x^2 */ + "MUL \"{2}\".y, \"{2}\".x, \"{0}\";\n" /* x^3 */ + "MUL \"{2}\".z, \"{2}\".y, \"{2}\".x;\n" /* x^5 */ + "MUL \"{2}\".w, \"{2}\".z, \"{2}\".x;\n" /* x^7 */ + "MAD \"{0}\", \"{2}\".y,-inv_3_fact, \"{0}\";\n" /* x - x^3/3! */ + "MAD \"{0}\", \"{2}\".z, inv_5_fact, \"{0}\";\n" /* x - x^3/3! + x^5/5! */ + "MAD \"{0}\", \"{2}\".w,-inv_7_fact, \"{0}\";\n" /* x - x^3/3! + x^5/5! - x^7/7!*/ , d, s, t); } /* This is the actual vertex program. * It computes sin(wave.x + pos.x / 5) and sin(wave.x + pos.z), adds them up, * scales the result by 2.5 and stores that as the vertex's y component. * * Then, it does the modelview-projection transform on the vertex. * * XXX Broken ATI drivers need a \n after each "line" */ static string program = "!!ARBvp1.0\n" + "ATTRIB pos = vertex.position;\n" + "ATTRIB wave = vertex.attrib[1];\n" + "PARAM modelview[4] = { state.matrix.mvp };\n" + "PARAM one_over_pi = 0.1591549;\n" + "PARAM pi = 3.1415926;\n" + "PARAM two_pi = 6.2831853;\n" + "PARAM inv_3_fact = 0.1666666;\n" + "PARAM inv_5_fact = 0.00833333333;\n" + "PARAM inv_7_fact = 0.00019841269841269;\n" + "TEMP temp, temp2;\n" + /* temp.y = sin(wave.x + pos.x / 5) */ "MAD temp.y, pos.x, 0.2, wave.x;\n" + SIN("temp.y", "temp.y", "temp2") + /* temp.y += sin(wave.x + pos.z / 4) */ "MAD temp.x, pos.z, 0.25, wave.x;\n" + SIN("temp.x", "temp.x", "temp2") + "ADD temp.y, temp.x, temp.y;\n" + /* pos.y = temp.y * 2.5 */ "MOV temp2, pos;\n" + "MUL temp2.y, temp.y, 2.5;\n" + /* Transform the position by the modelview matrix */ "DP4 result.position.w, temp2, modelview[3];\n" + "DP4 result.position.x, temp2, modelview[0];\n" + "DP4 result.position.y, temp2, modelview[1];\n" + "DP4 result.position.z, temp2, modelview[2];\n" + "MOV result.color, vertex.color;\n" + "END"; /* NVIDIA drivers do a better job; let's use a simpler program if we can. */ static string program_nv = "!!ARBvp1.0" + "OPTION NV_vertex_program2;" + "ATTRIB wave = vertex.attrib[1];" + "PARAM modelview[4] = { state.matrix.mvp };" + "TEMP temp;" + "TEMP pos;" + "MOV pos, vertex.position;" + /* temp.x = sin(wave.x + pos.x / 5) */ /* temp.z = sin(wave.x + pos.z / 4) */ "MAD temp.xz, pos, {0.2, 1.0, 0.25, 1.0}, wave.x;" + "SIN temp.x, temp.x;" + "SIN temp.z, temp.z;" + /* temp.y = temp.x + temp.z */ "ADD temp.y, temp.x, temp.z;" + /* pos.y = temp.y * 2.5 */ "MUL pos.y, temp.y, 2.5;" + /* Transform the position by the modelview matrix */ "DP4 result.position.w, pos, modelview[3];" + "DP4 result.position.x, pos, modelview[0];" + "DP4 result.position.y, pos, modelview[1];" + "DP4 result.position.z, pos, modelview[2];" + "MOV result.color, vertex.color;" + "END"; static void create_mesh() { int x, z; /* Create our mesh */ for (x = 0; x < MESH_SIZE; x++) { for (z = 0; z < MESH_SIZE; z++) { mesh[x, z, 0] = (float)(MESH_SIZE / 2) - x; mesh[x, z, 1] = 0.0f; mesh[x, z, 2] = (float)(MESH_SIZE / 2) - z; } } } static void draw_mesh() { int x, z; OpenGL.glClear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); OpenGL.glColor4f(0.5f, 1.0f, 0.5f, 1.0f); for (x = 0; x < MESH_SIZE - 1; x++) { OpenGL.glBegin(OpenGL.GL_TRIANGLE_STRIP); for (z = 0; z < MESH_SIZE - 1; z++) { //OpenGL.glVertexAttrib1fARB(1, wave_movement); //OpenGL.glVertex3fv(&mesh[x][z][0]); //OpenGL.glVertex3fv(&mesh[x + 1][z][0]); wave_movement += 0.00001f; if (wave_movement > 2 * M_PI) { wave_movement = 0.0f; } } OpenGL.glEnd(); } OpenGL.glFlush(); } static int Main() { uint pid = 0; allegro_init(); install_allegro_gl(); install_keyboard(); allegro_gl_clear_settings(); allegro_gl_set(AGL_COLOR_DEPTH, 32); allegro_gl_set(AGL_DOUBLEBUFFER, 1); allegro_gl_set(AGL_Z_DEPTH, 32); allegro_gl_set(AGL_WINDOWED, TRUE); allegro_gl_set(AGL_RENDERMETHOD, 1); allegro_gl_set(AGL_SAMPLES, 4); allegro_gl_set(AGL_SAMPLE_BUFFERS, 1); allegro_gl_set(AGL_SUGGEST, AGL_COLOR_DEPTH | AGL_DOUBLEBUFFER | AGL_RENDERMETHOD | AGL_Z_DEPTH | AGL_WINDOWED | AGL_SAMPLES | AGL_SAMPLE_BUFFERS); if (set_gfx_mode(GFX_OPENGL, WINDOW_W, WINDOW_H, 0, 0) != 0) { allegro_message(string.Format("Error setting OpenGL graphics mode:\n{0}\n" + "Allegro GL error : {1}\n", allegro_error, allegro_gl_error)); return -1; } if (AGL_EXTENSION_LIST_GL.allegro_gl_extensions_GL.ARB_multisample != 0) { OpenGL.glEnable(OpenGLExt.GL_MULTISAMPLE_ARB); } if (AGL_EXTENSION_LIST_GL.allegro_gl_extensions_GL.ARB_vertex_program == 0) { allegro_message("This example requires a video card that supports " + " the ARB_vertex_program extension.\n"); return -1; } OpenGL.glEnable(OpenGL.GL_DEPTH_TEST); OpenGL.glShadeModel(OpenGL.GL_SMOOTH); OpenGL.glHint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_NICEST); OpenGL.glPolygonMode(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_LINE); OpenGL.glDisable(OpenGL.GL_CULL_FACE); /* Setup projection and modelview matrices */ OpenGL.glMatrixMode(OpenGL.GL_PROJECTION); OpenGL.glLoadIdentity(); OpenGLU.gluPerspective(45.0, WINDOW_W / (float)WINDOW_H, 0.1, 100.0); OpenGL.glMatrixMode(OpenGL.GL_MODELVIEW); OpenGL.glLoadIdentity(); /* Position the camera to look at our mesh from a distance */ OpenGLU.gluLookAt(0.0f, 20.0f, -45.0f, 0.0f, 0.0f, 0.0f, 0, 1, 0); create_mesh(); /* Define the vertex program */ OpenGL.glEnable(OpenGLExt.GL_VERTEX_PROGRAM_ARB); OpenGLExt.glGenProgramsARB(1, ref pid); //OpenGL.glBindProgramARB(OpenGL.GL_VERTEX_PROGRAM_ARB, pid); OpenGL.glGetError(); //if (allegro_gl_extensions_GL.NV_vertex_program2_option) { // OpenGL.glProgramStringARB(OpenGL.GL_VERTEX_PROGRAM_ARB, OpenGL.GL_PROGRAM_FORMAT_ASCII_ARB, // program_nv.Length, program_nv); //} //else { // OpenGL.glProgramStringARB(OpenGL.GL_VERTEX_PROGRAM_ARB, OpenGL.GL_PROGRAM_FORMAT_ASCII_ARB, // program.Length, program); //} /* Check for errors */ if (OpenGL.glGetError() != 0) { //string pgm = // allegro_gl_extensions_GL.NV_vertex_program2_option // ? program_nv : program; int error_pos; //string error_str = OpenGL.glGetString(OpenGL.GL_PROGRAM_ERROR_STRING_ARB); //OpenGL.glGetIntegerv(OpenGL.GL_PROGRAM_ERROR_POSITION_ARB, &error_pos); //allegro_message(string.Format("Error compiling the vertex program:\n{0}\n\nat " + // "character: {1}\n{2}\n", error_str, (int)error_pos, // pgm + error_pos)); //Debug.WriteLine(string.Format("Error compiling the vertex program:\n{0}\n\nat " + // "character: {1}\n{2}\n", error_str, (int)error_pos, // pgm + error_pos)); return -1; } while (!key[KEY_ESC]) { draw_mesh(); allegro_gl_flip(); } //OpenGL.glDeleteProgramsARB(1, &pid); return 0; } }