2017-01-25 09:16:29 +01:00
|
|
|
/*
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2017 by Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
|
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** \file blender/blenkernel/intern/mesh_render.c
|
|
|
|
* \ingroup bke
|
|
|
|
*
|
|
|
|
* \brief Mesh API for render engines
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
|
|
|
|
#include "BKE_customdata.h"
|
|
|
|
#include "BKE_depsgraph.h"
|
|
|
|
#include "BKE_DerivedMesh.h"
|
|
|
|
#include "BKE_editmesh.h"
|
|
|
|
#include "BKE_mesh.h"
|
|
|
|
#include "BKE_mesh_render.h"
|
|
|
|
|
|
|
|
#include "GPU_batch.h"
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Mesh Interface */
|
|
|
|
|
|
|
|
#define MESH_RENDER_FUNCTION(func_name) \
|
2017-02-07 11:20:15 +01:00
|
|
|
if (me->edit_btmesh && me->edit_btmesh->derivedFinal) { \
|
2017-01-25 09:16:29 +01:00
|
|
|
return mesh_bmesh_##func_name(me); \
|
|
|
|
} \
|
|
|
|
else { \
|
|
|
|
return mesh_struct_##func_name(me); \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Mesh Implementation */
|
|
|
|
|
|
|
|
static int mesh_struct_get_num_edges(Mesh *me)
|
|
|
|
{
|
|
|
|
return me->totedge;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_struct_get_num_verts(Mesh *me)
|
|
|
|
{
|
|
|
|
return me->totvert;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_struct_get_num_faces(Mesh *me)
|
|
|
|
{
|
|
|
|
BKE_mesh_tessface_ensure(me);
|
|
|
|
return me->totface;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_struct_get_num_polys(Mesh *me)
|
|
|
|
{
|
|
|
|
return me->totpoly;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MEdge *mesh_struct_get_array_edge(Mesh *me)
|
|
|
|
{
|
|
|
|
return CustomData_get_layer(&me->edata, CD_MEDGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MFace *mesh_struct_get_array_face(Mesh *me)
|
|
|
|
{
|
|
|
|
BKE_mesh_tessface_ensure(me);
|
|
|
|
return CustomData_get_layer(&me->fdata, CD_MFACE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MLoop *mesh_struct_get_array_loop(Mesh *me)
|
|
|
|
{
|
|
|
|
return me->mloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MPoly *mesh_struct_get_array_poly(Mesh *me)
|
|
|
|
{
|
|
|
|
return me->mpoly;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MVert *mesh_struct_get_array_vert(Mesh *me)
|
|
|
|
{
|
|
|
|
return CustomData_get_layer(&me->vdata, CD_MVERT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BMesh Implementation */
|
|
|
|
|
|
|
|
/* NOTE: we may want to get rid of Derived Mesh and
|
|
|
|
* access BMesh directly */
|
|
|
|
|
|
|
|
static int mesh_bmesh_get_num_verts(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getNumVerts(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_bmesh_get_num_edges(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getNumEdges(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_bmesh_get_num_faces(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getNumTessFaces(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_bmesh_get_num_polys(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getNumPolys(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MEdge *mesh_bmesh_get_array_edge(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getEdgeArray(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MFace *mesh_bmesh_get_array_face(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getTessFaceArray(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MLoop *mesh_bmesh_get_array_loop(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getLoopArray(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MPoly *mesh_bmesh_get_array_poly(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getPolyArray(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MVert *mesh_bmesh_get_array_vert(Mesh *me)
|
|
|
|
{
|
|
|
|
BMEditMesh *bm = me->edit_btmesh;
|
|
|
|
DerivedMesh *dm = bm->derivedFinal;
|
|
|
|
return dm->getVertArray(dm);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mesh API */
|
|
|
|
|
|
|
|
static int mesh_render_get_num_edges(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_num_edges);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_render_get_num_faces(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_num_faces);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_render_get_num_polys(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_num_polys);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mesh_render_get_num_verts(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_num_verts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MEdge *mesh_render_get_array_edge(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_array_edge);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MFace *mesh_render_get_array_face(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_array_face);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MLoop *mesh_render_get_array_loop(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_array_loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MPoly *mesh_render_get_array_poly(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_array_poly);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MVert *mesh_render_get_array_vert(Mesh *me)
|
|
|
|
{
|
|
|
|
MESH_RENDER_FUNCTION(get_array_vert);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Mesh Batch Cache */
|
|
|
|
|
|
|
|
typedef struct MeshBatchCache {
|
|
|
|
VertexBuffer *pos_in_order;
|
|
|
|
ElementList *edges_in_order;
|
|
|
|
ElementList *triangles_in_order;
|
|
|
|
|
|
|
|
Batch *all_verts;
|
|
|
|
Batch *all_edges;
|
|
|
|
Batch *all_triangles;
|
|
|
|
|
|
|
|
Batch *fancy_edges; /* owns its vertex buffer (not shared) */
|
|
|
|
Batch *overlay_edges; /* owns its vertex buffer */
|
|
|
|
|
2017-01-26 18:56:52 +01:00
|
|
|
/* settings to determine if cache is invalid */
|
|
|
|
bool is_dirty;
|
2017-01-25 09:16:29 +01:00
|
|
|
int tot_edges;
|
|
|
|
int tot_faces;
|
|
|
|
int tot_polys;
|
|
|
|
int tot_verts;
|
|
|
|
bool is_editmode;
|
|
|
|
} MeshBatchCache;
|
|
|
|
|
|
|
|
static bool mesh_batch_cache_valid(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = me->batch_cache;
|
|
|
|
|
|
|
|
if (cache == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cache->is_editmode != (me->edit_btmesh != NULL)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-26 18:56:52 +01:00
|
|
|
if (cache->is_dirty == false) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
2017-01-26 19:08:27 +01:00
|
|
|
if (cache->is_editmode) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if ((cache->tot_edges != mesh_render_get_num_edges(me)) ||
|
2017-01-26 18:56:52 +01:00
|
|
|
(cache->tot_faces != mesh_render_get_num_faces(me)) ||
|
|
|
|
(cache->tot_polys != mesh_render_get_num_polys(me)) ||
|
|
|
|
(cache->tot_verts != mesh_render_get_num_verts(me)))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-25 09:16:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mesh_batch_cache_init(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = me->batch_cache;
|
|
|
|
cache->is_editmode = me->edit_btmesh != NULL;
|
|
|
|
|
2017-01-26 19:08:27 +01:00
|
|
|
if (cache->is_editmode == false) {
|
|
|
|
cache->tot_edges = mesh_render_get_num_edges(me);
|
|
|
|
cache->tot_faces = mesh_render_get_num_faces(me);
|
|
|
|
cache->tot_polys = mesh_render_get_num_polys(me);
|
|
|
|
cache->tot_verts = mesh_render_get_num_verts(me);
|
2017-01-25 09:16:29 +01:00
|
|
|
}
|
2017-01-26 18:56:52 +01:00
|
|
|
|
|
|
|
cache->is_dirty = false;
|
2017-01-25 09:16:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static MeshBatchCache *mesh_batch_cache_get(Mesh *me)
|
|
|
|
{
|
|
|
|
if (!mesh_batch_cache_valid(me)) {
|
|
|
|
BKE_mesh_batch_cache_free(me);
|
|
|
|
me->batch_cache = MEM_callocN(sizeof(MeshBatchCache), "MeshBatchCache");
|
|
|
|
mesh_batch_cache_init(me);
|
|
|
|
}
|
|
|
|
return me->batch_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VertexBuffer *mesh_batch_cache_get_pos_in_order(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->pos_in_order == NULL) {
|
|
|
|
static VertexFormat format = { 0 };
|
|
|
|
static unsigned pos_id;
|
|
|
|
if (format.attrib_ct == 0) {
|
|
|
|
/* initialize vertex format */
|
|
|
|
pos_id = add_attrib(&format, "pos", GL_FLOAT, 3, KEEP_FLOAT);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int vertex_ct = mesh_render_get_num_verts(me);
|
|
|
|
const MVert *verts = mesh_render_get_array_vert(me);
|
|
|
|
|
|
|
|
cache->pos_in_order = VertexBuffer_create_with_format(&format);
|
|
|
|
VertexBuffer_allocate_data(cache->pos_in_order, vertex_ct);
|
|
|
|
#if 0
|
|
|
|
const unsigned stride = (verts + 1) - verts; /* or sizeof(MVert) */
|
|
|
|
fillAttribStride(cache->pos_in_order, pos_id, stride, &verts[0].co);
|
|
|
|
#else
|
|
|
|
for (int i = 0; i < vertex_ct; ++i) {
|
|
|
|
setAttrib(cache->pos_in_order, pos_id, i, &verts[i].co);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache->pos_in_order;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ElementList *mesh_batch_cache_get_edges_in_order(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->edges_in_order == NULL) {
|
|
|
|
const int vertex_ct = mesh_render_get_num_verts(me);
|
|
|
|
const int edge_ct = mesh_render_get_num_edges(me);
|
|
|
|
const MEdge *edges = mesh_render_get_array_edge(me);
|
|
|
|
|
|
|
|
ElementListBuilder elb;
|
|
|
|
ElementListBuilder_init(&elb, GL_LINES, edge_ct, vertex_ct);
|
|
|
|
for (int i = 0; i < edge_ct; ++i) {
|
|
|
|
const MEdge *edge = edges + i;
|
|
|
|
add_line_vertices(&elb, edge->v1, edge->v2);
|
|
|
|
}
|
|
|
|
cache->edges_in_order = ElementList_build(&elb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache->edges_in_order;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ElementList *mesh_batch_cache_get_triangles_in_order(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->triangles_in_order == NULL) {
|
|
|
|
const int vertex_ct = mesh_render_get_num_verts(me);
|
|
|
|
const int tessface_ct = mesh_render_get_num_faces(me);
|
|
|
|
MFace *tessfaces = mesh_render_get_array_face(me);
|
|
|
|
|
|
|
|
ElementListBuilder elb;
|
|
|
|
ElementListBuilder_init(&elb, GL_TRIANGLES, tessface_ct * 2, vertex_ct); /* up to 2 triangles per tessface */
|
|
|
|
for (int i = 0; i < tessface_ct; ++i) {
|
|
|
|
const MFace *tess = tessfaces + i;
|
|
|
|
add_triangle_vertices(&elb, tess->v1, tess->v2, tess->v3);
|
|
|
|
|
|
|
|
if (tess->v4) {
|
|
|
|
add_triangle_vertices(&elb, tess->v1, tess->v3, tess->v4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cache->triangles_in_order = ElementList_build(&elb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NOTE: we are reallocating, it would be interesting to reallocating the memory once we
|
|
|
|
* know the exactly triangle count (like in BKE_mesh_batch_cache_get_overlay_edges) */
|
|
|
|
|
|
|
|
return cache->triangles_in_order;
|
|
|
|
}
|
|
|
|
|
2017-01-26 18:56:52 +01:00
|
|
|
void BKE_mesh_batch_cache_dirty(struct Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = me->batch_cache;
|
|
|
|
if (cache) {
|
|
|
|
cache->is_dirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 09:16:29 +01:00
|
|
|
void BKE_mesh_batch_cache_free(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = me->batch_cache;
|
|
|
|
if (!cache) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cache->all_verts) Batch_discard(cache->all_verts);
|
|
|
|
if (cache->all_edges) Batch_discard(cache->all_edges);
|
|
|
|
if (cache->all_triangles) Batch_discard(cache->all_triangles);
|
|
|
|
|
|
|
|
if (cache->pos_in_order) VertexBuffer_discard(cache->pos_in_order);
|
|
|
|
if (cache->edges_in_order) ElementList_discard(cache->edges_in_order);
|
|
|
|
if (cache->triangles_in_order) ElementList_discard(cache->triangles_in_order);
|
|
|
|
|
|
|
|
if (cache->fancy_edges) {
|
|
|
|
Batch_discard_all(cache->fancy_edges);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cache->overlay_edges) {
|
|
|
|
Batch_discard_all(cache->overlay_edges);
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_freeN(cache);
|
|
|
|
me->batch_cache = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Batch *BKE_mesh_batch_cache_get_all_edges(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->all_edges == NULL) {
|
|
|
|
/* create batch from Mesh */
|
|
|
|
cache->all_edges = Batch_create(GL_LINES, mesh_batch_cache_get_pos_in_order(me), mesh_batch_cache_get_edges_in_order(me));
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache->all_edges;
|
|
|
|
}
|
|
|
|
|
|
|
|
Batch *BKE_mesh_batch_cache_get_all_triangles(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->all_triangles == NULL) {
|
|
|
|
/* create batch from DM */
|
|
|
|
cache->all_triangles = Batch_create(GL_TRIANGLES, mesh_batch_cache_get_pos_in_order(me), mesh_batch_cache_get_triangles_in_order(me));
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache->all_triangles;
|
|
|
|
}
|
|
|
|
|
|
|
|
Batch *BKE_mesh_batch_cache_get_all_verts(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->all_verts == NULL) {
|
|
|
|
/* create batch from DM */
|
|
|
|
cache->all_verts = Batch_create(GL_POINTS, mesh_batch_cache_get_pos_in_order(me), NULL);
|
|
|
|
Batch_set_builtin_program(cache->all_verts, GPU_SHADER_3D_POINT_FIXED_SIZE_UNIFORM_COLOR);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache->all_verts;
|
|
|
|
}
|
|
|
|
|
|
|
|
Batch *BKE_mesh_batch_cache_get_fancy_edges(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->fancy_edges == NULL) {
|
|
|
|
/* create batch from DM */
|
|
|
|
static VertexFormat format = { 0 };
|
|
|
|
static unsigned pos_id, n1_id, n2_id;
|
|
|
|
if (format.attrib_ct == 0) {
|
|
|
|
/* initialize vertex format */
|
|
|
|
pos_id = add_attrib(&format, "pos", COMP_F32, 3, KEEP_FLOAT);
|
|
|
|
|
|
|
|
#if USE_10_10_10 /* takes 1/3 the space */
|
|
|
|
n1_id = add_attrib(&format, "N1", COMP_I10, 3, NORMALIZE_INT_TO_FLOAT);
|
|
|
|
n2_id = add_attrib(&format, "N2", COMP_I10, 3, NORMALIZE_INT_TO_FLOAT);
|
|
|
|
#else
|
|
|
|
n1_id = add_attrib(&format, "N1", COMP_F32, 3, KEEP_FLOAT);
|
|
|
|
n2_id = add_attrib(&format, "N2", COMP_F32, 3, KEEP_FLOAT);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
VertexBuffer *vbo = VertexBuffer_create_with_format(&format);
|
|
|
|
|
|
|
|
const MVert *verts = mesh_render_get_array_vert(me);
|
|
|
|
const MEdge *edges = mesh_render_get_array_edge(me);
|
|
|
|
const MPoly *polys = mesh_render_get_array_poly(me);
|
|
|
|
const MLoop *loops = mesh_render_get_array_loop(me);
|
|
|
|
const int edge_ct = mesh_render_get_num_edges(me);
|
|
|
|
const int poly_ct = mesh_render_get_num_polys(me);
|
|
|
|
|
|
|
|
/* need normal of each face, and which faces are adjacent to each edge */
|
|
|
|
typedef struct {
|
|
|
|
int count;
|
|
|
|
int face_index[2];
|
|
|
|
} AdjacentFaces;
|
|
|
|
|
|
|
|
float (*face_normal)[3] = MEM_mallocN(poly_ct * 3 * sizeof(float), "face_normal");
|
|
|
|
AdjacentFaces *adj_faces = MEM_callocN(edge_ct * sizeof(AdjacentFaces), "adj_faces");
|
|
|
|
|
|
|
|
for (int i = 0; i < poly_ct; ++i) {
|
|
|
|
const MPoly *poly = polys + i;
|
|
|
|
|
|
|
|
BKE_mesh_calc_poly_normal(poly, loops + poly->loopstart, verts, face_normal[i]);
|
|
|
|
|
|
|
|
for (int j = poly->loopstart; j < (poly->loopstart + poly->totloop); ++j) {
|
|
|
|
AdjacentFaces *adj = adj_faces + loops[j].e;
|
|
|
|
if (adj->count < 2)
|
|
|
|
adj->face_index[adj->count] = i;
|
|
|
|
adj->count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const int vertex_ct = edge_ct * 2; /* these are GL_LINE verts, not mesh verts */
|
|
|
|
VertexBuffer_allocate_data(vbo, vertex_ct);
|
|
|
|
for (int i = 0; i < edge_ct; ++i) {
|
|
|
|
const MEdge *edge = edges + i;
|
|
|
|
const AdjacentFaces *adj = adj_faces + i;
|
|
|
|
|
|
|
|
#if USE_10_10_10
|
|
|
|
PackedNormal n1value = { .x = 0, .y = 0, .z = +511 };
|
|
|
|
PackedNormal n2value = { .x = 0, .y = 0, .z = -511 };
|
|
|
|
|
|
|
|
if (adj->count == 2) {
|
|
|
|
n1value = convert_i10_v3(face_normal[adj->face_index[0]]);
|
|
|
|
n2value = convert_i10_v3(face_normal[adj->face_index[1]]);
|
|
|
|
}
|
|
|
|
|
|
|
|
const PackedNormal *n1 = &n1value;
|
|
|
|
const PackedNormal *n2 = &n2value;
|
|
|
|
#else
|
|
|
|
const float dummy1[3] = { 0.0f, 0.0f, +1.0f };
|
|
|
|
const float dummy2[3] = { 0.0f, 0.0f, -1.0f };
|
|
|
|
|
|
|
|
const float *n1 = (adj->count == 2) ? face_normal[adj->face_index[0]] : dummy1;
|
|
|
|
const float *n2 = (adj->count == 2) ? face_normal[adj->face_index[1]] : dummy2;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
setAttrib(vbo, pos_id, 2 * i, &verts[edge->v1].co);
|
|
|
|
setAttrib(vbo, n1_id, 2 * i, n1);
|
|
|
|
setAttrib(vbo, n2_id, 2 * i, n2);
|
|
|
|
|
|
|
|
setAttrib(vbo, pos_id, 2 * i + 1, &verts[edge->v2].co);
|
|
|
|
setAttrib(vbo, n1_id, 2 * i + 1, n1);
|
|
|
|
setAttrib(vbo, n2_id, 2 * i + 1, n2);
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_freeN(adj_faces);
|
|
|
|
MEM_freeN(face_normal);
|
|
|
|
|
|
|
|
cache->fancy_edges = Batch_create(GL_LINES, vbo, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache->fancy_edges;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool edge_is_real(const MEdge *edges, int edge_ct, int v1, int v2)
|
|
|
|
{
|
|
|
|
/* TODO: same thing, except not ridiculously slow */
|
|
|
|
|
|
|
|
for (int e = 0; e < edge_ct; ++e) {
|
|
|
|
const MEdge *edge = edges + e;
|
|
|
|
if ((edge->v1 == v1 && edge->v2 == v2) || (edge->v1 == v2 && edge->v2 == v1)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_overlay_tri(
|
|
|
|
VertexBuffer *vbo, unsigned pos_id, unsigned edgeMod_id, const MVert *verts,
|
|
|
|
const MEdge *edges, int edge_ct, int v1, int v2, int v3, int base_vert_idx)
|
|
|
|
{
|
|
|
|
const float edgeMods[2] = { 0.0f, 1.0f };
|
|
|
|
|
|
|
|
const float *pos = verts[v1].co;
|
|
|
|
setAttrib(vbo, pos_id, base_vert_idx + 0, pos);
|
|
|
|
setAttrib(vbo, edgeMod_id, base_vert_idx + 0, edgeMods + (edge_is_real(edges, edge_ct, v2, v3) ? 1 : 0));
|
|
|
|
|
|
|
|
pos = verts[v2].co;
|
|
|
|
setAttrib(vbo, pos_id, base_vert_idx + 1, pos);
|
|
|
|
setAttrib(vbo, edgeMod_id, base_vert_idx + 1, edgeMods + (edge_is_real(edges, edge_ct, v3, v1) ? 1 : 0));
|
|
|
|
|
|
|
|
pos = verts[v3].co;
|
|
|
|
setAttrib(vbo, pos_id, base_vert_idx + 2, pos);
|
|
|
|
setAttrib(vbo, edgeMod_id, base_vert_idx + 2, edgeMods + (edge_is_real(edges, edge_ct, v1, v2) ? 1 : 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
Batch *BKE_mesh_batch_cache_get_overlay_edges(Mesh *me)
|
|
|
|
{
|
|
|
|
MeshBatchCache *cache = mesh_batch_cache_get(me);
|
|
|
|
|
|
|
|
if (cache->overlay_edges == NULL) {
|
|
|
|
/* create batch from DM */
|
|
|
|
static VertexFormat format = { 0 };
|
|
|
|
static unsigned pos_id, edgeMod_id;
|
|
|
|
|
|
|
|
if (format.attrib_ct == 0) {
|
|
|
|
/* initialize vertex format */
|
|
|
|
pos_id = add_attrib(&format, "pos", GL_FLOAT, 3, KEEP_FLOAT);
|
|
|
|
edgeMod_id = add_attrib(&format, "edgeWidthModulator", GL_FLOAT, 1, KEEP_FLOAT);
|
|
|
|
}
|
|
|
|
VertexBuffer *vbo = VertexBuffer_create_with_format(&format);
|
|
|
|
|
|
|
|
const int edge_ct = mesh_render_get_num_edges(me);
|
|
|
|
const int tessface_ct = mesh_render_get_num_faces(me);
|
|
|
|
const MVert *verts = mesh_render_get_array_vert(me);
|
|
|
|
const MEdge *edges = mesh_render_get_array_edge(me);
|
|
|
|
const MFace *tessfaces = mesh_render_get_array_face(me);
|
|
|
|
|
|
|
|
VertexBuffer_allocate_data(vbo, tessface_ct * 6); /* up to 2 triangles per tessface */
|
|
|
|
|
|
|
|
int gpu_vert_idx = 0;
|
|
|
|
for (int i = 0; i < tessface_ct; ++i) {
|
|
|
|
const MFace *tess = tessfaces + i;
|
|
|
|
add_overlay_tri(vbo, pos_id, edgeMod_id, verts, edges, edge_ct, tess->v1, tess->v2, tess->v3, gpu_vert_idx);
|
|
|
|
gpu_vert_idx += 3;
|
|
|
|
/* tessface can be triangle or quad */
|
|
|
|
if (tess->v4) {
|
|
|
|
add_overlay_tri(vbo, pos_id, edgeMod_id, verts, edges, edge_ct, tess->v3, tess->v2, tess->v4, gpu_vert_idx);
|
|
|
|
gpu_vert_idx += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* in some cases all the faces are quad, so no need to reallocate */
|
|
|
|
if (vbo->vertex_ct != gpu_vert_idx) {
|
|
|
|
VertexBuffer_resize_data(vbo, gpu_vert_idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
cache->overlay_edges = Batch_create(GL_TRIANGLES, vbo, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache->overlay_edges;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef MESH_RENDER_FUNCTION
|