0

I'm making and OpenGL application that has MULTIPLE meshes that are described as lists of positions, normals, and uvs. I am binding these data to a vertex buffer but I was wondering how I would draw these meshes per frame without re-binding the vertex buffer. Correct me if I'm wrong, but isn't copying ~100KB of data to the vertex buffer slowish? How would I draw each mesh with separate transforms (position, rotation, scale). Thanks :) Here is my Mesh code:

using System;
using System.IO;
using OpenTK;
using OpenTK.Graphics.OpenGL;

public class Mesh
{
    public Vector3[] positions;
    public Vector3[] normals;
    public Vector2[] uvs;
    public Triangle[] triangles;
    public int buffer;
    public Mesh()
    {
        this.positions = new Vector3[0];
        this.normals = new Vector3[0];
        this.uvs = new Vector2[0];
        this.triangles = new Triangle[0];
        this.buffer = 0;
    }
    public Mesh(Vector3[] positions, Vector3[] normals, Vector2[] uvs, Triangle[] triangles, int buffer)
    {
        this.positions = positions;
        this.normals = normals;
        this.uvs = uvs;
        this.triangles = triangles;
        this.buffer = buffer;
    }
    public static Mesh fromFile(string fileName)
    {
        Mesh mesh = new Mesh();
        BinaryReader binaryReader = new BinaryReader(new FileStream(fileName, FileMode.Open));
        int positionCount = binaryReader.ReadInt32();
        mesh.positions = new Vector3[positionCount];
        for (int i = 0; i < positionCount; i++)
        {
            mesh.positions[i] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
        }
        int normalCount = binaryReader.ReadInt32();
        mesh.normals = new Vector3[normalCount];
        for (int i = 0; i < normalCount; i++)
        {
            mesh.normals[i] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
        }
        int uvCount = binaryReader.ReadInt32();
        mesh.uvs = new Vector2[uvCount];
        for (int i = 0; i < uvCount; i++)
        {
            mesh.uvs[i] = new Vector2(binaryReader.ReadSingle(), binaryReader.ReadSingle());
        }
        int triangleCount = binaryReader.ReadInt32();
        mesh.triangles = new Triangle[triangleCount];
        for (int i = 0; i < triangleCount; i++)
        {
            mesh.triangles[i] = new Triangle(binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32());
        }
        binaryReader.Close();
        return mesh;
    }
    public void toFile(string fileName)
    {
        BinaryWriter binaryWriter = new BinaryWriter(new FileStream(fileName, FileMode.OpenOrCreate));
        binaryWriter.Write(positions.Length);
        for (int i = 0; i < positions.Length; i++)
        {
            binaryWriter.Write(positions[i].X);
            binaryWriter.Write(positions[i].Y);
            binaryWriter.Write(positions[i].Z);
        }
        binaryWriter.Write(normals.Length);
        for (int i = 0; i < normals.Length; i++)
        {
            binaryWriter.Write(normals[i].X);
            binaryWriter.Write(normals[i].Y);
            binaryWriter.Write(normals[i].Z);
        }
        binaryWriter.Write(uvs.Length);
        for (int i = 0; i < uvs.Length; i++)
        {
            binaryWriter.Write(uvs[i].X);
            binaryWriter.Write(uvs[i].Y);
        }
        binaryWriter.Write(triangles.Length);
        for (int i = 0; i < triangles.Length; i++)
        {
            binaryWriter.Write(triangles[i].positionIndex0);
            binaryWriter.Write(triangles[i].normalIndex0);
            binaryWriter.Write(triangles[i].uvIndex0);
            binaryWriter.Write(triangles[i].positionIndex1);
            binaryWriter.Write(triangles[i].normalIndex1);
            binaryWriter.Write(triangles[i].uvIndex1);
            binaryWriter.Write(triangles[i].positionIndex2);
            binaryWriter.Write(triangles[i].normalIndex2);
            binaryWriter.Write(triangles[i].uvIndex2);
        }
        binaryWriter.Close();
    }
    public void draw(Transform transform)
    {
        float[] data = new float[triangles.Length * 24];
        for (int i = 0; i < triangles.Length; i++)
        {
            data[(i * 9) + 0] = positions[triangles[i].positionIndex0].X;
            data[(i * 9) + 1] = positions[triangles[i].positionIndex0].Y;
            data[(i * 9) + 2] = positions[triangles[i].positionIndex0].Z;
            data[(i * 9) + 3] = positions[triangles[i].positionIndex1].X;
            data[(i * 9) + 4] = positions[triangles[i].positionIndex1].Y;
            data[(i * 9) + 5] = positions[triangles[i].positionIndex1].Z;
            data[(i * 9) + 6] = positions[triangles[i].positionIndex2].X;
            data[(i * 9) + 7] = positions[triangles[i].positionIndex2].Y;
            data[(i * 9) + 8] = positions[triangles[i].positionIndex2].Z;
            data[(triangles.Length * 9) + (i * 9) + 0] = normals[triangles[i].normalIndex0].X;
            data[(triangles.Length * 9) + (i * 9) + 1] = normals[triangles[i].normalIndex0].Y;
            data[(triangles.Length * 9) + (i * 9) + 2] = normals[triangles[i].normalIndex0].Z;
            data[(triangles.Length * 9) + (i * 9) + 3] = normals[triangles[i].normalIndex1].X;
            data[(triangles.Length * 9) + (i * 9) + 4] = normals[triangles[i].normalIndex1].Y;
            data[(triangles.Length * 9) + (i * 9) + 5] = normals[triangles[i].normalIndex1].Z;
            data[(triangles.Length * 9) + (i * 9) + 6] = normals[triangles[i].normalIndex2].X;
            data[(triangles.Length * 9) + (i * 9) + 7] = normals[triangles[i].normalIndex2].Y;
            data[(triangles.Length * 9) + (i * 9) + 8] = normals[triangles[i].normalIndex2].Z;
            data[(triangles.Length * 18) + (i * 6) + 0] = uvs[triangles[i].uvIndex0].X;
            data[(triangles.Length * 18) + (i * 6) + 1] = uvs[triangles[i].uvIndex0].Y;
            data[(triangles.Length * 18) + (i * 6) + 2] = uvs[triangles[i].uvIndex1].X;
            data[(triangles.Length * 18) + (i * 6) + 3] = uvs[triangles[i].uvIndex1].Y;
            data[(triangles.Length * 18) + (i * 6) + 4] = uvs[triangles[i].uvIndex2].X;
            data[(triangles.Length * 18) + (i * 6) + 5] = uvs[triangles[i].uvIndex2].Y;
        }
        buffer = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(triangles.Length * 96), data, BufferUsageHint.StaticDraw);
        //------------------------
        //----------TODO----------
        //------------------------
    }
}

The last function, draw, is the one I'm working on.

user3551745
  • 33
  • 2
  • 9
  • This isn't C++... Don't abuse the tags! – vallentin Nov 28 '14 at 07:25
  • The act of binding mesh data to a VBO is sending the data from system memory to GPU memory, as it says so in this [Wikipedia page](http://en.m.wikipedia.org/wiki/Vertex_Buffer_Object). If so, technically if the amount of mesh data you have does not exceed the size of the GPU's memory, you only need to send them once in the beginning as one gigantic VBO. – Nard Nov 28 '14 at 07:27
  • @Nard But what if each mesh is moving in its own direction? – user3551745 Nov 28 '14 at 07:29
  • Before drawing each object, you need to specify a vertex shader, a pixel shader, and the number of vertices with the index of the first vertex in the VBO to draw. Using the vertex shader, you can apply different transformations by simply supplying the transformation to the vertex shader before you draw each object. – Nard Nov 28 '14 at 07:39
  • @Nard So, 1)create massive untransformed VBO, 2)foreach mesh, `DrawArrays` with offset and count and shader parameter transform set, 3)never change the vbo unless you're adding or removing a mesh? Sorry if this sounds dumb, but I'm just a noob at this. – user3551745 Nov 28 '14 at 07:43
  • A vertex shader is a mini program that you tell the GPU to run on every single vertex that the GPU. Instead of altering the mesh for every object and getting multiple copies of the same mesh before sending them to the GPU (1000000 objects = 1000000 copies of the same mesh Σ(゚д゚lll)), what you do is supply the base mesh, also known as model space, to the GPU, and you supply a vertex shader program to the GPU which is like a function call `TransformToScreen(Point3D position, Vertex v)` on every vertex you will tell the GPU to draw to transform them to world space > camera space > screen space. – Nard Nov 28 '14 at 07:52

1 Answers1

2

The point is to have a single VBO per mesh that you load once and then just rebind as needed.

if you are in openGL 3.3+ you can collect all needed bindings for each mesh in a VAO per mesh: (pseudo-ish code)

class MeshBuffer{
    int vao;
    int vbo;
    int numVertices;

    void Dispose(){
        if(vao==0)return;
        GL.DeleteVertexArrays(1, ref vao);
        GL.DeleteBuffers(1, ref vbo);
        vao = 0;
        vbo = 0;
    }

    void Bind(){
        GL.BindVertexArray(vao);
    }

    void Unbind(){
        GL.BindVertexArray(0);
    }

    void FillVBO(Mesh mesh){
        Dispose();
        GL.GenVertexArrays(1, out vao);
        GL.GenBuffers(1, out vbo);
        float[] data = new float[mesh.triangles.Length * 24];
        //your for loop
        GL.BindVertexArray(vao);
        GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(triangles.Length * 96), data, BufferUsageHint.StaticDraw);

        GL.VertexAttribPointer(0, 3,  VertexAttribPointerType.Float, 0, 0);
        GL.VertexAttribPointer(1, 3,  VertexAttribPointerType.Float, 0, triangles.Length * 9*4);
        GL.VertexAttribPointer(2, 3,  VertexAttribPointerType.Float, 0, triangles.Length * 18*4);

        GL.BindVertexArray(0);
        GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
    }
}

Then to draw you just bind the MeshBuffer and load the transformation matrix into the relevant uniform.

int mvpMat = GL.GetUniformLocation(prog, "mvp");

GL.UseProgram(prog);
meshBuffer.Bind();
GL.UniformMatrix4(mvpMat, transform.Mat4());
GL.DrawArrays(GL_TRIANGLES, 0, meshBuffer.numVertices);
ratchet freak
  • 47,288
  • 5
  • 68
  • 106