OpenGL ESはサブセットのため関数が少ないです。入門書などでもよく使われるglBegin()とglEnd()がないのはつらいので実装しました。glNormal3f()やglVertex3f()をキャッシュしてglNormalPointer()やglVertexPointer()に渡しています。GL_QUADS/GL_QUAD_STRIPは四角形を2つの三角形に分割することで実装しました。また、glFrustum()もglMultMatrixf()に行列を渡すことで実装しました。
以下にコードを示します(パブリックドメイン)。ラッピングを進めているためC#です。完成したラッパーはid:n7shi:20091006にあります。
public static class GL { public const int GL_QUADS = 7; public const int GL_QUAD_STRIP = 8; private static GLenum _mode; private static List<float> vertices, normals; public static void glBegin(GLenum mode) { _mode = mode; normals = null; vertices = new List<float>(); } private static float _nx, _ny, _nz; public static void glVertex3f(float x, float y, float z) { vertices.AddRange(new[] { x, y, z }); if (normals != null) normals.AddRange(new[] { _nx, _ny, _nz }); } public static new void glNormal3f(float nx, float ny, float nz) { if (vertices != null) { if (normals == null) normals = new List<float>(); while (normals.Count < vertices.Count) normals.AddRange(new[] { _nx, _ny, _nz }); } NativeWrappers.GL.glNormal3f(_nx = nx, _ny = ny, _nz = nz); } public static void glEnd() { float[] vs, ns = null; if (_mode == GL_QUADS) { _mode = GL_TRIANGLES; var vsrc = vertices.ToArray(); vs = new float[vsrc.Length * 3 / 2]; for (int i = 0, j = 0; i < vsrc.Length; i += 12, j += 18) QuadToTriangle(vsrc, i, vs, j); if (normals != null) { var nsrc = normals.ToArray(); ns = new float[nsrc.Length * 3 / 2]; for (int i = 0, j = 0; i < nsrc.Length; i += 12, j += 18) QuadToTriangle(nsrc, i, vs, j); } } else { vs = vertices.ToArray(); if (normals != null) ns = normals.ToArray(); if (_mode == GL_QUAD_STRIP) _mode = GL_TRIANGLE_STRIP; } normals = null; vertices = null; glVertexPointer(3, GL_FLOAT, 0, vs); if (ns != null) glNormalPointer(GL_FLOAT, 0, ns); glEnableClientState(GL_VERTEX_ARRAY); if (ns != null) glEnableClientState(GL_NORMAL_ARRAY); glDrawArrays(_mode, 0, vs.Length / 3); glDisableClientState(GL_VERTEX_ARRAY); if (ns != null) { glDisableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT, 0, null); NativeWrappers.GL.glNormal3f(_nx, _ny, _nz); } } private static void QuadToTriangle(float[] source, int sourceIndex, float[] dest, int destIndex) { Array.Copy(source, sourceIndex, dest, destIndex, 9); Array.Copy(source, sourceIndex, dest, destIndex + 9, 3); Array.Copy(source, sourceIndex + 6, dest, destIndex + 12, 6); } public static void glFrustum( double left, double right, double bottom, double top, double near_val, double far_val) { var m = new float[16]; m[0] = (float)((2 * near_val) / (right - left)); m[5] = (float)((2 * near_val) / (top - bottom)); m[8] = (float)((right + left) / (right - left)); m[9] = (float)((top + bottom) / (top - bottom)); m[10] = (float)(-(far_val + near_val) / (far_val - near_val)); m[11] = -1; m[14] = (float)(-(2 * far_val * near_val) / (far_val - near_val)); glMultMatrixf(m); } }
glEnd()の最後で glNormalPointer(GL_FLOAT, 0, null); としていますが、これを省略すると挙動がおかしくなります。ESではないOpenGLでは省略しても問題ないことから、Vincent ESの癖のようです。これに気付くまで4時間くらいハマりました。