1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup freestyle
19 */
20
21 #include <iostream>
22 #include <map>
23 #include <set>
24
25 #include "../application/AppCanvas.h"
26 #include "../application/AppConfig.h"
27 #include "../application/AppView.h"
28 #include "../application/Controller.h"
29
30 #include "BlenderStrokeRenderer.h"
31
32 using namespace std;
33 using namespace Freestyle;
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_camera_types.h"
38 #include "DNA_collection_types.h"
39 #include "DNA_freestyle_types.h"
40 #include "DNA_material_types.h"
41 #include "DNA_text_types.h"
42
43 #include "BKE_callbacks.h"
44 #include "BKE_context.h"
45 #include "BKE_freestyle.h"
46 #include "BKE_global.h"
47 #include "BKE_lib_id.h"
48 #include "BKE_linestyle.h"
49 #include "BKE_scene.h"
50 #include "BKE_text.h"
51
52 #include "BLT_translation.h"
53
54 #include "BLI_blenlib.h"
55 #include "BLI_math.h"
56 #include "BLI_math_color_blend.h"
57
58 #include "BPY_extern.h"
59
60 #include "DEG_depsgraph_query.h"
61
62 #include "renderpipeline.h"
63
64 #include "FRS_freestyle.h"
65
66 extern "C" {
67
68 #define DEFAULT_SPHERE_RADIUS 1.0f
69 #define DEFAULT_DKR_EPSILON 0.0f
70
71 struct FreestyleGlobals g_freestyle;
72
73 // Freestyle configuration
74 static bool freestyle_is_initialized = false;
75 static Config::Path *pathconfig = NULL;
76 static Controller *controller = NULL;
77 static AppView *view = NULL;
78
79 // line set buffer for copy & paste
80 static FreestyleLineSet lineset_buffer;
81 static bool lineset_copied = false;
82
load_post_callback(struct Main *,struct PointerRNA **,const int,void *)83 static void load_post_callback(struct Main * /*main*/,
84 struct PointerRNA ** /*pointers*/,
85 const int /*num_pointers*/,
86 void * /*arg*/)
87 {
88 lineset_copied = false;
89 }
90
91 static bCallbackFuncStore load_post_callback_funcstore = {
92 NULL,
93 NULL, /* next, prev */
94 load_post_callback, /* func */
95 NULL, /* arg */
96 0 /* alloc */
97 };
98
99 //=======================================================
100 // Initialization
101 //=======================================================
102
FRS_init()103 void FRS_init()
104 {
105 if (freestyle_is_initialized) {
106 return;
107 }
108
109 pathconfig = new Config::Path;
110 controller = new Controller();
111 view = new AppView;
112 controller->setView(view);
113 controller->Clear();
114 g_freestyle.scene = NULL;
115 lineset_copied = false;
116
117 BKE_callback_add(&load_post_callback_funcstore, BKE_CB_EVT_LOAD_POST);
118
119 freestyle_is_initialized = 1;
120 }
121
FRS_set_context(bContext * C)122 void FRS_set_context(bContext *C)
123 {
124 if (G.debug & G_DEBUG_FREESTYLE) {
125 cout << "FRS_set_context: context 0x" << C << " scene 0x" << CTX_data_scene(C) << endl;
126 }
127 controller->setContext(C);
128 }
129
FRS_exit()130 void FRS_exit()
131 {
132 delete pathconfig;
133 delete controller;
134 delete view;
135 }
136
137 //=======================================================
138 // Rendering
139 //=======================================================
140
init_view(Render * re)141 static void init_view(Render *re)
142 {
143 int width = re->winx;
144 int height = re->winy;
145 int xmin = re->disprect.xmin;
146 int ymin = re->disprect.ymin;
147 int xmax = re->disprect.xmax;
148 int ymax = re->disprect.ymax;
149
150 float thickness = 1.0f;
151 switch (re->r.line_thickness_mode) {
152 case R_LINE_THICKNESS_ABSOLUTE:
153 thickness = re->r.unit_line_thickness * (re->r.size / 100.f);
154 break;
155 case R_LINE_THICKNESS_RELATIVE:
156 thickness = height / 480.f;
157 break;
158 }
159
160 g_freestyle.viewport[0] = g_freestyle.viewport[1] = 0;
161 g_freestyle.viewport[2] = width;
162 g_freestyle.viewport[3] = height;
163
164 view->setWidth(width);
165 view->setHeight(height);
166 view->setBorder(xmin, ymin, xmax, ymax);
167 view->setThickness(thickness);
168
169 if (G.debug & G_DEBUG_FREESTYLE) {
170 cout << "\n=== Dimensions of the 2D image coordinate system ===" << endl;
171 cout << "Width : " << width << endl;
172 cout << "Height : " << height << endl;
173 if (re->r.mode & R_BORDER) {
174 cout << "Border : (" << xmin << ", " << ymin << ") - (" << xmax << ", " << ymax << ")"
175 << endl;
176 }
177 cout << "Unit line thickness : " << thickness << " pixel(s)" << endl;
178 }
179 }
180
init_camera(Render * re)181 static void init_camera(Render *re)
182 {
183 // It is assumed that imported meshes are in the camera coordinate system.
184 // Therefore, the view point (i.e., camera position) is at the origin, and
185 // the model-view matrix is simply the identity matrix.
186
187 zero_v3(g_freestyle.viewpoint);
188
189 unit_m4(g_freestyle.mv);
190
191 copy_m4_m4(g_freestyle.proj, re->winmat);
192
193 #if 0
194 print_m4("mv", g_freestyle.mv);
195 print_m4("proj", g_freestyle.proj);
196 #endif
197 }
198
escape_quotes(char * name)199 static char *escape_quotes(char *name)
200 {
201 char *s = (char *)MEM_mallocN(strlen(name) * 2 + 1, "escape_quotes");
202 char *p = s;
203 while (*name) {
204 if (*name == '\'') {
205 *(p++) = '\\';
206 }
207 *(p++) = *(name++);
208 }
209 *p = '\0';
210 return s;
211 }
212
create_lineset_handler(char * layer_name,char * lineset_name)213 static char *create_lineset_handler(char *layer_name, char *lineset_name)
214 {
215 const char *fmt = "__import__('parameter_editor').process('%s', '%s')\n";
216 char *s1 = escape_quotes(layer_name);
217 char *s2 = escape_quotes(lineset_name);
218 char *text = BLI_sprintfN(fmt, s1, s2);
219 MEM_freeN(s1);
220 MEM_freeN(s2);
221 return text;
222 }
223
224 struct edge_type_condition {
225 int edge_type, value;
226 };
227
228 // examines the conditions and returns true if the target edge type needs to be computed
test_edge_type_conditions(struct edge_type_condition * conditions,int num_edge_types,bool logical_and,int target,bool distinct)229 static bool test_edge_type_conditions(struct edge_type_condition *conditions,
230 int num_edge_types,
231 bool logical_and,
232 int target,
233 bool distinct)
234 {
235 int target_condition = 0;
236 int num_non_target_positive_conditions = 0;
237 int num_non_target_negative_conditions = 0;
238
239 for (int i = 0; i < num_edge_types; i++) {
240 if (conditions[i].edge_type == target) {
241 target_condition = conditions[i].value;
242 }
243 else if (conditions[i].value > 0) {
244 ++num_non_target_positive_conditions;
245 }
246 else if (conditions[i].value < 0) {
247 ++num_non_target_negative_conditions;
248 }
249 }
250 if (distinct) {
251 // In this case, the 'target' edge type is assumed to appear on distinct edge
252 // of its own and never together with other edge types.
253 if (logical_and) {
254 if (num_non_target_positive_conditions > 0) {
255 return false;
256 }
257 if (target_condition > 0) {
258 return true;
259 }
260 if (target_condition < 0) {
261 return false;
262 }
263 if (num_non_target_negative_conditions > 0) {
264 return true;
265 }
266 }
267 else {
268 if (target_condition > 0) {
269 return true;
270 }
271 if (num_non_target_negative_conditions > 0) {
272 return true;
273 }
274 if (target_condition < 0) {
275 return false;
276 }
277 if (num_non_target_positive_conditions > 0) {
278 return false;
279 }
280 }
281 }
282 else {
283 // In this case, the 'target' edge type may appear together with other edge types.
284 if (target_condition > 0) {
285 return true;
286 }
287 if (target_condition < 0) {
288 return true;
289 }
290 if (logical_and) {
291 if (num_non_target_positive_conditions > 0) {
292 return false;
293 }
294 if (num_non_target_negative_conditions > 0) {
295 return true;
296 }
297 }
298 else {
299 if (num_non_target_negative_conditions > 0) {
300 return true;
301 }
302 if (num_non_target_positive_conditions > 0) {
303 return false;
304 }
305 }
306 }
307 return true;
308 }
309
prepare(Render * re,ViewLayer * view_layer,Depsgraph * depsgraph)310 static void prepare(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph)
311 {
312 // load mesh
313 re->i.infostr = TIP_("Freestyle: Mesh loading");
314 re->stats_draw(re->sdh, &re->i);
315 re->i.infostr = NULL;
316 if (controller->LoadMesh(
317 re, view_layer, depsgraph)) { // returns if scene cannot be loaded or if empty
318 return;
319 }
320 if (re->test_break(re->tbh)) {
321 return;
322 }
323
324 // add style modules
325 FreestyleConfig *config = &view_layer->freestyle_config;
326
327 if (G.debug & G_DEBUG_FREESTYLE) {
328 cout << "\n=== Rendering options ===" << endl;
329 }
330 int layer_count = 0;
331
332 switch (config->mode) {
333 case FREESTYLE_CONTROL_SCRIPT_MODE:
334 if (G.debug & G_DEBUG_FREESTYLE) {
335 cout << "Modules :" << endl;
336 }
337 for (FreestyleModuleConfig *module_conf = (FreestyleModuleConfig *)config->modules.first;
338 module_conf;
339 module_conf = module_conf->next) {
340 if (module_conf->script && module_conf->is_displayed) {
341 const char *id_name = module_conf->script->id.name + 2;
342 if (G.debug & G_DEBUG_FREESTYLE) {
343 cout << " " << layer_count + 1 << ": " << id_name;
344 if (module_conf->script->filepath) {
345 cout << " (" << module_conf->script->filepath << ")";
346 }
347 cout << endl;
348 }
349 controller->InsertStyleModule(layer_count, id_name, module_conf->script);
350 controller->toggleLayer(layer_count, true);
351 layer_count++;
352 }
353 }
354 if (G.debug & G_DEBUG_FREESTYLE) {
355 cout << endl;
356 }
357 controller->setComputeRidgesAndValleysFlag(
358 (config->flags & FREESTYLE_RIDGES_AND_VALLEYS_FLAG) ? true : false);
359 controller->setComputeSuggestiveContoursFlag(
360 (config->flags & FREESTYLE_SUGGESTIVE_CONTOURS_FLAG) ? true : false);
361 controller->setComputeMaterialBoundariesFlag(
362 (config->flags & FREESTYLE_MATERIAL_BOUNDARIES_FLAG) ? true : false);
363 break;
364 case FREESTYLE_CONTROL_EDITOR_MODE:
365 int use_ridges_and_valleys = 0;
366 int use_suggestive_contours = 0;
367 int use_material_boundaries = 0;
368 struct edge_type_condition conditions[] = {
369 {FREESTYLE_FE_SILHOUETTE, 0},
370 {FREESTYLE_FE_BORDER, 0},
371 {FREESTYLE_FE_CREASE, 0},
372 {FREESTYLE_FE_RIDGE_VALLEY, 0},
373 {FREESTYLE_FE_SUGGESTIVE_CONTOUR, 0},
374 {FREESTYLE_FE_MATERIAL_BOUNDARY, 0},
375 {FREESTYLE_FE_CONTOUR, 0},
376 {FREESTYLE_FE_EXTERNAL_CONTOUR, 0},
377 {FREESTYLE_FE_EDGE_MARK, 0},
378 };
379 int num_edge_types = ARRAY_SIZE(conditions);
380 if (G.debug & G_DEBUG_FREESTYLE) {
381 cout << "Linesets:" << endl;
382 }
383 for (FreestyleLineSet *lineset = (FreestyleLineSet *)config->linesets.first; lineset;
384 lineset = lineset->next) {
385 if (lineset->flags & FREESTYLE_LINESET_ENABLED) {
386 if (G.debug & G_DEBUG_FREESTYLE) {
387 cout << " " << layer_count + 1 << ": " << lineset->name << " - "
388 << (lineset->linestyle ? (lineset->linestyle->id.name + 2) : "<NULL>") << endl;
389 }
390 char *buffer = create_lineset_handler(view_layer->name, lineset->name);
391 controller->InsertStyleModule(layer_count, lineset->name, buffer);
392 controller->toggleLayer(layer_count, true);
393 MEM_freeN(buffer);
394 if (!(lineset->selection & FREESTYLE_SEL_EDGE_TYPES) || !lineset->edge_types) {
395 ++use_ridges_and_valleys;
396 ++use_suggestive_contours;
397 ++use_material_boundaries;
398 }
399 else {
400 // conditions for feature edge selection by edge types
401 for (int i = 0; i < num_edge_types; i++) {
402 if (!(lineset->edge_types & conditions[i].edge_type)) {
403 conditions[i].value = 0; // no condition specified
404 }
405 else if (!(lineset->exclude_edge_types & conditions[i].edge_type)) {
406 conditions[i].value = 1; // condition: X
407 }
408 else {
409 conditions[i].value = -1; // condition: NOT X
410 }
411 }
412 // logical operator for the selection conditions
413 bool logical_and = ((lineset->flags & FREESTYLE_LINESET_FE_AND) != 0);
414 // negation operator
415 if (lineset->flags & FREESTYLE_LINESET_FE_NOT) {
416 // convert an Exclusive condition into an
417 // Inclusive equivalent using De Morgan's laws:
418 // - NOT (X OR Y) --> (NOT X) AND (NOT Y)
419 // - NOT (X AND Y) --> (NOT X) OR (NOT Y)
420 for (int i = 0; i < num_edge_types; i++) {
421 conditions[i].value *= -1;
422 }
423 logical_and = !logical_and;
424 }
425 if (test_edge_type_conditions(
426 conditions, num_edge_types, logical_and, FREESTYLE_FE_RIDGE_VALLEY, true)) {
427 ++use_ridges_and_valleys;
428 }
429 if (test_edge_type_conditions(conditions,
430 num_edge_types,
431 logical_and,
432 FREESTYLE_FE_SUGGESTIVE_CONTOUR,
433 true)) {
434 ++use_suggestive_contours;
435 }
436 if (test_edge_type_conditions(conditions,
437 num_edge_types,
438 logical_and,
439 FREESTYLE_FE_MATERIAL_BOUNDARY,
440 true)) {
441 ++use_material_boundaries;
442 }
443 }
444 layer_count++;
445 }
446 }
447 controller->setComputeRidgesAndValleysFlag(use_ridges_and_valleys > 0);
448 controller->setComputeSuggestiveContoursFlag(use_suggestive_contours > 0);
449 controller->setComputeMaterialBoundariesFlag(use_material_boundaries > 0);
450 break;
451 }
452
453 // set parameters
454 if (config->flags & FREESTYLE_ADVANCED_OPTIONS_FLAG) {
455 controller->setSphereRadius(config->sphere_radius);
456 controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon);
457 }
458 else {
459 controller->setSphereRadius(DEFAULT_SPHERE_RADIUS);
460 controller->setSuggestiveContourKrDerivativeEpsilon(DEFAULT_DKR_EPSILON);
461 }
462 controller->setFaceSmoothness((config->flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) ? true : false);
463 controller->setCreaseAngle(RAD2DEGF(config->crease_angle));
464 controller->setVisibilityAlgo((config->flags & FREESTYLE_CULLING) ?
465 FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE :
466 FREESTYLE_ALGO_ADAPTIVE_CUMULATIVE);
467
468 if (G.debug & G_DEBUG_FREESTYLE) {
469 cout << "Crease angle : " << controller->getCreaseAngle() << endl;
470 cout << "Sphere radius : " << controller->getSphereRadius() << endl;
471 cout << "Face smoothness : " << (controller->getFaceSmoothness() ? "enabled" : "disabled")
472 << endl;
473 cout << "Ridges and valleys : "
474 << (controller->getComputeRidgesAndValleysFlag() ? "enabled" : "disabled") << endl;
475 cout << "Suggestive contours : "
476 << (controller->getComputeSuggestiveContoursFlag() ? "enabled" : "disabled") << endl;
477 cout << "Suggestive contour Kr derivative epsilon : "
478 << controller->getSuggestiveContourKrDerivativeEpsilon() << endl;
479 cout << "Material boundaries : "
480 << (controller->getComputeMaterialBoundariesFlag() ? "enabled" : "disabled") << endl;
481 cout << endl;
482 }
483
484 // set diffuse and z depth passes
485 RenderLayer *rl = RE_GetRenderLayer(re->result, view_layer->name);
486 bool diffuse = false, z = false;
487 for (RenderPass *rpass = (RenderPass *)rl->passes.first; rpass; rpass = rpass->next) {
488 if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE_COLOR)) {
489 controller->setPassDiffuse(rpass->rect, rpass->rectx, rpass->recty);
490 diffuse = true;
491 }
492 if (STREQ(rpass->name, RE_PASSNAME_Z)) {
493 controller->setPassZ(rpass->rect, rpass->rectx, rpass->recty);
494 z = true;
495 }
496 }
497 if (G.debug & G_DEBUG_FREESTYLE) {
498 cout << "Passes :" << endl;
499 cout << " Diffuse = " << (diffuse ? "enabled" : "disabled") << endl;
500 cout << " Z = " << (z ? "enabled" : "disabled") << endl;
501 }
502
503 if (controller->hitViewMapCache()) {
504 return;
505 }
506
507 // compute view map
508 re->i.infostr = TIP_("Freestyle: View map creation");
509 re->stats_draw(re->sdh, &re->i);
510 re->i.infostr = NULL;
511 controller->ComputeViewMap();
512 }
513
FRS_composite_result(Render * re,ViewLayer * view_layer,Render * freestyle_render)514 void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_render)
515 {
516 RenderLayer *rl;
517 float *src, *dest, *pixSrc, *pixDest;
518 int x, y, rectx, recty;
519
520 if (freestyle_render == NULL || freestyle_render->result == NULL) {
521 return;
522 }
523
524 rl = render_get_active_layer(freestyle_render, freestyle_render->result);
525 if (!rl) {
526 if (G.debug & G_DEBUG_FREESTYLE) {
527 cout << "No source render layer to composite" << endl;
528 }
529 return;
530 }
531
532 src = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, freestyle_render->viewname);
533 if (!src) {
534 if (G.debug & G_DEBUG_FREESTYLE) {
535 cout << "No source result image to composite" << endl;
536 }
537 return;
538 }
539 #if 0
540 if (G.debug & G_DEBUG_FREESTYLE) {
541 cout << "src: " << rl->rectx << " x " << rl->recty << endl;
542 }
543 #endif
544
545 rl = RE_GetRenderLayer(re->result, view_layer->name);
546 if (!rl) {
547 if (G.debug & G_DEBUG_FREESTYLE) {
548 cout << "No destination render layer to composite to" << endl;
549 }
550 return;
551 }
552
553 if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) {
554 RE_create_render_pass(
555 re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname);
556 dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_FREESTYLE, re->viewname);
557 }
558 else {
559 dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, re->viewname);
560 }
561 if (!dest) {
562 if (G.debug & G_DEBUG_FREESTYLE) {
563 cout << "No destination result image to composite to" << endl;
564 }
565 return;
566 }
567 #if 0
568 if (G.debug & G_DEBUG_FREESTYLE) {
569 cout << "dest: " << rl->rectx << " x " << rl->recty << endl;
570 }
571 #endif
572
573 rectx = re->rectx;
574 recty = re->recty;
575 for (y = 0; y < recty; y++) {
576 for (x = 0; x < rectx; x++) {
577 pixSrc = src + 4 * (rectx * y + x);
578 if (pixSrc[3] > 0.0) {
579 pixDest = dest + 4 * (rectx * y + x);
580 blend_color_mix_float(pixDest, pixDest, pixSrc);
581 }
582 }
583 }
584 }
585
displayed_layer_count(ViewLayer * view_layer)586 static int displayed_layer_count(ViewLayer *view_layer)
587 {
588 int count = 0;
589
590 switch (view_layer->freestyle_config.mode) {
591 case FREESTYLE_CONTROL_SCRIPT_MODE:
592 for (FreestyleModuleConfig *module =
593 (FreestyleModuleConfig *)view_layer->freestyle_config.modules.first;
594 module;
595 module = module->next) {
596 if (module->script && module->is_displayed) {
597 count++;
598 }
599 }
600 break;
601 case FREESTYLE_CONTROL_EDITOR_MODE:
602 for (FreestyleLineSet *lineset =
603 (FreestyleLineSet *)view_layer->freestyle_config.linesets.first;
604 lineset;
605 lineset = lineset->next) {
606 if (lineset->flags & FREESTYLE_LINESET_ENABLED) {
607 count++;
608 }
609 }
610 break;
611 }
612 return count;
613 }
614
FRS_is_freestyle_enabled(ViewLayer * view_layer)615 int FRS_is_freestyle_enabled(ViewLayer *view_layer)
616 {
617 return ((view_layer->flag & VIEW_LAYER_RENDER) && (view_layer->flag & VIEW_LAYER_FREESTYLE) &&
618 displayed_layer_count(view_layer) > 0);
619 }
620
FRS_init_stroke_renderer(Render * re)621 void FRS_init_stroke_renderer(Render *re)
622 {
623 if (G.debug & G_DEBUG_FREESTYLE) {
624 cout << endl;
625 cout << "#===============================================================" << endl;
626 cout << "# Freestyle" << endl;
627 cout << "#===============================================================" << endl;
628 }
629
630 init_view(re);
631
632 controller->ResetRenderCount();
633 }
634
FRS_begin_stroke_rendering(Render * re)635 void FRS_begin_stroke_rendering(Render *re)
636 {
637 init_camera(re);
638 }
639
FRS_do_stroke_rendering(Render * re,ViewLayer * view_layer)640 void FRS_do_stroke_rendering(Render *re, ViewLayer *view_layer)
641 {
642 RenderMonitor monitor(re);
643 controller->setRenderMonitor(&monitor);
644 controller->setViewMapCache(
645 (view_layer->freestyle_config.flags & FREESTYLE_VIEW_MAP_CACHE) ? true : false);
646
647 if (G.debug & G_DEBUG_FREESTYLE) {
648 cout << endl;
649 cout << "----------------------------------------------------------" << endl;
650 cout << "| " << (re->scene->id.name + 2) << "|" << view_layer->name << endl;
651 cout << "----------------------------------------------------------" << endl;
652 }
653
654 /* Create depsgraph and evaluate scene. */
655 ViewLayer *scene_view_layer = (ViewLayer *)BLI_findstring(
656 &re->scene->view_layers, view_layer->name, offsetof(ViewLayer, name));
657 Depsgraph *depsgraph = DEG_graph_new(re->main, re->scene, scene_view_layer, DAG_EVAL_RENDER);
658 BKE_scene_graph_update_for_newframe(depsgraph);
659
660 // prepare Freestyle:
661 // - load mesh
662 // - add style modules
663 // - set parameters
664 // - compute view map
665 prepare(re, view_layer, depsgraph);
666
667 if (re->test_break(re->tbh)) {
668 controller->CloseFile();
669 if (G.debug & G_DEBUG_FREESTYLE) {
670 cout << "Break" << endl;
671 }
672 }
673 else {
674 // render and composite Freestyle result
675 if (controller->_ViewMap) {
676 // render strokes
677 re->i.infostr = TIP_("Freestyle: Stroke rendering");
678 re->stats_draw(re->sdh, &re->i);
679 re->i.infostr = NULL;
680 g_freestyle.scene = DEG_get_evaluated_scene(depsgraph);
681 int strokeCount = controller->DrawStrokes();
682 Render *freestyle_render = NULL;
683 if (strokeCount > 0) {
684 freestyle_render = controller->RenderStrokes(re, true);
685 }
686 controller->CloseFile();
687 g_freestyle.scene = NULL;
688
689 // composite result
690 if (freestyle_render) {
691 FRS_composite_result(re, view_layer, freestyle_render);
692 RE_FreeRender(freestyle_render);
693 }
694 }
695 }
696
697 DEG_graph_free(depsgraph);
698 }
699
FRS_end_stroke_rendering(Render *)700 void FRS_end_stroke_rendering(Render * /*re*/)
701 {
702 // clear canvas
703 controller->Clear();
704 }
705
FRS_free_view_map_cache(void)706 void FRS_free_view_map_cache(void)
707 {
708 // free cache
709 controller->DeleteViewMap(true);
710 #if 0
711 if (G.debug & G_DEBUG_FREESTYLE) {
712 printf("View map cache freed\n");
713 }
714 #endif
715 }
716
717 //=======================================================
718 // Freestyle Panel Configuration
719 //=======================================================
720
FRS_copy_active_lineset(FreestyleConfig * config)721 void FRS_copy_active_lineset(FreestyleConfig *config)
722 {
723 FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
724
725 if (lineset) {
726 lineset_buffer.linestyle = lineset->linestyle;
727 lineset_buffer.flags = lineset->flags;
728 lineset_buffer.selection = lineset->selection;
729 lineset_buffer.qi = lineset->qi;
730 lineset_buffer.qi_start = lineset->qi_start;
731 lineset_buffer.qi_end = lineset->qi_end;
732 lineset_buffer.edge_types = lineset->edge_types;
733 lineset_buffer.exclude_edge_types = lineset->exclude_edge_types;
734 lineset_buffer.group = lineset->group;
735 strcpy(lineset_buffer.name, lineset->name);
736 lineset_copied = true;
737 }
738 }
739
FRS_paste_active_lineset(FreestyleConfig * config)740 void FRS_paste_active_lineset(FreestyleConfig *config)
741 {
742 if (!lineset_copied) {
743 return;
744 }
745
746 FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
747
748 if (lineset) {
749 if (lineset->linestyle) {
750 id_us_min(&lineset->linestyle->id);
751 }
752 lineset->linestyle = lineset_buffer.linestyle;
753 if (lineset->linestyle) {
754 id_us_plus(&lineset->linestyle->id);
755 }
756 lineset->flags = lineset_buffer.flags;
757 lineset->selection = lineset_buffer.selection;
758 lineset->qi = lineset_buffer.qi;
759 lineset->qi_start = lineset_buffer.qi_start;
760 lineset->qi_end = lineset_buffer.qi_end;
761 lineset->edge_types = lineset_buffer.edge_types;
762 lineset->exclude_edge_types = lineset_buffer.exclude_edge_types;
763 if (lineset->group) {
764 id_us_min(&lineset->group->id);
765 lineset->group = NULL;
766 }
767 if (lineset_buffer.group) {
768 lineset->group = lineset_buffer.group;
769 id_us_plus(&lineset->group->id);
770 }
771 strcpy(lineset->name, lineset_buffer.name);
772 BKE_freestyle_lineset_unique_name(config, lineset);
773 lineset->flags |= FREESTYLE_LINESET_CURRENT;
774 }
775 }
776
FRS_delete_active_lineset(FreestyleConfig * config)777 void FRS_delete_active_lineset(FreestyleConfig *config)
778 {
779 FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
780
781 if (lineset) {
782 BKE_freestyle_lineset_delete(config, lineset);
783 }
784 }
785
786 /**
787 * Reinsert the active lineset at an offset \a direction from current position.
788 * \return if position of active lineset has changed.
789 */
FRS_move_active_lineset(FreestyleConfig * config,int direction)790 bool FRS_move_active_lineset(FreestyleConfig *config, int direction)
791 {
792 FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
793 return (lineset != NULL) && BLI_listbase_link_move(&config->linesets, lineset, direction);
794 }
795
796 // Testing
797
FRS_create_stroke_material(Main * bmain,struct FreestyleLineStyle * linestyle)798 Material *FRS_create_stroke_material(Main *bmain, struct FreestyleLineStyle *linestyle)
799 {
800 bNodeTree *nt = (linestyle->use_nodes) ? linestyle->nodetree : NULL;
801 Material *ma = BlenderStrokeRenderer::GetStrokeShader(bmain, nt, true);
802 ma->id.us = 0;
803 return ma;
804 }
805
806 } // extern "C"
807