public void Render(Matrix4 view) { shader = Runtime.shaders["MBN"]; GL.UseProgram(shader.programID); GL.Uniform1(shader.getAttribute("renderVertColor"), Runtime.renderVertColor ? 1 : 0); GL.Uniform1(shader.getAttribute("renderType"), 0);//(int)Runtime.renderType GL.Uniform1(shader.getAttribute("selectedBoneIndex"), Runtime.selectedBoneIndex); GL.UniformMatrix4(shader.getAttribute("modelview"), false, ref view); GL.Uniform3(shader.getAttribute("difLightColor"), Lights.diffuseLight.difR, Lights.diffuseLight.difG, Lights.diffuseLight.difB); GL.Uniform3(shader.getAttribute("ambLightColor"), Lights.diffuseLight.ambR, Lights.diffuseLight.ambG, Lights.diffuseLight.ambB); GL.ActiveTexture(TextureUnit.Texture10); GL.BindTexture(TextureTarget.Texture2D, RenderTools.UVTestPattern); GL.Uniform1(shader.getAttribute("UVTestPattern"), 10); Matrix4[] f = skeleton.getShaderMatrix(); int maxUniformBlockSize = GL.GetInteger(GetPName.MaxUniformBlockSize); int boneCount = skeleton.bones.Count; int dataSize = boneCount * Vector4.SizeInBytes * 4; GL.BindBuffer(BufferTarget.UniformBuffer, vbo_bone); GL.BufferData(BufferTarget.UniformBuffer, (IntPtr)(dataSize), IntPtr.Zero, BufferUsageHint.DynamicDraw); GL.BindBuffer(BufferTarget.UniformBuffer, 0); var blockIndex = GL.GetUniformBlockIndex(shader.programID, "bones"); GL.BindBufferBase(BufferRangeTarget.UniformBuffer, blockIndex, vbo_bone); if (f.Length > 0) { GL.BindBuffer(BufferTarget.UniformBuffer, vbo_bone); GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, (IntPtr)(f.Length * Vector4.SizeInBytes * 4), f); } shader.enableAttrib(); GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_vert); GL.BufferData <Vertex>(BufferTarget.ArrayBuffer, (IntPtr)(Vertex.Stride * Vertices.Length), Vertices, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(shader.getAttribute("pos"), 3, VertexAttribPointerType.Float, false, Vertex.Stride, 0); GL.VertexAttribPointer(shader.getAttribute("nrm"), 3, VertexAttribPointerType.Float, false, Vertex.Stride, 12); GL.VertexAttribPointer(shader.getAttribute("col"), 4, VertexAttribPointerType.Float, false, Vertex.Stride, 24); GL.VertexAttribPointer(shader.getAttribute("tx0"), 2, VertexAttribPointerType.Float, false, Vertex.Stride, 40); GL.VertexAttribPointer(shader.getAttribute("bone"), 2, VertexAttribPointerType.Float, false, Vertex.Stride, 48); GL.VertexAttribPointer(shader.getAttribute("weight"), 2, VertexAttribPointerType.Float, false, Vertex.Stride, 56); GL.PointSize(4f); //GL.DrawArrays(PrimitiveType.Points, 0, Vertices.Length); foreach (BCH_Mesh m in Nodes) { GL.Uniform4(shader.getAttribute("colorSamplerUV"), new Vector4(1, 1, 0, 0)); GL.ActiveTexture(TextureUnit.Texture0); BCH_Material material = (BCH_Material)((BCH)Parent.Parent).Materials.Nodes[m.MaterialIndex]; BCH_Texture tex = ((BCH)Parent.Parent).GetTexture(material.Text); GL.BindTexture(TextureTarget.Texture2D, tex == null ? VBNViewport.defaulttex : tex.display); GL.Uniform1(shader.getAttribute("tex"), 0); if (!m.Checked) { continue; } foreach (BCH_PolyGroup pg in m.Nodes) { GL.Uniform1(shader.getAttribute("boneList"), pg.BoneList.Length, pg.BoneList); GL.Disable(EnableCap.CullFace); GL.CullFace(CullFaceMode.Back); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_faces); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(pg.Faces.Length * sizeof(int)), pg.Faces, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.DrawElements(PrimitiveType.Triangles, pg.Faces.Length, DrawElementsType.UnsignedInt, 0); } } shader.disableAttrib(); }
public override void Read(string filename) { FileData f = new FileData(filename); f.Endian = System.IO.Endianness.Little; f.skip(4); int backwardCompatibility = f.readByte(); int forwardCompatibility = f.readByte(); int version = f.readShort(); int mainHeaderOffset = f.readInt(); int stringTableOffset = f.readInt(); int gpuCommandsOffset = f.readInt(); int dataOffset = f.readInt(); int dataExtendedOffset = 0; int dataExtendedLength = 0; if (backwardCompatibility > 0x20) { dataExtendedOffset = f.readInt(); } int relocationTableOffset = f.readInt(); int mainHeaderLength = f.readInt(); int stringTableLength = f.readInt(); int gpuCommandsLength = f.readInt(); int dataLength = f.readInt(); if (backwardCompatibility > 0x20) { dataExtendedLength = f.readInt(); } int relocationTableLength = f.readInt(); int uninitializedDataSectionLength = f.readInt(); int uninitializedDescriptionSectionLength = f.readInt(); if (backwardCompatibility > 7) { int flags = f.readShort(); int addressCount = f.readShort(); } // Relocation table for (int i = 0; i < relocationTableLength; i += 4) { f.seek(relocationTableOffset + i); int val = f.readInt(); int off = val & 0x1FFFFFF; byte flag = (byte)(val >> 25); switch (flag) { case 0: f.seek((off * 4) + mainHeaderOffset); f.writeInt((off * 4) + mainHeaderOffset, f.readInt() + mainHeaderOffset); break; case 1: f.seek(off + mainHeaderOffset); f.writeInt((off) + mainHeaderOffset, f.readInt() + stringTableOffset); break; case 2: f.seek((off * 4) + mainHeaderOffset); f.writeInt((off * 4) + mainHeaderOffset, f.readInt() + gpuCommandsOffset); break; case 0xc: f.seek((off * 4) + mainHeaderOffset); f.writeInt((off * 4) + mainHeaderOffset, f.readInt() + dataOffset); break; } f.seek((off * 4) + gpuCommandsOffset); if (backwardCompatibility < 6) { switch (flag) { case 0x23: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Texture case 0x25: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Vertex //case 0x26: f.writeInt((off * 4) + int gpuCommandsOffset, ((f.readInt() + int dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x27: f.writeInt((off * 4) + gpuCommandsOffset, (f.readInt() + dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (backwardCompatibility < 8) { switch (flag) { case 0x24: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Texture case 0x26: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Vertex //case 0x27: writer.Write(((peek(input) + int dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x28: f.writeInt((off * 4) + gpuCommandsOffset, (f.readInt() + dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (backwardCompatibility < 0x21) { switch (flag) { case 0x25: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Texture case 0x27: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Vertex //case 0x28: writer.Write(((peek(input) + int dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x29: f.writeInt((off * 4) + gpuCommandsOffset, (f.readInt() + dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else { switch (flag) { case 0x25: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Texture case 0x26: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataOffset); break; //Vertex relative to Data Offset //case 0x27: writer.Write(((peek(input) + int dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset case 0x28: f.writeInt((off * 4) + gpuCommandsOffset, (f.readInt() + dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset case 0x2b: f.writeInt((off * 4) + gpuCommandsOffset, f.readInt() + dataExtendedOffset); break; //Vertex relative to Data Extended Offset //case 0x2c: writer.Write(((peek(input) + int dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset case 0x2d: f.writeInt((off * 4) + gpuCommandsOffset, (f.readInt() + dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset } } } //File.WriteAllBytes(filename + "_offset", f.getSection(0, f.size())); f.seek(mainHeaderOffset); int modelsPointerTableOffset = f.readInt(); int modelsPointerTableEntries = f.readInt(); int modelsNameOffset = f.readInt(); int materialsPointerTableOffset = f.readInt(); int materialsPointerTableEntries = f.readInt(); int materialsNameOffset = f.readInt(); int shadersPointerTableOffset = f.readInt(); int shadersPointerTableEntries = f.readInt(); int shadersNameOffset = f.readInt(); int texturesPointerTableOffset = f.readInt(); int texturesPointerTableEntries = f.readInt(); int texturesNameOffset = f.readInt(); int materialsLUTPointerTableOffset = f.readInt(); int materialsLUTPointerTableEntries = f.readInt(); int materialsLUTNameOffset = f.readInt(); int lightsPointerTableOffset = f.readInt(); int lightsPointerTableEntries = f.readInt(); int lightsNameOffset = f.readInt(); int camerasPointerTableOffset = f.readInt(); int camerasPointerTableEntries = f.readInt(); int camerasNameOffset = f.readInt(); int fogsPointerTableOffset = f.readInt(); int fogsPointerTableEntries = f.readInt(); int fogsNameOffset = f.readInt(); int skeletalAnimationsPointerTableOffset = f.readInt(); int skeletalAnimationsPointerTableEntries = f.readInt(); int skeletalAnimationsNameOffset = f.readInt(); int materialAnimationsPointerTableOffset = f.readInt(); int materialAnimationsPointerTableEntries = f.readInt(); int materialAnimationsNameOffset = f.readInt(); int visibilityAnimationsPointerTableOffset = f.readInt(); int visibilityAnimationsPointerTableEntries = f.readInt(); int visibilityAnimationsNameOffset = f.readInt(); int lightAnimationsPointerTableOffset = f.readInt(); int lightAnimationsPointerTableEntries = f.readInt(); int lightAnimationsNameOffset = f.readInt(); int cameraAnimationsPointerTableOffset = f.readInt(); int cameraAnimationsPointerTableEntries = f.readInt(); int cameraAnimationsNameOffset = f.readInt(); int fogAnimationsPointerTableOffset = f.readInt(); int fogAnimationsPointerTableEntries = f.readInt(); int fogAnimationsNameOffset = f.readInt(); int scenePointerTableOffset = f.readInt(); int scenePointerTableEntries = f.readInt(); int sceneNameOffset = f.readInt(); Console.WriteLine(modelsPointerTableEntries > 0 ? "Has Models" : ""); Console.WriteLine(shadersPointerTableEntries > 0 ? "Has Shaders" : ""); Console.WriteLine(texturesPointerTableEntries > 0 ? "Has Textures" : ""); Console.WriteLine(materialsPointerTableEntries > 0 ? "Has Materials" : ""); Console.WriteLine(materialsLUTPointerTableEntries > 0 ? "Has Material LUT" : ""); Console.WriteLine(materialAnimationsPointerTableEntries > 0 ? "Has Material Animation" : ""); Console.WriteLine(lightsPointerTableEntries > 0 ? "Has Lights" : ""); Console.WriteLine(lightAnimationsPointerTableEntries > 0 ? "Has LightAnimations" : ""); Console.WriteLine(camerasPointerTableEntries > 0 ? "Has Camera" : ""); Console.WriteLine(cameraAnimationsPointerTableEntries > 0 ? "Has CameraAnimation" : ""); Console.WriteLine(fogsPointerTableEntries > 0 ? "Has Fog" : ""); Console.WriteLine(fogAnimationsPointerTableEntries > 0 ? "Has FogAnimation" : ""); Console.WriteLine(skeletalAnimationsPointerTableEntries > 0 ? "Has Skeletal Animations" : ""); Console.WriteLine(visibilityAnimationsPointerTableEntries > 0 ? "Has Visibility" : ""); Console.WriteLine(scenePointerTableEntries > 0 ? "Has Scene" : ""); // Textures for (int index = 0; index < texturesPointerTableEntries; index++) { f.seek(texturesPointerTableOffset + (index * 4)); int dOffset = f.readInt(); f.seek(dOffset); // one for each mip I assume int textureCommandsOffset = f.readInt(); int textureCommandsWordCount = f.readInt(); int textureCommandsOffset2 = f.readInt(); int textureCommandsWordCount2 = f.readInt(); int textureCommandsOffset3 = f.readInt(); int textureCommandsWordCount3 = f.readInt(); int unk = f.readInt(); BCH_Texture tex = new BCH_Texture(); tex.Text = f.readString(f.readInt(), -1); Textures.Nodes.Add(tex); f.seek(textureCommandsOffset); tex.ReadParameters(f, textureCommandsWordCount); } //Models for (int index = 0; index < modelsPointerTableEntries; index++) { f.seek(modelsPointerTableOffset + (index * 4)); f.seek(f.readInt()); BCH_Model model = new BCH_Model(); Models.Nodes.Add(model); model.flags = f.readByte(); model.skeletonScaleType = f.readByte(); model.silhouetteMaterialEntries = f.readShort(); model.worldTransform = new OpenTK.Matrix4(f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , f.readFloat(), f.readFloat(), f.readFloat(), f.readFloat() , 0, 0, 0, 1); int materialsTableOffset = f.readInt(); int materialsTableEntries = f.readInt(); int materialNameOffset = f.readInt(); int verticesTableOffset = f.readInt(); int verticesTableEntries = f.readInt(); f.skip(0x28); int skeletonOffset = f.readInt(); int skeletonEntries = f.readInt(); int skeletonNameOffset = f.readInt(); int objectsNodeVisibilityOffset = f.readInt(); int objectsNodeCount = f.readInt(); model.Text = f.readString(f.readInt(), -1); int objectsNodeNameEntries = f.readInt(); int objectsNodeNameOffset = f.readInt(); f.readInt(); //0x0 int metaDataPointerOffset = f.readInt(); f.seek(objectsNodeVisibilityOffset); int nodeVisibility = f.readInt(); string[] objectName = new string[objectsNodeNameEntries]; f.seek(objectsNodeNameOffset); int rootReferenceBit = f.readInt(); int rootLeftNode = f.readShort(); int rootRightNode = f.readShort(); int rootNameOffset = f.readInt(); //Console.WriteLine(rootReferenceBit.ToString("x") + " " + f.readString(rootNameOffset, -1) + " " + rootLeftNode + " " + rootRightNode); // Object name tree Radix Tree for (int i = 0; i < objectsNodeNameEntries; i++) { int referenceBit = f.readInt(); short leftNode = (short)f.readShort(); short rightNode = (short)f.readShort(); objectName[i] = f.readString(f.readInt(), -1); Console.WriteLine((referenceBit >> 3) + " " + (referenceBit & 0x7) + " " + objectName[i] + " " + leftNode + " " + rightNode); } //TODO: Metadata, boundingbox, normal mesh, materials f.seek(verticesTableOffset); Dictionary <int, BCH_Mesh> MeshIndex = new Dictionary <int, BCH_Mesh>(); int nim = 0; for (int i = 0; i < verticesTableEntries; i++) { BCH_Mesh Mesh = new BCH_Mesh(); Mesh.MaterialIndex = f.readShort(); int mflags = f.readShort(); int meshId = f.readShort(); if (!MeshIndex.ContainsKey(meshId)) { MeshIndex.Add(meshId, Mesh); Mesh.Text = nim < objectName.Length ? objectName[nim++] : i + ""; model.Nodes.Add(Mesh); } else { BCH_Mesh m = MeshIndex[meshId]; Mesh.Text = m.Text; model.Nodes.Insert(model.Nodes.IndexOf(m) - 1, Mesh); } // node visibility TODO: finish... Mesh.Checked = ((nodeVisibility & (1 << i)) > 0); Mesh.renderPriority = f.readShort(); int vshAttBufferCommandOffset = f.readInt(); int vshAttBufferCommandCount = f.readInt(); int faceOffset = f.readInt(); int faceCount = f.readInt(); int vshAttBufferCommandOffsetEx = f.readInt(); int vshAttBufferCommandCountEx = f.readInt(); Vector3 Center = new Vector3(f.readFloat(), f.readFloat(), f.readFloat()); int flagoffset = f.readInt(); // flagsOffset f.skip(4); // 0? int boundingBoxOffset = f.readInt(); } //Materials Console.WriteLine(materialsTableOffset.ToString("x") + " " + materialsPointerTableOffset.ToString("x")); for (int i = 0; i < materialsTableEntries; i++) { f.seek(materialsTableOffset + (i * 0x2c)); int paramOffset = f.readInt(); f.skip(12); // other offsets int texCommandOffset = f.readInt(); int texCommandCount = f.readInt(); int mapperOffset = f.readInt(); BCH_Material mat = new BCH_Material(); Materials.Nodes.Add(mat); mat.Text = f.readString(f.readInt(), -1); Console.WriteLine(mat.Text); //Console.WriteLine(f.readString(f.readInt(), -1)); //Console.WriteLine(f.readString(f.readInt(), -1)); //Console.WriteLine(f.readString(f.readInt(), -1)); // TODO: Parameters } //Skeleton f.seek(skeletonOffset); for (int bindex = 0; bindex < skeletonEntries; bindex++) { Bone bone = new Bone(model.skeleton); int boneFlags = f.readInt(); bone.parentIndex = (short)f.readShort(); short boneSpace = (short)f.readShort(); bone.scale = new float[3]; bone.rotation = new float[3]; bone.position = new float[3]; bone.scale[0] = f.readFloat(); bone.scale[1] = f.readFloat(); bone.scale[2] = f.readFloat(); bone.rotation[0] = f.readFloat(); bone.rotation[1] = f.readFloat(); bone.rotation[2] = f.readFloat(); bone.position[0] = f.readFloat(); bone.position[1] = f.readFloat(); bone.position[2] = f.readFloat(); // bone matrix... not really needed to be stored per say f.skip(4 * 4 * 3); bone.Text = f.readString(f.readInt(), -1); int metaDataPointerOffset2 = f.readInt(); if (metaDataPointerOffset2 != 0) { int position = f.pos(); f.seek(metaDataPointerOffset2); //bone.userData = getMetaData(input); f.seek(position); } model.skeleton.bones.Add(bone); } model.skeleton.reset(); model.skeleton.update(); } }
public void Render(Matrix4 view) { if (vertices == null) { return; } bool buffersWereInitialized = vertVbo != 0 && boneVbo != 0 && facesIbo != 0; if (!buffersWereInitialized) { GenerateBuffers(); } shader = OpenTKSharedResources.shaders["Mbn"]; shader.UseProgram(); GL.Uniform1(shader.GetVertexAttributeUniformLocation("renderVertColor"), Runtime.renderVertColor ? 1 : 0); GL.Uniform1(shader.GetVertexAttributeUniformLocation("renderType"), (int)Runtime.renderType); GL.Uniform1(shader.GetVertexAttributeUniformLocation("selectedBoneIndex"), Runtime.selectedBoneIndex); GL.UniformMatrix4(shader.GetVertexAttributeUniformLocation("modelview"), false, ref view); GL.Uniform3(shader.GetVertexAttributeUniformLocation("difLightColor"), Runtime.lightSetParam.characterDiffuse.diffuseColor.R, Runtime.lightSetParam.characterDiffuse.diffuseColor.G, Runtime.lightSetParam.characterDiffuse.diffuseColor.B); GL.Uniform3(shader.GetVertexAttributeUniformLocation("ambLightColor"), Runtime.lightSetParam.characterDiffuse.ambientColor.R, Runtime.lightSetParam.characterDiffuse.ambientColor.G, Runtime.lightSetParam.characterDiffuse.ambientColor.B); GL.ActiveTexture(TextureUnit.Texture10); RenderTools.uvTestPattern.Bind(); GL.Uniform1(shader.GetVertexAttributeUniformLocation("UVTestPattern"), 10); Matrix4[] f = skeleton.getShaderMatrix(); int maxUniformBlockSize = GL.GetInteger(GetPName.MaxUniformBlockSize); int boneCount = skeleton.bones.Count; int dataSize = boneCount * Vector4.SizeInBytes * 4; GL.BindBuffer(BufferTarget.UniformBuffer, boneVbo); GL.BufferData(BufferTarget.UniformBuffer, (IntPtr)(dataSize), IntPtr.Zero, BufferUsageHint.DynamicDraw); GL.BindBuffer(BufferTarget.UniformBuffer, 0); var blockIndex = GL.GetUniformBlockIndex(shader.Id, "bones"); GL.BindBufferBase(BufferRangeTarget.UniformBuffer, blockIndex, boneVbo); if (f.Length > 0) { GL.BindBuffer(BufferTarget.UniformBuffer, boneVbo); GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, (IntPtr)(f.Length * Vector4.SizeInBytes * 4), f); } shader.EnableVertexAttributes(); GL.BindBuffer(BufferTarget.ArrayBuffer, vertVbo); GL.BufferData <Vertex>(BufferTarget.ArrayBuffer, (IntPtr)(Vertex.sizeInBytes * vertices.Length), vertices, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(shader.GetVertexAttributeUniformLocation("pos"), 3, VertexAttribPointerType.Float, false, Vertex.sizeInBytes, 0); GL.VertexAttribPointer(shader.GetVertexAttributeUniformLocation("nrm"), 3, VertexAttribPointerType.Float, false, Vertex.sizeInBytes, 12); GL.VertexAttribPointer(shader.GetVertexAttributeUniformLocation("col"), 4, VertexAttribPointerType.Float, false, Vertex.sizeInBytes, 24); GL.VertexAttribPointer(shader.GetVertexAttributeUniformLocation("tx0"), 2, VertexAttribPointerType.Float, false, Vertex.sizeInBytes, 40); GL.VertexAttribPointer(shader.GetVertexAttributeUniformLocation("bone"), 2, VertexAttribPointerType.Float, false, Vertex.sizeInBytes, 48); GL.VertexAttribPointer(shader.GetVertexAttributeUniformLocation("weight"), 2, VertexAttribPointerType.Float, false, Vertex.sizeInBytes, 56); GL.PointSize(4f); foreach (BCH_Mesh m in Nodes) { GL.Uniform4(shader.GetVertexAttributeUniformLocation("colorSamplerUV"), new Vector4(1, 1, 0, 0)); GL.ActiveTexture(TextureUnit.Texture0); BCH_Material material = (BCH_Material)((BCH)Parent.Parent).Materials.Nodes[m.MaterialIndex]; BCH_Texture tex = ((BCH)Parent.Parent).GetTexture(material.Text); GL.BindTexture(TextureTarget.Texture2D, tex == null ? RenderTools.defaultTex.Id : tex.display); GL.Uniform1(shader.GetVertexAttributeUniformLocation("tex"), 0); if (!m.Checked) { continue; } foreach (BCH_PolyGroup pg in m.Nodes) { GL.Uniform1(shader.GetVertexAttributeUniformLocation("boneList"), pg.BoneList.Length, pg.BoneList); GL.Disable(EnableCap.CullFace); GL.CullFace(CullFaceMode.Back); GL.BindBuffer(BufferTarget.ElementArrayBuffer, facesIbo); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(pg.Faces.Length * sizeof(int)), pg.Faces, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.DrawElements(PrimitiveType.Triangles, pg.Faces.Length, DrawElementsType.UnsignedInt, 0); } } shader.DisableVertexAttributes(); }