Display optimizations part 1.
authorAntony Riakiotakis <kalast@gmail.com>
Tue, 14 Jul 2015 14:48:23 +0000 (16:48 +0200)
committerAntony Riakiotakis <kalast@gmail.com>
Tue, 14 Jul 2015 14:48:23 +0000 (16:48 +0200)
This patch changes the way we draw meshes by introducing
indexed drawing. This makes it possible to easily
upload and rearrange faces ad lib according to any criteria.

Currently we use material sorting but textured sorting and
hiding will be added to optimize textured drawing and skip
per face testing.

It also adds support for vertex buffers for subsurf
modifiers (Except from GLSL drawing), making drawing of
subsurf much faster without need for bogus modifiers.

Tests show that we gain approximately 20-25% performance
by that for solid mode drawing with up to 50% gains for
material drawing. Textured drawing should also have a
small performance gain, but more substantial optimizations
are possible there.

Reviewers: campbellbarton

Differential Revision: https://developer.blender.org/D1406

source/blender/blenkernel/BKE_DerivedMesh.h
source/blender/blenkernel/BKE_pbvh.h
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/pbvh.c
source/blender/blenkernel/intern/subsurf_ccg.c
source/blender/gpu/GPU_buffers.h
source/blender/gpu/intern/gpu_buffers.c

index 984147de3af4bfbc5e7db404956f1ac1e31a5046..881b233dc3cacfe40d485b454accb21b82646c6e 100644 (file)
@@ -463,6 +463,9 @@ struct DerivedMesh {
                                   void (*setMaterial)(void *userData, int matnr, void *attribs),
                                   bool (*setFace)(void *userData, int index), void *userData);
 
+       struct GPUDrawObject *(*gpuObjectNew)(DerivedMesh *dm);
+       void (*copy_gpu_data)(DerivedMesh *dm, int type, float *varray, int *mat_orig_to_new, void *user_data);
+
        /** Release reference to the DerivedMesh. This function decides internally
         * if the DerivedMesh will be freed, or cached for later use. */
        void (*release)(DerivedMesh *dm);
index 0832e2498d4df692eff663b71549f40819e1da0c..fbaf91d60bddca3320e2426a226cacde005b9508 100644 (file)
@@ -112,7 +112,7 @@ void BKE_pbvh_raycast_project_ray_root(
 
 void BKE_pbvh_node_draw(PBVHNode *node, void *data);
 void BKE_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3],
-                   int (*setMaterial)(int matnr, void *attribs), bool wireframe);
+                   int (*setMaterial)(int matnr, void *attribs), bool wireframe, bool fast);
 
 /* PBVH Access */
 typedef enum {
index 37c3376ecdbd88ccaaa989d26a76f70e838911af..475233206f8593ab83f663645abca7ef779d8b5d 100644 (file)
@@ -341,8 +341,8 @@ static void cdDM_update_normals_from_pbvh(DerivedMesh *dm)
 static void cdDM_drawVerts(DerivedMesh *dm)
 {
        GPU_vertex_setup(dm);
-       if (dm->drawObject->tot_triangle_point)
-               glDrawArrays(GL_POINTS, 0, dm->drawObject->tot_triangle_point);
+       if (dm->drawObject->tot_loop_verts)
+               glDrawArrays(GL_POINTS, 0, dm->drawObject->tot_loop_verts);
        else
                glDrawArrays(GL_POINTS, 0, dm->drawObject->tot_loose_point);
        GPU_buffer_unbind();
@@ -391,7 +391,7 @@ static void cdDM_drawEdges(DerivedMesh *dm, bool drawLooseEdges, bool drawAllEdg
        if (cddm->pbvh && cddm->pbvh_draw &&
            BKE_pbvh_type(cddm->pbvh) == PBVH_BMESH)
        {
-               BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, true);
+               BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, true, false);
 
                return;
        }
@@ -442,7 +442,7 @@ static void cdDM_drawFacesSolid(DerivedMesh *dm,
                        float (*face_nors)[3] = CustomData_get_layer(&dm->faceData, CD_NORMAL);
 
                        BKE_pbvh_draw(cddm->pbvh, partial_redraw_planes, face_nors,
-                                     setMaterial, false);
+                                     setMaterial, false, false);
                        glShadeModel(GL_FLAT);
                }
 
@@ -451,11 +451,11 @@ static void cdDM_drawFacesSolid(DerivedMesh *dm,
        
        GPU_vertex_setup(dm);
        GPU_normal_setup(dm);
+       GPU_triangle_setup(dm);
        glShadeModel(GL_SMOOTH);
        for (a = 0; a < dm->drawObject->totmaterial; a++) {
                if (!setMaterial || setMaterial(dm->drawObject->materials[a].mat_nr + 1, NULL)) {
-                       glDrawArrays(GL_TRIANGLES, dm->drawObject->materials[a].start,
-                                    dm->drawObject->materials[a].totpoint);
+                       GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, dm->drawObject->materials[a].start, dm->drawObject->materials[a].totelements);
                }
        }
        GPU_buffer_unbind();
@@ -473,12 +473,13 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm,
        const MFace *mf = DM_get_tessface_data_layer(dm, CD_MFACE);
        MTexPoly *mtexpoly = DM_get_poly_data_layer(dm, CD_MTEXPOLY);
        MCol *mcol;
-       int i;
-       int colType, startFace = 0;
+       int i, orig;
+       int colType, start_element;
        bool use_tface = (uvflag & DM_DRAW_USE_ACTIVE_UV) != 0;
-       int tottri;
+       int totpoly;
        int next_actualFace;
-       
+       int mat_index;
+       int tot_element;
 
        /* double lookup */
        const int *index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
@@ -497,7 +498,7 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm,
        if (cddm->pbvh && cddm->pbvh_draw && BKE_pbvh_type(cddm->pbvh) == PBVH_BMESH) {
                if (BKE_pbvh_has_faces(cddm->pbvh)) {
                        GPU_set_tpage(NULL, false, false);
-                       BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, false);
+                       BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, false, false);
                }
 
                return;
@@ -518,6 +519,7 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm,
        
        GPU_vertex_setup(dm);
        GPU_normal_setup(dm);
+       GPU_triangle_setup(dm);
        if (uvflag & DM_DRAW_USE_TEXPAINT_UV)
                GPU_texpaint_uv_setup(dm);
        else
@@ -525,76 +527,84 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm,
        if (mcol) {
                GPU_color_setup(dm, colType);
        }
-       
-       tottri = dm->drawObject->tot_triangle_point / 3;
-       next_actualFace = dm->drawObject->triangle_to_mface[0];
-       
+               
        glShadeModel(GL_SMOOTH);
        /* lastFlag = 0; */ /* UNUSED */
-       for (i = 0; i < tottri; i++) {
-               int actualFace = next_actualFace;
-               DMDrawOption draw_option = DM_DRAW_OPTION_NORMAL;
-               int flush = 0;
-               
-               if (i != tottri - 1)
-                       next_actualFace = dm->drawObject->triangle_to_mface[i + 1];
-
-               if (drawParams) {
-                       MTexPoly *tp = NULL;
-                       if (use_tface && mtexpoly && index_mf_to_mpoly) {
-                               int actualFace_poly = index_mf_to_mpoly[actualFace];
-                               if (actualFace_poly != ORIGINDEX_NONE) {
-                                       tp = &mtexpoly[actualFace_poly];
+       for (mat_index = 0; mat_index < dm->drawObject->totmaterial; mat_index++) {
+               GPUBufferMaterial *bufmat = dm->drawObject->materials + mat_index;
+               next_actualFace = bufmat->polys[0];
+               totpoly = bufmat->totpolys;
+
+               tot_element = 0;
+               start_element = bufmat->start;
+
+               for (i = 0; i < totpoly; i++) {
+                       int actualFace = bufmat->polys[i];
+                       DMDrawOption draw_option = DM_DRAW_OPTION_NORMAL;
+                       int flush = 0;
+
+                       if (i != totpoly - 1)
+                               next_actualFace = bufmat->polys[i + 1];
+
+                       if (drawParams) {
+                               MTexPoly *tp = NULL;
+                               if (use_tface && mtexpoly && index_mf_to_mpoly) {
+                                       int actualFace_poly = index_mf_to_mpoly[actualFace];
+                                       if (actualFace_poly != ORIGINDEX_NONE) {
+                                               tp = &mtexpoly[actualFace_poly];
+                                       }
                                }
-                       }
 
-                       draw_option = drawParams(tp, (mcol != NULL), mf[actualFace].mat_nr);
-               }
-               else {
-                       if (index_mf_to_mpoly) {
-                               const int orig = DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, actualFace);
-                               if (orig == ORIGINDEX_NONE) {
-                                       /* XXX, this is not really correct
-                                                        * it will draw the previous faces context for this one when we don't know its settings.
-                                                        * but better then skipping it altogether. - campbell */
-                                       draw_option = DM_DRAW_OPTION_NORMAL;
+                               draw_option = drawParams(tp, (mcol != NULL), mf[actualFace].mat_nr);
+                       }
+                       else {
+                               if (index_mf_to_mpoly) {
+                                       orig = DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, actualFace);
+                                       if (orig == ORIGINDEX_NONE) {
+                                               /* XXX, this is not really correct
+                                                * it will draw the previous faces context for this one when we don't know its settings.
+                                                * but better then skipping it altogether. - campbell */
+                                               draw_option = DM_DRAW_OPTION_NORMAL;
+                                       }
+                                       else if (drawParamsMapped) {
+                                               draw_option = drawParamsMapped(userData, orig, mf[actualFace].mat_nr);
+                                       }
                                }
                                else if (drawParamsMapped) {
-                                       draw_option = drawParamsMapped(userData, orig, mf[actualFace].mat_nr);
+                                       draw_option = drawParamsMapped(userData, actualFace, mf[actualFace].mat_nr);
                                }
                        }
-                       else if (drawParamsMapped) {
-                               draw_option = drawParamsMapped(userData, actualFace, mf[actualFace].mat_nr);
+
+                       /* flush buffer if current triangle isn't drawable or it's last triangle */
+                       flush = (draw_option == DM_DRAW_OPTION_SKIP) || (i == totpoly - 1);
+
+                       if (!flush && compareDrawOptions) {
+                               /* also compare draw options and flush buffer if they're different
+                                * need for face selection highlight in edit mode */
+                               flush |= compareDrawOptions(userData, actualFace, next_actualFace) == 0;
                        }
-               }
-               
-               /* flush buffer if current triangle isn't drawable or it's last triangle */
-               flush = (draw_option == DM_DRAW_OPTION_SKIP) || (i == tottri - 1);
-               
-               if (!flush && compareDrawOptions) {
-                       /* also compare draw options and flush buffer if they're different
-                                        * need for face selection highlight in edit mode */
-                       flush |= compareDrawOptions(userData, actualFace, next_actualFace) == 0;
-               }
-               
-               if (flush) {
-                       int first = startFace * 3;
-                       /* Add one to the length if we're drawing at the end of the array */
-                       int count = (i - startFace + (draw_option != DM_DRAW_OPTION_SKIP ? 1 : 0)) * 3;
-                       
-                       if (count) {
-                               if (mcol && draw_option != DM_DRAW_OPTION_NO_MCOL)
-                                       GPU_color_switch(1);
-                               else
-                                       GPU_color_switch(0);
-                               
-                               glDrawArrays(GL_TRIANGLES, first, count);
+
+                       if (flush) {
+                               if (draw_option != DM_DRAW_OPTION_SKIP)
+                                       tot_element += mf[actualFace].v4 ? 6 : 3;
+
+                               if (tot_element) {
+                                       if (mcol && draw_option != DM_DRAW_OPTION_NO_MCOL)
+                                               GPU_color_switch(1);
+                                       else
+                                               GPU_color_switch(0);
+
+                                       GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, start_element, tot_element);
+                               }
+
+                               start_element = tot_element;
+                       }
+                       else {
+                               tot_element += mf[actualFace].v4 ? 6 : 3;
                        }
-                       
-                       startFace = i + 1;
                }
        }
-       
+
        GPU_buffer_unbind();
        glShadeModel(GL_FLAT);
        
@@ -747,87 +757,92 @@ static void cdDM_drawMappedFaces(DerivedMesh *dm,
                }
        }
        else { /* use OpenGL VBOs or Vertex Arrays instead for better, faster rendering */
-               int prevstart = 0;
+               int start_element = 0, tot_element;
+               int totpoly;
                int tottri;
+               int mat_index;
                
                GPU_vertex_setup(dm);
                GPU_normal_setup(dm);
+               GPU_triangle_setup(dm);
                if (useColors && mcol) {
                        GPU_color_setup(dm, colType);
                }
-               tottri = dm->drawObject->tot_triangle_point / 3;
                glShadeModel(GL_SMOOTH);
                
+               tottri = dm->drawObject->tot_triangle_point / 3;
+
                if (tottri == 0) {
                        /* avoid buffer problems in following code */
                }
                else if (setDrawOptions == NULL) {
                        /* just draw the entire face array */
-                       glDrawArrays(GL_TRIANGLES, 0, (tottri) * 3);
+                       GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, 0, 3 * tottri);
                }
-               else {
-                       /* we need to check if the next material changes */
-                       int next_actualFace = dm->drawObject->triangle_to_mface[0];
-                       short prev_mat_nr = -1;
-                       
-                       for (i = 0; i < tottri; i++) {
-                               //int actualFace = dm->drawObject->triangle_to_mface[i];
-                               int actualFace = next_actualFace;
-                               MFace *mface = mf + actualFace;
-                               /*int drawSmooth = (flag & DM_DRAW_ALWAYS_SMOOTH) ? 1 : (mface->flag & ME_SMOOTH);*/ /* UNUSED */
+               else {                  
+                       for (mat_index = 0; mat_index < dm->drawObject->totmaterial; mat_index++) {
+                               GPUBufferMaterial *bufmat = dm->drawObject->materials + mat_index;
                                DMDrawOption draw_option = DM_DRAW_OPTION_NORMAL;
-                               int flush = 0;
-                               
-                               if (i != tottri - 1)
-                                       next_actualFace = dm->drawObject->triangle_to_mface[i + 1];
-                               
-                               orig = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, actualFace) : actualFace;
-                               
-                               if (mface->mat_nr != prev_mat_nr) {
-                                       if (setMaterial)
-                                               draw_option = setMaterial(mface->mat_nr + 1, NULL);
-                                       
-                                       prev_mat_nr = mface->mat_nr;
-                               }
-                               
-                               if (setDrawOptions != NULL && (orig != ORIGINDEX_NONE))
-                                       draw_option = setDrawOptions(userData, orig);
-                               
-                               if (draw_option == DM_DRAW_OPTION_STIPPLE) {
-                                       glEnable(GL_POLYGON_STIPPLE);
-                                       glPolygonStipple(stipple_quarttone);
-                               }
-                               
-                               /* Goal is to draw as long of a contiguous triangle
+                               int next_actualFace = bufmat->polys[0];
+                               totpoly = bufmat->totpolys;
+
+                               tot_element = 0;
+                               start_element = bufmat->start;
+
+                               if (setMaterial)
+                                       draw_option = setMaterial(bufmat->mat_nr + 1, NULL);
+
+                               if (draw_option != DM_DRAW_OPTION_SKIP) {
+                                       for (i = 0; i < totpoly; i++) {
+                                               //int actualFace = dm->drawObject->triangle_to_mface[i];
+                                               int actualFace = next_actualFace;
+                                               int flush = 0;
+                                               draw_option = DM_DRAW_OPTION_NORMAL;
+
+                                               if (i != totpoly - 1)
+                                                       next_actualFace = bufmat->polys[i + 1];
+
+                                               orig = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, actualFace) : actualFace;
+
+                                               if (setDrawOptions != NULL && (orig != ORIGINDEX_NONE))
+                                                       draw_option = setDrawOptions(userData, orig);
+
+                                               if (draw_option == DM_DRAW_OPTION_STIPPLE) {
+                                                       glEnable(GL_POLYGON_STIPPLE);
+                                                       glPolygonStipple(stipple_quarttone);
+                                               }
+
+                                               /* Goal is to draw as long of a contiguous triangle
                                         * array as possible, so draw when we hit either an
                                         * invisible triangle or at the end of the array */
-                               
-                               /* flush buffer if current triangle isn't drawable or it's last triangle... */
-                               flush = (ELEM(draw_option, DM_DRAW_OPTION_SKIP, DM_DRAW_OPTION_STIPPLE)) || (i == tottri - 1);
-                               
-                               /* ... or when material setting is dissferent  */
-                               flush |= mf[actualFace].mat_nr != mf[next_actualFace].mat_nr;
-                               
-                               if (!flush && compareDrawOptions) {
-                                       flush |= compareDrawOptions(userData, actualFace, next_actualFace) == 0;
-                               }
-                               
-                               if (flush) {
-                                       int first = prevstart * 3;
-                                       /* Add one to the length if we're drawing at the end of the array */
-                                       int count = (i - prevstart + (draw_option != DM_DRAW_OPTION_SKIP ? 1 : 0)) * 3;
-                                       
-                                       if (count)
-                                               glDrawArrays(GL_TRIANGLES, first, count);
-                                       
-                                       prevstart = i + 1;
-                                       
-                                       if (draw_option == DM_DRAW_OPTION_STIPPLE)
-                                               glDisable(GL_POLYGON_STIPPLE);
+
+                                               /* flush buffer if current triangle isn't drawable or it's last triangle... */
+                                               flush = (ELEM(draw_option, DM_DRAW_OPTION_SKIP, DM_DRAW_OPTION_STIPPLE)) || (i == totpoly - 1);
+
+                                               if (!flush && compareDrawOptions) {
+                                                       flush |= compareDrawOptions(userData, actualFace, next_actualFace) == 0;
+                                               }
+
+                                               if (flush) {
+                                                       if (!ELEM(draw_option, DM_DRAW_OPTION_SKIP, DM_DRAW_OPTION_STIPPLE))
+                                                               tot_element += mf[actualFace].v4 ? 6 : 3;
+
+                                                       if (tot_element)
+                                                               GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, start_element, tot_element);
+
+                                                       start_element = tot_element;
+
+                                                       if (draw_option == DM_DRAW_OPTION_STIPPLE)
+                                                               glDisable(GL_POLYGON_STIPPLE);
+                                               }
+                                               else {
+                                                       tot_element += mf[actualFace].v4 ? 6 : 3;
+                                               }
+                                       }
+
+                                       glShadeModel(GL_FLAT);
                                }
                        }
-                       
-                       glShadeModel(GL_FLAT);
                }
                GPU_buffer_unbind();
        }
@@ -858,6 +873,13 @@ static void cddm_draw_attrib_vertex(DMVertexAttribs *attribs, const MVert *mvert
        glVertex3fv(mvert[index].co);
 }
 
+typedef struct {
+       DMVertexAttribs attribs;
+       int numdata;
+
+       GPUAttrib datatypes[GPU_MAX_ATTRIB]; /* TODO, messing up when switching materials many times - [#21056]*/
+} GPUMaterialConv;
+
 static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm,
                                      DMSetMaterial setMaterial,
                                      DMSetDrawOptions setDrawOptions,
@@ -865,7 +887,6 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm,
 {
        CDDerivedMesh *cddm = (CDDerivedMesh *) dm;
        GPUVertexAttribs gattribs;
-       DMVertexAttribs attribs;
        const MVert *mvert = cddm->mvert;
        const MFace *mface = cddm->mface;
        /* MTFace *tf = dm->getTessFaceDataArray(dm, CD_MTFACE); */ /* UNUSED */
@@ -889,7 +910,7 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm,
        if (cddm->pbvh && cddm->pbvh_draw && BKE_pbvh_type(cddm->pbvh) == PBVH_BMESH) {
                if (BKE_pbvh_has_faces(cddm->pbvh)) {
                        setMaterial(1, &gattribs);
-                       BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, false);
+                       BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, false, false);
                }
 
                return;
@@ -906,6 +927,7 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm,
        if ((GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_MAC, GPU_DRIVER_ANY) && (U.gameflags & USER_DISABLE_VBO)) ||
            setDrawOptions != NULL)
        {
+               DMVertexAttribs attribs;
                DEBUG_VBO("Using legacy code. cdDM_drawMappedFacesGLSL\n");
                memset(&attribs, 0, sizeof(attribs));
 
@@ -914,7 +936,7 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm,
                for (a = 0; a < dm->numTessFaceData; a++, mface++) {
                        const bool smoothnormal = lnors || (mface->flag & ME_SMOOTH);
                        const short (*ln1)[3] = NULL, (*ln2)[3] = NULL, (*ln3)[3] = NULL, (*ln4)[3] = NULL;
-                       new_matnr = mface->mat_nr + 1;
+                       new_matnr = mface->mat_nr;
 
                        if (new_matnr != matnr) {
                                glEnd();
@@ -977,214 +999,190 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm,
                glEnd();
        }
        else {
+               GPUMaterialConv *matconv;
+               int offset;
+               int totface = dm->getNumTessFaces(dm);
+               int *mat_orig_to_new;
+               int tot_active_mat;
                GPUBuffer *buffer = NULL;
-               char *varray = NULL;
-               int numdata = 0, elementsize = 0, offset;
-               int start = 0, numfaces = 0 /* , prevdraw = 0 */ /* UNUSED */, curface = 0;
-               int i;
+               char *varray;
+               int max_element_size = 0;
+               int tot_loops = 0;
                
                const MFace *mf = mface;
-               GPUAttrib datatypes[GPU_MAX_ATTRIB]; /* TODO, messing up when switching materials many times - [#21056]*/
-               memset(&attribs, 0, sizeof(attribs));
                
                GPU_vertex_setup(dm);
                GPU_normal_setup(dm);
-               
-               for (i = 0; i < dm->drawObject->tot_triangle_point / 3; i++) {
-                       
-                       a = dm->drawObject->triangle_to_mface[i];
-                       
-                       mface = mf + a;
-                       new_matnr = mface->mat_nr + 1;
-                       
-                       if (new_matnr != matnr) {
-                               numfaces = curface - start;
-                               if (numfaces > 0) {
-                                       
-                                       if (do_draw) {
-                                               
-                                               if (numdata != 0) {
-                                                       
-                                                       GPU_buffer_unlock(buffer);
-                                                       
-                                                       GPU_interleaved_attrib_setup(buffer, datatypes, numdata);
-                                               }
-                                               
-                                               glDrawArrays(GL_TRIANGLES, start * 3, numfaces * 3);
-                                               
-                                               if (numdata != 0) {
-                                                       
-                                                       GPU_buffer_free(buffer);
-                                                       
-                                                       buffer = NULL;
-                                               }
-                                               
-                                       }
+               GPU_triangle_setup(dm);
+
+               tot_active_mat = dm->drawObject->totmaterial;
+
+               matconv = MEM_callocN(sizeof(*matconv) * tot_active_mat,
+                                     "cdDM_drawMappedFacesGLSL.matconv");
+               mat_orig_to_new = MEM_mallocN(sizeof(*mat_orig_to_new) * dm->totmat,
+                                             "cdDM_drawMappedFacesGLSL.mat_orig_to_new");
+
+               /* part one, check what attributes are needed per material */
+               for (a = 0; a < tot_active_mat; a++) {
+                       new_matnr = dm->drawObject->materials[a].mat_nr;
+
+                       /* map from original material index to new
+                        * GPUBufferMaterial index */
+                       mat_orig_to_new[new_matnr] = a;
+                       do_draw = setMaterial(new_matnr + 1, &gattribs);
+
+                       if (do_draw) {
+                               int numdata = 0;
+                               DM_vertex_attributes_from_gpu(dm, &gattribs, &matconv[a].attribs);
+
+                               if (matconv[a].attribs.totorco && matconv[a].attribs.orco.array) {
+                                       matconv[a].datatypes[numdata].index = matconv[a].attribs.orco.gl_index;
+                                       matconv[a].datatypes[numdata].size = 3;
+                                       matconv[a].datatypes[numdata].type = GL_FLOAT;
+                                       numdata++;
                                }
-                               numdata = 0;
-                               start = curface;
-                               /* prevdraw = do_draw; */ /* UNUSED */
-                               do_draw = setMaterial(matnr = new_matnr, &gattribs);
-                               if (do_draw) {
-                                       DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs);
-                                       
-                                       if (attribs.totorco && attribs.orco.array) {
-                                               datatypes[numdata].index = attribs.orco.gl_index;
-                                               datatypes[numdata].size = 3;
-                                               datatypes[numdata].type = GL_FLOAT;
+                               for (b = 0; b < matconv[a].attribs.tottface; b++) {
+                                       if (matconv[a].attribs.tface[b].array) {
+                                               matconv[a].datatypes[numdata].index = matconv[a].attribs.tface[b].gl_index;
+                                               matconv[a].datatypes[numdata].size = 2;
+                                               matconv[a].datatypes[numdata].type = GL_FLOAT;
                                                numdata++;
                                        }
-                                       for (b = 0; b < attribs.tottface; b++) {
-                                               if (attribs.tface[b].array) {
-                                                       datatypes[numdata].index = attribs.tface[b].gl_index;
-                                                       datatypes[numdata].size = 2;
-                                                       datatypes[numdata].type = GL_FLOAT;
-                                                       numdata++;
-                                               }
-                                       }
-                                       for (b = 0; b < attribs.totmcol; b++) {
-                                               if (attribs.mcol[b].array) {
-                                                       datatypes[numdata].index = attribs.mcol[b].gl_index;
-                                                       datatypes[numdata].size = 4;
-                                                       datatypes[numdata].type = GL_UNSIGNED_BYTE;
-                                                       numdata++;
-                                               }
-                                       }
-                                       if (attribs.tottang && attribs.tang.array) {
-                                               datatypes[numdata].index = attribs.tang.gl_index;
-                                               datatypes[numdata].size = 4;
-                                               datatypes[numdata].type = GL_FLOAT;
-                                               numdata++;
-                                       }
-                                       if (numdata != 0) {
-                                               elementsize = GPU_attrib_element_size(datatypes, numdata);
-                                               buffer = GPU_buffer_alloc(elementsize * dm->drawObject->tot_triangle_point, false);
-                                               if (buffer == NULL) {
-                                                       buffer = GPU_buffer_alloc(elementsize * dm->drawObject->tot_triangle_point, true);
-                                               }
-                                               varray = GPU_buffer_lock_stream(buffer);
-                                               if (varray == NULL) {
-                                                       GPU_buffer_unbind();
-                                                       GPU_buffer_free(buffer);
-                                                       fprintf(stderr, "Out of memory, can't draw object\n");
-                                                       return;
-                                               }
-                                       }
-                                       else {
-                                               /* if the buffer was set, don't use it again.
-                                                * prevdraw was assumed true but didnt run so set to false - [#21036] */
-                                               /* prevdraw = 0; */ /* UNUSED */
-                                               buffer = NULL;
-                                       }
-                               }
-                       }
-                       
-                       if (do_draw && numdata != 0) {
-                               offset = 0;
-                               if (attribs.totorco && attribs.orco.array) {
-                                       copy_v3_v3((float *)&varray[elementsize * curface * 3], (float *)attribs.orco.array[mface->v1]);
-                                       copy_v3_v3((float *)&varray[elementsize * curface * 3 + elementsize], (float *)attribs.orco.array[mface->v2]);
-                                       copy_v3_v3((float *)&varray[elementsize * curface * 3 + elementsize * 2], (float *)attribs.orco.array[mface->v3]);
-                                       offset += sizeof(float) * 3;
                                }
-                               for (b = 0; b < attribs.tottface; b++) {
-                                       if (attribs.tface[b].array) {
-                                               MTFace *tf = &attribs.tface[b].array[a];
-                                               copy_v2_v2((float *)&varray[elementsize * curface * 3 + offset], tf->uv[0]);
-                                               copy_v2_v2((float *)&varray[elementsize * curface * 3 + offset + elementsize], tf->uv[1]);
-                                               
-                                               copy_v2_v2((float *)&varray[elementsize * curface * 3 + offset + elementsize * 2], tf->uv[2]);
-                                               offset += sizeof(float) * 2;
+                               for (b = 0; b < matconv[a].attribs.totmcol; b++) {
+                                       if (matconv[a].attribs.mcol[b].array) {
+                                               matconv[a].datatypes[numdata].index = matconv[a].attribs.mcol[b].gl_index;
+                                               matconv[a].datatypes[numdata].size = 4;
+                                               matconv[a].datatypes[numdata].type = GL_UNSIGNED_BYTE;
+                                               numdata++;
                                        }
                                }
-                               for (b = 0; b < attribs.totmcol; b++) {
-                                       if (attribs.mcol[b].array) {
-                                               MCol *cp = &attribs.mcol[b].array[a * 4 + 0];
-                                               GLubyte col[4];
-                                               col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a;
-                                               copy_v4_v4_char((char *)&varray[elementsize * curface * 3 + offset], (char *)col);
-                                               cp = &attribs.mcol[b].array[a * 4 + 1];
-                                               col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a;
-                                               copy_v4_v4_char((char *)&varray[elementsize * curface * 3 + offset + elementsize], (char *)col);
-                                               cp = &attribs.mcol[b].array[a * 4 + 2];
-                                               col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a;
-                                               copy_v4_v4_char((char *)&varray[elementsize * curface * 3 + offset + elementsize * 2], (char *)col);
-                                               offset += sizeof(unsigned char) * 4;
-                                       }
+                               if (matconv[a].attribs.tottang && matconv[a].attribs.tang.array) {
+                                       matconv[a].datatypes[numdata].index = matconv[a].attribs.tang.gl_index;
+                                       matconv[a].datatypes[numdata].size = 4;
+                                       matconv[a].datatypes[numdata].type = GL_FLOAT;
+                                       numdata++;
                                }
-                               if (attribs.tottang && attribs.tang.array) {
-                                       const float *tang = attribs.tang.array[a * 4 + 0];
-                                       copy_v4_v4((float *)&varray[elementsize * curface * 3 + offset], tang);
-                                       tang = attribs.tang.array[a * 4 + 1];
-                                       copy_v4_v4((float *)&varray[elementsize * curface * 3 + offset + elementsize], tang);
-                                       tang = attribs.tang.array[a * 4 + 2];
-                                       copy_v4_v4((float *)&varray[elementsize * curface * 3 + offset + elementsize * 2], tang);
-                                       offset += sizeof(float) * 4;
+                               if (numdata != 0) {
+                                       matconv[a].numdata = numdata;
+                                       max_element_size = max_ii(GPU_attrib_element_size(matconv[a].datatypes, numdata), max_element_size);
                                }
-                               (void)offset;
                        }
-                       curface++;
-                       if (mface->v4) {
-                               if (do_draw && numdata != 0) {
-                                       offset = 0;
-                                       if (attribs.totorco && attribs.orco.array) {
-                                               copy_v3_v3((float *)&varray[elementsize * curface * 3], (float *)attribs.orco.array[mface->v3]);
-                                               copy_v3_v3((float *)&varray[elementsize * curface * 3 + elementsize], (float *)attribs.orco.array[mface->v4]);
-                                               copy_v3_v3((float *)&varray[elementsize * curface * 3 + elementsize * 2], (float *)attribs.orco.array[mface->v1]);
+               }
+
+               /* part two, generate and fill the arrays with the data */
+               if (max_element_size > 0) {
+                       buffer = GPU_buffer_alloc(max_element_size * dm->drawObject->tot_loop_verts, false);
+
+                       if (buffer == NULL) {
+                               buffer = GPU_buffer_alloc(max_element_size * dm->drawObject->tot_loop_verts, true);
+                       }
+                       varray = GPU_buffer_lock_stream(buffer);
+                       if (varray == NULL) {
+                               GPU_buffer_unbind();
+                               GPU_buffer_free(buffer);
+                               MEM_freeN(mat_orig_to_new);
+                               MEM_freeN(matconv);
+                               fprintf(stderr, "Out of memory, can't draw object\n");
+                               return;
+                       }
+
+                       mface = mf;
+
+                       for (a = 0; a < totface; a++, mface++) {
+                               int i = mat_orig_to_new[mface->mat_nr];
+                               offset = tot_loops * max_element_size;
+
+                               if (matconv[i].numdata != 0) {
+                                       if (matconv[i].attribs.totorco && matconv[i].attribs.orco.array) {
+                                               copy_v3_v3((float *)&varray[offset], (float *)matconv[i].attribs.orco.array[mface->v1]);
+                                               copy_v3_v3((float *)&varray[offset + max_element_size], (float *)matconv[i].attribs.orco.array[mface->v2]);
+                                               copy_v3_v3((float *)&varray[offset + max_element_size * 2], (float *)matconv[i].attribs.orco.array[mface->v3]);
+                                               if (mface->v4) {
+                                                       copy_v3_v3((float *)&varray[offset + max_element_size * 3], (float *)matconv[i].attribs.orco.array[mface->v4]);
+                                               }
                                                offset += sizeof(float) * 3;
                                        }
-                                       for (b = 0; b < attribs.tottface; b++) {
-                                               if (attribs.tface[b].array) {
-                                                       MTFace *tf = &attribs.tface[b].array[a];
-                                                       copy_v2_v2((float *)&varray[elementsize * curface * 3 + offset], tf->uv[2]);
-                                                       copy_v2_v2((float *)&varray[elementsize * curface * 3 + offset + elementsize], tf->uv[3]);
-                                                       copy_v2_v2((float *)&varray[elementsize * curface * 3 + offset + elementsize * 2], tf->uv[0]);
+                                       for (b = 0; b < matconv[i].attribs.tottface; b++) {
+                                               if (matconv[i].attribs.tface[b].array) {
+                                                       MTFace *tf = &matconv[i].attribs.tface[b].array[a];
+                                                       copy_v2_v2((float *)&varray[offset], tf->uv[0]);
+                                                       copy_v2_v2((float *)&varray[offset + max_element_size], tf->uv[1]);
+                                                       copy_v2_v2((float *)&varray[offset + max_element_size * 2], tf->uv[2]);
+                                                       if (mface->v4) {
+                                                               copy_v2_v2((float *)&varray[offset + max_element_size * 3], tf->uv[3]);
+                                                       }
                                                        offset += sizeof(float) * 2;
                                                }
                                        }
-                                       for (b = 0; b < attribs.totmcol; b++) {
-                                               if (attribs.mcol[b].array) {
-                                                       MCol *cp = &attribs.mcol[b].array[a * 4 + 2];
+                                       for (b = 0; b < matconv[i].attribs.totmcol; b++) {
+                                               if (matconv[i].attribs.mcol[b].array) {
+                                                       MCol *cp = &matconv[i].attribs.mcol[b].array[a * 4 + 0];
                                                        GLubyte col[4];
                                                        col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a;
-                                                       copy_v4_v4_char((char *)&varray[elementsize * curface * 3 + offset], (char *)col);
-                                                       cp = &attribs.mcol[b].array[a * 4 + 3];
+                                                       copy_v4_v4_char((char *)&varray[offset], (char *)col);
+                                                       cp = &matconv[i].attribs.mcol[b].array[a * 4 + 1];
                                                        col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a;
-                                                       copy_v4_v4_char((char *)&varray[elementsize * curface * 3 + offset + elementsize], (char *)col);
-                                                       cp = &attribs.mcol[b].array[a * 4 + 0];
+                                                       copy_v4_v4_char((char *)&varray[offset + max_element_size], (char *)col);
+                                                       cp = &matconv[i].attribs.mcol[b].array[a * 4 + 2];
                                                        col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a;
-                                                       copy_v4_v4_char((char *)&varray[elementsize * curface * 3 + offset + elementsize * 2], (char *)col);
+                                                       copy_v4_v4_char((char *)&varray[offset + max_element_size * 2], (char *)col);
+                                                       if (mface->v4) {
+                                                               cp = &matconv[i].attribs.mcol[b].array[a * 4 + 3];
+                                                               col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a;
+                                                               copy_v4_v4_char((char *)&varray[offset + max_element_size * 3], (char *)col);
+                                                       }
                                                        offset += sizeof(unsigned char) * 4;
                                                }
                                        }
-                                       if (attribs.tottang && attribs.tang.array) {
-                                               const float *tang = attribs.tang.array[a * 4 + 2];
-                                               copy_v4_v4((float *)&varray[elementsize * curface * 3 + offset], tang);
-                                               tang = attribs.tang.array[a * 4 + 3];
-                                               copy_v4_v4((float *)&varray[elementsize * curface * 3 + offset + elementsize], tang);
-                                               tang = attribs.tang.array[a * 4 + 0];
-                                               copy_v4_v4((float *)&varray[elementsize * curface * 3 + offset + elementsize * 2], tang);
+                                       if (matconv[i].attribs.tottang && matconv[i].attribs.tang.array) {
+                                               const float *tang = matconv[i].attribs.tang.array[a * 4 + 0];
+                                               copy_v4_v4((float *)&varray[offset], tang);
+                                               tang = matconv[i].attribs.tang.array[a * 4 + 1];
+                                               copy_v4_v4((float *)&varray[offset + max_element_size], tang);
+                                               tang = matconv[i].attribs.tang.array[a * 4 + 2];
+                                               copy_v4_v4((float *)&varray[offset + max_element_size * 2], tang);
+                                               if (mface->v4) {
+                                                       tang = matconv[i].attribs.tang.array[a * 4 + 3];
+                                                       copy_v4_v4((float *)&varray[offset + max_element_size * 3], tang);
+                                               }
                                                offset += sizeof(float) * 4;
                                        }
-                                       (void)offset;
                                }
-                               curface++;
-                               i++;
+
+                               if (mface->v4) {
+                                       tot_loops += 4;
+                               }
+                               else {
+                                       tot_loops += 3;
+                               }
                        }
+                       GPU_buffer_unlock(buffer);
                }
-               numfaces = curface - start;
-               if (numfaces > 0) {
+
+               for (a = 0; a < tot_active_mat; a++) {
+                       new_matnr = dm->drawObject->materials[a].mat_nr;
+
+                       do_draw = setMaterial(new_matnr + 1, &gattribs);
+
                        if (do_draw) {
-                               if (numdata != 0) {
-                                       GPU_buffer_unlock(buffer);
-                                       GPU_interleaved_attrib_setup(buffer, datatypes, numdata);
+                               if (matconv[a].numdata) {
+                                       GPU_interleaved_attrib_setup(buffer, matconv[a].datatypes, matconv[a].numdata, max_element_size);
+                               }
+                               GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES,
+                                                        dm->drawObject->materials[a].start, dm->drawObject->materials[a].totelements);
+                               if (matconv[a].numdata) {
+                                       GPU_interleaved_attrib_unbind();
                                }
-                               glDrawArrays(GL_TRIANGLES, start * 3, (curface - start) * 3);
                        }
                }
+
                GPU_buffer_unbind();
-               
-               GPU_buffer_free(buffer);
+               if (buffer)
+                       GPU_buffer_free(buffer);
+
+               MEM_freeN(mat_orig_to_new);
+               MEM_freeN(matconv);
        }
        
        glShadeModel(GL_FLAT);
@@ -1223,7 +1221,7 @@ static void cdDM_drawMappedFacesMat(DerivedMesh *dm,
        if (cddm->pbvh && cddm->pbvh_draw && BKE_pbvh_type(cddm->pbvh) == PBVH_BMESH) {
                if (BKE_pbvh_has_faces(cddm->pbvh)) {
                        setMaterial(userData, 1, &gattribs);
-                       BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, false);
+                       BKE_pbvh_draw(cddm->pbvh, NULL, NULL, NULL, false, false);
                }
 
                return;
@@ -1326,6 +1324,582 @@ static void cdDM_drawMappedEdges(DerivedMesh *dm, DMSetDrawOptions setDrawOption
        glEnd();
 }
 
+static void cdDM_buffer_copy_triangles(DerivedMesh *dm, float *varray_, int *mat_orig_to_new, void *UNUSED(user))
+{
+       GPUBufferMaterial *gpumat;
+       MFace *f;
+       int i, start, totface, findex = 0;
+       unsigned int *varray = (unsigned int *)varray_;
+
+       f = dm->getTessFaceArray(dm);
+
+       totface = dm->getNumTessFaces(dm);
+       for (i = 0; i < totface; i++, f++) {
+               gpumat = dm->drawObject->materials + mat_orig_to_new[f->mat_nr];
+               start = gpumat->counter;
+
+               /* v1 v2 v3 */
+               varray[start++] = findex;
+               varray[start++] = findex + 1;
+               varray[start++] = findex + 2;
+
+               if (f->v4) {
+                       /* v3 v4 v1 */
+                       varray[start++] = findex + 2;
+                       varray[start++] = findex + 3;
+                       varray[start++] = findex;
+
+                       gpumat->counter += 6;
+                       findex += 4;
+               }
+               else {
+                       gpumat->counter += 3;
+                       findex += 3;
+               }
+       }
+}
+
+static void cdDM_buffer_copy_vertex(DerivedMesh *dm, float *varray, int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+       MVert *mvert;
+       MFace *f;
+       int i, j, start, totface;
+
+       mvert = dm->getVertArray(dm);
+       f = dm->getTessFaceArray(dm);
+
+       start = 0;
+       totface = dm->getNumTessFaces(dm);
+       for (i = 0; i < totface; i++, f++) {
+               /* v1 v2 v3 */
+               copy_v3_v3(&varray[start], mvert[f->v1].co);
+               copy_v3_v3(&varray[start + 3], mvert[f->v2].co);
+               copy_v3_v3(&varray[start + 6], mvert[f->v3].co);
+               start += 9;
+
+               if (f->v4) {
+                       /* v4 */
+                       copy_v3_v3(&varray[start], mvert[f->v4].co);
+                       start += 3;
+               }
+       }
+
+       /* copy loose points */
+       j = dm->drawObject->tot_triangle_point * 3;
+       for (i = 0; i < dm->drawObject->totvert; i++) {
+               if (dm->drawObject->vert_points[i].point_index >= dm->drawObject->tot_triangle_point) {
+                       copy_v3_v3(&varray[j], mvert[i].co);
+                       j += 3;
+               }
+       }
+}
+
+static void cdDM_buffer_copy_normal(DerivedMesh *dm, float *varray_, int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+       int i, totface;
+       int start;
+       float f_no[3];
+
+       short *varray = (short *)varray_;
+       const float *nors = dm->getTessFaceDataArray(dm, CD_NORMAL);
+       short (*tlnors)[4][3] = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL);
+       MVert *mvert = dm->getVertArray(dm);
+       MFace *f = dm->getTessFaceArray(dm);
+
+       totface = dm->getNumTessFaces(dm);
+       start = 0;
+       for (i = 0; i < totface; i++, f++) {
+               const int smoothnormal = (f->flag & ME_SMOOTH);
+
+               if (tlnors) {
+                       short (*tlnor)[3] = tlnors[i];
+                       /* Copy loop normals */
+                       copy_v3_v3_short(&varray[start], tlnor[0]);
+                       copy_v3_v3_short(&varray[start + 3], tlnor[1]);
+                       copy_v3_v3_short(&varray[start + 6], tlnor[2]);
+                       start += 9;
+
+                       if (f->v4) {
+                               copy_v3_v3_short(&varray[start], tlnor[3]);
+                               start += 3;
+                       }
+               }
+               else if (smoothnormal) {
+                       /* copy vertex normal */
+                       copy_v3_v3_short(&varray[start], mvert[f->v1].no);
+                       copy_v3_v3_short(&varray[start + 4], mvert[f->v2].no);
+                       copy_v3_v3_short(&varray[start + 8], mvert[f->v3].no);
+                       start += 12;
+
+                       if (f->v4) {
+                               copy_v3_v3_short(&varray[start], mvert[f->v4].no);
+                               start += 4;
+                       }
+               }
+               else if (nors) {
+                       /* copy cached face normal */
+                       normal_float_to_short_v3(&varray[start], &nors[i * 3]);
+                       normal_float_to_short_v3(&varray[start + 4], &nors[i * 3]);
+                       normal_float_to_short_v3(&varray[start + 8], &nors[i * 3]);
+                       start += 12;
+
+                       if (f->v4) {
+                               normal_float_to_short_v3(&varray[start], &nors[i * 3]);
+                               start += 4;
+                       }
+               }
+               else {
+                       /* calculate face normal */
+                       if (f->v4)
+                               normal_quad_v3(f_no, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co, mvert[f->v4].co);
+                       else
+                               normal_tri_v3(f_no, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co);
+
+                       normal_float_to_short_v3(&varray[start], f_no);
+                       normal_float_to_short_v3(&varray[start + 4], f_no);
+                       normal_float_to_short_v3(&varray[start + 8], f_no);
+                       start += 12;
+
+                       if (f->v4) {
+                               normal_float_to_short_v3(&varray[start], f_no);
+                               start += 4;
+                       }
+               }
+       }
+}
+
+static void cdDM_buffer_copy_uv(DerivedMesh *dm, float *varray, int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+       int start;
+       int i, totface;
+
+       MTFace *mtface;
+       MFace *f;
+
+       if (!(mtface = DM_get_tessface_data_layer(dm, CD_MTFACE)))
+               return;
+       f = dm->getTessFaceArray(dm);
+
+       start = 0;
+       totface = dm->getNumTessFaces(dm);
+       for (i = 0; i < totface; i++, f++) {
+               /* v1 v2 v3 */
+               copy_v2_v2(&varray[start], mtface[i].uv[0]);
+               copy_v2_v2(&varray[start + 2], mtface[i].uv[1]);
+               copy_v2_v2(&varray[start + 4], mtface[i].uv[2]);
+               start += 6;
+
+               if (f->v4) {
+                       /* v4 */
+                       copy_v2_v2(&varray[start], mtface[i].uv[3]);
+                       start += 2;
+               }
+       }
+}
+
+
+static void cdDM_buffer_copy_uv_texpaint(DerivedMesh *dm, float *varray, int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+       int start;
+       int i, totface;
+
+       int totmaterial = dm->totmat;
+       MTFace **mtface_base;
+       MTFace *stencil_base;
+       int stencil;
+       MFace *mf;
+
+       /* should have been checked for before, reassert */
+       BLI_assert(DM_get_tessface_data_layer(dm, CD_MTFACE));
+       mf = dm->getTessFaceArray(dm);
+       mtface_base = MEM_mallocN(totmaterial * sizeof(*mtface_base), "texslots");
+
+       for (i = 0; i < totmaterial; i++) {
+               mtface_base[i] = DM_paint_uvlayer_active_get(dm, i);
+       }
+
+       stencil = CustomData_get_stencil_layer(&dm->faceData, CD_MTFACE);
+       stencil_base = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, stencil);
+
+       totface = dm->getNumTessFaces(dm);
+       start = 0;
+
+       for (i = 0; i < totface; i++, mf++) {
+               int mat_i = mf->mat_nr;
+
+               /* v1 v2 v3 */
+               copy_v2_v2(&varray[start], mtface_base[mat_i][i].uv[0]);
+               copy_v2_v2(&varray[start + 2], stencil_base[i].uv[0]);
+               copy_v2_v2(&varray[start + 4], mtface_base[mat_i][i].uv[1]);
+               copy_v2_v2(&varray[start + 6], stencil_base[i].uv[1]);
+               copy_v2_v2(&varray[start + 8], mtface_base[mat_i][i].uv[2]);
+               copy_v2_v2(&varray[start + 10], stencil_base[i].uv[2]);
+               start += 12;
+
+               if (mf->v4) {
+                       /* v4 */
+                       copy_v2_v2(&varray[start], mtface_base[mat_i][i].uv[3]);
+                       copy_v2_v2(&varray[start + 2], stencil_base[i].uv[3]);
+                       start += 4;
+               }
+       }
+
+       MEM_freeN(mtface_base);
+}
+
+
+static void copy_mcol_uc3(unsigned char *v, unsigned char *col)
+{
+       v[0] = col[3];
+       v[1] = col[2];
+       v[2] = col[1];
+}
+
+/* treat varray_ as an array of MCol, four MCol's per face */
+static void cdDM_buffer_copy_mcol(DerivedMesh *dm, float *varray_, int *UNUSED(mat_orig_to_new), void *user)
+{
+       int i, totface, start;
+       unsigned char *varray = (unsigned char *)varray_;
+       unsigned char *mcol = (unsigned char *)user;
+       MFace *f = dm->getTessFaceArray(dm);
+
+       totface = dm->getNumTessFaces(dm);
+       start = 0;
+       for (i = 0; i < totface; i++, f++) {
+               /* v1 v2 v3 */
+               copy_mcol_uc3(&varray[start], &mcol[i * 16]);
+               copy_mcol_uc3(&varray[start + 3], &mcol[i * 16 + 4]);
+               copy_mcol_uc3(&varray[start + 6], &mcol[i * 16 + 8]);
+               start += 9;
+
+               if (f->v4) {
+                       /* v3 v4 v1 */
+                       copy_mcol_uc3(&varray[start], &mcol[i * 16 + 12]);
+                       start += 3;
+               }
+       }
+}
+
+static void cdDM_buffer_copy_edge(DerivedMesh *dm, float *varray_, int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+       MEdge *medge, *medge_base;
+       unsigned int *varray = (unsigned int *)varray_;
+       int i, totedge, iloose, inorm, iloosehidden, inormhidden;
+       int tot_loose_hidden = 0, tot_loose = 0;
+       int tot_hidden = 0, tot = 0;
+
+       medge_base = medge = dm->getEdgeArray(dm);
+       totedge = dm->getNumEdges(dm);
+
+       for (i = 0; i < totedge; i++, medge++) {
+               if (medge->flag & ME_EDGEDRAW) {
+                       if (medge->flag & ME_LOOSEEDGE) tot_loose++;
+                       else tot++;
+               }
+               else {
+                       if (medge->flag & ME_LOOSEEDGE) tot_loose_hidden++;
+                       else tot_hidden++;
+               }
+       }
+
+       inorm = 0;
+       inormhidden = tot;
+       iloose = tot + tot_hidden;
+       iloosehidden = iloose + tot_loose;
+
+       medge = medge_base;
+       for (i = 0; i < totedge; i++, medge++) {
+               if (medge->flag & ME_EDGEDRAW) {
+                       if (medge->flag & ME_LOOSEEDGE) {
+                               varray[iloose * 2] = dm->drawObject->vert_points[medge->v1].point_index;
+                               varray[iloose * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
+                               iloose++;
+                       }
+                       else {
+                               varray[inorm * 2] = dm->drawObject->vert_points[medge->v1].point_index;
+                               varray[inorm * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
+                               inorm++;
+                       }
+               }
+               else {
+                       if (medge->flag & ME_LOOSEEDGE) {
+                               varray[iloosehidden * 2] = dm->drawObject->vert_points[medge->v1].point_index;
+                               varray[iloosehidden * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
+                               iloosehidden++;
+                       }
+                       else {
+                               varray[inormhidden * 2] = dm->drawObject->vert_points[medge->v1].point_index;
+                               varray[inormhidden * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
+                               inormhidden++;
+                       }
+               }
+       }
+
+       dm->drawObject->tot_loose_edge_drawn = tot_loose;
+       dm->drawObject->loose_edge_offset = tot + tot_hidden;
+       dm->drawObject->tot_edge_drawn = tot;
+}
+
+static void cdDM_buffer_copy_uvedge(DerivedMesh *dm, float *varray, int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+       MTFace *tf = DM_get_tessface_data_layer(dm, CD_MTFACE);
+       int i, j = 0;
+
+       if (!tf)
+               return;
+
+       for (i = 0; i < dm->numTessFaceData; i++, tf++) {
+               MFace mf;
+               dm->getTessFace(dm, i, &mf);
+
+               copy_v2_v2(&varray[j], tf->uv[0]);
+               copy_v2_v2(&varray[j + 2], tf->uv[1]);
+
+               copy_v2_v2(&varray[j + 4], tf->uv[1]);
+               copy_v2_v2(&varray[j + 6], tf->uv[2]);
+
+               if (!mf.v4) {
+                       copy_v2_v2(&varray[j + 8], tf->uv[2]);
+                       copy_v2_v2(&varray[j + 10], tf->uv[0]);
+                       j += 12;
+               }
+               else {
+                       copy_v2_v2(&varray[j + 8], tf->uv[2]);
+                       copy_v2_v2(&varray[j + 10], tf->uv[3]);
+
+                       copy_v2_v2(&varray[j + 12], tf->uv[3]);
+                       copy_v2_v2(&varray[j + 14], tf->uv[0]);
+                       j += 16;
+               }
+       }
+}
+
+static void cdDM_copy_gpu_data(DerivedMesh *dm, int type, float *varray,
+                        int *mat_orig_to_new, void *user_data)
+{
+       switch(type) {
+               case GPU_BUFFER_VERTEX:
+                       cdDM_buffer_copy_vertex(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_NORMAL:
+                       cdDM_buffer_copy_normal(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_COLOR:
+                       cdDM_buffer_copy_mcol(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_UV:
+                       cdDM_buffer_copy_uv(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_UV_TEXPAINT:
+                       cdDM_buffer_copy_uv_texpaint(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_EDGE:
+                       cdDM_buffer_copy_edge(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_UVEDGE:
+                       cdDM_buffer_copy_uvedge(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_TRIANGLES:
+                       cdDM_buffer_copy_triangles(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               default:
+                       break;
+       }
+}
+
+/* add a new point to the list of points related to a particular
+ * vertex */
+#ifdef USE_GPU_POINT_LINK
+
+static void cdDM_drawobject_add_vert_point(GPUDrawObject *gdo, int vert_index, int point_index)
+{
+       GPUVertPointLink *lnk;
+
+       lnk = &gdo->vert_points[vert_index];
+
+       /* if first link is in use, add a new link at the end */
+       if (lnk->point_index != -1) {
+               /* get last link */
+               for (; lnk->next; lnk = lnk->next) ;
+
+               /* add a new link from the pool */
+               lnk = lnk->next = &gdo->vert_points_mem[gdo->vert_points_usage];
+               gdo->vert_points_usage++;
+       }
+
+       lnk->point_index = point_index;
+}
+
+#else
+
+static void cdDM_drawobject_add_vert_point(GPUDrawObject *gdo, int vert_index, int point_index)
+{
+       GPUVertPointLink *lnk;
+       lnk = &gdo->vert_points[vert_index];
+       if (lnk->point_index == -1) {
+               lnk->point_index = point_index;
+       }
+}
+
+#endif  /* USE_GPU_POINT_LINK */
+
+static int tri_indices[2][3] = {{0, 1, 2}, {2, 3, 0}};
+
+/* update the vert_points and triangle_to_mface fields with a new
+ * triangle */
+static void cdDM_drawobject_add_triangle(GPUDrawObject *gdo,
+                                        int v1, int v2, int v3, bool quad, int loopindex)
+{
+       int i, v[3] = {v1, v2, v3};
+       int *tri_i = quad ? tri_indices[1] : tri_indices[0];
+       for (i = 0; i < 3; i++)
+               cdDM_drawobject_add_vert_point(gdo, v[i], loopindex + tri_i[i]);
+}
+
+/* for each vertex, build a list of points related to it; these lists
+ * are stored in an array sized to the number of vertices */
+static void cdDM_drawobject_init_vert_points(GPUDrawObject *gdo, MFace *f, int totface, int totmat)
+{
+       GPUBufferMaterial *mat;
+       int i, *mat_orig_to_new;
+       int tot_loops = 0;
+
+       mat_orig_to_new = MEM_callocN(sizeof(*mat_orig_to_new) * totmat,
+                                                    "GPUDrawObject.mat_orig_to_new");
+       /* allocate the array and space for links */
+       gdo->vert_points = MEM_mallocN(sizeof(GPUVertPointLink) * gdo->totvert,
+                                      "GPUDrawObject.vert_points");
+#ifdef USE_GPU_POINT_LINK
+       gdo->vert_points_mem = MEM_callocN(sizeof(GPUVertPointLink) * gdo->tot_triangle_point,
+                                          "GPUDrawObject.vert_points_mem");
+       gdo->vert_points_usage = 0;
+#endif
+
+       /* build a map from the original material indices to the new
+        * GPUBufferMaterial indices */
+       for (i = 0; i < gdo->totmaterial; i++) {
+               mat_orig_to_new[gdo->materials[i].mat_nr] = i;
+               gdo->materials[i].counter = 0;
+       }
+
+       /* -1 indicates the link is not yet used */
+       for (i = 0; i < gdo->totvert; i++) {
+#ifdef USE_GPU_POINT_LINK
+               gdo->vert_points[i].link = NULL;
+#endif
+               gdo->vert_points[i].point_index = -1;
+       }
+
+       for (i = 0; i < totface; i++, f++) {
+               mat = &gdo->materials[mat_orig_to_new[f->mat_nr]];
+
+               mat->polys[mat->counter++] = i;
+
+               /* add triangle */
+               cdDM_drawobject_add_triangle(gdo, f->v1, f->v2, f->v3, false, tot_loops);
+               mat->totelements += 3;
+
+               /* add second triangle for quads */
+               if (f->v4) {
+                       cdDM_drawobject_add_triangle(gdo, f->v3, f->v4, f->v1, true, tot_loops);
+                       mat->totelements += 3;
+                       tot_loops += 4;
+               }
+               else {
+                       tot_loops += 3;
+               }
+       }
+
+       /* map any unused vertices to loose points */
+       for (i = 0; i < gdo->totvert; i++) {
+               if (gdo->vert_points[i].point_index == -1) {
+                       gdo->vert_points[i].point_index = gdo->tot_triangle_point + gdo->tot_loose_point;
+                       gdo->tot_loose_point++;
+               }
+       }
+
+       MEM_freeN(mat_orig_to_new);
+}
+
+typedef struct {
+       int elements;
+       int loops;
+       int polys;
+} GPUMaterialInfo;
+
+/* see GPUDrawObject's structure definition for a description of the
+ * data being initialized here */
+static GPUDrawObject *cdDM_GPUobject_new(DerivedMesh *dm)
+{
+       GPUDrawObject *gdo;
+       MFace *mface;
+       int totmat = dm->totmat;
+       GPUMaterialInfo *mat_info;
+       int i, curmat, totelements, totface, totloops;
+
+       /* object contains at least one material (default included) so zero means uninitialized dm */
+       BLI_assert(totmat != 0);
+
+       mface = dm->getTessFaceArray(dm);
+       totface = dm->getNumTessFaces(dm);
+
+       /* get the number of points used by each material, treating
+        * each quad as two triangles */
+       mat_info = MEM_callocN(sizeof(*mat_info) * totmat, "GPU_drawobject_new.mat_orig_to_new");
+       for (i = 0; i < totface; i++) {
+               mat_info[mface[i].mat_nr].polys++;
+               if (mface[i].v4) {
+                       mat_info[mface[i].mat_nr].elements += 6;
+                       mat_info[mface[i].mat_nr].loops += 4;
+               }
+               else {
+                       mat_info[mface[i].mat_nr].elements += 3;
+                       mat_info[mface[i].mat_nr].loops += 3;
+               }
+       }
+       /* create the GPUDrawObject */
+       gdo = MEM_callocN(sizeof(GPUDrawObject), "GPUDrawObject");
+       gdo->totvert = dm->getNumVerts(dm);
+       gdo->totedge = dm->getNumEdges(dm);
+
+       /* count the number of materials used by this DerivedMesh */
+       for (i = 0; i < totmat; i++) {
+               if (mat_info[i].elements > 0)
+                       gdo->totmaterial++;
+       }
+
+       /* allocate an array of materials used by this DerivedMesh */
+       gdo->materials = MEM_mallocN(sizeof(GPUBufferMaterial) * gdo->totmaterial,
+                                    "GPUDrawObject.materials");
+
+       /* initialize the materials array */
+       for (i = 0, curmat = 0, totelements = 0, totloops = 0; i < totmat; i++) {
+               if (mat_info[i].elements > 0) {
+                       gdo->materials[curmat].start = totelements;
+                       /* can set it to points now but used in cdDM_drawobject_init_vert_points as counter */
+                       gdo->materials[curmat].totelements = 0;
+                       gdo->materials[curmat].totloops = mat_info[i].loops;
+                       gdo->materials[curmat].mat_nr = i;
+                       gdo->materials[curmat].totpolys = mat_info[i].polys;
+                       gdo->materials[curmat].polys = MEM_mallocN(sizeof(int) * mat_info[i].polys, "GPUBufferMaterial.polys");
+
+                       totelements += mat_info[i].elements;
+                       totloops += mat_info[i].loops;
+                       curmat++;
+               }
+       }
+
+       gdo->tot_loop_verts = totloops;
+
+       /* store total number of points used for triangles */
+       gdo->tot_triangle_point = totelements;
+
+       cdDM_drawobject_init_vert_points(gdo, mface, totface, totmat);
+       MEM_freeN(mat_info);
+
+       return gdo;
+}
+
 static void cdDM_foreachMappedVert(
         DerivedMesh *dm,
         void (*func)(void *userData, int index, const float co[3], const float no_f[3], const short no_s[3]),
@@ -1541,6 +2115,9 @@ static CDDerivedMesh *cdDM_create(const char *desc)
        dm->drawMappedFacesGLSL = cdDM_drawMappedFacesGLSL;
        dm->drawMappedFacesMat = cdDM_drawMappedFacesMat;
 
+       dm->copy_gpu_data = cdDM_copy_gpu_data;
+       dm->gpuObjectNew = cdDM_GPUobject_new;
+
        dm->foreachMappedVert = cdDM_foreachMappedVert;
        dm->foreachMappedEdge = cdDM_foreachMappedEdge;
        dm->foreachMappedLoop = cdDM_foreachMappedLoop;
index 361557633cbcb335f070ccbd31ea0827ea9b2f93..e5e36b24a460d9170b0e62ce5e6f74cb62192aec 100644 (file)
@@ -1679,6 +1679,7 @@ void BKE_pbvh_raycast_project_ray_root(
 typedef struct {
        DMSetMaterial setMaterial;
        bool wireframe;
+       bool fast;
 } PBVHNodeDrawData;
 
 void BKE_pbvh_node_draw(PBVHNode *node, void *data_v)
@@ -1705,7 +1706,8 @@ void BKE_pbvh_node_draw(PBVHNode *node, void *data_v)
        if (!(node->flag & PBVH_FullyHidden)) {
                GPU_draw_pbvh_buffers(node->draw_buffers,
                                 data->setMaterial,
-                                data->wireframe);
+                                data->wireframe,
+                                data->fast);
        }
 }
 
@@ -1775,9 +1777,9 @@ static void pbvh_node_check_diffuse_changed(PBVH *bvh, PBVHNode *node)
 }
 
 void BKE_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3],
-                   DMSetMaterial setMaterial, bool wireframe)
+                   DMSetMaterial setMaterial, bool wireframe, bool fast)
 {
-       PBVHNodeDrawData draw_data = {setMaterial, wireframe};
+       PBVHNodeDrawData draw_data = {setMaterial, wireframe, fast};
        PBVHNode **nodes;
        int a, totnode;
 
index cc680224c31b0c366ee1d7fdd9b49d2400b36668..733cb15ea865c2f30755b5f4d613a3e4a49c3f5e 100644 (file)
@@ -72,6 +72,7 @@
 #include "GPU_draw.h"
 #include "GPU_extensions.h"
 #include "GPU_glew.h"
+#include "GPU_buffers.h"
 
 #include "CCGSubSurf.h"
 
@@ -1734,12 +1735,25 @@ static void ccgDM_drawLooseEdges(DerivedMesh *dm)
        }
 }
 
+static void ccgDM_NormalFast(float *a, float *b, float *c, float *d, float no[3])
+{
+       float a_cX = c[0] - a[0], a_cY = c[1] - a[1], a_cZ = c[2] - a[2];
+       float b_dX = d[0] - b[0], b_dY = d[1] - b[1], b_dZ = d[2] - b[2];
+
+       no[0] = b_dY * a_cZ - b_dZ * a_cY;
+       no[1] = b_dZ * a_cX - b_dX * a_cZ;
+       no[2] = b_dX * a_cY - b_dY * a_cX;
+       
+       normalize_v3(no);
+}
+
+
 static void ccgDM_glNormalFast(float *a, float *b, float *c, float *d)
 {
        float a_cX = c[0] - a[0], a_cY = c[1] - a[1], a_cZ = c[2] - a[2];
        float b_dX = d[0] - b[0], b_dY = d[1] - b[1], b_dZ = d[2] - b[2];
        float no[3];
-
+       
        no[0] = b_dY * a_cZ - b_dZ * a_cY;
        no[1] = b_dZ * a_cX - b_dX * a_cZ;
        no[2] = b_dX * a_cY - b_dY * a_cX;
@@ -1749,46 +1763,35 @@ static void ccgDM_glNormalFast(float *a, float *b, float *c, float *d)
 }
 
 /* Only used by non-editmesh types */
-static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)[4], bool fast, DMSetMaterial setMaterial)
+static void ccgDM_buffer_copy_normal(DerivedMesh *dm, float *varray_,
+                                    int *UNUSED(mat_orig_to_new), void *UNUSED(user_data))
 {
        CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
        CCGSubSurf *ss = ccgdm->ss;
        CCGKey key;
+       short *varray = (short *)varray_;
        short (*lnors)[4][3] = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL);
        int gridSize = ccgSubSurf_getGridSize(ss);
        int gridFaces = gridSize - 1;
        DMFlagMat *faceFlags = ccgdm->faceFlags;
-       int step = (fast) ? gridSize - 1 : 1;
        int i, totface = ccgSubSurf_getNumFaces(ss);
-       int drawcurrent = 0, matnr = -1, shademodel = -1;
+       int shademodel;
+       int start = 0;
 
        CCG_key_top_level(&key, ss);
        ccgdm_pbvh_update(ccgdm);
 
-       if (ccgdm->pbvh && ccgdm->multires.mmd && !fast) {
-               if (BKE_pbvh_has_faces(ccgdm->pbvh)) {
-                       BKE_pbvh_draw(ccgdm->pbvh, partial_redraw_planes, NULL,
-                                     setMaterial, false);
-                       glShadeModel(GL_FLAT);
-               }
-
-               return;
-       }
-
        for (i = 0; i < totface; i++) {
                CCGFace *f = ccgdm->faceMap[i].face;
                int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
                int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
-               int new_matnr, new_shademodel;
                short (*ln)[4][3] = NULL;
 
                if (faceFlags) {
-                       new_shademodel = (lnors || (faceFlags[index].flag & ME_SMOOTH)) ? GL_SMOOTH : GL_FLAT;
-                       new_matnr = faceFlags[index].mat_nr;
+                       shademodel = (lnors || (faceFlags[index].flag & ME_SMOOTH)) ? GL_SMOOTH : GL_FLAT;
                }
                else {
-                       new_shademodel = GL_SMOOTH;
-                       new_matnr = 0;
+                       shademodel = GL_SMOOTH;
                }
 
                if (lnors) {
@@ -1796,85 +1799,468 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
                        lnors += gridFaces * gridFaces * numVerts;
                }
 
-               if (shademodel != new_shademodel || matnr != new_matnr) {
-                       matnr = new_matnr;
-                       shademodel = new_shademodel;
-
-                       if (setMaterial)
-                               drawcurrent = setMaterial(matnr + 1, NULL);
-                       else
-                               drawcurrent = 1;
-
-                       glShadeModel(shademodel);
-               }
-
-               if (!drawcurrent)
-                       continue;
-
                for (S = 0; S < numVerts; S++) {
                        CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S);
 
                        if (ln) {
                                /* Can't use quad strips here... */
-                               glBegin(GL_QUADS);
-                               for (y = 0; y < gridFaces; y += step) {
-                                       for (x = 0; x < gridFaces; x += step) {
-                                               float *a = CCG_grid_elem_co(&key, faceGridData, x, y + 0);
-                                               float *b = CCG_grid_elem_co(&key, faceGridData, x + step, y + 0);
-                                               float *c = CCG_grid_elem_co(&key, faceGridData, x + step, y + step);
-                                               float *d = CCG_grid_elem_co(&key, faceGridData, x, y + step);
-
-                                               glNormal3sv(ln[0][1]);
-                                               glVertex3fv(d);
-                                               glNormal3sv(ln[0][2]);
-                                               glVertex3fv(c);
-                                               glNormal3sv(ln[0][3]);
-                                               glVertex3fv(b);
-                                               glNormal3sv(ln[0][0]);
-                                               glVertex3fv(a);
-                                               ln += step;
+                               for (y = 0; y < gridFaces; y ++) {
+                                       for (x = 0; x < gridFaces; x ++) {
+                                               copy_v3_v3_short(&varray[start], ln[0][0]);
+                                               copy_v3_v3_short(&varray[start + 4], ln[0][3]);
+                                               copy_v3_v3_short(&varray[start + 8], ln[0][2]);
+                                               copy_v3_v3_short(&varray[start + 12], ln[0][1]);
+
+                                               start += 16;
+                                               ln ++;
                                        }
                                }
-                               glEnd();
                        }
                        else if (shademodel == GL_SMOOTH) {
-                               for (y = 0; y < gridFaces; y += step) {
-                                       glBegin(GL_QUAD_STRIP);
-                                       for (x = 0; x < gridSize; x += step) {
-                                               CCGElem *a = CCG_grid_elem(&key, faceGridData, x, y + 0);
-                                               CCGElem *b = CCG_grid_elem(&key, faceGridData, x, y + step);
-
-                                               glNormal3fv(CCG_elem_no(&key, a));
-                                               glVertex3fv(CCG_elem_co(&key, a));
-                                               glNormal3fv(CCG_elem_no(&key, b));
-                                               glVertex3fv(CCG_elem_co(&key, b));
+                               for (y = 0; y < gridFaces; y ++) {
+                                       for (x = 0; x < gridFaces; x ++) {
+                                               float *a = CCG_grid_elem_no(&key, faceGridData, x, y );
+                                               float *b = CCG_grid_elem_no(&key, faceGridData, x + 1, y);
+                                               float *c = CCG_grid_elem_no(&key, faceGridData, x + 1, y + 1);
+                                               float *d = CCG_grid_elem_no(&key, faceGridData, x, y + 1);
+
+                                               normal_float_to_short_v3(&varray[start], a);
+                                               normal_float_to_short_v3(&varray[start + 4], b);
+                                               normal_float_to_short_v3(&varray[start + 8], c);
+                                               normal_float_to_short_v3(&varray[start + 12], d);
+
+                                               start += 16;
                                        }
-                                       glEnd();
                                }
                        }
                        else {
-                               glBegin(GL_QUADS);
-                               for (y = 0; y < gridFaces; y += step) {
-                                       for (x = 0; x < gridFaces; x += step) {
-                                               float *a = CCG_grid_elem_co(&key, faceGridData, x, y + 0);
-                                               float *b = CCG_grid_elem_co(&key, faceGridData, x + step, y + 0);
-                                               float *c = CCG_grid_elem_co(&key, faceGridData, x + step, y + step);
-                                               float *d = CCG_grid_elem_co(&key, faceGridData, x, y + step);
-
-                                               ccgDM_glNormalFast(a, b, c, d);
-
-                                               glVertex3fv(d);
-                                               glVertex3fv(c);
-                                               glVertex3fv(b);
-                                               glVertex3fv(a);
+                               for (y = 0; y < gridFaces; y ++) {
+                                       for (x = 0; x < gridFaces; x ++) {
+                                               float no[3];
+                                               float *a = CCG_grid_elem_co(&key, faceGridData, x, y );
+                                               float *b = CCG_grid_elem_co(&key, faceGridData, x + 1, y );
+                                               float *c = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1);
+                                               float *d = CCG_grid_elem_co(&key, faceGridData, x, y + 1);
+
+                                               ccgDM_NormalFast(a, b, c, d, no);
+       
+                                               normal_float_to_short_v3(&varray[start], no);
+                                               normal_float_to_short_v3(&varray[start + 4], no);
+                                               normal_float_to_short_v3(&varray[start + 8], no);
+                                               normal_float_to_short_v3(&varray[start + 12], no);
+
+                                               start += 16;
                                        }
                                }
-                               glEnd();
                        }
                }
        }
 }
 
+/* Only used by non-editmesh types */
+static void ccgDM_buffer_copy_triangles(DerivedMesh *dm, float *varray_,
+                                        int *mat_orig_to_new, void *UNUSED(user_data))
+{
+       GPUBufferMaterial *gpumat;
+       CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+       CCGSubSurf *ss = ccgdm->ss;
+       CCGKey key;
+       unsigned int *varray = (unsigned int *)varray_;
+       int gridSize = ccgSubSurf_getGridSize(ss);
+       int gridFaces = gridSize - 1;
+       DMFlagMat *faceFlags = ccgdm->faceFlags;
+       int i, totface = ccgSubSurf_getNumFaces(ss);
+       int matnr = -1, start;
+       int totloops = 0;
+
+       CCG_key_top_level(&key, ss);
+
+       for (i = 0; i < totface; i++) {
+               CCGFace *f = ccgdm->faceMap[i].face;
+               int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
+               int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+
+               if (faceFlags) {
+                       matnr = faceFlags[index].mat_nr;
+               }
+               else {
+                       matnr = 0;
+               }
+
+               for (S = 0; S < numVerts; S++) {
+                       for (y = 0; y < gridFaces; y++) {
+                               for (x = 0; x < gridFaces; x++) {
+                                       gpumat = dm->drawObject->materials + mat_orig_to_new[matnr];
+                                       start = gpumat->counter;
+
+                                       varray[start] = totloops + 3;
+                                       varray[start + 1] = totloops + 2;
+                                       varray[start + 2] = totloops + 1;
+
+                                       varray[start + 3] = totloops + 3;
+                                       varray[start + 4] = totloops + 1;
+                                       varray[start + 5] = totloops;
+
+                                       gpumat->counter += 6;
+                                       totloops += 4;
+                               }
+                       }
+               }
+       }
+}
+
+
+/* Only used by non-editmesh types */
+static void ccgDM_buffer_copy_vertex(DerivedMesh *dm, float *varray,
+                                      int *UNUSED(mat_orig_to_new), void *UNUSED(user_data))
+{
+       CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+       CCGSubSurf *ss = ccgdm->ss;
+       CCGKey key;
+       int gridSize = ccgSubSurf_getGridSize(ss);
+       int gridFaces = gridSize - 1;
+       int i, totface = ccgSubSurf_getNumFaces(ss);
+       int start = 0;
+       
+       CCG_key_top_level(&key, ss);
+       ccgdm_pbvh_update(ccgdm);
+       
+       for (i = 0; i < totface; i++) {
+               CCGFace *f = ccgdm->faceMap[i].face;
+               int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
+               
+               for (S = 0; S < numVerts; S++) {
+                       CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S);
+                       for (y = 0; y < gridFaces; y++) {
+                               for (x = 0; x < gridFaces; x++) {
+                                       float *a = CCG_grid_elem_co(&key, faceGridData, x, y);
+                                       float *b = CCG_grid_elem_co(&key, faceGridData, x + 1, y);
+                                       float *c = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1);
+                                       float *d = CCG_grid_elem_co(&key, faceGridData, x, y + 1);
+
+                                       copy_v3_v3(&varray[start], a);
+                                       copy_v3_v3(&varray[start + 3], b);
+                                       copy_v3_v3(&varray[start + 6], c);
+                                       copy_v3_v3(&varray[start + 9], d);
+
+                                       start += 12;
+                               }
+                       }
+               }
+       }
+}
+
+static void copy_mcol_uc3(unsigned char *v, unsigned char *col)
+{
+       v[0] = col[3];
+       v[1] = col[2];
+       v[2] = col[1];
+}
+
+/* Only used by non-editmesh types */
+static void ccgDM_buffer_copy_color(DerivedMesh *dm, float *varray_,
+                                      int *UNUSED(mat_orig_to_new), void *user_data)
+{
+       CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+       CCGSubSurf *ss = ccgdm->ss;
+       CCGKey key;
+       unsigned char *varray = (unsigned char *)varray_;
+       unsigned char *mcol = (unsigned char *)user_data;
+       int gridSize = ccgSubSurf_getGridSize(ss);
+       int gridFaces = gridSize - 1;
+       int i, totface = ccgSubSurf_getNumFaces(ss);
+       int start = 0;
+       int iface = 0;
+
+       CCG_key_top_level(&key, ss);
+
+
+       for (i = 0; i < totface; i++) {
+               CCGFace *f = ccgdm->faceMap[i].face;
+               int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
+
+               for (S = 0; S < numVerts; S++) {
+                       for (y = 0; y < gridFaces; y++) {
+                               for (x = 0; x < gridFaces; x++) {
+                                       copy_mcol_uc3(&varray[start], &mcol[iface * 16]);
+                                       copy_mcol_uc3(&varray[start + 3], &mcol[iface * 16 + 12]);
+                                       copy_mcol_uc3(&varray[start + 6], &mcol[iface * 16 + 8]);
+                                       copy_mcol_uc3(&varray[start + 9], &mcol[iface * 16 + 4]);
+
+                                       start += 12;
+                                       iface++;
+                               }
+                       }
+               }
+       }
+}
+
+static void ccgDM_buffer_copy_uv(DerivedMesh *dm, float *varray,
+                                      int *UNUSED(mat_orig_to_new), void *UNUSED(user_data))
+{
+       CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+       CCGSubSurf *ss = ccgdm->ss;
+       CCGKey key;
+       MTFace *tf = DM_get_tessface_data_layer(dm, CD_MTFACE);
+       int gridSize = ccgSubSurf_getGridSize(ss);
+       int gridFaces = gridSize - 1;
+       int i, totface = ccgSubSurf_getNumFaces(ss);
+       int start = 0;
+
+       CCG_key_top_level(&key, ss);
+
+       for (i = 0; i < totface; i++) {
+               CCGFace *f = ccgdm->faceMap[i].face;
+               int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
+
+               for (S = 0; S < numVerts; S++) {
+                       for (y = 0; y < gridFaces; y++) {
+                               for (x = 0; x < gridFaces; x++) {
+                                       copy_v2_v2(&varray[start], tf->uv[0]);
+                                       copy_v2_v2(&varray[start + 2], tf->uv[3]);
+                                       copy_v2_v2(&varray[start + 4], tf->uv[2]);
+                                       copy_v2_v2(&varray[start + 6], tf->uv[1]);
+
+                                       tf++;
+                                       start += 8;
+                               }
+                       }
+               }
+       }
+}
+
+static void ccgDM_buffer_copy_uv_texpaint(DerivedMesh *dm, float *varray, int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+       CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+       CCGSubSurf *ss = ccgdm->ss;
+       CCGKey key;
+       int gridSize = ccgSubSurf_getGridSize(ss);
+       int gridFaces = gridSize - 1;
+       int i, totface = ccgSubSurf_getNumFaces(ss);
+       int start = 0;
+       DMFlagMat *faceFlags = ccgdm->faceFlags;
+       int totmaterial = dm->totmat;
+       MTFace **mtface_base;
+       MTFace *stencil_base;
+       int stencil;
+
+       CCG_key_top_level(&key, ss);
+
+       /* should have been checked for before, reassert */
+       BLI_assert(DM_get_tessface_data_layer(dm, CD_MTFACE));
+       mtface_base = MEM_mallocN(totmaterial * sizeof(*mtface_base), "texslots");
+
+       for (i = 0; i < totmaterial; i++) {
+               mtface_base[i] = DM_paint_uvlayer_active_get(dm, i);
+       }
+
+       stencil = CustomData_get_stencil_layer(&dm->faceData, CD_MTFACE);
+       stencil_base = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, stencil);
+
+       start = 0;
+
+       for (i = 0; i < totface; i++) {
+               CCGFace *f = ccgdm->faceMap[i].face;
+               int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
+               int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+               int matnr;
+
+               if (faceFlags) {
+                       matnr = faceFlags[index].mat_nr;
+               }
+               else {
+                       matnr = 0;
+               }
+
+               for (S = 0; S < numVerts; S++) {
+                       for (y = 0; y < gridFaces; y++) {
+                               for (x = 0; x < gridFaces; x++) {
+                                       /* divide by 16, gives us current tface */
+                                       unsigned int i_tf = start / 16;
+                                       copy_v2_v2(&varray[start], mtface_base[matnr][i_tf].uv[0]);
+                                       copy_v2_v2(&varray[start + 2], stencil_base[i_tf].uv[0]);
+                                       copy_v2_v2(&varray[start + 4], mtface_base[matnr][i_tf].uv[3]);
+                                       copy_v2_v2(&varray[start + 6], stencil_base[i_tf].uv[3]);
+                                       copy_v2_v2(&varray[start + 8], mtface_base[matnr][i_tf].uv[2]);
+                                       copy_v2_v2(&varray[start + 10], stencil_base[i_tf].uv[2]);
+                                       copy_v2_v2(&varray[start + 12], mtface_base[matnr][i_tf].uv[1]);
+                                       copy_v2_v2(&varray[start + 14], stencil_base[i_tf].uv[1]);
+                                       start += 16;
+                               }
+                       }
+               }
+       }
+
+       MEM_freeN(mtface_base);
+}
+
+static void ccgDM_copy_gpu_data(DerivedMesh *dm, int type, float *varray,
+                         int *mat_orig_to_new, void *user_data)
+{      
+       switch(type) {
+               case GPU_BUFFER_VERTEX:
+                       ccgDM_buffer_copy_vertex(dm, varray, mat_orig_to_new, NULL);
+                       break;
+               case GPU_BUFFER_NORMAL:
+                       ccgDM_buffer_copy_normal(dm, varray, mat_orig_to_new, NULL);
+                       break;
+               case GPU_BUFFER_UV:
+                       ccgDM_buffer_copy_uv(dm, varray, mat_orig_to_new, NULL);
+                       break;
+               case GPU_BUFFER_UV_TEXPAINT:
+                       ccgDM_buffer_copy_uv_texpaint(dm, varray, mat_orig_to_new, NULL);
+                       break;
+               case GPU_BUFFER_COLOR:
+                       ccgDM_buffer_copy_color(dm, varray, mat_orig_to_new, user_data);
+                       break;
+               case GPU_BUFFER_TRIANGLES:
+                       ccgDM_buffer_copy_triangles(dm, varray, mat_orig_to_new, NULL);
+                       break;
+               default:
+                       break;
+       }
+}
+
+typedef struct {
+       int elements;
+       int loops;
+       int polys;
+} GPUMaterialInfo;
+
+static GPUDrawObject *ccgDM_GPUObjectNew(DerivedMesh *dm) {
+       GPUBufferMaterial *mat;
+       int *mat_orig_to_new;
+       CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+       CCGSubSurf *ss = ccgdm->ss;
+       GPUDrawObject *gdo;
+       DMFlagMat *faceFlags = ccgdm->faceFlags;
+       int gridSize = ccgSubSurf_getGridSize(ss);
+       int gridFaces = gridSize - 1;
+       int totmat = (faceFlags) ? dm->totmat : 1;
+       GPUMaterialInfo *matinfo;
+       int i, curmat, curelement, totface;
+
+       /* object contains at least one material (default included) so zero means uninitialized dm */
+       BLI_assert(totmat != 0);
+
+       totface = ccgSubSurf_getNumFaces(ss);
+
+       matinfo = MEM_callocN(sizeof(*matinfo) * totmat, "GPU_drawobject_new.mat_orig_to_new");
+       
+       if (faceFlags) {
+               for (i = 0; i < totface; i++) {
+                       CCGFace *f = ccgdm->faceMap[i].face;
+                       int numVerts = ccgSubSurf_getFaceNumVerts(f);
+                       int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+                       int new_matnr = faceFlags[index].mat_nr;
+                       matinfo[new_matnr].elements += numVerts * gridFaces * gridFaces * 6;
+                       matinfo[new_matnr].loops += numVerts * gridFaces * gridFaces * 4;
+                       matinfo[new_matnr].polys++;
+               }
+       }
+       else {
+               for (i = 0; i < totface; i++) {
+                       matinfo[0].elements += gridFaces * gridFaces * 6;
+                       matinfo[0].loops += gridFaces * gridFaces * 4;
+                       matinfo[0].polys++;
+               }
+       }
+       
+       /* create the GPUDrawObject */
+       gdo = MEM_callocN(sizeof(GPUDrawObject), "GPUDrawObject");
+       gdo->totvert = ccgSubSurf_getNumFinalFaces(ss) * 6;
+       gdo->totedge = ccgSubSurf_getNumFinalEdges(ss) * 2;
+
+       /* count the number of materials used by this DerivedMesh */
+       for (i = 0; i < totmat; i++) {
+               if (matinfo[i].elements > 0)
+                       gdo->totmaterial++;
+       }
+
+       /* allocate an array of materials used by this DerivedMesh */
+       gdo->materials = MEM_mallocN(sizeof(GPUBufferMaterial) * gdo->totmaterial,
+                                    "GPUDrawObject.materials");
+
+       /* initialize the materials array */
+       for (i = 0, curmat = 0, curelement = 0; i < totmat; i++) {
+               if (matinfo[i].elements > 0) {
+                       gdo->materials[curmat].start = curelement;
+                       gdo->materials[curmat].totelements = matinfo[i].elements;
+                       gdo->materials[curmat].totloops = matinfo[i].loops;
+                       gdo->materials[curmat].mat_nr = i;
+                       gdo->materials[curmat].totpolys = matinfo[i].polys;
+                       gdo->materials[curmat].polys = MEM_mallocN(sizeof(int) * matinfo[0].polys, "GPUBufferMaterial.polys");
+
+                       curelement += matinfo[i].elements;
+                       curmat++;
+               }
+       }
+
+       /* store total number of points used for triangles */
+       gdo->tot_triangle_point = curelement;
+
+       mat_orig_to_new = MEM_callocN(sizeof(*mat_orig_to_new) * totmat,
+                                                    "GPUDrawObject.mat_orig_to_new");
+
+       /* build a map from the original material indices to the new
+        * GPUBufferMaterial indices */
+       for (i = 0; i < gdo->totmaterial; i++) {
+               mat_orig_to_new[gdo->materials[i].mat_nr] = i;
+               gdo->materials[i].counter = 0;
+       }
+
+       if (faceFlags) {
+               for (i = 0; i < totface; i++) {
+                       CCGFace *f = ccgdm->faceMap[i].face;
+                       int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+                       int new_matnr = faceFlags[index].mat_nr;
+
+                       mat = &gdo->materials[mat_orig_to_new[new_matnr]];
+                       mat->polys[mat->counter++] = i;
+               }
+       }
+       else {
+               mat = &gdo->materials[0];
+               for (i = 0; i < totface; i++)
+                       mat->polys[mat->counter++] = i;
+       }
+
+
+       MEM_freeN(mat_orig_to_new);
+       MEM_freeN(matinfo);
+
+       return gdo;
+}
+
+/* Only used by non-editmesh types */
+static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)[4], bool fast, DMSetMaterial setMaterial)
+{
+       int a;
+       CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+
+       if (ccgdm->pbvh && ccgdm->multires.mmd) {
+               if (BKE_pbvh_has_faces(ccgdm->pbvh)) {
+                       BKE_pbvh_draw(ccgdm->pbvh, partial_redraw_planes, NULL,
+                                     setMaterial, false, fast);
+                       glShadeModel(GL_FLAT);
+               }
+
+               return;
+       }
+       
+       GPU_vertex_setup(dm);
+       GPU_normal_setup(dm);
+       GPU_triangle_setup(dm);
+       glShadeModel(GL_SMOOTH);
+       for (a = 0; a < dm->drawObject->totmaterial; a++) {
+               if (!setMaterial || setMaterial(dm->drawObject->materials[a].mat_nr + 1, NULL)) {
+                       GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, dm->drawObject->materials[a].start,
+                                                dm->drawObject->materials[a].totelements);
+               }
+       }
+       GPU_buffer_unbind();
+}
+
 /* Only used by non-editmesh types */
 static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm,
                                       DMSetMaterial setMaterial,
@@ -2228,18 +2614,15 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
        int colType = CD_TEXTURE_MCOL;
        MCol *mcol = dm->getTessFaceDataArray(dm, colType);
        MTFace *tf = DM_get_tessface_data_layer(dm, CD_MTFACE);
-       MTFace *tf_stencil_base = NULL;
-       MTFace *tf_stencil = NULL;
-       MTFace *tf_base;
-       short (*lnors)[4][3] = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL);
        DMFlagMat *faceFlags = ccgdm->faceFlags;
        DMDrawOption draw_option;
-       int i, totface, gridSize = ccgSubSurf_getGridSize(ss);
-       int gridFaces = gridSize - 1;
-       int gridOffset = 0;
-       int mat_nr_cache = -1;
-
-       (void) compareDrawOptions;
+       int i, totpoly;
+       bool flush;
+       bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0;
+       unsigned int next_actualFace;
+       unsigned int gridFaces = ccgSubSurf_getGridSize(ss) - 1;
+       int mat_index;
+       int tot_element, start_element;
 
        CCG_key_top_level(&key, ss);
        ccgdm_pbvh_update(ccgdm);
@@ -2253,210 +2636,96 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
                mcol = dm->getTessFaceDataArray(dm, colType);
        }
 
-       totface = ccgSubSurf_getNumFaces(ss);
-
-       if (flag & DM_DRAW_USE_TEXPAINT_UV) {
-               int stencil = CustomData_get_stencil_layer(&dm->faceData, CD_MTFACE);
-               tf_stencil_base = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, stencil);
+       GPU_vertex_setup(dm);
+       GPU_normal_setup(dm);
+       GPU_triangle_setup(dm);
+       if (flag & DM_DRAW_USE_TEXPAINT_UV)
+               GPU_texpaint_uv_setup(dm);
+       else
+               GPU_uv_setup(dm);
+       if (mcol) {
+               GPU_color_setup(dm, colType);
        }
 
-       for (i = 0; i < totface; i++) {
-               CCGFace *f = ccgdm->faceMap[i].face;
-               int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
-               int drawSmooth, index = ccgDM_getFaceMapIndex(ss, f);
-               int origIndex = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
-               unsigned char *cp = NULL;
-               short (*ln)[4][3] = NULL;
-               int mat_nr;
+       next_actualFace = 0;
 
-               if (faceFlags) {
-                       drawSmooth = (lnors || (faceFlags[origIndex].flag & ME_SMOOTH));
-                       mat_nr = faceFlags[origIndex].mat_nr;
-               }
-               else {
-                       drawSmooth = 1;
-                       mat_nr = 0;
-               }
+       glShadeModel(GL_SMOOTH);
+       /* lastFlag = 0; */ /* UNUSED */
+       for (mat_index = 0; mat_index < dm->drawObject->totmaterial; mat_index++) {
+               GPUBufferMaterial *bufmat = dm->drawObject->materials + mat_index;
+               next_actualFace = bufmat->polys[0];
+               totpoly = bufmat->totpolys;
 
-               /* texture painting, handle the correct uv layer here */
-               if (flag & DM_DRAW_USE_TEXPAINT_UV) {
-                       if (mat_nr != mat_nr_cache) {
-                               tf_base = DM_paint_uvlayer_active_get(dm, mat_nr);
+               tot_element = 0;
+               start_element = bufmat->start;
 
-                               mat_nr_cache = mat_nr;
+               for (i = 0; i < totpoly; i++) {
+                       int polyindex = bufmat->polys[i];
+                       CCGFace *f = ccgdm->faceMap[polyindex].face;
+                       int numVerts = ccgSubSurf_getFaceNumVerts(f);
+                       int index = ccgDM_getFaceMapIndex(ss, f);
+                       int origIndex = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+                       int mat_nr;
+                       int facequads = numVerts * gridFaces * gridFaces;
+                       int actualFace = ccgdm->faceMap[polyindex].startFace;
+
+                       if (i != totpoly - 1) {
+                               polyindex = bufmat->polys[i + 1];
+                               next_actualFace = ccgdm->faceMap[polyindex].startFace;
                        }
 
-                       tf = tf_base ? tf_base + gridOffset : NULL;
-                       tf_stencil = tf_stencil_base ? tf_stencil_base + gridOffset : NULL;
-                       gridOffset += gridFaces * gridFaces * numVerts;
-               }
-
-               if (drawParams) {
-                       MTexPoly tpoly;
-                       if (tf) {
-                               ME_MTEXFACE_CPY(&tpoly, tf);
+                       if (faceFlags) {
+                               mat_nr = faceFlags[origIndex].mat_nr;
+                       }
+                       else {
+                               mat_nr = 0;
                        }
-                       draw_option = drawParams(tf ? &tpoly : NULL, (mcol != NULL), mat_nr);
-               }
-               else if (index != ORIGINDEX_NONE)
-                       draw_option = (drawParamsMapped) ? drawParamsMapped(userData, index, mat_nr) : DM_DRAW_OPTION_NORMAL;
-               else
-                       draw_option = GPU_enable_material(mat_nr, NULL) ? DM_DRAW_OPTION_NORMAL : DM_DRAW_OPTION_SKIP;
-
-               if (lnors) {
-                       ln = lnors;
-                       lnors += gridFaces * gridFaces * numVerts;
-               }
-
-               if (draw_option == DM_DRAW_OPTION_SKIP) {
-                       if (tf) tf += gridFaces * gridFaces * numVerts;
-                       if (mcol) mcol += gridFaces * gridFaces * numVerts * 4;
-                       continue;
-               }
-
-               /* flag 1 == use vertex colors */
-               if (mcol) {
-                       if (draw_option != DM_DRAW_OPTION_NO_MCOL)
-                               cp = (unsigned char *)mcol;
-                       mcol += gridFaces * gridFaces * numVerts * 4;
-               }
-
-               for (S = 0; S < numVerts; S++) {
-                       CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S);
-                       CCGElem *a, *b;
-
-                       if (ln) {
-                               glShadeModel(GL_SMOOTH);
-                               glBegin(GL_QUADS);
-                               for (y = 0; y < gridFaces; y++) {
-                                       for (x = 0; x < gridFaces; x++) {
-                                               float *a_co = CCG_grid_elem_co(&key, faceGridData, x, y + 0);
-                                               float *b_co = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 0);
-                                               float *c_co = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1);
-                                               float *d_co = CCG_grid_elem_co(&key, faceGridData, x, y + 1);
-
-                                               if (tf) glTexCoord2fv(tf->uv[1]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[1]);
-                                               if (cp) glColor3ub(cp[7], cp[6], cp[5]);
-                                               glNormal3sv(ln[0][1]);
-                                               glVertex3fv(d_co);
-
-                                               if (tf) glTexCoord2fv(tf->uv[2]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[2]);
-                                               if (cp) glColor3ub(cp[11], cp[10], cp[9]);
-                                               glNormal3sv(ln[0][2]);
-                                               glVertex3fv(c_co);
-
-                                               if (tf) glTexCoord2fv(tf->uv[3]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[3]);
-                                               if (cp) glColor3ub(cp[15], cp[14], cp[13]);
-                                               glNormal3sv(ln[0][3]);
-                                               glVertex3fv(b_co);
-
-                                               if (tf) glTexCoord2fv(tf->uv[0]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[0]);
-                                               if (cp) glColor3ub(cp[3], cp[2], cp[1]);
-                                               glNormal3sv(ln[0][0]);
-                                               glVertex3fv(a_co);
 
-                                               if (tf) tf++;
-                                               if (tf_stencil) tf_stencil++;
-                                               if (cp) cp += 16;
-                                               ln++;
-                                       }
+                       if (drawParams) {
+                               MTexPoly tpoly;
+                               if (tf) {
+                                       ME_MTEXFACE_CPY(&tpoly, tf + actualFace);
                                }
-                               glEnd();
+
+                               draw_option = drawParams((use_tface && tf) ? &tpoly : NULL, (mcol != NULL), mat_nr);
                        }
-                       else if (drawSmooth) {
-                               glShadeModel(GL_SMOOTH);
-                               for (y = 0; y < gridFaces; y++) {
-                                       glBegin(GL_QUAD_STRIP);
-                                       for (x = 0; x < gridFaces; x++) {
-                                               a = CCG_grid_elem(&key, faceGridData, x, y + 0);
-                                               b = CCG_grid_elem(&key, faceGridData, x, y + 1);
-
-                                               if (tf) glTexCoord2fv(tf->uv[0]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[0]);
-                                               if (cp) glColor3ub(cp[3], cp[2], cp[1]);
-                                               glNormal3fv(CCG_elem_no(&key, a));
-                                               glVertex3fv(CCG_elem_co(&key, a));
-
-                                               if (tf) glTexCoord2fv(tf->uv[1]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[1]);
-                                               if (cp) glColor3ub(cp[7], cp[6], cp[5]);
-                                               glNormal3fv(CCG_elem_no(&key, b));
-                                               glVertex3fv(CCG_elem_co(&key, b));
-                                               
-                                               if (x != gridFaces - 1) {
-                                                       if (tf) tf++;
-                                                       if (tf_stencil) tf_stencil++;
-                                                       if (cp) cp += 16;
-                                               }
-                                       }
+                       else if (index != ORIGINDEX_NONE)
+                               draw_option = (drawParamsMapped) ? drawParamsMapped(userData, index, mat_nr) : DM_DRAW_OPTION_NORMAL;
+                       else
+                               draw_option = DM_DRAW_OPTION_NORMAL;
 
-                                       a = CCG_grid_elem(&key, faceGridData, x, y + 0);
-                                       b = CCG_grid_elem(&key, faceGridData, x, y + 1);
+                       /* flush buffer if current triangle isn't drawable or it's last triangle */
+                       flush = (draw_option == DM_DRAW_OPTION_SKIP) || (i == totpoly - 1);
 
-                                       if (tf) glTexCoord2fv(tf->uv[3]);
-                                       if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[3]);
-                                       if (cp) glColor3ub(cp[15], cp[14], cp[13]);
-                                       glNormal3fv(CCG_elem_no(&key, a));
-                                       glVertex3fv(CCG_elem_co(&key, a));
+                       if (!flush && compareDrawOptions) {
+                               /* also compare draw options and flush buffer if they're different
+                                        * need for face selection highlight in edit mode */
+                               flush |= compareDrawOptions(userData, actualFace, next_actualFace) == 0;
+                       }
 
-                                       if (tf) glTexCoord2fv(tf->uv[2]);
-                                       if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[2]);
-                                       if (cp) glColor3ub(cp[11], cp[10], cp[9]);
-                                       glNormal3fv(CCG_elem_no(&key, b));
-                                       glVertex3fv(CCG_elem_co(&key, b));
+                       if (flush) {
+                               if (draw_option != DM_DRAW_OPTION_SKIP)
+                                       tot_element += facequads * 6;
 
-                                       if (tf) tf++;
-                                       if (tf_stencil) tf_stencil++;
-                                       if (cp) cp += 16;
+                               if (tot_element) {
+                                       if (mcol && draw_option != DM_DRAW_OPTION_NO_MCOL)
+                                               GPU_color_switch(1);
+                                       else
+                                               GPU_color_switch(0);
 
-                                       glEnd();
+                                       GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, start_element, tot_element);
                                }
+
+                               start_element = tot_element;
                        }
                        else {
-                               glShadeModel((cp) ? GL_SMOOTH : GL_FLAT);
-                               glBegin(GL_QUADS);
-                               for (y = 0; y < gridFaces; y++) {
-                                       for (x = 0; x < gridFaces; x++) {
-                                               float *a_co = CCG_grid_elem_co(&key, faceGridData, x, y + 0);
-                                               float *b_co = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 0);
-                                               float *c_co = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1);
-                                               float *d_co = CCG_grid_elem_co(&key, faceGridData, x, y + 1);
-
-                                               ccgDM_glNormalFast(a_co, b_co, c_co, d_co);
-
-                                               if (tf) glTexCoord2fv(tf->uv[1]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[1]);
-                                               if (cp) glColor3ub(cp[7], cp[6], cp[5]);
-                                               glVertex3fv(d_co);
-
-                                               if (tf) glTexCoord2fv(tf->uv[2]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[2]);
-                                               if (cp) glColor3ub(cp[11], cp[10], cp[9]);
-                                               glVertex3fv(c_co);
-
-                                               if (tf) glTexCoord2fv(tf->uv[3]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[3]);
-                                               if (cp) glColor3ub(cp[15], cp[14], cp[13]);
-                                               glVertex3fv(b_co);
-
-                                               if (tf) glTexCoord2fv(tf->uv[0]);
-                                               if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE2, tf_stencil->uv[0]);
-                                               if (cp) glColor3ub(cp[3], cp[2], cp[1]);
-                                               glVertex3fv(a_co);
-
-                                               if (tf) tf++;
-                                               if (tf_stencil) tf_stencil++;
-                                               if (cp) cp += 16;
-                                       }
-                               }
-                               glEnd();
+                               tot_element += facequads * 6;
                        }
                }
        }
+
+
+       GPU_buffer_unbind();
 }
 
 static void ccgDM_drawFacesTex(DerivedMesh *dm,
@@ -3440,7 +3709,9 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
 
        ccgdm->dm.drawMappedEdgesInterp = ccgDM_drawMappedEdgesInterp;
        ccgdm->dm.drawMappedEdges = ccgDM_drawMappedEdges;
-       
+       ccgdm->dm.gpuObjectNew = ccgDM_GPUObjectNew;
+       ccgdm->dm.copy_gpu_data = ccgDM_copy_gpu_data;
+
        ccgdm->dm.release = ccgDM_release;
        
        ccgdm->ss = ss;
index 350ecf22e1bd374b93e5c52bc2f572601bb02bd2..07b9c60d101ea281e2b99a81a8cac31e9f361361 100644 (file)
@@ -48,6 +48,10 @@ struct GSet;
 struct GPUVertPointLink;
 struct PBVH;
 
+typedef void (*GPUBufferCopyFunc)(DerivedMesh *dm, float *varray, int *index,
+                                  int *mat_orig_to_new, void *user_data);
+
+
 typedef struct GPUBuffer {
        int size;        /* in bytes */
        void *pointer;   /* used with vertex arrays */
@@ -58,7 +62,11 @@ typedef struct GPUBuffer {
 typedef struct GPUBufferMaterial {
        /* range of points used for this material */
        int start;
-       int totpoint;
+       int totelements;
+       int totloops;
+       int *polys; /* array of polygons for this material */
+       int totpolys; /* total polygons in polys */
+       int counter; /* general purpose counter, initialize first! */
 
        /* original material index */
        short mat_nr;
@@ -88,9 +96,7 @@ typedef struct GPUDrawObject {
        GPUBuffer *colors;
        GPUBuffer *edges;
        GPUBuffer *uvedges;
-
-       /* for each triangle, the original MFace index */
-       int *triangle_to_mface;
+       GPUBuffer *triangles; /* triangle index buffer */
 
        /* for each original vertex, the list of related points */
        struct GPUVertPointLink *vert_points;
@@ -103,12 +109,15 @@ typedef struct GPUDrawObject {
 #endif
        
        int colType;
+       int index_setup; /* how indices are setup, starting from start of buffer or start of material */
 
        GPUBufferMaterial *materials;
        int totmaterial;
        
        int tot_triangle_point;
        int tot_loose_point;
+       /* different than total loops since ngons get tesselated still */
+       int tot_loop_verts;
        
        /* caches of the original DerivedMesh values */
        int totvert;
@@ -119,6 +128,19 @@ typedef struct GPUDrawObject {
        int tot_edge_drawn;
 } GPUDrawObject;
 
+/* currently unused */
+// #define USE_GPU_POINT_LINK
+
+typedef struct GPUVertPointLink {
+#ifdef USE_GPU_POINT_LINK
+       struct GPUVertPointLink *next;
+#endif
+       /* -1 means uninitialized */
+       int point_index;
+} GPUVertPointLink;
+
+
+
 /* used for GLSL materials */
 typedef struct GPUAttrib {
        int index;
@@ -129,12 +151,24 @@ typedef struct GPUAttrib {
 void GPU_global_buffer_pool_free(void);
 void GPU_global_buffer_pool_free_unused(void);
 
-GPUBuffer *GPU_buffer_alloc(int size, bool force_vertex_arrays);
+GPUBuffer *GPU_buffer_alloc(size_t size, bool force_vertex_arrays);
 void GPU_buffer_free(GPUBuffer *buffer);
 
-GPUDrawObject *GPU_drawobject_new(struct DerivedMesh *dm);
 void GPU_drawobject_free(struct DerivedMesh *dm);
 
+/* flag that controls data type to fill buffer with, a modifier will prepare. */
+typedef enum {
+       GPU_BUFFER_VERTEX = 0,
+       GPU_BUFFER_NORMAL,
+       GPU_BUFFER_COLOR,
+       GPU_BUFFER_UV,
+       GPU_BUFFER_UV_TEXPAINT,
+       GPU_BUFFER_EDGE,
+       GPU_BUFFER_UVEDGE,
+       GPU_BUFFER_TRIANGLES
+} GPUBufferType;
+
+
 /* called before drawing */
 void GPU_vertex_setup(struct DerivedMesh *dm);
 void GPU_normal_setup(struct DerivedMesh *dm);
@@ -144,8 +178,11 @@ void GPU_texpaint_uv_setup(struct DerivedMesh *dm);
 void GPU_color_setup(struct DerivedMesh *dm, int colType);
 void GPU_edge_setup(struct DerivedMesh *dm); /* does not mix with other data */
 void GPU_uvedge_setup(struct DerivedMesh *dm);
+
+void GPU_triangle_setup(struct DerivedMesh *dm);
+
 int GPU_attrib_element_size(GPUAttrib data[], int numdata);
-void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numdata);
+void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numdata, int element_size);
 
 /* can't lock more than one buffer at once */
 void *GPU_buffer_lock(GPUBuffer *buffer);
@@ -161,6 +198,9 @@ void GPU_buffer_draw_elements(GPUBuffer *elements, unsigned int mode, int start,
 /* called after drawing */
 void GPU_buffer_unbind(void);
 
+/* only unbind interleaved data */
+void GPU_interleaved_attrib_unbind(void);
+
 /* Buffers for non-DerivedMesh drawing */
 typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers;
 
@@ -196,7 +236,7 @@ void GPU_update_grid_pbvh_buffers(GPU_PBVH_Buffers *buffers, struct CCGElem **gr
 
 /* draw */
 void GPU_draw_pbvh_buffers(GPU_PBVH_Buffers *buffers, DMSetMaterial setMaterial,
-                           bool wireframe);
+                           bool wireframe, bool fast);
 
 /* debug PBVH draw*/
 void GPU_draw_pbvh_BB(float min[3], float max[3], bool leaf);
index bcbae9958d12c03ab2ecdded5f4fa9b2427a6e4b..ac6329910c98f7450b1148eea654948dc373d35d 100644 (file)
@@ -69,6 +69,35 @@ typedef enum {
        GPU_BUFFER_ELEMENT_STATE = (1 << 5),
 } GPUBufferState;
 
+typedef struct {
+       GLenum gl_buffer_type;
+       int num_components; /* number of data components for one vertex */
+} GPUBufferTypeSettings;
+
+
+static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type);
+
+const GPUBufferTypeSettings gpu_buffer_type_settings[] = {
+    /* vertex */
+    {GL_ARRAY_BUFFER_ARB, 3},
+    /* normal */
+    {GL_ARRAY_BUFFER_ARB, 4}, /* we copy 3 shorts per normal but we add a fourth for alignment */
+    /* mcol */
+    {GL_ARRAY_BUFFER_ARB, 3},
+    /* uv */
+    {GL_ARRAY_BUFFER_ARB, 2},
+    /* uv for texpaint */
+    {GL_ARRAY_BUFFER_ARB, 4},
+    /* edge */
+    {GL_ELEMENT_ARRAY_BUFFER_ARB, 2},
+    /* uv edge */
+    {GL_ELEMENT_ARRAY_BUFFER_ARB, 4},
+    /* triangles, 1 point since we are allocating from tottriangle points, which account for all points */
+    {GL_ELEMENT_ARRAY_BUFFER_ARB, 1},
+    /* fast triangles */
+    {GL_ELEMENT_ARRAY_BUFFER_ARB, 1},
+};
+
 #define MAX_GPU_ATTRIB_DATA 32
 
 #define BUFFER_OFFSET(n) ((GLubyte *)NULL + (n))
@@ -221,11 +250,12 @@ void GPU_global_buffer_pool_free_unused(void)
  *
  * Thread-unsafe version for internal usage only.
  */
-static GPUBuffer *gpu_buffer_alloc_intern(int size, bool use_VBO)
+static GPUBuffer *gpu_buffer_alloc_intern(size_t size, bool use_VBO)
 {
        GPUBufferPool *pool;
        GPUBuffer *buf;
-       int i, bufsize, bestfit = -1;
+       int i, bestfit = -1;
+       size_t bufsize;
 
        /* bad case, leads to leak of buf since buf->pointer will allocate
         * NULL, leading to return without cleanup. In any case better detect early
@@ -290,8 +320,11 @@ static GPUBuffer *gpu_buffer_alloc_intern(int size, bool use_VBO)
                glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
        }
        else {
+               static int time = 0;
+
                buf->pointer = MEM_mallocN(size, "GPUBuffer.pointer");
-               
+
+               time++;
                /* purpose of this seems to be dealing with
                 * out-of-memory errors? looks a bit iffy to me
                 * though, at least on Linux I expect malloc() would
@@ -308,7 +341,7 @@ static GPUBuffer *gpu_buffer_alloc_intern(int size, bool use_VBO)
 }
 
 /* Same as above, but safe for threading. */
-GPUBuffer *GPU_buffer_alloc(int size, bool force_vertex_arrays)
+GPUBuffer *GPU_buffer_alloc(size_t size, bool force_vertex_arrays)
 {
        GPUBuffer *buffer;
        bool use_VBOs = (GLEW_ARB_vertex_buffer_object) && !(U.gameflags & USER_DISABLE_VBO) && !force_vertex_arrays;
@@ -383,197 +416,22 @@ void GPU_buffer_free(GPUBuffer *buffer)
        BLI_mutex_unlock(&buffer_mutex);
 }
 
-#if 0 /* currently unused */
-#  define USE_GPU_POINT_LINK
-#endif
-
-typedef struct GPUVertPointLink {
-#ifdef USE_GPU_POINT_LINK
-       struct GPUVertPointLink *next;
-#endif
-       /* -1 means uninitialized */
-       int point_index;
-} GPUVertPointLink;
-
-
-/* add a new point to the list of points related to a particular
- * vertex */
-#ifdef USE_GPU_POINT_LINK
-
-static void gpu_drawobject_add_vert_point(GPUDrawObject *gdo, int vert_index, int point_index)
-{
-       GPUVertPointLink *lnk;
-
-       lnk = &gdo->vert_points[vert_index];
-
-       /* if first link is in use, add a new link at the end */
-       if (lnk->point_index != -1) {
-               /* get last link */
-               for (; lnk->next; lnk = lnk->next) ;
-
-               /* add a new link from the pool */
-               lnk = lnk->next = &gdo->vert_points_mem[gdo->vert_points_usage];
-               gdo->vert_points_usage++;
-       }
-
-       lnk->point_index = point_index;
-}
-
-#else
-
-static void gpu_drawobject_add_vert_point(GPUDrawObject *gdo, int vert_index, int point_index)
-{
-       GPUVertPointLink *lnk;
-       lnk = &gdo->vert_points[vert_index];
-       if (lnk->point_index == -1) {
-               lnk->point_index = point_index;
-       }
-}
-
-#endif  /* USE_GPU_POINT_LINK */
-
-/* update the vert_points and triangle_to_mface fields with a new
- * triangle */
-static void gpu_drawobject_add_triangle(GPUDrawObject *gdo,
-                                        int base_point_index,
-                                        int face_index,
-                                        int v1, int v2, int v3)
-{
-       int i, v[3] = {v1, v2, v3};
-       for (i = 0; i < 3; i++)
-               gpu_drawobject_add_vert_point(gdo, v[i], base_point_index + i);
-       gdo->triangle_to_mface[base_point_index / 3] = face_index;
-}
-
-/* for each vertex, build a list of points related to it; these lists
- * are stored in an array sized to the number of vertices */
-static void gpu_drawobject_init_vert_points(GPUDrawObject *gdo, const MFace *f, int totface, int totmat)
-{
-       GPUBufferMaterial *mat;
-       int i, *mat_orig_to_new;
-
-       mat_orig_to_new = MEM_callocN(sizeof(*mat_orig_to_new) * totmat,
-                                     "GPUDrawObject.mat_orig_to_new");
-       /* allocate the array and space for links */
-       gdo->vert_points = MEM_mallocN(sizeof(GPUVertPointLink) * gdo->totvert,
-                                      "GPUDrawObject.vert_points");
-#ifdef USE_GPU_POINT_LINK
-       gdo->vert_points_mem = MEM_callocN(sizeof(GPUVertPointLink) * gdo->tot_triangle_point,
-                                          "GPUDrawObject.vert_points_mem");
-       gdo->vert_points_usage = 0;
-#endif
-
-       /* build a map from the original material indices to the new
-        * GPUBufferMaterial indices */
-       for (i = 0; i < gdo->totmaterial; i++)
-               mat_orig_to_new[gdo->materials[i].mat_nr] = i;
-
-       /* -1 indicates the link is not yet used */
-       for (i = 0; i < gdo->totvert; i++) {
-#ifdef USE_GPU_POINT_LINK
-               gdo->vert_points[i].link = NULL;
-#endif
-               gdo->vert_points[i].point_index = -1;
-       }
-
-       for (i = 0; i < totface; i++, f++) {
-               mat = &gdo->materials[mat_orig_to_new[f->mat_nr]];
-
-               /* add triangle */
-               gpu_drawobject_add_triangle(gdo, mat->start + mat->totpoint,
-                                           i, f->v1, f->v2, f->v3);
-               mat->totpoint += 3;
-
-               /* add second triangle for quads */
-               if (f->v4) {
-                       gpu_drawobject_add_triangle(gdo, mat->start + mat->totpoint,
-                                                   i, f->v3, f->v4, f->v1);
-                       mat->totpoint += 3;
-               }
-       }
-
-       /* map any unused vertices to loose points */
-       for (i = 0; i < gdo->totvert; i++) {
-               if (gdo->vert_points[i].point_index == -1) {
-                       gdo->vert_points[i].point_index = gdo->tot_triangle_point + gdo->tot_loose_point;
-                       gdo->tot_loose_point++;
-               }
-       }
-
-       MEM_freeN(mat_orig_to_new);
-}
-
-/* see GPUDrawObject's structure definition for a description of the
- * data being initialized here */
-GPUDrawObject *GPU_drawobject_new(DerivedMesh *dm)
-{
-       GPUDrawObject *gdo;
-       const MFace *mface;
-       int totmat = dm->totmat;
-       int *points_per_mat;
-       int i, curmat, curpoint, totface;
-
-       /* object contains at least one material (default included) so zero means uninitialized dm */
-       BLI_assert(totmat != 0);
-
-       mface = dm->getTessFaceArray(dm);
-       totface = dm->getNumTessFaces(dm);
-
-       /* get the number of points used by each material, treating
-        * each quad as two triangles */
-       points_per_mat = MEM_callocN(sizeof(*points_per_mat) * totmat, "GPU_drawobject_new.mat_orig_to_new");
-       for (i = 0; i < totface; i++)
-               points_per_mat[mface[i].mat_nr] += mface[i].v4 ? 6 : 3;
-
-       /* create the GPUDrawObject */
-       gdo = MEM_callocN(sizeof(GPUDrawObject), "GPUDrawObject");
-       gdo->totvert = dm->getNumVerts(dm);
-       gdo->totedge = dm->getNumEdges(dm);
-
-       /* count the number of materials used by this DerivedMesh */
-       for (i = 0; i < totmat; i++) {
-               if (points_per_mat[i] > 0)
-                       gdo->totmaterial++;
-       }
-
-       /* allocate an array of materials used by this DerivedMesh */
-       gdo->materials = MEM_mallocN(sizeof(GPUBufferMaterial) * gdo->totmaterial,
-                                    "GPUDrawObject.materials");
-
-       /* initialize the materials array */
-       for (i = 0, curmat = 0, curpoint = 0; i < totmat; i++) {
-               if (points_per_mat[i] > 0) {
-                       gdo->materials[curmat].start = curpoint;
-                       gdo->materials[curmat].totpoint = 0;
-                       gdo->materials[curmat].mat_nr = i;
-
-                       curpoint += points_per_mat[i];
-                       curmat++;
-               }
-       }
-
-       /* store total number of points used for triangles */
-       gdo->tot_triangle_point = curpoint;
-
-       gdo->triangle_to_mface = MEM_mallocN(sizeof(int) * (gdo->tot_triangle_point / 3),
-                                            "GPUDrawObject.triangle_to_mface");
-
-       gpu_drawobject_init_vert_points(gdo, mface, totface, totmat);
-       MEM_freeN(points_per_mat);
-
-       return gdo;
-}
-
 void GPU_drawobject_free(DerivedMesh *dm)
 {
        GPUDrawObject *gdo;
+       int i;
 
        if (!dm || !(gdo = dm->drawObject))
                return;
 
+       for (i = 0; i < gdo->totmaterial; i++) {
+               if (gdo->materials[i].polys)
+                       MEM_freeN(gdo->materials[i].polys);
+       }
+
        MEM_freeN(gdo->materials);
-       MEM_freeN(gdo->triangle_to_mface);
-       MEM_freeN(gdo->vert_points);
+       if (gdo->vert_points)
+               MEM_freeN(gdo->vert_points);
 #ifdef USE_GPU_POINT_LINK
        MEM_freeN(gdo->vert_points_mem);
 #endif
@@ -584,6 +442,7 @@ void GPU_drawobject_free(DerivedMesh *dm)
        GPU_buffer_free(gdo->colors);
        GPU_buffer_free(gdo->edges);
        GPU_buffer_free(gdo->uvedges);
+       GPU_buffer_free(gdo->triangles);
 
        MEM_freeN(gdo);
        dm->drawObject = NULL;
@@ -605,20 +464,18 @@ static GPUBuffer *gpu_try_realloc(GPUBufferPool *pool, GPUBuffer *buffer, int si
        return buffer;
 }
 
-typedef void (*GPUBufferCopyFunc)(
-        DerivedMesh *dm, float *varray, int *index,
-        const int *mat_orig_to_new, const void *user_data);
-
 static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
-                                   int vector_size, int size, GLenum target,
-                                   void *user, GPUBufferCopyFunc copy_f)
+                                   int type, void *user)
 {
        GPUBufferPool *pool;
        GPUBuffer *buffer;
        float *varray;
        int *mat_orig_to_new;
-       int *cur_index_per_mat;
        int i;
+       const GPUBufferTypeSettings *ts = &gpu_buffer_type_settings[type];
+       GLenum target = ts->gl_buffer_type;
+       int num_components = ts->num_components;
+       int size = gpu_buffer_size_from_type(dm, type);
        bool use_VBOs = (GLEW_ARB_vertex_buffer_object) && !(U.gameflags & USER_DISABLE_VBO);
        GLboolean uploaded;
 
@@ -634,11 +491,9 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
 
        mat_orig_to_new = MEM_mallocN(sizeof(*mat_orig_to_new) * dm->totmat,
                                      "GPU_buffer_setup.mat_orig_to_new");
-       cur_index_per_mat = MEM_mallocN(sizeof(int) * object->totmaterial,
-                                       "GPU_buffer_setup.cur_index_per_mat");
        for (i = 0; i < object->totmaterial; i++) {
                /* for each material, the current index to copy data to */
-               cur_index_per_mat[i] = object->materials[i].start * vector_size;
+               object->materials[i].counter = object->materials[i].start * num_components;
 
                /* map from original material index to new
                 * GPUBufferMaterial index */
@@ -675,7 +530,7 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
                        uploaded = GL_FALSE;
                        /* attempt to upload the data to the VBO */
                        while (uploaded == GL_FALSE) {
-                               (*copy_f)(dm, varray, cur_index_per_mat, mat_orig_to_new, user);
+                               dm->copy_gpu_data(dm, type, varray, mat_orig_to_new, user);
                                /* glUnmapBuffer returns GL_FALSE if
                                 * the data store is corrupted; retry
                                 * in that case */
@@ -692,11 +547,10 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
                
                if (buffer) {
                        varray = buffer->pointer;
-                       (*copy_f)(dm, varray, cur_index_per_mat, mat_orig_to_new, user);
+                       dm->copy_gpu_data(dm, type, varray, mat_orig_to_new, user);
                }
        }
 
-       MEM_freeN(cur_index_per_mat);
        MEM_freeN(mat_orig_to_new);
 
        BLI_mutex_unlock(&buffer_mutex);
@@ -704,375 +558,6 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
        return buffer;
 }
 
-static void GPU_buffer_copy_vertex(
-        DerivedMesh *dm, float *varray,
-        int *index, const int *mat_orig_to_new, const void *UNUSED(user))
-{
-       const MVert *mvert;
-       const MFace *f;
-       int i, j, start, totface;
-
-       mvert = dm->getVertArray(dm);
-       f = dm->getTessFaceArray(dm);
-
-       totface = dm->getNumTessFaces(dm);
-       for (i = 0; i < totface; i++, f++) {
-               start = index[mat_orig_to_new[f->mat_nr]];
-
-               /* v1 v2 v3 */
-               copy_v3_v3(&varray[start], mvert[f->v1].co);
-               copy_v3_v3(&varray[start + 3], mvert[f->v2].co);
-               copy_v3_v3(&varray[start + 6], mvert[f->v3].co);
-               index[mat_orig_to_new[f->mat_nr]] += 9;
-
-               if (f->v4) {
-                       /* v3 v4 v1 */
-                       copy_v3_v3(&varray[start + 9], mvert[f->v3].co);
-                       copy_v3_v3(&varray[start + 12], mvert[f->v4].co);
-                       copy_v3_v3(&varray[start + 15], mvert[f->v1].co);
-                       index[mat_orig_to_new[f->mat_nr]] += 9;
-               }
-       }
-
-       /* copy loose points */
-       j = dm->drawObject->tot_triangle_point * 3;
-       for (i = 0; i < dm->drawObject->totvert; i++) {
-               if (dm->drawObject->vert_points[i].point_index >= dm->drawObject->tot_triangle_point) {
-                       copy_v3_v3(&varray[j], mvert[i].co);
-                       j += 3;
-               }
-       }
-}
-
-static void GPU_buffer_copy_normal(
-        DerivedMesh *dm, float *varray, int *index,
-        const int *mat_orig_to_new, const void *UNUSED(user))
-{
-       int i, totface;
-       int start;
-       float f_no[3];
-
-       const float *nors = dm->getTessFaceDataArray(dm, CD_NORMAL);
-       short (*tlnors)[4][3] = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL);
-       const MVert *mvert = dm->getVertArray(dm);
-       const MFace *f = dm->getTessFaceArray(dm);
-
-       totface = dm->getNumTessFaces(dm);
-       for (i = 0; i < totface; i++, f++) {
-               const int smoothnormal = (f->flag & ME_SMOOTH);
-
-               start = index[mat_orig_to_new[f->mat_nr]];
-               index[mat_orig_to_new[f->mat_nr]] += f->v4 ? 18 : 9;
-
-               if (tlnors) {
-                       short (*tlnor)[3] = tlnors[i];
-                       /* Copy loop normals */
-                       normal_short_to_float_v3(&varray[start], tlnor[0]);
-                       normal_short_to_float_v3(&varray[start + 3], tlnor[1]);
-                       normal_short_to_float_v3(&varray[start + 6], tlnor[2]);
-
-                       if (f->v4) {
-                               normal_short_to_float_v3(&varray[start + 9], tlnor[2]);
-                               normal_short_to_float_v3(&varray[start + 12], tlnor[3]);
-                               normal_short_to_float_v3(&varray[start + 15], tlnor[0]);
-                       }
-               }
-               else if (smoothnormal) {
-                       /* copy vertex normal */
-                       normal_short_to_float_v3(&varray[start], mvert[f->v1].no);
-                       normal_short_to_float_v3(&varray[start + 3], mvert[f->v2].no);
-                       normal_short_to_float_v3(&varray[start + 6], mvert[f->v3].no);
-
-                       if (f->v4) {
-                               normal_short_to_float_v3(&varray[start + 9], mvert[f->v3].no);
-                               normal_short_to_float_v3(&varray[start + 12], mvert[f->v4].no);
-                               normal_short_to_float_v3(&varray[start + 15], mvert[f->v1].no);
-                       }
-               }
-               else if (nors) {
-                       /* copy cached face normal */
-                       copy_v3_v3(&varray[start], &nors[i * 3]);
-                       copy_v3_v3(&varray[start + 3], &nors[i * 3]);
-                       copy_v3_v3(&varray[start + 6], &nors[i * 3]);
-
-                       if (f->v4) {
-                               copy_v3_v3(&varray[start + 9], &nors[i * 3]);
-                               copy_v3_v3(&varray[start + 12], &nors[i * 3]);
-                               copy_v3_v3(&varray[start + 15], &nors[i * 3]);
-                       }
-               }
-               else {
-                       /* calculate face normal */
-                       if (f->v4)
-                               normal_quad_v3(f_no, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co, mvert[f->v4].co);
-                       else
-                               normal_tri_v3(f_no, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co);
-
-                       copy_v3_v3(&varray[start], f_no);
-                       copy_v3_v3(&varray[start + 3], f_no);
-                       copy_v3_v3(&varray[start + 6], f_no);
-
-                       if (f->v4) {
-                               copy_v3_v3(&varray[start + 9], f_no);
-                               copy_v3_v3(&varray[start + 12], f_no);
-                               copy_v3_v3(&varray[start + 15], f_no);
-                       }
-               }
-       }
-}
-
-static void GPU_buffer_copy_uv(
-        DerivedMesh *dm, float *varray, int *index,
-        const int *mat_orig_to_new, const void *UNUSED(user))
-{
-       int start;
-       int i, totface;
-
-       const MTFace *mtface;
-       const MFace *f;
-
-       if (!(mtface = DM_get_tessface_data_layer(dm, CD_MTFACE)))
-               return;
-       f = dm->getTessFaceArray(dm);
-               
-       totface = dm->getNumTessFaces(dm);
-       for (i = 0; i < totface; i++, f++) {
-               start = index[mat_orig_to_new[f->mat_nr]];
-
-               /* v1 v2 v3 */
-               copy_v2_v2(&varray[start], mtface[i].uv[0]);
-               copy_v2_v2(&varray[start + 2], mtface[i].uv[1]);
-               copy_v2_v2(&varray[start + 4], mtface[i].uv[2]);
-               index[mat_orig_to_new[f->mat_nr]] += 6;
-
-               if (f->v4) {
-                       /* v3 v4 v1 */
-                       copy_v2_v2(&varray[start + 6], mtface[i].uv[2]);
-                       copy_v2_v2(&varray[start + 8], mtface[i].uv[3]);
-                       copy_v2_v2(&varray[start + 10], mtface[i].uv[0]);
-                       index[mat_orig_to_new[f->mat_nr]] += 6;
-               }
-       }
-}
-
-
-static void GPU_buffer_copy_uv_texpaint(
-        DerivedMesh *dm, float *varray, int *index,
-        const int *mat_orig_to_new, const void *UNUSED(user))
-{
-       int start;
-       int i, totface;
-
-       int totmaterial = dm->totmat;
-       const MTFace **mtface_base;
-       const MTFace *stencil_base;
-       int stencil;
-       const MFace *mf;
-
-       /* should have been checked for before, reassert */
-       BLI_assert(DM_get_tessface_data_layer(dm, CD_MTFACE));
-       mf = dm->getTessFaceArray(dm);
-       mtface_base = MEM_mallocN(totmaterial * sizeof(*mtface_base), "texslots");
-
-       for (i = 0; i < totmaterial; i++) {
-               mtface_base[i] = DM_paint_uvlayer_active_get(dm, i);
-       }
-
-       stencil = CustomData_get_stencil_layer(&dm->faceData, CD_MTFACE);
-       stencil_base = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, stencil);
-
-       totface = dm->getNumTessFaces(dm);
-
-       for (i = 0; i < totface; i++, mf++) {
-               int mat_i = mf->mat_nr;
-               start = index[mat_orig_to_new[mat_i]];
-
-               /* v1 v2 v3 */
-               copy_v2_v2(&varray[start], mtface_base[mat_i][i].uv[0]);
-               copy_v2_v2(&varray[start + 2], stencil_base[i].uv[0]);
-               copy_v2_v2(&varray[start + 4], mtface_base[mat_i][i].uv[1]);
-               copy_v2_v2(&varray[start + 6], stencil_base[i].uv[1]);
-               copy_v2_v2(&varray[start + 8], mtface_base[mat_i][i].uv[2]);
-               copy_v2_v2(&varray[start + 10], stencil_base[i].uv[2]);
-               index[mat_orig_to_new[mat_i]] += 12;
-
-               if (mf->v4) {
-                       /* v3 v4 v1 */
-                       copy_v2_v2(&varray[start + 12], mtface_base[mat_i][i].uv[2]);
-                       copy_v2_v2(&varray[start + 14], stencil_base[i].uv[2]);
-                       copy_v2_v2(&varray[start + 16], mtface_base[mat_i][i].uv[3]);
-                       copy_v2_v2(&varray[start + 18], stencil_base[i].uv[3]);
-                       copy_v2_v2(&varray[start + 20], mtface_base[mat_i][i].uv[0]);
-                       copy_v2_v2(&varray[start + 22], stencil_base[i].uv[0]);
-                       index[mat_orig_to_new[mat_i]] += 12;
-               }
-       }
-
-       MEM_freeN(mtface_base);
-}
-
-
-static void copy_mcol_uc3(unsigned char *v, unsigned char *col)
-{
-       v[0] = col[3];
-       v[1] = col[2];
-       v[2] = col[1];
-}
-
-/* treat varray_ as an array of MCol, four MCol's per face */
-static void GPU_buffer_copy_mcol(
-        DerivedMesh *dm, float *varray_, int *index,
-        const int *mat_orig_to_new, const void *user)
-{
-       int i, totface;
-       unsigned char *varray = (unsigned char *)varray_;
-       unsigned char *mcol = (unsigned char *)user;
-       const MFace *f = dm->getTessFaceArray(dm);
-
-       totface = dm->getNumTessFaces(dm);
-       for (i = 0; i < totface; i++, f++) {
-               int start = index[mat_orig_to_new[f->mat_nr]];
-
-               /* v1 v2 v3 */
-               copy_mcol_uc3(&varray[start], &mcol[i * 16]);
-               copy_mcol_uc3(&varray[start + 3], &mcol[i * 16 + 4]);
-               copy_mcol_uc3(&varray[start + 6], &mcol[i * 16 + 8]);
-               index[mat_orig_to_new[f->mat_nr]] += 9;
-
-               if (f->v4) {
-                       /* v3 v4 v1 */
-                       copy_mcol_uc3(&varray[start + 9], &mcol[i * 16 + 8]);
-                       copy_mcol_uc3(&varray[start + 12], &mcol[i * 16 + 12]);
-                       copy_mcol_uc3(&varray[start + 15], &mcol[i * 16]);
-                       index[mat_orig_to_new[f->mat_nr]] += 9;
-               }
-       }
-}
-
-static void GPU_buffer_copy_edge(
-        DerivedMesh *dm, float *varray_, int *UNUSED(index),
-        const int *UNUSED(mat_orig_to_new), const void *UNUSED(user))
-{
-       const MEdge *medge, *medge_base;
-       unsigned int *varray = (unsigned int *)varray_;
-       int i, totedge, iloose, inorm, iloosehidden, inormhidden;
-       int tot_loose_hidden = 0, tot_loose = 0;
-       int tot_hidden = 0, tot = 0;
-
-       medge_base = medge = dm->getEdgeArray(dm);
-       totedge = dm->getNumEdges(dm);
-
-       for (i = 0; i < totedge; i++, medge++) {
-               if (medge->flag & ME_EDGEDRAW) {
-                       if (medge->flag & ME_LOOSEEDGE) tot_loose++;
-                       else tot++;
-               }
-               else {
-                       if (medge->flag & ME_LOOSEEDGE) tot_loose_hidden++;
-                       else tot_hidden++;
-               }
-       }
-
-       inorm = 0;
-       inormhidden = tot;
-       iloose = tot + tot_hidden;
-       iloosehidden = iloose + tot_loose;
-
-       medge = medge_base;
-       for (i = 0; i < totedge; i++, medge++) {
-               if (medge->flag & ME_EDGEDRAW) {
-                       if (medge->flag & ME_LOOSEEDGE) {
-                               varray[iloose * 2] = dm->drawObject->vert_points[medge->v1].point_index;
-                               varray[iloose * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
-                               iloose++;
-                       }
-                       else {
-                               varray[inorm * 2] = dm->drawObject->vert_points[medge->v1].point_index;
-                               varray[inorm * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
-                               inorm++;
-                       }
-               }
-               else {
-                       if (medge->flag & ME_LOOSEEDGE) {
-                               varray[iloosehidden * 2] = dm->drawObject->vert_points[medge->v1].point_index;
-                               varray[iloosehidden * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
-                               iloosehidden++;
-                       }
-                       else {
-                               varray[inormhidden * 2] = dm->drawObject->vert_points[medge->v1].point_index;
-                               varray[inormhidden * 2 + 1] = dm->drawObject->vert_points[medge->v2].point_index;
-                               inormhidden++;
-                       }
-               }
-       }
-
-       dm->drawObject->tot_loose_edge_drawn = tot_loose;
-       dm->drawObject->loose_edge_offset = tot + tot_hidden;
-       dm->drawObject->tot_edge_drawn = tot;
-}
-
-static void GPU_buffer_copy_uvedge(
-        DerivedMesh *dm, float *varray, int *UNUSED(index),
-        const int *UNUSED(mat_orig_to_new), const void *UNUSED(user))
-{
-       const MTFace *tf = DM_get_tessface_data_layer(dm, CD_MTFACE);
-       int i, j = 0;
-
-       if (!tf)
-               return;
-
-       for (i = 0; i < dm->numTessFaceData; i++, tf++) {
-               MFace mf;
-               dm->getTessFace(dm, i, &mf);
-
-               copy_v2_v2(&varray[j], tf->uv[0]);
-               copy_v2_v2(&varray[j + 2], tf->uv[1]);
-
-               copy_v2_v2(&varray[j + 4], tf->uv[1]);
-               copy_v2_v2(&varray[j + 6], tf->uv[2]);
-
-               if (!mf.v4) {
-                       copy_v2_v2(&varray[j + 8], tf->uv[2]);
-                       copy_v2_v2(&varray[j + 10], tf->uv[0]);
-                       j += 12;
-               }
-               else {
-                       copy_v2_v2(&varray[j + 8], tf->uv[2]);
-                       copy_v2_v2(&varray[j + 10], tf->uv[3]);
-
-                       copy_v2_v2(&varray[j + 12], tf->uv[3]);
-                       copy_v2_v2(&varray[j + 14], tf->uv[0]);
-                       j += 16;
-               }
-       }
-}
-
-typedef enum {
-       GPU_BUFFER_VERTEX = 0,
-       GPU_BUFFER_NORMAL,
-       GPU_BUFFER_COLOR,
-       GPU_BUFFER_UV,
-       GPU_BUFFER_UV_TEXPAINT,
-       GPU_BUFFER_EDGE,
-       GPU_BUFFER_UVEDGE,
-} GPUBufferType;
-
-typedef struct {
-       GPUBufferCopyFunc copy;
-       GLenum gl_buffer_type;
-       int vector_size;
-} GPUBufferTypeSettings;
-
-const GPUBufferTypeSettings gpu_buffer_type_settings[] = {
-       {GPU_buffer_copy_vertex, GL_ARRAY_BUFFER_ARB, 3},
-       {GPU_buffer_copy_normal, GL_ARRAY_BUFFER_ARB, 3},
-       {GPU_buffer_copy_mcol, GL_ARRAY_BUFFER_ARB, 3},
-       {GPU_buffer_copy_uv, GL_ARRAY_BUFFER_ARB, 2},
-       {GPU_buffer_copy_uv_texpaint, GL_ARRAY_BUFFER_ARB, 4},
-       {GPU_buffer_copy_edge, GL_ELEMENT_ARRAY_BUFFER_ARB, 2},
-       {GPU_buffer_copy_uvedge, GL_ELEMENT_ARRAY_BUFFER_ARB, 4}
-};
-
 /* get the GPUDrawObject buffer associated with a type */
 static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBufferType type)
 {
@@ -1091,6 +576,8 @@ static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBuffer
                        return &gdo->edges;
                case GPU_BUFFER_UVEDGE:
                        return &gdo->uvedges;
+               case GPU_BUFFER_TRIANGLES:
+                       return &gdo->triangles;
                default:
                        return NULL;
        }
@@ -1101,24 +588,26 @@ static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type)
 {
        switch (type) {
                case GPU_BUFFER_VERTEX:
-                       return sizeof(float) * 3 * (dm->drawObject->tot_triangle_point + dm->drawObject->tot_loose_point);
+                       return sizeof(float) * gpu_buffer_type_settings[type].num_components * (dm->drawObject->tot_triangle_point + dm->drawObject->tot_loose_point);
                case GPU_BUFFER_NORMAL:
-                       return sizeof(float) * 3 * dm->drawObject->tot_triangle_point;
+                       return sizeof(short) * gpu_buffer_type_settings[type].num_components * dm->drawObject->tot_triangle_point;
                case GPU_BUFFER_COLOR:
-                       return sizeof(char) * 3 * dm->drawObject->tot_triangle_point;
+                       return sizeof(char) * gpu_buffer_type_settings[type].num_components * dm->drawObject->tot_triangle_point;
                case GPU_BUFFER_UV:
-                       return sizeof(float) * 2 * dm->drawObject->tot_triangle_point;
+                       return sizeof(float) * gpu_buffer_type_settings[type].num_components * dm->drawObject->tot_triangle_point;
                case GPU_BUFFER_UV_TEXPAINT:
-                       return sizeof(float) * 4 * dm->drawObject->tot_triangle_point;
+                       return sizeof(float) * gpu_buffer_type_settings[type].num_components * dm->drawObject->tot_triangle_point;
                case GPU_BUFFER_EDGE:
-                       return sizeof(int) * 2 * dm->drawObject->totedge;
+                       return sizeof(int) * gpu_buffer_type_settings[type].num_components * dm->drawObject->totedge;
                case GPU_BUFFER_UVEDGE:
                        /* each face gets 3 points, 3 edges per triangle, and
                         * each edge has its own, non-shared coords, so each
                         * tri corner needs minimum of 4 floats, quads used
                         * less so here we can over allocate and assume all
                         * tris. */
-                       return sizeof(float) * 4 * dm->drawObject->tot_triangle_point;
+                       return sizeof(int) * gpu_buffer_type_settings[type].num_components * dm->drawObject->tot_triangle_point;
+               case GPU_BUFFER_TRIANGLES:
+                       return sizeof(int) * gpu_buffer_type_settings[type].num_components * dm->drawObject->tot_triangle_point;
                default:
                        return -1;
        }
@@ -1127,12 +616,9 @@ static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type)
 /* call gpu_buffer_setup with settings for a particular type of buffer */
 static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type)
 {
-       const GPUBufferTypeSettings *ts;
        void *user_data = NULL;
        GPUBuffer *buf;
 
-       ts = &gpu_buffer_type_settings[type];
-
        /* special handling for MCol and UV buffers */
        if (type == GPU_BUFFER_COLOR) {
                if (!(user_data = DM_get_tessface_data_layer(dm, dm->drawObject->colType)))
@@ -1143,9 +629,7 @@ static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type)
                        return NULL;
        }
 
-       buf = gpu_buffer_setup(dm, dm->drawObject, ts->vector_size,
-                              gpu_buffer_size_from_type(dm, type),
-                              ts->gl_buffer_type, user_data, ts->copy);
+       buf = gpu_buffer_setup(dm, dm->drawObject, type, user_data);
 
        return buf;
 }
@@ -1157,7 +641,7 @@ static GPUBuffer *gpu_buffer_setup_common(DerivedMesh *dm, GPUBufferType type)
        GPUBuffer **buf;
 
        if (!dm->drawObject)
-               dm->drawObject = GPU_drawobject_new(dm);
+               dm->drawObject = dm->gpuObjectNew(dm);
 
        buf = gpu_drawobject_buffer_from_type(dm->drawObject, type);
        if (!(*buf))
@@ -1191,10 +675,10 @@ void GPU_normal_setup(DerivedMesh *dm)
        glEnableClientState(GL_NORMAL_ARRAY);
        if (dm->drawObject->normals->use_vbo) {
                glBindBufferARB(GL_ARRAY_BUFFER_ARB, dm->drawObject->normals->id);
-               glNormalPointer(GL_FLOAT, 0, 0);
+               glNormalPointer(GL_SHORT, 4 * sizeof(short), 0);
        }
        else {
-               glNormalPointer(GL_FLOAT, 0, dm->drawObject->normals->pointer);
+               glNormalPointer(GL_SHORT, 4 * sizeof(short), dm->drawObject->normals->pointer);
        }
 
        GLStates |= GPU_BUFFER_NORMAL_STATE;
@@ -1248,7 +732,7 @@ void GPU_color_setup(DerivedMesh *dm, int colType)
        if (!dm->drawObject) {
                /* XXX Not really nice, but we need a valid gpu draw object to set the colType...
                 *     Else we would have to add a new param to gpu_buffer_setup_common. */
-               dm->drawObject = GPU_drawobject_new(dm);
+               dm->drawObject = dm->gpuObjectNew(dm);
                dm->dirty &= ~DM_DIRTY_MCOL_UPDATE_DRAW;
                dm->drawObject->colType = colType;
        }
@@ -1322,6 +806,18 @@ void GPU_uvedge_setup(DerivedMesh *dm)
        GLStates |= GPU_BUFFER_VERTEX_STATE;
 }
 
+void GPU_triangle_setup(struct DerivedMesh *dm)
+{
+       if (!gpu_buffer_setup_common(dm, GPU_BUFFER_TRIANGLES))
+               return;
+
+       if (dm->drawObject->triangles->use_vbo) {
+               glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, dm->drawObject->triangles->id);
+       }
+
+       GLStates |= GPU_BUFFER_ELEMENT_STATE;
+}
+
 static int GPU_typesize(int type)
 {
        switch (type) {
@@ -1352,7 +848,7 @@ int GPU_attrib_element_size(GPUAttrib data[], int numdata)
        return elementsize;
 }
 
-void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numdata)
+void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numdata, int element_size)
 {
        int i;
        int elementsize;
@@ -1366,7 +862,10 @@ void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numda
                else
                        break;
        }
-       elementsize = GPU_attrib_element_size(data, numdata);
+       if (element_size == 0)
+               elementsize = GPU_attrib_element_size(data, numdata);
+       else
+               elementsize = element_size;
 
        if (buffer->use_vbo) {
                glBindBufferARB(GL_ARRAY_BUFFER_ARB, buffer->id);
@@ -1390,6 +889,18 @@ void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numda
        attribData[numdata].index = -1; 
 }
 
+void GPU_interleaved_attrib_unbind(void)
+{
+       int i;
+       for (i = 0; i < MAX_GPU_ATTRIB_DATA; i++) {
+               if (attribData[i].index != -1) {
+                       glDisableVertexAttribArrayARB(attribData[i].index);
+               }
+               else
+                       break;
+       }
+       attribData[0].index = -1;
+}
 
 void GPU_buffer_unbind(void)
 {
@@ -1492,6 +1003,7 @@ void GPU_buffer_unlock(GPUBuffer *buffer)
        }
 }
 
+
 /* used for drawing edges */
 void GPU_buffer_draw_elements(GPUBuffer *elements, unsigned int mode, int start, int count)
 {
@@ -1528,7 +1040,7 @@ typedef struct {
 
 struct GPU_PBVH_Buffers {
        /* opengl buffer handles */
-       GLuint vert_buf, index_buf;
+       GLuint vert_buf, index_buf, index_buf_fast;
        GLenum index_type;
 
        /* mesh pointers in case buffer allocation fails */
@@ -2084,6 +1596,32 @@ static GLuint gpu_get_grid_buffer(int gridsize, GLenum *index_type, unsigned *to
        return buffer;
 }
 
+#define FILL_FAST_BUFFER(type_) \
+{ \
+    type_ *buffer; \
+    glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, \
+                    sizeof(type_) * 6 * totgrid, NULL, \
+                    GL_STATIC_DRAW_ARB); \
+    buffer = glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); \
+    if (buffer) { \
+        int i; \
+        for (i = 0; i < totgrid; i++) { \
+            int currentquad = i * 6; \
+            buffer[currentquad] = i * gridsize * gridsize; \
+            buffer[currentquad + 1] = i * gridsize * gridsize + gridsize - 1; \
+            buffer[currentquad + 2] = (i + 1) * gridsize * gridsize - gridsize; \
+            buffer[currentquad + 3] = (i + 1) * gridsize * gridsize - 1; \
+            buffer[currentquad + 4] = i * gridsize * gridsize + gridsize - 1; \
+            buffer[currentquad + 5] = (i + 1) * gridsize * gridsize - gridsize; \
+        } \
+        glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB); \
+       } \
+       else { \
+        glDeleteBuffersARB(1, &buffers->index_buf_fast); \
+        buffers->index_buf_fast = 0; \
+    } \
+} (void)0
+
 GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers(int *grid_indices, int totgrid,
                                               BLI_bitmap **grid_hidden, int gridsize)
 {
@@ -2105,6 +1643,22 @@ GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers(int *grid_indices, int totgrid,
        if (totquad == 0)
                return buffers;
 
+       /* create and fill indices of the fast buffer too */
+       glGenBuffersARB(1, &buffers->index_buf_fast);
+
+       if (buffers->index_buf_fast) {
+               glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buffers->index_buf_fast);
+
+               if (totgrid * gridsize * gridsize < USHRT_MAX) {
+                       FILL_FAST_BUFFER(unsigned short);
+               }
+               else {
+                       FILL_FAST_BUFFER(unsigned int);
+               }
+
+               glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+       }
+
        if (totquad == fully_visible_totquad) {
                buffers->index_buf = gpu_get_grid_buffer(gridsize, &buffers->index_type, &buffers->tot_quad);
                buffers->has_hidden = 0;
@@ -2619,8 +2173,9 @@ static void gpu_draw_buffers_legacy_grids(GPU_PBVH_Buffers *buffers)
 }
 
 void GPU_draw_pbvh_buffers(GPU_PBVH_Buffers *buffers, DMSetMaterial setMaterial,
-                           bool wireframe)
+                           bool wireframe, bool fast)
 {
+       bool do_fast = fast && buffers->index_buf_fast;
        /* sets material from the first face, to solve properly face would need to
         * be sorted in buckets by materials */
        if (setMaterial) {
@@ -2651,7 +2206,9 @@ void GPU_draw_pbvh_buffers(GPU_PBVH_Buffers *buffers, DMSetMaterial setMaterial,
 
                glBindBufferARB(GL_ARRAY_BUFFER_ARB, buffers->vert_buf);
 
-               if (buffers->index_buf)
+               if (do_fast)
+                       glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buffers->index_buf_fast);
+               else if (buffers->index_buf)
                        glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buffers->index_buf);
 
                if (wireframe)
@@ -2668,7 +2225,10 @@ void GPU_draw_pbvh_buffers(GPU_PBVH_Buffers *buffers, DMSetMaterial setMaterial,
                                glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VertexBufferFormat),
                                               offset + offsetof(VertexBufferFormat, color));
                                
-                               glDrawElements(GL_TRIANGLES, buffers->tot_quad * 6, buffers->index_type, 0);
+                               if (do_fast)
+                                       glDrawElements(GL_TRIANGLES, buffers->totgrid * 6, buffers->index_type, 0);
+                               else
+                                       glDrawElements(GL_TRIANGLES, buffers->tot_quad * 6, buffers->index_type, 0);
 
                                offset += buffers->gridkey.grid_area * sizeof(VertexBufferFormat);
                        }
@@ -2797,6 +2357,8 @@ void GPU_free_pbvh_buffers(GPU_PBVH_Buffers *buffers)
                        gpu_pbvh_buffer_free_intern(buffers->vert_buf);
                if (buffers->index_buf && (buffers->tot_tri || buffers->has_hidden))
                        gpu_pbvh_buffer_free_intern(buffers->index_buf);
+               if (buffers->index_buf_fast)
+                       gpu_pbvh_buffer_free_intern(buffers->index_buf_fast);
 
                MEM_freeN(buffers);
        }