Freestyle: memory consumption optimization in stroke rendering.
Previously individual strokes were represented by distinct mesh objects no matter how many vertices and materials each stroke has, although the vertex and material counts can be quite small depending on the input scene data. Now stroke meshes are packed into a minimum number of mesh objects, so as to reduce the overheads of Blender object creation.
This commit is contained in:
@@ -882,10 +882,13 @@ void Controller::ResetRenderCount()
|
|||||||
|
|
||||||
Render *Controller::RenderStrokes(Render *re, bool render)
|
Render *Controller::RenderStrokes(Render *re, bool render)
|
||||||
{
|
{
|
||||||
|
int totmesh = 0;
|
||||||
_Chrono.start();
|
_Chrono.start();
|
||||||
BlenderStrokeRenderer *blenderRenderer = new BlenderStrokeRenderer(re, ++_render_count);
|
BlenderStrokeRenderer *blenderRenderer = new BlenderStrokeRenderer(re, ++_render_count);
|
||||||
if (render)
|
if (render) {
|
||||||
_Canvas->Render(blenderRenderer);
|
_Canvas->Render(blenderRenderer);
|
||||||
|
totmesh = blenderRenderer->GenerateScene();
|
||||||
|
}
|
||||||
real d = _Chrono.stop();
|
real d = _Chrono.stop();
|
||||||
if (G.debug & G_DEBUG_FREESTYLE) {
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
||||||
cout << "Temporary scene generation: " << d << endl;
|
cout << "Temporary scene generation: " << d << endl;
|
||||||
@@ -904,8 +907,8 @@ Render *Controller::RenderStrokes(Render *re, bool render)
|
|||||||
float mmap_used_memory = (mmap_in_use) / (1024.0 * 1024.0);
|
float mmap_used_memory = (mmap_in_use) / (1024.0 * 1024.0);
|
||||||
float megs_peak_memory = (peak_memory) / (1024.0 * 1024.0);
|
float megs_peak_memory = (peak_memory) / (1024.0 * 1024.0);
|
||||||
|
|
||||||
printf("%d verts, %d faces, mem %.2fM (%.2fM, peak %.2fM)\n",
|
printf("%d objs, %d verts, %d faces, mem %.2fM (%.2fM, peak %.2fM)\n",
|
||||||
freestyle_render->i.totvert, freestyle_render->i.totface,
|
totmesh, freestyle_render->i.totvert, freestyle_render->i.totface,
|
||||||
megs_used_memory, mmap_used_memory, megs_peak_memory);
|
megs_used_memory, mmap_used_memory, megs_peak_memory);
|
||||||
}
|
}
|
||||||
delete blenderRenderer;
|
delete blenderRenderer;
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ extern "C" {
|
|||||||
|
|
||||||
namespace Freestyle {
|
namespace Freestyle {
|
||||||
|
|
||||||
|
const char *BlenderStrokeRenderer::uvNames[] = {"along_stroke", "along_stroke_tips"};
|
||||||
|
|
||||||
BlenderStrokeRenderer::BlenderStrokeRenderer(Render *re, int render_count) : StrokeRenderer()
|
BlenderStrokeRenderer::BlenderStrokeRenderer(Render *re, int render_count) : StrokeRenderer()
|
||||||
{
|
{
|
||||||
freestyle_bmain = re->freestyle_bmain;
|
freestyle_bmain = re->freestyle_bmain;
|
||||||
@@ -208,6 +210,8 @@ BlenderStrokeRenderer::~BlenderStrokeRenderer()
|
|||||||
|
|
||||||
if (_use_shading_nodes)
|
if (_use_shading_nodes)
|
||||||
BLI_ghash_free(_nodetree_hash, NULL, NULL);
|
BLI_ghash_free(_nodetree_hash, NULL, NULL);
|
||||||
|
|
||||||
|
FreeStrokeGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
float BlenderStrokeRenderer::get_stroke_vertex_z(void) const
|
float BlenderStrokeRenderer::get_stroke_vertex_z(void) const
|
||||||
@@ -414,10 +418,10 @@ Material* BlenderStrokeRenderer::GetStrokeShader(Main *bmain, bNodeTree *iNodeTr
|
|||||||
input_uvmap->locy = node->locy;
|
input_uvmap->locy = node->locy;
|
||||||
NodeShaderUVMap *storage = (NodeShaderUVMap *)input_uvmap->storage;
|
NodeShaderUVMap *storage = (NodeShaderUVMap *)input_uvmap->storage;
|
||||||
if (node->custom1 & 1) { // use_tips
|
if (node->custom1 & 1) { // use_tips
|
||||||
BLI_strncpy(storage->uv_map, "along_stroke_tips", sizeof(storage->uv_map));
|
BLI_strncpy(storage->uv_map, uvNames[1], sizeof(storage->uv_map));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BLI_strncpy(storage->uv_map, "along_stroke", sizeof(storage->uv_map));
|
BLI_strncpy(storage->uv_map, uvNames[0], sizeof(storage->uv_map));
|
||||||
}
|
}
|
||||||
fromsock = (bNodeSocket *)BLI_findlink(&input_uvmap->outputs, 0); // UV
|
fromsock = (bNodeSocket *)BLI_findlink(&input_uvmap->outputs, 0); // UV
|
||||||
|
|
||||||
@@ -439,6 +443,11 @@ Material* BlenderStrokeRenderer::GetStrokeShader(Main *bmain, bNodeTree *iNodeTr
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlenderStrokeRenderer::RenderStrokeRep(StrokeRep *iStrokeRep) const
|
void BlenderStrokeRenderer::RenderStrokeRep(StrokeRep *iStrokeRep) const
|
||||||
|
{
|
||||||
|
RenderStrokeRepBasic(iStrokeRep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlenderStrokeRenderer::RenderStrokeRepBasic(StrokeRep *iStrokeRep) const
|
||||||
{
|
{
|
||||||
if (_use_shading_nodes) {
|
if (_use_shading_nodes) {
|
||||||
bNodeTree *nt = iStrokeRep->getNodeTree();
|
bNodeTree *nt = iStrokeRep->getNodeTree();
|
||||||
@@ -509,10 +518,10 @@ void BlenderStrokeRenderer::RenderStrokeRep(StrokeRep *iStrokeRep) const
|
|||||||
// We'll generate both with tips and without tips
|
// We'll generate both with tips and without tips
|
||||||
// coordinates, on two different UV layers.
|
// coordinates, on two different UV layers.
|
||||||
if (ma->mtex[a]->texflag & MTEX_TIPS) {
|
if (ma->mtex[a]->texflag & MTEX_TIPS) {
|
||||||
BLI_strncpy(ma->mtex[a]->uvname, "along_stroke_tips", sizeof(ma->mtex[a]->uvname));
|
BLI_strncpy(ma->mtex[a]->uvname, uvNames[1], sizeof(ma->mtex[a]->uvname));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BLI_strncpy(ma->mtex[a]->uvname, "along_stroke", sizeof(ma->mtex[a]->uvname));
|
BLI_strncpy(ma->mtex[a]->uvname, uvNames[0], sizeof(ma->mtex[a]->uvname));
|
||||||
}
|
}
|
||||||
a++;
|
a++;
|
||||||
}
|
}
|
||||||
@@ -521,7 +530,42 @@ void BlenderStrokeRenderer::RenderStrokeRep(StrokeRep *iStrokeRep) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderStrokeRepBasic(iStrokeRep);
|
const vector<Strip*>& strips = iStrokeRep->getStrips();
|
||||||
|
const bool hasTex = iStrokeRep->hasTex();
|
||||||
|
int totvert = 0, totedge = 0, totpoly = 0, totloop = 0;
|
||||||
|
int visible_faces, visible_segments;
|
||||||
|
for (vector<Strip*>::const_iterator s = strips.begin(), send = strips.end(); s != send; ++s) {
|
||||||
|
Strip::vertex_container& strip_vertices = (*s)->vertices();
|
||||||
|
|
||||||
|
// count visible faces and strip segments
|
||||||
|
test_strip_visibility(strip_vertices, &visible_faces, &visible_segments);
|
||||||
|
if (visible_faces == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
totvert += visible_faces + visible_segments * 2;
|
||||||
|
totedge += visible_faces * 2 + visible_segments;
|
||||||
|
totpoly += visible_faces;
|
||||||
|
totloop += visible_faces * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlenderStrokeRenderer *self = const_cast<BlenderStrokeRenderer *>(this); // FIXME
|
||||||
|
vector<StrokeGroup*> *groups = hasTex ? &self->texturedStrokeGroups : &self->strokeGroups;
|
||||||
|
StrokeGroup *group;
|
||||||
|
if (groups->empty() || !(groups->back()->totvert + totvert < MESH_MAX_VERTS &&
|
||||||
|
groups->back()->totcol + 1 < MAXMAT))
|
||||||
|
{
|
||||||
|
group = new StrokeGroup;
|
||||||
|
groups->push_back(group);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
group = groups->back();
|
||||||
|
}
|
||||||
|
group->strokes.push_back(iStrokeRep);
|
||||||
|
group->totvert += totvert;
|
||||||
|
group->totedge += totedge;
|
||||||
|
group->totpoly += totpoly;
|
||||||
|
group->totloop += totloop;
|
||||||
|
group->totcol++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the triangle is visible (i.e., within the render image boundary)
|
// Check if the triangle is visible (i.e., within the render image boundary)
|
||||||
@@ -578,89 +622,112 @@ void BlenderStrokeRenderer::test_strip_visibility(Strip::vertex_container& strip
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a mesh object representing a stroke
|
// Release allocated memory for stroke groups
|
||||||
void BlenderStrokeRenderer::RenderStrokeRepBasic(StrokeRep *iStrokeRep) const
|
void BlenderStrokeRenderer::FreeStrokeGroups()
|
||||||
{
|
{
|
||||||
vector<Strip*>& strips = iStrokeRep->getStrips();
|
vector<StrokeGroup*>::const_iterator it, itend;
|
||||||
const bool hasTex = iStrokeRep->hasTex();
|
|
||||||
Strip::vertex_container::iterator v[3];
|
|
||||||
StrokeVertexRep *svRep[3];
|
|
||||||
unsigned int vertex_index, edge_index, loop_index;
|
|
||||||
Vec2r p;
|
|
||||||
|
|
||||||
int totvert = 0, totedge = 0, totpoly = 0, totloop = 0;
|
for (it = strokeGroups.begin(), itend = strokeGroups.end();
|
||||||
int visible_faces, visible_segments;
|
it != itend; ++it)
|
||||||
|
{
|
||||||
bool visible;
|
delete (*it);
|
||||||
for (vector<Strip*>::iterator s = strips.begin(), send = strips.end(); s != send; ++s) {
|
}
|
||||||
Strip::vertex_container& strip_vertices = (*s)->vertices();
|
for (it = texturedStrokeGroups.begin(), itend = texturedStrokeGroups.end();
|
||||||
|
it != itend; ++it)
|
||||||
// count visible faces and strip segments
|
{
|
||||||
test_strip_visibility(strip_vertices, &visible_faces, &visible_segments);
|
delete (*it);
|
||||||
if (visible_faces == 0)
|
}
|
||||||
continue;
|
|
||||||
|
|
||||||
totvert += visible_faces + visible_segments * 2;
|
|
||||||
totedge += visible_faces * 2 + visible_segments;
|
|
||||||
totpoly += visible_faces;
|
|
||||||
totloop += visible_faces * 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build a scene populated by mesh objects representing stylized strokes
|
||||||
|
int BlenderStrokeRenderer::GenerateScene()
|
||||||
|
{
|
||||||
|
vector<StrokeGroup*>::const_iterator it, itend;
|
||||||
|
|
||||||
|
for (it = strokeGroups.begin(), itend = strokeGroups.end();
|
||||||
|
it != itend; ++it)
|
||||||
|
{
|
||||||
|
GenerateStrokeMesh(*it, false);
|
||||||
|
}
|
||||||
|
for (it = texturedStrokeGroups.begin(), itend = texturedStrokeGroups.end();
|
||||||
|
it != itend; ++it)
|
||||||
|
{
|
||||||
|
GenerateStrokeMesh(*it, true);
|
||||||
|
}
|
||||||
|
return strokeGroups.size() + texturedStrokeGroups.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a mesh object representing a group of stylized strokes
|
||||||
|
void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex)
|
||||||
|
{
|
||||||
#if 0
|
#if 0
|
||||||
Object *object_mesh = BKE_object_add(freestyle_bmain, freestyle_scene, OB_MESH);
|
Object *object_mesh = BKE_object_add(freestyle_bmain, freestyle_scene, OB_MESH);
|
||||||
#else
|
#else
|
||||||
Object *object_mesh = NewMesh();
|
Object *object_mesh = NewMesh();
|
||||||
#endif
|
#endif
|
||||||
Mesh *mesh = (Mesh *)object_mesh->data;
|
Mesh *mesh = (Mesh *)object_mesh->data;
|
||||||
mesh->mat = (Material **)MEM_mallocN(1 * sizeof(Material *), "MaterialList");
|
|
||||||
mesh->mat[0] = iStrokeRep->getMaterial();
|
|
||||||
mesh->totcol = 1;
|
|
||||||
test_object_materials(freestyle_bmain, (ID *)mesh);
|
|
||||||
|
|
||||||
// vertices allocation
|
mesh->totvert = group->totvert;
|
||||||
mesh->totvert = totvert; // visible_faces + visible_segments * 2;
|
mesh->totedge = group->totedge;
|
||||||
CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, NULL, mesh->totvert);
|
mesh->totpoly = group->totpoly;
|
||||||
|
mesh->totloop = group->totloop;
|
||||||
|
mesh->totcol = group->totcol;
|
||||||
|
|
||||||
// edges allocation
|
mesh->mvert = (MVert *)CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, NULL, mesh->totvert);
|
||||||
mesh->totedge = totedge; // visible_faces * 2 + visible_segments;
|
mesh->medge = (MEdge *)CustomData_add_layer(&mesh->edata, CD_MEDGE, CD_CALLOC, NULL, mesh->totedge);
|
||||||
CustomData_add_layer(&mesh->edata, CD_MEDGE, CD_CALLOC, NULL, mesh->totedge);
|
mesh->mpoly = (MPoly *)CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, NULL, mesh->totpoly);
|
||||||
|
mesh->mloop = (MLoop *)CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, NULL, mesh->totloop);
|
||||||
// faces allocation
|
|
||||||
mesh->totpoly = totpoly; // visible_faces;
|
|
||||||
CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, NULL, mesh->totpoly);
|
|
||||||
|
|
||||||
// loops allocation
|
|
||||||
mesh->totloop = totloop; // visible_faces * 3;
|
|
||||||
CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, NULL, mesh->totloop);
|
|
||||||
|
|
||||||
// uv maps
|
|
||||||
MLoopUV *loopsuv[2] = { NULL };
|
|
||||||
if (hasTex) {
|
|
||||||
loopsuv[0] = (MLoopUV *)CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_CALLOC, NULL, mesh->totloop, "along_stroke");
|
|
||||||
loopsuv[1] = (MLoopUV *)CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_CALLOC, NULL, mesh->totloop, "along_stroke_tips");
|
|
||||||
|
|
||||||
CustomData_add_layer_named(&mesh->pdata, CD_MTEXPOLY, CD_CALLOC, NULL, mesh->totpoly, "along_stroke");
|
|
||||||
CustomData_add_layer_named(&mesh->pdata, CD_MTEXPOLY, CD_CALLOC, NULL, mesh->totpoly, "along_stroke_tips");
|
|
||||||
}
|
|
||||||
|
|
||||||
// colors and transparency (the latter represented by grayscale colors)
|
|
||||||
MLoopCol *colors = (MLoopCol *)CustomData_add_layer_named(&mesh->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, mesh->totloop, "Color");
|
|
||||||
MLoopCol *transp = (MLoopCol *)CustomData_add_layer_named(&mesh->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, mesh->totloop, "Alpha");
|
|
||||||
|
|
||||||
BKE_mesh_update_customdata_pointers(mesh, true);
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// Data copy
|
|
||||||
////////////////////
|
|
||||||
|
|
||||||
MVert *vertices = mesh->mvert;
|
MVert *vertices = mesh->mvert;
|
||||||
MEdge *edges = mesh->medge;
|
MEdge *edges = mesh->medge;
|
||||||
MPoly *polys = mesh->mpoly;
|
MPoly *polys = mesh->mpoly;
|
||||||
MLoop *loops = mesh->mloop;
|
MLoop *loops = mesh->mloop;
|
||||||
|
MLoopUV *loopsuv[2] = { NULL };
|
||||||
|
|
||||||
vertex_index = edge_index = loop_index = 0;
|
if (hasTex) {
|
||||||
|
// First UV layer
|
||||||
|
CustomData_add_layer_named(&mesh->pdata, CD_MTEXPOLY, CD_CALLOC, NULL, mesh->totpoly, uvNames[0]);
|
||||||
|
CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_CALLOC, NULL, mesh->totloop, uvNames[0]);
|
||||||
|
CustomData_set_layer_active(&mesh->pdata, CD_MTEXPOLY, 0);
|
||||||
|
CustomData_set_layer_active(&mesh->ldata, CD_MLOOPUV, 0);
|
||||||
|
BKE_mesh_update_customdata_pointers(mesh, true);
|
||||||
|
loopsuv[0] = mesh->mloopuv;
|
||||||
|
|
||||||
for (vector<Strip*>::iterator s = strips.begin(), send = strips.end(); s != send; ++s) {
|
// Second UV layer
|
||||||
|
CustomData_add_layer_named(&mesh->pdata, CD_MTEXPOLY, CD_CALLOC, NULL, mesh->totpoly, uvNames[1]);
|
||||||
|
CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_CALLOC, NULL, mesh->totloop, uvNames[1]);
|
||||||
|
CustomData_set_layer_active(&mesh->pdata, CD_MTEXPOLY, 1);
|
||||||
|
CustomData_set_layer_active(&mesh->ldata, CD_MLOOPUV, 1);
|
||||||
|
BKE_mesh_update_customdata_pointers(mesh, true);
|
||||||
|
loopsuv[1] = mesh->mloopuv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// colors and transparency (the latter represented by grayscale colors)
|
||||||
|
MLoopCol *colors = (MLoopCol *)CustomData_add_layer_named(&mesh->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, mesh->totloop, "Color");
|
||||||
|
MLoopCol *transp = (MLoopCol *)CustomData_add_layer_named(&mesh->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, mesh->totloop, "Alpha");
|
||||||
|
mesh->mloopcol = colors;
|
||||||
|
|
||||||
|
mesh->mat = (Material **)MEM_mallocN(sizeof(Material *) * mesh->totcol, "MaterialList");
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// Data copy
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
int vertex_index = 0, edge_index = 0, loop_index = 0, material_index = 0;
|
||||||
|
int visible_faces, visible_segments;
|
||||||
|
bool visible;
|
||||||
|
Strip::vertex_container::iterator v[3];
|
||||||
|
StrokeVertexRep *svRep[3];
|
||||||
|
Vec2r p;
|
||||||
|
|
||||||
|
for (vector<StrokeRep*>::const_iterator it = group->strokes.begin(), itend = group->strokes.end();
|
||||||
|
it != itend; ++it)
|
||||||
|
{
|
||||||
|
mesh->mat[material_index] = (*it)->getMaterial();
|
||||||
|
material_index++;
|
||||||
|
|
||||||
|
vector<Strip*>& strips = (*it)->getStrips();
|
||||||
|
for (vector<Strip*>::const_iterator s = strips.begin(), send = strips.end(); s != send; ++s) {
|
||||||
Strip::vertex_container& strip_vertices = (*s)->vertices();
|
Strip::vertex_container& strip_vertices = (*s)->vertices();
|
||||||
int strip_vertex_count = strip_vertices.size();
|
int strip_vertex_count = strip_vertices.size();
|
||||||
|
|
||||||
@@ -836,10 +903,15 @@ void BlenderStrokeRenderer::RenderStrokeRepBasic(StrokeRep *iStrokeRep) const
|
|||||||
}
|
}
|
||||||
} // loop over strip vertices
|
} // loop over strip vertices
|
||||||
} // loop over strips
|
} // loop over strips
|
||||||
#if 0
|
} // loop over strokes
|
||||||
BLI_assert(totvert == vertex_index);
|
|
||||||
BLI_assert(totedge == edge_index);
|
test_object_materials(freestyle_bmain, (ID *)mesh);
|
||||||
BLI_assert(totloop == loop_index);
|
|
||||||
|
#if 0 // XXX
|
||||||
|
BLI_assert(mesh->totvert == vertex_index);
|
||||||
|
BLI_assert(mesh->totedge == edge_index);
|
||||||
|
BLI_assert(mesh->totloop == loop_index);
|
||||||
|
BLI_assert(mesh->totcol == material_index);
|
||||||
BKE_mesh_validate(mesh, true);
|
BKE_mesh_validate(mesh, true);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,21 @@ public:
|
|||||||
|
|
||||||
Object *NewMesh() const;
|
Object *NewMesh() const;
|
||||||
|
|
||||||
|
struct StrokeGroup {
|
||||||
|
explicit StrokeGroup() : totvert(0), totedge(0), totpoly(0), totloop(0), totcol(0) {}
|
||||||
|
vector<StrokeRep*> strokes;
|
||||||
|
int totvert;
|
||||||
|
int totedge;
|
||||||
|
int totpoly;
|
||||||
|
int totloop;
|
||||||
|
int totcol;
|
||||||
|
};
|
||||||
|
vector<StrokeGroup*> strokeGroups, texturedStrokeGroups;
|
||||||
|
|
||||||
|
int GenerateScene();
|
||||||
|
void GenerateStrokeMesh(StrokeGroup *group, bool hasTex);
|
||||||
|
void FreeStrokeGroups();
|
||||||
|
|
||||||
Render *RenderScene(Render *re, bool render);
|
Render *RenderScene(Render *re, bool render);
|
||||||
|
|
||||||
static Material* GetStrokeShader(Main *bmain, bNodeTree *iNodeTree, bool do_id_user);
|
static Material* GetStrokeShader(Main *bmain, bNodeTree *iNodeTree, bool do_id_user);
|
||||||
@@ -68,12 +83,16 @@ protected:
|
|||||||
bool _use_shading_nodes;
|
bool _use_shading_nodes;
|
||||||
struct GHash *_nodetree_hash;
|
struct GHash *_nodetree_hash;
|
||||||
|
|
||||||
|
static const char *uvNames[];
|
||||||
|
|
||||||
float get_stroke_vertex_z(void) const;
|
float get_stroke_vertex_z(void) const;
|
||||||
unsigned int get_stroke_mesh_id(void) const;
|
unsigned int get_stroke_mesh_id(void) const;
|
||||||
bool test_triangle_visibility(StrokeVertexRep *svRep[3]) const;
|
bool test_triangle_visibility(StrokeVertexRep *svRep[3]) const;
|
||||||
void test_strip_visibility(Strip::vertex_container& strip_vertices,
|
void test_strip_visibility(Strip::vertex_container& strip_vertices,
|
||||||
int *visible_faces, int *visible_segments) const;
|
int *visible_faces, int *visible_segments) const;
|
||||||
|
|
||||||
|
vector<StrokeRep *> _strokeReps;
|
||||||
|
|
||||||
#ifdef WITH_CXX_GUARDEDALLOC
|
#ifdef WITH_CXX_GUARDEDALLOC
|
||||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BlenderStrokeRenderer")
|
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BlenderStrokeRenderer")
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -400,6 +400,7 @@ Stroke::Stroke()
|
|||||||
}
|
}
|
||||||
_nodeTree = NULL;
|
_nodeTree = NULL;
|
||||||
_tips = false;
|
_tips = false;
|
||||||
|
_rep = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stroke::Stroke(const Stroke& iBrother)
|
Stroke::Stroke(const Stroke& iBrother)
|
||||||
@@ -427,6 +428,10 @@ Stroke::Stroke(const Stroke& iBrother)
|
|||||||
}
|
}
|
||||||
_nodeTree = iBrother._nodeTree;
|
_nodeTree = iBrother._nodeTree;
|
||||||
_tips = iBrother._tips;
|
_tips = iBrother._tips;
|
||||||
|
if (iBrother._rep)
|
||||||
|
_rep = new StrokeRep(*(iBrother._rep));
|
||||||
|
else
|
||||||
|
_rep = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stroke::~Stroke()
|
Stroke::~Stroke()
|
||||||
@@ -439,6 +444,10 @@ Stroke::~Stroke()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ViewEdges.clear();
|
_ViewEdges.clear();
|
||||||
|
if (_rep) {
|
||||||
|
delete _rep;
|
||||||
|
_rep = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stroke& Stroke::operator=(const Stroke& iBrother)
|
Stroke& Stroke::operator=(const Stroke& iBrother)
|
||||||
@@ -456,6 +465,12 @@ Stroke& Stroke::operator=(const Stroke& iBrother)
|
|||||||
_id = iBrother._id;
|
_id = iBrother._id;
|
||||||
_ViewEdges = iBrother._ViewEdges;
|
_ViewEdges = iBrother._ViewEdges;
|
||||||
_sampling = iBrother._sampling;
|
_sampling = iBrother._sampling;
|
||||||
|
if (_rep)
|
||||||
|
delete _rep;
|
||||||
|
if (iBrother._rep)
|
||||||
|
_rep = new StrokeRep(*(iBrother._rep));
|
||||||
|
else
|
||||||
|
_rep = NULL;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,14 +772,16 @@ void Stroke::ScaleThickness(float iFactor)
|
|||||||
|
|
||||||
void Stroke::Render(const StrokeRenderer *iRenderer)
|
void Stroke::Render(const StrokeRenderer *iRenderer)
|
||||||
{
|
{
|
||||||
StrokeRep rep(this);
|
if (!_rep)
|
||||||
iRenderer->RenderStrokeRep(&rep);
|
_rep = new StrokeRep(this);
|
||||||
|
iRenderer->RenderStrokeRep(_rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stroke::RenderBasic(const StrokeRenderer *iRenderer)
|
void Stroke::RenderBasic(const StrokeRenderer *iRenderer)
|
||||||
{
|
{
|
||||||
StrokeRep rep(this);
|
if (!_rep)
|
||||||
iRenderer->RenderStrokeRepBasic(&rep);
|
_rep = new StrokeRep(this);
|
||||||
|
iRenderer->RenderStrokeRep(_rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stroke::vertex_iterator Stroke::vertices_begin(float sampling)
|
Stroke::vertex_iterator Stroke::vertices_begin(float sampling)
|
||||||
|
|||||||
@@ -549,6 +549,7 @@ private:
|
|||||||
MTex *_mtex[MAX_MTEX];
|
MTex *_mtex[MAX_MTEX];
|
||||||
bNodeTree *_nodeTree;
|
bNodeTree *_nodeTree;
|
||||||
bool _tips;
|
bool _tips;
|
||||||
|
StrokeRep *_rep;
|
||||||
Vec2r _extremityOrientations[2]; // the orientations of the first and last extermity
|
Vec2r _extremityOrientations[2]; // the orientations of the first and last extermity
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user